]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/tt-rss-layer.js.uncompressed.js
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
[tt-rss.git] / lib / dojo / tt-rss-layer.js.uncompressed.js
1 /*
2         Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3         Available via Academic Free License >= 2.1 OR the modified BSD license.
4         see: http://dojotoolkit.org/license for details
5 */
6
7 /*
8         This is an optimized version of Dojo, built for deployment and not for
9         development. To get sources and documentation, please visit:
10
11                 http://dojotoolkit.org
12 */
13
14 dojo.provide("tt-rss-layer");
15 if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16 dojo._hasResource["dojo.date.stamp"] = true;
17 dojo.provide("dojo.date.stamp");
18
19 // Methods to convert dates to or from a wire (string) format using well-known conventions
20
21 dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
22         //      summary:
23         //              Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
24         //
25         //      description:
26         //              Accepts a string formatted according to a profile of ISO8601 as defined by
27         //              [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
28         //              Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
29         //              The following combinations are valid:
30         //
31         //                      * dates only
32         //                      |       * yyyy
33         //                      |       * yyyy-MM
34         //                      |       * yyyy-MM-dd
35         //                      * times only, with an optional time zone appended
36         //                      |       * THH:mm
37         //                      |       * THH:mm:ss
38         //                      |       * THH:mm:ss.SSS
39         //                      * and "datetimes" which could be any combination of the above
40         //
41         //              timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
42         //              Assumes the local time zone if not specified.  Does not validate.  Improperly formatted
43         //              input may return null.  Arguments which are out of bounds will be handled
44         //              by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
45         //              Only years between 100 and 9999 are supported.
46         //
47         //      formattedString:
48         //              A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
49         //
50         //      defaultTime:
51         //              Used for defaults for fields omitted in the formattedString.
52         //              Uses 1970-01-01T00:00:00.0Z by default.
53
54         if(!dojo.date.stamp._isoRegExp){
55                 dojo.date.stamp._isoRegExp =
56 //TODO: could be more restrictive and check for 00-59, etc.
57                         /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
58         }
59
60         var match = dojo.date.stamp._isoRegExp.exec(formattedString),
61                 result = null;
62
63         if(match){
64                 match.shift();
65                 if(match[1]){match[1]--;} // Javascript Date months are 0-based
66                 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
67
68                 if(defaultTime){
69                         // mix in defaultTime.  Relatively expensive, so use || operators for the fast path of defaultTime === 0
70                         defaultTime = new Date(defaultTime);
71                         dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
72                                 return defaultTime["get" + prop]();
73                         }), function(value, index){
74                                 match[index] = match[index] || value;
75                         });
76                 }
77                 result = new Date(match[0]||1970, match[1]||0, match[2]||1, match[3]||0, match[4]||0, match[5]||0, match[6]||0); //TODO: UTC defaults
78                 if(match[0] < 100){
79                         result.setFullYear(match[0] || 1970);
80                 }
81
82                 var offset = 0,
83                         zoneSign = match[7] && match[7].charAt(0);
84                 if(zoneSign != 'Z'){
85                         offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
86                         if(zoneSign != '-'){ offset *= -1; }
87                 }
88                 if(zoneSign){
89                         offset -= result.getTimezoneOffset();
90                 }
91                 if(offset){
92                         result.setTime(result.getTime() + offset * 60000);
93                 }
94         }
95
96         return result; // Date or null
97 }
98
99 /*=====
100         dojo.date.stamp.__Options = function(){
101                 //      selector: String
102                 //              "date" or "time" for partial formatting of the Date object.
103                 //              Both date and time will be formatted by default.
104                 //      zulu: Boolean
105                 //              if true, UTC/GMT is used for a timezone
106                 //      milliseconds: Boolean
107                 //              if true, output milliseconds
108                 this.selector = selector;
109                 this.zulu = zulu;
110                 this.milliseconds = milliseconds;
111         }
112 =====*/
113
114 dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
115         //      summary:
116         //              Format a Date object as a string according a subset of the ISO-8601 standard
117         //
118         //      description:
119         //              When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
120         //              The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
121         //              Does not check bounds.  Only years between 100 and 9999 are supported.
122         //
123         //      dateObject:
124         //              A Date object
125
126         var _ = function(n){ return (n < 10) ? "0" + n : n; };
127         options = options || {};
128         var formattedDate = [],
129                 getter = options.zulu ? "getUTC" : "get",
130                 date = "";
131         if(options.selector != "time"){
132                 var year = dateObject[getter+"FullYear"]();
133                 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
134         }
135         formattedDate.push(date);
136         if(options.selector != "date"){
137                 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
138                 var millis = dateObject[getter+"Milliseconds"]();
139                 if(options.milliseconds){
140                         time += "."+ (millis < 100 ? "0" : "") + _(millis);
141                 }
142                 if(options.zulu){
143                         time += "Z";
144                 }else if(options.selector != "time"){
145                         var timezoneOffset = dateObject.getTimezoneOffset();
146                         var absOffset = Math.abs(timezoneOffset);
147                         time += (timezoneOffset > 0 ? "-" : "+") + 
148                                 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
149                 }
150                 formattedDate.push(time);
151         }
152         return formattedDate.join('T'); // String
153 }
154
155 }
156
157 if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
158 dojo._hasResource["dojo.parser"] = true;
159 dojo.provide("dojo.parser");
160
161
162 new Date("X"); // workaround for #11279, new Date("") == NaN
163
164 dojo.parser = new function(){
165         // summary: The Dom/Widget parsing package
166
167         var d = dojo;
168         this._attrName = d._scopeName + "Type";
169         this._query = "[" + this._attrName + "]";
170
171         function val2type(/*Object*/ value){
172                 // summary:
173                 //              Returns name of type of given value.
174
175                 if(d.isString(value)){ return "string"; }
176                 if(typeof value == "number"){ return "number"; }
177                 if(typeof value == "boolean"){ return "boolean"; }
178                 if(d.isFunction(value)){ return "function"; }
179                 if(d.isArray(value)){ return "array"; } // typeof [] == "object"
180                 if(value instanceof Date) { return "date"; } // assume timestamp
181                 if(value instanceof d._Url){ return "url"; }
182                 return "object";
183         }
184
185         function str2obj(/*String*/ value, /*String*/ type){
186                 // summary:
187                 //              Convert given string value to given type
188                 switch(type){
189                         case "string":
190                                 return value;
191                         case "number":
192                                 return value.length ? Number(value) : NaN;
193                         case "boolean":
194                                 // for checked/disabled value might be "" or "checked".  interpret as true.
195                                 return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
196                         case "function":
197                                 if(d.isFunction(value)){
198                                         // IE gives us a function, even when we say something like onClick="foo"
199                                         // (in which case it gives us an invalid function "function(){ foo }"). 
200                                         //  Therefore, convert to string
201                                         value=value.toString();
202                                         value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
203                                 }
204                                 try{
205                                         if(value === "" || value.search(/[^\w\.]+/i) != -1){
206                                                 // The user has specified some text for a function like "return x+5"
207                                                 return new Function(value);
208                                         }else{
209                                                 // The user has specified the name of a function like "myOnClick"
210                                                 // or a single word function "return"
211                                                 return d.getObject(value, false) || new Function(value);
212                                         }
213                                 }catch(e){ return new Function(); }
214                         case "array":
215                                 return value ? value.split(/\s*,\s*/) : [];
216                         case "date":
217                                 switch(value){
218                                         case "": return new Date("");   // the NaN of dates
219                                         case "now": return new Date();  // current date
220                                         default: return d.date.stamp.fromISOString(value);
221                                 }
222                         case "url":
223                                 return d.baseUrl + value;
224                         default:
225                                 return d.fromJson(value);
226                 }
227         }
228
229         var instanceClasses = {
230                 // map from fully qualified name (like "dijit.Button") to structure like
231                 // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
232         };
233
234         // Widgets like BorderContainer add properties to _Widget via dojo.extend().
235         // If BorderContainer is loaded after _Widget's parameter list has been cached,
236         // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
237         dojo.connect(dojo, "extend", function(){
238                 instanceClasses = {};
239         });
240
241         function getClassInfo(/*String*/ className){
242                 // className:
243                 //              fully qualified name (like "dijit.form.Button")
244                 // returns:
245                 //              structure like
246                 //                      { 
247                 //                              cls: dijit.Button, 
248                 //                              params: { label: "string", disabled: "boolean"}
249                 //                      }
250
251                 if(!instanceClasses[className]){
252                         // get pointer to widget class
253                         var cls = d.getObject(className);
254                         if(!cls){ return null; }                // class not defined [yet]
255
256                         var proto = cls.prototype;
257         
258                         // get table of parameter names & types
259                         var params = {}, dummyClass = {};
260                         for(var name in proto){
261                                 if(name.charAt(0)=="_"){ continue; }    // skip internal properties
262                                 if(name in dummyClass){ continue; }             // skip "constructor" and "toString"
263                                 var defVal = proto[name];
264                                 params[name]=val2type(defVal);
265                         }
266
267                         instanceClasses[className] = { cls: cls, params: params };
268                 }
269                 return instanceClasses[className];
270         }
271
272         this._functionFromScript = function(script){
273                 var preamble = "";
274                 var suffix = "";
275                 var argsStr = script.getAttribute("args");
276                 if(argsStr){
277                         d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
278                                 preamble += "var "+part+" = arguments["+idx+"]; ";
279                         });
280                 }
281                 var withStr = script.getAttribute("with");
282                 if(withStr && withStr.length){
283                         d.forEach(withStr.split(/\s*,\s*/), function(part){
284                                 preamble += "with("+part+"){";
285                                 suffix += "}";
286                         });
287                 }
288                 return new Function(preamble+script.innerHTML+suffix);
289         }
290
291         this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){
292                 // summary:
293                 //              Takes array of nodes, and turns them into class instances and
294                 //              potentially calls a startup method to allow them to connect with
295                 //              any children.
296                 // nodes: Array
297                 //              Array of nodes or objects like
298                 //      |               {
299                 //      |                       type: "dijit.form.Button",
300                 //      |                       node: DOMNode,
301                 //      |                       scripts: [ ... ],       // array of <script type="dojo/..."> children of node
302                 //      |                       inherited: { ... }      // settings inherited from ancestors like dir, theme, etc.
303                 //      |               }
304                 // mixin: Object?
305                 //              An object that will be mixed in with each node in the array.
306                 //              Values in the mixin will override values in the node, if they
307                 //              exist.
308                 // args: Object?
309                 //              An object used to hold kwArgs for instantiation.
310                 //              Supports 'noStart' and inherited.
311                 var thelist = [], dp = dojo.parser;
312                 mixin = mixin||{};
313                 args = args||{};
314                 
315                 d.forEach(nodes, function(obj){
316                         if(!obj){ return; }
317
318                         // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.s
319                         var node, type, clsInfo, clazz, scripts;
320                         if(obj.node){
321                                 // new format of nodes[] array, object w/lots of properties pre-computed for me
322                                 node = obj.node;
323                                 type = obj.type;
324                                 clsInfo = obj.clsInfo || (type && getClassInfo(type));
325                                 clazz = clsInfo && clsInfo.cls;
326                                 scripts = obj.scripts;
327                         }else{
328                                 // old (backwards compatible) format of nodes[] array, simple array of DOMNodes
329                                 node = obj;
330                                 type = dp._attrName in mixin ? mixin[dp._attrName] : node.getAttribute(dp._attrName);
331                                 clsInfo = type && getClassInfo(type);
332                                 clazz = clsInfo && clsInfo.cls;
333                                 scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] : 
334                                                         d.query("> script[type^='dojo/']", node));
335                         }
336                         if(!clsInfo){
337                                 throw new Error("Could not load class '" + type);
338                         }
339
340                         // Setup hash to hold parameter settings for this widget.   Start with the parameter
341                         // settings inherited from ancestors ("dir" and "lang").
342                         // Inherited setting may later be overridden by explicit settings on node itself.
343                         var params = {},
344                                 attributes = node.attributes;
345                         if(args.defaults){
346                                 // settings for the document itself (or whatever subtree is being parsed)
347                                 dojo.mixin(params, args.defaults);
348                         }
349                         if(obj.inherited){
350                                 // settings from dir=rtl or lang=... on a node above this node
351                                 dojo.mixin(params, obj.inherited);
352                         }
353
354                         // read parameters (ie, attributes) specified on DOMNode
355                         // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
356                         for(var name in clsInfo.params){
357                                 var item = name in mixin?{value:mixin[name],specified:true}:attributes.getNamedItem(name);
358                                 if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
359                                 var value = item.value;
360                                 // Deal with IE quirks for 'class' and 'style'
361                                 switch(name){
362                                 case "class":
363                                         value = "className" in mixin?mixin.className:node.className;
364                                         break;
365                                 case "style":
366                                         value = "style" in mixin?mixin.style:(node.style && node.style.cssText); // FIXME: Opera?
367                                 }
368                                 var _type = clsInfo.params[name];
369                                 if(typeof value == "string"){
370                                         params[name] = str2obj(value, _type);
371                                 }else{
372                                         params[name] = value;
373                                 }
374                         }
375
376                         // Process <script type="dojo/*"> script tags
377                         // <script type="dojo/method" event="foo"> tags are added to params, and passed to
378                         // the widget on instantiation.
379                         // <script type="dojo/method"> tags (with no event) are executed after instantiation
380                         // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
381                         // note: dojo/* script tags cannot exist in self closing widgets, like <input />
382                         var connects = [],      // functions to connect after instantiation
383                                 calls = [];             // functions to call after instantiation
384
385                         d.forEach(scripts, function(script){
386                                 node.removeChild(script);
387                                 var event = script.getAttribute("event"),
388                                         type = script.getAttribute("type"),
389                                         nf = d.parser._functionFromScript(script);
390                                 if(event){
391                                         if(type == "dojo/connect"){
392                                                 connects.push({event: event, func: nf});
393                                         }else{
394                                                 params[event] = nf;
395                                         }
396                                 }else{
397                                         calls.push(nf);
398                                 }
399                         });
400
401                         var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory;
402                         // create the instance
403                         var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
404                         thelist.push(instance);
405
406                         // map it to the JS namespace if that makes sense
407                         var jsname = node.getAttribute("jsId");
408                         if(jsname){
409                                 d.setObject(jsname, instance);
410                         }
411
412                         // process connections and startup functions
413                         d.forEach(connects, function(connect){
414                                 d.connect(instance, connect.event, null, connect.func);
415                         });
416                         d.forEach(calls, function(func){
417                                 func.call(instance);
418                         });
419                 });
420
421                 // Call startup on each top level instance if it makes sense (as for
422                 // widgets).  Parent widgets will recursively call startup on their
423                 // (non-top level) children
424                 if(!mixin._started){
425                         // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
426                         // relationships in the nodes[] array so that no getParent() call is needed.
427                         // Note that will  require a parse() call from ContentPane setting a param that the
428                         // ContentPane is the parent widget (so that the parse doesn't call startup() on the
429                         // ContentPane's children)
430                         d.forEach(thelist, function(instance){
431                                 if(     !args.noStart && instance  && 
432                                         instance.startup &&
433                                         !instance._started && 
434                                         (!instance.getParent || !instance.getParent())
435                                 ){
436                                         instance.startup();
437                                 }
438                         });
439                 }
440                 return thelist;
441         };
442
443         this.parse = function(/*DomNode?*/ rootNode, /* Object? */ args){
444                 // summary:
445                 //              Scan the DOM for class instances, and instantiate them.
446                 //
447                 // description:
448                 //              Search specified node (or root node) recursively for class instances,
449                 //              and instantiate them Searches for
450                 //              dojoType="qualified.class.name"
451                 //
452                 // rootNode: DomNode?
453                 //              A default starting root node from which to start the parsing. Can be
454                 //              omitted, defaulting to the entire document. If omitted, the `args`
455                 //              object can be passed in this place. If the `args` object has a 
456                 //              `rootNode` member, that is used.
457                 //
458                 // args:
459                 //              a kwArgs object passed along to instantiate()
460                 //              
461                 //                      * noStart: Boolean?
462                 //                              when set will prevent the parser from calling .startup()
463                 //                              when locating the nodes. 
464                 //                      * rootNode: DomNode?
465                 //                              identical to the function's `rootNode` argument, though
466                 //                              allowed to be passed in via this `args object. 
467                 //                      * inherited: Object
468                 //                              Hash possibly containing dir and lang settings to be applied to
469                 //                              parsed widgets, unless there's another setting on a sub-node that overrides
470                 //
471                 //
472                 // example:
473                 //              Parse all widgets on a page:
474                 //      |               dojo.parser.parse();
475                 //
476                 // example:
477                 //              Parse all classes within the node with id="foo"
478                 //      |               dojo.parser.parse(dojo.byId(foo));
479                 //
480                 // example:
481                 //              Parse all classes in a page, but do not call .startup() on any 
482                 //              child
483                 //      |               dojo.parser.parse({ noStart: true })
484                 //
485                 // example:
486                 //              Parse all classes in a node, but do not call .startup()
487                 //      |               dojo.parser.parse(someNode, { noStart:true });
488                 //      |               // or
489                 //      |               dojo.parser.parse({ noStart:true, rootNode: someNode });
490
491                 // determine the root node based on the passed arguments.
492                 var root;
493                 if(!args && rootNode && rootNode.rootNode){
494                         args = rootNode;
495                         root = args.rootNode;
496                 }else{
497                         root = rootNode;
498                 }
499
500                 var attrName = this._attrName;
501                 function scan(parent, list){
502                         // summary:
503                         //              Parent is an Object representing a DOMNode, with or without a dojoType specified.
504                         //              Scan parent's children looking for nodes with dojoType specified, storing in list[].
505                         //              If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
506                         // parent: Object
507                         //              Object representing the parent node, like
508                         //      |       {
509                         //      |               node: DomNode,                  // scan children of this node
510                         //      |               inherited: {dir: "rtl"},        // dir/lang setting inherited from above node
511                         //      |
512                         //      |               // attributes only set if node has dojoType specified
513                         //      |               scripts: [],                    // empty array, put <script type=dojo/*> in here
514                         //      |               clsInfo: { cls: dijit.form.Button, ...}
515                         //      |       }
516                         // list: DomNode[]
517                         //              Output array of objects (same format as parent) representing nodes to be turned into widgets
518
519                         // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
520                         var inherited = dojo.clone(parent.inherited);
521                         dojo.forEach(["dir", "lang"], function(name){
522                                 var val = parent.node.getAttribute(name);
523                                 if(val){
524                                         inherited[name] = val;
525                                 }
526                         });
527
528                         // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
529                         var scripts = parent.scripts;
530
531                         // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
532                         var recurse = !parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser;
533
534                         // scan parent's children looking for dojoType and <script type=dojo/*>
535                         for(var child = parent.node.firstChild; child; child = child.nextSibling){
536                                 if(child.nodeType == 1){
537                                         var type = recurse && child.getAttribute(attrName);
538                                         if(type){
539                                                 // if dojoType specified, add to output array of nodes to instantiate
540                                                 var params = {
541                                                         "type": type,
542                                                         clsInfo: getClassInfo(type),    // note: won't find classes declared via dojo.Declaration
543                                                         node: child,
544                                                         scripts: [], // <script> nodes that are parent's children
545                                                         inherited: inherited // dir & lang attributes inherited from parent
546                                                 };
547                                                 list.push(params);
548
549                                                 // Recurse, collecting <script type="dojo/..."> children, and also looking for
550                                                 // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
551                                                 scan(params, list);
552                                         }else if(scripts && child.nodeName.toLowerCase() == "script"){
553                                                 // if <script type="dojo/...">, save in scripts[]
554                                                 type = child.getAttribute("type");
555                                                 if (type && /^dojo\//i.test(type)) {
556                                                         scripts.push(child);
557                                                 }
558                                         }else if(recurse){
559                                                 // Recurse, looking for grandchild nodes with dojoType specified
560                                                 scan({
561                                                         node: child,
562                                                         inherited: inherited
563                                                 }, list);
564                                         }
565                                 }
566                         }
567                 }
568
569                 // Make list of all nodes on page w/dojoType specified
570                 var list = [];
571                 scan({
572                         node: root ? dojo.byId(root) : dojo.body(),
573                         inherited: (args && args.inherited) || {
574                                 dir: dojo._isBodyLtr() ? "ltr" : "rtl"
575                         }
576                 }, list);
577
578                 // go build the object instances
579                 return this.instantiate(list, null, args); // Array
580         };
581 }();
582
583 //Register the parser callback. It should be the first callback
584 //after the a11y test.
585
586 (function(){
587         var parseRunner = function(){ 
588                 if(dojo.config.parseOnLoad){
589                         dojo.parser.parse(); 
590                 }
591         };
592
593         // FIXME: need to clobber cross-dependency!!
594         if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){
595                 dojo._loaders.splice(1, 0, parseRunner);
596         }else{
597                 dojo._loaders.unshift(parseRunner);
598         }
599 })();
600
601 }
602
603 if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
604 dojo._hasResource["dojo.window"] = true;
605 dojo.provide("dojo.window");
606
607 dojo.window.getBox = function(){
608         // summary:
609         //              Returns the dimensions and scroll position of the viewable area of a browser window
610
611         var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement;
612
613         // get scroll position
614         var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work
615         return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
616 };
617
618 dojo.window.get = function(doc){
619         // summary:
620         //              Get window object associated with document doc
621
622         // In some IE versions (at least 6.0), document.parentWindow does not return a
623         // reference to the real window object (maybe a copy), so we must fix it as well
624         // We use IE specific execScript to attach the real window reference to
625         // document._parentWindow for later use
626         if(dojo.isIE && window !== document.parentWindow){
627                 /*
628                 In IE 6, only the variable "window" can be used to connect events (others
629                 may be only copies).
630                 */
631                 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
632                 //to prevent memory leak, unset it after use
633                 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
634                 var win = doc._parentWindow;
635                 doc._parentWindow = null;
636                 return win;     //      Window
637         }
638
639         return doc.parentWindow || doc.defaultView;     //      Window
640 };
641
642 dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
643         // summary:
644         //              Scroll the passed node into view, if it is not already.
645         
646         // don't rely on node.scrollIntoView working just because the function is there
647
648         try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
649                 node = dojo.byId(node);
650                 var doc = node.ownerDocument || dojo.doc,
651                         body = doc.body || dojo.body(),
652                         html = doc.documentElement || body.parentNode,
653                         isIE = dojo.isIE, isWK = dojo.isWebKit;
654                 // if an untested browser, then use the native method
655                 if((!(dojo.isMoz || isIE || isWK || dojo.isOpera) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
656                         node.scrollIntoView(false); // short-circuit to native if possible
657                         return;
658                 }
659                 var backCompat = doc.compatMode == 'BackCompat',
660                         clientAreaRoot = backCompat? body : html,
661                         scrollRoot = isWK ? body : clientAreaRoot,
662                         rootWidth = clientAreaRoot.clientWidth,
663                         rootHeight = clientAreaRoot.clientHeight,
664                         rtl = !dojo._isBodyLtr(),
665                         nodePos = pos || dojo.position(node),
666                         el = node.parentNode,
667                         isFixed = function(el){
668                                 return ((isIE <= 6 || (isIE && backCompat))? false : (dojo.style(el, 'position').toLowerCase() == "fixed"));
669                         };
670                 if(isFixed(node)){ return; } // nothing to do
671
672                 while(el){
673                         if(el == body){ el = scrollRoot; }
674                         var elPos = dojo.position(el),
675                                 fixedPos = isFixed(el);
676         
677                         if(el == scrollRoot){
678                                 elPos.w = rootWidth; elPos.h = rootHeight;
679                                 if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
680                                 if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
681                                 if(elPos.y < 0 || !isIE){ elPos.y = 0; }
682                         }else{
683                                 var pb = dojo._getPadBorderExtents(el);
684                                 elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
685                         }
686         
687                         if(el != scrollRoot){ // body, html sizes already have the scrollbar removed
688                                 var clientSize = el.clientWidth,
689                                         scrollBarSize = elPos.w - clientSize;
690                                 if(clientSize > 0 && scrollBarSize > 0){
691                                         elPos.w = clientSize;
692                                         if(isIE && rtl){ elPos.x += scrollBarSize; }
693                                 }
694                                 clientSize = el.clientHeight;
695                                 scrollBarSize = elPos.h - clientSize;
696                                 if(clientSize > 0 && scrollBarSize > 0){
697                                         elPos.h = clientSize;
698                                 }
699                         }
700                         if(fixedPos){ // bounded by viewport, not parents
701                                 if(elPos.y < 0){
702                                         elPos.h += elPos.y; elPos.y = 0;
703                                 }
704                                 if(elPos.x < 0){
705                                         elPos.w += elPos.x; elPos.x = 0;
706                                 }
707                                 if(elPos.y + elPos.h > rootHeight){
708                                         elPos.h = rootHeight - elPos.y;
709                                 }
710                                 if(elPos.x + elPos.w > rootWidth){
711                                         elPos.w = rootWidth - elPos.x;
712                                 }
713                         }
714                         // calculate overflow in all 4 directions
715                         var l = nodePos.x - elPos.x, // beyond left: < 0
716                                 t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
717                                 r = l + nodePos.w - elPos.w, // beyond right: > 0
718                                 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
719                         if(r * l > 0){
720                                 var s = Math[l < 0? "max" : "min"](l, r);
721                                 nodePos.x += el.scrollLeft;
722                                 el.scrollLeft += (isIE >= 8 && !backCompat && rtl)? -s : s;
723                                 nodePos.x -= el.scrollLeft;
724                         }
725                         if(bot * t > 0){
726                                 nodePos.y += el.scrollTop;
727                                 el.scrollTop += Math[t < 0? "max" : "min"](t, bot);
728                                 nodePos.y -= el.scrollTop;
729                         }
730                         el = (el != scrollRoot) && !fixedPos && el.parentNode;
731                 }       
732         }catch(error){
733                 console.error('scrollIntoView: ' + error);
734                 node.scrollIntoView(false);
735         }
736 };
737
738 }
739
740 if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
741 dojo._hasResource["dijit._base.manager"] = true;
742 dojo.provide("dijit._base.manager");
743
744 dojo.declare("dijit.WidgetSet", null, {
745         // summary:
746         //              A set of widgets indexed by id. A default instance of this class is
747         //              available as `dijit.registry`
748         //
749         // example:
750         //              Create a small list of widgets:
751         //              |       var ws = new dijit.WidgetSet();
752         //              |       ws.add(dijit.byId("one"));
753         //              |       ws.add(dijit.byId("two"));
754         //              |       // destroy both:
755         //              |       ws.forEach(function(w){ w.destroy(); });
756         //
757         // example:
758         //              Using dijit.registry:
759         //              |       dijit.registry.forEach(function(w){ /* do something */ });
760
761         constructor: function(){
762                 this._hash = {};
763                 this.length = 0;
764         },
765
766         add: function(/*dijit._Widget*/ widget){
767                 // summary:
768                 //              Add a widget to this list. If a duplicate ID is detected, a error is thrown.
769                 //
770                 // widget: dijit._Widget
771                 //              Any dijit._Widget subclass.
772                 if(this._hash[widget.id]){
773                         throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
774                 }
775                 this._hash[widget.id] = widget;
776                 this.length++;
777         },
778
779         remove: function(/*String*/ id){
780                 // summary:
781                 //              Remove a widget from this WidgetSet. Does not destroy the widget; simply
782                 //              removes the reference.
783                 if(this._hash[id]){
784                         delete this._hash[id];
785                         this.length--;
786                 }
787         },
788
789         forEach: function(/*Function*/ func, /* Object? */thisObj){
790                 // summary:
791                 //              Call specified function for each widget in this set.
792                 //
793                 // func:
794                 //              A callback function to run for each item. Is passed the widget, the index
795                 //              in the iteration, and the full hash, similar to `dojo.forEach`.
796                 //
797                 // thisObj:
798                 //              An optional scope parameter
799                 //
800                 // example:
801                 //              Using the default `dijit.registry` instance:
802                 //              |       dijit.registry.forEach(function(widget){
803                 //              |               console.log(widget.declaredClass);
804                 //              |       });
805                 //
806                 // returns:
807                 //              Returns self, in order to allow for further chaining.
808
809                 thisObj = thisObj || dojo.global;
810                 var i = 0, id;
811                 for(id in this._hash){
812                         func.call(thisObj, this._hash[id], i++, this._hash);
813                 }
814                 return this;    // dijit.WidgetSet
815         },
816
817         filter: function(/*Function*/ filter, /* Object? */thisObj){
818                 // summary:
819                 //              Filter down this WidgetSet to a smaller new WidgetSet
820                 //              Works the same as `dojo.filter` and `dojo.NodeList.filter`
821                 //
822                 // filter:
823                 //              Callback function to test truthiness. Is passed the widget
824                 //              reference and the pseudo-index in the object.
825                 //
826                 // thisObj: Object?
827                 //              Option scope to use for the filter function.
828                 //
829                 // example:
830                 //              Arbitrary: select the odd widgets in this list
831                 //              |       dijit.registry.filter(function(w, i){
832                 //              |               return i % 2 == 0;
833                 //              |       }).forEach(function(w){ /* odd ones */ });
834
835                 thisObj = thisObj || dojo.global;
836                 var res = new dijit.WidgetSet(), i = 0, id;
837                 for(id in this._hash){
838                         var w = this._hash[id];
839                         if(filter.call(thisObj, w, i++, this._hash)){
840                                 res.add(w);
841                         }
842                 }
843                 return res; // dijit.WidgetSet
844         },
845
846         byId: function(/*String*/ id){
847                 // summary:
848                 //              Find a widget in this list by it's id.
849                 // example:
850                 //              Test if an id is in a particular WidgetSet
851                 //              | var ws = new dijit.WidgetSet();
852                 //              | ws.add(dijit.byId("bar"));
853                 //              | var t = ws.byId("bar") // returns a widget
854                 //              | var x = ws.byId("foo"); // returns undefined
855
856                 return this._hash[id];  // dijit._Widget
857         },
858
859         byClass: function(/*String*/ cls){
860                 // summary:
861                 //              Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
862                 //
863                 // cls: String
864                 //              The Class to scan for. Full dot-notated string.
865                 //
866                 // example:
867                 //              Find all `dijit.TitlePane`s in a page:
868                 //              |       dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
869
870                 var res = new dijit.WidgetSet(), id, widget;
871                 for(id in this._hash){
872                         widget = this._hash[id];
873                         if(widget.declaredClass == cls){
874                                 res.add(widget);
875                         }
876                  }
877                  return res; // dijit.WidgetSet
878 },
879
880         toArray: function(){
881                 // summary:
882                 //              Convert this WidgetSet into a true Array
883                 //
884                 // example:
885                 //              Work with the widget .domNodes in a real Array
886                 //              |       dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
887
888                 var ar = [];
889                 for(var id in this._hash){
890                         ar.push(this._hash[id]);
891                 }
892                 return ar;      // dijit._Widget[]
893 },
894
895         map: function(/* Function */func, /* Object? */thisObj){
896                 // summary:
897                 //              Create a new Array from this WidgetSet, following the same rules as `dojo.map`
898                 // example:
899                 //              |       var nodes = dijit.registry.map(function(w){ return w.domNode; });
900                 //
901                 // returns:
902                 //              A new array of the returned values.
903                 return dojo.map(this.toArray(), func, thisObj); // Array
904         },
905
906         every: function(func, thisObj){
907                 // summary:
908                 //              A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
909                 //
910                 // func: Function
911                 //              A callback function run for every widget in this list. Exits loop
912                 //              when the first false return is encountered.
913                 //
914                 // thisObj: Object?
915                 //              Optional scope parameter to use for the callback
916
917                 thisObj = thisObj || dojo.global;
918                 var x = 0, i;
919                 for(i in this._hash){
920                         if(!func.call(thisObj, this._hash[i], x++, this._hash)){
921                                 return false; // Boolean
922                         }
923                 }
924                 return true; // Boolean
925         },
926
927         some: function(func, thisObj){
928                 // summary:
929                 //              A synthetic clone of `dojo.some` acting explictly on this WidgetSet
930                 //
931                 // func: Function
932                 //              A callback function run for every widget in this list. Exits loop
933                 //              when the first true return is encountered.
934                 //
935                 // thisObj: Object?
936                 //              Optional scope parameter to use for the callback
937
938                 thisObj = thisObj || dojo.global;
939                 var x = 0, i;
940                 for(i in this._hash){
941                         if(func.call(thisObj, this._hash[i], x++, this._hash)){
942                                 return true; // Boolean
943                         }
944                 }
945                 return false; // Boolean
946         }
947
948 });
949
950 (function(){
951
952         /*=====
953         dijit.registry = {
954                 // summary:
955                 //              A list of widgets on a page.
956                 // description:
957                 //              Is an instance of `dijit.WidgetSet`
958         };
959         =====*/
960         dijit.registry = new dijit.WidgetSet();
961
962         var hash = dijit.registry._hash,
963                 attr = dojo.attr,
964                 hasAttr = dojo.hasAttr,
965                 style = dojo.style;
966
967         dijit.byId = function(/*String|dijit._Widget*/ id){
968                 // summary:
969                 //              Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
970                 return typeof id == "string" ? hash[id] : id; // dijit._Widget
971         };
972
973         var _widgetTypeCtr = {};
974         dijit.getUniqueId = function(/*String*/widgetType){
975                 // summary:
976                 //              Generates a unique id for a given widgetType
977         
978                 var id;
979                 do{
980                         id = widgetType + "_" +
981                                 (widgetType in _widgetTypeCtr ?
982                                         ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
983                 }while(hash[id]);
984                 return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
985         };
986         
987         dijit.findWidgets = function(/*DomNode*/ root){
988                 // summary:
989                 //              Search subtree under root returning widgets found.
990                 //              Doesn't search for nested widgets (ie, widgets inside other widgets).
991         
992                 var outAry = [];
993         
994                 function getChildrenHelper(root){
995                         for(var node = root.firstChild; node; node = node.nextSibling){
996                                 if(node.nodeType == 1){
997                                         var widgetId = node.getAttribute("widgetId");
998                                         if(widgetId){
999                                                 outAry.push(hash[widgetId]);
1000                                         }else{
1001                                                 getChildrenHelper(node);
1002                                         }
1003                                 }
1004                         }
1005                 }
1006         
1007                 getChildrenHelper(root);
1008                 return outAry;
1009         };
1010         
1011         dijit._destroyAll = function(){
1012                 // summary:
1013                 //              Code to destroy all widgets and do other cleanup on page unload
1014         
1015                 // Clean up focus manager lingering references to widgets and nodes
1016                 dijit._curFocus = null;
1017                 dijit._prevFocus = null;
1018                 dijit._activeStack = [];
1019         
1020                 // Destroy all the widgets, top down
1021                 dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
1022                         // Avoid double destroy of widgets like Menu that are attached to <body>
1023                         // even though they are logically children of other widgets.
1024                         if(!widget._destroyed){
1025                                 if(widget.destroyRecursive){
1026                                         widget.destroyRecursive();
1027                                 }else if(widget.destroy){
1028                                         widget.destroy();
1029                                 }
1030                         }
1031                 });
1032         };
1033         
1034         if(dojo.isIE){
1035                 // Only run _destroyAll() for IE because we think it's only necessary in that case,
1036                 // and because it causes problems on FF.  See bug #3531 for details.
1037                 dojo.addOnWindowUnload(function(){
1038                         dijit._destroyAll();
1039                 });
1040         }
1041         
1042         dijit.byNode = function(/*DOMNode*/ node){
1043                 // summary:
1044                 //              Returns the widget corresponding to the given DOMNode
1045                 return hash[node.getAttribute("widgetId")]; // dijit._Widget
1046         };
1047         
1048         dijit.getEnclosingWidget = function(/*DOMNode*/ node){
1049                 // summary:
1050                 //              Returns the widget whose DOM tree contains the specified DOMNode, or null if
1051                 //              the node is not contained within the DOM tree of any widget
1052                 while(node){
1053                         var id = node.getAttribute && node.getAttribute("widgetId");
1054                         if(id){
1055                                 return hash[id];
1056                         }
1057                         node = node.parentNode;
1058                 }
1059                 return null;
1060         };
1061
1062         var shown = (dijit._isElementShown = function(/*Element*/ elem){
1063                 var s = style(elem);
1064                 return (s.visibility != "hidden")
1065                         && (s.visibility != "collapsed")
1066                         && (s.display != "none")
1067                         && (attr(elem, "type") != "hidden");
1068         });
1069         
1070         dijit.hasDefaultTabStop = function(/*Element*/ elem){
1071                 // summary:
1072                 //              Tests if element is tab-navigable even without an explicit tabIndex setting
1073         
1074                 // No explicit tabIndex setting, need to investigate node type
1075                 switch(elem.nodeName.toLowerCase()){
1076                         case "a":
1077                                 // An <a> w/out a tabindex is only navigable if it has an href
1078                                 return hasAttr(elem, "href");
1079                         case "area":
1080                         case "button":
1081                         case "input":
1082                         case "object":
1083                         case "select":
1084                         case "textarea":
1085                                 // These are navigable by default
1086                                 return true;
1087                         case "iframe":
1088                                 // If it's an editor <iframe> then it's tab navigable.
1089                                 //TODO: feature detect "designMode" in elem.contentDocument?
1090                                 if(dojo.isMoz){
1091                                         try{
1092                                                 return elem.contentDocument.designMode == "on";
1093                                         }catch(err){
1094                                                 return false;
1095                                         }
1096                                 }else if(dojo.isWebKit){
1097                                         var doc = elem.contentDocument,
1098                                                 body = doc && doc.body;
1099                                         return body && body.contentEditable == 'true';
1100                                 }else{
1101                                         // contentWindow.document isn't accessible within IE7/8
1102                                         // if the iframe.src points to a foreign url and this
1103                                         // page contains an element, that could get focus
1104                                         try{
1105                                                 doc = elem.contentWindow.document;
1106                                                 body = doc && doc.body;
1107                                                 return body && body.firstChild && body.firstChild.contentEditable == 'true';
1108                                         }catch(e){
1109                                                 return false;
1110                                         }
1111                                 }
1112                         default:
1113                                 return elem.contentEditable == 'true';
1114                 }
1115         };
1116         
1117         var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
1118                 // summary:
1119                 //              Tests if an element is tab-navigable
1120         
1121                 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
1122                 if(attr(elem, "disabled")){
1123                         return false;
1124                 }else if(hasAttr(elem, "tabIndex")){
1125                         // Explicit tab index setting
1126                         return attr(elem, "tabIndex") >= 0; // boolean
1127                 }else{
1128                         // No explicit tabIndex setting, so depends on node type
1129                         return dijit.hasDefaultTabStop(elem);
1130                 }
1131         });
1132
1133         dijit._getTabNavigable = function(/*DOMNode*/ root){
1134                 // summary:
1135                 //              Finds descendants of the specified root node.
1136                 //
1137                 // description:
1138                 //              Finds the following descendants of the specified root node:
1139                 //              * the first tab-navigable element in document order
1140                 //                without a tabIndex or with tabIndex="0"
1141                 //              * the last tab-navigable element in document order
1142                 //                without a tabIndex or with tabIndex="0"
1143                 //              * the first element in document order with the lowest
1144                 //                positive tabIndex value
1145                 //              * the last element in document order with the highest
1146                 //                positive tabIndex value
1147                 var first, last, lowest, lowestTabindex, highest, highestTabindex;
1148                 var walkTree = function(/*DOMNode*/parent){
1149                         dojo.query("> *", parent).forEach(function(child){
1150                                 // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
1151                                 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
1152                                 if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){
1153                                         return;
1154                                 }
1155
1156                                 if(isTabNavigable(child)){
1157                                         var tabindex = attr(child, "tabIndex");
1158                                         if(!hasAttr(child, "tabIndex") || tabindex == 0){
1159                                                 if(!first){ first = child; }
1160                                                 last = child;
1161                                         }else if(tabindex > 0){
1162                                                 if(!lowest || tabindex < lowestTabindex){
1163                                                         lowestTabindex = tabindex;
1164                                                         lowest = child;
1165                                                 }
1166                                                 if(!highest || tabindex >= highestTabindex){
1167                                                         highestTabindex = tabindex;
1168                                                         highest = child;
1169                                                 }
1170                                         }
1171                                 }
1172                                 if(child.nodeName.toUpperCase() != 'SELECT'){
1173                                         walkTree(child);
1174                                 }
1175                         });
1176                 };
1177                 if(shown(root)){ walkTree(root) }
1178                 return { first: first, last: last, lowest: lowest, highest: highest };
1179         }
1180         dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
1181                 // summary:
1182                 //              Finds the descendant of the specified root node
1183                 //              that is first in the tabbing order
1184                 var elems = dijit._getTabNavigable(dojo.byId(root));
1185                 return elems.lowest ? elems.lowest : elems.first; // DomNode
1186         };
1187         
1188         dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
1189                 // summary:
1190                 //              Finds the descendant of the specified root node
1191                 //              that is last in the tabbing order
1192                 var elems = dijit._getTabNavigable(dojo.byId(root));
1193                 return elems.last ? elems.last : elems.highest; // DomNode
1194         };
1195         
1196         /*=====
1197         dojo.mixin(dijit, {
1198                 // defaultDuration: Integer
1199                 //              The default animation speed (in ms) to use for all Dijit
1200                 //              transitional animations, unless otherwise specified
1201                 //              on a per-instance basis. Defaults to 200, overrided by
1202                 //              `djConfig.defaultDuration`
1203                 defaultDuration: 200
1204         });
1205         =====*/
1206         
1207         dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
1208
1209 })();
1210
1211 }
1212
1213 if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1214 dojo._hasResource["dijit._base.focus"] = true;
1215 dojo.provide("dijit._base.focus");
1216
1217
1218         // for dijit.isTabNavigable()
1219
1220 // summary:
1221 //              These functions are used to query or set the focus and selection.
1222 //
1223 //              Also, they trace when widgets become activated/deactivated,
1224 //              so that the widget can fire _onFocus/_onBlur events.
1225 //              "Active" here means something similar to "focused", but
1226 //              "focus" isn't quite the right word because we keep track of
1227 //              a whole stack of "active" widgets.  Example: ComboButton --> Menu -->
1228 //              MenuItem.  The onBlur event for ComboButton doesn't fire due to focusing
1229 //              on the Menu or a MenuItem, since they are considered part of the
1230 //              ComboButton widget.  It only happens when focus is shifted
1231 //              somewhere completely different.
1232
1233 dojo.mixin(dijit, {
1234         // _curFocus: DomNode
1235         //              Currently focused item on screen
1236         _curFocus: null,
1237
1238         // _prevFocus: DomNode
1239         //              Previously focused item on screen
1240         _prevFocus: null,
1241
1242         isCollapsed: function(){
1243                 // summary:
1244                 //              Returns true if there is no text selected
1245                 return dijit.getBookmark().isCollapsed;
1246         },
1247
1248         getBookmark: function(){
1249                 // summary:
1250                 //              Retrieves a bookmark that can be used with moveToBookmark to return to the same range
1251                 var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus;
1252
1253                 if(dojo.global.getSelection){
1254                         //W3C Range API for selections.
1255                         sel = dojo.global.getSelection();
1256                         if(sel){
1257                                 if(sel.isCollapsed){
1258                                         tg = cf? cf.tagName : "";
1259                                         if(tg){
1260                                                 //Create a fake rangelike item to restore selections.
1261                                                 tg = tg.toLowerCase();
1262                                                 if(tg == "textarea" ||
1263                                                                 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
1264                                                         sel = {
1265                                                                 start: cf.selectionStart,
1266                                                                 end: cf.selectionEnd,
1267                                                                 node: cf,
1268                                                                 pRange: true
1269                                                         };
1270                                                         return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
1271                                                 }
1272                                         }
1273                                         bm = {isCollapsed:true};
1274                                 }else{
1275                                         rg = sel.getRangeAt(0);
1276                                         bm = {isCollapsed: false, mark: rg.cloneRange()};
1277                                 }
1278                         }
1279                 }else if(sel){
1280                         // If the current focus was a input of some sort and no selection, don't bother saving
1281                         // a native bookmark.  This is because it causes issues with dialog/page selection restore.
1282                         // So, we need to create psuedo bookmarks to work with.
1283                         tg = cf ? cf.tagName : "";
1284                         tg = tg.toLowerCase();
1285                         if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
1286                                 if(sel.type && sel.type.toLowerCase() == "none"){
1287                                         return {
1288                                                 isCollapsed: true,
1289                                                 mark: null
1290                                         }
1291                                 }else{
1292                                         rg = sel.createRange();
1293                                         return {
1294                                                 isCollapsed: rg.text && rg.text.length?false:true,
1295                                                 mark: {
1296                                                         range: rg,
1297                                                         pRange: true
1298                                                 }
1299                                         };
1300                                 }
1301                         }
1302                         bm = {};
1303
1304                         //'IE' way for selections.
1305                         try{
1306                                 // createRange() throws exception when dojo in iframe
1307                                 //and nothing selected, see #9632
1308                                 rg = sel.createRange();
1309                                 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
1310                         }catch(e){
1311                                 bm.isCollapsed = true;
1312                                 return bm;
1313                         }
1314                         if(sel.type.toUpperCase() == 'CONTROL'){
1315                                 if(rg.length){
1316                                         bm.mark=[];
1317                                         var i=0,len=rg.length;
1318                                         while(i<len){
1319                                                 bm.mark.push(rg.item(i++));
1320                                         }
1321                                 }else{
1322                                         bm.isCollapsed = true;
1323                                         bm.mark = null;
1324                                 }
1325                         }else{
1326                                 bm.mark = rg.getBookmark();
1327                         }
1328                 }else{
1329                         console.warn("No idea how to store the current selection for this browser!");
1330                 }
1331                 return bm; // Object
1332         },
1333
1334         moveToBookmark: function(/*Object*/bookmark){
1335                 // summary:
1336                 //              Moves current selection to a bookmark
1337                 // bookmark:
1338                 //              This should be a returned object from dijit.getBookmark()
1339
1340                 var _doc = dojo.doc,
1341                         mark = bookmark.mark;
1342                 if(mark){
1343                         if(dojo.global.getSelection){
1344                                 //W3C Rangi API (FF, WebKit, Opera, etc)
1345                                 var sel = dojo.global.getSelection();
1346                                 if(sel && sel.removeAllRanges){
1347                                         if(mark.pRange){
1348                                                 var r = mark;
1349                                                 var n = r.node;
1350                                                 n.selectionStart = r.start;
1351                                                 n.selectionEnd = r.end;
1352                                         }else{
1353                                                 sel.removeAllRanges();
1354                                                 sel.addRange(mark);
1355                                         }
1356                                 }else{
1357                                         console.warn("No idea how to restore selection for this browser!");
1358                                 }
1359                         }else if(_doc.selection && mark){
1360                                 //'IE' way.
1361                                 var rg;
1362                                 if(mark.pRange){
1363                                         rg = mark.range;
1364                                 }else if(dojo.isArray(mark)){
1365                                         rg = _doc.body.createControlRange();
1366                                         //rg.addElement does not have call/apply method, so can not call it directly
1367                                         //rg is not available in "range.addElement(item)", so can't use that either
1368                                         dojo.forEach(mark, function(n){
1369                                                 rg.addElement(n);
1370                                         });
1371                                 }else{
1372                                         rg = _doc.body.createTextRange();
1373                                         rg.moveToBookmark(mark);
1374                                 }
1375                                 rg.select();
1376                         }
1377                 }
1378         },
1379
1380         getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
1381                 // summary:
1382                 //              Called as getFocus(), this returns an Object showing the current focus
1383                 //              and selected text.
1384                 //
1385                 //              Called as getFocus(widget), where widget is a (widget representing) a button
1386                 //              that was just pressed, it returns where focus was before that button
1387                 //              was pressed.   (Pressing the button may have either shifted focus to the button,
1388                 //              or removed focus altogether.)   In this case the selected text is not returned,
1389                 //              since it can't be accurately determined.
1390                 //
1391                 // menu: dijit._Widget or {domNode: DomNode} structure
1392                 //              The button that was just pressed.  If focus has disappeared or moved
1393                 //              to this button, returns the previous focus.  In this case the bookmark
1394                 //              information is already lost, and null is returned.
1395                 //
1396                 // openedForWindow:
1397                 //              iframe in which menu was opened
1398                 //
1399                 // returns:
1400                 //              A handle to restore focus/selection, to be passed to `dijit.focus`
1401                 var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus;
1402                 return {
1403                         node: node,
1404                         bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark),
1405                         openedForWindow: openedForWindow
1406                 }; // Object
1407         },
1408
1409         focus: function(/*Object || DomNode */ handle){
1410                 // summary:
1411                 //              Sets the focused node and the selection according to argument.
1412                 //              To set focus to an iframe's content, pass in the iframe itself.
1413                 // handle:
1414                 //              object returned by get(), or a DomNode
1415
1416                 if(!handle){ return; }
1417
1418                 var node = "node" in handle ? handle.node : handle,             // because handle is either DomNode or a composite object
1419                         bookmark = handle.bookmark,
1420                         openedForWindow = handle.openedForWindow,
1421                         collapsed = bookmark ? bookmark.isCollapsed : false;
1422
1423                 // Set the focus
1424                 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
1425                 // but we need to set focus to iframe.contentWindow
1426                 if(node){
1427                         var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
1428                         if(focusNode && focusNode.focus){
1429                                 try{
1430                                         // Gecko throws sometimes if setting focus is impossible,
1431                                         // node not displayed or something like that
1432                                         focusNode.focus();
1433                                 }catch(e){/*quiet*/}
1434                         }
1435                         dijit._onFocusNode(node);
1436                 }
1437
1438                 // set the selection
1439                 // do not need to restore if current selection is not empty
1440                 // (use keyboard to select a menu item) or if previous selection was collapsed
1441                 // as it may cause focus shift (Esp in IE).
1442                 if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){
1443                         if(openedForWindow){
1444                                 openedForWindow.focus();
1445                         }
1446                         try{
1447                                 dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]);
1448                         }catch(e2){
1449                                 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
1450                         }
1451                 }
1452         },
1453
1454         // _activeStack: dijit._Widget[]
1455         //              List of currently active widgets (focused widget and it's ancestors)
1456         _activeStack: [],
1457
1458         registerIframe: function(/*DomNode*/ iframe){
1459                 // summary:
1460                 //              Registers listeners on the specified iframe so that any click
1461                 //              or focus event on that iframe (or anything in it) is reported
1462                 //              as a focus/click event on the <iframe> itself.
1463                 // description:
1464                 //              Currently only used by editor.
1465                 // returns:
1466                 //              Handle to pass to unregisterIframe()
1467                 return dijit.registerWin(iframe.contentWindow, iframe);
1468         },
1469
1470         unregisterIframe: function(/*Object*/ handle){
1471                 // summary:
1472                 //              Unregisters listeners on the specified iframe created by registerIframe.
1473                 //              After calling be sure to delete or null out the handle itself.
1474                 // handle:
1475                 //              Handle returned by registerIframe()
1476
1477                 dijit.unregisterWin(handle);
1478         },
1479
1480         registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
1481                 // summary:
1482                 //              Registers listeners on the specified window (either the main
1483                 //              window or an iframe's window) to detect when the user has clicked somewhere
1484                 //              or focused somewhere.
1485                 // description:
1486                 //              Users should call registerIframe() instead of this method.
1487                 // targetWindow:
1488                 //              If specified this is the window associated with the iframe,
1489                 //              i.e. iframe.contentWindow.
1490                 // effectiveNode:
1491                 //              If specified, report any focus events inside targetWindow as
1492                 //              an event on effectiveNode, rather than on evt.target.
1493                 // returns:
1494                 //              Handle to pass to unregisterWin()
1495
1496                 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
1497
1498                 var mousedownListener = function(evt){
1499                         dijit._justMouseDowned = true;
1500                         setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
1501                         
1502                         // workaround weird IE bug where the click is on an orphaned node
1503                         // (first time clicking a Select/DropDownButton inside a TooltipDialog)
1504                         if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){
1505                                 return;
1506                         }
1507
1508                         dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
1509                 };
1510                 //dojo.connect(targetWindow, "onscroll", ???);
1511
1512                 // Listen for blur and focus events on targetWindow's document.
1513                 // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
1514                 // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
1515                 // fire.
1516                 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
1517                 // (at least for FF) the focus event doesn't fire on <html> or <body>.
1518                 var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document;
1519                 if(doc){
1520                         if(dojo.isIE){
1521                                 doc.attachEvent('onmousedown', mousedownListener);
1522                                 var activateListener = function(evt){
1523                                         // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
1524                                         // Should consider those more like a mouse-click than a focus....
1525                                         if(evt.srcElement.tagName.toLowerCase() != "#document" &&
1526                                                 dijit.isTabNavigable(evt.srcElement)){
1527                                                 dijit._onFocusNode(effectiveNode || evt.srcElement);
1528                                         }else{
1529                                                 dijit._onTouchNode(effectiveNode || evt.srcElement);
1530                                         }
1531                                 };
1532                                 doc.attachEvent('onactivate', activateListener);
1533                                 var deactivateListener =  function(evt){
1534                                         dijit._onBlurNode(effectiveNode || evt.srcElement);
1535                                 };
1536                                 doc.attachEvent('ondeactivate', deactivateListener);
1537
1538                                 return function(){
1539                                         doc.detachEvent('onmousedown', mousedownListener);
1540                                         doc.detachEvent('onactivate', activateListener);
1541                                         doc.detachEvent('ondeactivate', deactivateListener);
1542                                         doc = null;     // prevent memory leak (apparent circular reference via closure)
1543                                 };
1544                         }else{
1545                                 doc.addEventListener('mousedown', mousedownListener, true);
1546                                 var focusListener = function(evt){
1547                                         dijit._onFocusNode(effectiveNode || evt.target);
1548                                 };
1549                                 doc.addEventListener('focus', focusListener, true);
1550                                 var blurListener = function(evt){
1551                                         dijit._onBlurNode(effectiveNode || evt.target);
1552                                 };
1553                                 doc.addEventListener('blur', blurListener, true);
1554
1555                                 return function(){
1556                                         doc.removeEventListener('mousedown', mousedownListener, true);
1557                                         doc.removeEventListener('focus', focusListener, true);
1558                                         doc.removeEventListener('blur', blurListener, true);
1559                                         doc = null;     // prevent memory leak (apparent circular reference via closure)
1560                                 };
1561                         }
1562                 }
1563         },
1564
1565         unregisterWin: function(/*Handle*/ handle){
1566                 // summary:
1567                 //              Unregisters listeners on the specified window (either the main
1568                 //              window or an iframe's window) according to handle returned from registerWin().
1569                 //              After calling be sure to delete or null out the handle itself.
1570
1571                 // Currently our handle is actually a function
1572                 handle && handle();
1573         },
1574
1575         _onBlurNode: function(/*DomNode*/ node){
1576                 // summary:
1577                 //              Called when focus leaves a node.
1578                 //              Usually ignored, _unless_ it *isn't* follwed by touching another node,
1579                 //              which indicates that we tabbed off the last field on the page,
1580                 //              in which case every widget is marked inactive
1581                 dijit._prevFocus = dijit._curFocus;
1582                 dijit._curFocus = null;
1583
1584                 if(dijit._justMouseDowned){
1585                         // the mouse down caused a new widget to be marked as active; this blur event
1586                         // is coming late, so ignore it.
1587                         return;
1588                 }
1589
1590                 // if the blur event isn't followed by a focus event then mark all widgets as inactive.
1591                 if(dijit._clearActiveWidgetsTimer){
1592                         clearTimeout(dijit._clearActiveWidgetsTimer);
1593                 }
1594                 dijit._clearActiveWidgetsTimer = setTimeout(function(){
1595                         delete dijit._clearActiveWidgetsTimer;
1596                         dijit._setStack([]);
1597                         dijit._prevFocus = null;
1598                 }, 100);
1599         },
1600
1601         _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
1602                 // summary:
1603                 //              Callback when node is focused or mouse-downed
1604                 // node:
1605                 //              The node that was touched.
1606                 // by:
1607                 //              "mouse" if the focus/touch was caused by a mouse down event
1608
1609                 // ignore the recent blurNode event
1610                 if(dijit._clearActiveWidgetsTimer){
1611                         clearTimeout(dijit._clearActiveWidgetsTimer);
1612                         delete dijit._clearActiveWidgetsTimer;
1613                 }
1614
1615                 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
1616                 var newStack=[];
1617                 try{
1618                         while(node){
1619                                 var popupParent = dojo.attr(node, "dijitPopupParent");
1620                                 if(popupParent){
1621                                         node=dijit.byId(popupParent).domNode;
1622                                 }else if(node.tagName && node.tagName.toLowerCase() == "body"){
1623                                         // is this the root of the document or just the root of an iframe?
1624                                         if(node === dojo.body()){
1625                                                 // node is the root of the main document
1626                                                 break;
1627                                         }
1628                                         // otherwise, find the iframe this node refers to (can't access it via parentNode,
1629                                         // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
1630                                         node=dojo.window.get(node.ownerDocument).frameElement;
1631                                 }else{
1632                                         // if this node is the root node of a widget, then add widget id to stack,
1633                                         // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
1634                                         // to support MenuItem)
1635                                         var id = node.getAttribute && node.getAttribute("widgetId"),
1636                                                 widget = id && dijit.byId(id);
1637                                         if(widget && !(by == "mouse" && widget.get("disabled"))){
1638                                                 newStack.unshift(id);
1639                                         }
1640                                         node=node.parentNode;
1641                                 }
1642                         }
1643                 }catch(e){ /* squelch */ }
1644
1645                 dijit._setStack(newStack, by);
1646         },
1647
1648         _onFocusNode: function(/*DomNode*/ node){
1649                 // summary:
1650                 //              Callback when node is focused
1651
1652                 if(!node){
1653                         return;
1654                 }
1655
1656                 if(node.nodeType == 9){
1657                         // Ignore focus events on the document itself.  This is here so that
1658                         // (for example) clicking the up/down arrows of a spinner
1659                         // (which don't get focus) won't cause that widget to blur. (FF issue)
1660                         return;
1661                 }
1662
1663                 dijit._onTouchNode(node);
1664
1665                 if(node == dijit._curFocus){ return; }
1666                 if(dijit._curFocus){
1667                         dijit._prevFocus = dijit._curFocus;
1668                 }
1669                 dijit._curFocus = node;
1670                 dojo.publish("focusNode", [node]);
1671         },
1672
1673         _setStack: function(/*String[]*/ newStack, /*String*/ by){
1674                 // summary:
1675                 //              The stack of active widgets has changed.  Send out appropriate events and records new stack.
1676                 // newStack:
1677                 //              array of widget id's, starting from the top (outermost) widget
1678                 // by:
1679                 //              "mouse" if the focus/touch was caused by a mouse down event
1680
1681                 var oldStack = dijit._activeStack;
1682                 dijit._activeStack = newStack;
1683
1684                 // compare old stack to new stack to see how many elements they have in common
1685                 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
1686                         if(oldStack[nCommon] != newStack[nCommon]){
1687                                 break;
1688                         }
1689                 }
1690
1691                 var widget;
1692                 // for all elements that have gone out of focus, send blur event
1693                 for(var i=oldStack.length-1; i>=nCommon; i--){
1694                         widget = dijit.byId(oldStack[i]);
1695                         if(widget){
1696                                 widget._focused = false;
1697                                 widget._hasBeenBlurred = true;
1698                                 if(widget._onBlur){
1699                                         widget._onBlur(by);
1700                                 }
1701                                 dojo.publish("widgetBlur", [widget, by]);
1702                         }
1703                 }
1704
1705                 // for all element that have come into focus, send focus event
1706                 for(i=nCommon; i<newStack.length; i++){
1707                         widget = dijit.byId(newStack[i]);
1708                         if(widget){
1709                                 widget._focused = true;
1710                                 if(widget._onFocus){
1711                                         widget._onFocus(by);
1712                                 }
1713                                 dojo.publish("widgetFocus", [widget, by]);
1714                         }
1715                 }
1716         }
1717 });
1718
1719 // register top window and all the iframes it contains
1720 dojo.addOnLoad(function(){
1721         var handle = dijit.registerWin(window);
1722         if(dojo.isIE){
1723                 dojo.addOnWindowUnload(function(){
1724                         dijit.unregisterWin(handle);
1725                         handle = null;
1726                 })
1727         }
1728 });
1729
1730 }
1731
1732 if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1733 dojo._hasResource["dojo.AdapterRegistry"] = true;
1734 dojo.provide("dojo.AdapterRegistry");
1735
1736 dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
1737         //      summary:
1738         //              A registry to make contextual calling/searching easier.
1739         //      description:
1740         //              Objects of this class keep list of arrays in the form [name, check,
1741         //              wrap, directReturn] that are used to determine what the contextual
1742         //              result of a set of checked arguments is. All check/wrap functions
1743         //              in this registry should be of the same arity.
1744         //      example:
1745         //      |       // create a new registry
1746         //      |       var reg = new dojo.AdapterRegistry();
1747         //      |       reg.register("handleString",
1748         //      |               dojo.isString,
1749         //      |               function(str){
1750         //      |                       // do something with the string here
1751         //      |               }
1752         //      |       );
1753         //      |       reg.register("handleArr",
1754         //      |               dojo.isArray,
1755         //      |               function(arr){
1756         //      |                       // do something with the array here
1757         //      |               }
1758         //      |       );
1759         //      |
1760         //      |       // now we can pass reg.match() *either* an array or a string and
1761         //      |       // the value we pass will get handled by the right function
1762         //      |       reg.match("someValue"); // will call the first function
1763         //      |       reg.match(["someValue"]); // will call the second
1764
1765         this.pairs = [];
1766         this.returnWrappers = returnWrappers || false; // Boolean
1767 }
1768
1769 dojo.extend(dojo.AdapterRegistry, {
1770         register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
1771                 //      summary: 
1772                 //              register a check function to determine if the wrap function or
1773                 //              object gets selected
1774                 //      name:
1775                 //              a way to identify this matcher.
1776                 //      check:
1777                 //              a function that arguments are passed to from the adapter's
1778                 //              match() function.  The check function should return true if the
1779                 //              given arguments are appropriate for the wrap function.
1780                 //      directReturn:
1781                 //              If directReturn is true, the value passed in for wrap will be
1782                 //              returned instead of being called. Alternately, the
1783                 //              AdapterRegistry can be set globally to "return not call" using
1784                 //              the returnWrappers property. Either way, this behavior allows
1785                 //              the registry to act as a "search" function instead of a
1786                 //              function interception library.
1787                 //      override:
1788                 //              If override is given and true, the check function will be given
1789                 //              highest priority. Otherwise, it will be the lowest priority
1790                 //              adapter.
1791                 this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
1792         },
1793
1794         match: function(/* ... */){
1795                 // summary:
1796                 //              Find an adapter for the given arguments. If no suitable adapter
1797                 //              is found, throws an exception. match() accepts any number of
1798                 //              arguments, all of which are passed to all matching functions
1799                 //              from the registered pairs.
1800                 for(var i = 0; i < this.pairs.length; i++){
1801                         var pair = this.pairs[i];
1802                         if(pair[1].apply(this, arguments)){
1803                                 if((pair[3])||(this.returnWrappers)){
1804                                         return pair[2];
1805                                 }else{
1806                                         return pair[2].apply(this, arguments);
1807                                 }
1808                         }
1809                 }
1810                 throw new Error("No match found");
1811         },
1812
1813         unregister: function(name){
1814                 // summary: Remove a named adapter from the registry
1815
1816                 // FIXME: this is kind of a dumb way to handle this. On a large
1817                 // registry this will be slow-ish and we can use the name as a lookup
1818                 // should we choose to trade memory for speed.
1819                 for(var i = 0; i < this.pairs.length; i++){
1820                         var pair = this.pairs[i];
1821                         if(pair[0] == name){
1822                                 this.pairs.splice(i, 1);
1823                                 return true;
1824                         }
1825                 }
1826                 return false;
1827         }
1828 });
1829
1830 }
1831
1832 if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1833 dojo._hasResource["dijit._base.place"] = true;
1834 dojo.provide("dijit._base.place");
1835
1836
1837
1838
1839
1840 dijit.getViewport = function(){
1841         // summary:
1842         //              Returns the dimensions and scroll position of the viewable area of a browser window
1843
1844         return dojo.window.getBox();
1845 };
1846
1847 /*=====
1848 dijit.__Position = function(){
1849         // x: Integer
1850         //              horizontal coordinate in pixels, relative to document body
1851         // y: Integer
1852         //              vertical coordinate in pixels, relative to document body
1853
1854         thix.x = x;
1855         this.y = y;
1856 }
1857 =====*/
1858
1859
1860 dijit.placeOnScreen = function(
1861         /* DomNode */                   node,
1862         /* dijit.__Position */  pos,
1863         /* String[] */                  corners,
1864         /* dijit.__Position? */ padding){
1865         // summary:
1866         //              Positions one of the node's corners at specified position
1867         //              such that node is fully visible in viewport.
1868         // description:
1869         //              NOTE: node is assumed to be absolutely or relatively positioned.
1870         //      pos:
1871         //              Object like {x: 10, y: 20}
1872         //      corners:
1873         //              Array of Strings representing order to try corners in, like ["TR", "BL"].
1874         //              Possible values are:
1875         //                      * "BL" - bottom left
1876         //                      * "BR" - bottom right
1877         //                      * "TL" - top left
1878         //                      * "TR" - top right
1879         //      padding:
1880         //              set padding to put some buffer around the element you want to position.
1881         // example:
1882         //              Try to place node's top right corner at (10,20).
1883         //              If that makes node go (partially) off screen, then try placing
1884         //              bottom left corner at (10,20).
1885         //      |       placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
1886
1887         var choices = dojo.map(corners, function(corner){
1888                 var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
1889                 if(padding){
1890                         c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
1891                         c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
1892                 }
1893                 return c;
1894         });
1895
1896         return dijit._place(node, choices);
1897 }
1898
1899 dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){
1900         // summary:
1901         //              Given a list of spots to put node, put it at the first spot where it fits,
1902         //              of if it doesn't fit anywhere then the place with the least overflow
1903         // choices: Array
1904         //              Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1905         //              Above example says to put the top-left corner of the node at (10,20)
1906         // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1907         //              for things like tooltip, they are displayed differently (and have different dimensions)
1908         //              based on their orientation relative to the parent.   This adjusts the popup based on orientation.
1909
1910         // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1911         // viewport over document
1912         var view = dojo.window.getBox();
1913
1914         // This won't work if the node is inside a <div style="position: relative">,
1915         // so reattach it to dojo.doc.body.   (Otherwise, the positioning will be wrong
1916         // and also it might get cutoff)
1917         if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
1918                 dojo.body().appendChild(node);
1919         }
1920
1921         var best = null;
1922         dojo.some(choices, function(choice){
1923                 var corner = choice.corner;
1924                 var pos = choice.pos;
1925
1926                 // configure node to be displayed in given position relative to button
1927                 // (need to do this in order to get an accurate size for the node, because
1928                 // a tooltips size changes based on position, due to triangle)
1929                 if(layoutNode){
1930                         layoutNode(node, choice.aroundCorner, corner);
1931                 }
1932
1933                 // get node's size
1934                 var style = node.style;
1935                 var oldDisplay = style.display;
1936                 var oldVis = style.visibility;
1937                 style.visibility = "hidden";
1938                 style.display = "";
1939                 var mb = dojo.marginBox(node);
1940                 style.display = oldDisplay;
1941                 style.visibility = oldVis;
1942
1943                 // coordinates and size of node with specified corner placed at pos,
1944                 // and clipped by viewport
1945                 var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)),
1946                         startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)),
1947                         endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x),
1948                         endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y),
1949                         width = endX - startX,
1950                         height = endY - startY,
1951                         overflow = (mb.w - width) + (mb.h - height);
1952
1953                 if(best == null || overflow < best.overflow){
1954                         best = {
1955                                 corner: corner,
1956                                 aroundCorner: choice.aroundCorner,
1957                                 x: startX,
1958                                 y: startY,
1959                                 w: width,
1960                                 h: height,
1961                                 overflow: overflow
1962                         };
1963                 }
1964                 return !overflow;
1965         });
1966
1967         node.style.left = best.x + "px";
1968         node.style.top = best.y + "px";
1969         if(best.overflow && layoutNode){
1970                 layoutNode(node, best.aroundCorner, best.corner);
1971         }
1972         return best;
1973 }
1974
1975 dijit.placeOnScreenAroundNode = function(
1976         /* DomNode */           node,
1977         /* DomNode */           aroundNode,
1978         /* Object */            aroundCorners,
1979         /* Function? */         layoutNode){
1980
1981         // summary:
1982         //              Position node adjacent or kitty-corner to aroundNode
1983         //              such that it's fully visible in viewport.
1984         //
1985         // description:
1986         //              Place node such that corner of node touches a corner of
1987         //              aroundNode, and that node is fully visible.
1988         //
1989         // aroundCorners:
1990         //              Ordered list of pairs of corners to try matching up.
1991         //              Each pair of corners is represented as a key/value in the hash,
1992         //              where the key corresponds to the aroundNode's corner, and
1993         //              the value corresponds to the node's corner:
1994         //
1995         //      |       { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...}
1996         //
1997         //              The following strings are used to represent the four corners:
1998         //                      * "BL" - bottom left
1999         //                      * "BR" - bottom right
2000         //                      * "TL" - top left
2001         //                      * "TR" - top right
2002         //
2003         // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
2004         //              For things like tooltip, they are displayed differently (and have different dimensions)
2005         //              based on their orientation relative to the parent.   This adjusts the popup based on orientation.
2006         //
2007         // example:
2008         //      |       dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
2009         //              This will try to position node such that node's top-left corner is at the same position
2010         //              as the bottom left corner of the aroundNode (ie, put node below
2011         //              aroundNode, with left edges aligned).  If that fails it will try to put
2012         //              the bottom-right corner of node where the top right corner of aroundNode is
2013         //              (ie, put node above aroundNode, with right edges aligned)
2014         //
2015
2016         // get coordinates of aroundNode
2017         aroundNode = dojo.byId(aroundNode);
2018         var oldDisplay = aroundNode.style.display;
2019         aroundNode.style.display="";
2020         // #3172: use the slightly tighter border box instead of marginBox
2021         var aroundNodePos = dojo.position(aroundNode, true);
2022         aroundNode.style.display=oldDisplay;
2023
2024         // place the node around the calculated rectangle
2025         return dijit._placeOnScreenAroundRect(node,
2026                 aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h,     // rectangle
2027                 aroundCorners, layoutNode);
2028 };
2029
2030 /*=====
2031 dijit.__Rectangle = function(){
2032         // x: Integer
2033         //              horizontal offset in pixels, relative to document body
2034         // y: Integer
2035         //              vertical offset in pixels, relative to document body
2036         // width: Integer
2037         //              width in pixels
2038         // height: Integer
2039         //              height in pixels
2040
2041         this.x = x;
2042         this.y = y;
2043         this.width = width;
2044         this.height = height;
2045 }
2046 =====*/
2047
2048
2049 dijit.placeOnScreenAroundRectangle = function(
2050         /* DomNode */                   node,
2051         /* dijit.__Rectangle */ aroundRect,
2052         /* Object */                    aroundCorners,
2053         /* Function */                  layoutNode){
2054
2055         // summary:
2056         //              Like dijit.placeOnScreenAroundNode(), except that the "around"
2057         //              parameter is an arbitrary rectangle on the screen (x, y, width, height)
2058         //              instead of a dom node.
2059
2060         return dijit._placeOnScreenAroundRect(node,
2061                 aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height,        // rectangle
2062                 aroundCorners, layoutNode);
2063 };
2064
2065 dijit._placeOnScreenAroundRect = function(
2066         /* DomNode */           node,
2067         /* Number */            x,
2068         /* Number */            y,
2069         /* Number */            width,
2070         /* Number */            height,
2071         /* Object */            aroundCorners,
2072         /* Function */          layoutNode){
2073
2074         // summary:
2075         //              Like dijit.placeOnScreenAroundNode(), except it accepts coordinates
2076         //              of a rectangle to place node adjacent to.
2077
2078         // TODO: combine with placeOnScreenAroundRectangle()
2079
2080         // Generate list of possible positions for node
2081         var choices = [];
2082         for(var nodeCorner in aroundCorners){
2083                 choices.push( {
2084                         aroundCorner: nodeCorner,
2085                         corner: aroundCorners[nodeCorner],
2086                         pos: {
2087                                 x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width),
2088                                 y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height)
2089                         }
2090                 });
2091         }
2092
2093         return dijit._place(node, choices, layoutNode);
2094 };
2095
2096 dijit.placementRegistry= new dojo.AdapterRegistry();
2097 dijit.placementRegistry.register("node",
2098         function(n, x){
2099                 return typeof x == "object" &&
2100                         typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined";
2101         },
2102         dijit.placeOnScreenAroundNode);
2103 dijit.placementRegistry.register("rect",
2104         function(n, x){
2105                 return typeof x == "object" &&
2106                         "x" in x && "y" in x && "width" in x && "height" in x;
2107         },
2108         dijit.placeOnScreenAroundRectangle);
2109
2110 dijit.placeOnScreenAroundElement = function(
2111         /* DomNode */           node,
2112         /* Object */            aroundElement,
2113         /* Object */            aroundCorners,
2114         /* Function */          layoutNode){
2115
2116         // summary:
2117         //              Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
2118         //              for the "around" argument and finds a proper processor to place a node.
2119
2120         return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments);
2121 };
2122
2123 dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
2124         // summary:
2125         //              Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
2126         //
2127         // position: String[]
2128         //              This variable controls the position of the drop down.
2129         //              It's an array of strings with the following values:
2130         //
2131         //                      * before: places drop down to the left of the target node/widget, or to the right in
2132         //                        the case of RTL scripts like Hebrew and Arabic
2133         //                      * after: places drop down to the right of the target node/widget, or to the left in
2134         //                        the case of RTL scripts like Hebrew and Arabic
2135         //                      * above: drop down goes above target node
2136         //                      * below: drop down goes below target node
2137         //
2138         //              The list is positions is tried, in order, until a position is found where the drop down fits
2139         //              within the viewport.
2140         //
2141         // leftToRight: Boolean
2142         //              Whether the popup will be displaying in leftToRight mode.
2143         //
2144         var align = {};
2145         dojo.forEach(position, function(pos){
2146                 switch(pos){
2147                         case "after":
2148                                 align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
2149                                 break;
2150                         case "before":
2151                                 align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
2152                                 break;
2153                         case "below":
2154                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
2155                                 align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR";
2156                                 align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL";
2157                                 break;
2158                         case "above":
2159                         default:
2160                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
2161                                 align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR";
2162                                 align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL";
2163                                 break;
2164                 }
2165         });
2166         return align;
2167 };
2168
2169 }
2170
2171 if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2172 dojo._hasResource["dijit._base.window"] = true;
2173 dojo.provide("dijit._base.window");
2174
2175
2176
2177 dijit.getDocumentWindow = function(doc){
2178         return dojo.window.get(doc);
2179 };
2180
2181 }
2182
2183 if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2184 dojo._hasResource["dijit._base.popup"] = true;
2185 dojo.provide("dijit._base.popup");
2186
2187
2188
2189
2190
2191 /*=====
2192 dijit.popup.__OpenArgs = function(){
2193         // popup: Widget
2194         //              widget to display
2195         // parent: Widget
2196         //              the button etc. that is displaying this popup
2197         // around: DomNode
2198         //              DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
2199         // x: Integer
2200         //              Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
2201         // y: Integer
2202         //              Absolute vertical position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
2203         // orient: Object|String
2204         //              When the around parameter is specified, orient should be an
2205         //              ordered list of tuples of the form (around-node-corner, popup-node-corner).
2206         //              dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
2207         //              until the popup appears fully within the viewport.
2208         //
2209         //              The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
2210         //                      1. (BL, TL)
2211         //                      2. (TL, BL)
2212         //              where BL means "bottom left" and "TL" means "top left".
2213         //              So by default, it first tries putting the popup below the around node, left-aligning them,
2214         //              and then tries to put it above the around node, still left-aligning them.   Note that the
2215         //              default is horizontally reversed when in RTL mode.
2216         //
2217         //              When an (x,y) position is specified rather than an around node, orient is either
2218         //              "R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
2219         //              specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
2220         //              fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
2221         //              and the top-right corner.
2222         // onCancel: Function
2223         //              callback when user has canceled the popup by
2224         //                      1. hitting ESC or
2225         //                      2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
2226         //                         i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
2227         // onClose: Function
2228         //              callback whenever this popup is closed
2229         // onExecute: Function
2230         //              callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
2231         // padding: dijit.__Position
2232         //              adding a buffer around the opening position. This is only useful when around is not set.
2233         this.popup = popup;
2234         this.parent = parent;
2235         this.around = around;
2236         this.x = x;
2237         this.y = y;
2238         this.orient = orient;
2239         this.onCancel = onCancel;
2240         this.onClose = onClose;
2241         this.onExecute = onExecute;
2242         this.padding = padding;
2243 }
2244 =====*/
2245
2246 dijit.popup = {
2247         // summary:
2248         //              This singleton is used to show/hide widgets as popups.
2249
2250         // _stack: dijit._Widget[]
2251         //              Stack of currently popped up widgets.
2252         //              (someone opened _stack[0], and then it opened _stack[1], etc.)
2253         _stack: [],
2254         
2255         // _beginZIndex: Number
2256         //              Z-index of the first popup.   (If first popup opens other
2257         //              popups they get a higher z-index.)
2258         _beginZIndex: 1000,
2259
2260         _idGen: 1,
2261
2262         moveOffScreen: function(/*DomNode*/ node){
2263                 // summary:
2264                 //              Initialization for nodes that will be used as popups
2265                 //
2266                 // description:
2267                 //              Puts node inside a wrapper <div>, and
2268                 //              positions wrapper div off screen, but not display:none, so that
2269                 //              the widget doesn't appear in the page flow and/or cause a blank
2270                 //              area at the bottom of the viewport (making scrollbar longer), but
2271                 //              initialization of contained widgets works correctly
2272
2273                 var wrapper = node.parentNode;
2274
2275                 // Create a wrapper widget for when this node (in the future) will be used as a popup.
2276                 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
2277                 // to go wonky, see tests/robot/Toolbar.html to reproduce
2278                 if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){
2279                         wrapper = dojo.create("div",{
2280                                 "class":"dijitPopup",
2281                                 style:{
2282                                         visibility:"hidden",
2283                                         top: "-9999px"
2284                                 }
2285                         }, dojo.body());
2286                         dijit.setWaiRole(wrapper, "presentation");
2287                         wrapper.appendChild(node);
2288                 }
2289
2290
2291                 var s = node.style;
2292                 s.display = "";
2293                 s.visibility = "";
2294                 s.position = "";
2295                 s.top = "0px";
2296
2297                 dojo.style(wrapper, {
2298                         visibility: "hidden",
2299                         // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
2300                         top: "-9999px"
2301                 });
2302         },
2303
2304         getTopPopup: function(){
2305                 // summary:
2306                 //              Compute the closest ancestor popup that's *not* a child of another popup.
2307                 //              Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
2308                 var stack = this._stack;
2309                 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
2310                         /* do nothing, just trying to get right value for pi */
2311                 }
2312                 return stack[pi];
2313         },
2314
2315         open: function(/*dijit.popup.__OpenArgs*/ args){
2316                 // summary:
2317                 //              Popup the widget at the specified position
2318                 //
2319                 // example:
2320                 //              opening at the mouse position
2321                 //              |               dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
2322                 //
2323                 // example:
2324                 //              opening the widget as a dropdown
2325                 //              |               dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
2326                 //
2327                 //              Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
2328                 //              (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
2329
2330                 var stack = this._stack,
2331                         widget = args.popup,
2332                         orient = args.orient || (
2333                                 (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ?
2334                                 {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
2335                                 {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
2336                         ),
2337                         around = args.around,
2338                         id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
2339
2340
2341                 // The wrapper may have already been created, but in case it wasn't, create here
2342                 var wrapper = widget.domNode.parentNode;
2343                 if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){
2344                         this.moveOffScreen(widget.domNode);
2345                         wrapper = widget.domNode.parentNode;
2346                 }
2347
2348                 dojo.attr(wrapper, {
2349                         id: id,
2350                         style: {
2351                                 zIndex: this._beginZIndex + stack.length
2352                         },
2353                         "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
2354                         dijitPopupParent: args.parent ? args.parent.id : ""
2355                 });
2356
2357                 if(dojo.isIE || dojo.isMoz){
2358                         var iframe = wrapper.childNodes[1];
2359                         if(!iframe){
2360                                 iframe = new dijit.BackgroundIframe(wrapper);
2361                         }
2362                 }
2363
2364                 // position the wrapper node and make it visible
2365                 var best = around ?
2366                         dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
2367                         dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
2368
2369                 wrapper.style.visibility = "visible";
2370                 widget.domNode.style.visibility = "visible";    // counteract effects from _HasDropDown
2371
2372                 var handlers = [];
2373
2374                 // provide default escape and tab key handling
2375                 // (this will work for any widget, not just menu)
2376                 handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
2377                         if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
2378                                 dojo.stopEvent(evt);
2379                                 args.onCancel();
2380                         }else if(evt.charOrCode === dojo.keys.TAB){
2381                                 dojo.stopEvent(evt);
2382                                 var topPopup = this.getTopPopup();
2383                                 if(topPopup && topPopup.onCancel){
2384                                         topPopup.onCancel();
2385                                 }
2386                         }
2387                 }));
2388
2389                 // watch for cancel/execute events on the popup and notify the caller
2390                 // (for a menu, "execute" means clicking an item)
2391                 if(widget.onCancel){
2392                         handlers.push(dojo.connect(widget, "onCancel", args.onCancel));
2393                 }
2394
2395                 handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){
2396                         var topPopup = this.getTopPopup();
2397                         if(topPopup && topPopup.onExecute){
2398                                 topPopup.onExecute();
2399                         }
2400                 }));
2401
2402                 stack.push({
2403                         wrapper: wrapper,
2404                         iframe: iframe,
2405                         widget: widget,
2406                         parent: args.parent,
2407                         onExecute: args.onExecute,
2408                         onCancel: args.onCancel,
2409                         onClose: args.onClose,
2410                         handlers: handlers
2411                 });
2412
2413                 if(widget.onOpen){
2414                         // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
2415                         widget.onOpen(best);
2416                 }
2417
2418                 return best;
2419         },
2420
2421         close: function(/*dijit._Widget*/ popup){
2422                 // summary:
2423                 //              Close specified popup and any popups that it parented
2424
2425                 var stack = this._stack;
2426
2427                 // Basically work backwards from the top of the stack closing popups
2428                 // until we hit the specified popup, but IIRC there was some issue where closing
2429                 // a popup would cause others to close too.  Thus if we are trying to close B in [A,B,C]
2430                 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
2431                 // so the while condition is constructed defensively.
2432                 while(dojo.some(stack, function(elem){return elem.widget == popup;})){
2433                         var top = stack.pop(),
2434                                 wrapper = top.wrapper,
2435                                 iframe = top.iframe,
2436                                 widget = top.widget,
2437                                 onClose = top.onClose;
2438
2439                         if(widget.onClose){
2440                                 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
2441                                 widget.onClose();
2442                         }
2443                         dojo.forEach(top.handlers, dojo.disconnect);
2444
2445                         // Move the widget plus it's wrapper off screen, unless it has already been destroyed in above onClose() etc.
2446                         if(widget && widget.domNode){
2447                                 this.moveOffScreen(widget.domNode);
2448                         }else{
2449                                 dojo.destroy(wrapper);
2450                         }
2451                         
2452                         if(onClose){
2453                                 onClose();
2454                         }
2455                 }
2456         }
2457 };
2458
2459 dijit._frames = new function(){
2460         // summary:
2461         //              cache of iframes
2462         var queue = [];
2463
2464         this.pop = function(){
2465                 var iframe;
2466                 if(queue.length){
2467                         iframe = queue.pop();
2468                         iframe.style.display="";
2469                 }else{
2470                         if(dojo.isIE){
2471                                 var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
2472                                 var html="<iframe src='" + burl + "'"
2473                                         + " style='position: absolute; left: 0px; top: 0px;"
2474                                         + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
2475                                 iframe = dojo.doc.createElement(html);
2476                         }else{
2477                                 iframe = dojo.create("iframe");
2478                                 iframe.src = 'javascript:""';
2479                                 iframe.className = "dijitBackgroundIframe";
2480                                 dojo.style(iframe, "opacity", 0.1);
2481                         }
2482                         iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work.
2483                         dijit.setWaiRole(iframe,"presentation");
2484                 }
2485                 return iframe;
2486         };
2487
2488         this.push = function(iframe){
2489                 iframe.style.display="none";
2490                 queue.push(iframe);
2491         }
2492 }();
2493
2494
2495 dijit.BackgroundIframe = function(/* DomNode */node){
2496         // summary:
2497         //              For IE/FF z-index schenanigans. id attribute is required.
2498         //
2499         // description:
2500         //              new dijit.BackgroundIframe(node)
2501         //                      Makes a background iframe as a child of node, that fills
2502         //                      area (and position) of node
2503
2504         if(!node.id){ throw new Error("no id"); }
2505         if(dojo.isIE || dojo.isMoz){
2506                 var iframe = dijit._frames.pop();
2507                 node.appendChild(iframe);
2508                 if(dojo.isIE<7){
2509                         this.resize(node);
2510                         this._conn = dojo.connect(node, 'onresize', this, function(){
2511                                 this.resize(node);
2512                         });
2513                 }else{
2514                         dojo.style(iframe, {
2515                                 width: '100%',
2516                                 height: '100%'
2517                         });
2518                 }
2519                 this.iframe = iframe;
2520         }
2521 };
2522
2523 dojo.extend(dijit.BackgroundIframe, {
2524         resize: function(node){
2525                 // summary:
2526                 //              resize the iframe so its the same size as node
2527                 // description:
2528                 //              this function is a no-op in all browsers except
2529                 //              IE6, which does not support 100% width/height 
2530                 //              of absolute positioned iframes
2531                 if(this.iframe && dojo.isIE<7){
2532                         dojo.style(this.iframe, {
2533                                 width: node.offsetWidth + 'px',
2534                                 height: node.offsetHeight + 'px'
2535                         });
2536                 }
2537         },
2538         destroy: function(){
2539                 // summary:
2540                 //              destroy the iframe
2541                 if(this._conn){
2542                         dojo.disconnect(this._conn);
2543                         this._conn = null;
2544                 }
2545                 if(this.iframe){
2546                         dijit._frames.push(this.iframe);
2547                         delete this.iframe;
2548                 }
2549         }
2550 });
2551
2552 }
2553
2554 if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2555 dojo._hasResource["dijit._base.scroll"] = true;
2556 dojo.provide("dijit._base.scroll");
2557
2558
2559
2560 dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
2561         // summary:
2562         //              Scroll the passed node into view, if it is not already.
2563         //              Deprecated, use `dojo.window.scrollIntoView` instead.
2564         
2565         dojo.window.scrollIntoView(node, pos);
2566 };
2567
2568 }
2569
2570 if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2571 dojo._hasResource["dojo.uacss"] = true;
2572 dojo.provide("dojo.uacss");
2573
2574 (function(){
2575         // summary:
2576         //              Applies pre-set CSS classes to the top-level HTML node, based on:
2577         //                      - browser (ex: dj_ie)
2578         //                      - browser version (ex: dj_ie6)
2579         //                      - box model (ex: dj_contentBox)
2580         //                      - text direction (ex: dijitRtl)
2581         //
2582         //              In addition, browser, browser version, and box model are
2583         //              combined with an RTL flag when browser text is RTL.  ex: dj_ie-rtl.
2584
2585         var d = dojo,
2586                 html = d.doc.documentElement,
2587                 ie = d.isIE,
2588                 opera = d.isOpera,
2589                 maj = Math.floor,
2590                 ff = d.isFF,
2591                 boxModel = d.boxModel.replace(/-/,''),
2592
2593                 classes = {
2594                         dj_ie: ie,
2595                         dj_ie6: maj(ie) == 6,
2596                         dj_ie7: maj(ie) == 7,
2597                         dj_ie8: maj(ie) == 8,
2598                         dj_quirks: d.isQuirks,
2599                         dj_iequirks: ie && d.isQuirks,
2600
2601                         // NOTE: Opera not supported by dijit
2602                         dj_opera: opera,
2603
2604                         dj_khtml: d.isKhtml,
2605
2606                         dj_webkit: d.isWebKit,
2607                         dj_safari: d.isSafari,
2608                         dj_chrome: d.isChrome,
2609
2610                         dj_gecko: d.isMozilla,
2611                         dj_ff3: maj(ff) == 3
2612                 }; // no dojo unsupported browsers
2613
2614         classes["dj_" + boxModel] = true;
2615
2616         // apply browser, browser version, and box model class names
2617         var classStr = "";
2618         for(var clz in classes){
2619                 if(classes[clz]){
2620                         classStr += clz + " ";
2621                 }
2622         }
2623         html.className = d.trim(html.className + " " + classStr);
2624
2625         // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
2626         // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).  
2627         // Unshift() is to run sniff code before the parser.
2628         dojo._loaders.unshift(function(){
2629                 if(!dojo._isBodyLtr()){
2630                         var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")
2631                         html.className = d.trim(html.className + " " + rtlClassStr);
2632                 }
2633         });
2634 })();
2635
2636 }
2637
2638 if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2639 dojo._hasResource["dijit._base.sniff"] = true;
2640 // summary:
2641 //              Applies pre-set CSS classes to the top-level HTML node, see
2642 //              `dojo.uacss` for details.
2643 //
2644 //              Simply doing a require on this module will
2645 //              establish this CSS.  Modified version of Morris' CSS hack.
2646
2647 dojo.provide("dijit._base.sniff");
2648
2649
2650
2651 }
2652
2653 if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2654 dojo._hasResource["dijit._base.typematic"] = true;
2655 dojo.provide("dijit._base.typematic");
2656
2657 dijit.typematic = {
2658         // summary:
2659         //              These functions are used to repetitively call a user specified callback
2660         //              method when a specific key or mouse click over a specific DOM node is
2661         //              held down for a specific amount of time.
2662         //              Only 1 such event is allowed to occur on the browser page at 1 time.
2663
2664         _fireEventAndReload: function(){
2665                 this._timer = null;
2666                 this._callback(++this._count, this._node, this._evt);
2667                 
2668                 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
2669                 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
2670                 this._currentTimeout = Math.max(
2671                         this._currentTimeout < 0 ? this._initialDelay :
2672                                 (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
2673                         this._minDelay);
2674                 this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout);
2675         },
2676
2677         trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2678                 // summary:
2679                 //              Start a timed, repeating callback sequence.
2680                 //              If already started, the function call is ignored.
2681                 //              This method is not normally called by the user but can be
2682                 //              when the normal listener code is insufficient.
2683                 // evt:
2684                 //              key or mouse event object to pass to the user callback
2685                 // _this:
2686                 //              pointer to the user's widget space.
2687                 // node:
2688                 //              the DOM node object to pass the the callback function
2689                 // callback:
2690                 //              function to call until the sequence is stopped called with 3 parameters:
2691                 // count:
2692                 //              integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
2693                 // node:
2694                 //              the DOM node object passed in
2695                 // evt:
2696                 //              key or mouse event object
2697                 // obj:
2698                 //              user space object used to uniquely identify each typematic sequence
2699                 // subsequentDelay (optional):
2700                 //              if > 1, the number of milliseconds until the 3->n events occur
2701                 //              or else the fractional time multiplier for the next event's delay, default=0.9
2702                 // initialDelay (optional):
2703                 //              the number of milliseconds until the 2nd event occurs, default=500ms
2704                 // minDelay (optional):
2705                 //              the maximum delay in milliseconds for event to fire, default=10ms
2706                 if(obj != this._obj){
2707                         this.stop();
2708                         this._initialDelay = initialDelay || 500;
2709                         this._subsequentDelay = subsequentDelay || 0.90;
2710                         this._minDelay = minDelay || 10;
2711                         this._obj = obj;
2712                         this._evt = evt;
2713                         this._node = node;
2714                         this._currentTimeout = -1;
2715                         this._count = -1;
2716                         this._callback = dojo.hitch(_this, callback);
2717                         this._fireEventAndReload();
2718                         this._evt = dojo.mixin({faux: true}, evt);
2719                 }
2720         },
2721
2722         stop: function(){
2723                 // summary:
2724                 //              Stop an ongoing timed, repeating callback sequence.
2725                 if(this._timer){
2726                         clearTimeout(this._timer);
2727                         this._timer = null;
2728                 }
2729                 if(this._obj){
2730                         this._callback(-1, this._node, this._evt);
2731                         this._obj = null;
2732                 }
2733         },
2734
2735         addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2736                 // summary:
2737                 //              Start listening for a specific typematic key.
2738                 //              See also the trigger method for other parameters.
2739                 // keyObject:
2740                 //              an object defining the key to listen for:
2741                 //              charOrCode:
2742                 //                      the printable character (string) or keyCode (number) to listen for.
2743                 //              keyCode:
2744                 //                      (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
2745                 //              charCode:
2746                 //                      (deprecated - use charOrCode) the charCode (number) to listen for.
2747                 //              ctrlKey:
2748                 //                      desired ctrl key state to initiate the callback sequence:
2749                 //                      - pressed (true)
2750                 //                      - released (false)
2751                 //                      - either (unspecified)
2752                 //              altKey:
2753                 //                      same as ctrlKey but for the alt key
2754                 //              shiftKey:
2755                 //                      same as ctrlKey but for the shift key
2756                 // returns:
2757                 //              an array of dojo.connect handles
2758                 if(keyObject.keyCode){
2759                         keyObject.charOrCode = keyObject.keyCode;
2760                         dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
2761                 }else if(keyObject.charCode){
2762                         keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
2763                         dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
2764                 }
2765                 return [
2766                         dojo.connect(node, "onkeypress", this, function(evt){
2767                                 if(evt.charOrCode == keyObject.charOrCode &&
2768                                 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
2769                                 (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
2770                                 (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
2771                                 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
2772                                         dojo.stopEvent(evt);
2773                                         dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
2774                                 }else if(dijit.typematic._obj == keyObject){
2775                                         dijit.typematic.stop();
2776                                 }
2777                         }),
2778                         dojo.connect(node, "onkeyup", this, function(evt){
2779                                 if(dijit.typematic._obj == keyObject){
2780                                         dijit.typematic.stop();
2781                                 }
2782                         })
2783                 ];
2784         },
2785
2786         addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2787                 // summary:
2788                 //              Start listening for a typematic mouse click.
2789                 //              See the trigger method for other parameters.
2790                 // returns:
2791                 //              an array of dojo.connect handles
2792                 var dc = dojo.connect;
2793                 return [
2794                         dc(node, "mousedown", this, function(evt){
2795                                 dojo.stopEvent(evt);
2796                                 dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
2797                         }),
2798                         dc(node, "mouseup", this, function(evt){
2799                                 dojo.stopEvent(evt);
2800                                 dijit.typematic.stop();
2801                         }),
2802                         dc(node, "mouseout", this, function(evt){
2803                                 dojo.stopEvent(evt);
2804                                 dijit.typematic.stop();
2805                         }),
2806                         dc(node, "mousemove", this, function(evt){
2807                                 evt.preventDefault();
2808                         }),
2809                         dc(node, "dblclick", this, function(evt){
2810                                 dojo.stopEvent(evt);
2811                                 if(dojo.isIE){
2812                                         dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
2813                                         setTimeout(dojo.hitch(this, dijit.typematic.stop), 50);
2814                                 }
2815                         })
2816                 ];
2817         },
2818
2819         addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2820                 // summary:
2821                 //              Start listening for a specific typematic key and mouseclick.
2822                 //              This is a thin wrapper to addKeyListener and addMouseListener.
2823                 //              See the addMouseListener and addKeyListener methods for other parameters.
2824                 // mouseNode:
2825                 //              the DOM node object to listen on for mouse events.
2826                 // keyNode:
2827                 //              the DOM node object to listen on for key events.
2828                 // returns:
2829                 //              an array of dojo.connect handles
2830                 return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat(
2831                         this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay));
2832         }
2833 };
2834
2835 }
2836
2837 if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2838 dojo._hasResource["dijit._base.wai"] = true;
2839 dojo.provide("dijit._base.wai");
2840
2841 dijit.wai = {
2842         onload: function(){
2843                 // summary:
2844                 //              Detects if we are in high-contrast mode or not
2845
2846                 // This must be a named function and not an anonymous
2847                 // function, so that the widget parsing code can make sure it
2848                 // registers its onload function after this function.
2849                 // DO NOT USE "this" within this function.
2850
2851                 // create div for testing if high contrast mode is on or images are turned off
2852                 var div = dojo.create("div",{
2853                         id: "a11yTestNode",
2854                         style:{
2855                                 cssText:'border: 1px solid;'
2856                                         + 'border-color:red green;'
2857                                         + 'position: absolute;'
2858                                         + 'height: 5px;'
2859                                         + 'top: -999px;'
2860                                         + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");'
2861                         }
2862                 }, dojo.body());
2863
2864                 // test it
2865                 var cs = dojo.getComputedStyle(div);
2866                 if(cs){
2867                         var bkImg = cs.backgroundImage;
2868                         var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
2869                         dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
2870                         if(dojo.isIE){
2871                                 div.outerHTML = "";             // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
2872                         }else{
2873                                 dojo.body().removeChild(div);
2874                         }
2875                 }
2876         }
2877 };
2878
2879 // Test if computer is in high contrast mode.
2880 // Make sure the a11y test runs first, before widgets are instantiated.
2881 if(dojo.isIE || dojo.isMoz){    // NOTE: checking in Safari messes things up
2882         dojo._loaders.unshift(dijit.wai.onload);
2883 }
2884
2885 dojo.mixin(dijit, {
2886         _XhtmlRoles: /banner|contentinfo|definition|main|navigation|search|note|secondary|seealso/,
2887
2888         hasWaiRole: function(/*Element*/ elem, /*String*/ role){
2889                 // summary:
2890                 //              Determines if an element has a particular non-XHTML role.
2891                 // returns:
2892                 //              True if elem has the specific non-XHTML role attribute and false if not.
2893                 //              For backwards compatibility if role parameter not provided,
2894                 //              returns true if has non XHTML role
2895                 var waiRole = this.getWaiRole(elem);
2896                 return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
2897         },
2898
2899         getWaiRole: function(/*Element*/ elem){
2900                 // summary:
2901                 //              Gets the non-XHTML role for an element (which should be a wai role).
2902                 // returns:
2903                 //              The non-XHTML role of elem or an empty string if elem
2904                 //              does not have a role.
2905                  return dojo.trim((dojo.attr(elem, "role") || "").replace(this._XhtmlRoles,"").replace("wairole:",""));
2906         },
2907
2908         setWaiRole: function(/*Element*/ elem, /*String*/ role){
2909                 // summary:
2910                 //              Sets the role on an element.
2911                 // description:
2912                 //              Replace existing role attribute with new role.
2913                 //              If elem already has an XHTML role, append this role to XHTML role
2914                 //              and remove other ARIA roles.
2915
2916                 var curRole = dojo.attr(elem, "role") || "";
2917                 if(!this._XhtmlRoles.test(curRole)){
2918                         dojo.attr(elem, "role", role);
2919                 }else{
2920                         if((" "+ curRole +" ").indexOf(" " + role + " ") < 0){
2921                                 var clearXhtml = dojo.trim(curRole.replace(this._XhtmlRoles, ""));
2922                                 var cleanRole = dojo.trim(curRole.replace(clearXhtml, ""));
2923                                 dojo.attr(elem, "role", cleanRole + (cleanRole ? ' ' : '') + role);
2924                         }
2925                 }
2926         },
2927
2928         removeWaiRole: function(/*Element*/ elem, /*String*/ role){
2929                 // summary:
2930                 //              Removes the specified non-XHTML role from an element.
2931                 //              Removes role attribute if no specific role provided (for backwards compat.)
2932
2933                 var roleValue = dojo.attr(elem, "role");
2934                 if(!roleValue){ return; }
2935                 if(role){
2936                         var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
2937                         dojo.attr(elem, "role", t);
2938                 }else{
2939                         elem.removeAttribute("role");
2940                 }
2941         },
2942
2943         hasWaiState: function(/*Element*/ elem, /*String*/ state){
2944                 // summary:
2945                 //              Determines if an element has a given state.
2946                 // description:
2947                 //              Checks for an attribute called "aria-"+state.
2948                 // returns:
2949                 //              true if elem has a value for the given state and
2950                 //              false if it does not.
2951
2952                 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
2953         },
2954
2955         getWaiState: function(/*Element*/ elem, /*String*/ state){
2956                 // summary:
2957                 //              Gets the value of a state on an element.
2958                 // description:
2959                 //              Checks for an attribute called "aria-"+state.
2960                 // returns:
2961                 //              The value of the requested state on elem
2962                 //              or an empty string if elem has no value for state.
2963
2964                 return elem.getAttribute("aria-"+state) || "";
2965         },
2966
2967         setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
2968                 // summary:
2969                 //              Sets a state on an element.
2970                 // description:
2971                 //              Sets an attribute called "aria-"+state.
2972
2973                 elem.setAttribute("aria-"+state, value);
2974         },
2975
2976         removeWaiState: function(/*Element*/ elem, /*String*/ state){
2977                 // summary:
2978                 //              Removes a state from an element.
2979                 // description:
2980                 //              Sets an attribute called "aria-"+state.
2981
2982                 elem.removeAttribute("aria-"+state);
2983         }
2984 });
2985
2986 }
2987
2988 if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2989 dojo._hasResource["dijit._base"] = true;
2990 dojo.provide("dijit._base");
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002 }
3003
3004 if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3005 dojo._hasResource["dijit._Widget"] = true;
3006 dojo.provide("dijit._Widget");
3007
3008 dojo.require( "dijit._base" );
3009
3010
3011 // This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets'
3012 // DOM nodes) until someone actually needs to monitor that event.
3013 dojo.connect(dojo, "_connect",
3014         function(/*dijit._Widget*/ widget, /*String*/ event){
3015                 if(widget && dojo.isFunction(widget._onConnect)){
3016                         widget._onConnect(event);
3017                 }
3018         });
3019
3020 dijit._connectOnUseEventHandler = function(/*Event*/ event){};
3021
3022 // Keep track of where the last keydown event was, to help avoid generating
3023 // spurious ondijitclick events when:
3024 // 1. focus is on a <button> or <a>
3025 // 2. user presses then releases the ENTER key
3026 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
3027 // 4. onkeyup event fires, causing the ondijitclick handler to fire
3028 dijit._lastKeyDownNode = null;
3029 if(dojo.isIE){
3030         (function(){
3031                 var keydownCallback = function(evt){
3032                         dijit._lastKeyDownNode = evt.srcElement;
3033                 };
3034                 dojo.doc.attachEvent('onkeydown', keydownCallback);
3035                 dojo.addOnWindowUnload(function(){
3036                         dojo.doc.detachEvent('onkeydown', keydownCallback);
3037                 });
3038         })();
3039 }else{
3040         dojo.doc.addEventListener('keydown', function(evt){
3041                 dijit._lastKeyDownNode = evt.target;
3042         }, true);
3043 }
3044
3045 (function(){
3046
3047 var _attrReg = {},      // cached results from getSetterAttributes
3048         getSetterAttributes = function(widget){
3049                 // summary:
3050                 //              Returns list of attributes with custom setters for specified widget
3051                 var dc = widget.declaredClass;
3052                 if(!_attrReg[dc]){
3053                         var r = [],
3054                                 attrs,
3055                                 proto = widget.constructor.prototype;
3056                         for(var fxName in proto){
3057                                 if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){
3058                                         r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1));
3059                                 }
3060                         }
3061                         _attrReg[dc] = r;
3062                 }
3063                 return _attrReg[dc] || [];      // String[]
3064         };
3065
3066 dojo.declare("dijit._Widget", null, {
3067         // summary:
3068         //              Base class for all Dijit widgets.
3069
3070         // id: [const] String
3071         //              A unique, opaque ID string that can be assigned by users or by the
3072         //              system. If the developer passes an ID which is known not to be
3073         //              unique, the specified ID is ignored and the system-generated ID is
3074         //              used instead.
3075         id: "",
3076
3077         // lang: [const] String
3078         //              Rarely used.  Overrides the default Dojo locale used to render this widget,
3079         //              as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
3080         //              Value must be among the list of locales specified during by the Dojo bootstrap,
3081         //              formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
3082         lang: "",
3083
3084         // dir: [const] String
3085         //              Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
3086         //              attribute. Either left-to-right "ltr" or right-to-left "rtl".  If undefined, widgets renders in page's
3087         //              default direction.
3088         dir: "",
3089
3090         // class: String
3091         //              HTML class attribute
3092         "class": "",
3093
3094         // style: String||Object
3095         //              HTML style attributes as cssText string or name/value hash
3096         style: "",
3097
3098         // title: String
3099         //              HTML title attribute.
3100         //
3101         //              For form widgets this specifies a tooltip to display when hovering over
3102         //              the widget (just like the native HTML title attribute).
3103         //
3104         //              For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
3105         //              etc., it's used to specify the tab label, accordion pane title, etc.
3106         title: "",
3107
3108         // tooltip: String
3109         //              When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
3110         //              this specifies the tooltip to appear when the mouse is hovered over that text.
3111         tooltip: "",
3112
3113         // baseClass: [protected] String
3114         //              Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
3115         //              widget state.
3116         baseClass: "",
3117
3118         // srcNodeRef: [readonly] DomNode
3119         //              pointer to original DOM node
3120         srcNodeRef: null,
3121
3122         // domNode: [readonly] DomNode
3123         //              This is our visible representation of the widget! Other DOM
3124         //              Nodes may by assigned to other properties, usually through the
3125         //              template system's dojoAttachPoint syntax, but the domNode
3126         //              property is the canonical "top level" node in widget UI.
3127         domNode: null,
3128
3129         // containerNode: [readonly] DomNode
3130         //              Designates where children of the source DOM node will be placed.
3131         //              "Children" in this case refers to both DOM nodes and widgets.
3132         //              For example, for myWidget:
3133         //
3134         //              |       <div dojoType=myWidget>
3135         //              |               <b> here's a plain DOM node
3136         //              |               <span dojoType=subWidget>and a widget</span>
3137         //              |               <i> and another plain DOM node </i>
3138         //              |       </div>
3139         //
3140         //              containerNode would point to:
3141         //
3142         //              |               <b> here's a plain DOM node
3143         //              |               <span dojoType=subWidget>and a widget</span>
3144         //              |               <i> and another plain DOM node </i>
3145         //
3146         //              In templated widgets, "containerNode" is set via a
3147         //              dojoAttachPoint assignment.
3148         //
3149         //              containerNode must be defined for any widget that accepts innerHTML
3150         //              (like ContentPane or BorderContainer or even Button), and conversely
3151         //              is null for widgets that don't, like TextBox.
3152         containerNode: null,
3153
3154 /*=====
3155         // _started: Boolean
3156         //              startup() has completed.
3157         _started: false,
3158 =====*/
3159
3160         // attributeMap: [protected] Object
3161         //              attributeMap sets up a "binding" between attributes (aka properties)
3162         //              of the widget and the widget's DOM.
3163         //              Changes to widget attributes listed in attributeMap will be
3164         //              reflected into the DOM.
3165         //
3166         //              For example, calling attr('title', 'hello')
3167         //              on a TitlePane will automatically cause the TitlePane's DOM to update
3168         //              with the new title.
3169         //
3170         //              attributeMap is a hash where the key is an attribute of the widget,
3171         //              and the value reflects a binding to a:
3172         //
3173         //              - DOM node attribute
3174         // |            focus: {node: "focusNode", type: "attribute"}
3175         //              Maps this.focus to this.focusNode.focus
3176         //
3177         //              - DOM node innerHTML
3178         //      |               title: { node: "titleNode", type: "innerHTML" }
3179         //              Maps this.title to this.titleNode.innerHTML
3180         //
3181         //              - DOM node innerText
3182         //      |               title: { node: "titleNode", type: "innerText" }
3183         //              Maps this.title to this.titleNode.innerText
3184         //
3185         //              - DOM node CSS class
3186         // |            myClass: { node: "domNode", type: "class" }
3187         //              Maps this.myClass to this.domNode.className
3188         //
3189         //              If the value is an array, then each element in the array matches one of the
3190         //              formats of the above list.
3191         //
3192         //              There are also some shorthands for backwards compatibility:
3193         //              - string --> { node: string, type: "attribute" }, for example:
3194         //      |       "focusNode" ---> { node: "focusNode", type: "attribute" }
3195         //              - "" --> { node: "domNode", type: "attribute" }
3196         attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},
3197
3198         // _deferredConnects: [protected] Object
3199         //              attributeMap addendum for event handlers that should be connected only on first use
3200         _deferredConnects: {
3201                 onClick: "",
3202                 onDblClick: "",
3203                 onKeyDown: "",
3204                 onKeyPress: "",
3205                 onKeyUp: "",
3206                 onMouseMove: "",
3207                 onMouseDown: "",
3208                 onMouseOut: "",
3209                 onMouseOver: "",
3210                 onMouseLeave: "",
3211                 onMouseEnter: "",
3212                 onMouseUp: ""
3213         },
3214
3215         onClick: dijit._connectOnUseEventHandler,
3216         /*=====
3217         onClick: function(event){
3218                 // summary:
3219                 //              Connect to this function to receive notifications of mouse click events.
3220                 // event:
3221                 //              mouse Event
3222                 // tags:
3223                 //              callback
3224         },
3225         =====*/
3226         onDblClick: dijit._connectOnUseEventHandler,
3227         /*=====
3228         onDblClick: function(event){
3229                 // summary:
3230                 //              Connect to this function to receive notifications of mouse double click events.
3231                 // event:
3232                 //              mouse Event
3233                 // tags:
3234                 //              callback
3235         },
3236         =====*/
3237         onKeyDown: dijit._connectOnUseEventHandler,
3238         /*=====
3239         onKeyDown: function(event){
3240                 // summary:
3241                 //              Connect to this function to receive notifications of keys being pressed down.
3242                 // event:
3243                 //              key Event
3244                 // tags:
3245                 //              callback
3246         },
3247         =====*/
3248         onKeyPress: dijit._connectOnUseEventHandler,
3249         /*=====
3250         onKeyPress: function(event){
3251                 // summary:
3252                 //              Connect to this function to receive notifications of printable keys being typed.
3253                 // event:
3254                 //              key Event
3255                 // tags:
3256                 //              callback
3257         },
3258         =====*/
3259         onKeyUp: dijit._connectOnUseEventHandler,
3260         /*=====
3261         onKeyUp: function(event){
3262                 // summary:
3263                 //              Connect to this function to receive notifications of keys being released.
3264                 // event:
3265                 //              key Event
3266                 // tags:
3267                 //              callback
3268         },
3269         =====*/
3270         onMouseDown: dijit._connectOnUseEventHandler,
3271         /*=====
3272         onMouseDown: function(event){
3273                 // summary:
3274                 //              Connect to this function to receive notifications of when the mouse button is pressed down.
3275                 // event:
3276                 //              mouse Event
3277                 // tags:
3278                 //              callback
3279         },
3280         =====*/
3281         onMouseMove: dijit._connectOnUseEventHandler,
3282         /*=====
3283         onMouseMove: function(event){
3284                 // summary:
3285                 //              Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
3286                 // event:
3287                 //              mouse Event
3288                 // tags:
3289                 //              callback
3290         },
3291         =====*/
3292         onMouseOut: dijit._connectOnUseEventHandler,
3293         /*=====
3294         onMouseOut: function(event){
3295                 // summary:
3296                 //              Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
3297                 // event:
3298                 //              mouse Event
3299                 // tags:
3300                 //              callback
3301         },
3302         =====*/
3303         onMouseOver: dijit._connectOnUseEventHandler,
3304         /*=====
3305         onMouseOver: function(event){
3306                 // summary:
3307                 //              Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
3308                 // event:
3309                 //              mouse Event
3310                 // tags:
3311                 //              callback
3312         },
3313         =====*/
3314         onMouseLeave: dijit._connectOnUseEventHandler,
3315         /*=====
3316         onMouseLeave: function(event){
3317                 // summary:
3318                 //              Connect to this function to receive notifications of when the mouse moves off of this widget.
3319                 // event:
3320                 //              mouse Event
3321                 // tags:
3322                 //              callback
3323         },
3324         =====*/
3325         onMouseEnter: dijit._connectOnUseEventHandler,
3326         /*=====
3327         onMouseEnter: function(event){
3328                 // summary:
3329                 //              Connect to this function to receive notifications of when the mouse moves onto this widget.
3330                 // event:
3331                 //              mouse Event
3332                 // tags:
3333                 //              callback
3334         },
3335         =====*/
3336         onMouseUp: dijit._connectOnUseEventHandler,
3337         /*=====
3338         onMouseUp: function(event){
3339                 // summary:
3340                 //              Connect to this function to receive notifications of when the mouse button is released.
3341                 // event:
3342                 //              mouse Event
3343                 // tags:
3344                 //              callback
3345         },
3346         =====*/
3347
3348         // Constants used in templates
3349
3350         // _blankGif: [protected] String
3351         //              Path to a blank 1x1 image.
3352         //              Used by <img> nodes in templates that really get their image via CSS background-image.
3353         _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(),
3354
3355         //////////// INITIALIZATION METHODS ///////////////////////////////////////
3356
3357         postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
3358                 // summary:
3359                 //              Kicks off widget instantiation.  See create() for details.
3360                 // tags:
3361                 //              private
3362                 this.create(params, srcNodeRef);
3363         },
3364
3365         create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
3366                 // summary:
3367                 //              Kick off the life-cycle of a widget
3368                 // params:
3369                 //              Hash of initialization parameters for widget, including
3370                 //              scalar values (like title, duration etc.) and functions,
3371                 //              typically callbacks like onClick.
3372                 // srcNodeRef:
3373                 //              If a srcNodeRef (DOM node) is specified:
3374                 //                      - use srcNodeRef.innerHTML as my contents
3375                 //                      - if this is a behavioral widget then apply behavior
3376                 //                        to that srcNodeRef
3377                 //                      - otherwise, replace srcNodeRef with my generated DOM
3378                 //                        tree
3379                 // description:
3380                 //              Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
3381                 //              etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget
3382                 //              for a discussion of the widget creation lifecycle.
3383                 //
3384                 //              Of course, adventurous developers could override create entirely, but this should
3385                 //              only be done as a last resort.
3386                 // tags:
3387                 //              private
3388
3389                 // store pointer to original DOM tree
3390                 this.srcNodeRef = dojo.byId(srcNodeRef);
3391
3392                 // For garbage collection.  An array of handles returned by Widget.connect()
3393                 // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
3394                 this._connects = [];
3395
3396                 // For garbage collection.  An array of handles returned by Widget.subscribe()
3397                 // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe()
3398                 this._subscribes = [];
3399
3400                 // To avoid double-connects, remove entries from _deferredConnects
3401                 // that have been setup manually by a subclass (ex, by dojoAttachEvent).
3402                 // If a subclass has redefined a callback (ex: onClick) then assume it's being
3403                 // connected to manually.
3404                 this._deferredConnects = dojo.clone(this._deferredConnects);
3405                 for(var attr in this.attributeMap){
3406                         delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
3407                 }
3408                 for(attr in this._deferredConnects){
3409                         if(this[attr] !== dijit._connectOnUseEventHandler){
3410                                 delete this._deferredConnects[attr];    // redefined, probably dojoAttachEvent exists
3411                         }
3412                 }
3413
3414                 //mixin our passed parameters
3415                 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
3416                 if(params){
3417                         this.params = params;
3418                         dojo.mixin(this,params);
3419                 }
3420                 this.postMixInProperties();
3421
3422                 // generate an id for the widget if one wasn't specified
3423                 // (be sure to do this before buildRendering() because that function might
3424                 // expect the id to be there.)
3425                 if(!this.id){
3426                         this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
3427                 }
3428                 dijit.registry.add(this);
3429
3430                 this.buildRendering();
3431
3432                 if(this.domNode){
3433                         // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
3434                         this._applyAttributes();
3435
3436                         var source = this.srcNodeRef;
3437                         if(source && source.parentNode){
3438                                 source.parentNode.replaceChild(this.domNode, source);
3439                         }
3440
3441                         // If the developer has specified a handler as a widget parameter
3442                         // (ex: new Button({onClick: ...})
3443                         // then naturally need to connect from DOM node to that handler immediately,
3444                         for(attr in this.params){
3445                                 this._onConnect(attr);
3446                         }
3447                 }
3448
3449                 if(this.domNode){
3450                         this.domNode.setAttribute("widgetId", this.id);
3451                 }
3452                 this.postCreate();
3453
3454                 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
3455                 if(this.srcNodeRef && !this.srcNodeRef.parentNode){
3456                         delete this.srcNodeRef;
3457                 }
3458
3459                 this._created = true;
3460         },
3461
3462         _applyAttributes: function(){
3463                 // summary:
3464                 //              Step during widget creation to copy all widget attributes to the
3465                 //              DOM as per attributeMap and _setXXXAttr functions.
3466                 // description:
3467                 //              Skips over blank/false attribute values, unless they were explicitly specified
3468                 //              as parameters to the widget, since those are the default anyway,
3469                 //              and setting tabIndex="" is different than not setting tabIndex at all.
3470                 //
3471                 //              It processes the attributes in the attribute map first, and then
3472                 //              it goes through and processes the attributes for the _setXXXAttr
3473                 //              functions that have been specified
3474                 // tags:
3475                 //              private
3476                 var condAttrApply = function(attr, scope){
3477                         if((scope.params && attr in scope.params) || scope[attr]){
3478                                 scope.set(attr, scope[attr]);
3479                         }
3480                 };
3481
3482                 // Do the attributes in attributeMap
3483                 for(var attr in this.attributeMap){
3484                         condAttrApply(attr, this);
3485                 }
3486
3487                 // And also any attributes with custom setters
3488                 dojo.forEach(getSetterAttributes(this), function(a){
3489                         if(!(a in this.attributeMap)){
3490                                 condAttrApply(a, this);
3491                         }
3492                 }, this);
3493         },
3494
3495         postMixInProperties: function(){
3496                 // summary:
3497                 //              Called after the parameters to the widget have been read-in,
3498                 //              but before the widget template is instantiated. Especially
3499                 //              useful to set properties that are referenced in the widget
3500                 //              template.
3501                 // tags:
3502                 //              protected
3503         },
3504
3505         buildRendering: function(){
3506                 // summary:
3507                 //              Construct the UI for this widget, setting this.domNode
3508                 // description:
3509                 //              Most widgets will mixin `dijit._Templated`, which implements this
3510                 //              method.
3511                 // tags:
3512                 //              protected
3513                 this.domNode = this.srcNodeRef || dojo.create('div');
3514         },
3515
3516         postCreate: function(){
3517                 // summary:
3518                 //              Processing after the DOM fragment is created
3519                 // description:
3520                 //              Called after the DOM fragment has been created, but not necessarily
3521                 //              added to the document.  Do not include any operations which rely on
3522                 //              node dimensions or placement.
3523                 // tags:
3524                 //              protected
3525
3526                 // baseClass is a single class name or occasionally a space-separated list of names.
3527                 // Add those classes to the DOMNod.  If RTL mode then also add with Rtl suffix.         
3528                 if(this.baseClass){
3529                         var classes = this.baseClass.split(" ");
3530                         if(!this.isLeftToRight()){
3531                                 classes = classes.concat( dojo.map(classes, function(name){ return name+"Rtl"; }));
3532                         }
3533                         dojo.addClass(this.domNode, classes);
3534                 }
3535         },
3536
3537         startup: function(){
3538                 // summary:
3539                 //              Processing after the DOM fragment is added to the document
3540                 // description:
3541                 //              Called after a widget and its children have been created and added to the page,
3542                 //              and all related widgets have finished their create() cycle, up through postCreate().
3543                 //              This is useful for composite widgets that need to control or layout sub-widgets.
3544                 //              Many layout widgets can use this as a wiring phase.
3545                 this._started = true;
3546         },
3547
3548         //////////// DESTROY FUNCTIONS ////////////////////////////////
3549
3550         destroyRecursive: function(/*Boolean?*/ preserveDom){
3551                 // summary:
3552                 //              Destroy this widget and its descendants
3553                 // description:
3554                 //              This is the generic "destructor" function that all widget users
3555                 //              should call to cleanly discard with a widget. Once a widget is
3556                 //              destroyed, it is removed from the manager object.
3557                 // preserveDom:
3558                 //              If true, this method will leave the original DOM structure
3559                 //              alone of descendant Widgets. Note: This will NOT work with
3560                 //              dijit._Templated widgets.
3561
3562                 this._beingDestroyed = true;
3563                 this.destroyDescendants(preserveDom);
3564                 this.destroy(preserveDom);
3565         },
3566
3567         destroy: function(/*Boolean*/ preserveDom){
3568                 // summary:
3569                 //              Destroy this widget, but not its descendants.
3570                 //              This method will, however, destroy internal widgets such as those used within a template.
3571                 // preserveDom: Boolean
3572                 //              If true, this method will leave the original DOM structure alone.
3573                 //              Note: This will not yet work with _Templated widgets
3574
3575                 this._beingDestroyed = true;
3576                 this.uninitialize();
3577                 var d = dojo,
3578                         dfe = d.forEach,
3579                         dun = d.unsubscribe;
3580                 dfe(this._connects, function(array){
3581                         dfe(array, d.disconnect);
3582                 });
3583                 dfe(this._subscribes, function(handle){
3584                         dun(handle);
3585                 });
3586
3587                 // destroy widgets created as part of template, etc.
3588                 dfe(this._supportingWidgets || [], function(w){
3589                         if(w.destroyRecursive){
3590                                 w.destroyRecursive();
3591                         }else if(w.destroy){
3592                                 w.destroy();
3593                         }
3594                 });
3595
3596                 this.destroyRendering(preserveDom);
3597                 dijit.registry.remove(this.id);
3598                 this._destroyed = true;
3599         },
3600
3601         destroyRendering: function(/*Boolean?*/ preserveDom){
3602                 // summary:
3603                 //              Destroys the DOM nodes associated with this widget
3604                 // preserveDom:
3605                 //              If true, this method will leave the original DOM structure alone
3606                 //              during tear-down. Note: this will not work with _Templated
3607                 //              widgets yet.
3608                 // tags:
3609                 //              protected
3610
3611                 if(this.bgIframe){
3612                         this.bgIframe.destroy(preserveDom);
3613                         delete this.bgIframe;
3614                 }
3615
3616                 if(this.domNode){
3617                         if(preserveDom){
3618                                 dojo.removeAttr(this.domNode, "widgetId");
3619                         }else{
3620                                 dojo.destroy(this.domNode);
3621                         }
3622                         delete this.domNode;
3623                 }
3624
3625                 if(this.srcNodeRef){
3626                         if(!preserveDom){
3627                                 dojo.destroy(this.srcNodeRef);
3628                         }
3629                         delete this.srcNodeRef;
3630                 }
3631         },
3632
3633         destroyDescendants: function(/*Boolean?*/ preserveDom){
3634                 // summary:
3635                 //              Recursively destroy the children of this widget and their
3636                 //              descendants.
3637                 // preserveDom:
3638                 //              If true, the preserveDom attribute is passed to all descendant
3639                 //              widget's .destroy() method. Not for use with _Templated
3640                 //              widgets.
3641
3642                 // get all direct descendants and destroy them recursively
3643                 dojo.forEach(this.getChildren(), function(widget){
3644                         if(widget.destroyRecursive){
3645                                 widget.destroyRecursive(preserveDom);
3646                         }
3647                 });
3648         },
3649
3650
3651         uninitialize: function(){
3652                 // summary:
3653                 //              Stub function. Override to implement custom widget tear-down
3654                 //              behavior.
3655                 // tags:
3656                 //              protected
3657                 return false;
3658         },
3659
3660         ////////////////// MISCELLANEOUS METHODS ///////////////////
3661
3662         onFocus: function(){
3663                 // summary:
3664                 //              Called when the widget becomes "active" because
3665                 //              it or a widget inside of it either has focus, or has recently
3666                 //              been clicked.
3667                 // tags:
3668                 //              callback
3669         },
3670
3671         onBlur: function(){
3672                 // summary:
3673                 //              Called when the widget stops being "active" because
3674                 //              focus moved to something outside of it, or the user
3675                 //              clicked somewhere outside of it, or the widget was
3676                 //              hidden.
3677                 // tags:
3678                 //              callback
3679         },
3680
3681         _onFocus: function(e){
3682                 // summary:
3683                 //              This is where widgets do processing for when they are active,
3684                 //              such as changing CSS classes.  See onFocus() for more details.
3685                 // tags:
3686                 //              protected
3687                 this.onFocus();
3688         },
3689
3690         _onBlur: function(){
3691                 // summary:
3692                 //              This is where widgets do processing for when they stop being active,
3693                 //              such as changing CSS classes.  See onBlur() for more details.
3694                 // tags:
3695                 //              protected
3696                 this.onBlur();
3697         },
3698
3699         _onConnect: function(/*String*/ event){
3700                 // summary:
3701                 //              Called when someone connects to one of my handlers.
3702                 //              "Turn on" that handler if it isn't active yet.
3703                 //
3704                 //              This is also called for every single initialization parameter
3705                 //              so need to do nothing for parameters like "id".
3706                 // tags:
3707                 //              private
3708                 if(event in this._deferredConnects){
3709                         var mapNode = this[this._deferredConnects[event] || 'domNode'];
3710                         this.connect(mapNode, event.toLowerCase(), event);
3711                         delete this._deferredConnects[event];
3712                 }
3713         },
3714
3715         _setClassAttr: function(/*String*/ value){
3716                 // summary:
3717                 //              Custom setter for the CSS "class" attribute
3718                 // tags:
3719                 //              protected
3720                 var mapNode = this[this.attributeMap["class"] || 'domNode'];
3721                 dojo.removeClass(mapNode, this["class"])
3722                 this["class"] = value;
3723                 dojo.addClass(mapNode, value);
3724         },
3725
3726         _setStyleAttr: function(/*String||Object*/ value){
3727                 // summary:
3728                 //              Sets the style attribut of the widget according to value,
3729                 //              which is either a hash like {height: "5px", width: "3px"}
3730                 //              or a plain string
3731                 // description:
3732                 //              Determines which node to set the style on based on style setting
3733                 //              in attributeMap.
3734                 // tags:
3735                 //              protected
3736
3737                 var mapNode = this[this.attributeMap.style || 'domNode'];
3738
3739                 // Note: technically we should revert any style setting made in a previous call
3740                 // to his method, but that's difficult to keep track of.
3741
3742                 if(dojo.isObject(value)){
3743                         dojo.style(mapNode, value);
3744                 }else{
3745                         if(mapNode.style.cssText){
3746                                 mapNode.style.cssText += "; " + value;
3747                         }else{
3748                                 mapNode.style.cssText = value;
3749                         }
3750                 }
3751
3752                 this.style = value;
3753         },
3754
3755         setAttribute: function(/*String*/ attr, /*anything*/ value){
3756                 // summary:
3757                 //              Deprecated.  Use set() instead.
3758                 // tags:
3759                 //              deprecated
3760                 dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
3761                 this.set(attr, value);
3762         },
3763
3764         _attrToDom: function(/*String*/ attr, /*String*/ value){
3765                 // summary:
3766                 //              Reflect a widget attribute (title, tabIndex, duration etc.) to
3767                 //              the widget DOM, as specified in attributeMap.
3768                 //
3769                 // description:
3770                 //              Also sets this["attr"] to the new value.
3771                 //              Note some attributes like "type"
3772                 //              cannot be processed this way as they are not mutable.
3773                 //
3774                 // tags:
3775                 //              private
3776
3777                 var commands = this.attributeMap[attr];
3778                 dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){
3779
3780                         // Get target node and what we are doing to that node
3781                         var mapNode = this[command.node || command || "domNode"];       // DOM node
3782                         var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
3783
3784                         switch(type){
3785                                 case "attribute":
3786                                         if(dojo.isFunction(value)){ // functions execute in the context of the widget
3787                                                 value = dojo.hitch(this, value);
3788                                         }
3789
3790                                         // Get the name of the DOM node attribute; usually it's the same
3791                                         // as the name of the attribute in the widget (attr), but can be overridden.
3792                                         // Also maps handler names to lowercase, like onSubmit --> onsubmit
3793                                         var attrName = command.attribute ? command.attribute :
3794                                                 (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
3795
3796                                         dojo.attr(mapNode, attrName, value);
3797                                         break;
3798                                 case "innerText":
3799                                         mapNode.innerHTML = "";
3800                                         mapNode.appendChild(dojo.doc.createTextNode(value));
3801                                         break;
3802                                 case "innerHTML":
3803                                         mapNode.innerHTML = value;
3804                                         break;
3805                                 case "class":
3806                                         dojo.removeClass(mapNode, this[attr]);
3807                                         dojo.addClass(mapNode, value);
3808                                         break;
3809                         }
3810                 }, this);
3811                 this[attr] = value;
3812         },
3813
3814         attr: function(/*String|Object*/name, /*Object?*/value){
3815                 // summary:
3816                 //              Set or get properties on a widget instance.
3817                 //      name:
3818                 //              The property to get or set. If an object is passed here and not
3819                 //              a string, its keys are used as names of attributes to be set
3820                 //              and the value of the object as values to set in the widget.
3821                 //      value:
3822                 //              Optional. If provided, attr() operates as a setter. If omitted,
3823                 //              the current value of the named property is returned.
3824                 // description:
3825                 //              This method is deprecated, use get() or set() directly.
3826
3827                 // Print deprecation warning but only once per calling function
3828                 if(dojo.config.isDebug){
3829                         var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
3830                                 caller = (arguments.callee.caller || "unknown caller").toString();
3831                         if(!alreadyCalledHash[caller]){
3832                                 dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
3833                                 caller, "", "2.0");
3834                                 alreadyCalledHash[caller] = true;
3835                         }
3836                 }
3837
3838                 var args = arguments.length;
3839                 if(args >= 2 || typeof name === "object"){ // setter
3840                         return this.set.apply(this, arguments);
3841                 }else{ // getter
3842                         return this.get(name);
3843                 }
3844         },
3845         
3846         get: function(name){
3847                 // summary:
3848                 //              Get a property from a widget.
3849                 //      name:
3850                 //              The property to get.
3851                 // description:
3852                 //              Get a named property from a widget. The property may
3853                 //              potentially be retrieved via a getter method. If no getter is defined, this
3854                 //              just retrieves the object's property.  
3855                 //              For example, if the widget has a properties "foo"
3856                 //              and "bar" and a method named "_getFooAttr", calling:
3857                 //      |       myWidget.get("foo");
3858                 //              would be equivalent to writing:
3859                 //      |       widget._getFooAttr();
3860                 //              and:
3861                 //      |       myWidget.get("bar");
3862                 //              would be equivalent to writing:
3863                 //      |       widget.bar;
3864                 var names = this._getAttrNames(name);
3865                 return this[names.g] ? this[names.g]() : this[name];
3866         },
3867         
3868         set: function(name, value){
3869                 // summary:
3870                 //              Set a property on a widget
3871                 //      name:
3872                 //              The property to set. 
3873                 //      value:
3874                 //              The value to set in the property.
3875                 // description:
3876                 //              Sets named properties on a widget which may potentially be handled by a 
3877                 //              setter in the widget.
3878                 //              For example, if the widget has a properties "foo"
3879                 //              and "bar" and a method named "_setFooAttr", calling:
3880                 //      |       myWidget.set("foo", "Howdy!");
3881                 //              would be equivalent to writing:
3882                 //      |       widget._setFooAttr("Howdy!");
3883                 //              and:
3884                 //      |       myWidget.set("bar", 3);
3885                 //              would be equivalent to writing:
3886                 //      |       widget.bar = 3;
3887                 //
3888                 //      set() may also be called with a hash of name/value pairs, ex:
3889                 //      |       myWidget.set({
3890                 //      |               foo: "Howdy",
3891                 //      |               bar: 3
3892                 //      |       })
3893                 //      This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
3894
3895                 if(typeof name === "object"){
3896                         for(var x in name){
3897                                 this.set(x, name[x]); 
3898                         }
3899                         return this;
3900                 }
3901                 var names = this._getAttrNames(name);
3902                 if(this[names.s]){
3903                         // use the explicit setter
3904                         var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
3905                 }else{
3906                         // if param is specified as DOM node attribute, copy it
3907                         if(name in this.attributeMap){
3908                                 this._attrToDom(name, value);
3909                         }
3910                         var oldValue = this[name];
3911                         // FIXME: what about function assignments? Any way to connect() here?
3912                         this[name] = value;
3913                 }
3914                 return result || this;
3915         },
3916         
3917         _attrPairNames: {},             // shared between all widgets
3918         _getAttrNames: function(name){
3919                 // summary:
3920                 //              Helper function for get() and set().
3921                 //              Caches attribute name values so we don't do the string ops every time.
3922                 // tags:
3923                 //              private
3924
3925                 var apn = this._attrPairNames;
3926                 if(apn[name]){ return apn[name]; }
3927                 var uc = name.charAt(0).toUpperCase() + name.substr(1);
3928                 return (apn[name] = {
3929                         n: name+"Node",
3930                         s: "_set"+uc+"Attr",
3931                         g: "_get"+uc+"Attr"
3932                 });
3933         },
3934
3935         toString: function(){
3936                 // summary:
3937                 //              Returns a string that represents the widget
3938                 // description:
3939                 //              When a widget is cast to a string, this method will be used to generate the
3940                 //              output. Currently, it does not implement any sort of reversible
3941                 //              serialization.
3942                 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
3943         },
3944
3945         getDescendants: function(){
3946                 // summary:
3947                 //              Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
3948                 //              This method should generally be avoided as it returns widgets declared in templates, which are
3949                 //              supposed to be internal/hidden, but it's left here for back-compat reasons.
3950
3951                 return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[]
3952         },
3953
3954         getChildren: function(){
3955                 // summary:
3956                 //              Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
3957                 //              Does not return nested widgets, nor widgets that are part of this widget's template.
3958                 return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[]
3959         },
3960
3961         // nodesWithKeyClick: [private] String[]
3962         //              List of nodes that correctly handle click events via native browser support,
3963         //              and don't need dijit's help
3964         nodesWithKeyClick: ["input", "button"],
3965
3966         connect: function(
3967                         /*Object|null*/ obj,
3968                         /*String|Function*/ event,
3969                         /*String|Function*/ method){
3970                 // summary:
3971                 //              Connects specified obj/event to specified method of this object
3972                 //              and registers for disconnect() on widget destroy.
3973                 // description:
3974                 //              Provide widget-specific analog to dojo.connect, except with the
3975                 //              implicit use of this widget as the target object.
3976                 //              This version of connect also provides a special "ondijitclick"
3977                 //              event which triggers on a click or space or enter keyup
3978                 // returns:
3979                 //              A handle that can be passed to `disconnect` in order to disconnect before
3980                 //              the widget is destroyed.
3981                 // example:
3982                 //      |       var btn = new dijit.form.Button();
3983                 //      |       // when foo.bar() is called, call the listener we're going to
3984                 //      |       // provide in the scope of btn
3985                 //      |       btn.connect(foo, "bar", function(){
3986                 //      |               console.debug(this.toString());
3987                 //      |       });
3988                 // tags:
3989                 //              protected
3990
3991                 var d = dojo,
3992                         dc = d._connect,
3993                         handles = [];
3994                 if(event == "ondijitclick"){
3995                         // add key based click activation for unsupported nodes.
3996                         // do all processing onkey up to prevent spurious clicks
3997                         // for details see comments at top of this file where _lastKeyDownNode is defined
3998                         if(dojo.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button
3999                                 var m = d.hitch(this, method);
4000                                 handles.push(
4001                                         dc(obj, "onkeydown", this, function(e){
4002                                                 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
4003                                                 if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
4004                                                         !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
4005                                                         // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
4006                                                         dijit._lastKeyDownNode = e.target;
4007                                                         e.preventDefault();             // stop event to prevent scrolling on space key in IE
4008                                                 }
4009                                         }),
4010                                         dc(obj, "onkeyup", this, function(e){
4011                                                 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
4012                                                 if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
4013                                                         e.target === dijit._lastKeyDownNode &&
4014                                                         !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
4015                                                                 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
4016                                                                 dijit._lastKeyDownNode = null;
4017                                                                 return m(e);
4018                                                 }
4019                                         })
4020                                 );
4021                         }
4022                         event = "onclick";
4023                 }
4024                 handles.push(dc(obj, event, this, method));
4025
4026                 this._connects.push(handles);
4027                 return handles;         // _Widget.Handle
4028         },
4029
4030         disconnect: function(/* _Widget.Handle */ handles){
4031                 // summary:
4032                 //              Disconnects handle created by `connect`.
4033                 //              Also removes handle from this widget's list of connects.
4034                 // tags:
4035                 //              protected
4036                 for(var i=0; i<this._connects.length; i++){
4037                         if(this._connects[i] == handles){
4038                                 dojo.forEach(handles, dojo.disconnect);
4039                                 this._connects.splice(i, 1);
4040                                 return;
4041                         }
4042                 }
4043         },
4044
4045         subscribe: function(
4046                         /*String*/ topic,
4047                         /*String|Function*/ method){
4048                 // summary:
4049                 //              Subscribes to the specified topic and calls the specified method
4050                 //              of this object and registers for unsubscribe() on widget destroy.
4051                 // description:
4052                 //              Provide widget-specific analog to dojo.subscribe, except with the
4053                 //              implicit use of this widget as the target object.
4054                 // example:
4055                 //      |       var btn = new dijit.form.Button();
4056                 //      |       // when /my/topic is published, this button changes its label to
4057                 //      |   // be the parameter of the topic.
4058                 //      |       btn.subscribe("/my/topic", function(v){
4059                 //      |               this.set("label", v);
4060                 //      |       });
4061                 var d = dojo,
4062                         handle = d.subscribe(topic, this, method);
4063
4064                 // return handles for Any widget that may need them
4065                 this._subscribes.push(handle);
4066                 return handle;
4067         },
4068
4069         unsubscribe: function(/*Object*/ handle){
4070                 // summary:
4071                 //              Unsubscribes handle created by this.subscribe.
4072                 //              Also removes handle from this widget's list of subscriptions
4073                 for(var i=0; i<this._subscribes.length; i++){
4074                         if(this._subscribes[i] == handle){
4075                                 dojo.unsubscribe(handle);
4076                                 this._subscribes.splice(i, 1);
4077                                 return;
4078                         }
4079                 }
4080         },
4081
4082         isLeftToRight: function(){
4083                 // summary:
4084                 //              Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
4085                 // tags:
4086                 //              protected
4087                 return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean
4088         },
4089
4090         isFocusable: function(){
4091                 // summary:
4092                 //              Return true if this widget can currently be focused
4093                 //              and false if not
4094                 return this.focus && (dojo.style(this.domNode, "display") != "none");
4095         },
4096
4097         placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
4098                 // summary:
4099                 //              Place this widget's domNode reference somewhere in the DOM based
4100                 //              on standard dojo.place conventions, or passing a Widget reference that
4101                 //              contains and addChild member.
4102                 //
4103                 // description:
4104                 //              A convenience function provided in all _Widgets, providing a simple
4105                 //              shorthand mechanism to put an existing (or newly created) Widget
4106                 //              somewhere in the dom, and allow chaining.
4107                 //
4108                 // reference:
4109                 //              The String id of a domNode, a domNode reference, or a reference to a Widget posessing
4110                 //              an addChild method.
4111                 //
4112                 // position:
4113                 //              If passed a string or domNode reference, the position argument
4114                 //              accepts a string just as dojo.place does, one of: "first", "last",
4115                 //              "before", or "after".
4116                 //
4117                 //              If passed a _Widget reference, and that widget reference has an ".addChild" method,
4118                 //              it will be called passing this widget instance into that method, supplying the optional
4119                 //              position index passed.
4120                 //
4121                 // returns:
4122                 //              dijit._Widget
4123                 //              Provides a useful return of the newly created dijit._Widget instance so you
4124                 //              can "chain" this function by instantiating, placing, then saving the return value
4125                 //              to a variable.
4126                 //
4127                 // example:
4128                 // |    // create a Button with no srcNodeRef, and place it in the body:
4129                 // |    var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body());
4130                 // |    // now, 'button' is still the widget reference to the newly created button
4131                 // |    dojo.connect(button, "onClick", function(e){ console.log('click'); });
4132                 //
4133                 // example:
4134                 // |    // create a button out of a node with id="src" and append it to id="wrapper":
4135                 // |    var button = new dijit.form.Button({},"src").placeAt("wrapper");
4136                 //
4137                 // example:
4138                 // |    // place a new button as the first element of some div
4139                 // |    var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
4140                 //
4141                 // example:
4142                 // |    // create a contentpane and add it to a TabContainer
4143                 // |    var tc = dijit.byId("myTabs");
4144                 // |    new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
4145
4146                 if(reference.declaredClass && reference.addChild){
4147                         reference.addChild(this, position);
4148                 }else{
4149                         dojo.place(this.domNode, reference, position);
4150                 }
4151                 return this;
4152         },
4153
4154         _onShow: function(){
4155                 // summary:
4156                 //              Internal method called when this widget is made visible.
4157                 //              See `onShow` for details.
4158                 this.onShow();
4159         },
4160
4161         onShow: function(){
4162                 // summary:
4163                 //              Called when this widget becomes the selected pane in a
4164                 //              `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
4165                 //              `dijit.layout.AccordionContainer`, etc.
4166                 //
4167                 //              Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
4168                 // tags:
4169                 //              callback
4170         },
4171
4172         onHide: function(){
4173                 // summary:
4174                         //              Called when another widget becomes the selected pane in a
4175                         //              `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
4176                         //              `dijit.layout.AccordionContainer`, etc.
4177                         //
4178                         //              Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
4179                         // tags:
4180                         //              callback
4181         },
4182
4183         onClose: function(){
4184                 // summary:
4185                 //              Called when this widget is being displayed as a popup (ex: a Calendar popped
4186                 //              up from a DateTextBox), and it is hidden.
4187                 //              This is called from the dijit.popup code, and should not be called directly.
4188                 //
4189                 //              Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
4190                 //              Callback if a user tries to close the child.   Child will be closed if this function returns true.
4191                 // tags:
4192                 //              extension
4193
4194                 return true;            // Boolean
4195         }
4196 });
4197
4198 })();
4199
4200 }
4201
4202 if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4203 dojo._hasResource["dojo.string"] = true;
4204 dojo.provide("dojo.string");
4205
4206 /*=====
4207 dojo.string = { 
4208         // summary: String utilities for Dojo
4209 };
4210 =====*/
4211
4212 dojo.string.rep = function(/*String*/str, /*Integer*/num){
4213         //      summary:
4214         //              Efficiently replicate a string `n` times.
4215         //      str:
4216         //              the string to replicate
4217         //      num:
4218         //              number of times to replicate the string
4219         
4220         if(num <= 0 || !str){ return ""; }
4221         
4222         var buf = [];
4223         for(;;){
4224                 if(num & 1){
4225                         buf.push(str);
4226                 }
4227                 if(!(num >>= 1)){ break; }
4228                 str += str;
4229         }
4230         return buf.join("");    // String
4231 };
4232
4233 dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
4234         //      summary:
4235         //              Pad a string to guarantee that it is at least `size` length by
4236         //              filling with the character `ch` at either the start or end of the
4237         //              string. Pads at the start, by default.
4238         //      text:
4239         //              the string to pad
4240         //      size:
4241         //              length to provide padding
4242         //      ch:
4243         //              character to pad, defaults to '0'
4244         //      end:
4245         //              adds padding at the end if true, otherwise pads at start
4246         //      example:
4247         //      |       // Fill the string to length 10 with "+" characters on the right.  Yields "Dojo++++++".
4248         //      |       dojo.string.pad("Dojo", 10, "+", true);
4249
4250         if(!ch){
4251                 ch = '0';
4252         }
4253         var out = String(text),
4254                 pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
4255         return end ? out + pad : pad + out;     // String
4256 };
4257
4258 dojo.string.substitute = function(      /*String*/              template, 
4259                                                                         /*Object|Array*/map, 
4260                                                                         /*Function?*/   transform, 
4261                                                                         /*Object?*/             thisObject){
4262         //      summary:
4263         //              Performs parameterized substitutions on a string. Throws an
4264         //              exception if any parameter is unmatched.
4265         //      template: 
4266         //              a string with expressions in the form `${key}` to be replaced or
4267         //              `${key:format}` which specifies a format function. keys are case-sensitive. 
4268         //      map:
4269         //              hash to search for substitutions
4270         //      transform: 
4271         //              a function to process all parameters before substitution takes
4272         //              place, e.g. mylib.encodeXML
4273         //      thisObject: 
4274         //              where to look for optional format function; default to the global
4275         //              namespace
4276         //      example:
4277         //              Substitutes two expressions in a string from an Array or Object
4278         //      |       // returns "File 'foo.html' is not found in directory '/temp'."
4279         //      |       // by providing substitution data in an Array
4280         //      |       dojo.string.substitute(
4281         //      |               "File '${0}' is not found in directory '${1}'.",
4282         //      |               ["foo.html","/temp"]
4283         //      |       );
4284         //      |
4285         //      |       // also returns "File 'foo.html' is not found in directory '/temp'."
4286         //      |       // but provides substitution data in an Object structure.  Dotted
4287         //      |       // notation may be used to traverse the structure.
4288         //      |       dojo.string.substitute(
4289         //      |               "File '${name}' is not found in directory '${info.dir}'.",
4290         //      |               { name: "foo.html", info: { dir: "/temp" } }
4291         //      |       );
4292         //      example:
4293         //              Use a transform function to modify the values:
4294         //      |       // returns "file 'foo.html' is not found in directory '/temp'."
4295         //      |       dojo.string.substitute(
4296         //      |               "${0} is not found in ${1}.",
4297         //      |               ["foo.html","/temp"],
4298         //      |               function(str){
4299         //      |                       // try to figure out the type
4300         //      |                       var prefix = (str.charAt(0) == "/") ? "directory": "file";
4301         //      |                       return prefix + " '" + str + "'";
4302         //      |               }
4303         //      |       );
4304         //      example:
4305         //              Use a formatter
4306         //      |       // returns "thinger -- howdy"
4307         //      |       dojo.string.substitute(
4308         //      |               "${0:postfix}", ["thinger"], null, {
4309         //      |                       postfix: function(value, key){
4310         //      |                               return value + " -- howdy";
4311         //      |                       }
4312         //      |               }
4313         //      |       );
4314
4315         thisObject = thisObject || dojo.global;
4316         transform = transform ? 
4317                 dojo.hitch(thisObject, transform) : function(v){ return v; };
4318
4319         return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
4320                 function(match, key, format){
4321                         var value = dojo.getObject(key, false, map);
4322                         if(format){
4323                                 value = dojo.getObject(format, false, thisObject).call(thisObject, value, key);
4324                         }
4325                         return transform(value, key).toString();
4326                 }); // String
4327 };
4328
4329 /*=====
4330 dojo.string.trim = function(str){
4331         //      summary:
4332         //              Trims whitespace from both sides of the string
4333         //      str: String
4334         //              String to be trimmed
4335         //      returns: String
4336         //              Returns the trimmed string
4337         //      description:
4338         //              This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
4339         //              The short yet performant version of this function is dojo.trim(),
4340         //              which is part of Dojo base.  Uses String.prototype.trim instead, if available.
4341         return "";      // String
4342 }
4343 =====*/
4344
4345 dojo.string.trim = String.prototype.trim ?
4346         dojo.trim : // aliasing to the native function
4347         function(str){
4348                 str = str.replace(/^\s+/, '');
4349                 for(var i = str.length - 1; i >= 0; i--){
4350                         if(/\S/.test(str.charAt(i))){
4351                                 str = str.substring(0, i + 1);
4352                                 break;
4353                         }
4354                 }
4355                 return str;
4356         };
4357
4358 }
4359
4360 if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4361 dojo._hasResource["dojo.cache"] = true;
4362 dojo.provide("dojo.cache");
4363
4364 /*=====
4365 dojo.cache = { 
4366         // summary:
4367         //              A way to cache string content that is fetchable via `dojo.moduleUrl`.
4368 };
4369 =====*/
4370
4371 (function(){
4372         var cache = {};
4373         dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
4374                 // summary:
4375                 //              A getter and setter for storing the string content associated with the
4376                 //              module and url arguments.
4377                 // description:
4378                 //              module and url are used to call `dojo.moduleUrl()` to generate a module URL.
4379                 //              If value is specified, the cache value for the moduleUrl will be set to
4380                 //              that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
4381                 //              in its internal cache and return that cached value for the URL. To clear
4382                 //              a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
4383                 //              the URL contents, only modules on the same domain of the page can use this capability.
4384                 //              The build system can inline the cache values though, to allow for xdomain hosting.
4385                 // module: String||Object
4386                 //              If a String, the module name to use for the base part of the URL, similar to module argument
4387                 //              to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
4388                 //              generates a valid path for the cache item. For example, a dojo._Url object.
4389                 // url: String
4390                 //              The rest of the path to append to the path derived from the module argument. If
4391                 //              module is an object, then this second argument should be the "value" argument instead.
4392                 // value: String||Object?
4393                 //              If a String, the value to use in the cache for the module/url combination.
4394                 //              If an Object, it can have two properties: value and sanitize. The value property
4395                 //              should be the value to use in the cache, and sanitize can be set to true or false,
4396                 //              to indicate if XML declarations should be removed from the value and if the HTML
4397                 //              inside a body tag in the value should be extracted as the real value. The value argument
4398                 //              or the value property on the value argument are usually only used by the build system
4399                 //              as it inlines cache content.
4400                 //      example:
4401                 //              To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
4402                 //              of call is used to avoid an issue with the build system erroneously trying to intern
4403                 //              this example. To get the build system to intern your dojo.cache calls, use the
4404                 //              "dojo.cache" style of call):
4405                 //              |       //If template.html contains "<h1>Hello</h1>" that will be
4406                 //              |       //the value for the text variable.
4407                 //              |       var text = dojo["cache"]("my.module", "template.html");
4408                 //      example:
4409                 //              To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
4410                 //               (the dojo["cache"] style of call is used to avoid an issue with the build system 
4411                 //              erroneously trying to intern this example. To get the build system to intern your
4412                 //              dojo.cache calls, use the "dojo.cache" style of call):
4413                 //              |       //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
4414                 //              |       //text variable will contain just "<h1>Hello</h1>".
4415                 //              |       var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
4416                 //      example:
4417                 //              Same example as previous, but demostrates how an object can be passed in as
4418                 //              the first argument, then the value argument can then be the second argument.
4419                 //              |       //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
4420                 //              |       //text variable will contain just "<h1>Hello</h1>".
4421                 //              |       var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
4422
4423                 //Module could be a string, or an object that has a toString() method
4424                 //that will return a useful path. If it is an object, then the "url" argument
4425                 //will actually be the value argument.
4426                 if(typeof module == "string"){
4427                         var pathObj = dojo.moduleUrl(module, url);
4428                 }else{
4429                         pathObj = module;
4430                         value = url;
4431                 }
4432                 var key = pathObj.toString();
4433
4434                 var val = value;
4435                 if(value != undefined && !dojo.isString(value)){
4436                         val = ("value" in value ? value.value : undefined);
4437                 }
4438
4439                 var sanitize = value && value.sanitize ? true : false;
4440
4441                 if(typeof val == "string"){
4442                         //We have a string, set cache value
4443                         val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
4444                 }else if(val === null){
4445                         //Remove cached value
4446                         delete cache[key];
4447                 }else{
4448                         //Allow cache values to be empty strings. If key property does
4449                         //not exist, fetch it.
4450                         if(!(key in cache)){
4451                                 val = dojo._getText(key);
4452                                 cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
4453                         }
4454                         val = cache[key];
4455                 }
4456                 return val; //String
4457         };
4458
4459         dojo.cache._sanitize = function(/*String*/val){
4460                 // summary: 
4461                 //              Strips <?xml ...?> declarations so that external SVG and XML
4462                 //              documents can be added to a document without worry. Also, if the string
4463                 //              is an HTML document, only the part inside the body tag is returned.
4464                 // description:
4465                 //              Copied from dijit._Templated._sanitizeTemplateString.
4466                 if(val){
4467                         val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
4468                         var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
4469                         if(matches){
4470                                 val = matches[1];
4471                         }
4472                 }else{
4473                         val = "";
4474                 }
4475                 return val; //String
4476         };
4477 })();
4478
4479 }
4480
4481 if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4482 dojo._hasResource["dijit._Templated"] = true;
4483 dojo.provide("dijit._Templated");
4484
4485
4486
4487
4488
4489
4490 dojo.declare("dijit._Templated",
4491         null,
4492         {
4493                 // summary:
4494                 //              Mixin for widgets that are instantiated from a template
4495
4496                 // templateString: [protected] String
4497                 //              A string that represents the widget template. Pre-empts the
4498                 //              templatePath. In builds that have their strings "interned", the
4499                 //              templatePath is converted to an inline templateString, thereby
4500                 //              preventing a synchronous network call.
4501                 //
4502                 //              Use in conjunction with dojo.cache() to load from a file.
4503                 templateString: null,
4504
4505                 // templatePath: [protected deprecated] String
4506                 //              Path to template (HTML file) for this widget relative to dojo.baseUrl.
4507                 //              Deprecated: use templateString with dojo.cache() instead.
4508                 templatePath: null,
4509
4510                 // widgetsInTemplate: [protected] Boolean
4511                 //              Should we parse the template to find widgets that might be
4512                 //              declared in markup inside it?  False by default.
4513                 widgetsInTemplate: false,
4514
4515                 // skipNodeCache: [protected] Boolean
4516                 //              If using a cached widget template node poses issues for a
4517                 //              particular widget class, it can set this property to ensure
4518                 //              that its template is always re-built from a string
4519                 _skipNodeCache: false,
4520
4521                 // _earlyTemplatedStartup: Boolean
4522                 //              A fallback to preserve the 1.0 - 1.3 behavior of children in
4523                 //              templates having their startup called before the parent widget
4524                 //              fires postCreate. Defaults to 'false', causing child widgets to
4525                 //              have their .startup() called immediately before a parent widget
4526                 //              .startup(), but always after the parent .postCreate(). Set to
4527                 //              'true' to re-enable to previous, arguably broken, behavior.
4528                 _earlyTemplatedStartup: false,
4529
4530                 // _attachPoints: [private] String[]
4531                 //              List of widget attribute names associated with dojoAttachPoint=... in the
4532                 //              template, ex: ["containerNode", "labelNode"]
4533 /*=====
4534                 _attachPoints: [],
4535  =====*/
4536
4537                 constructor: function(){
4538                         this._attachPoints = [];
4539                 },
4540
4541                 _stringRepl: function(tmpl){
4542                         // summary:
4543                         //              Does substitution of ${foo} type properties in template string
4544                         // tags:
4545                         //              private
4546                         var className = this.declaredClass, _this = this;
4547                         // Cache contains a string because we need to do property replacement
4548                         // do the property replacement
4549                         return dojo.string.substitute(tmpl, this, function(value, key){
4550                                 if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); }
4551                                 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
4552                                 if(value == null){ return ""; }
4553
4554                                 // Substitution keys beginning with ! will skip the transform step,
4555                                 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
4556                                 return key.charAt(0) == "!" ? value :
4557                                         // Safer substitution, see heading "Attribute values" in
4558                                         // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
4559                                         value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
4560                         }, this);
4561                 },
4562
4563                 // method over-ride
4564                 buildRendering: function(){
4565                         // summary:
4566                         //              Construct the UI for this widget from a template, setting this.domNode.
4567                         // tags:
4568                         //              protected
4569
4570                         // Lookup cached version of template, and download to cache if it
4571                         // isn't there already.  Returns either a DomNode or a string, depending on
4572                         // whether or not the template contains ${foo} replacement parameters.
4573                         var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
4574
4575                         var node;
4576                         if(dojo.isString(cached)){
4577                                 node = dojo._toDom(this._stringRepl(cached));
4578                                 if(node.nodeType != 1){
4579                                         // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
4580                                         throw new Error("Invalid template: " + cached);
4581                                 }
4582                         }else{
4583                                 // if it's a node, all we have to do is clone it
4584                                 node = cached.cloneNode(true);
4585                         }
4586
4587                         this.domNode = node;
4588
4589                         // recurse through the node, looking for, and attaching to, our
4590                         // attachment points and events, which should be defined on the template node.
4591                         this._attachTemplateNodes(node);
4592
4593                         if(this.widgetsInTemplate){
4594                                 // Make sure dojoType is used for parsing widgets in template.
4595                                 // The dojo.parser.query could be changed from multiversion support.
4596                                 var parser = dojo.parser, qry, attr;
4597                                 if(parser._query != "[dojoType]"){
4598                                         qry = parser._query;
4599                                         attr = parser._attrName;
4600                                         parser._query = "[dojoType]";
4601                                         parser._attrName = "dojoType";
4602                                 }
4603
4604                                 // Store widgets that we need to start at a later point in time
4605                                 var cw = (this._startupWidgets = dojo.parser.parse(node, {
4606                                         noStart: !this._earlyTemplatedStartup,
4607                                         inherited: {dir: this.dir, lang: this.lang}
4608                                 }));
4609
4610                                 // Restore the query.
4611                                 if(qry){
4612                                         parser._query = qry;
4613                                         parser._attrName = attr;
4614                                 }
4615
4616                                 this._supportingWidgets = dijit.findWidgets(node);
4617
4618                                 this._attachTemplateNodes(cw, function(n,p){
4619                                         return n[p];
4620                                 });
4621                         }
4622
4623                         this._fillContent(this.srcNodeRef);
4624                 },
4625
4626                 _fillContent: function(/*DomNode*/ source){
4627                         // summary:
4628                         //              Relocate source contents to templated container node.
4629                         //              this.containerNode must be able to receive children, or exceptions will be thrown.
4630                         // tags:
4631                         //              protected
4632                         var dest = this.containerNode;
4633                         if(source && dest){
4634                                 while(source.hasChildNodes()){
4635                                         dest.appendChild(source.firstChild);
4636                                 }
4637                         }
4638                 },
4639
4640                 _attachTemplateNodes: function(rootNode, getAttrFunc){
4641                         // summary:
4642                         //              Iterate through the template and attach functions and nodes accordingly.
4643                         // description:
4644                         //              Map widget properties and functions to the handlers specified in
4645                         //              the dom node and it's descendants. This function iterates over all
4646                         //              nodes and looks for these properties:
4647                         //                      * dojoAttachPoint
4648                         //                      * dojoAttachEvent
4649                         //                      * waiRole
4650                         //                      * waiState
4651                         // rootNode: DomNode|Array[Widgets]
4652                         //              the node to search for properties. All children will be searched.
4653                         // getAttrFunc: Function?
4654                         //              a function which will be used to obtain property for a given
4655                         //              DomNode/Widget
4656                         // tags:
4657                         //              private
4658
4659                         getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
4660
4661                         var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
4662                         var x = dojo.isArray(rootNode) ? 0 : -1;
4663                         for(; x<nodes.length; x++){
4664                                 var baseNode = (x == -1) ? rootNode : nodes[x];
4665                                 if(this.widgetsInTemplate && getAttrFunc(baseNode, "dojoType")){
4666                                         continue;
4667                                 }
4668                                 // Process dojoAttachPoint
4669                                 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
4670                                 if(attachPoint){
4671                                         var point, points = attachPoint.split(/\s*,\s*/);
4672                                         while((point = points.shift())){
4673                                                 if(dojo.isArray(this[point])){
4674                                                         this[point].push(baseNode);
4675                                                 }else{
4676                                                         this[point]=baseNode;
4677                                                 }
4678                                                 this._attachPoints.push(point);
4679                                         }
4680                                 }
4681
4682                                 // Process dojoAttachEvent
4683                                 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
4684                                 if(attachEvent){
4685                                         // NOTE: we want to support attributes that have the form
4686                                         // "domEvent: nativeEvent; ..."
4687                                         var event, events = attachEvent.split(/\s*,\s*/);
4688                                         var trim = dojo.trim;
4689                                         while((event = events.shift())){
4690                                                 if(event){
4691                                                         var thisFunc = null;
4692                                                         if(event.indexOf(":") != -1){
4693                                                                 // oh, if only JS had tuple assignment
4694                                                                 var funcNameArr = event.split(":");
4695                                                                 event = trim(funcNameArr[0]);
4696                                                                 thisFunc = trim(funcNameArr[1]);
4697                                                         }else{
4698                                                                 event = trim(event);
4699                                                         }
4700                                                         if(!thisFunc){
4701                                                                 thisFunc = event;
4702                                                         }
4703                                                         this.connect(baseNode, event, thisFunc);
4704                                                 }
4705                                         }
4706                                 }
4707
4708                                 // waiRole, waiState
4709                                 var role = getAttrFunc(baseNode, "waiRole");
4710                                 if(role){
4711                                         dijit.setWaiRole(baseNode, role);
4712                                 }
4713                                 var values = getAttrFunc(baseNode, "waiState");
4714                                 if(values){
4715                                         dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
4716                                                 if(stateValue.indexOf('-') != -1){
4717                                                         var pair = stateValue.split('-');
4718                                                         dijit.setWaiState(baseNode, pair[0], pair[1]);
4719                                                 }
4720                                         });
4721                                 }
4722                         }
4723                 },
4724
4725                 startup: function(){
4726                         dojo.forEach(this._startupWidgets, function(w){
4727                                 if(w && !w._started && w.startup){
4728                                         w.startup();
4729                                 }
4730                         });
4731                         this.inherited(arguments);
4732                 },
4733
4734                 destroyRendering: function(){
4735                         // Delete all attach points to prevent IE6 memory leaks.
4736                         dojo.forEach(this._attachPoints, function(point){
4737                                 delete this[point];
4738                         }, this);
4739                         this._attachPoints = [];
4740
4741                         this.inherited(arguments);
4742                 }
4743         }
4744 );
4745
4746 // key is either templatePath or templateString; object is either string or DOM tree
4747 dijit._Templated._templateCache = {};
4748
4749 dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
4750         // summary:
4751         //              Static method to get a template based on the templatePath or
4752         //              templateString key
4753         // templatePath: String||dojo.uri.Uri
4754         //              The URL to get the template from.
4755         // templateString: String?
4756         //              a string to use in lieu of fetching the template from a URL. Takes precedence
4757         //              over templatePath
4758         // returns: Mixed
4759         //              Either string (if there are ${} variables that need to be replaced) or just
4760         //              a DOM tree (if the node can be cloned directly)
4761
4762         // is it already cached?
4763         var tmplts = dijit._Templated._templateCache;
4764         var key = templateString || templatePath;
4765         var cached = tmplts[key];
4766         if(cached){
4767                 try{
4768                         // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value
4769                         if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
4770                                 // string or node of the same document
4771                                 return cached;
4772                         }
4773                 }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
4774                 dojo.destroy(cached);
4775         }
4776
4777         // If necessary, load template string from template path
4778         if(!templateString){
4779                 templateString = dojo.cache(templatePath, {sanitize: true});
4780         }
4781         templateString = dojo.string.trim(templateString);
4782
4783         if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
4784                 // there are variables in the template so all we can do is cache the string
4785                 return (tmplts[key] = templateString); //String
4786         }else{
4787                 // there are no variables in the template so we can cache the DOM tree
4788                 var node = dojo._toDom(templateString);
4789                 if(node.nodeType != 1){
4790                         throw new Error("Invalid template: " + templateString);
4791                 }
4792                 return (tmplts[key] = node); //Node
4793         }
4794 };
4795
4796 if(dojo.isIE){
4797         dojo.addOnWindowUnload(function(){
4798                 var cache = dijit._Templated._templateCache;
4799                 for(var key in cache){
4800                         var value = cache[key];
4801                         if(typeof value == "object"){ // value is either a string or a DOM node template
4802                                 dojo.destroy(value);
4803                         }
4804                         delete cache[key];
4805                 }
4806         });
4807 }
4808
4809 // These arguments can be specified for widgets which are used in templates.
4810 // Since any widget can be specified as sub widgets in template, mix it
4811 // into the base widget class.  (This is a hack, but it's effective.)
4812 dojo.extend(dijit._Widget,{
4813         dojoAttachEvent: "",
4814         dojoAttachPoint: "",
4815         waiRole: "",
4816         waiState:""
4817 });
4818
4819 }
4820
4821 if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4822 dojo._hasResource["dijit._Container"] = true;
4823 dojo.provide("dijit._Container");
4824
4825 dojo.declare("dijit._Container",
4826         null,
4827         {
4828                 // summary:
4829                 //              Mixin for widgets that contain a set of widget children.
4830                 // description:
4831                 //              Use this mixin for widgets that needs to know about and
4832                 //              keep track of their widget children. Suitable for widgets like BorderContainer
4833                 //              and TabContainer which contain (only) a set of child widgets.
4834                 //
4835                 //              It's not suitable for widgets like ContentPane
4836                 //              which contains mixed HTML (plain DOM nodes in addition to widgets),
4837                 //              and where contained widgets are not necessarily directly below
4838                 //              this.containerNode.   In that case calls like addChild(node, position)
4839                 //              wouldn't make sense.
4840
4841                 // isContainer: [protected] Boolean
4842                 //              Indicates that this widget acts as a "parent" to the descendant widgets.
4843                 //              When the parent is started it will call startup() on the child widgets.
4844                 //              See also `isLayoutContainer`.
4845                 isContainer: true,
4846
4847                 buildRendering: function(){
4848                         this.inherited(arguments);
4849                         if(!this.containerNode){
4850                                 // all widgets with descendants must set containerNode
4851                                         this.containerNode = this.domNode;
4852                         }
4853                 },
4854
4855                 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
4856                         // summary:
4857                         //              Makes the given widget a child of this widget.
4858                         // description:
4859                         //              Inserts specified child widget's dom node as a child of this widget's
4860                         //              container node, and possibly does other processing (such as layout).
4861
4862                         var refNode = this.containerNode;
4863                         if(insertIndex && typeof insertIndex == "number"){
4864                                 var children = this.getChildren();
4865                                 if(children && children.length >= insertIndex){
4866                                         refNode = children[insertIndex-1].domNode;
4867                                         insertIndex = "after";
4868                                 }
4869                         }
4870                         dojo.place(widget.domNode, refNode, insertIndex);
4871
4872                         // If I've been started but the child widget hasn't been started,
4873                         // start it now.  Make sure to do this after widget has been
4874                         // inserted into the DOM tree, so it can see that it's being controlled by me,
4875                         // so it doesn't try to size itself.
4876                         if(this._started && !widget._started){
4877                                 widget.startup();
4878                         }
4879                 },
4880
4881                 removeChild: function(/*Widget or int*/ widget){
4882                         // summary:
4883                         //              Removes the passed widget instance from this widget but does
4884                         //              not destroy it.  You can also pass in an integer indicating
4885                         //              the index within the container to remove
4886
4887                         if(typeof widget == "number" && widget > 0){
4888                                 widget = this.getChildren()[widget];
4889                         }
4890
4891                         if(widget){
4892                                 var node = widget.domNode;
4893                                 if(node && node.parentNode){
4894                                         node.parentNode.removeChild(node); // detach but don't destroy
4895                                 }
4896                         }
4897                 },
4898
4899                 hasChildren: function(){
4900                         // summary:
4901                         //              Returns true if widget has children, i.e. if this.containerNode contains something.
4902                         return this.getChildren().length > 0;   // Boolean
4903                 },
4904
4905                 destroyDescendants: function(/*Boolean*/ preserveDom){
4906                         // summary:
4907                         //      Destroys all the widgets inside this.containerNode,
4908                         //      but not this widget itself
4909                         dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); });
4910                 },
4911
4912                 _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
4913                         // summary:
4914                         //              Get the next or previous widget sibling of child
4915                         // dir:
4916                         //              if 1, get the next sibling
4917                         //              if -1, get the previous sibling
4918                         // tags:
4919                         //      private
4920                         var node = child.domNode,
4921                                 which = (dir>0 ? "nextSibling" : "previousSibling");
4922                         do{
4923                                 node = node[which];
4924                         }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
4925                         return node && dijit.byNode(node);      // dijit._Widget
4926                 },
4927
4928                 getIndexOfChild: function(/*dijit._Widget*/ child){
4929                         // summary:
4930                         //              Gets the index of the child in this container or -1 if not found
4931                         return dojo.indexOf(this.getChildren(), child); // int
4932                 },
4933
4934                 startup: function(){
4935                         // summary:
4936                         //              Called after all the widgets have been instantiated and their
4937                         //              dom nodes have been inserted somewhere under dojo.doc.body.
4938                         //
4939                         //              Widgets should override this method to do any initialization
4940                         //              dependent on other widgets existing, and then call
4941                         //              this superclass method to finish things off.
4942                         //
4943                         //              startup() in subclasses shouldn't do anything
4944                         //              size related because the size of the widget hasn't been set yet.
4945
4946                         if(this._started){ return; }
4947
4948                         // Startup all children of this widget
4949                         dojo.forEach(this.getChildren(), function(child){ child.startup(); });
4950
4951                         this.inherited(arguments);
4952                 }
4953         }
4954 );
4955
4956 }
4957
4958 if(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4959 dojo._hasResource["dijit._Contained"] = true;
4960 dojo.provide("dijit._Contained");
4961
4962 dojo.declare("dijit._Contained",
4963                 null,
4964                 {
4965                         // summary:
4966                         //              Mixin for widgets that are children of a container widget
4967                         //
4968                         // example:
4969                         // |    // make a basic custom widget that knows about it's parents
4970                         // |    dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
4971
4972                         getParent: function(){
4973                                 // summary:
4974                                 //              Returns the parent widget of this widget, assuming the parent
4975                                 //              specifies isContainer
4976                                 var parent = dijit.getEnclosingWidget(this.domNode.parentNode);
4977                                 return parent && parent.isContainer ? parent : null;
4978                         },
4979
4980                         _getSibling: function(/*String*/ which){
4981                                 // summary:
4982                                 //      Returns next or previous sibling
4983                                 // which:
4984                                 //      Either "next" or "previous"
4985                                 // tags:
4986                                 //      private
4987                                 var node = this.domNode;
4988                                 do{
4989                                         node = node[which+"Sibling"];
4990                                 }while(node && node.nodeType != 1);
4991                                 return node && dijit.byNode(node);      // dijit._Widget
4992                         },
4993
4994                         getPreviousSibling: function(){
4995                                 // summary:
4996                                 //              Returns null if this is the first child of the parent,
4997                                 //              otherwise returns the next element sibling to the "left".
4998
4999                                 return this._getSibling("previous"); // dijit._Widget
5000                         },
5001
5002                         getNextSibling: function(){
5003                                 // summary:
5004                                 //              Returns null if this is the last child of the parent,
5005                                 //              otherwise returns the next element sibling to the "right".
5006
5007                                 return this._getSibling("next"); // dijit._Widget
5008                         },
5009
5010                         getIndexInParent: function(){
5011                                 // summary:
5012                                 //              Returns the index of this widget within its container parent.
5013                                 //              It returns -1 if the parent does not exist, or if the parent
5014                                 //              is not a dijit._Container
5015
5016                                 var p = this.getParent();
5017                                 if(!p || !p.getIndexOfChild){
5018                                         return -1; // int
5019                                 }
5020                                 return p.getIndexOfChild(this); // int
5021                         }
5022                 }
5023         );
5024
5025
5026 }
5027
5028 if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5029 dojo._hasResource["dijit.layout._LayoutWidget"] = true;
5030 dojo.provide("dijit.layout._LayoutWidget");
5031
5032
5033
5034
5035
5036 dojo.declare("dijit.layout._LayoutWidget",
5037         [dijit._Widget, dijit._Container, dijit._Contained],
5038         {
5039                 // summary:
5040                 //              Base class for a _Container widget which is responsible for laying out its children.
5041                 //              Widgets which mixin this code must define layout() to manage placement and sizing of the children.
5042
5043                 // baseClass: [protected extension] String
5044                 //              This class name is applied to the widget's domNode
5045                 //              and also may be used to generate names for sub nodes,
5046                 //              for example dijitTabContainer-content.
5047                 baseClass: "dijitLayoutContainer",
5048
5049                 // isLayoutContainer: [protected] Boolean
5050                 //              Indicates that this widget is going to call resize() on its
5051                 //              children widgets, setting their size, when they become visible.
5052                 isLayoutContainer: true,
5053
5054                 postCreate: function(){
5055                         dojo.addClass(this.domNode, "dijitContainer");
5056
5057                         this.inherited(arguments);
5058                 },
5059
5060                 startup: function(){
5061                         // summary:
5062                         //              Called after all the widgets have been instantiated and their
5063                         //              dom nodes have been inserted somewhere under dojo.doc.body.
5064                         //
5065                         //              Widgets should override this method to do any initialization
5066                         //              dependent on other widgets existing, and then call
5067                         //              this superclass method to finish things off.
5068                         //
5069                         //              startup() in subclasses shouldn't do anything
5070                         //              size related because the size of the widget hasn't been set yet.
5071
5072                         if(this._started){ return; }
5073
5074                         // Need to call inherited first - so that child widgets get started
5075                         // up correctly
5076                         this.inherited(arguments);
5077
5078                         // If I am a not being controlled by a parent layout widget...
5079                         var parent = this.getParent && this.getParent()
5080                         if(!(parent && parent.isLayoutContainer)){
5081                                 // Do recursive sizing and layout of all my descendants
5082                                 // (passing in no argument to resize means that it has to glean the size itself)
5083                                 this.resize();
5084
5085                                 // Since my parent isn't a layout container, and my style *may be* width=height=100%
5086                                 // or something similar (either set directly or via a CSS class),
5087                                 // monitor when my size changes so that I can re-layout.
5088                                 // For browsers where I can't directly monitor when my size changes,
5089                                 // monitor when the viewport changes size, which *may* indicate a size change for me.
5090                                 this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
5091                                         // Using function(){} closure to ensure no arguments to resize.
5092                                         this.resize();
5093                                 });
5094                         }
5095                 },
5096
5097                 resize: function(changeSize, resultSize){
5098                         // summary:
5099                         //              Call this to resize a widget, or after its size has changed.
5100                         // description:
5101                         //              Change size mode:
5102                         //                      When changeSize is specified, changes the marginBox of this widget
5103                         //                      and forces it to relayout its contents accordingly.
5104                         //                      changeSize may specify height, width, or both.
5105                         //
5106                         //                      If resultSize is specified it indicates the size the widget will
5107                         //                      become after changeSize has been applied.
5108                         //
5109                         //              Notification mode:
5110                         //                      When changeSize is null, indicates that the caller has already changed
5111                         //                      the size of the widget, or perhaps it changed because the browser
5112                         //                      window was resized.  Tells widget to relayout its contents accordingly.
5113                         //
5114                         //                      If resultSize is also specified it indicates the size the widget has
5115                         //                      become.
5116                         //
5117                         //              In either mode, this method also:
5118                         //                      1. Sets this._borderBox and this._contentBox to the new size of
5119                         //                              the widget.  Queries the current domNode size if necessary.
5120                         //                      2. Calls layout() to resize contents (and maybe adjust child widgets).
5121                         //
5122                         // changeSize: Object?
5123                         //              Sets the widget to this margin-box size and position.
5124                         //              May include any/all of the following properties:
5125                         //      |       {w: int, h: int, l: int, t: int}
5126                         //
5127                         // resultSize: Object?
5128                         //              The margin-box size of this widget after applying changeSize (if
5129                         //              changeSize is specified).  If caller knows this size and
5130                         //              passes it in, we don't need to query the browser to get the size.
5131                         //      |       {w: int, h: int}
5132
5133                         var node = this.domNode;
5134
5135                         // set margin box size, unless it wasn't specified, in which case use current size
5136                         if(changeSize){
5137                                 dojo.marginBox(node, changeSize);
5138
5139                                 // set offset of the node
5140                                 if(changeSize.t){ node.style.top = changeSize.t + "px"; }
5141                                 if(changeSize.l){ node.style.left = changeSize.l + "px"; }
5142                         }
5143
5144                         // If either height or width wasn't specified by the user, then query node for it.
5145                         // But note that setting the margin box and then immediately querying dimensions may return
5146                         // inaccurate results, so try not to depend on it.
5147                         var mb = resultSize || {};
5148                         dojo.mixin(mb, changeSize || {});       // changeSize overrides resultSize
5149                         if( !("h" in mb) || !("w" in mb) ){
5150                                 mb = dojo.mixin(dojo.marginBox(node), mb);      // just use dojo.marginBox() to fill in missing values
5151                         }
5152
5153                         // Compute and save the size of my border box and content box
5154                         // (w/out calling dojo.contentBox() since that may fail if size was recently set)
5155                         var cs = dojo.getComputedStyle(node);
5156                         var me = dojo._getMarginExtents(node, cs);
5157                         var be = dojo._getBorderExtents(node, cs);
5158                         var bb = (this._borderBox = {
5159                                 w: mb.w - (me.w + be.w),
5160                                 h: mb.h - (me.h + be.h)
5161                         });
5162                         var pe = dojo._getPadExtents(node, cs);
5163                         this._contentBox = {
5164                                 l: dojo._toPixelValue(node, cs.paddingLeft),
5165                                 t: dojo._toPixelValue(node, cs.paddingTop),
5166                                 w: bb.w - pe.w,
5167                                 h: bb.h - pe.h
5168                         };
5169
5170                         // Callback for widget to adjust size of its children
5171                         this.layout();
5172                 },
5173
5174                 layout: function(){
5175                         // summary:
5176                         //              Widgets override this method to size and position their contents/children.
5177                         //              When this is called this._contentBox is guaranteed to be set (see resize()).
5178                         //
5179                         //              This is called after startup(), and also when the widget's size has been
5180                         //              changed.
5181                         // tags:
5182                         //              protected extension
5183                 },
5184
5185                 _setupChild: function(/*dijit._Widget*/child){
5186                         // summary:
5187                         //              Common setup for initial children and children which are added after startup
5188                         // tags:
5189                         //              protected extension
5190
5191                         dojo.addClass(child.domNode, this.baseClass+"-child");
5192                         if(child.baseClass){
5193                                 dojo.addClass(child.domNode, this.baseClass+"-"+child.baseClass);
5194                         }
5195                 },
5196
5197                 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
5198                         // Overrides _Container.addChild() to call _setupChild()
5199                         this.inherited(arguments);
5200                         if(this._started){
5201                                 this._setupChild(child);
5202                         }
5203                 },
5204
5205                 removeChild: function(/*dijit._Widget*/ child){
5206                         // Overrides _Container.removeChild() to remove class added by _setupChild()
5207                         dojo.removeClass(child.domNode, this.baseClass+"-child");
5208                         if(child.baseClass){
5209                                 dojo.removeClass(child.domNode, this.baseClass+"-"+child.baseClass);
5210                         }
5211                         this.inherited(arguments);
5212                 }
5213         }
5214 );
5215
5216 dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
5217         // summary:
5218         //              Given the margin-box size of a node, return its content box size.
5219         //              Functions like dojo.contentBox() but is more reliable since it doesn't have
5220         //              to wait for the browser to compute sizes.
5221         var cs = dojo.getComputedStyle(node);
5222         var me = dojo._getMarginExtents(node, cs);
5223         var pb = dojo._getPadBorderExtents(node, cs);
5224         return {
5225                 l: dojo._toPixelValue(node, cs.paddingLeft),
5226                 t: dojo._toPixelValue(node, cs.paddingTop),
5227                 w: mb.w - (me.w + pb.w),
5228                 h: mb.h - (me.h + pb.h)
5229         };
5230 };
5231
5232 (function(){
5233         var capitalize = function(word){
5234                 return word.substring(0,1).toUpperCase() + word.substring(1);
5235         };
5236
5237         var size = function(widget, dim){
5238                 // size the child
5239                 widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
5240
5241                 // record child's size, but favor our own numbers when we have them.
5242                 // the browser lies sometimes
5243                 dojo.mixin(widget, dojo.marginBox(widget.domNode));
5244                 dojo.mixin(widget, dim);
5245         };
5246
5247         dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Object[]*/ children){
5248                 // summary
5249                 //              Layout a bunch of child dom nodes within a parent dom node
5250                 // container:
5251                 //              parent node
5252                 // dim:
5253                 //              {l, t, w, h} object specifying dimensions of container into which to place children
5254                 // children:
5255                 //              an array like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ]
5256
5257                 // copy dim because we are going to modify it
5258                 dim = dojo.mixin({}, dim);
5259
5260                 dojo.addClass(container, "dijitLayoutContainer");
5261
5262                 // Move "client" elements to the end of the array for layout.  a11y dictates that the author
5263                 // needs to be able to put them in the document in tab-order, but this algorithm requires that
5264                 // client be last.
5265                 children = dojo.filter(children, function(item){ return item.layoutAlign != "client"; })
5266                         .concat(dojo.filter(children, function(item){ return item.layoutAlign == "client"; }));
5267
5268                 // set positions/sizes
5269                 dojo.forEach(children, function(child){
5270                         var elm = child.domNode,
5271                                 pos = child.layoutAlign;
5272
5273                         // set elem to upper left corner of unused space; may move it later
5274                         var elmStyle = elm.style;
5275                         elmStyle.left = dim.l+"px";
5276                         elmStyle.top = dim.t+"px";
5277                         elmStyle.bottom = elmStyle.right = "auto";
5278
5279                         dojo.addClass(elm, "dijitAlign" + capitalize(pos));
5280
5281                         // set size && adjust record of remaining space.
5282                         // note that setting the width of a <div> may affect its height.
5283                         if(pos == "top" || pos == "bottom"){
5284                                 size(child, { w: dim.w });
5285                                 dim.h -= child.h;
5286                                 if(pos == "top"){
5287                                         dim.t += child.h;
5288                                 }else{
5289                                         elmStyle.top = dim.t + dim.h + "px";
5290                                 }
5291                         }else if(pos == "left" || pos == "right"){
5292                                 size(child, { h: dim.h });
5293                                 dim.w -= child.w;
5294                                 if(pos == "left"){
5295                                         dim.l += child.w;
5296                                 }else{
5297                                         elmStyle.left = dim.l + dim.w + "px";
5298                                 }
5299                         }else if(pos == "client"){
5300                                 size(child, dim);
5301                         }
5302                 });
5303         };
5304
5305 })();
5306
5307 }
5308
5309 if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5310 dojo._hasResource["dijit._CssStateMixin"] = true;
5311 dojo.provide("dijit._CssStateMixin");
5312
5313
5314 dojo.declare("dijit._CssStateMixin", [], {
5315         // summary:
5316         //              Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
5317         //              state changes, and also higher-level state changes such becoming disabled or selected.
5318         //
5319         // description:
5320         //              By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
5321         //              maintain CSS classes on the widget root node (this.domNode) depending on hover,
5322         //              active, focus, etc. state.   Ex: with a baseClass of dijitButton, it will apply the classes
5323         //              dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
5324         //
5325         //              It also sets CSS like dijitButtonDisabled based on widget semantic state.
5326         //
5327         //              By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
5328         //              within the widget).
5329
5330         // cssStateNodes: [protected] Object
5331         //              List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
5332         //.
5333         //              Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
5334         //              (like "dijitUpArrowButton"). Example:
5335         //      |               {
5336         //      |                       "upArrowButton": "dijitUpArrowButton",
5337         //      |                       "downArrowButton": "dijitDownArrowButton"
5338         //      |               }
5339         //              The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
5340         //              is hovered, etc.
5341         cssStateNodes: {},
5342
5343         postCreate: function(){
5344                 this.inherited(arguments);
5345
5346                 // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
5347                 dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
5348                         this.connect(this.domNode, e, "_cssMouseEvent");
5349                 }, this);
5350                 
5351                 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
5352                 this.connect(this, "set", function(name, value){
5353                         if(arguments.length >= 2 && {disabled: true, readOnly: true, checked:true, selected:true}[name]){
5354                                 this._setStateClass();
5355                         }
5356                 });
5357
5358                 // The widget coming in/out of the focus change affects it's state
5359                 dojo.forEach(["_onFocus", "_onBlur"], function(ap){
5360                         this.connect(this, ap, "_setStateClass");
5361                 }, this);
5362
5363                 // Events on sub nodes within the widget
5364                 for(var ap in this.cssStateNodes){
5365                         this._trackMouseState(this[ap], this.cssStateNodes[ap]);
5366                 }
5367                 // Set state initially; there's probably no hover/active/focus state but widget might be
5368                 // disabled/readonly so we want to set CSS classes for those conditions.
5369                 this._setStateClass();
5370         },
5371
5372         _cssMouseEvent: function(/*Event*/ event){
5373                 // summary:
5374                 //      Sets _hovering and _active properties depending on mouse state,
5375                 //      then calls _setStateClass() to set appropriate CSS classes for this.domNode.
5376
5377                 if(!this.disabled){
5378                         switch(event.type){
5379                                 case "mouseenter":
5380                                 case "mouseover":       // generated on non-IE browsers even though we connected to mouseenter
5381                                         this._hovering = true;
5382                                         this._active = this._mouseDown;
5383                                         break;
5384
5385                                 case "mouseleave":
5386                                 case "mouseout":        // generated on non-IE browsers even though we connected to mouseleave
5387                                         this._hovering = false;
5388                                         this._active = false;
5389                                         break;
5390
5391                                 case "mousedown" :
5392                                         this._active = true;
5393                                         this._mouseDown = true;
5394                                         // Set a global event to handle mouseup, so it fires properly
5395                                         // even if the cursor leaves this.domNode before the mouse up event.
5396                                         // Alternately could set active=false on mouseout.
5397                                         var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
5398                                                 this._active = false;
5399                                                 this._mouseDown = false;
5400                                                 this._setStateClass();
5401                                                 this.disconnect(mouseUpConnector);
5402                                         });
5403                                         break;
5404                         }
5405                         this._setStateClass();
5406                 }
5407         },
5408
5409         _setStateClass: function(){
5410                 // summary:
5411                 //              Update the visual state of the widget by setting the css classes on this.domNode
5412                 //              (or this.stateNode if defined) by combining this.baseClass with
5413                 //              various suffixes that represent the current widget state(s).
5414                 //
5415                 // description:
5416                 //              In the case where a widget has multiple
5417                 //              states, it sets the class based on all possible
5418                 //              combinations.  For example, an invalid form widget that is being hovered
5419                 //              will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
5420                 //
5421                 //              The widget may have one or more of the following states, determined
5422                 //              by this.state, this.checked, this.valid, and this.selected:
5423                 //                      - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
5424                 //                      - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
5425                 //                      - Selected - ex: currently selected tab will have this.selected==true
5426                 //
5427                 //              In addition, it may have one or more of the following states,
5428                 //              based on this.disabled and flags set in _onMouse (this._active, this._hovering, this._focused):
5429                 //                      - Disabled      - if the widget is disabled
5430                 //                      - Active                - if the mouse (or space/enter key?) is being pressed down
5431                 //                      - Focused               - if the widget has focus
5432                 //                      - Hover         - if the mouse is over the widget
5433
5434                 // Compute new set of classes
5435                 var newStateClasses = this.baseClass.split(" ");
5436
5437                 function multiply(modifier){
5438                         newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
5439                 }
5440
5441                 if(!this.isLeftToRight()){
5442                         // For RTL mode we need to set an addition class like dijitTextBoxRtl.
5443                         multiply("Rtl");
5444                 }
5445
5446                 if(this.checked){
5447                         multiply("Checked");
5448                 }
5449                 if(this.state){
5450                         multiply(this.state);
5451                 }
5452                 if(this.selected){
5453                         multiply("Selected");
5454                 }
5455
5456                 if(this.disabled){
5457                         multiply("Disabled");
5458                 }else if(this.readOnly){
5459                         multiply("ReadOnly");
5460                 }else{
5461                         if(this._active){
5462                                 multiply("Active");
5463                         }else if(this._hovering){
5464                                 multiply("Hover");
5465                         }
5466                 }
5467
5468                 if(this._focused){
5469                         multiply("Focused");
5470                 }
5471
5472                 // Remove old state classes and add new ones.
5473                 // For performance concerns we only write into domNode.className once.
5474                 var tn = this.stateNode || this.domNode,
5475                         classHash = {}; // set of all classes (state and otherwise) for node
5476
5477                 dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
5478
5479                 if("_stateClasses" in this){
5480                         dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
5481                 }
5482
5483                 dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
5484
5485                 var newClasses = [];
5486                 for(var c in classHash){
5487                         newClasses.push(c);
5488                 }
5489                 tn.className = newClasses.join(" ");
5490
5491                 this._stateClasses = newStateClasses;
5492         },
5493
5494         _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
5495                 // summary:
5496                 //              Track mouse/focus events on specified node and set CSS class on that node to indicate
5497                 //              current state.   Usually not called directly, but via cssStateNodes attribute.
5498                 // description:
5499                 //              Given class=foo, will set the following CSS class on the node
5500                 //                      - fooActive: if the user is currently pressing down the mouse button while over the node
5501                 //                      - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
5502                 //                      - fooFocus: if the node is focused
5503                 //
5504                 //              Note that it won't set any classes if the widget is disabled.
5505                 // node: DomNode
5506                 //              Should be a sub-node of the widget, not the top node (this.domNode), since the top node
5507                 //              is handled specially and automatically just by mixing in this class.
5508                 // clazz: String
5509                 //              CSS class name (ex: dijitSliderUpArrow).
5510
5511                 // Current state of node (initially false)
5512                 // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
5513                 var hovering=false, active=false, focused=false;
5514
5515                 var self = this,
5516                         cn = dojo.hitch(this, "connect", node);
5517
5518                 function setClass(){
5519                         var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
5520                         dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
5521                         dojo.toggleClass(node, clazz+"Active", active && !disabled);
5522                         dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
5523                 }
5524
5525                 // Mouse
5526                 cn("onmouseenter", function(){
5527                         hovering = true;
5528                         setClass();
5529                 });
5530                 cn("onmouseleave", function(){
5531                         hovering = false;
5532                         active = false;
5533                         setClass();
5534                 });
5535                 cn("onmousedown", function(){
5536                         active = true;
5537                         setClass();
5538                 });
5539                 cn("onmouseup", function(){
5540                         active = false;
5541                         setClass();
5542                 });
5543
5544                 // Focus
5545                 cn("onfocus", function(){
5546                         focused = true;
5547                         setClass();
5548                 });
5549                 cn("onblur", function(){
5550                         focused = false;
5551                         setClass();
5552                 });
5553
5554                 // Just in case widget is enabled/disabled while it has focus/hover/active state.
5555                 // Maybe this is overkill.
5556                 this.connect(this, "set", function(name, value){
5557                         if(name == "disabled" || name == "readOnly"){
5558                                 setClass();
5559                         }
5560                 });
5561         }
5562 });
5563
5564 }
5565
5566 if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5567 dojo._hasResource["dijit.form._FormWidget"] = true;
5568 dojo.provide("dijit.form._FormWidget");
5569
5570
5571
5572
5573
5574
5575
5576 dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
5577         {
5578         // summary:
5579         //              Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
5580         //              which can be children of a <form> node or a `dijit.form.Form` widget.
5581         //
5582         // description:
5583         //              Represents a single HTML element.
5584         //              All these widgets should have these attributes just like native HTML input elements.
5585         //              You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
5586         //
5587         //              They also share some common methods.
5588
5589         // name: String
5590         //              Name used when submitting form; same as "name" attribute or plain HTML elements
5591         name: "",
5592
5593         // alt: String
5594         //              Corresponds to the native HTML <input> element's attribute.
5595         alt: "",
5596
5597         // value: String
5598         //              Corresponds to the native HTML <input> element's attribute.
5599         value: "",
5600
5601         // type: String
5602         //              Corresponds to the native HTML <input> element's attribute.
5603         type: "text",
5604
5605         // tabIndex: Integer
5606         //              Order fields are traversed when user hits the tab key
5607         tabIndex: "0",
5608
5609         // disabled: Boolean
5610         //              Should this widget respond to user input?
5611         //              In markup, this is specified as "disabled='disabled'", or just "disabled".
5612         disabled: false,
5613
5614         // intermediateChanges: Boolean
5615         //              Fires onChange for each value change or only on demand
5616         intermediateChanges: false,
5617
5618         // scrollOnFocus: Boolean
5619         //              On focus, should this widget scroll into view?
5620         scrollOnFocus: true,
5621
5622         // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
5623         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
5624                 value: "focusNode",
5625                 id: "focusNode",
5626                 tabIndex: "focusNode",
5627                 alt: "focusNode",
5628                 title: "focusNode"
5629         }),
5630
5631         postMixInProperties: function(){
5632                 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
5633                 // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
5634                 // Regarding escaping, see heading "Attribute values" in
5635                 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
5636                 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
5637                 this.inherited(arguments);
5638         },
5639
5640         postCreate: function(){
5641                 this.inherited(arguments);
5642                 this.connect(this.domNode, "onmousedown", "_onMouseDown");
5643         },
5644
5645         _setDisabledAttr: function(/*Boolean*/ value){
5646                 this.disabled = value;
5647                 dojo.attr(this.focusNode, 'disabled', value);
5648                 if(this.valueNode){
5649                         dojo.attr(this.valueNode, 'disabled', value);
5650                 }
5651                 dijit.setWaiState(this.focusNode, "disabled", value);
5652
5653                 if(value){
5654                         // reset these, because after the domNode is disabled, we can no longer receive
5655                         // mouse related events, see #4200
5656                         this._hovering = false;
5657                         this._active = false;
5658
5659                         // clear tab stop(s) on this widget's focusable node(s)  (ComboBox has two focusable nodes)
5660                         var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
5661                         dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
5662                                 var node = this[attachPointName];
5663                                 // complex code because tabIndex=-1 on a <div> doesn't work on FF
5664                                 if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){     // see #11064 about webkit bug
5665                                         node.setAttribute('tabIndex', "-1");
5666                                 }else{
5667                                         node.removeAttribute('tabIndex');                               
5668                                 }
5669                         }, this);
5670                 }else{
5671                         this.focusNode.setAttribute('tabIndex', this.tabIndex);
5672                 }
5673         },
5674
5675         setDisabled: function(/*Boolean*/ disabled){
5676                 // summary:
5677                 //              Deprecated.   Use set('disabled', ...) instead.
5678                 dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
5679                 this.set('disabled', disabled);
5680         },
5681
5682         _onFocus: function(e){
5683                 if(this.scrollOnFocus){
5684                         dojo.window.scrollIntoView(this.domNode);
5685                 }
5686                 this.inherited(arguments);
5687         },
5688
5689         isFocusable: function(){
5690                 // summary:
5691                 //              Tells if this widget is focusable or not.   Used internally by dijit.
5692                 // tags:
5693                 //              protected
5694                 return !this.disabled && !this.readOnly && this.focusNode && (dojo.style(this.domNode, "display") != "none");
5695         },
5696
5697         focus: function(){
5698                 // summary:
5699                 //              Put focus on this widget
5700                 dijit.focus(this.focusNode);
5701         },
5702
5703         compare: function(/*anything*/val1, /*anything*/val2){
5704                 // summary:
5705                 //              Compare 2 values (as returned by attr('value') for this widget).
5706                 // tags:
5707                 //              protected
5708                 if(typeof val1 == "number" && typeof val2 == "number"){
5709                         return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
5710                 }else if(val1 > val2){
5711                         return 1;
5712                 }else if(val1 < val2){
5713                         return -1;
5714                 }else{
5715                         return 0;
5716                 }
5717         },
5718
5719         onChange: function(newValue){
5720                 // summary:
5721                 //              Callback when this widget's value is changed.
5722                 // tags:
5723                 //              callback
5724         },
5725
5726         // _onChangeActive: [private] Boolean
5727         //              Indicates that changes to the value should call onChange() callback.
5728         //              This is false during widget initialization, to avoid calling onChange()
5729         //              when the initial value is set.
5730         _onChangeActive: false,
5731
5732         _handleOnChange: function(/*anything*/ newValue, /* Boolean? */ priorityChange){
5733                 // summary:
5734                 //              Called when the value of the widget is set.  Calls onChange() if appropriate
5735                 // newValue:
5736                 //              the new value
5737                 // priorityChange:
5738                 //              For a slider, for example, dragging the slider is priorityChange==false,
5739                 //              but on mouse up, it's priorityChange==true.  If intermediateChanges==true,
5740                 //              onChange is only called form priorityChange=true events.
5741                 // tags:
5742                 //              private
5743                 this._lastValue = newValue;
5744                 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
5745                         // this block executes not for a change, but during initialization,
5746                         // and is used to store away the original value (or for ToggleButton, the original checked state)
5747                         this._resetValue = this._lastValueReported = newValue;
5748                 }
5749                 if((this.intermediateChanges || priorityChange || priorityChange === undefined) &&
5750                         ((typeof newValue != typeof this._lastValueReported) ||
5751                                 this.compare(newValue, this._lastValueReported) != 0)){
5752                         this._lastValueReported = newValue;
5753                         if(this._onChangeActive){
5754                                 if(this._onChangeHandle){
5755                                         clearTimeout(this._onChangeHandle);
5756                                 }
5757                                 // setTimout allows hidden value processing to run and
5758                                 // also the onChange handler can safely adjust focus, etc
5759                                 this._onChangeHandle = setTimeout(dojo.hitch(this,
5760                                         function(){
5761                                                 this._onChangeHandle = null;
5762                                                 this.onChange(newValue);
5763                                         }), 0); // try to collapse multiple onChange's fired faster than can be processed
5764                         }
5765                 }
5766         },
5767
5768         create: function(){
5769                 // Overrides _Widget.create()
5770                 this.inherited(arguments);
5771                 this._onChangeActive = true;
5772         },
5773
5774         destroy: function(){
5775                 if(this._onChangeHandle){ // destroy called before last onChange has fired
5776                         clearTimeout(this._onChangeHandle);
5777                         this.onChange(this._lastValueReported);
5778                 }
5779                 this.inherited(arguments);
5780         },
5781
5782         setValue: function(/*String*/ value){
5783                 // summary:
5784                 //              Deprecated.   Use set('value', ...) instead.
5785                 dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated.  Use set('value',"+value+") instead.", "", "2.0");
5786                 this.set('value', value);
5787         },
5788
5789         getValue: function(){
5790                 // summary:
5791                 //              Deprecated.   Use get('value') instead.
5792                 dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
5793                 return this.get('value');
5794         },
5795         
5796         _onMouseDown: function(e){
5797                 // If user clicks on the button, even if the mouse is released outside of it,
5798                 // this button should get focus (to mimics native browser buttons).
5799                 // This is also needed on chrome because otherwise buttons won't get focus at all,
5800                 // which leads to bizarre focus restore on Dialog close etc.
5801                 if(!e.ctrlKey && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
5802                         // Set a global event to handle mouseup, so it fires properly
5803                         // even if the cursor leaves this.domNode before the mouse up event.
5804                         var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
5805                                 if (this.isFocusable()) {
5806                                         this.focus();
5807                                 }
5808                                 this.disconnect(mouseUpConnector);
5809                         });
5810                 }
5811         }
5812 });
5813
5814 dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
5815 {
5816         // summary:
5817         //              Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
5818         // description:
5819         //              Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
5820         //              to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
5821         //              works as expected.
5822
5823         // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
5824         // directly in the template as read by the parser in order to function. IE is known to specifically
5825         // require the 'name' attribute at element creation time.   See #8484, #8660.
5826         // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
5827         // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
5828         // Seems like we really want value removed from attributeMap altogether
5829         // (although there's no easy way to do that now)
5830
5831         // readOnly: Boolean
5832         //              Should this widget respond to user input?
5833         //              In markup, this is specified as "readOnly".
5834         //              Similar to disabled except readOnly form values are submitted.
5835         readOnly: false,
5836
5837         attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
5838                 value: "",
5839                 readOnly: "focusNode"
5840         }),
5841
5842         _setReadOnlyAttr: function(/*Boolean*/ value){
5843                 this.readOnly = value;
5844                 dojo.attr(this.focusNode, 'readOnly', value);
5845                 dijit.setWaiState(this.focusNode, "readonly", value);
5846         },
5847
5848         postCreate: function(){
5849                 this.inherited(arguments);
5850
5851                 if(dojo.isIE){ // IE won't stop the event with keypress
5852                         this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
5853                 }
5854                 // Update our reset value if it hasn't yet been set (because this.set()
5855                 // is only called when there *is* a value)
5856                 if(this._resetValue === undefined){
5857                         this._resetValue = this.value;
5858                 }
5859         },
5860
5861         _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
5862                 // summary:
5863                 //              Hook so attr('value', value) works.
5864                 // description:
5865                 //              Sets the value of the widget.
5866                 //              If the value has changed, then fire onChange event, unless priorityChange
5867                 //              is specified as null (or false?)
5868                 this.value = newValue;
5869                 this._handleOnChange(newValue, priorityChange);
5870         },
5871
5872         _getValueAttr: function(){
5873                 // summary:
5874                 //              Hook so attr('value') works.
5875                 return this._lastValue;
5876         },
5877
5878         undo: function(){
5879                 // summary:
5880                 //              Restore the value to the last value passed to onChange
5881                 this._setValueAttr(this._lastValueReported, false);
5882         },
5883
5884         reset: function(){
5885                 // summary:
5886                 //              Reset the widget's value to what it was at initialization time
5887                 this._hasBeenBlurred = false;
5888                 this._setValueAttr(this._resetValue, true);
5889         },
5890
5891         _onKeyDown: function(e){
5892                 if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
5893                         var te;
5894                         if(dojo.isIE){
5895                                 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
5896                                 te = document.createEventObject();
5897                                 te.keyCode = dojo.keys.ESCAPE;
5898                                 te.shiftKey = e.shiftKey;
5899                                 e.srcElement.fireEvent('onkeypress', te);
5900                         }
5901                 }
5902         },
5903
5904         _layoutHackIE7: function(){
5905                 // summary:
5906                 //              Work around table sizing bugs on IE7 by forcing redraw
5907
5908                 if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
5909                         var domNode = this.domNode;
5910                         var parent = domNode.parentNode;
5911                         var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
5912                         var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
5913                         var _this = this;
5914                         while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
5915                                 (function ping(){
5916                                         var disconnectHandle = _this.connect(parent, "onscroll",
5917                                                 function(e){
5918                                                         _this.disconnect(disconnectHandle); // only call once
5919                                                         pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
5920                                                         setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
5921                                                 }
5922                                         );
5923                                 })();
5924                                 parent = parent.parentNode;
5925                         }
5926                 }
5927         }
5928 });
5929
5930 }
5931
5932 if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5933 dojo._hasResource["dijit.dijit"] = true;
5934 dojo.provide("dijit.dijit");
5935
5936 /*=====
5937 dijit.dijit = {
5938         // summary:
5939         //              A roll-up for common dijit methods
5940         // description:
5941         //      A rollup file for the build system including the core and common
5942         //      dijit files.
5943         //
5944         // example:
5945         // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script>
5946         //
5947 };
5948 =====*/
5949
5950 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
5951
5952
5953 // And some other stuff that we tend to pull in all the time anyway
5954
5955
5956
5957
5958
5959
5960
5961 }
5962
5963 if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5964 dojo._hasResource["dojo.fx.Toggler"] = true;
5965 dojo.provide("dojo.fx.Toggler");
5966
5967 dojo.declare("dojo.fx.Toggler", null, {
5968         // summary:
5969         //              A simple `dojo.Animation` toggler API.
5970         //
5971         // description:
5972         //              class constructor for an animation toggler. It accepts a packed
5973         //              set of arguments about what type of animation to use in each
5974         //              direction, duration, etc. All available members are mixed into 
5975         //              these animations from the constructor (for example, `node`, 
5976         //              `showDuration`, `hideDuration`). 
5977         //
5978         // example:
5979         //      |       var t = new dojo.fx.Toggler({
5980         //      |               node: "nodeId",
5981         //      |               showDuration: 500,
5982         //      |               // hideDuration will default to "200"
5983         //      |               showFunc: dojo.fx.wipeIn, 
5984         //      |               // hideFunc will default to "fadeOut"
5985         //      |       });
5986         //      |       t.show(100); // delay showing for 100ms
5987         //      |       // ...time passes...
5988         //      |       t.hide();
5989
5990         // node: DomNode
5991         //              the node to target for the showing and hiding animations
5992         node: null,
5993
5994         // showFunc: Function
5995         //              The function that returns the `dojo.Animation` to show the node
5996         showFunc: dojo.fadeIn,
5997
5998         // hideFunc: Function   
5999         //              The function that returns the `dojo.Animation` to hide the node
6000         hideFunc: dojo.fadeOut,
6001
6002         // showDuration:
6003         //              Time in milliseconds to run the show Animation
6004         showDuration: 200,
6005
6006         // hideDuration:
6007         //              Time in milliseconds to run the hide Animation
6008         hideDuration: 200,
6009
6010         // FIXME: need a policy for where the toggler should "be" the next
6011         // time show/hide are called if we're stopped somewhere in the
6012         // middle.
6013         // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
6014         // each animation individually. 
6015         // FIXME: also would be nice to have events from the animations exposed/bridged
6016
6017         /*=====
6018         _showArgs: null,
6019         _showAnim: null,
6020
6021         _hideArgs: null,
6022         _hideAnim: null,
6023
6024         _isShowing: false,
6025         _isHiding: false,
6026         =====*/
6027
6028         constructor: function(args){
6029                 var _t = this;
6030
6031                 dojo.mixin(_t, args);
6032                 _t.node = args.node;
6033                 _t._showArgs = dojo.mixin({}, args);
6034                 _t._showArgs.node = _t.node;
6035                 _t._showArgs.duration = _t.showDuration;
6036                 _t.showAnim = _t.showFunc(_t._showArgs);
6037
6038                 _t._hideArgs = dojo.mixin({}, args);
6039                 _t._hideArgs.node = _t.node;
6040                 _t._hideArgs.duration = _t.hideDuration;
6041                 _t.hideAnim = _t.hideFunc(_t._hideArgs);
6042
6043                 dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
6044                 dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
6045         },
6046
6047         show: function(delay){
6048                 // summary: Toggle the node to showing
6049                 // delay: Integer?
6050                 //              Ammount of time to stall playing the show animation
6051                 return this.showAnim.play(delay || 0);
6052         },
6053
6054         hide: function(delay){
6055                 // summary: Toggle the node to hidden
6056                 // delay: Integer?
6057                 //              Ammount of time to stall playing the hide animation
6058                 return this.hideAnim.play(delay || 0);
6059         }
6060 });
6061
6062 }
6063
6064 if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6065 dojo._hasResource["dojo.fx"] = true;
6066 dojo.provide("dojo.fx");
6067  // FIXME: remove this back-compat require in 2.0 
6068 /*=====
6069 dojo.fx = {
6070         // summary: Effects library on top of Base animations
6071 };
6072 =====*/
6073 (function(){
6074         
6075         var d = dojo, 
6076                 _baseObj = {
6077                         _fire: function(evt, args){
6078                                 if(this[evt]){
6079                                         this[evt].apply(this, args||[]);
6080                                 }
6081                                 return this;
6082                         }
6083                 };
6084
6085         var _chain = function(animations){
6086                 this._index = -1;
6087                 this._animations = animations||[];
6088                 this._current = this._onAnimateCtx = this._onEndCtx = null;
6089
6090                 this.duration = 0;
6091                 d.forEach(this._animations, function(a){
6092                         this.duration += a.duration;
6093                         if(a.delay){ this.duration += a.delay; }
6094                 }, this);
6095         };
6096         d.extend(_chain, {
6097                 _onAnimate: function(){
6098                         this._fire("onAnimate", arguments);
6099                 },
6100                 _onEnd: function(){
6101                         d.disconnect(this._onAnimateCtx);
6102                         d.disconnect(this._onEndCtx);
6103                         this._onAnimateCtx = this._onEndCtx = null;
6104                         if(this._index + 1 == this._animations.length){
6105                                 this._fire("onEnd");
6106                         }else{
6107                                 // switch animations
6108                                 this._current = this._animations[++this._index];
6109                                 this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
6110                                 this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
6111                                 this._current.play(0, true);
6112                         }
6113                 },
6114                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
6115                         if(!this._current){ this._current = this._animations[this._index = 0]; }
6116                         if(!gotoStart && this._current.status() == "playing"){ return this; }
6117                         var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
6118                                         this._fire("beforeBegin");
6119                                 }),
6120                                 onBegin = d.connect(this._current, "onBegin", this, function(arg){
6121                                         this._fire("onBegin", arguments);
6122                                 }),
6123                                 onPlay = d.connect(this._current, "onPlay", this, function(arg){
6124                                         this._fire("onPlay", arguments);
6125                                         d.disconnect(beforeBegin);
6126                                         d.disconnect(onBegin);
6127                                         d.disconnect(onPlay);
6128                                 });
6129                         if(this._onAnimateCtx){
6130                                 d.disconnect(this._onAnimateCtx);
6131                         }
6132                         this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
6133                         if(this._onEndCtx){
6134                                 d.disconnect(this._onEndCtx);
6135                         }
6136                         this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
6137                         this._current.play.apply(this._current, arguments);
6138                         return this;
6139                 },
6140                 pause: function(){
6141                         if(this._current){
6142                                 var e = d.connect(this._current, "onPause", this, function(arg){
6143                                                 this._fire("onPause", arguments);
6144                                                 d.disconnect(e);
6145                                         });
6146                                 this._current.pause();
6147                         }
6148                         return this;
6149                 },
6150                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
6151                         this.pause();
6152                         var offset = this.duration * percent;
6153                         this._current = null;
6154                         d.some(this._animations, function(a){
6155                                 if(a.duration <= offset){
6156                                         this._current = a;
6157                                         return true;
6158                                 }
6159                                 offset -= a.duration;
6160                                 return false;
6161                         });
6162                         if(this._current){
6163                                 this._current.gotoPercent(offset / this._current.duration, andPlay);
6164                         }
6165                         return this;
6166                 },
6167                 stop: function(/*boolean?*/ gotoEnd){
6168                         if(this._current){
6169                                 if(gotoEnd){
6170                                         for(; this._index + 1 < this._animations.length; ++this._index){
6171                                                 this._animations[this._index].stop(true);
6172                                         }
6173                                         this._current = this._animations[this._index];
6174                                 }
6175                                 var e = d.connect(this._current, "onStop", this, function(arg){
6176                                                 this._fire("onStop", arguments);
6177                                                 d.disconnect(e);
6178                                         });
6179                                 this._current.stop();
6180                         }
6181                         return this;
6182                 },
6183                 status: function(){
6184                         return this._current ? this._current.status() : "stopped";
6185                 },
6186                 destroy: function(){
6187                         if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
6188                         if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
6189                 }
6190         });
6191         d.extend(_chain, _baseObj);
6192
6193         dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
6194                 // summary: 
6195                 //              Chain a list of `dojo.Animation`s to run in sequence
6196                 //
6197                 // description:
6198                 //              Return a `dojo.Animation` which will play all passed
6199                 //              `dojo.Animation` instances in sequence, firing its own
6200                 //              synthesized events simulating a single animation. (eg:
6201                 //              onEnd of this animation means the end of the chain, 
6202                 //              not the individual animations within)
6203                 //
6204                 // example:
6205                 //      Once `node` is faded out, fade in `otherNode`
6206                 //      |       dojo.fx.chain([
6207                 //      |               dojo.fadeIn({ node:node }),
6208                 //      |               dojo.fadeOut({ node:otherNode })
6209                 //      |       ]).play();
6210                 //
6211                 return new _chain(animations) // dojo.Animation
6212         };
6213
6214         var _combine = function(animations){
6215                 this._animations = animations||[];
6216                 this._connects = [];
6217                 this._finished = 0;
6218
6219                 this.duration = 0;
6220                 d.forEach(animations, function(a){
6221                         var duration = a.duration;
6222                         if(a.delay){ duration += a.delay; }
6223                         if(this.duration < duration){ this.duration = duration; }
6224                         this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
6225                 }, this);
6226                 
6227                 this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
6228                 var self = this;
6229                 d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"], 
6230                         function(evt){
6231                                 self._connects.push(d.connect(self._pseudoAnimation, evt,
6232                                         function(){ self._fire(evt, arguments); }
6233                                 ));
6234                         }
6235                 );
6236         };
6237         d.extend(_combine, {
6238                 _doAction: function(action, args){
6239                         d.forEach(this._animations, function(a){
6240                                 a[action].apply(a, args);
6241                         });
6242                         return this;
6243                 },
6244                 _onEnd: function(){
6245                         if(++this._finished > this._animations.length){
6246                                 this._fire("onEnd");
6247                         }
6248                 },
6249                 _call: function(action, args){
6250                         var t = this._pseudoAnimation;
6251                         t[action].apply(t, args);
6252                 },
6253                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
6254                         this._finished = 0;
6255                         this._doAction("play", arguments);
6256                         this._call("play", arguments);
6257                         return this;
6258                 },
6259                 pause: function(){
6260                         this._doAction("pause", arguments);
6261                         this._call("pause", arguments);
6262                         return this;
6263                 },
6264                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
6265                         var ms = this.duration * percent;
6266                         d.forEach(this._animations, function(a){
6267                                 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
6268                         });
6269                         this._call("gotoPercent", arguments);
6270                         return this;
6271                 },
6272                 stop: function(/*boolean?*/ gotoEnd){
6273                         this._doAction("stop", arguments);
6274                         this._call("stop", arguments);
6275                         return this;
6276                 },
6277                 status: function(){
6278                         return this._pseudoAnimation.status();
6279                 },
6280                 destroy: function(){
6281                         d.forEach(this._connects, dojo.disconnect);
6282                 }
6283         });
6284         d.extend(_combine, _baseObj);
6285
6286         dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
6287                 // summary: 
6288                 //              Combine a list of `dojo.Animation`s to run in parallel
6289                 //
6290                 // description:
6291                 //              Combine an array of `dojo.Animation`s to run in parallel, 
6292                 //              providing a new `dojo.Animation` instance encompasing each
6293                 //              animation, firing standard animation events.
6294                 //
6295                 // example:
6296                 //      Fade out `node` while fading in `otherNode` simultaneously
6297                 //      |       dojo.fx.combine([
6298                 //      |               dojo.fadeIn({ node:node }),
6299                 //      |               dojo.fadeOut({ node:otherNode })
6300                 //      |       ]).play();
6301                 //
6302                 // example:
6303                 //      When the longest animation ends, execute a function:
6304                 //      |       var anim = dojo.fx.combine([
6305                 //      |               dojo.fadeIn({ node: n, duration:700 }),
6306                 //      |               dojo.fadeOut({ node: otherNode, duration: 300 })
6307                 //      |       ]);
6308                 //      |       dojo.connect(anim, "onEnd", function(){
6309                 //      |               // overall animation is done.
6310                 //      |       });
6311                 //      |       anim.play(); // play the animation
6312                 //
6313                 return new _combine(animations); // dojo.Animation
6314         };
6315
6316         dojo.fx.wipeIn = function(/*Object*/ args){
6317                 // summary:
6318                 //              Expand a node to it's natural height.
6319                 //
6320                 // description:
6321                 //              Returns an animation that will expand the
6322                 //              node defined in 'args' object from it's current height to
6323                 //              it's natural height (with no scrollbar).
6324                 //              Node must have no margin/border/padding.
6325                 //
6326                 // args: Object
6327                 //              A hash-map of standard `dojo.Animation` constructor properties
6328                 //              (such as easing: node: duration: and so on)
6329                 //
6330                 // example:
6331                 //      |       dojo.fx.wipeIn({
6332                 //      |               node:"someId"
6333                 //      |       }).play()
6334                 var node = args.node = d.byId(args.node), s = node.style, o;
6335
6336                 var anim = d.animateProperty(d.mixin({
6337                         properties: {
6338                                 height: {
6339                                         // wrapped in functions so we wait till the last second to query (in case value has changed)
6340                                         start: function(){
6341                                                 // start at current [computed] height, but use 1px rather than 0
6342                                                 // because 0 causes IE to display the whole panel
6343                                                 o = s.overflow;
6344                                                 s.overflow = "hidden";
6345                                                 if(s.visibility == "hidden" || s.display == "none"){
6346                                                         s.height = "1px";
6347                                                         s.display = "";
6348                                                         s.visibility = "";
6349                                                         return 1;
6350                                                 }else{
6351                                                         var height = d.style(node, "height");
6352                                                         return Math.max(height, 1);
6353                                                 }
6354                                         },
6355                                         end: function(){
6356                                                 return node.scrollHeight;
6357                                         }
6358                                 }
6359                         }
6360                 }, args));
6361
6362                 d.connect(anim, "onEnd", function(){ 
6363                         s.height = "auto";
6364                         s.overflow = o;
6365                 });
6366
6367                 return anim; // dojo.Animation
6368         }
6369
6370         dojo.fx.wipeOut = function(/*Object*/ args){
6371                 // summary:
6372                 //              Shrink a node to nothing and hide it. 
6373                 //
6374                 // description:
6375                 //              Returns an animation that will shrink node defined in "args"
6376                 //              from it's current height to 1px, and then hide it.
6377                 //
6378                 // args: Object
6379                 //              A hash-map of standard `dojo.Animation` constructor properties
6380                 //              (such as easing: node: duration: and so on)
6381                 // 
6382                 // example:
6383                 //      |       dojo.fx.wipeOut({ node:"someId" }).play()
6384                 
6385                 var node = args.node = d.byId(args.node), s = node.style, o;
6386                 
6387                 var anim = d.animateProperty(d.mixin({
6388                         properties: {
6389                                 height: {
6390                                         end: 1 // 0 causes IE to display the whole panel
6391                                 }
6392                         }
6393                 }, args));
6394
6395                 d.connect(anim, "beforeBegin", function(){
6396                         o = s.overflow;
6397                         s.overflow = "hidden";
6398                         s.display = "";
6399                 });
6400                 d.connect(anim, "onEnd", function(){
6401                         s.overflow = o;
6402                         s.height = "auto";
6403                         s.display = "none";
6404                 });
6405
6406                 return anim; // dojo.Animation
6407         }
6408
6409         dojo.fx.slideTo = function(/*Object*/ args){
6410                 // summary:
6411                 //              Slide a node to a new top/left position
6412                 //
6413                 // description:
6414                 //              Returns an animation that will slide "node" 
6415                 //              defined in args Object from its current position to
6416                 //              the position defined by (args.left, args.top).
6417                 //
6418                 // args: Object
6419                 //              A hash-map of standard `dojo.Animation` constructor properties
6420                 //              (such as easing: node: duration: and so on). Special args members
6421                 //              are `top` and `left`, which indicate the new position to slide to.
6422                 //
6423                 // example:
6424                 //      |       dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
6425
6426                 var node = args.node = d.byId(args.node), 
6427                         top = null, left = null;
6428
6429                 var init = (function(n){
6430                         return function(){
6431                                 var cs = d.getComputedStyle(n);
6432                                 var pos = cs.position;
6433                                 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
6434                                 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
6435                                 if(pos != 'absolute' && pos != 'relative'){
6436                                         var ret = d.position(n, true);
6437                                         top = ret.y;
6438                                         left = ret.x;
6439                                         n.style.position="absolute";
6440                                         n.style.top=top+"px";
6441                                         n.style.left=left+"px";
6442                                 }
6443                         };
6444                 })(node);
6445                 init();
6446
6447                 var anim = d.animateProperty(d.mixin({
6448                         properties: {
6449                                 top: args.top || 0,
6450                                 left: args.left || 0
6451                         }
6452                 }, args));
6453                 d.connect(anim, "beforeBegin", anim, init);
6454
6455                 return anim; // dojo.Animation
6456         }
6457
6458 })();
6459
6460 }
6461
6462 if(!dojo._hasResource["dojo.NodeList-fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6463 dojo._hasResource["dojo.NodeList-fx"] = true;
6464 dojo.provide("dojo.NodeList-fx");
6465
6466
6467 /*=====
6468 dojo["NodeList-fx"] = {
6469         // summary: Adds dojo.fx animation support to dojo.query()
6470 };
6471 =====*/
6472
6473 dojo.extend(dojo.NodeList, {
6474         _anim: function(obj, method, args){
6475                 args = args||{};
6476                 var a = dojo.fx.combine(
6477                         this.map(function(item){
6478                                 var tmpArgs = { node: item };
6479                                 dojo.mixin(tmpArgs, args);
6480                                 return obj[method](tmpArgs);
6481                         })
6482                 ); 
6483                 return args.auto ? a.play() && this : a; // dojo.Animation|dojo.NodeList
6484         },
6485
6486         wipeIn: function(args){
6487                 //      summary:
6488                 //              wipe in all elements of this NodeList via `dojo.fx.wipeIn`
6489                 //
6490                 //      args: Object?
6491                 //              Additional dojo.Animation arguments to mix into this set with the addition of 
6492                 //              an `auto` parameter.
6493                 //
6494                 //      returns: dojo.Animation|dojo.NodeList
6495                 //              A special args member `auto` can be passed to automatically play the animation.
6496                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6497                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6498                 //
6499                 //      example:
6500                 //              Fade in all tables with class "blah":
6501                 //              |       dojo.query("table.blah").wipeIn().play();
6502                 //
6503                 //      example:
6504                 //              Utilizing `auto` to get the NodeList back:
6505                 //              |       dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
6506                 //
6507                 return this._anim(dojo.fx, "wipeIn", args); // dojo.Animation|dojo.NodeList
6508         },
6509
6510         wipeOut: function(args){
6511                 //      summary:
6512                 //              wipe out all elements of this NodeList via `dojo.fx.wipeOut`
6513                 //
6514                 //      args: Object?
6515                 //              Additional dojo.Animation arguments to mix into this set with the addition of 
6516                 //              an `auto` parameter.
6517                 //
6518                 //      returns: dojo.Animation|dojo.NodeList
6519                 //              A special args member `auto` can be passed to automatically play the animation.
6520                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6521                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6522                 //
6523                 //      example:
6524                 //              Wipe out all tables with class "blah":
6525                 //              |       dojo.query("table.blah").wipeOut().play();
6526                 return this._anim(dojo.fx, "wipeOut", args); // dojo.Animation|dojo.NodeList
6527         },
6528
6529         slideTo: function(args){
6530                 //      summary:
6531                 //              slide all elements of the node list to the specified place via `dojo.fx.slideTo`
6532                 //
6533                 //      args: Object?
6534                 //              Additional dojo.Animation arguments to mix into this set with the addition of 
6535                 //              an `auto` parameter.
6536                 //
6537                 //      returns: dojo.Animation|dojo.NodeList
6538                 //              A special args member `auto` can be passed to automatically play the animation.
6539                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6540                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6541                 //
6542                 //      example:
6543                 //              |       Move all tables with class "blah" to 300/300:
6544                 //              |       dojo.query("table.blah").slideTo({
6545                 //              |               left: 40,
6546                 //              |               top: 50
6547                 //              |       }).play();
6548                 return this._anim(dojo.fx, "slideTo", args); // dojo.Animation|dojo.NodeList
6549         },
6550
6551
6552         fadeIn: function(args){
6553                 //      summary:
6554                 //              fade in all elements of this NodeList via `dojo.fadeIn`
6555                 //
6556                 //      args: Object?
6557                 //              Additional dojo.Animation arguments to mix into this set with the addition of 
6558                 //              an `auto` parameter.
6559                 //
6560                 //      returns: dojo.Animation|dojo.NodeList
6561                 //              A special args member `auto` can be passed to automatically play the animation.
6562                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6563                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6564                 //
6565                 //      example:
6566                 //              Fade in all tables with class "blah":
6567                 //              |       dojo.query("table.blah").fadeIn().play();
6568                 return this._anim(dojo, "fadeIn", args); // dojo.Animation|dojo.NodeList
6569         },
6570
6571         fadeOut: function(args){
6572                 //      summary:
6573                 //              fade out all elements of this NodeList via `dojo.fadeOut`
6574                 //
6575                 //      args: Object?
6576                 //              Additional dojo.Animation arguments to mix into this set with the addition of 
6577                 //              an `auto` parameter.
6578                 //
6579                 //      returns: dojo.Animation|dojo.NodeList
6580                 //              A special args member `auto` can be passed to automatically play the animation.
6581                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6582                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6583                 //
6584                 //      example:
6585                 //              Fade out all elements with class "zork":
6586                 //              |       dojo.query(".zork").fadeOut().play();
6587                 //      example:
6588                 //              Fade them on a delay and do something at the end:
6589                 //              |       var fo = dojo.query(".zork").fadeOut();
6590                 //              |       dojo.connect(fo, "onEnd", function(){ /*...*/ });
6591                 //              |       fo.play();
6592                 //      example:
6593                 //              Using `auto`:
6594                 //              |       dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
6595                 //
6596                 return this._anim(dojo, "fadeOut", args); // dojo.Animation|dojo.NodeList
6597         },
6598
6599         animateProperty: function(args){
6600                 //      summary:
6601                 //              Animate all elements of this NodeList across the properties specified.
6602                 //              syntax identical to `dojo.animateProperty`
6603                 //
6604                 // returns: dojo.Animation|dojo.NodeList
6605                 //              A special args member `auto` can be passed to automatically play the animation.
6606                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6607                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6608                 //
6609                 //      example:
6610                 //      |       dojo.query(".zork").animateProperty({
6611                 //      |               duration: 500,
6612                 //      |               properties: { 
6613                 //      |                       color:          { start: "black", end: "white" },
6614                 //      |                       left:           { end: 300 } 
6615                 //      |               } 
6616                 //      |       }).play();
6617                 //
6618                 //      example:
6619                 //      |       dojo.query(".grue").animateProperty({ 
6620                 //      |               auto:true,
6621                 //      |               properties: {
6622                 //      |                       height:240
6623                 //      |               }
6624                 //      |       }).onclick(handler);
6625                 return this._anim(dojo, "animateProperty", args); // dojo.Animation|dojo.NodeList
6626         },
6627
6628         anim: function( /*Object*/                      properties, 
6629                                         /*Integer?*/            duration, 
6630                                         /*Function?*/           easing, 
6631                                         /*Function?*/           onEnd,
6632                                         /*Integer?*/            delay){
6633                 //      summary:
6634                 //              Animate one or more CSS properties for all nodes in this list.
6635                 //              The returned animation object will already be playing when it
6636                 //              is returned. See the docs for `dojo.anim` for full details.
6637                 //      properties: Object
6638                 //              the properties to animate. does NOT support the `auto` parameter like other 
6639                 //              NodeList-fx methods. 
6640                 //      duration: Integer?
6641                 //              Optional. The time to run the animations for
6642                 //      easing: Function?
6643                 //              Optional. The easing function to use.
6644                 //      onEnd: Function?
6645                 //              A function to be called when the animation ends
6646                 //      delay:
6647                 //              how long to delay playing the returned animation
6648                 //      example:
6649                 //              Another way to fade out:
6650                 //      |       dojo.query(".thinger").anim({ opacity: 0 });
6651                 //      example:
6652                 //              animate all elements with the "thigner" class to a width of 500
6653                 //              pixels over half a second
6654                 //      |       dojo.query(".thinger").anim({ width: 500 }, 700);
6655                 var canim = dojo.fx.combine(
6656                         this.map(function(item){
6657                                 return dojo.animateProperty({
6658                                         node: item,
6659                                         properties: properties,
6660                                         duration: duration||350,
6661                                         easing: easing
6662                                 });
6663                         })
6664                 ); 
6665                 if(onEnd){
6666                         dojo.connect(canim, "onEnd", onEnd);
6667                 }
6668                 return canim.play(delay||0); // dojo.Animation
6669         }
6670 });
6671
6672 }
6673
6674 if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6675 dojo._hasResource["dojo.colors"] = true;
6676 dojo.provide("dojo.colors");
6677
6678 //TODO: this module appears to break naming conventions
6679
6680 /*=====
6681 dojo.colors = {
6682         // summary: Color utilities
6683 }
6684 =====*/
6685
6686 (function(){
6687         // this is a standard conversion prescribed by the CSS3 Color Module
6688         var hue2rgb = function(m1, m2, h){
6689                 if(h < 0){ ++h; }
6690                 if(h > 1){ --h; }
6691                 var h6 = 6 * h;
6692                 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
6693                 if(2 * h < 1){ return m2; }
6694                 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
6695                 return m1;
6696         };
6697         
6698         dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
6699                 // summary:
6700                 //              get rgb(a) array from css-style color declarations
6701                 // description:
6702                 //              this function can handle all 4 CSS3 Color Module formats: rgb,
6703                 //              rgba, hsl, hsla, including rgb(a) with percentage values.
6704                 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
6705                 if(m){
6706                         var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
6707                         if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
6708                                 var r = c[0];
6709                                 if(r.charAt(r.length - 1) == "%"){
6710                                         // 3 rgb percentage values
6711                                         a = dojo.map(c, function(x){
6712                                                 return parseFloat(x) * 2.56;
6713                                         });
6714                                         if(l == 4){ a[3] = c[3]; }
6715                                         return dojo.colorFromArray(a, obj);     // dojo.Color
6716                                 }
6717                                 return dojo.colorFromArray(c, obj);     // dojo.Color
6718                         }
6719                         if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
6720                                 // normalize hsl values
6721                                 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
6722                                         S = parseFloat(c[1]) / 100,
6723                                         L = parseFloat(c[2]) / 100,
6724                                         // calculate rgb according to the algorithm 
6725                                         // recommended by the CSS3 Color Module 
6726                                         m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, 
6727                                         m1 = 2 * L - m2;
6728                                 a = [
6729                                         hue2rgb(m1, m2, H + 1 / 3) * 256,
6730                                         hue2rgb(m1, m2, H) * 256,
6731                                         hue2rgb(m1, m2, H - 1 / 3) * 256,
6732                                         1
6733                                 ];
6734                                 if(l == 4){ a[3] = c[3]; }
6735                                 return dojo.colorFromArray(a, obj);     // dojo.Color
6736                         }
6737                 }
6738                 return null;    // dojo.Color
6739         };
6740         
6741         var confine = function(c, low, high){
6742                 // summary:
6743                 //              sanitize a color component by making sure it is a number,
6744                 //              and clamping it to valid values
6745                 c = Number(c);
6746                 return isNaN(c) ? high : c < low ? low : c > high ? high : c;   // Number
6747         };
6748         
6749         dojo.Color.prototype.sanitize = function(){
6750                 // summary: makes sure that the object has correct attributes
6751                 var t = this;
6752                 t.r = Math.round(confine(t.r, 0, 255));
6753                 t.g = Math.round(confine(t.g, 0, 255));
6754                 t.b = Math.round(confine(t.b, 0, 255));
6755                 t.a = confine(t.a, 0, 1);
6756                 return this;    // dojo.Color
6757         };
6758 })();
6759
6760
6761 dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
6762         // summary: creates a greyscale color with an optional alpha
6763         return dojo.colorFromArray([g, g, g, a]);
6764 };
6765
6766 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
6767 dojo.mixin(dojo.Color.named, {
6768         aliceblue:      [240,248,255],
6769         antiquewhite:   [250,235,215],
6770         aquamarine:     [127,255,212],
6771         azure:  [240,255,255],
6772         beige:  [245,245,220],
6773         bisque: [255,228,196],
6774         blanchedalmond: [255,235,205],
6775         blueviolet:     [138,43,226],
6776         brown:  [165,42,42],
6777         burlywood:      [222,184,135],
6778         cadetblue:      [95,158,160],
6779         chartreuse:     [127,255,0],
6780         chocolate:      [210,105,30],
6781         coral:  [255,127,80],
6782         cornflowerblue: [100,149,237],
6783         cornsilk:       [255,248,220],
6784         crimson:        [220,20,60],
6785         cyan:   [0,255,255],
6786         darkblue:       [0,0,139],
6787         darkcyan:       [0,139,139],
6788         darkgoldenrod:  [184,134,11],
6789         darkgray:       [169,169,169],
6790         darkgreen:      [0,100,0],
6791         darkgrey:       [169,169,169],
6792         darkkhaki:      [189,183,107],
6793         darkmagenta:    [139,0,139],
6794         darkolivegreen: [85,107,47],
6795         darkorange:     [255,140,0],
6796         darkorchid:     [153,50,204],
6797         darkred:        [139,0,0],
6798         darksalmon:     [233,150,122],
6799         darkseagreen:   [143,188,143],
6800         darkslateblue:  [72,61,139],
6801         darkslategray:  [47,79,79],
6802         darkslategrey:  [47,79,79],
6803         darkturquoise:  [0,206,209],
6804         darkviolet:     [148,0,211],
6805         deeppink:       [255,20,147],
6806         deepskyblue:    [0,191,255],
6807         dimgray:        [105,105,105],
6808         dimgrey:        [105,105,105],
6809         dodgerblue:     [30,144,255],
6810         firebrick:      [178,34,34],
6811         floralwhite:    [255,250,240],
6812         forestgreen:    [34,139,34],
6813         gainsboro:      [220,220,220],
6814         ghostwhite:     [248,248,255],
6815         gold:   [255,215,0],
6816         goldenrod:      [218,165,32],
6817         greenyellow:    [173,255,47],
6818         grey:   [128,128,128],
6819         honeydew:       [240,255,240],
6820         hotpink:        [255,105,180],
6821         indianred:      [205,92,92],
6822         indigo: [75,0,130],
6823         ivory:  [255,255,240],
6824         khaki:  [240,230,140],
6825         lavender:       [230,230,250],
6826         lavenderblush:  [255,240,245],
6827         lawngreen:      [124,252,0],
6828         lemonchiffon:   [255,250,205],
6829         lightblue:      [173,216,230],
6830         lightcoral:     [240,128,128],
6831         lightcyan:      [224,255,255],
6832         lightgoldenrodyellow:   [250,250,210],
6833         lightgray:      [211,211,211],
6834         lightgreen:     [144,238,144],
6835         lightgrey:      [211,211,211],
6836         lightpink:      [255,182,193],
6837         lightsalmon:    [255,160,122],
6838         lightseagreen:  [32,178,170],
6839         lightskyblue:   [135,206,250],
6840         lightslategray: [119,136,153],
6841         lightslategrey: [119,136,153],
6842         lightsteelblue: [176,196,222],
6843         lightyellow:    [255,255,224],
6844         limegreen:      [50,205,50],
6845         linen:  [250,240,230],
6846         magenta:        [255,0,255],
6847         mediumaquamarine:       [102,205,170],
6848         mediumblue:     [0,0,205],
6849         mediumorchid:   [186,85,211],
6850         mediumpurple:   [147,112,219],
6851         mediumseagreen: [60,179,113],
6852         mediumslateblue:        [123,104,238],
6853         mediumspringgreen:      [0,250,154],
6854         mediumturquoise:        [72,209,204],
6855         mediumvioletred:        [199,21,133],
6856         midnightblue:   [25,25,112],
6857         mintcream:      [245,255,250],
6858         mistyrose:      [255,228,225],
6859         moccasin:       [255,228,181],
6860         navajowhite:    [255,222,173],
6861         oldlace:        [253,245,230],
6862         olivedrab:      [107,142,35],
6863         orange: [255,165,0],
6864         orangered:      [255,69,0],
6865         orchid: [218,112,214],
6866         palegoldenrod:  [238,232,170],
6867         palegreen:      [152,251,152],
6868         paleturquoise:  [175,238,238],
6869         palevioletred:  [219,112,147],
6870         papayawhip:     [255,239,213],
6871         peachpuff:      [255,218,185],
6872         peru:   [205,133,63],
6873         pink:   [255,192,203],
6874         plum:   [221,160,221],
6875         powderblue:     [176,224,230],
6876         rosybrown:      [188,143,143],
6877         royalblue:      [65,105,225],
6878         saddlebrown:    [139,69,19],
6879         salmon: [250,128,114],
6880         sandybrown:     [244,164,96],
6881         seagreen:       [46,139,87],
6882         seashell:       [255,245,238],
6883         sienna: [160,82,45],
6884         skyblue:        [135,206,235],
6885         slateblue:      [106,90,205],
6886         slategray:      [112,128,144],
6887         slategrey:      [112,128,144],
6888         snow:   [255,250,250],
6889         springgreen:    [0,255,127],
6890         steelblue:      [70,130,180],
6891         tan:    [210,180,140],
6892         thistle:        [216,191,216],
6893         tomato: [255,99,71],
6894         transparent: [0, 0, 0, 0],
6895         turquoise:      [64,224,208],
6896         violet: [238,130,238],
6897         wheat:  [245,222,179],
6898         whitesmoke:     [245,245,245],
6899         yellowgreen:    [154,205,50]
6900 });
6901
6902 }
6903
6904 if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6905 dojo._hasResource["dojo.i18n"] = true;
6906 dojo.provide("dojo.i18n");
6907
6908 /*=====
6909 dojo.i18n = {
6910         // summary: Utility classes to enable loading of resources for internationalization (i18n)
6911 };
6912 =====*/
6913
6914 dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
6915         //      summary:
6916         //              Returns an Object containing the localization for a given resource
6917         //              bundle in a package, matching the specified locale.
6918         //      description:
6919         //              Returns a hash containing name/value pairs in its prototypesuch
6920         //              that values can be easily overridden.  Throws an exception if the
6921         //              bundle is not found.  Bundle must have already been loaded by
6922         //              `dojo.requireLocalization()` or by a build optimization step.  NOTE:
6923         //              try not to call this method as part of an object property
6924         //              definition (`var foo = { bar: dojo.i18n.getLocalization() }`).  In
6925         //              some loading situations, the bundle may not be available in time
6926         //              for the object definition.  Instead, call this method inside a
6927         //              function that is run after all modules load or the page loads (like
6928         //              in `dojo.addOnLoad()`), or in a widget lifecycle method.
6929         //      packageName:
6930         //              package which is associated with this resource
6931         //      bundleName:
6932         //              the base filename of the resource bundle (without the ".js" suffix)
6933         //      locale:
6934         //              the variant to load (optional).  By default, the locale defined by
6935         //              the host environment: dojo.locale
6936
6937         locale = dojo.i18n.normalizeLocale(locale);
6938
6939         // look for nearest locale match
6940         var elements = locale.split('-');
6941         var module = [packageName,"nls",bundleName].join('.');
6942         var bundle = dojo._loadedModules[module];
6943         if(bundle){
6944                 var localization;
6945                 for(var i = elements.length; i > 0; i--){
6946                         var loc = elements.slice(0, i).join('_');
6947                         if(bundle[loc]){
6948                                 localization = bundle[loc];
6949                                 break;
6950                         }
6951                 }
6952                 if(!localization){
6953                         localization = bundle.ROOT;
6954                 }
6955
6956                 // make a singleton prototype so that the caller won't accidentally change the values globally
6957                 if(localization){
6958                         var clazz = function(){};
6959                         clazz.prototype = localization;
6960                         return new clazz(); // Object
6961                 }
6962         }
6963
6964         throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
6965 };
6966
6967 dojo.i18n.normalizeLocale = function(/*String?*/locale){
6968         //      summary:
6969         //              Returns canonical form of locale, as used by Dojo.
6970         //
6971         //  description:
6972         //              All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
6973         //              If no locale is specified, the dojo.locale is returned.  dojo.locale is defined by
6974         //              the user agent's locale unless overridden by djConfig.
6975
6976         var result = locale ? locale.toLowerCase() : dojo.locale;
6977         if(result == "root"){
6978                 result = "ROOT";
6979         }
6980         return result; // String
6981 };
6982
6983 dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
6984         //      summary:
6985         //              See dojo.requireLocalization()
6986         //      description:
6987         //              Called by the bootstrap, but factored out so that it is only
6988         //              included in the build when needed.
6989
6990         var targetLocale = dojo.i18n.normalizeLocale(locale);
6991         var bundlePackage = [moduleName, "nls", bundleName].join(".");
6992         // NOTE: 
6993         //              When loading these resources, the packaging does not match what is
6994         //              on disk.  This is an implementation detail, as this is just a
6995         //              private data structure to hold the loaded resources.  e.g.
6996         //              `tests/hello/nls/en-us/salutations.js` is loaded as the object
6997         //              `tests.hello.nls.salutations.en_us={...}` The structure on disk is
6998         //              intended to be most convenient for developers and translators, but
6999         //              in memory it is more logical and efficient to store in a different
7000         //              order.  Locales cannot use dashes, since the resulting path will
7001         //              not evaluate as valid JS, so we translate them to underscores.
7002         
7003         //Find the best-match locale to load if we have available flat locales.
7004         var bestLocale = "";
7005         if(availableFlatLocales){
7006                 var flatLocales = availableFlatLocales.split(",");
7007                 for(var i = 0; i < flatLocales.length; i++){
7008                         //Locale must match from start of string.
7009                         //Using ["indexOf"] so customBase builds do not see
7010                         //this as a dojo._base.array dependency.
7011                         if(targetLocale["indexOf"](flatLocales[i]) == 0){
7012                                 if(flatLocales[i].length > bestLocale.length){
7013                                         bestLocale = flatLocales[i];
7014                                 }
7015                         }
7016                 }
7017                 if(!bestLocale){
7018                         bestLocale = "ROOT";
7019                 }               
7020         }
7021
7022         //See if the desired locale is already loaded.
7023         var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
7024         var bundle = dojo._loadedModules[bundlePackage];
7025         var localizedBundle = null;
7026         if(bundle){
7027                 if(dojo.config.localizationComplete && bundle._built){return;}
7028                 var jsLoc = tempLocale.replace(/-/g, '_');
7029                 var translationPackage = bundlePackage+"."+jsLoc;
7030                 localizedBundle = dojo._loadedModules[translationPackage];
7031         }
7032
7033         if(!localizedBundle){
7034                 bundle = dojo["provide"](bundlePackage);
7035                 var syms = dojo._getModuleSymbols(moduleName);
7036                 var modpath = syms.concat("nls").join("/");
7037                 var parent;
7038
7039                 dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
7040                         var jsLoc = loc.replace(/-/g, '_');
7041                         var translationPackage = bundlePackage + "." + jsLoc;
7042                         var loaded = false;
7043                         if(!dojo._loadedModules[translationPackage]){
7044                                 // Mark loaded whether it's found or not, so that further load attempts will not be made
7045                                 dojo["provide"](translationPackage);
7046                                 var module = [modpath];
7047                                 if(loc != "ROOT"){module.push(loc);}
7048                                 module.push(bundleName);
7049                                 var filespec = module.join("/") + '.js';
7050                                 loaded = dojo._loadPath(filespec, null, function(hash){
7051                                         // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
7052                                         var clazz = function(){};
7053                                         clazz.prototype = parent;
7054                                         bundle[jsLoc] = new clazz();
7055                                         for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
7056                                 });
7057                         }else{
7058                                 loaded = true;
7059                         }
7060                         if(loaded && bundle[jsLoc]){
7061                                 parent = bundle[jsLoc];
7062                         }else{
7063                                 bundle[jsLoc] = parent;
7064                         }
7065                         
7066                         if(availableFlatLocales){
7067                                 //Stop the locale path searching if we know the availableFlatLocales, since
7068                                 //the first call to this function will load the only bundle that is needed.
7069                                 return true;
7070                         }
7071                 });
7072         }
7073
7074         //Save the best locale bundle as the target locale bundle when we know the
7075         //the available bundles.
7076         if(availableFlatLocales && targetLocale != bestLocale){
7077                 bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
7078         }
7079 };
7080
7081 (function(){
7082         // If other locales are used, dojo.requireLocalization should load them as
7083         // well, by default. 
7084         // 
7085         // Override dojo.requireLocalization to do load the default bundle, then
7086         // iterate through the extraLocale list and load those translations as
7087         // well, unless a particular locale was requested.
7088
7089         var extra = dojo.config.extraLocale;
7090         if(extra){
7091                 if(!extra instanceof Array){
7092                         extra = [extra];
7093                 }
7094
7095                 var req = dojo.i18n._requireLocalization;
7096                 dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
7097                         req(m,b,locale, availableFlatLocales);
7098                         if(locale){return;}
7099                         for(var i=0; i<extra.length; i++){
7100                                 req(m,b,extra[i], availableFlatLocales);
7101                         }
7102                 };
7103         }
7104 })();
7105
7106 dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
7107         //      summary:
7108         //              A helper method to assist in searching for locale-based resources.
7109         //              Will iterate through the variants of a particular locale, either up
7110         //              or down, executing a callback function.  For example, "en-us" and
7111         //              true will try "en-us" followed by "en" and finally "ROOT".
7112
7113         locale = dojo.i18n.normalizeLocale(locale);
7114
7115         var elements = locale.split('-');
7116         var searchlist = [];
7117         for(var i = elements.length; i > 0; i--){
7118                 searchlist.push(elements.slice(0, i).join('-'));
7119         }
7120         searchlist.push(false);
7121         if(down){searchlist.reverse();}
7122
7123         for(var j = searchlist.length - 1; j >= 0; j--){
7124                 var loc = searchlist[j] || "ROOT";
7125                 var stop = searchFunc(loc);
7126                 if(stop){ break; }
7127         }
7128 };
7129
7130 dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
7131         //      summary:
7132         //              Load built, flattened resource bundles, if available for all
7133         //              locales used in the page. Only called by built layer files.
7134
7135         function preload(locale){
7136                 locale = dojo.i18n.normalizeLocale(locale);
7137                 dojo.i18n._searchLocalePath(locale, true, function(loc){
7138                         for(var i=0; i<localesGenerated.length;i++){
7139                                 if(localesGenerated[i] == loc){
7140                                         dojo["require"](bundlePrefix+"_"+loc);
7141                                         return true; // Boolean
7142                                 }
7143                         }
7144                         return false; // Boolean
7145                 });
7146         }
7147         preload();
7148         var extra = dojo.config.extraLocale||[];
7149         for(var i=0; i<extra.length; i++){
7150                 preload(extra[i]);
7151         }
7152 };
7153
7154 }
7155
7156 if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7157 dojo._hasResource["dijit._PaletteMixin"] = true;
7158 dojo.provide("dijit._PaletteMixin");
7159
7160
7161 dojo.declare("dijit._PaletteMixin",
7162         [dijit._CssStateMixin],
7163         {
7164         // summary:
7165         //              A keyboard accessible palette, for picking a color/emoticon/etc.
7166         // description:
7167         //              A mixin for a grid showing various entities, so the user can pick a certain entity.
7168
7169         // defaultTimeout: Number
7170         //              Number of milliseconds before a held key or button becomes typematic
7171         defaultTimeout: 500,
7172
7173         // timeoutChangeRate: Number
7174         //              Fraction of time used to change the typematic timer between events
7175         //              1.0 means that each typematic event fires at defaultTimeout intervals
7176         //              < 1.0 means that each typematic event fires at an increasing faster rate
7177         timeoutChangeRate: 0.90,
7178
7179         // value: String
7180         //              Currently selected color/emoticon/etc.
7181         value: null,
7182         
7183         // _selectedCell: [private] Integer
7184         //              Index of the currently selected cell. Initially, none selected
7185         _selectedCell: -1,
7186
7187         // _currentFocus: [private] DomNode
7188         //              The currently focused cell (if the palette itself has focus), or otherwise
7189         //              the cell to be focused when the palette itself gets focus.
7190         //              Different from value, which represents the selected (i.e. clicked) cell.
7191 /*=====
7192         _currentFocus: null,
7193 =====*/
7194
7195         // _xDim: [protected] Integer
7196         //              This is the number of cells horizontally across.
7197 /*=====
7198         _xDim: null,
7199 =====*/
7200
7201         // _yDim: [protected] Integer
7202         //              This is the number of cells vertically down.
7203 /*=====
7204         _yDim: null,
7205 =====*/
7206
7207         // tabIndex: String
7208         //              Widget tab index.
7209         tabIndex: "0",
7210
7211         // cellClass: [protected] String
7212         //              CSS class applied to each cell in the palette
7213         cellClass: "dijitPaletteCell",
7214
7215         // dyeClass: [protected] String
7216         //       Name of javascript class for Object created for each cell of the palette.
7217         //       dyeClass should implements dijit.Dye interface
7218         dyeClass: '',
7219
7220         _preparePalette: function(choices, titles) {
7221                 // summary:
7222                 //              Subclass must call _preparePalette() from postCreate(), passing in the tooltip
7223                 //              for each cell
7224                 // choices: String[][]
7225                 //              id's for each cell of the palette, used to create Dye JS object for each cell
7226                 // titles: String[]
7227                 //              Localized tooltip for each cell
7228
7229                 this._cells = [];
7230                 var url = this._blankGif;
7231                 
7232                 var dyeClassObj = dojo.getObject(this.dyeClass);
7233
7234                 for(var row=0; row < choices.length; row++){
7235                         var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
7236                         for(var col=0; col < choices[row].length; col++){
7237                                 var value = choices[row][col];
7238                                 if(value){
7239                                         var cellObject = new dyeClassObj(value);
7240                                         
7241                                         var cellNode = dojo.create("td", {
7242                                                 "class": this.cellClass,
7243                                                 tabIndex: "-1",
7244                                                 title: titles[value]
7245                                         });
7246
7247                                         // prepare cell inner structure
7248                                         cellObject.fillCell(cellNode, url);
7249
7250                                         this.connect(cellNode, "ondijitclick", "_onCellClick");
7251                                         this._trackMouseState(cellNode, this.cellClass);
7252
7253                                         dojo.place(cellNode, rowNode);
7254
7255                                         cellNode.index = this._cells.length;
7256
7257                                         // save cell info into _cells
7258                                         this._cells.push({node:cellNode, dye:cellObject});
7259                                 }
7260                         }
7261                 }
7262                 this._xDim = choices[0].length;
7263                 this._yDim = choices.length;
7264
7265                 // Now set all events
7266                 // The palette itself is navigated to with the tab key on the keyboard
7267                 // Keyboard navigation within the Palette is with the arrow keys
7268                 // Spacebar selects the cell.
7269                 // For the up key the index is changed by negative the x dimension.
7270
7271                 var keyIncrementMap = {
7272                         UP_ARROW: -this._xDim,
7273                         // The down key the index is increase by the x dimension.
7274                         DOWN_ARROW: this._xDim,
7275                         // Right and left move the index by 1.
7276                         RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
7277                         LEFT_ARROW: this.isLeftToRight() ? -1 : 1
7278                 };
7279                 for(var key in keyIncrementMap){
7280                         this._connects.push(
7281                                 dijit.typematic.addKeyListener(
7282                                         this.domNode,
7283                                         {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
7284                                         this,
7285                                         function(){
7286                                                 var increment = keyIncrementMap[key];
7287                                                 return function(count){ this._navigateByKey(increment, count); };
7288                                         }(),
7289                                         this.timeoutChangeRate,
7290                                         this.defaultTimeout
7291                                 )
7292                         );
7293                 }
7294         },
7295
7296         postCreate: function(){
7297                 this.inherited(arguments);
7298
7299                 // Set initial navigable node.
7300                 this._setCurrent(this._cells[0].node);
7301         },
7302
7303         focus: function(){
7304                 // summary:
7305                 //              Focus this widget.  Puts focus on the most recently focused cell.
7306
7307                 // The cell already has tabIndex set, just need to set CSS and focus it
7308                 dijit.focus(this._currentFocus);
7309         },
7310
7311         _onCellClick: function(/*Event*/ evt){
7312                 // summary:
7313                 //              Handler for click, enter key & space key. Selects the cell.
7314                 // evt:
7315                 //              The event.
7316                 // tags:
7317                 //              private
7318
7319                 var target = evt.currentTarget, 
7320                         value = this._getDye(target).getValue();
7321
7322                 // First focus the clicked cell, and then send onChange() notification.
7323                 // onChange() (via _setValueAttr) must be after the focus call, because
7324                 // it may trigger a refocus to somewhere else (like the Editor content area), and that
7325                 // second focus should win.
7326                 // Use setTimeout because IE doesn't like changing focus inside of an event handler.
7327                 this._setCurrent(target);
7328                 setTimeout(dojo.hitch(this, function(){
7329                         dijit.focus(target);            
7330                         this._setValueAttr(value, true);                
7331                 }));
7332
7333                 // workaround bug where hover class is not removed on popup because the popup is
7334                 // closed and then there's no onblur event on the cell
7335                 dojo.removeClass(target, "dijitPaletteCellHover");
7336
7337                 dojo.stopEvent(evt);
7338         },
7339
7340         _setCurrent: function(/*DomNode*/ node){
7341                 // summary:
7342                 //              Sets which node is the focused cell.
7343                 // description:
7344                 //              At any point in time there's exactly one
7345                 //              cell with tabIndex != -1.   If focus is inside the palette then
7346                 //              focus is on that cell.
7347                 //
7348                 //              After calling this method, arrow key handlers and mouse click handlers
7349                 //              should focus the cell in a setTimeout().
7350                 // tags:
7351                 //              protected
7352                 if("_currentFocus" in this){
7353                         // Remove tabIndex on old cell
7354                         dojo.attr(this._currentFocus, "tabIndex", "-1");
7355                 }
7356
7357                 // Set tabIndex of new cell
7358                 this._currentFocus = node;
7359                 if(node){
7360                         dojo.attr(node, "tabIndex", this.tabIndex);
7361                 }
7362         },
7363
7364         _setValueAttr: function(value, priorityChange){
7365                 // summary:
7366                 //              This selects a cell. It triggers the onChange event.
7367                 // value: String value of the cell to select
7368                 // tags:
7369                 //              protected
7370                 // priorityChange:
7371                 //              Optional parameter used to tell the select whether or not to fire
7372                 //              onChange event.
7373                 
7374                 // clear old value and selected cell
7375                 this.value = null;
7376                 if(this._selectedCell >= 0){
7377                         dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
7378                 }
7379                 this._selectedCell = -1;
7380
7381                 // search for cell matching specified value
7382                 if(value){
7383                         for(var i = 0; i < this._cells.length; i++){
7384                                 if(value == this._cells[i].dye.getValue()){
7385                                         this._selectedCell = i;
7386                                         this.value = value;
7387
7388                                         dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
7389
7390                                         if(priorityChange || priorityChange === undefined){
7391                                                 this.onChange(value);
7392                                         }
7393
7394                                         break;
7395                                 }
7396                         }
7397                 }
7398         },
7399
7400         onChange: function(value){
7401                 // summary:
7402                 //              Callback when a cell is selected.
7403                 // value: String
7404                 //              Value corresponding to cell.
7405         },
7406
7407         _navigateByKey: function(increment, typeCount){
7408                 // summary:
7409                 //              This is the callback for typematic.
7410                 //              It changes the focus and the highlighed cell.
7411                 // increment:
7412                 //              How much the key is navigated.
7413                 // typeCount:
7414                 //              How many times typematic has fired.
7415                 // tags:
7416                 //              private
7417
7418                 // typecount == -1 means the key is released.
7419                 if(typeCount == -1){ return; }
7420
7421                 var newFocusIndex = this._currentFocus.index + increment;
7422                 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
7423                         var focusNode = this._cells[newFocusIndex].node;
7424                         this._setCurrent(focusNode);
7425
7426                         // Actually focus the node, for the benefit of screen readers.
7427                         // Use setTimeout because IE doesn't like changing focus inside of an event handler
7428                         setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
7429                 }
7430         },
7431
7432         _getDye: function(/*DomNode*/ cell){
7433                 // summary:
7434                 //              Get JS object for given cell DOMNode
7435
7436                 return this._cells[cell.index].dye;
7437         }
7438 });
7439
7440 /*=====
7441 dojo.declare("dijit.Dye",
7442         null,
7443         {
7444                 // summary:
7445                 //              Interface for the JS Object associated with a palette cell (i.e. DOMNode)
7446
7447                 constructor: function(alias){
7448                         // summary:
7449                         //              Initialize according to value or alias like "white"
7450                         // alias: String
7451                 },
7452
7453                 getValue: function(){
7454                         // summary:
7455                         //              Return "value" of cell; meaning of "value" varies by subclass.
7456                         // description:
7457                         //              For example color hex value, emoticon ascii value etc, entity hex value.
7458                 },
7459
7460                 fillCell: function(cell, blankGif){
7461                         // summary:
7462                         //              Add cell DOMNode inner structure
7463                         //      cell: DomNode
7464                         //              The surrounding cell
7465                         //      blankGif: String
7466                         //              URL for blank cell image
7467                 }
7468         }
7469 );
7470 =====*/
7471
7472 }
7473
7474 if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7475 dojo._hasResource["dijit.ColorPalette"] = true;
7476 dojo.provide("dijit.ColorPalette");
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487 dojo.declare("dijit.ColorPalette",
7488         [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
7489         {
7490         // summary:
7491         //              A keyboard accessible color-picking widget
7492         // description:
7493         //              Grid showing various colors, so the user can pick a certain color.
7494         //              Can be used standalone, or as a popup.
7495         //
7496         // example:
7497         // |    <div dojoType="dijit.ColorPalette"></div>
7498         //
7499         // example:
7500         // |    var picker = new dijit.ColorPalette({ },srcNode);
7501         // |    picker.startup();
7502
7503
7504         // palette: String
7505         //              Size of grid, either "7x10" or "3x4".
7506         palette: "7x10",
7507
7508         // _palettes: [protected] Map
7509         //              This represents the value of the colors.
7510         //              The first level is a hashmap of the different palettes available.
7511         //              The next two dimensions represent the columns and rows of colors.
7512         _palettes: {
7513                 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan",     "lavender", "plum"],
7514                                 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
7515                                 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise",      "skyblue", "mediumslateblue","orchid"],
7516                                 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen",       "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
7517                                 ["dimgray", "crimson",  "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
7518                                 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
7519                                 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo",        "purple"]],
7520
7521                 "3x4": [["white", "lime", "green", "blue"],
7522                         ["silver", "yellow", "fuchsia", "navy"],
7523                         ["gray", "red", "purple", "black"]]
7524         },
7525
7526         // _imagePaths: [protected] Map
7527         //              This is stores the path to the palette images
7528         _imagePaths: {
7529                 "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
7530                 "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png"),
7531                 "7x10-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors7x10-rtl.png"),
7532                 "3x4-rtl": dojo.moduleUrl("dijit.themes", "a11y/colors3x4-rtl.png")
7533         },
7534
7535         // templateString: String
7536         //              The template of this widget.
7537         templateString: dojo.cache("dijit", "templates/ColorPalette.html", "<div class=\"dijitInline dijitColorPalette\">\n\t<img class=\"dijitColorPaletteUnder\" dojoAttachPoint=\"imageNode\" waiRole=\"presentation\" alt=\"\"/>\n\t<table class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\">\n\t\t<tbody dojoAttachPoint=\"gridNode\"></tbody>\n\t</table>\n</div>\n"),
7538
7539         baseClass: "dijitColorPalette",
7540
7541         dyeClass: 'dijit._Color',
7542
7543         buildRendering: function(){
7544                 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
7545                 // <img> nodes
7546
7547                 this.inherited(arguments);
7548
7549                 this.imageNode.setAttribute("src", this._imagePaths[this.palette + (this.isLeftToRight() ? "" : "-rtl")].toString());
7550
7551                 var i18nColorNames = dojo.i18n.getLocalization("dojo", "colors", this.lang);
7552                 this._preparePalette(
7553                         this._palettes[this.palette],
7554                         i18nColorNames
7555                 );
7556         }
7557 });
7558
7559 dojo.declare("dijit._Color", dojo.Color,
7560         // summary:
7561         //              Object associated with each cell in a ColorPalette palette.
7562         //              Implements dijit.Dye.
7563         {
7564                 constructor: function(/*String*/alias){
7565                         this._alias = alias;
7566                         this.setColor(dojo.Color.named[alias]);
7567                 },
7568
7569                 getValue: function(){
7570                         // summary:
7571                         //              Note that although dijit._Color is initialized with a value like "white" getValue() always
7572                         //              returns a hex value
7573                         return this.toHex();
7574                 },
7575
7576                 fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
7577                         dojo.create("img", {
7578                                 src: blankGif,
7579                                 "class": "dijitPaletteImg",
7580                                 alt: this._alias
7581                         }, cell);
7582                 }
7583         }
7584 );
7585
7586 }
7587
7588 if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7589 dojo._hasResource["dojo.dnd.common"] = true;
7590 dojo.provide("dojo.dnd.common");
7591
7592 dojo.dnd.getCopyKeyState = dojo.isCopyKey;
7593
7594 dojo.dnd._uniqueId = 0;
7595 dojo.dnd.getUniqueId = function(){
7596         // summary:
7597         //              returns a unique string for use with any DOM element
7598         var id;
7599         do{
7600                 id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
7601         }while(dojo.byId(id));
7602         return id;
7603 };
7604
7605 dojo.dnd._empty = {};
7606
7607 dojo.dnd.isFormElement = function(/*Event*/ e){
7608         // summary:
7609         //              returns true if user clicked on a form element
7610         var t = e.target;
7611         if(t.nodeType == 3 /*TEXT_NODE*/){
7612                 t = t.parentNode;
7613         }
7614         return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;       // Boolean
7615 };
7616
7617 }
7618
7619 if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7620 dojo._hasResource["dojo.dnd.autoscroll"] = true;
7621 dojo.provide("dojo.dnd.autoscroll");
7622
7623 dojo.dnd.getViewport = function(){
7624         // summary:
7625         //              Returns a viewport size (visible part of the window)
7626
7627         // TODO: remove this when getViewport() moved to dojo core, see #7028
7628
7629         // FIXME: need more docs!!
7630         var d = dojo.doc, dd = d.documentElement, w = window, b = dojo.body();
7631         if(dojo.isMozilla){
7632                 return {w: dd.clientWidth, h: w.innerHeight};   // Object
7633         }else if(!dojo.isOpera && w.innerWidth){
7634                 return {w: w.innerWidth, h: w.innerHeight};             // Object
7635         }else if (!dojo.isOpera && dd && dd.clientWidth){
7636                 return {w: dd.clientWidth, h: dd.clientHeight}; // Object
7637         }else if (b.clientWidth){
7638                 return {w: b.clientWidth, h: b.clientHeight};   // Object
7639         }
7640         return null;    // Object
7641 };
7642
7643 dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
7644 dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
7645
7646 dojo.dnd.V_AUTOSCROLL_VALUE = 16;
7647 dojo.dnd.H_AUTOSCROLL_VALUE = 16;
7648
7649 dojo.dnd.autoScroll = function(e){
7650         // summary:
7651         //              a handler for onmousemove event, which scrolls the window, if
7652         //              necesary
7653         // e: Event
7654         //              onmousemove event
7655
7656         // FIXME: needs more docs!
7657         var v = dojo.dnd.getViewport(), dx = 0, dy = 0;
7658         if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
7659                 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
7660         }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
7661                 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
7662         }
7663         if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
7664                 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
7665         }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
7666                 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
7667         }
7668         window.scrollBy(dx, dy);
7669 };
7670
7671 dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
7672 dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
7673
7674 dojo.dnd.autoScrollNodes = function(e){
7675         // summary:
7676         //              a handler for onmousemove event, which scrolls the first avaialble
7677         //              Dom element, it falls back to dojo.dnd.autoScroll()
7678         // e: Event
7679         //              onmousemove event
7680
7681         // FIXME: needs more docs!
7682         for(var n = e.target; n;){
7683                 if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
7684                         var s = dojo.getComputedStyle(n);
7685                         if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
7686                                 var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
7687                                 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
7688                                 var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2), 
7689                                         h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
7690                                         rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
7691                                 if(dojo.isWebKit || dojo.isOpera){
7692                                         // FIXME: this code should not be here, it should be taken into account 
7693                                         // either by the event fixing code, or the dojo.position()
7694                                         // FIXME: this code doesn't work on Opera 9.5 Beta
7695                                         rx += dojo.body().scrollLeft, ry += dojo.body().scrollTop;
7696                                 }
7697                                 if(rx > 0 && rx < b.w){
7698                                         if(rx < w){
7699                                                 dx = -w;
7700                                         }else if(rx > b.w - w){
7701                                                 dx = w;
7702                                         }
7703                                 }
7704                                 //console.log("ry =", ry, "b.h =", b.h, "h =", h);
7705                                 if(ry > 0 && ry < b.h){
7706                                         if(ry < h){
7707                                                 dy = -h;
7708                                         }else if(ry > b.h - h){
7709                                                 dy = h;
7710                                         }
7711                                 }
7712                                 var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
7713                                 n.scrollLeft = n.scrollLeft + dx;
7714                                 n.scrollTop  = n.scrollTop  + dy;
7715                                 if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
7716                         }
7717                 }
7718                 try{
7719                         n = n.parentNode;
7720                 }catch(x){
7721                         n = null;
7722                 }
7723         }
7724         dojo.dnd.autoScroll(e);
7725 };
7726
7727 }
7728
7729 if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7730 dojo._hasResource["dojo.dnd.Mover"] = true;
7731 dojo.provide("dojo.dnd.Mover");
7732
7733
7734
7735
7736 dojo.declare("dojo.dnd.Mover", null, {
7737         constructor: function(node, e, host){
7738                 // summary:
7739                 //              an object, which makes a node follow the mouse. 
7740                 //              Used as a default mover, and as a base class for custom movers.
7741                 // node: Node
7742                 //              a node (or node's id) to be moved
7743                 // e: Event
7744                 //              a mouse event, which started the move;
7745                 //              only pageX and pageY properties are used
7746                 // host: Object?
7747                 //              object which implements the functionality of the move,
7748                 //              and defines proper events (onMoveStart and onMoveStop)
7749                 this.node = dojo.byId(node);
7750                 this.marginBox = {l: e.pageX, t: e.pageY};
7751                 this.mouseButton = e.button;
7752                 var h = this.host = host, d = node.ownerDocument, 
7753                         firstEvent = dojo.connect(d, "onmousemove", this, "onFirstMove");
7754                 this.events = [
7755                         dojo.connect(d, "onmousemove", this, "onMouseMove"),
7756                         dojo.connect(d, "onmouseup",   this, "onMouseUp"),
7757                         // cancel text selection and text dragging
7758                         dojo.connect(d, "ondragstart",   dojo.stopEvent),
7759                         dojo.connect(d.body, "onselectstart", dojo.stopEvent),
7760                         firstEvent
7761                 ];
7762                 // notify that the move has started
7763                 if(h && h.onMoveStart){
7764                         h.onMoveStart(this);
7765                 }
7766         },
7767         // mouse event processors
7768         onMouseMove: function(e){
7769                 // summary:
7770                 //              event processor for onmousemove
7771                 // e: Event
7772                 //              mouse event
7773                 dojo.dnd.autoScroll(e);
7774                 var m = this.marginBox;
7775                 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
7776                 dojo.stopEvent(e);
7777         },
7778         onMouseUp: function(e){
7779                 if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ? 
7780                                 e.button == 0 : this.mouseButton == e.button){
7781                         this.destroy();
7782                 }
7783                 dojo.stopEvent(e);
7784         },
7785         // utilities
7786         onFirstMove: function(e){
7787                 // summary:
7788                 //              makes the node absolute; it is meant to be called only once. 
7789                 //              relative and absolutely positioned nodes are assumed to use pixel units
7790                 var s = this.node.style, l, t, h = this.host;
7791                 switch(s.position){
7792                         case "relative":
7793                         case "absolute":
7794                                 // assume that left and top values are in pixels already
7795                                 l = Math.round(parseFloat(s.left)) || 0;
7796                                 t = Math.round(parseFloat(s.top)) || 0;
7797                                 break;
7798                         default:
7799                                 s.position = "absolute";        // enforcing the absolute mode
7800                                 var m = dojo.marginBox(this.node);
7801                                 // event.pageX/pageY (which we used to generate the initial
7802                                 // margin box) includes padding and margin set on the body.
7803                                 // However, setting the node's position to absolute and then
7804                                 // doing dojo.marginBox on it *doesn't* take that additional
7805                                 // space into account - so we need to subtract the combined
7806                                 // padding and margin.  We use getComputedStyle and
7807                                 // _getMarginBox/_getContentBox to avoid the extra lookup of
7808                                 // the computed style. 
7809                                 var b = dojo.doc.body;
7810                                 var bs = dojo.getComputedStyle(b);
7811                                 var bm = dojo._getMarginBox(b, bs);
7812                                 var bc = dojo._getContentBox(b, bs);
7813                                 l = m.l - (bc.l - bm.l);
7814                                 t = m.t - (bc.t - bm.t);
7815                                 break;
7816                 }
7817                 this.marginBox.l = l - this.marginBox.l;
7818                 this.marginBox.t = t - this.marginBox.t;
7819                 if(h && h.onFirstMove){
7820                         h.onFirstMove(this, e);
7821                 }
7822                 dojo.disconnect(this.events.pop());
7823         },
7824         destroy: function(){
7825                 // summary:
7826                 //              stops the move, deletes all references, so the object can be garbage-collected
7827                 dojo.forEach(this.events, dojo.disconnect);
7828                 // undo global settings
7829                 var h = this.host;
7830                 if(h && h.onMoveStop){
7831                         h.onMoveStop(this);
7832                 }
7833                 // destroy objects
7834                 this.events = this.node = this.host = null;
7835         }
7836 });
7837
7838 }
7839
7840 if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7841 dojo._hasResource["dojo.dnd.Moveable"] = true;
7842 dojo.provide("dojo.dnd.Moveable");
7843
7844
7845
7846 /*=====
7847 dojo.declare("dojo.dnd.__MoveableArgs", [], {
7848         // handle: Node||String
7849         //              A node (or node's id), which is used as a mouse handle.
7850         //              If omitted, the node itself is used as a handle.
7851         handle: null,
7852
7853         // delay: Number
7854         //              delay move by this number of pixels
7855         delay: 0,
7856
7857         // skip: Boolean
7858         //              skip move of form elements
7859         skip: false,
7860
7861         // mover: Object
7862         //              a constructor of custom Mover
7863         mover: dojo.dnd.Mover
7864 });
7865 =====*/
7866
7867 dojo.declare("dojo.dnd.Moveable", null, {
7868         // object attributes (for markup)
7869         handle: "",
7870         delay: 0,
7871         skip: false,
7872         
7873         constructor: function(node, params){
7874                 // summary:
7875                 //              an object, which makes a node moveable
7876                 // node: Node
7877                 //              a node (or node's id) to be moved
7878                 // params: dojo.dnd.__MoveableArgs?
7879                 //              optional parameters
7880                 this.node = dojo.byId(node);
7881                 if(!params){ params = {}; }
7882                 this.handle = params.handle ? dojo.byId(params.handle) : null;
7883                 if(!this.handle){ this.handle = this.node; }
7884                 this.delay = params.delay > 0 ? params.delay : 0;
7885                 this.skip  = params.skip;
7886                 this.mover = params.mover ? params.mover : dojo.dnd.Mover;
7887                 this.events = [
7888                         dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
7889                         // cancel text selection and text dragging
7890                         dojo.connect(this.handle, "ondragstart",   this, "onSelectStart"),
7891                         dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
7892                 ];
7893         },
7894
7895         // markup methods
7896         markupFactory: function(params, node){
7897                 return new dojo.dnd.Moveable(node, params);
7898         },
7899
7900         // methods
7901         destroy: function(){
7902                 // summary:
7903                 //              stops watching for possible move, deletes all references, so the object can be garbage-collected
7904                 dojo.forEach(this.events, dojo.disconnect);
7905                 this.events = this.node = this.handle = null;
7906         },
7907         
7908         // mouse event processors
7909         onMouseDown: function(e){
7910                 // summary:
7911                 //              event processor for onmousedown, creates a Mover for the node
7912                 // e: Event
7913                 //              mouse event
7914                 if(this.skip && dojo.dnd.isFormElement(e)){ return; }
7915                 if(this.delay){
7916                         this.events.push(
7917                                 dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
7918                                 dojo.connect(this.handle, "onmouseup", this, "onMouseUp")
7919                         );
7920                         this._lastX = e.pageX;
7921                         this._lastY = e.pageY;
7922                 }else{
7923                         this.onDragDetected(e);
7924                 }
7925                 dojo.stopEvent(e);
7926         },
7927         onMouseMove: function(e){
7928                 // summary:
7929                 //              event processor for onmousemove, used only for delayed drags
7930                 // e: Event
7931                 //              mouse event
7932                 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
7933                         this.onMouseUp(e);
7934                         this.onDragDetected(e);
7935                 }
7936                 dojo.stopEvent(e);
7937         },
7938         onMouseUp: function(e){
7939                 // summary:
7940                 //              event processor for onmouseup, used only for delayed drags
7941                 // e: Event
7942                 //              mouse event
7943                 for(var i = 0; i < 2; ++i){
7944                         dojo.disconnect(this.events.pop());
7945                 }
7946                 dojo.stopEvent(e);
7947         },
7948         onSelectStart: function(e){
7949                 // summary:
7950                 //              event processor for onselectevent and ondragevent
7951                 // e: Event
7952                 //              mouse event
7953                 if(!this.skip || !dojo.dnd.isFormElement(e)){
7954                         dojo.stopEvent(e);
7955                 }
7956         },
7957         
7958         // local events
7959         onDragDetected: function(/* Event */ e){
7960                 // summary:
7961                 //              called when the drag is detected;
7962                 //              responsible for creation of the mover
7963                 new this.mover(this.node, e, this);
7964         },
7965         onMoveStart: function(/* dojo.dnd.Mover */ mover){
7966                 // summary:
7967                 //              called before every move operation
7968                 dojo.publish("/dnd/move/start", [mover]);
7969                 dojo.addClass(dojo.body(), "dojoMove"); 
7970                 dojo.addClass(this.node, "dojoMoveItem"); 
7971         },
7972         onMoveStop: function(/* dojo.dnd.Mover */ mover){
7973                 // summary:
7974                 //              called after every move operation
7975                 dojo.publish("/dnd/move/stop", [mover]);
7976                 dojo.removeClass(dojo.body(), "dojoMove");
7977                 dojo.removeClass(this.node, "dojoMoveItem");
7978         },
7979         onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
7980                 // summary:
7981                 //              called during the very first move notification;
7982                 //              can be used to initialize coordinates, can be overwritten.
7983                 
7984                 // default implementation does nothing
7985         },
7986         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
7987                 // summary:
7988                 //              called during every move notification;
7989                 //              should actually move the node; can be overwritten.
7990                 this.onMoving(mover, leftTop);
7991                 var s = mover.node.style;
7992                 s.left = leftTop.l + "px";
7993                 s.top  = leftTop.t + "px";
7994                 this.onMoved(mover, leftTop);
7995         },
7996         onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
7997                 // summary:
7998                 //              called before every incremental move; can be overwritten.
7999                 
8000                 // default implementation does nothing
8001         },
8002         onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8003                 // summary:
8004                 //              called after every incremental move; can be overwritten.
8005                 
8006                 // default implementation does nothing
8007         }
8008 });
8009
8010 }
8011
8012 if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8013 dojo._hasResource["dojo.dnd.move"] = true;
8014 dojo.provide("dojo.dnd.move");
8015
8016
8017
8018
8019 /*=====
8020 dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
8021         // constraints: Function
8022         //              Calculates a constraint box.
8023         //              It is called in a context of the moveable object.
8024         constraints: function(){},
8025
8026         // within: Boolean
8027         //              restrict move within boundaries.
8028         within: false
8029 });
8030 =====*/
8031
8032 dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
8033         // object attributes (for markup)
8034         constraints: function(){},
8035         within: false,
8036         
8037         // markup methods
8038         markupFactory: function(params, node){
8039                 return new dojo.dnd.move.constrainedMoveable(node, params);
8040         },
8041
8042         constructor: function(node, params){
8043                 // summary:
8044                 //              an object that makes a node moveable
8045                 // node: Node
8046                 //              a node (or node's id) to be moved
8047                 // params: dojo.dnd.move.__constrainedMoveableArgs?
8048                 //              an optional object with additional parameters;
8049                 //              the rest is passed to the base class
8050                 if(!params){ params = {}; }
8051                 this.constraints = params.constraints;
8052                 this.within = params.within;
8053         },
8054         onFirstMove: function(/* dojo.dnd.Mover */ mover){
8055                 // summary:
8056                 //              called during the very first move notification;
8057                 //              can be used to initialize coordinates, can be overwritten.
8058                 var c = this.constraintBox = this.constraints.call(this, mover);
8059                 c.r = c.l + c.w;
8060                 c.b = c.t + c.h;
8061                 if(this.within){
8062                         var mb = dojo.marginBox(mover.node);
8063                         c.r -= mb.w;
8064                         c.b -= mb.h;
8065                 }
8066         },
8067         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8068                 // summary:
8069                 //              called during every move notification;
8070                 //              should actually move the node; can be overwritten.
8071                 var c = this.constraintBox, s = mover.node.style;
8072                 s.left = (leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l) + "px";
8073                 s.top  = (leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t) + "px";
8074         }
8075 });
8076
8077 /*=====
8078 dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
8079         // box: Object
8080         //              a constraint box
8081         box: {}
8082 });
8083 =====*/
8084
8085 dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
8086         // box:
8087         //              object attributes (for markup)
8088         box: {},
8089         
8090         // markup methods
8091         markupFactory: function(params, node){
8092                 return new dojo.dnd.move.boxConstrainedMoveable(node, params);
8093         },
8094
8095         constructor: function(node, params){
8096                 // summary:
8097                 //              an object, which makes a node moveable
8098                 // node: Node
8099                 //              a node (or node's id) to be moved
8100                 // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
8101                 //              an optional object with parameters
8102                 var box = params && params.box;
8103                 this.constraints = function(){ return box; };
8104         }
8105 });
8106
8107 /*=====
8108 dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
8109         // area: String
8110         //              A parent's area to restrict the move.
8111         //              Can be "margin", "border", "padding", or "content".
8112         area: ""
8113 });
8114 =====*/
8115
8116 dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
8117         // area:
8118         //              object attributes (for markup)
8119         area: "content",
8120
8121         // markup methods
8122         markupFactory: function(params, node){
8123                 return new dojo.dnd.move.parentConstrainedMoveable(node, params);
8124         },
8125
8126         constructor: function(node, params){
8127                 // summary:
8128                 //              an object, which makes a node moveable
8129                 // node: Node
8130                 //              a node (or node's id) to be moved
8131                 // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
8132                 //              an optional object with parameters
8133                 var area = params && params.area;
8134                 this.constraints = function(){
8135                         var n = this.node.parentNode, 
8136                                 s = dojo.getComputedStyle(n), 
8137                                 mb = dojo._getMarginBox(n, s);
8138                         if(area == "margin"){
8139                                 return mb;      // Object
8140                         }
8141                         var t = dojo._getMarginExtents(n, s);
8142                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8143                         if(area == "border"){
8144                                 return mb;      // Object
8145                         }
8146                         t = dojo._getBorderExtents(n, s);
8147                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8148                         if(area == "padding"){
8149                                 return mb;      // Object
8150                         }
8151                         t = dojo._getPadExtents(n, s);
8152                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8153                         return mb;      // Object
8154                 };
8155         }
8156 });
8157
8158 // WARNING: below are obsolete objects, instead of custom movers use custom moveables (above)
8159
8160 dojo.dnd.move.constrainedMover = function(fun, within){
8161         // summary:
8162         //              returns a constrained version of dojo.dnd.Mover
8163         // description:
8164         //              this function produces n object, which will put a constraint on 
8165         //              the margin box of dragged object in absolute coordinates
8166         // fun: Function
8167         //              called on drag, and returns a constraint box
8168         // within: Boolean
8169         //              if true, constraints the whole dragged object withtin the rectangle, 
8170         //              otherwise the constraint is applied to the left-top corner
8171
8172         dojo.deprecated("dojo.dnd.move.constrainedMover, use dojo.dnd.move.constrainedMoveable instead");
8173         var mover = function(node, e, notifier){
8174                 dojo.dnd.Mover.call(this, node, e, notifier);
8175         };
8176         dojo.extend(mover, dojo.dnd.Mover.prototype);
8177         dojo.extend(mover, {
8178                 onMouseMove: function(e){
8179                         // summary: event processor for onmousemove
8180                         // e: Event: mouse event
8181                         dojo.dnd.autoScroll(e);
8182                         var m = this.marginBox, c = this.constraintBox,
8183                                 l = m.l + e.pageX, t = m.t + e.pageY;
8184                         l = l < c.l ? c.l : c.r < l ? c.r : l;
8185                         t = t < c.t ? c.t : c.b < t ? c.b : t;
8186                         this.host.onMove(this, {l: l, t: t});
8187                 },
8188                 onFirstMove: function(){
8189                         // summary: called once to initialize things; it is meant to be called only once
8190                         dojo.dnd.Mover.prototype.onFirstMove.call(this);
8191                         var c = this.constraintBox = fun.call(this);
8192                         c.r = c.l + c.w;
8193                         c.b = c.t + c.h;
8194                         if(within){
8195                                 var mb = dojo.marginBox(this.node);
8196                                 c.r -= mb.w;
8197                                 c.b -= mb.h;
8198                         }
8199                 }
8200         });
8201         return mover;   // Object
8202 };
8203
8204 dojo.dnd.move.boxConstrainedMover = function(box, within){
8205         // summary:
8206         //              a specialization of dojo.dnd.constrainedMover, which constrains to the specified box
8207         // box: Object
8208         //              a constraint box (l, t, w, h)
8209         // within: Boolean
8210         //              if true, constraints the whole dragged object withtin the rectangle, 
8211         //              otherwise the constraint is applied to the left-top corner
8212
8213         dojo.deprecated("dojo.dnd.move.boxConstrainedMover, use dojo.dnd.move.boxConstrainedMoveable instead");
8214         return dojo.dnd.move.constrainedMover(function(){ return box; }, within);       // Object
8215 };
8216
8217 dojo.dnd.move.parentConstrainedMover = function(area, within){
8218         // summary:
8219         //              a specialization of dojo.dnd.constrainedMover, which constrains to the parent node
8220         // area: String
8221         //              "margin" to constrain within the parent's margin box, "border" for the border box,
8222         //              "padding" for the padding box, and "content" for the content box; "content" is the default value.
8223         // within: Boolean
8224         //              if true, constraints the whole dragged object within the rectangle, 
8225         //              otherwise the constraint is applied to the left-top corner
8226
8227         dojo.deprecated("dojo.dnd.move.parentConstrainedMover, use dojo.dnd.move.parentConstrainedMoveable instead");
8228         var fun = function(){
8229                 var n = this.node.parentNode, 
8230                         s = dojo.getComputedStyle(n), 
8231                         mb = dojo._getMarginBox(n, s);
8232                 if(area == "margin"){
8233                         return mb;      // Object
8234                 }
8235                 var t = dojo._getMarginExtents(n, s);
8236                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8237                 if(area == "border"){
8238                         return mb;      // Object
8239                 }
8240                 t = dojo._getBorderExtents(n, s);
8241                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8242                 if(area == "padding"){
8243                         return mb;      // Object
8244                 }
8245                 t = dojo._getPadExtents(n, s);
8246                 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8247                 return mb;      // Object
8248         };
8249         return dojo.dnd.move.constrainedMover(fun, within);     // Object
8250 };
8251
8252 // patching functions one level up for compatibility
8253
8254 dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
8255 dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
8256 dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
8257
8258 }
8259
8260 if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8261 dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
8262 dojo.provide("dojo.dnd.TimedMoveable");
8263
8264
8265
8266 /*=====
8267 dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
8268         // timeout: Number
8269         //              delay move by this number of ms,
8270         //              accumulating position changes during the timeout
8271         timeout: 0
8272 });
8273 =====*/
8274
8275 (function(){
8276         // precalculate long expressions
8277         var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
8278                 
8279         dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
8280                 // summary:
8281                 //              A specialized version of Moveable to support an FPS throttling.
8282                 //              This class puts an upper restriction on FPS, which may reduce 
8283                 //              the CPU load. The additional parameter "timeout" regulates
8284                 //              the delay before actually moving the moveable object.
8285                 
8286                 // object attributes (for markup)
8287                 timeout: 40,    // in ms, 40ms corresponds to 25 fps
8288         
8289                 constructor: function(node, params){
8290                         // summary:
8291                         //              an object that makes a node moveable with a timer
8292                         // node: Node||String
8293                         //              a node (or node's id) to be moved
8294                         // params: dojo.dnd.__TimedMoveableArgs
8295                         //              object with additional parameters.
8296                         
8297                         // sanitize parameters
8298                         if(!params){ params = {}; }
8299                         if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
8300                                 this.timeout = params.timeout;
8301                         }
8302                 },
8303         
8304                 // markup methods
8305                 markupFactory: function(params, node){
8306                         return new dojo.dnd.TimedMoveable(node, params);
8307                 },
8308         
8309                 onMoveStop: function(/* dojo.dnd.Mover */ mover){
8310                         if(mover._timer){
8311                                 // stop timer
8312                                 clearTimeout(mover._timer)
8313                                 // reflect the last received position
8314                                 oldOnMove.call(this, mover, mover._leftTop)
8315                         }
8316                         dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
8317                 },
8318                 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8319                         mover._leftTop = leftTop;
8320                         if(!mover._timer){
8321                                 var _t = this;  // to avoid using dojo.hitch()
8322                                 mover._timer = setTimeout(function(){
8323                                         // we don't have any pending requests
8324                                         mover._timer = null;
8325                                         // reflect the last received position
8326                                         oldOnMove.call(_t, mover, mover._leftTop);
8327                                 }, this.timeout);
8328                         }
8329                 }
8330         });
8331 })();
8332
8333 }
8334
8335 if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8336 dojo._hasResource["dijit.form._FormMixin"] = true;
8337 dojo.provide("dijit.form._FormMixin");
8338
8339
8340
8341 dojo.declare("dijit.form._FormMixin", null,
8342         {
8343         // summary:
8344         //              Mixin for containers of form widgets (i.e. widgets that represent a single value
8345         //              and can be children of a <form> node or dijit.form.Form widget)
8346         // description:
8347         //              Can extract all the form widgets
8348         //              values and combine them into a single javascript object, or alternately
8349         //              take such an object and set the values for all the contained
8350         //              form widgets
8351
8352 /*=====
8353     // value: Object
8354         //              Name/value hash for each child widget with a name and value.
8355         //              Child widgets without names are not part of the hash.
8356         // 
8357         //              If there are multiple child widgets w/the same name, value is an array,
8358         //              unless they are radio buttons in which case value is a scalar (since only
8359         //              one radio button can be checked at a time).
8360         //
8361         //              If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
8362         //
8363         //              Example:
8364         //      |       { name: "John Smith", interests: ["sports", "movies"] }
8365 =====*/
8366
8367         //      TODO:
8368         //      * Repeater
8369         //      * better handling for arrays.  Often form elements have names with [] like
8370         //      * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
8371         //
8372         //
8373
8374                 reset: function(){
8375                         dojo.forEach(this.getDescendants(), function(widget){
8376                                 if(widget.reset){
8377                                         widget.reset();
8378                                 }
8379                         });
8380                 },
8381
8382                 validate: function(){
8383                         // summary:
8384                         //              returns if the form is valid - same as isValid - but
8385                         //                      provides a few additional (ui-specific) features.
8386                         //                      1 - it will highlight any sub-widgets that are not
8387                         //                              valid
8388                         //                      2 - it will call focus() on the first invalid
8389                         //                              sub-widget
8390                         var didFocus = false;
8391                         return dojo.every(dojo.map(this.getDescendants(), function(widget){
8392                                 // Need to set this so that "required" widgets get their
8393                                 // state set.
8394                                 widget._hasBeenBlurred = true;
8395                                 var valid = widget.disabled || !widget.validate || widget.validate();
8396                                 if(!valid && !didFocus){
8397                                         // Set focus of the first non-valid widget
8398                                         dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
8399                                         widget.focus();
8400                                         didFocus = true;
8401                                 }
8402                                 return valid;
8403                         }), function(item){ return item; });
8404                 },
8405
8406                 setValues: function(val){
8407                         dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
8408                         return this.set('value', val);
8409                 },
8410                 _setValueAttr: function(/*object*/obj){
8411                         // summary:
8412                         //              Fill in form values from according to an Object (in the format returned by attr('value'))
8413
8414                         // generate map from name --> [list of widgets with that name]
8415                         var map = { };
8416                         dojo.forEach(this.getDescendants(), function(widget){
8417                                 if(!widget.name){ return; }
8418                                 var entry = map[widget.name] || (map[widget.name] = [] );
8419                                 entry.push(widget);
8420                         });
8421
8422                         for(var name in map){
8423                                 if(!map.hasOwnProperty(name)){
8424                                         continue;
8425                                 }
8426                                 var widgets = map[name],                                                // array of widgets w/this name
8427                                         values = dojo.getObject(name, false, obj);      // list of values for those widgets
8428
8429                                 if(values === undefined){
8430                                         continue;
8431                                 }
8432                                 if(!dojo.isArray(values)){
8433                                         values = [ values ];
8434                                 }
8435                                 if(typeof widgets[0].checked == 'boolean'){
8436                                         // for checkbox/radio, values is a list of which widgets should be checked
8437                                         dojo.forEach(widgets, function(w, i){
8438                                                 w.set('value', dojo.indexOf(values, w.value) != -1);
8439                                         });
8440                                 }else if(widgets[0].multiple){
8441                                         // it takes an array (e.g. multi-select)
8442                                         widgets[0].set('value', values);
8443                                 }else{
8444                                         // otherwise, values is a list of values to be assigned sequentially to each widget
8445                                         dojo.forEach(widgets, function(w, i){
8446                                                 w.set('value', values[i]);
8447                                         });
8448                                 }
8449                         }
8450
8451                         /***
8452                          *      TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
8453
8454                         dojo.forEach(this.containerNode.elements, function(element){
8455                                 if(element.name == ''){return}; // like "continue"
8456                                 var namePath = element.name.split(".");
8457                                 var myObj=obj;
8458                                 var name=namePath[namePath.length-1];
8459                                 for(var j=1,len2=namePath.length;j<len2;++j){
8460                                         var p=namePath[j - 1];
8461                                         // repeater support block
8462                                         var nameA=p.split("[");
8463                                         if(nameA.length > 1){
8464                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
8465                                                         myObj[nameA[0]]=[ ];
8466                                                 } // if
8467
8468                                                 nameIndex=parseInt(nameA[1]);
8469                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
8470                                                         myObj[nameA[0]][nameIndex] = { };
8471                                                 }
8472                                                 myObj=myObj[nameA[0]][nameIndex];
8473                                                 continue;
8474                                         } // repeater support ends
8475
8476                                         if(typeof(myObj[p]) == "undefined"){
8477                                                 myObj=undefined;
8478                                                 break;
8479                                         };
8480                                         myObj=myObj[p];
8481                                 }
8482
8483                                 if(typeof(myObj) == "undefined"){
8484                                         return;         // like "continue"
8485                                 }
8486                                 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
8487                                         return;         // like "continue"
8488                                 }
8489
8490                                 // TODO: widget values (just call attr('value', ...) on the widget)
8491
8492                                 // TODO: maybe should call dojo.getNodeProp() instead
8493                                 switch(element.type){
8494                                         case "checkbox":
8495                                                 element.checked = (name in myObj) &&
8496                                                         dojo.some(myObj[name], function(val){ return val == element.value; });
8497                                                 break;
8498                                         case "radio":
8499                                                 element.checked = (name in myObj) && myObj[name] == element.value;
8500                                                 break;
8501                                         case "select-multiple":
8502                                                 element.selectedIndex=-1;
8503                                                 dojo.forEach(element.options, function(option){
8504                                                         option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
8505                                                 });
8506                                                 break;
8507                                         case "select-one":
8508                                                 element.selectedIndex="0";
8509                                                 dojo.forEach(element.options, function(option){
8510                                                         option.selected = option.value == myObj[name];
8511                                                 });
8512                                                 break;
8513                                         case "hidden":
8514                                         case "text":
8515                                         case "textarea":
8516                                         case "password":
8517                                                 element.value = myObj[name] || "";
8518                                                 break;
8519                                 }
8520                         });
8521                         */
8522                 },
8523
8524                 getValues: function(){
8525                         dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
8526                         return this.get('value');
8527                 },
8528                 _getValueAttr: function(){
8529                         // summary:
8530                         //              Returns Object representing form values.
8531                         // description:
8532                         //              Returns name/value hash for each form element.
8533                         //              If there are multiple elements w/the same name, value is an array,
8534                         //              unless they are radio buttons in which case value is a scalar since only
8535                         //              one can be checked at a time.
8536                         //
8537                         //              If the name is a dot separated list (like a.b.c.d), creates a nested structure.
8538                         //              Only works on widget form elements.
8539                         // example:
8540                         //              | { name: "John Smith", interests: ["sports", "movies"] }
8541
8542                         // get widget values
8543                         var obj = { };
8544                         dojo.forEach(this.getDescendants(), function(widget){
8545                                 var name = widget.name;
8546                                 if(!name || widget.disabled){ return; }
8547
8548                                 // Single value widget (checkbox, radio, or plain <input> type widget
8549                                 var value = widget.get('value');
8550
8551                                 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
8552                                 if(typeof widget.checked == 'boolean'){
8553                                         if(/Radio/.test(widget.declaredClass)){
8554                                                 // radio button
8555                                                 if(value !== false){
8556                                                         dojo.setObject(name, value, obj);
8557                                                 }else{
8558                                                         // give radio widgets a default of null
8559                                                         value = dojo.getObject(name, false, obj);
8560                                                         if(value === undefined){
8561                                                                 dojo.setObject(name, null, obj);
8562                                                         }
8563                                                 }
8564                                         }else{
8565                                                 // checkbox/toggle button
8566                                                 var ary=dojo.getObject(name, false, obj);
8567                                                 if(!ary){
8568                                                         ary=[];
8569                                                         dojo.setObject(name, ary, obj);
8570                                                 }
8571                                                 if(value !== false){
8572                                                         ary.push(value);
8573                                                 }
8574                                         }
8575                                 }else{
8576                                         var prev=dojo.getObject(name, false, obj);
8577                                         if(typeof prev != "undefined"){
8578                                                 if(dojo.isArray(prev)){
8579                                                         prev.push(value);
8580                                                 }else{
8581                                                         dojo.setObject(name, [prev, value], obj);
8582                                                 }
8583                                         }else{
8584                                                 // unique name
8585                                                 dojo.setObject(name, value, obj);
8586                                         }
8587                                 }
8588                         });
8589
8590                         /***
8591                          * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
8592                          * but it doesn't understand [] notation, presumably)
8593                         var obj = { };
8594                         dojo.forEach(this.containerNode.elements, function(elm){
8595                                 if(!elm.name)   {
8596                                         return;         // like "continue"
8597                                 }
8598                                 var namePath = elm.name.split(".");
8599                                 var myObj=obj;
8600                                 var name=namePath[namePath.length-1];
8601                                 for(var j=1,len2=namePath.length;j<len2;++j){
8602                                         var nameIndex = null;
8603                                         var p=namePath[j - 1];
8604                                         var nameA=p.split("[");
8605                                         if(nameA.length > 1){
8606                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
8607                                                         myObj[nameA[0]]=[ ];
8608                                                 } // if
8609                                                 nameIndex=parseInt(nameA[1]);
8610                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
8611                                                         myObj[nameA[0]][nameIndex] = { };
8612                                                 }
8613                                         } else if(typeof(myObj[nameA[0]]) == "undefined"){
8614                                                 myObj[nameA[0]] = { }
8615                                         } // if
8616
8617                                         if(nameA.length == 1){
8618                                                 myObj=myObj[nameA[0]];
8619                                         } else{
8620                                                 myObj=myObj[nameA[0]][nameIndex];
8621                                         } // if
8622                                 } // for
8623
8624                                 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
8625                                         if(name == name.split("[")[0]){
8626                                                 myObj[name]=elm.value;
8627                                         } else{
8628                                                 // can not set value when there is no name
8629                                         }
8630                                 } else if(elm.type == "checkbox" && elm.checked){
8631                                         if(typeof(myObj[name]) == 'undefined'){
8632                                                 myObj[name]=[ ];
8633                                         }
8634                                         myObj[name].push(elm.value);
8635                                 } else if(elm.type == "select-multiple"){
8636                                         if(typeof(myObj[name]) == 'undefined'){
8637                                                 myObj[name]=[ ];
8638                                         }
8639                                         for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
8640                                                 if(elm.options[jdx].selected){
8641                                                         myObj[name].push(elm.options[jdx].value);
8642                                                 }
8643                                         }
8644                                 } // if
8645                                 name=undefined;
8646                         }); // forEach
8647                         ***/
8648                         return obj;
8649                 },
8650
8651                 // TODO: ComboBox might need time to process a recently input value.  This should be async?
8652                 isValid: function(){
8653                         // summary:
8654                         //              Returns true if all of the widgets are valid
8655
8656                         // This also populate this._invalidWidgets[] array with list of invalid widgets...
8657                         // TODO: put that into separate function?   It's confusing to have that as a side effect
8658                         // of a method named isValid().
8659
8660                         this._invalidWidgets = dojo.filter(this.getDescendants(), function(widget){
8661                                 return !widget.disabled && widget.isValid && !widget.isValid();
8662                         });
8663                         return !this._invalidWidgets.length;
8664                 },
8665
8666
8667                 onValidStateChange: function(isValid){
8668                         // summary:
8669                         //              Stub function to connect to if you want to do something
8670                         //              (like disable/enable a submit button) when the valid
8671                         //              state changes on the form as a whole.
8672                 },
8673
8674                 _widgetChange: function(widget){
8675                         // summary:
8676                         //              Connected to a widget's onChange function - update our
8677                         //              valid state, if needed.
8678                         var isValid = this._lastValidState;
8679                         if(!widget || this._lastValidState === undefined){
8680                                 // We have passed a null widget, or we haven't been validated
8681                                 // yet - let's re-check all our children
8682                                 // This happens when we connect (or reconnect) our children
8683                                 isValid = this.isValid();
8684                                 if(this._lastValidState === undefined){
8685                                         // Set this so that we don't fire an onValidStateChange
8686                                         // the first time
8687                                         this._lastValidState = isValid;
8688                                 }
8689                         }else if(widget.isValid){
8690                                 this._invalidWidgets = dojo.filter(this._invalidWidgets || [], function(w){
8691                                         return (w != widget);
8692                                 }, this);
8693                                 if(!widget.isValid() && !widget.get("disabled")){
8694                                         this._invalidWidgets.push(widget);
8695                                 }
8696                                 isValid = (this._invalidWidgets.length === 0);
8697                         }
8698                         if(isValid !== this._lastValidState){
8699                                 this._lastValidState = isValid;
8700                                 this.onValidStateChange(isValid);
8701                         }
8702                 },
8703
8704                 connectChildren: function(){
8705                         // summary:
8706                         //              Connects to the onChange function of all children to
8707                         //              track valid state changes.  You can call this function
8708                         //              directly, ex. in the event that you programmatically
8709                         //              add a widget to the form *after* the form has been
8710                         //              initialized.
8711                         dojo.forEach(this._changeConnections, dojo.hitch(this, "disconnect"));
8712                         var _this = this;
8713
8714                         // we connect to validate - so that it better reflects the states
8715                         // of the widgets - also, we only connect if it has a validate
8716                         // function (to avoid too many unneeded connections)
8717                         var conns = (this._changeConnections = []);
8718                         dojo.forEach(dojo.filter(this.getDescendants(),
8719                                 function(item){ return item.validate; }
8720                         ),
8721                         function(widget){
8722                                 // We are interested in whenever the widget is validated - or
8723                                 // whenever the disabled attribute on that widget is changed
8724                                 conns.push(_this.connect(widget, "validate",
8725                                                                         dojo.hitch(_this, "_widgetChange", widget)));
8726                                 conns.push(_this.connect(widget, "_setDisabledAttr",
8727                                                                         dojo.hitch(_this, "_widgetChange", widget)));
8728                         });
8729
8730                         // Call the widget change function to update the valid state, in
8731                         // case something is different now.
8732                         this._widgetChange(null);
8733                 },
8734
8735                 startup: function(){
8736                         this.inherited(arguments);
8737                         // Initialize our valid state tracking.  Needs to be done in startup
8738                         // because it's not guaranteed that our children are initialized
8739                         // yet.
8740                         this._changeConnections = [];
8741                         this.connectChildren();
8742                 }
8743         });
8744
8745 }
8746
8747 if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8748 dojo._hasResource["dijit._DialogMixin"] = true;
8749 dojo.provide("dijit._DialogMixin");
8750
8751
8752
8753 dojo.declare("dijit._DialogMixin", null,
8754         {
8755                 // summary:
8756                 //              This provides functions useful to Dialog and TooltipDialog
8757
8758                 attributeMap: dijit._Widget.prototype.attributeMap,
8759
8760                 execute: function(/*Object*/ formContents){
8761                         // summary:
8762                         //              Callback when the user hits the submit button.
8763                         //              Override this method to handle Dialog execution.
8764                         // description:
8765                         //              After the user has pressed the submit button, the Dialog
8766                         //              first calls onExecute() to notify the container to hide the
8767                         //              dialog and restore focus to wherever it used to be.
8768                         //
8769                         //              *Then* this method is called.
8770                         // type:
8771                         //              callback
8772                 },
8773
8774                 onCancel: function(){
8775                         // summary:
8776                         //          Called when user has pressed the Dialog's cancel button, to notify container.
8777                         // description:
8778                         //          Developer shouldn't override or connect to this method;
8779                         //              it's a private communication device between the TooltipDialog
8780                         //              and the thing that opened it (ex: `dijit.form.DropDownButton`)
8781                         // type:
8782                         //              protected
8783                 },
8784
8785                 onExecute: function(){
8786                         // summary:
8787                         //          Called when user has pressed the dialog's OK button, to notify container.
8788                         // description:
8789                         //          Developer shouldn't override or connect to this method;
8790                         //              it's a private communication device between the TooltipDialog
8791                         //              and the thing that opened it (ex: `dijit.form.DropDownButton`)
8792                         // type:
8793                         //              protected
8794                 },
8795
8796                 _onSubmit: function(){
8797                         // summary:
8798                         //              Callback when user hits submit button
8799                         // type:
8800                         //              protected
8801                         this.onExecute();       // notify container that we are about to execute
8802                         this.execute(this.get('value'));
8803                 },
8804
8805                 _getFocusItems: function(/*Node*/ dialogNode){
8806                         // summary:
8807                         //              Find focusable Items each time a dialog is opened,
8808                         //              setting _firstFocusItem and _lastFocusItem
8809                         // tags:
8810                         //              protected
8811
8812                         var elems = dijit._getTabNavigable(dojo.byId(dialogNode));
8813                         this._firstFocusItem = elems.lowest || elems.first || dialogNode;
8814                         this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
8815                         if(dojo.isMoz && this._firstFocusItem.tagName.toLowerCase() == "input" &&
8816                                         dojo.getNodeProp(this._firstFocusItem, "type").toLowerCase() == "file"){
8817                                 // FF doesn't behave well when first element is input type=file, set first focusable to dialog container
8818                                 dojo.attr(dialogNode, "tabIndex", "0");
8819                                 this._firstFocusItem = dialogNode;
8820                         }
8821                 }
8822         }
8823 );
8824
8825 }
8826
8827 if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8828 dojo._hasResource["dijit.DialogUnderlay"] = true;
8829 dojo.provide("dijit.DialogUnderlay");
8830
8831
8832
8833
8834
8835
8836 dojo.declare(
8837         "dijit.DialogUnderlay",
8838         [dijit._Widget, dijit._Templated],
8839         {
8840                 // summary:
8841                 //              The component that blocks the screen behind a `dijit.Dialog`
8842                 //
8843                 // description:
8844                 //              A component used to block input behind a `dijit.Dialog`. Only a single
8845                 //              instance of this widget is created by `dijit.Dialog`, and saved as
8846                 //              a reference to be shared between all Dialogs as `dijit._underlay`
8847                 //
8848                 //              The underlay itself can be styled based on and id:
8849                 //      |       #myDialog_underlay { background-color:red; }
8850                 //
8851                 //              In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
8852                 //              suffixed with _underlay.
8853
8854                 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
8855                 // Inner div has opacity specified in CSS file.
8856                 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>",
8857
8858                 // Parameters on creation or updatable later
8859
8860                 // dialogId: String
8861                 //              Id of the dialog.... DialogUnderlay's id is based on this id
8862                 dialogId: "",
8863
8864                 // class: String
8865                 //              This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
8866                 "class": "",
8867
8868                 attributeMap: { id: "domNode" },
8869
8870                 _setDialogIdAttr: function(id){
8871                         dojo.attr(this.node, "id", id + "_underlay");
8872                 },
8873
8874                 _setClassAttr: function(clazz){
8875                         this.node.className = "dijitDialogUnderlay " + clazz;
8876                 },
8877
8878                 postCreate: function(){
8879                         // summary:
8880                         //              Append the underlay to the body
8881                         dojo.body().appendChild(this.domNode);
8882                 },
8883
8884                 layout: function(){
8885                         // summary:
8886                         //              Sets the background to the size of the viewport
8887                         //
8888                         // description:
8889                         //              Sets the background to the size of the viewport (rather than the size
8890                         //              of the document) since we need to cover the whole browser window, even
8891                         //              if the document is only a few lines long.
8892                         // tags:
8893                         //              private
8894
8895                         var is = this.node.style,
8896                                 os = this.domNode.style;
8897
8898                         // hide the background temporarily, so that the background itself isn't
8899                         // causing scrollbars to appear (might happen when user shrinks browser
8900                         // window and then we are called to resize)
8901                         os.display = "none";
8902
8903                         // then resize and show
8904                         var viewport = dojo.window.getBox();
8905                         os.top = viewport.t + "px";
8906                         os.left = viewport.l + "px";
8907                         is.width = viewport.w + "px";
8908                         is.height = viewport.h + "px";
8909                         os.display = "block";
8910                 },
8911
8912                 show: function(){
8913                         // summary:
8914                         //              Show the dialog underlay
8915                         this.domNode.style.display = "block";
8916                         this.layout();
8917                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
8918                 },
8919
8920                 hide: function(){
8921                         // summary:
8922                         //              Hides the dialog underlay
8923                         this.bgIframe.destroy();
8924                         this.domNode.style.display = "none";
8925                 },
8926
8927                 uninitialize: function(){
8928                         if(this.bgIframe){
8929                                 this.bgIframe.destroy();
8930                         }
8931                         this.inherited(arguments);
8932                 }
8933         }
8934 );
8935
8936 }
8937
8938 if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8939 dojo._hasResource["dojo.html"] = true;
8940 dojo.provide("dojo.html");
8941
8942 // the parser might be needed..
8943  
8944
8945 (function(){ // private scope, sort of a namespace
8946
8947         // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
8948         var idCounter = 0, 
8949                 d = dojo;
8950         
8951         dojo.html._secureForInnerHtml = function(/*String*/ cont){
8952                 // summary:
8953                 //              removes !DOCTYPE and title elements from the html string.
8954                 // 
8955                 //              khtml is picky about dom faults, you can't attach a style or <title> node as child of body
8956                 //              must go into head, so we need to cut out those tags
8957                 //      cont:
8958                 //              An html string for insertion into the dom
8959                 //      
8960                 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
8961         };
8962
8963 /*====
8964         dojo.html._emptyNode = function(node){
8965                 // summary:
8966                 //              removes all child nodes from the given node
8967                 //      node: DOMNode
8968                 //              the parent element
8969         };
8970 =====*/
8971         dojo.html._emptyNode = dojo.empty;
8972
8973         dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
8974                 // summary:
8975                 //              inserts the given content into the given node
8976                 //      node:
8977                 //              the parent element
8978                 //      content:
8979                 //              the content to be set on the parent element. 
8980                 //              This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
8981                 
8982                 // always empty
8983                 d.empty(node);
8984
8985                 if(cont) {
8986                         if(typeof cont == "string") {
8987                                 cont = d._toDom(cont, node.ownerDocument);
8988                         }
8989                         if(!cont.nodeType && d.isArrayLike(cont)) {
8990                                 // handle as enumerable, but it may shrink as we enumerate it
8991                                 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
8992                                         d.place( cont[i], node, "last");
8993                                 }
8994                         } else {
8995                                 // pass nodes, documentFragments and unknowns through to dojo.place
8996                                 d.place(cont, node, "last");
8997                         }
8998                 }
8999
9000                 // return DomNode
9001                 return node;
9002         };
9003
9004         // we wrap up the content-setting operation in a object
9005         dojo.declare("dojo.html._ContentSetter", null, 
9006                 {
9007                         // node: DomNode|String
9008                         //              An node which will be the parent element that we set content into
9009                         node: "",
9010
9011                         // content: String|DomNode|DomNode[]
9012                         //              The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
9013                         content: "",
9014                         
9015                         // id: String?
9016                         //              Usually only used internally, and auto-generated with each instance 
9017                         id: "",
9018
9019                         // cleanContent: Boolean
9020                         //              Should the content be treated as a full html document, 
9021                         //              and the real content stripped of <html>, <body> wrapper before injection
9022                         cleanContent: false,
9023                         
9024                         // extractContent: Boolean
9025                         //              Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
9026                         extractContent: false,
9027
9028                         // parseContent: Boolean
9029                         //              Should the node by passed to the parser after the new content is set
9030                         parseContent: false,
9031                         
9032                         // lifecyle methods
9033                         constructor: function(/* Object */params, /* String|DomNode */node){
9034                                 //      summary:
9035                                 //              Provides a configurable, extensible object to wrap the setting on content on a node
9036                                 //              call the set() method to actually set the content..
9037  
9038                                 // the original params are mixed directly into the instance "this"
9039                                 dojo.mixin(this, params || {});
9040
9041                                 // give precedence to params.node vs. the node argument
9042                                 // and ensure its a node, not an id string
9043                                 node = this.node = dojo.byId( this.node || node );
9044         
9045                                 if(!this.id){
9046                                         this.id = [
9047                                                 "Setter",
9048                                                 (node) ? node.id || node.tagName : "", 
9049                                                 idCounter++
9050                                         ].join("_");
9051                                 }
9052                         },
9053                         set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
9054                                 // summary:
9055                                 //              front-end to the set-content sequence 
9056                                 //      cont:
9057                                 //              An html string, node or enumerable list of nodes for insertion into the dom
9058                                 //              If not provided, the object's content property will be used
9059                                 if(undefined !== cont){
9060                                         this.content = cont;
9061                                 }
9062                                 // in the re-use scenario, set needs to be able to mixin new configuration
9063                                 if(params){
9064                                         this._mixin(params);
9065                                 }
9066
9067                                 this.onBegin();
9068                                 this.setContent();
9069                                 this.onEnd();
9070
9071                                 return this.node;
9072                         },
9073                         setContent: function(){
9074                                 // summary:
9075                                 //              sets the content on the node 
9076
9077                                 var node = this.node; 
9078                                 if(!node) {
9079                                     // can't proceed
9080                                         throw new Error(this.declaredClass + ": setContent given no node");
9081                                 }
9082                                 try{
9083                                         node = dojo.html._setNodeContent(node, this.content);
9084                                 }catch(e){
9085                                         // check if a domfault occurs when we are appending this.errorMessage
9086                                         // like for instance if domNode is a UL and we try append a DIV
9087         
9088                                         // FIXME: need to allow the user to provide a content error message string
9089                                         var errMess = this.onContentError(e); 
9090                                         try{
9091                                                 node.innerHTML = errMess;
9092                                         }catch(e){
9093                                                 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
9094                                         }
9095                                 }
9096                                 // always put back the node for the next method
9097                                 this.node = node; // DomNode
9098                         },
9099                         
9100                         empty: function() {
9101                                 // summary
9102                                 //      cleanly empty out existing content
9103
9104                                 // destroy any widgets from a previous run
9105                                 // NOTE: if you dont want this you'll need to empty 
9106                                 // the parseResults array property yourself to avoid bad things happenning
9107                                 if(this.parseResults && this.parseResults.length) {
9108                                         dojo.forEach(this.parseResults, function(w) {
9109                                                 if(w.destroy){
9110                                                         w.destroy();
9111                                                 }
9112                                         });
9113                                         delete this.parseResults;
9114                                 }
9115                                 // this is fast, but if you know its already empty or safe, you could 
9116                                 // override empty to skip this step
9117                                 dojo.html._emptyNode(this.node);
9118                         },
9119         
9120                         onBegin: function(){
9121                                 // summary
9122                                 //              Called after instantiation, but before set(); 
9123                                 //              It allows modification of any of the object properties 
9124                                 //              - including the node and content provided - before the set operation actually takes place
9125                                 //              This default implementation checks for cleanContent and extractContent flags to 
9126                                 //              optionally pre-process html string content
9127                                 var cont = this.content;
9128         
9129                                 if(dojo.isString(cont)){
9130                                         if(this.cleanContent){
9131                                                 cont = dojo.html._secureForInnerHtml(cont);
9132                                         }
9133   
9134                                         if(this.extractContent){
9135                                                 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
9136                                                 if(match){ cont = match[1]; }
9137                                         }
9138                                 }
9139
9140                                 // clean out the node and any cruft associated with it - like widgets
9141                                 this.empty();
9142                                 
9143                                 this.content = cont;
9144                                 return this.node; /* DomNode */
9145                         },
9146         
9147                         onEnd: function(){
9148                                 // summary
9149                                 //              Called after set(), when the new content has been pushed into the node
9150                                 //              It provides an opportunity for post-processing before handing back the node to the caller
9151                                 //              This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
9152                                 if(this.parseContent){
9153                                         // populates this.parseResults if you need those..
9154                                         this._parse();
9155                                 }
9156                                 return this.node; /* DomNode */
9157                         },
9158         
9159                         tearDown: function(){
9160                                 // summary
9161                                 //              manually reset the Setter instance if its being re-used for example for another set()
9162                                 // description
9163                                 //              tearDown() is not called automatically. 
9164                                 //              In normal use, the Setter instance properties are simply allowed to fall out of scope
9165                                 //              but the tearDown method can be called to explicitly reset this instance.
9166                                 delete this.parseResults; 
9167                                 delete this.node; 
9168                                 delete this.content; 
9169                         },
9170   
9171                         onContentError: function(err){
9172                                 return "Error occured setting content: " + err; 
9173                         },
9174                         
9175                         _mixin: function(params){
9176                                 // mix properties/methods into the instance
9177                                 // TODO: the intention with tearDown is to put the Setter's state 
9178                                 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
9179                                 // so we could do something here to move the original properties aside for later restoration
9180                                 var empty = {}, key;
9181                                 for(key in params){
9182                                         if(key in empty){ continue; }
9183                                         // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
9184                                         // .. but history shows we'll almost always guess wrong
9185                                         this[key] = params[key]; 
9186                                 }
9187                         },
9188                         _parse: function(){
9189                                 // summary: 
9190                                 //              runs the dojo parser over the node contents, storing any results in this.parseResults
9191                                 //              Any errors resulting from parsing are passed to _onError for handling
9192
9193                                 var rootNode = this.node;
9194                                 try{
9195                                         // store the results (widgets, whatever) for potential retrieval
9196                                         this.parseResults = dojo.parser.parse({
9197                                                 rootNode: rootNode,
9198                                                 dir: this.dir,
9199                                                 lang: this.lang
9200                                         });
9201                                 }catch(e){
9202                                         this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
9203                                 }
9204                         },
9205   
9206                         _onError: function(type, err, consoleText){
9207                                 // summary:
9208                                 //              shows user the string that is returned by on[type]Error
9209                                 //              overide/implement on[type]Error and return your own string to customize
9210                                 var errText = this['on' + type + 'Error'].call(this, err);
9211                                 if(consoleText){
9212                                         console.error(consoleText, err);
9213                                 }else if(errText){ // a empty string won't change current content
9214                                         dojo.html._setNodeContent(this.node, errText, true);
9215                                 }
9216                         }
9217         }); // end dojo.declare()
9218
9219         dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
9220                         // summary:
9221                         //              inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
9222                         //              may be a better choice for simple HTML insertion.
9223                         // description:
9224                         //              Unless you need to use the params capabilities of this method, you should use
9225                         //              dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
9226                         //              an HTML string into the DOM, but it only handles inserting an HTML string as DOM
9227                         //              elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
9228                         //              or the other capabilities as defined by the params object for this method.
9229                         //      node:
9230                         //              the parent element that will receive the content
9231                         //      cont:
9232                         //              the content to be set on the parent element. 
9233                         //              This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
9234                         //      params: 
9235                         //              Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
9236                         //      example:
9237                         //              A safe string/node/nodelist content replacement/injection with hooks for extension
9238                         //              Example Usage: 
9239                         //              dojo.html.set(node, "some string"); 
9240                         //              dojo.html.set(node, contentNode, {options}); 
9241                         //              dojo.html.set(node, myNode.childNodes, {options}); 
9242                 if(undefined == cont){
9243                         console.warn("dojo.html.set: no cont argument provided, using empty string");
9244                         cont = "";
9245                 }       
9246                 if(!params){
9247                         // simple and fast
9248                         return dojo.html._setNodeContent(node, cont, true);
9249                 }else{ 
9250                         // more options but slower
9251                         // note the arguments are reversed in order, to match the convention for instantiation via the parser
9252                         var op = new dojo.html._ContentSetter(dojo.mixin( 
9253                                         params, 
9254                                         { content: cont, node: node } 
9255                         ));
9256                         return op.set();
9257                 }
9258         };
9259 })();
9260
9261 }
9262
9263 if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9264 dojo._hasResource["dijit.layout.ContentPane"] = true;
9265 dojo.provide("dijit.layout.ContentPane");
9266
9267
9268
9269         // for dijit.layout.marginBox2contentBox()
9270
9271
9272
9273
9274
9275
9276 dojo.declare(
9277         "dijit.layout.ContentPane", dijit._Widget,
9278 {
9279         // summary:
9280         //              A widget that acts as a container for mixed HTML and widgets, and includes an Ajax interface
9281         // description:
9282         //              A widget that can be used as a stand alone widget
9283         //              or as a base class for other widgets.
9284         //
9285         //              Handles replacement of document fragment using either external uri or javascript
9286         //              generated markup or DOM content, instantiating widgets within that content.
9287         //              Don't confuse it with an iframe, it only needs/wants document fragments.
9288         //              It's useful as a child of LayoutContainer, SplitContainer, or TabContainer.
9289         //              But note that those classes can contain any widget as a child.
9290         // example:
9291         //              Some quick samples:
9292         //              To change the innerHTML use .set('content', '<b>new content</b>')
9293         //
9294         //              Or you can send it a NodeList, .set('content', dojo.query('div [class=selected]', userSelection))
9295         //              please note that the nodes in NodeList will copied, not moved
9296         //
9297         //              To do a ajax update use .set('href', url)
9298
9299         // href: String
9300         //              The href of the content that displays now.
9301         //              Set this at construction if you want to load data externally when the
9302         //              pane is shown.  (Set preload=true to load it immediately.)
9303         //              Changing href after creation doesn't have any effect; Use set('href', ...);
9304         href: "",
9305
9306 /*=====
9307         // content: String || DomNode || NodeList || dijit._Widget
9308         //              The innerHTML of the ContentPane.
9309         //              Note that the initialization parameter / argument to attr("content", ...)
9310         //              can be a String, DomNode, Nodelist, or _Widget.
9311         content: "",
9312 =====*/
9313
9314         // extractContent: Boolean
9315         //              Extract visible content from inside of <body> .... </body>.
9316         //              I.e., strip <html> and <head> (and it's contents) from the href
9317         extractContent: false,
9318
9319         // parseOnLoad: Boolean
9320         //              Parse content and create the widgets, if any.
9321         parseOnLoad: true,
9322
9323         // preventCache: Boolean
9324         //              Prevent caching of data from href's by appending a timestamp to the href.
9325         preventCache: false,
9326
9327         // preload: Boolean
9328         //              Force load of data on initialization even if pane is hidden.
9329         preload: false,
9330
9331         // refreshOnShow: Boolean
9332         //              Refresh (re-download) content when pane goes from hidden to shown
9333         refreshOnShow: false,
9334
9335         // loadingMessage: String
9336         //              Message that shows while downloading
9337         loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
9338
9339         // errorMessage: String
9340         //              Message that shows if an error occurs
9341         errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
9342
9343         // isLoaded: [readonly] Boolean
9344         //              True if the ContentPane has data in it, either specified
9345         //              during initialization (via href or inline content), or set
9346         //              via attr('content', ...) / attr('href', ...)
9347         //
9348         //              False if it doesn't have any content, or if ContentPane is
9349         //              still in the process of downloading href.
9350         isLoaded: false,
9351
9352         baseClass: "dijitContentPane",
9353
9354         // doLayout: Boolean
9355         //              - false - don't adjust size of children
9356         //              - true - if there is a single visible child widget, set it's size to
9357         //                              however big the ContentPane is
9358         doLayout: true,
9359
9360         // ioArgs: Object
9361         //              Parameters to pass to xhrGet() request, for example:
9362         // |    <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
9363         ioArgs: {},
9364
9365         // isContainer: [protected] Boolean
9366         //              Indicates that this widget acts as a "parent" to the descendant widgets.
9367         //              When the parent is started it will call startup() on the child widgets.
9368         //              See also `isLayoutContainer`.
9369         isContainer: true,
9370
9371         // isLayoutContainer: [protected] Boolean
9372         //              Indicates that this widget will call resize() on it's child widgets
9373         //              when they become visible.
9374         isLayoutContainer: true,
9375
9376         // onLoadDeferred: [readonly] dojo.Deferred
9377         //              This is the `dojo.Deferred` returned by attr('href', ...) and refresh().
9378         //              Calling onLoadDeferred.addCallback() or addErrback() registers your
9379         //              callback to be called only once, when the prior attr('href', ...) call or
9380         //              the initial href parameter to the constructor finishes loading.
9381         //
9382         //              This is different than an onLoad() handler which gets called any time any href is loaded.
9383         onLoadDeferred: null,
9384
9385         // Override _Widget's attributeMap because we don't want the title attribute (used to specify
9386         // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
9387         // entire pane.
9388         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
9389                 title: []
9390         }),
9391
9392         postMixInProperties: function(){
9393                 this.inherited(arguments);
9394                 var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
9395                 this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
9396                 this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
9397
9398                 // Detect if we were initialized with data
9399                 if(!this.href && this.srcNodeRef && this.srcNodeRef.innerHTML){
9400                         this.isLoaded = true;
9401                 }
9402         },
9403
9404         buildRendering: function(){
9405                 // Overrides Widget.buildRendering().
9406                 // Since we have no template we need to set this.containerNode ourselves.
9407                 // For subclasses of ContentPane do have a template, does nothing.
9408                 this.inherited(arguments);
9409                 if(!this.containerNode){
9410                         // make getDescendants() work
9411                         this.containerNode = this.domNode;
9412                 }
9413         },
9414
9415         postCreate: function(){
9416                 // remove the title attribute so it doesn't show up when hovering
9417                 // over a node
9418                 this.domNode.title = "";
9419
9420                 if(!dojo.attr(this.domNode,"role")){
9421                         dijit.setWaiRole(this.domNode, "group");
9422                 }
9423
9424                 dojo.addClass(this.domNode, this.baseClass);
9425         },
9426
9427         startup: function(){
9428                 // summary:
9429                 //              See `dijit.layout._LayoutWidget.startup` for description.
9430                 //              Although ContentPane doesn't extend _LayoutWidget, it does implement
9431                 //              the same API.
9432                 if(this._started){ return; }
9433
9434                 var parent = dijit._Contained.prototype.getParent.call(this);
9435                 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
9436
9437                 // I need to call resize() on my child/children (when I become visible), unless
9438                 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
9439                 this._needLayout = !this._childOfLayoutWidget;
9440
9441                 if(this.isLoaded){
9442                         dojo.forEach(this.getChildren(), function(child){
9443                                 child.startup();
9444                         });
9445                 }
9446
9447                 if(this._isShown() || this.preload){
9448                         this._onShow();
9449                 }
9450
9451                 this.inherited(arguments);
9452         },
9453
9454         _checkIfSingleChild: function(){
9455                 // summary:
9456                 //              Test if we have exactly one visible widget as a child,
9457                 //              and if so assume that we are a container for that widget,
9458                 //              and should propogate startup() and resize() calls to it.
9459                 //              Skips over things like data stores since they aren't visible.
9460
9461                 var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
9462                                 return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
9463                         }),
9464                         childWidgetNodes = childNodes.filter(function(node){
9465                                 return dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
9466                         }),
9467                         candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
9468                                 return widget && widget.domNode && widget.resize;
9469                         });
9470
9471                 if(
9472                         // all child nodes are widgets
9473                         childNodes.length == childWidgetNodes.length &&
9474
9475                         // all but one are invisible (like dojo.data)
9476                         candidateWidgets.length == 1
9477                 ){
9478                         this._singleChild = candidateWidgets[0];
9479                 }else{
9480                         delete this._singleChild;
9481                 }
9482
9483                 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
9484                 dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
9485         },
9486
9487         setHref: function(/*String|Uri*/ href){
9488                 // summary:
9489                 //              Deprecated.   Use set('href', ...) instead.
9490                 dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
9491                 return this.set("href", href);
9492         },
9493         _setHrefAttr: function(/*String|Uri*/ href){
9494                 // summary:
9495                 //              Hook so attr("href", ...) works.
9496                 // description:
9497                 //              Reset the (external defined) content of this pane and replace with new url
9498                 //              Note: It delays the download until widget is shown if preload is false.
9499                 //      href:
9500                 //              url to the page you want to get, must be within the same domain as your mainpage
9501
9502                 // Cancel any in-flight requests (an attr('href') will cancel any in-flight attr('href', ...))
9503                 this.cancel();
9504
9505                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
9506
9507                 this.href = href;
9508
9509                 // _setHrefAttr() is called during creation and by the user, after creation.
9510                 // only in the second case do we actually load the URL; otherwise it's done in startup()
9511                 if(this._created && (this.preload || this._isShown())){
9512                         this._load();
9513                 }else{
9514                         // Set flag to indicate that href needs to be loaded the next time the
9515                         // ContentPane is made visible
9516                         this._hrefChanged = true;
9517                 }
9518
9519                 return this.onLoadDeferred;             // dojo.Deferred
9520         },
9521
9522         setContent: function(/*String|DomNode|Nodelist*/data){
9523                 // summary:
9524                 //              Deprecated.   Use set('content', ...) instead.
9525                 dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated.  Use set('content', ...) instead.", "", "2.0");
9526                 this.set("content", data);
9527         },
9528         _setContentAttr: function(/*String|DomNode|Nodelist*/data){
9529                 // summary:
9530                 //              Hook to make attr("content", ...) work.
9531                 //              Replaces old content with data content, include style classes from old content
9532                 //      data:
9533                 //              the new Content may be String, DomNode or NodeList
9534                 //
9535                 //              if data is a NodeList (or an array of nodes) nodes are copied
9536                 //              so you can import nodes from another document implicitly
9537
9538                 // clear href so we can't run refresh and clear content
9539                 // refresh should only work if we downloaded the content
9540                 this.href = "";
9541
9542                 // Cancel any in-flight requests (an attr('content') will cancel any in-flight attr('href', ...))
9543                 this.cancel();
9544
9545                 // Even though user is just setting content directly, still need to define an onLoadDeferred
9546                 // because the _onLoadHandler() handler is still getting called from setContent()
9547                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
9548
9549                 this._setContent(data || "");
9550
9551                 this._isDownloaded = false; // mark that content is from a attr('content') not an attr('href')
9552
9553                 return this.onLoadDeferred;     // dojo.Deferred
9554         },
9555         _getContentAttr: function(){
9556                 // summary:
9557                 //              Hook to make attr("content") work
9558                 return this.containerNode.innerHTML;
9559         },
9560
9561         cancel: function(){
9562                 // summary:
9563                 //              Cancels an in-flight download of content
9564                 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
9565                         this._xhrDfd.cancel();
9566                 }
9567                 delete this._xhrDfd; // garbage collect
9568
9569                 this.onLoadDeferred = null;
9570         },
9571
9572         uninitialize: function(){
9573                 if(this._beingDestroyed){
9574                         this.cancel();
9575                 }
9576                 this.inherited(arguments);
9577         },
9578
9579         destroyRecursive: function(/*Boolean*/ preserveDom){
9580                 // summary:
9581                 //              Destroy the ContentPane and its contents
9582
9583                 // if we have multiple controllers destroying us, bail after the first
9584                 if(this._beingDestroyed){
9585                         return;
9586                 }
9587                 this.inherited(arguments);
9588         },
9589
9590         resize: function(changeSize, resultSize){
9591                 // summary:
9592                 //              See `dijit.layout._LayoutWidget.resize` for description.
9593                 //              Although ContentPane doesn't extend _LayoutWidget, it does implement
9594                 //              the same API.
9595
9596                 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
9597                 // never called, so resize() is our trigger to do the initial href download.
9598                 if(!this._wasShown){
9599                         this._onShow();
9600                 }
9601
9602                 this._resizeCalled = true;
9603
9604                 // Set margin box size, unless it wasn't specified, in which case use current size.
9605                 if(changeSize){
9606                         dojo.marginBox(this.domNode, changeSize);
9607                 }
9608
9609                 // Compute content box size of containerNode in case we [later] need to size our single child.
9610                 var cn = this.containerNode;
9611                 if(cn === this.domNode){
9612                         // If changeSize or resultSize was passed to this method and this.containerNode ==
9613                         // this.domNode then we can compute the content-box size without querying the node,
9614                         // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
9615                         var mb = resultSize || {};
9616                         dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
9617                         if(!("h" in mb) || !("w" in mb)){
9618                                 mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
9619                         }
9620                         this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
9621                 }else{
9622                         this._contentBox = dojo.contentBox(cn);
9623                 }
9624
9625                 // Make my children layout, or size my single child widget
9626                 this._layoutChildren();
9627         },
9628
9629         _isShown: function(){
9630                 // summary:
9631                 //              Returns true if the content is currently shown.
9632                 // description:
9633                 //              If I am a child of a layout widget then it actually returns true if I've ever been visible,
9634                 //              not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
9635                 //              tree every call, and at least solves the performance problem on page load by deferring loading
9636                 //              hidden ContentPanes until they are first shown
9637
9638                 if(this._childOfLayoutWidget){
9639                         // If we are TitlePane, etc - we return that only *IF* we've been resized
9640                         if(this._resizeCalled && "open" in this){
9641                                 return this.open;
9642                         }
9643                         return this._resizeCalled;
9644                 }else if("open" in this){
9645                         return this.open;               // for TitlePane, etc.
9646                 }else{
9647                         // TODO: with _childOfLayoutWidget check maybe this branch no longer necessary?
9648                         var node = this.domNode;
9649                         return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden");
9650                 }
9651         },
9652
9653         _onShow: function(){
9654                 // summary:
9655                 //              Called when the ContentPane is made visible
9656                 // description:
9657                 //              For a plain ContentPane, this is called on initialization, from startup().
9658                 //              If the ContentPane is a hidden pane of a TabContainer etc., then it's
9659                 //              called whenever the pane is made visible.
9660                 //
9661                 //              Does necessary processing, including href download and layout/resize of
9662                 //              child widget(s)
9663
9664                 if(this.href){
9665                         if(!this._xhrDfd && // if there's an href that isn't already being loaded
9666                                 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
9667                         ){
9668                                 this.refresh();
9669                         }
9670                 }else{
9671                         // If we are the child of a layout widget then the layout widget will call resize() on
9672                         // us, and then we will size our child/children.   Otherwise, we need to do it now.
9673                         if(!this._childOfLayoutWidget && this._needLayout){
9674                                 // If a layout has been scheduled for when we become visible, do it now
9675                                 this._layoutChildren();
9676                         }
9677                 }
9678
9679                 this.inherited(arguments);
9680
9681                 // Need to keep track of whether ContentPane has been shown (which is different than
9682                 // whether or not it's currently visible).
9683                 this._wasShown = true;
9684         },
9685
9686         refresh: function(){
9687                 // summary:
9688                 //              [Re]download contents of href and display
9689                 // description:
9690                 //              1. cancels any currently in-flight requests
9691                 //              2. posts "loading..." message
9692                 //              3. sends XHR to download new data
9693
9694                 // Cancel possible prior in-flight request
9695                 this.cancel();
9696
9697                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
9698                 this._load();
9699                 return this.onLoadDeferred;
9700         },
9701
9702         _load: function(){
9703                 // summary:
9704                 //              Load/reload the href specified in this.href
9705
9706                 // display loading message
9707                 this._setContent(this.onDownloadStart(), true);
9708
9709                 var self = this;
9710                 var getArgs = {
9711                         preventCache: (this.preventCache || this.refreshOnShow),
9712                         url: this.href,
9713                         handleAs: "text"
9714                 };
9715                 if(dojo.isObject(this.ioArgs)){
9716                         dojo.mixin(getArgs, this.ioArgs);
9717                 }
9718
9719                 var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
9720
9721                 hand.addCallback(function(html){
9722                         try{
9723                                 self._isDownloaded = true;
9724                                 self._setContent(html, false);
9725                                 self.onDownloadEnd();
9726                         }catch(err){
9727                                 self._onError('Content', err); // onContentError
9728                         }
9729                         delete self._xhrDfd;
9730                         return html;
9731                 });
9732
9733                 hand.addErrback(function(err){
9734                         if(!hand.canceled){
9735                                 // show error message in the pane
9736                                 self._onError('Download', err); // onDownloadError
9737                         }
9738                         delete self._xhrDfd;
9739                         return err;
9740                 });
9741
9742                 // Remove flag saying that a load is needed
9743                 delete this._hrefChanged;
9744         },
9745
9746         _onLoadHandler: function(data){
9747                 // summary:
9748                 //              This is called whenever new content is being loaded
9749                 this.isLoaded = true;
9750                 try{
9751                         this.onLoadDeferred.callback(data);
9752                         this.onLoad(data);
9753                 }catch(e){
9754                         console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
9755                 }
9756         },
9757
9758         _onUnloadHandler: function(){
9759                 // summary:
9760                 //              This is called whenever the content is being unloaded
9761                 this.isLoaded = false;
9762                 try{
9763                         this.onUnload();
9764                 }catch(e){
9765                         console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
9766                 }
9767         },
9768
9769         destroyDescendants: function(){
9770                 // summary:
9771                 //              Destroy all the widgets inside the ContentPane and empty containerNode
9772
9773                 // Make sure we call onUnload (but only when the ContentPane has real content)
9774                 if(this.isLoaded){
9775                         this._onUnloadHandler();
9776                 }
9777
9778                 // Even if this.isLoaded == false there might still be a "Loading..." message
9779                 // to erase, so continue...
9780
9781                 // For historical reasons we need to delete all widgets under this.containerNode,
9782                 // even ones that the user has created manually.
9783                 var setter = this._contentSetter;
9784                 dojo.forEach(this.getChildren(), function(widget){
9785                         if(widget.destroyRecursive){
9786                                 widget.destroyRecursive();
9787                         }
9788                 });
9789                 if(setter){
9790                         // Most of the widgets in setter.parseResults have already been destroyed, but
9791                         // things like Menu that have been moved to <body> haven't yet
9792                         dojo.forEach(setter.parseResults, function(widget){
9793                                 if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
9794                                         widget.destroyRecursive();
9795                                 }
9796                         });
9797                         delete setter.parseResults;
9798                 }
9799
9800                 // And then clear away all the DOM nodes
9801                 dojo.html._emptyNode(this.containerNode);
9802
9803                 // Delete any state information we have about current contents
9804                 delete this._singleChild;
9805         },
9806
9807         _setContent: function(cont, isFakeContent){
9808                 // summary:
9809                 //              Insert the content into the container node
9810
9811                 // first get rid of child widgets
9812                 this.destroyDescendants();
9813
9814                 // dojo.html.set will take care of the rest of the details
9815                 // we provide an override for the error handling to ensure the widget gets the errors
9816                 // configure the setter instance with only the relevant widget instance properties
9817                 // NOTE: unless we hook into attr, or provide property setters for each property,
9818                 // we need to re-configure the ContentSetter with each use
9819                 var setter = this._contentSetter;
9820                 if(! (setter && setter instanceof dojo.html._ContentSetter)){
9821                         setter = this._contentSetter = new dojo.html._ContentSetter({
9822                                 node: this.containerNode,
9823                                 _onError: dojo.hitch(this, this._onError),
9824                                 onContentError: dojo.hitch(this, function(e){
9825                                         // fires if a domfault occurs when we are appending this.errorMessage
9826                                         // like for instance if domNode is a UL and we try append a DIV
9827                                         var errMess = this.onContentError(e);
9828                                         try{
9829                                                 this.containerNode.innerHTML = errMess;
9830                                         }catch(e){
9831                                                 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
9832                                         }
9833                                 })/*,
9834                                 _onError */
9835                         });
9836                 };
9837
9838                 var setterParams = dojo.mixin({
9839                         cleanContent: this.cleanContent,
9840                         extractContent: this.extractContent,
9841                         parseContent: this.parseOnLoad,
9842                         dir: this.dir,
9843                         lang: this.lang
9844                 }, this._contentSetterParams || {});
9845
9846                 dojo.mixin(setter, setterParams);
9847
9848                 setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont );
9849
9850                 // setter params must be pulled afresh from the ContentPane each time
9851                 delete this._contentSetterParams;
9852
9853                 if(!isFakeContent){
9854                         // Startup each top level child widget (and they will start their children, recursively)
9855                         dojo.forEach(this.getChildren(), function(child){
9856                                 // The parser has already called startup on all widgets *without* a getParent() method
9857                                 if(!this.parseOnLoad || child.getParent){
9858                                         child.startup();
9859                                 }
9860                         }, this);
9861
9862                         // Call resize() on each of my child layout widgets,
9863                         // or resize() on my single child layout widget...
9864                         // either now (if I'm currently visible)
9865                         // or when I become visible
9866                         this._scheduleLayout();
9867
9868                         this._onLoadHandler(cont);
9869                 }
9870         },
9871
9872         _onError: function(type, err, consoleText){
9873                 this.onLoadDeferred.errback(err);
9874
9875                 // shows user the string that is returned by on[type]Error
9876                 // overide on[type]Error and return your own string to customize
9877                 var errText = this['on' + type + 'Error'].call(this, err);
9878                 if(consoleText){
9879                         console.error(consoleText, err);
9880                 }else if(errText){// a empty string won't change current content
9881                         this._setContent(errText, true);
9882                 }
9883         },
9884
9885         _scheduleLayout: function(){
9886                 // summary:
9887                 //              Call resize() on each of my child layout widgets, either now
9888                 //              (if I'm currently visible) or when I become visible
9889                 if(this._isShown()){
9890                         this._layoutChildren();
9891                 }else{
9892                         this._needLayout = true;
9893                 }
9894         },
9895
9896         _layoutChildren: function(){
9897                 // summary:
9898                 //              Since I am a Container widget, each of my children expects me to
9899                 //              call resize() or layout() on them.
9900                 // description:
9901                 //              Should be called on initialization and also whenever we get new content
9902                 //              (from an href, or from attr('content', ...))... but deferred until
9903                 //              the ContentPane is visible
9904
9905                 if(this.doLayout){
9906                         this._checkIfSingleChild();
9907                 }
9908
9909                 if(this._singleChild && this._singleChild.resize){
9910                         var cb = this._contentBox || dojo.contentBox(this.containerNode);
9911
9912                         // note: if widget has padding this._contentBox will have l and t set,
9913                         // but don't pass them to resize() or it will doubly-offset the child
9914                         this._singleChild.resize({w: cb.w, h: cb.h});
9915                 }else{
9916                         // All my child widgets are independently sized (rather than matching my size),
9917                         // but I still need to call resize() on each child to make it layout.
9918                         dojo.forEach(this.getChildren(), function(widget){
9919                                 if(widget.resize){
9920                                         widget.resize();
9921                                 }
9922                         });
9923                 }
9924                 delete this._needLayout;
9925         },
9926
9927         // EVENT's, should be overide-able
9928         onLoad: function(data){
9929                 // summary:
9930                 //              Event hook, is called after everything is loaded and widgetified
9931                 // tags:
9932                 //              callback
9933         },
9934
9935         onUnload: function(){
9936                 // summary:
9937                 //              Event hook, is called before old content is cleared
9938                 // tags:
9939                 //              callback
9940         },
9941
9942         onDownloadStart: function(){
9943                 // summary:
9944                 //              Called before download starts.
9945                 // description:
9946                 //              The string returned by this function will be the html
9947                 //              that tells the user we are loading something.
9948                 //              Override with your own function if you want to change text.
9949                 // tags:
9950                 //              extension
9951                 return this.loadingMessage;
9952         },
9953
9954         onContentError: function(/*Error*/ error){
9955                 // summary:
9956                 //              Called on DOM faults, require faults etc. in content.
9957                 //
9958                 //              In order to display an error message in the pane, return
9959                 //              the error message from this method, as an HTML string.
9960                 //
9961                 //              By default (if this method is not overriden), it returns
9962                 //              nothing, so the error message is just printed to the console.
9963                 // tags:
9964                 //              extension
9965         },
9966
9967         onDownloadError: function(/*Error*/ error){
9968                 // summary:
9969                 //              Called when download error occurs.
9970                 //
9971                 //              In order to display an error message in the pane, return
9972                 //              the error message from this method, as an HTML string.
9973                 //
9974                 //              Default behavior (if this method is not overriden) is to display
9975                 //              the error message inside the pane.
9976                 // tags:
9977                 //              extension
9978                 return this.errorMessage;
9979         },
9980
9981         onDownloadEnd: function(){
9982                 // summary:
9983                 //              Called when download is finished.
9984                 // tags:
9985                 //              callback
9986         }
9987 });
9988
9989 }
9990
9991 if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9992 dojo._hasResource["dijit.TooltipDialog"] = true;
9993 dojo.provide("dijit.TooltipDialog");
9994
9995
9996
9997
9998
9999
10000 dojo.declare(
10001                 "dijit.TooltipDialog",
10002                 [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
10003                 {
10004                         // summary:
10005                         //              Pops up a dialog that appears like a Tooltip
10006
10007                         // title: String
10008                         //              Description of tooltip dialog (required for a11y)
10009                         title: "",
10010
10011                         // doLayout: [protected] Boolean
10012                         //              Don't change this parameter from the default value.
10013                         //              This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
10014                         //              is never a child of a layout container, nor can you specify the size of
10015                         //              TooltipDialog in order to control the size of an inner widget.
10016                         doLayout: false,
10017
10018                         // autofocus: Boolean
10019                         //              A Toggle to modify the default focus behavior of a Dialog, which
10020                         //              is to focus on the first dialog element after opening the dialog.
10021                         //              False will disable autofocusing. Default: true
10022                         autofocus: true,
10023
10024                         // baseClass: [protected] String
10025                         //              The root className to use for the various states of this widget
10026                         baseClass: "dijitTooltipDialog",
10027
10028                         // _firstFocusItem: [private] [readonly] DomNode
10029                         //              The pointer to the first focusable node in the dialog.
10030                         //              Set by `dijit._DialogMixin._getFocusItems`.
10031                         _firstFocusItem: null,
10032
10033                         // _lastFocusItem: [private] [readonly] DomNode
10034                         //              The pointer to which node has focus prior to our dialog.
10035                         //              Set by `dijit._DialogMixin._getFocusItems`.
10036                         _lastFocusItem: null,
10037
10038                         templateString: dojo.cache("dijit", "templates/TooltipDialog.html", "<div 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=\"presentation\"></div>\n</div>\n"),
10039
10040                         postCreate: function(){
10041                                 this.inherited(arguments);
10042                                 this.connect(this.containerNode, "onkeypress", "_onKey");
10043                                 this.containerNode.title = this.title;
10044                         },
10045
10046                         orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
10047                                 // summary:
10048                                 //              Configure widget to be displayed in given position relative to the button.
10049                                 //              This is called from the dijit.popup code, and should not be called
10050                                 //              directly.
10051                                 // tags:
10052                                 //              protected
10053                                 var c = this._currentOrientClass;
10054                                 if(c){
10055                                         dojo.removeClass(this.domNode, c);
10056                                 }
10057                                 c = "dijitTooltipAB"+(corner.charAt(1) == 'L'?"Left":"Right")+" dijitTooltip"+(corner.charAt(0) == 'T' ? "Below" : "Above");
10058                                 dojo.addClass(this.domNode, c);
10059                                 this._currentOrientClass = c;
10060                         },
10061
10062                         onOpen: function(/*Object*/ pos){
10063                                 // summary:
10064                                 //              Called when dialog is displayed.
10065                                 //              This is called from the dijit.popup code, and should not be called directly.
10066                                 // tags:
10067                                 //              protected
10068
10069                                 this.orient(this.domNode,pos.aroundCorner, pos.corner);
10070                                 this._onShow(); // lazy load trigger
10071
10072                                 if(this.autofocus){
10073                                         this._getFocusItems(this.containerNode);
10074                                         dijit.focus(this._firstFocusItem);
10075                                 }
10076                         },
10077
10078                         onClose: function(){
10079                                 // summary:
10080                                 //              Called when dialog is hidden.
10081                                 //              This is called from the dijit.popup code, and should not be called directly.
10082                                 // tags:
10083                                 //              protected
10084                                 this.onHide();
10085                         },
10086
10087                         _onKey: function(/*Event*/ evt){
10088                                 // summary:
10089                                 //              Handler for keyboard events
10090                                 // description:
10091                                 //              Keep keyboard focus in dialog; close dialog on escape key
10092                                 // tags:
10093                                 //              private
10094
10095                                 var node = evt.target;
10096                                 var dk = dojo.keys;
10097                                 if(evt.charOrCode === dk.TAB){
10098                                         this._getFocusItems(this.containerNode);
10099                                 }
10100                                 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
10101                                 if(evt.charOrCode == dk.ESCAPE){
10102                                         // Use setTimeout to avoid crash on IE, see #10396.
10103                                         setTimeout(dojo.hitch(this, "onCancel"), 0);
10104                                         dojo.stopEvent(evt);
10105                                 }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
10106                                         if(!singleFocusItem){
10107                                                 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
10108                                         }
10109                                         dojo.stopEvent(evt);
10110                                 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
10111                                         if(!singleFocusItem){
10112                                                 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
10113                                         }
10114                                         dojo.stopEvent(evt);
10115                                 }else if(evt.charOrCode === dk.TAB){
10116                                         // we want the browser's default tab handling to move focus
10117                                         // but we don't want the tab to propagate upwards
10118                                         evt.stopPropagation();
10119                                 }
10120                         }
10121                 }
10122         );
10123
10124 }
10125
10126 if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10127 dojo._hasResource["dijit.Dialog"] = true;
10128 dojo.provide("dijit.Dialog");
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143
10144 /*=====
10145 dijit._underlay = function(kwArgs){
10146         // summary:
10147         //              A shared instance of a `dijit.DialogUnderlay`
10148         //
10149         // description:
10150         //              A shared instance of a `dijit.DialogUnderlay` created and
10151         //              used by `dijit.Dialog`, though never created until some Dialog
10152         //              or subclass thereof is shown.
10153 };
10154 =====*/
10155
10156 dojo.declare(
10157         "dijit._DialogBase",
10158         [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
10159         {
10160                 // summary:
10161                 //              A modal dialog Widget
10162                 //
10163                 // description:
10164                 //              Pops up a modal dialog window, blocking access to the screen
10165                 //              and also graying out the screen Dialog is extended from
10166                 //              ContentPane so it supports all the same parameters (href, etc.)
10167                 //
10168                 // example:
10169                 // |    <div dojoType="dijit.Dialog" href="test.html"></div>
10170                 //
10171                 // example:
10172                 // |    var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
10173                 // |    dojo.body().appendChild(foo.domNode);
10174                 // |    foo.startup();
10175
10176                 templateString: dojo.cache("dijit", "templates/Dialog.html", "<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\"></span>\n\t<span dojoAttachPoint=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" dojoAttachEvent=\"onclick: onCancel\" title=\"${buttonCancel}\">\n\t\t<span dojoAttachPoint=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div dojoAttachPoint=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"),
10177                 
10178                 baseClass: "dijitDialog",
10179                 
10180                 cssStateNodes: {
10181                         closeButtonNode: "dijitDialogCloseIcon"
10182                 },
10183
10184                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
10185                         title: [
10186                                 { node: "titleNode", type: "innerHTML" },
10187                                 { node: "titleBar", type: "attribute" }
10188                         ],
10189                         "aria-describedby":""
10190                 }),
10191
10192                 // open: Boolean
10193                 //              True if Dialog is currently displayed on screen.
10194                 open: false,
10195
10196                 // duration: Integer
10197                 //              The time in milliseconds it takes the dialog to fade in and out
10198                 duration: dijit.defaultDuration,
10199
10200                 // refocus: Boolean
10201                 //              A Toggle to modify the default focus behavior of a Dialog, which
10202                 //              is to re-focus the element which had focus before being opened.
10203                 //              False will disable refocusing. Default: true
10204                 refocus: true,
10205
10206                 // autofocus: Boolean
10207                 //              A Toggle to modify the default focus behavior of a Dialog, which
10208                 //              is to focus on the first dialog element after opening the dialog.
10209                 //              False will disable autofocusing. Default: true
10210                 autofocus: true,
10211
10212                 // _firstFocusItem: [private] [readonly] DomNode
10213                 //              The pointer to the first focusable node in the dialog.
10214                 //              Set by `dijit._DialogMixin._getFocusItems`.
10215                 _firstFocusItem: null,
10216
10217                 // _lastFocusItem: [private] [readonly] DomNode
10218                 //              The pointer to which node has focus prior to our dialog.
10219                 //              Set by `dijit._DialogMixin._getFocusItems`.
10220                 _lastFocusItem: null,
10221
10222                 // doLayout: [protected] Boolean
10223                 //              Don't change this parameter from the default value.
10224                 //              This ContentPane parameter doesn't make sense for Dialog, since Dialog
10225                 //              is never a child of a layout container, nor can you specify the size of
10226                 //              Dialog in order to control the size of an inner widget.
10227                 doLayout: false,
10228
10229                 // draggable: Boolean
10230                 //              Toggles the moveable aspect of the Dialog. If true, Dialog
10231                 //              can be dragged by it's title. If false it will remain centered
10232                 //              in the viewport.
10233                 draggable: true,
10234
10235                 //aria-describedby: String
10236                 //              Allows the user to add an aria-describedby attribute onto the dialog.   The value should
10237                 //              be the id of the container element of text that describes the dialog purpose (usually
10238                 //              the first text in the dialog).
10239                 //              <div dojoType="dijit.Dialog" aria-describedby="intro" .....>
10240                 //                      <div id="intro">Introductory text</div>
10241                 //                      <div>rest of dialog contents</div>
10242                 //              </div>
10243                 "aria-describedby":"",
10244
10245                 postMixInProperties: function(){
10246                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
10247                         dojo.mixin(this, _nlsResources);
10248                         this.inherited(arguments);
10249                 },
10250
10251                 postCreate: function(){
10252                         dojo.style(this.domNode, {
10253                                 display: "none",
10254                                 position:"absolute"
10255                         });
10256                         dojo.body().appendChild(this.domNode);
10257
10258                         this.inherited(arguments);
10259
10260                         this.connect(this, "onExecute", "hide");
10261                         this.connect(this, "onCancel", "hide");
10262                         this._modalconnects = [];
10263                 },
10264
10265                 onLoad: function(){
10266                         // summary:
10267                         //              Called when data has been loaded from an href.
10268                         //              Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
10269                         //              but should *not* be overriden.
10270                         // tags:
10271                         //              callback
10272
10273                         // when href is specified we need to reposition the dialog after the data is loaded
10274                         // and find the focusable elements
10275                         this._position();
10276                         if(this.autofocus){
10277                                 this._getFocusItems(this.domNode);
10278                                 dijit.focus(this._firstFocusItem);
10279                         }
10280                         this.inherited(arguments);
10281                 },
10282
10283                 _endDrag: function(e){
10284                         // summary:
10285                         //              Called after dragging the Dialog. Saves the position of the dialog in the viewport.
10286                         // tags:
10287                         //              private
10288                         if(e && e.node && e.node === this.domNode){
10289                                 this._relativePosition = dojo.position(e.node);
10290                         }
10291                 },
10292
10293                 _setup: function(){
10294                         // summary:
10295                         //              Stuff we need to do before showing the Dialog for the first
10296                         //              time (but we defer it until right beforehand, for
10297                         //              performance reasons).
10298                         // tags:
10299                         //              private
10300
10301                         var node = this.domNode;
10302
10303                         if(this.titleBar && this.draggable){
10304                                 this._moveable = (dojo.isIE == 6) ?
10305                                         new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) :   // prevent overload, see #5285
10306                                         new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
10307                                 dojo.subscribe("/dnd/move/stop",this,"_endDrag");
10308                         }else{
10309                                 dojo.addClass(node,"dijitDialogFixed");
10310                         }
10311
10312                         this.underlayAttrs = {
10313                                 dialogId: this.id,
10314                                 "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
10315                         };
10316
10317                         this._fadeIn = dojo.fadeIn({
10318                                 node: node,
10319                                 duration: this.duration,
10320                                 beforeBegin: dojo.hitch(this, function(){
10321                                         var underlay = dijit._underlay;
10322                                         if(!underlay){
10323                                                 underlay = dijit._underlay = new dijit.DialogUnderlay(this.underlayAttrs);
10324                                         }else{
10325                                                 underlay.set(this.underlayAttrs);
10326                                         }
10327
10328                                         var ds = dijit._dialogStack,
10329                                                 zIndex = 948 + ds.length*2;
10330                                         if(ds.length == 1){     // first dialog
10331                                                 underlay.show();
10332                                         }
10333                                         dojo.style(dijit._underlay.domNode, 'zIndex', zIndex);
10334                                         dojo.style(this.domNode, 'zIndex', zIndex + 1);
10335                                 }),
10336                                 onEnd: dojo.hitch(this, function(){
10337                                         if(this.autofocus){
10338                                                 // find focusable Items each time dialog is shown since if dialog contains a widget the
10339                                                 // first focusable items can change
10340                                                 this._getFocusItems(this.domNode);
10341                                                 dijit.focus(this._firstFocusItem);
10342                                         }
10343                                 })
10344                          });
10345
10346                         this._fadeOut = dojo.fadeOut({
10347                                 node: node,
10348                                 duration: this.duration,
10349                                 onEnd: dojo.hitch(this, function(){
10350                                         node.style.display = "none";
10351
10352                                         // Restore the previous dialog in the stack, or if this is the only dialog
10353                                         // then restore to original page
10354                                         var ds = dijit._dialogStack;
10355                                         if(ds.length == 0){
10356                                                 dijit._underlay.hide();
10357                                         }else{
10358                                                 dojo.style(dijit._underlay.domNode, 'zIndex', 948 + ds.length*2);
10359                                                 dijit._underlay.set(ds[ds.length-1].underlayAttrs);
10360                                         }
10361
10362                                         // Restore focus to wherever it was before this dialog was displayed
10363                                         if(this.refocus){
10364                                                 var focus = this._savedFocus;
10365
10366                                                 // If we are returning control to a previous dialog but for some reason
10367                                                 // that dialog didn't have a focused field, set focus to first focusable item.
10368                                                 // This situation could happen if two dialogs appeared at nearly the same time,
10369                                                 // since a dialog doesn't set it's focus until the fade-in is finished.
10370                                                 if(ds.length > 0){
10371                                                         var pd = ds[ds.length-1];
10372                                                         if(!dojo.isDescendant(focus.node, pd.domNode)){
10373                                                                 pd._getFocusItems(pd.domNode);
10374                                                                 focus = pd._firstFocusItem;
10375                                                         }
10376                                                 }
10377
10378                                                 dijit.focus(focus);
10379                                         }
10380                                 })
10381                          });
10382                 },
10383
10384                 uninitialize: function(){
10385                         var wasPlaying = false;
10386                         if(this._fadeIn && this._fadeIn.status() == "playing"){
10387                                 wasPlaying = true;
10388                                 this._fadeIn.stop();
10389                         }
10390                         if(this._fadeOut && this._fadeOut.status() == "playing"){
10391                                 wasPlaying = true;
10392                                 this._fadeOut.stop();
10393                         }
10394                         
10395                         // Hide the underlay, unless the underlay widget has already been destroyed
10396                         // because we are being called during page unload (when all widgets are destroyed)
10397                         if((this.open || wasPlaying) && !dijit._underlay._destroyed){
10398                                 dijit._underlay.hide();
10399                         }
10400
10401                         if(this._moveable){
10402                                 this._moveable.destroy();
10403                         }
10404                         this.inherited(arguments);
10405                 },
10406
10407                 _size: function(){
10408                         // summary:
10409                         //              If necessary, shrink dialog contents so dialog fits in viewport
10410                         // tags:
10411                         //              private
10412
10413                         this._checkIfSingleChild();
10414
10415                         // If we resized the dialog contents earlier, reset them back to original size, so
10416                         // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
10417                         // Need to do this before the dojo.marginBox(this.domNode) call below.
10418                         if(this._singleChild){
10419                                 if(this._singleChildOriginalStyle){
10420                                         this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
10421                                 }
10422                                 delete this._singleChildOriginalStyle;
10423                         }else{
10424                                 dojo.style(this.containerNode, {
10425                                         width:"auto",
10426                                         height:"auto"
10427                                 });
10428                         }
10429
10430                         var mb = dojo.marginBox(this.domNode);
10431                         var viewport = dojo.window.getBox();
10432                         if(mb.w >= viewport.w || mb.h >= viewport.h){
10433                                 // Reduce size of dialog contents so that dialog fits in viewport
10434
10435                                 var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
10436                                         h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
10437
10438                                 if(this._singleChild && this._singleChild.resize){
10439                                         this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
10440                                         this._singleChild.resize({w: w, h: h});
10441                                 }else{
10442                                         dojo.style(this.containerNode, {
10443                                                 width: w + "px",
10444                                                 height: h + "px",
10445                                                 overflow: "auto",
10446                                                 position: "relative"    // workaround IE bug moving scrollbar or dragging dialog
10447                                         });
10448                                 }
10449                         }else{
10450                                 if(this._singleChild && this._singleChild.resize){
10451                                         this._singleChild.resize();
10452                                 }
10453                         }
10454                 },
10455
10456                 _position: function(){
10457                         // summary:
10458                         //              Position modal dialog in the viewport. If no relative offset
10459                         //              in the viewport has been determined (by dragging, for instance),
10460                         //              center the node. Otherwise, use the Dialog's stored relative offset,
10461                         //              and position the node to top: left: values based on the viewport.
10462                         // tags:
10463                         //              private
10464                         if(!dojo.hasClass(dojo.body(),"dojoMove")){
10465                                 var node = this.domNode,
10466                                         viewport = dojo.window.getBox(),
10467                                         p = this._relativePosition,
10468                                         bb = p ? null : dojo._getBorderBox(node),
10469                                         l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
10470                                         t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
10471                                 ;
10472                                 dojo.style(node,{
10473                                         left: l + "px",
10474                                         top: t + "px"
10475                                 });
10476                         }
10477                 },
10478
10479                 _onKey: function(/*Event*/ evt){
10480                         // summary:
10481                         //              Handles the keyboard events for accessibility reasons
10482                         // tags:
10483                         //              private
10484
10485                         var ds = dijit._dialogStack;
10486                         if(ds[ds.length-1] != this){
10487                                 // console.debug(this.id + ': skipping because', this, 'is not the active dialog');
10488                                 return;
10489                         }
10490
10491                         if(evt.charOrCode){
10492                                 var dk = dojo.keys;
10493                                 var node = evt.target;
10494                                 if(evt.charOrCode === dk.TAB){
10495                                         this._getFocusItems(this.domNode);
10496                                 }
10497                                 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
10498                                 // see if we are shift-tabbing from first focusable item on dialog
10499                                 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
10500                                         if(!singleFocusItem){
10501                                                 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
10502                                         }
10503                                         dojo.stopEvent(evt);
10504                                 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
10505                                         if(!singleFocusItem){
10506                                                 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
10507                                         }
10508                                         dojo.stopEvent(evt);
10509                                 }else{
10510                                         // see if the key is for the dialog
10511                                         while(node){
10512                                                 if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
10513                                                         if(evt.charOrCode == dk.ESCAPE){
10514                                                                 this.onCancel();
10515                                                         }else{
10516                                                                 return; // just let it go
10517                                                         }
10518                                                 }
10519                                                 node = node.parentNode;
10520                                         }
10521                                         // this key is for the disabled document window
10522                                         if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
10523                                                 dojo.stopEvent(evt);
10524                                         // opera won't tab to a div
10525                                         }else if(!dojo.isOpera){
10526                                                 try{
10527                                                         this._firstFocusItem.focus();
10528                                                 }catch(e){ /*squelch*/ }
10529                                         }
10530                                 }
10531                         }
10532                 },
10533
10534                 show: function(){
10535                         // summary:
10536                         //              Display the dialog
10537                         if(this.open){ return; }
10538
10539                         // first time we show the dialog, there's some initialization stuff to do
10540                         if(!this._alreadyInitialized){
10541                                 this._setup();
10542                                 this._alreadyInitialized=true;
10543                         }
10544
10545                         if(this._fadeOut.status() == "playing"){
10546                                 this._fadeOut.stop();
10547                         }
10548
10549                         this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
10550                         this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
10551                                 // IE gives spurious resize events and can actually get stuck
10552                                 // in an infinite loop if we don't ignore them
10553                                 var viewport = dojo.window.getBox();
10554                                 if(!this._oldViewport ||
10555                                                 viewport.h != this._oldViewport.h ||
10556                                                 viewport.w != this._oldViewport.w){
10557                                         this.layout();
10558                                         this._oldViewport = viewport;
10559                                 }
10560                         }));
10561                         this._modalconnects.push(dojo.connect(dojo.doc.documentElement, "onkeypress", this, "_onKey"));
10562
10563                         dojo.style(this.domNode, {
10564                                 opacity:0,
10565                                 display:""
10566                         });
10567
10568                         this.open = true;
10569                         this._onShow(); // lazy load trigger
10570
10571                         this._size();
10572                         this._position();
10573                         dijit._dialogStack.push(this);
10574                         this._fadeIn.play();
10575
10576                         this._savedFocus = dijit.getFocus(this);
10577                 },
10578
10579                 hide: function(){
10580                         // summary:
10581                         //              Hide the dialog
10582
10583                         // if we haven't been initialized yet then we aren't showing and we can just return
10584                         // or if we aren't the active dialog, don't allow us to close yet
10585                         var ds = dijit._dialogStack;
10586                         if(!this._alreadyInitialized || this != ds[ds.length-1]){
10587                                 return;
10588                         }
10589
10590                         if(this._fadeIn.status() == "playing"){
10591                                 this._fadeIn.stop();
10592                         }
10593
10594                         // throw away current active dialog from stack -- making the previous dialog or the node on the original page active
10595                         ds.pop();
10596
10597                         this._fadeOut.play();
10598
10599                         if(this._scrollConnected){
10600                                 this._scrollConnected = false;
10601                         }
10602                         dojo.forEach(this._modalconnects, dojo.disconnect);
10603                         this._modalconnects = [];
10604
10605                         if(this._relativePosition){
10606                                 delete this._relativePosition;
10607                         }
10608                         this.open = false;
10609
10610                         this.onHide();
10611                 },
10612
10613                 layout: function(){
10614                         // summary:
10615                         //              Position the Dialog and the underlay
10616                         // tags:
10617                         //              private
10618                         if(this.domNode.style.display != "none"){
10619                                 if(dijit._underlay){    // avoid race condition during show()
10620                                         dijit._underlay.layout();
10621                                 }
10622                                 this._position();
10623                         }
10624                 },
10625
10626                 destroy: function(){
10627                         dojo.forEach(this._modalconnects, dojo.disconnect);
10628                         if(this.refocus && this.open){
10629                                 setTimeout(dojo.hitch(dijit,"focus",this._savedFocus), 25);
10630                         }
10631                         this.inherited(arguments);
10632                 }
10633         }
10634 );
10635
10636 dojo.declare(
10637         "dijit.Dialog",
10638         [dijit.layout.ContentPane, dijit._DialogBase],
10639         {}
10640 );
10641
10642 // Stack of currenctly displayed dialogs, layered on top of each other
10643 dijit._dialogStack = [];
10644
10645 // For back-compat.  TODO: remove in 2.0
10646
10647
10648 }
10649
10650 if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10651 dojo._hasResource["dijit._HasDropDown"] = true;
10652 dojo.provide("dijit._HasDropDown");
10653
10654
10655
10656
10657 dojo.declare("dijit._HasDropDown",
10658         null,
10659         {
10660                 // summary:
10661                 //              Mixin for widgets that need drop down ability.
10662
10663                 // _buttonNode: [protected] DomNode
10664                 //              The button/icon/node to click to display the drop down.
10665                 //              Can be set via a dojoAttachPoint assignment.
10666                 //              If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
10667                 _buttonNode: null,
10668
10669                 // _arrowWrapperNode: [protected] DomNode
10670                 //              Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
10671                 //              on where the drop down is set to be positioned.
10672                 //              Can be set via a dojoAttachPoint assignment.
10673                 //              If missing, then _buttonNode will be used.
10674                 _arrowWrapperNode: null,
10675
10676                 // _popupStateNode: [protected] DomNode
10677                 //              The node to set the popupActive class on.
10678                 //              Can be set via a dojoAttachPoint assignment.
10679                 //              If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
10680                 _popupStateNode: null,
10681
10682                 // _aroundNode: [protected] DomNode
10683                 //              The node to display the popup around.
10684                 //              Can be set via a dojoAttachPoint assignment.
10685                 //              If missing, then domNode will be used.
10686                 _aroundNode: null,
10687
10688                 // dropDown: [protected] Widget
10689                 //              The widget to display as a popup.  This widget *must* be
10690                 //              defined before the startup function is called.
10691                 dropDown: null,
10692
10693                 // autoWidth: [protected] Boolean
10694                 //              Set to true to make the drop down at least as wide as this
10695                 //              widget.  Set to false if the drop down should just be its
10696                 //              default width
10697                 autoWidth: true,
10698
10699                 // forceWidth: [protected] Boolean
10700                 //              Set to true to make the drop down exactly as wide as this
10701                 //              widget.  Overrides autoWidth.
10702                 forceWidth: false,
10703
10704                 // maxHeight: [protected] Integer
10705                 //              The max height for our dropdown.  Set to 0 for no max height.
10706                 //              any dropdown taller than this will have scrollbars
10707                 maxHeight: 0,
10708
10709                 // dropDownPosition: [const] String[]
10710                 //              This variable controls the position of the drop down.
10711                 //              It's an array of strings with the following values:
10712                 //
10713                 //                      * before: places drop down to the left of the target node/widget, or to the right in
10714                 //                        the case of RTL scripts like Hebrew and Arabic
10715                 //                      * after: places drop down to the right of the target node/widget, or to the left in
10716                 //                        the case of RTL scripts like Hebrew and Arabic
10717                 //                      * above: drop down goes above target node
10718                 //                      * below: drop down goes below target node
10719                 //
10720                 //              The list is positions is tried, in order, until a position is found where the drop down fits
10721                 //              within the viewport.
10722                 //
10723                 dropDownPosition: ["below","above"],
10724
10725                 // _stopClickEvents: Boolean
10726                 //              When set to false, the click events will not be stopped, in
10727                 //              case you want to use them in your subwidget
10728                 _stopClickEvents: true,
10729
10730                 _onDropDownMouseDown: function(/*Event*/ e){
10731                         // summary:
10732                         //              Callback when the user mousedown's on the arrow icon
10733
10734                         if(this.disabled || this.readOnly){ return; }
10735
10736                         this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
10737
10738                         this.toggleDropDown();
10739                 },
10740
10741                 _onDropDownMouseUp: function(/*Event?*/ e){
10742                         // summary:
10743                         //              Callback when the user lifts their mouse after mouse down on the arrow icon.
10744                         //              If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
10745                         //              dropDown node.  If the event is missing, then we are not
10746                         //              a mouseup event.
10747                         //
10748                         //              This is useful for the common mouse movement pattern
10749                         //              with native browser <select> nodes:
10750                         //                      1. mouse down on the select node (probably on the arrow)
10751                         //                      2. move mouse to a menu item while holding down the mouse button
10752                         //                      3. mouse up.  this selects the menu item as though the user had clicked it.
10753                         if(e && this._docHandler){
10754                                 this.disconnect(this._docHandler);
10755                         }
10756                         var dropDown = this.dropDown, overMenu = false;
10757
10758                         if(e && this._opened){
10759                                 // This code deals with the corner-case when the drop down covers the original widget,
10760                                 // because it's so large.  In that case mouse-up shouldn't select a value from the menu.
10761                                 // Find out if our target is somewhere in our dropdown widget,
10762                                 // but not over our _buttonNode (the clickable node)
10763                                 var c = dojo.position(this._buttonNode, true);
10764                                 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
10765                                         !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
10766                                         var t = e.target;
10767                                         while(t && !overMenu){
10768                                                 if(dojo.hasClass(t, "dijitPopup")){
10769                                                         overMenu = true;
10770                                                 }else{
10771                                                         t = t.parentNode;
10772                                                 }
10773                                         }
10774                                         if(overMenu){
10775                                                 t = e.target;
10776                                                 if(dropDown.onItemClick){
10777                                                         var menuItem;
10778                                                         while(t && !(menuItem = dijit.byNode(t))){
10779                                                                 t = t.parentNode;
10780                                                         }
10781                                                         if(menuItem && menuItem.onClick && menuItem.getParent){
10782                                                                 menuItem.getParent().onItemClick(menuItem, e);
10783                                                         }
10784                                                 }
10785                                                 return;
10786                                         }
10787                                 }
10788                         }
10789                         if(this._opened && dropDown.focus){
10790                                 // Focus the dropdown widget - do it on a delay so that we
10791                                 // don't steal our own focus.
10792                                 window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
10793                         }
10794                 },
10795
10796                 _onDropDownClick: function(/*Event*/ e){
10797                         // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
10798                         if(this._stopClickEvents){
10799                                 dojo.stopEvent(e);
10800                         }                       
10801                 },
10802
10803                 _setupDropdown: function(){
10804                         // summary:
10805                         //              set up nodes and connect our mouse and keypress events
10806                         this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
10807                         this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
10808                         this._aroundNode = this._aroundNode || this.domNode;
10809                         this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
10810                         this.connect(this._buttonNode, "onclick", "_onDropDownClick");
10811                         this.connect(this._buttonNode, "onkeydown", "_onDropDownKeydown");
10812                         this.connect(this._buttonNode, "onkeyup", "_onKey");
10813
10814                         // If we have a _setStateClass function (which happens when
10815                         // we are a form widget), then we need to connect our open/close
10816                         // functions to it
10817                         if(this._setStateClass){
10818                                 this.connect(this, "openDropDown", "_setStateClass");
10819                                 this.connect(this, "closeDropDown", "_setStateClass");
10820                         }
10821
10822                         // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
10823                         // based on where drop down will normally appear
10824                         var defaultPos = {
10825                                         "after" : this.isLeftToRight() ? "Right" : "Left",
10826                                         "before" : this.isLeftToRight() ? "Left" : "Right",
10827                                         "above" : "Up",
10828                                         "below" : "Down",
10829                                         "left" : "Left",
10830                                         "right" : "Right"
10831                         }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
10832                         dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
10833                 },
10834
10835                 postCreate: function(){
10836                         this._setupDropdown();
10837                         this.inherited(arguments);
10838                 },
10839
10840                 destroyDescendants: function(){
10841                         if(this.dropDown){
10842                                 // Destroy the drop down, unless it's already been destroyed.  This can happen because
10843                                 // the drop down is a direct child of <body> even though it's logically my child.
10844                                 if(!this.dropDown._destroyed){
10845                                         this.dropDown.destroyRecursive();
10846                                 }
10847                                 delete this.dropDown;
10848                         }
10849                         this.inherited(arguments);
10850                 },
10851
10852                 _onDropDownKeydown: function(/*Event*/ e){
10853                         if(e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE){
10854                                 e.preventDefault();     // stop IE screen jump
10855                         }
10856                 },
10857
10858                 _onKey: function(/*Event*/ e){
10859                         // summary:
10860                         //              Callback when the user presses a key while focused on the button node
10861
10862                         if(this.disabled || this.readOnly){ return; }
10863                         var d = this.dropDown;
10864                         if(d && this._opened && d.handleKey){
10865                                 if(d.handleKey(e) === false){ return; }
10866                         }
10867                         if(d && this._opened && e.keyCode == dojo.keys.ESCAPE){
10868                                 this.toggleDropDown();
10869                         }else if(d && !this._opened && 
10870                                         (e.keyCode == dojo.keys.DOWN_ARROW || e.keyCode == dojo.keys.ENTER || e.keyCode == dojo.keys.SPACE)){
10871                                 this.toggleDropDown();
10872                                 if(d.focus){
10873                                         setTimeout(dojo.hitch(d, "focus"), 1);
10874                                 }
10875                         }
10876                 },
10877
10878                 _onBlur: function(){
10879                         // summary:
10880                         //              Called magically when focus has shifted away from this widget and it's dropdown
10881
10882                         this.closeDropDown();
10883                         // don't focus on button.  the user has explicitly focused on something else.
10884                         this.inherited(arguments);
10885                 },
10886
10887                 isLoaded: function(){
10888                         // summary:
10889                         //              Returns whether or not the dropdown is loaded.  This can
10890                         //              be overridden in order to force a call to loadDropDown().
10891                         // tags:
10892                         //              protected
10893
10894                         return true;
10895                 },
10896
10897                 loadDropDown: function(/* Function */ loadCallback){
10898                         // summary:
10899                         //              Loads the data for the dropdown, and at some point, calls
10900                         //              the given callback
10901                         // tags:
10902                         //              protected
10903
10904                         loadCallback();
10905                 },
10906
10907                 toggleDropDown: function(){
10908                         // summary:
10909                         //              Toggle the drop-down widget; if it is up, close it, if not, open it
10910                         // tags:
10911                         //              protected
10912
10913                         if(this.disabled || this.readOnly){ return; }
10914                         this.focus();
10915                         var dropDown = this.dropDown;
10916                         if(!dropDown){ return; }
10917                         if(!this._opened){
10918                                 // If we aren't loaded, load it first so there isn't a flicker
10919                                 if(!this.isLoaded()){
10920                                         this.loadDropDown(dojo.hitch(this, "openDropDown"));
10921                                         return;
10922                                 }else{
10923                                         this.openDropDown();
10924                                 }
10925                         }else{
10926                                 this.closeDropDown();
10927                         }
10928                 },
10929
10930                 openDropDown: function(){
10931                         // summary:
10932                         //              Opens the dropdown for this widget - it returns the
10933                         //              return value of dijit.popup.open
10934                         // tags:
10935                         //              protected
10936
10937                         var dropDown = this.dropDown;
10938                         var ddNode = dropDown.domNode;
10939                         var self = this;
10940
10941                         // Prepare our popup's height and honor maxHeight if it exists.
10942
10943                         // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
10944                         // ie, dependent on how much space is available (BK)
10945
10946                         if(!this._preparedNode){
10947                                 dijit.popup.moveOffScreen(ddNode);
10948                                 this._preparedNode = true;                      
10949                                 // Check if we have explicitly set width and height on the dropdown widget dom node
10950                                 if(ddNode.style.width){
10951                                         this._explicitDDWidth = true;
10952                                 }
10953                                 if(ddNode.style.height){
10954                                         this._explicitDDHeight = true;
10955                                 }
10956                         }
10957
10958                         // Code for resizing dropdown (height limitation, or increasing width to match my width)
10959                         if(this.maxHeight || this.forceWidth || this.autoWidth){
10960                                 var myStyle = {
10961                                         display: "",
10962                                         visibility: "hidden"
10963                                 };
10964                                 if(!this._explicitDDWidth){
10965                                         myStyle.width = "";
10966                                 }
10967                                 if(!this._explicitDDHeight){
10968                                         myStyle.height = "";
10969                                 }
10970                                 dojo.style(ddNode, myStyle);
10971                                 
10972                                 // Get size of drop down, and determine if vertical scroll bar needed
10973                                 var mb = dojo.marginBox(ddNode);
10974                                 var overHeight = (this.maxHeight && mb.h > this.maxHeight);
10975                                 dojo.style(ddNode, {
10976                                         overflowX: "hidden",
10977                                         overflowY: overHeight ? "auto" : "hidden"
10978                                 });
10979                                 if(overHeight){
10980                                         mb.h = this.maxHeight;
10981                                         if("w" in mb){
10982                                                 mb.w += 16;     // room for vertical scrollbar
10983                                         }
10984                                 }else{
10985                                         delete mb.h;
10986                                 }
10987                                 delete mb.t;
10988                                 delete mb.l;
10989
10990                                 // Adjust dropdown width to match or be larger than my width
10991                                 if(this.forceWidth){
10992                                         mb.w = this.domNode.offsetWidth;
10993                                 }else if(this.autoWidth){
10994                                         mb.w = Math.max(mb.w, this.domNode.offsetWidth);
10995                                 }else{
10996                                         delete mb.w;
10997                                 }
10998                                 
10999                                 // And finally, resize the dropdown to calculated height and width
11000                                 if(dojo.isFunction(dropDown.resize)){
11001                                         dropDown.resize(mb);
11002                                 }else{
11003                                         dojo.marginBox(ddNode, mb);
11004                                 }
11005                         }
11006
11007                         var retVal = dijit.popup.open({
11008                                 parent: this,
11009                                 popup: dropDown,
11010                                 around: this._aroundNode,
11011                                 orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
11012                                 onExecute: function(){
11013                                         self.closeDropDown(true);
11014                                 },
11015                                 onCancel: function(){
11016                                         self.closeDropDown(true);
11017                                 },
11018                                 onClose: function(){
11019                                         dojo.attr(self._popupStateNode, "popupActive", false);
11020                                         dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
11021                                         self._opened = false;
11022                                         self.state = "";
11023                                 }
11024                         });
11025                         dojo.attr(this._popupStateNode, "popupActive", "true");
11026                         dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
11027                         this._opened=true;
11028                         this.state="Opened";
11029                         // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
11030                         return retVal;
11031                 },
11032
11033                 closeDropDown: function(/*Boolean*/ focus){
11034                         // summary:
11035                         //              Closes the drop down on this widget
11036                         // tags:
11037                         //              protected
11038
11039                         if(this._opened){
11040                                 if(focus){ this.focus(); }
11041                                 dijit.popup.close(this.dropDown);
11042                                 this._opened = false;
11043                                 this.state = "";
11044                         }
11045                 }
11046
11047         }
11048 );
11049
11050 }
11051
11052 if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11053 dojo._hasResource["dijit.form.Button"] = true;
11054 dojo.provide("dijit.form.Button");
11055
11056
11057
11058
11059
11060 dojo.declare("dijit.form.Button",
11061         dijit.form._FormWidget,
11062         {
11063         // summary:
11064         //              Basically the same thing as a normal HTML button, but with special styling.
11065         // description:
11066         //              Buttons can display a label, an icon, or both.
11067         //              A label should always be specified (through innerHTML) or the label
11068         //              attribute.  It can be hidden via showLabel=false.
11069         // example:
11070         // |    <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
11071         //
11072         // example:
11073         // |    var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
11074         // |    dojo.body().appendChild(button1.domNode);
11075
11076         // label: HTML String
11077         //              Text to display in button.
11078         //              If the label is hidden (showLabel=false) then and no title has
11079         //              been specified, then label is also set as title attribute of icon.
11080         label: "",
11081
11082         // showLabel: Boolean
11083         //              Set this to true to hide the label text and display only the icon.
11084         //              (If showLabel=false then iconClass must be specified.)
11085         //              Especially useful for toolbars.
11086         //              If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
11087         //
11088         //              The exception case is for computers in high-contrast mode, where the label
11089         //              will still be displayed, since the icon doesn't appear.
11090         showLabel: true,
11091
11092         // iconClass: String
11093         //              Class to apply to DOMNode in button to make it display an icon
11094         iconClass: "",
11095
11096         // type: String
11097         //              Defines the type of button.  "button", "submit", or "reset".
11098         type: "button",
11099
11100         baseClass: "dijitButton",
11101
11102         templateString: dojo.cache("dijit.form", "templates/Button.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode,focusNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdojoAttachPoint=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
11103
11104         attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
11105                 value: "valueNode",
11106                 iconClass: { node: "iconNode", type: "class" }
11107         }),
11108
11109
11110         _onClick: function(/*Event*/ e){
11111                 // summary:
11112                 //              Internal function to handle click actions
11113                 if(this.disabled){
11114                         return false;
11115                 }
11116                 this._clicked(); // widget click actions
11117                 return this.onClick(e); // user click actions
11118         },
11119
11120         _onButtonClick: function(/*Event*/ e){
11121                 // summary:
11122                 //              Handler when the user activates the button portion.
11123                 if(this._onClick(e) === false){ // returning nothing is same as true
11124                         e.preventDefault(); // needed for checkbox
11125                 }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
11126                         for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
11127                                 var widget=dijit.byNode(node);
11128                                 if(widget && typeof widget._onSubmit == "function"){
11129                                         widget._onSubmit(e);
11130                                         break;
11131                                 }
11132                         }
11133                 }else if(this.valueNode){
11134                         this.valueNode.click();
11135                         e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
11136                 }
11137         },
11138
11139         _fillContent: function(/*DomNode*/ source){
11140                 // Overrides _Templated._fillContent().
11141                 // If button label is specified as srcNodeRef.innerHTML rather than
11142                 // this.params.label, handle it here.
11143                 if(source && (!this.params || !("label" in this.params))){
11144                         this.set('label', source.innerHTML);
11145                 }
11146         },
11147
11148         postCreate: function(){
11149                 dojo.setSelectable(this.focusNode, false);
11150                 this.inherited(arguments);
11151         },
11152
11153         _setShowLabelAttr: function(val){
11154                 if(this.containerNode){
11155                         dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
11156                 }
11157                 this.showLabel = val;
11158         },
11159
11160         onClick: function(/*Event*/ e){
11161                 // summary:
11162                 //              Callback for when button is clicked.
11163                 //              If type="submit", return true to perform submit, or false to cancel it.
11164                 // type:
11165                 //              callback
11166                 return true;            // Boolean
11167         },
11168
11169         _clicked: function(/*Event*/ e){
11170                 // summary:
11171                 //              Internal overridable function for when the button is clicked
11172         },
11173
11174         setLabel: function(/*String*/ content){
11175                 // summary:
11176                 //              Deprecated.  Use set('label', ...) instead.
11177                 dojo.deprecated("dijit.form.Button.setLabel() is deprecated.  Use set('label', ...) instead.", "", "2.0");
11178                 this.set("label", content);
11179         },
11180
11181         _setLabelAttr: function(/*String*/ content){
11182                 // summary:
11183                 //              Hook for attr('label', ...) to work.
11184                 // description:
11185                 //              Set the label (text) of the button; takes an HTML string.
11186                 this.containerNode.innerHTML = this.label = content;
11187                 if(this.showLabel == false && !this.params.title){
11188                         this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
11189                 }
11190         }
11191 });
11192
11193
11194 dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
11195         // summary:
11196         //              A button with a drop down
11197         //
11198         // example:
11199         // |    <button dojoType="dijit.form.DropDownButton" label="Hello world">
11200         // |            <div dojotype="dijit.Menu">...</div>
11201         // |    </button>
11202         //
11203         // example:
11204         // |    var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
11205         // |    dojo.body().appendChild(button1);
11206         //
11207
11208         baseClass : "dijitDropDownButton",
11209
11210         templateString: dojo.cache("dijit.form", "templates/DropDownButton.html", "<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdojoAttachEvent=\"ondijitclick:_onButtonClick\" dojoAttachPoint=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdojoAttachPoint=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdojoAttachPoint=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
11211
11212         _fillContent: function(){
11213                 // Overrides Button._fillContent().
11214                 //
11215                 // My inner HTML contains both the button contents and a drop down widget, like
11216                 // <DropDownButton>  <span>push me</span>  <Menu> ... </Menu> </DropDownButton>
11217                 // The first node is assumed to be the button content. The widget is the popup.
11218
11219                 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
11220                         //FIXME: figure out how to filter out the widget and use all remaining nodes as button
11221                         //      content, not just nodes[0]
11222                         var nodes = dojo.query("*", this.srcNodeRef);
11223                         dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
11224
11225                         // save pointer to srcNode so we can grab the drop down widget after it's instantiated
11226                         this.dropDownContainer = this.srcNodeRef;
11227                 }
11228         },
11229
11230         startup: function(){
11231                 if(this._started){ return; }
11232
11233                 // the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
11234                 // make it invisible, and store a reference to pass to the popup code.
11235                 if(!this.dropDown){
11236                         var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
11237                         this.dropDown = dijit.byNode(dropDownNode);
11238                         delete this.dropDownContainer;
11239                 }
11240                 dijit.popup.moveOffScreen(this.dropDown.domNode);
11241
11242                 this.inherited(arguments);
11243         },
11244
11245         isLoaded: function(){
11246                 // Returns whether or not we are loaded - if our dropdown has an href,
11247                 // then we want to check that.
11248                 var dropDown = this.dropDown;
11249                 return (!dropDown.href || dropDown.isLoaded);
11250         },
11251
11252         loadDropDown: function(){
11253                 // Loads our dropdown
11254                 var dropDown = this.dropDown;
11255                 if(!dropDown){ return; }
11256                 if(!this.isLoaded()){
11257                         var handler = dojo.connect(dropDown, "onLoad", this, function(){
11258                                 dojo.disconnect(handler);
11259                                 this.openDropDown();
11260                         });
11261                         dropDown.refresh();
11262                 }else{
11263                         this.openDropDown();
11264                 }
11265         },
11266
11267         isFocusable: function(){
11268                 // Overridden so that focus is handled by the _HasDropDown mixin, not by
11269                 // the _FormWidget mixin.
11270                 return this.inherited(arguments) && !this._mouseDown;
11271         }
11272 });
11273
11274 dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
11275         // summary:
11276         //              A combination button and drop-down button.
11277         //              Users can click one side to "press" the button, or click an arrow
11278         //              icon to display the drop down.
11279         //
11280         // example:
11281         // |    <button dojoType="dijit.form.ComboButton" onClick="...">
11282         // |            <span>Hello world</span>
11283         // |            <div dojoType="dijit.Menu">...</div>
11284         // |    </button>
11285         //
11286         // example:
11287         // |    var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
11288         // |    dojo.body().appendChild(button1.domNode);
11289         //
11290
11291         templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "<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 class=\"dijitReset dijitStretch dijitButtonNode\" dojoAttachPoint=\"buttonNode\" dojoAttachEvent=\"ondijitclick:_onButtonClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode\"\n\t\t\twaiRole=\"button\" waiState=\"labelledby-${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" waiRole=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdojoAttachPoint=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\twaiRole=\"button\" waiState=\"haspopup-true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" waiRole=\"presentation\">&#9660;</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" dojoAttachPoint=\"valueNode\"\n\t\t/></td></tr></tbody\n></table>\n"),
11292
11293         attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
11294                 id: "",
11295                 tabIndex: ["focusNode", "titleNode"],
11296                 title: "titleNode"
11297         }),
11298
11299         // optionsTitle: String
11300         //              Text that describes the options menu (accessibility)
11301         optionsTitle: "",
11302
11303         baseClass: "dijitComboButton",
11304
11305         // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
11306         // mouse action over specified node
11307         cssStateNodes: {
11308                 "buttonNode": "dijitButtonNode",
11309                 "titleNode": "dijitButtonContents",
11310                 "_popupStateNode": "dijitDownArrowButton"
11311         },
11312
11313         _focusedNode: null,
11314
11315         _onButtonKeyPress: function(/*Event*/ evt){
11316                 // summary:
11317                 //              Handler for right arrow key when focus is on left part of button
11318                 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
11319                         dijit.focus(this._popupStateNode);
11320                         dojo.stopEvent(evt);
11321                 }
11322         },
11323
11324         _onArrowKeyPress: function(/*Event*/ evt){
11325                 // summary:
11326                 //              Handler for left arrow key when focus is on right part of button
11327                 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
11328                         dijit.focus(this.titleNode);
11329                         dojo.stopEvent(evt);
11330                 }
11331         },
11332         
11333         focus: function(/*String*/ position){
11334                 // summary:
11335                 //              Focuses this widget to according to position, if specified,
11336                 //              otherwise on arrow node
11337                 // position:
11338                 //              "start" or "end"
11339                 
11340                 dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
11341         }
11342 });
11343
11344 dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
11345         // summary:
11346         //              A button that can be in two states (checked or not).
11347         //              Can be base class for things like tabs or checkbox or radio buttons
11348
11349         baseClass: "dijitToggleButton",
11350
11351         // checked: Boolean
11352         //              Corresponds to the native HTML <input> element's attribute.
11353         //              In markup, specified as "checked='checked'" or just "checked".
11354         //              True if the button is depressed, or the checkbox is checked,
11355         //              or the radio button is selected, etc.
11356         checked: false,
11357
11358         attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
11359                 checked:"focusNode"
11360         }),
11361
11362         _clicked: function(/*Event*/ evt){
11363                 this.set('checked', !this.checked);
11364         },
11365
11366         _setCheckedAttr: function(/*Boolean*/ value, /* Boolean? */ priorityChange){
11367                 this.checked = value;
11368                 dojo.attr(this.focusNode || this.domNode, "checked", value);
11369                 dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
11370                 this._handleOnChange(value, priorityChange);
11371         },
11372
11373         setChecked: function(/*Boolean*/ checked){
11374                 // summary:
11375                 //              Deprecated.   Use set('checked', true/false) instead.
11376                 dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
11377                 this.set('checked', checked);
11378         },
11379
11380         reset: function(){
11381                 // summary:
11382                 //              Reset the widget's value to what it was at initialization time
11383
11384                 this._hasBeenBlurred = false;
11385
11386                 // set checked state to original setting
11387                 this.set('checked', this.params.checked || false);
11388         }
11389 });
11390
11391 }
11392
11393 if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11394 dojo._hasResource["dijit.form.ToggleButton"] = true;
11395 dojo.provide("dijit.form.ToggleButton");
11396
11397
11398 }
11399
11400 if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11401 dojo._hasResource["dijit.form.CheckBox"] = true;
11402 dojo.provide("dijit.form.CheckBox");
11403
11404
11405
11406 dojo.declare(
11407         "dijit.form.CheckBox",
11408         dijit.form.ToggleButton,
11409         {
11410                 // summary:
11411                 //              Same as an HTML checkbox, but with fancy styling.
11412                 //
11413                 // description:
11414                 //              User interacts with real html inputs.
11415                 //              On onclick (which occurs by mouse click, space-bar, or
11416                 //              using the arrow keys to switch the selected radio button),
11417                 //              we update the state of the checkbox/radio.
11418                 //
11419                 //              There are two modes:
11420                 //                      1. High contrast mode
11421                 //                      2. Normal mode
11422                 //
11423                 //              In case 1, the regular html inputs are shown and used by the user.
11424                 //              In case 2, the regular html inputs are invisible but still used by
11425                 //              the user. They are turned quasi-invisible and overlay the background-image.
11426
11427                 templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "<div class=\"dijit dijitReset dijitInline\" waiRole=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdojoAttachPoint=\"focusNode\"\n\t \tdojoAttachEvent=\"onclick:_onClick\"\n/></div>\n"),
11428
11429                 baseClass: "dijitCheckBox",
11430
11431                 // type: [private] String
11432                 //              type attribute on <input> node.
11433                 //              Overrides `dijit.form.Button.type`.   Users should not change this value.
11434                 type: "checkbox",
11435
11436                 // value: String
11437                 //              As an initialization parameter, equivalent to value field on normal checkbox
11438                 //              (if checked, the value is passed as the value when form is submitted).
11439                 //
11440                 //              However, attr('value') will return either the string or false depending on
11441                 //              whether or not the checkbox is checked.
11442                 //
11443                 //              attr('value', string) will check the checkbox and change the value to the
11444                 //              specified string
11445                 //
11446                 //              attr('value', boolean) will change the checked state.
11447                 value: "on",
11448
11449                 // readOnly: Boolean
11450                 //              Should this widget respond to user input?
11451                 //              In markup, this is specified as "readOnly".
11452                 //              Similar to disabled except readOnly form values are submitted.
11453                 readOnly: false,
11454                 
11455                 // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap 
11456                 // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
11457                 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
11458                         readOnly: "focusNode"
11459                 }),
11460
11461                 _setReadOnlyAttr: function(/*Boolean*/ value){
11462                         this.readOnly = value;
11463                         dojo.attr(this.focusNode, 'readOnly', value);
11464                         dijit.setWaiState(this.focusNode, "readonly", value);
11465                 },
11466
11467                 _setValueAttr: function(/*String or Boolean*/ newValue, /*Boolean*/ priorityChange){
11468                         // summary:
11469                         //              Handler for value= attribute to constructor, and also calls to
11470                         //              attr('value', val).
11471                         // description:
11472                         //              During initialization, just saves as attribute to the <input type=checkbox>.
11473                         //
11474                         //              After initialization,
11475                         //              when passed a boolean, controls whether or not the CheckBox is checked.
11476                         //              If passed a string, changes the value attribute of the CheckBox (the one
11477                         //              specified as "value" when the CheckBox was constructed (ex: <input
11478                         //              dojoType="dijit.CheckBox" value="chicken">)
11479                         if(typeof newValue == "string"){
11480                                 this.value = newValue;
11481                                 dojo.attr(this.focusNode, 'value', newValue);
11482                                 newValue = true;
11483                         }
11484                         if(this._created){
11485                                 this.set('checked', newValue, priorityChange);
11486                         }
11487                 },
11488                 _getValueAttr: function(){
11489                         // summary:
11490                         //              Hook so attr('value') works.
11491                         // description:
11492                         //              If the CheckBox is checked, returns the value attribute.
11493                         //              Otherwise returns false.
11494                         return (this.checked ? this.value : false);
11495                 },
11496
11497                 // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
11498                 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
11499                 _setLabelAttr: undefined,
11500
11501                 postMixInProperties: function(){
11502                         if(this.value == ""){
11503                                 this.value = "on";
11504                         }
11505
11506                         // Need to set initial checked state as part of template, so that form submit works.
11507                         // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
11508                         // to <body>, see #8666
11509                         this.checkedAttrSetting = this.checked ? "checked" : "";
11510
11511                         this.inherited(arguments);
11512                 },
11513
11514                  _fillContent: function(/*DomNode*/ source){
11515                         // Override Button::_fillContent() since it doesn't make sense for CheckBox,
11516                         // since CheckBox doesn't even have a container
11517                 },
11518
11519                 reset: function(){
11520                         // Override ToggleButton.reset()
11521
11522                         this._hasBeenBlurred = false;
11523
11524                         this.set('checked', this.params.checked || false);
11525
11526                         // Handle unlikely event that the <input type=checkbox> value attribute has changed
11527                         this.value = this.params.value || "on";
11528                         dojo.attr(this.focusNode, 'value', this.value);
11529                 },
11530
11531                 _onFocus: function(){
11532                         if(this.id){
11533                                 dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
11534                         }
11535                         this.inherited(arguments);
11536                 },
11537
11538                 _onBlur: function(){
11539                         if(this.id){
11540                                 dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
11541                         }
11542                         this.inherited(arguments);
11543                 },
11544
11545                 _onClick: function(/*Event*/ e){
11546                         // summary:
11547                         //              Internal function to handle click actions - need to check
11548                         //              readOnly, since button no longer does that check.
11549                         if(this.readOnly){
11550                                 return false;
11551                         }
11552                         return this.inherited(arguments);
11553                 }
11554         }
11555 );
11556
11557 dojo.declare(
11558         "dijit.form.RadioButton",
11559         dijit.form.CheckBox,
11560         {
11561                 // summary:
11562                 //              Same as an HTML radio, but with fancy styling.
11563
11564                 type: "radio",
11565                 baseClass: "dijitRadio",
11566
11567                 _setCheckedAttr: function(/*Boolean*/ value){
11568                         // If I am being checked then have to deselect currently checked radio button
11569                         this.inherited(arguments);
11570                         if(!this._created){ return; }
11571                         if(value){
11572                                 var _this = this;
11573                                 // search for radio buttons with the same name that need to be unchecked
11574                                 dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
11575                                         function(inputNode){
11576                                                 if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
11577                                                         var widget = dijit.getEnclosingWidget(inputNode);
11578                                                         if(widget && widget.checked){
11579                                                                 widget.set('checked', false);
11580                                                         }
11581                                                 }
11582                                         }
11583                                 );
11584                         }
11585                 },
11586
11587                 _clicked: function(/*Event*/ e){
11588                         if(!this.checked){
11589                                 this.set('checked', true);
11590                         }
11591                 }
11592         }
11593 );
11594
11595 }
11596
11597 if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11598 dojo._hasResource["dijit.form.DropDownButton"] = true;
11599 dojo.provide("dijit.form.DropDownButton");
11600
11601
11602
11603 }
11604
11605 if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11606 dojo._hasResource["dojo.regexp"] = true;
11607 dojo.provide("dojo.regexp");
11608
11609 /*=====
11610 dojo.regexp = {
11611         // summary: Regular expressions and Builder resources
11612 };
11613 =====*/
11614
11615 dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
11616         //      summary:
11617         //              Adds escape sequences for special characters in regular expressions
11618         // except:
11619         //              a String with special characters to be left unescaped
11620
11621         return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
11622                 if(except && except.indexOf(ch) != -1){
11623                         return ch;
11624                 }
11625                 return "\\" + ch;
11626         }); // String
11627 }
11628
11629 dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
11630         //      summary:
11631         //              Builds a regular expression that groups subexpressions
11632         //      description:
11633         //              A utility function used by some of the RE generators. The
11634         //              subexpressions are constructed by the function, re, in the second
11635         //              parameter.  re builds one subexpression for each elem in the array
11636         //              a, in the first parameter. Returns a string for a regular
11637         //              expression that groups all the subexpressions.
11638         // arr:
11639         //              A single value or an array of values.
11640         // re:
11641         //              A function. Takes one parameter and converts it to a regular
11642         //              expression. 
11643         // nonCapture:
11644         //              If true, uses non-capturing match, otherwise matches are retained
11645         //              by regular expression. Defaults to false
11646
11647         // case 1: a is a single value.
11648         if(!(arr instanceof Array)){
11649                 return re(arr); // String
11650         }
11651
11652         // case 2: a is an array
11653         var b = [];
11654         for(var i = 0; i < arr.length; i++){
11655                 // convert each elem to a RE
11656                 b.push(re(arr[i]));
11657         }
11658
11659          // join the REs as alternatives in a RE group.
11660         return dojo.regexp.group(b.join("|"), nonCapture); // String
11661 }
11662
11663 dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
11664         // summary:
11665         //              adds group match to expression
11666         // nonCapture:
11667         //              If true, uses non-capturing match, otherwise matches are retained
11668         //              by regular expression. 
11669         return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
11670 }
11671
11672 }
11673
11674 if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11675 dojo._hasResource["dojo.data.util.sorter"] = true;
11676 dojo.provide("dojo.data.util.sorter");
11677
11678 dojo.data.util.sorter.basicComparator = function(       /*anything*/ a, 
11679                                                                                                         /*anything*/ b){
11680         //      summary:  
11681         //              Basic comparision function that compares if an item is greater or less than another item
11682         //      description:  
11683         //              returns 1 if a > b, -1 if a < b, 0 if equal.
11684         //              'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
11685         //              And compared to each other, null is equivalent to undefined.
11686         
11687         //null is a problematic compare, so if null, we set to undefined.
11688         //Makes the check logic simple, compact, and consistent
11689         //And (null == undefined) === true, so the check later against null
11690         //works for undefined and is less bytes.
11691         var r = -1;
11692         if(a === null){
11693                 a = undefined;
11694         }
11695         if(b === null){
11696                 b = undefined;
11697         }
11698         if(a == b){
11699                 r = 0; 
11700         }else if(a > b || a == null){
11701                 r = 1; 
11702         }
11703         return r; //int {-1,0,1}
11704 };
11705
11706 dojo.data.util.sorter.createSortFunction = function(    /* attributes array */sortSpec,
11707                                                                                                                 /*dojo.data.core.Read*/ store){
11708         //      summary:  
11709         //              Helper function to generate the sorting function based off the list of sort attributes.
11710         //      description:  
11711         //              The sort function creation will look for a property on the store called 'comparatorMap'.  If it exists
11712         //              it will look in the mapping for comparisons function for the attributes.  If one is found, it will
11713         //              use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
11714         //              Returns the sorting function for this particular list of attributes and sorting directions.
11715         //
11716         //      sortSpec: array
11717         //              A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
11718         //              The objects should be formatted as follows:
11719         //              {
11720         //                      attribute: "attributeName-string" || attribute,
11721         //                      descending: true|false;   // Default is false.
11722         //              }
11723         //      store: object
11724         //              The datastore object to look up item values from.
11725         //
11726         var sortFunctions=[];
11727
11728         function createSortFunction(attr, dir, comp, s){
11729                 //Passing in comp and s (comparator and store), makes this
11730                 //function much faster.
11731                 return function(itemA, itemB){
11732                         var a = s.getValue(itemA, attr);
11733                         var b = s.getValue(itemB, attr);
11734                         return dir * comp(a,b); //int
11735                 };
11736         }
11737         var sortAttribute;
11738         var map = store.comparatorMap;
11739         var bc = dojo.data.util.sorter.basicComparator;
11740         for(var i = 0; i < sortSpec.length; i++){
11741                 sortAttribute = sortSpec[i];
11742                 var attr = sortAttribute.attribute;
11743                 if(attr){
11744                         var dir = (sortAttribute.descending) ? -1 : 1;
11745                         var comp = bc;
11746                         if(map){
11747                                 if(typeof attr !== "string" && ("toString" in attr)){
11748                                          attr = attr.toString();
11749                                 }
11750                                 comp = map[attr] || bc;
11751                         }
11752                         sortFunctions.push(createSortFunction(attr, 
11753                                 dir, comp, store));
11754                 }
11755         }
11756         return function(rowA, rowB){
11757                 var i=0;
11758                 while(i < sortFunctions.length){
11759                         var ret = sortFunctions[i++](rowA, rowB);
11760                         if(ret !== 0){
11761                                 return ret;//int
11762                         }
11763                 }
11764                 return 0; //int  
11765         }; // Function
11766 };
11767
11768 }
11769
11770 if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11771 dojo._hasResource["dojo.data.util.simpleFetch"] = true;
11772 dojo.provide("dojo.data.util.simpleFetch");
11773
11774
11775 dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
11776         //      summary:
11777         //              The simpleFetch mixin is designed to serve as a set of function(s) that can
11778         //              be mixed into other datastore implementations to accelerate their development.  
11779         //              The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems() 
11780         //              call by returning an array of all the found items that matched the query.  The simpleFetch mixin
11781         //              is not designed to work for datastores that respond to a fetch() call by incrementally
11782         //              loading items, or sequentially loading partial batches of the result
11783         //              set.  For datastores that mixin simpleFetch, simpleFetch 
11784         //              implements a fetch method that automatically handles eight of the fetch()
11785         //              arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
11786         //              The class mixing in simpleFetch should not implement fetch(),
11787         //              but should instead implement a _fetchItems() method.  The _fetchItems() 
11788         //              method takes three arguments, the keywordArgs object that was passed 
11789         //              to fetch(), a callback function to be called when the result array is
11790         //              available, and an error callback to be called if something goes wrong.
11791         //              The _fetchItems() method should ignore any keywordArgs parameters for
11792         //              start, count, onBegin, onItem, onComplete, onError, sort, and scope.  
11793         //              The _fetchItems() method needs to correctly handle any other keywordArgs
11794         //              parameters, including the query parameter and any optional parameters 
11795         //              (such as includeChildren).  The _fetchItems() method should create an array of 
11796         //              result items and pass it to the fetchHandler along with the original request object 
11797         //              -- or, the _fetchItems() method may, if it wants to, create an new request object 
11798         //              with other specifics about the request that are specific to the datastore and pass 
11799         //              that as the request object to the handler.
11800         //
11801         //              For more information on this specific function, see dojo.data.api.Read.fetch()
11802         request = request || {};
11803         if(!request.store){
11804                 request.store = this;
11805         }
11806         var self = this;
11807
11808         var _errorHandler = function(errorData, requestObject){
11809                 if(requestObject.onError){
11810                         var scope = requestObject.scope || dojo.global;
11811                         requestObject.onError.call(scope, errorData, requestObject);
11812                 }
11813         };
11814
11815         var _fetchHandler = function(items, requestObject){
11816                 var oldAbortFunction = requestObject.abort || null;
11817                 var aborted = false;
11818
11819                 var startIndex = requestObject.start?requestObject.start:0;
11820                 var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
11821
11822                 requestObject.abort = function(){
11823                         aborted = true;
11824                         if(oldAbortFunction){
11825                                 oldAbortFunction.call(requestObject);
11826                         }
11827                 };
11828
11829                 var scope = requestObject.scope || dojo.global;
11830                 if(!requestObject.store){
11831                         requestObject.store = self;
11832                 }
11833                 if(requestObject.onBegin){
11834                         requestObject.onBegin.call(scope, items.length, requestObject);
11835                 }
11836                 if(requestObject.sort){
11837                         items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
11838                 }
11839                 if(requestObject.onItem){
11840                         for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
11841                                 var item = items[i];
11842                                 if(!aborted){
11843                                         requestObject.onItem.call(scope, item, requestObject);
11844                                 }
11845                         }
11846                 }
11847                 if(requestObject.onComplete && !aborted){
11848                         var subset = null;
11849                         if(!requestObject.onItem){
11850                                 subset = items.slice(startIndex, endIndex);
11851                         }
11852                         requestObject.onComplete.call(scope, subset, requestObject);
11853                 }
11854         };
11855         this._fetchItems(request, _fetchHandler, _errorHandler);
11856         return request; // Object
11857 };
11858
11859 }
11860
11861 if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11862 dojo._hasResource["dojo.data.util.filter"] = true;
11863 dojo.provide("dojo.data.util.filter");
11864
11865 dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
11866         //      summary:  
11867         //              Helper function to convert a simple pattern to a regular expression for matching.
11868         //      description:
11869         //              Returns a regular expression object that conforms to the defined conversion rules.
11870         //              For example:  
11871         //                      ca*   -> /^ca.*$/
11872         //                      *ca*  -> /^.*ca.*$/
11873         //                      *c\*a*  -> /^.*c\*a.*$/
11874         //                      *c\*a?*  -> /^.*c\*a..*$/
11875         //                      and so on.
11876         //
11877         //      pattern: string
11878         //              A simple matching pattern to convert that follows basic rules:
11879         //                      * Means match anything, so ca* means match anything starting with ca
11880         //                      ? Means match single character.  So, b?b will match to bob and bab, and so on.
11881         //              \ is an escape character.  So for example, \* means do not treat * as a match, but literal character *.
11882         //                              To use a \ as a character in the string, it must be escaped.  So in the pattern it should be 
11883         //                              represented by \\ to be treated as an ordinary \ character instead of an escape.
11884         //
11885         //      ignoreCase:
11886         //              An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
11887         //              By default, it is assumed case sensitive.
11888
11889         var rxp = "^";
11890         var c = null;
11891         for(var i = 0; i < pattern.length; i++){
11892                 c = pattern.charAt(i);
11893                 switch(c){
11894                         case '\\':
11895                                 rxp += c;
11896                                 i++;
11897                                 rxp += pattern.charAt(i);
11898                                 break;
11899                         case '*':
11900                                 rxp += ".*"; break;
11901                         case '?':
11902                                 rxp += "."; break;
11903                         case '$':
11904                         case '^':
11905                         case '/':
11906                         case '+':
11907                         case '.':
11908                         case '|':
11909                         case '(':
11910                         case ')':
11911                         case '{':
11912                         case '}':
11913                         case '[':
11914                         case ']':
11915                                 rxp += "\\"; //fallthrough
11916                         default:
11917                                 rxp += c;
11918                 }
11919         }
11920         rxp += "$";
11921         if(ignoreCase){
11922                 return new RegExp(rxp,"mi"); //RegExp
11923         }else{
11924                 return new RegExp(rxp,"m"); //RegExp
11925         }
11926         
11927 };
11928
11929 }
11930
11931 if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11932 dojo._hasResource["dijit.form.TextBox"] = true;
11933 dojo.provide("dijit.form.TextBox");
11934
11935
11936
11937 dojo.declare(
11938         "dijit.form.TextBox",
11939         dijit.form._FormValueWidget,
11940         {
11941                 // summary:
11942                 //              A base class for textbox form inputs
11943
11944                 // trim: Boolean
11945                 //              Removes leading and trailing whitespace if true.  Default is false.
11946                 trim: false,
11947
11948                 // uppercase: Boolean
11949                 //              Converts all characters to uppercase if true.  Default is false.
11950                 uppercase: false,
11951
11952                 // lowercase: Boolean
11953                 //              Converts all characters to lowercase if true.  Default is false.
11954                 lowercase: false,
11955
11956                 // propercase: Boolean
11957                 //              Converts the first character of each word to uppercase if true.
11958                 propercase: false,
11959
11960                 //      maxLength: String
11961                 //              HTML INPUT tag maxLength declaration.
11962                 maxLength: "",
11963
11964                 //      selectOnClick: [const] Boolean
11965                 //              If true, all text will be selected when focused with mouse
11966                 selectOnClick: false,
11967
11968                 //      placeHolder: String
11969                 //              Defines a hint to help users fill out the input field (as defined in HTML 5).
11970                 //              This should only contain plain text (no html markup).
11971                 placeHolder: "",
11972                 
11973                 templateString: dojo.cache("dijit.form", "templates/TextBox.html", "<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" waiRole=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
11974                 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
11975
11976                 _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
11977
11978                 baseClass: "dijitTextBox",
11979
11980                 attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
11981                         maxLength: "focusNode"
11982                 }),
11983                 
11984                 postMixInProperties: function(){
11985                         var type = this.type.toLowerCase();
11986                         if(this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
11987                                 this.templateString = this._singleNodeTemplate;
11988                         }
11989                         this.inherited(arguments);
11990                 },
11991
11992                 _setPlaceHolderAttr: function(v){
11993                         this.placeHolder = v;
11994                         if(!this._phspan){
11995                                 this._attachPoints.push('_phspan');
11996                                 /* dijitInputField class gives placeHolder same padding as the input field
11997                                  * parent node already has dijitInputField class but it doesn't affect this <span>
11998                                  * since it's position: absolute.
11999                                  */
12000                                 this._phspan = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
12001                         }
12002                         this._phspan.innerHTML="";
12003                         this._phspan.appendChild(document.createTextNode(v));
12004                         
12005                         this._updatePlaceHolder();
12006                 },
12007                 
12008                 _updatePlaceHolder: function(){
12009                         if(this._phspan){
12010                                 this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
12011                         }
12012                 },
12013
12014                 _getValueAttr: function(){
12015                         // summary:
12016                         //              Hook so attr('value') works as we like.
12017                         // description:
12018                         //              For `dijit.form.TextBox` this basically returns the value of the <input>.
12019                         //
12020                         //              For `dijit.form.MappedTextBox` subclasses, which have both
12021                         //              a "displayed value" and a separate "submit value",
12022                         //              This treats the "displayed value" as the master value, computing the
12023                         //              submit value from it via this.parse().
12024                         return this.parse(this.get('displayedValue'), this.constraints);
12025                 },
12026
12027                 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
12028                         // summary:
12029                         //              Hook so attr('value', ...) works.
12030                         //
12031                         // description:
12032                         //              Sets the value of the widget to "value" which can be of
12033                         //              any type as determined by the widget.
12034                         //
12035                         // value:
12036                         //              The visual element value is also set to a corresponding,
12037                         //              but not necessarily the same, value.
12038                         //
12039                         // formattedValue:
12040                         //              If specified, used to set the visual element value,
12041                         //              otherwise a computed visual value is used.
12042                         //
12043                         // priorityChange:
12044                         //              If true, an onChange event is fired immediately instead of
12045                         //              waiting for the next blur event.
12046
12047                         var filteredValue;
12048                         if(value !== undefined){
12049                                 // TODO: this is calling filter() on both the display value and the actual value.
12050                                 // I added a comment to the filter() definition about this, but it should be changed.
12051                                 filteredValue = this.filter(value);
12052                                 if(typeof formattedValue != "string"){
12053                                         if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
12054                                                 formattedValue = this.filter(this.format(filteredValue, this.constraints));
12055                                         }else{ formattedValue = ''; }
12056                                 }
12057                         }
12058                         if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
12059                                 this.textbox.value = formattedValue;
12060                         }
12061
12062                         this._updatePlaceHolder();
12063
12064                         this.inherited(arguments, [filteredValue, priorityChange]);
12065                 },
12066
12067                 // displayedValue: String
12068                 //              For subclasses like ComboBox where the displayed value
12069                 //              (ex: Kentucky) and the serialized value (ex: KY) are different,
12070                 //              this represents the displayed value.
12071                 //
12072                 //              Setting 'displayedValue' through attr('displayedValue', ...)
12073                 //              updates 'value', and vice-versa.  Otherwise 'value' is updated
12074                 //              from 'displayedValue' periodically, like onBlur etc.
12075                 //
12076                 //              TODO: move declaration to MappedTextBox?
12077                 //              Problem is that ComboBox references displayedValue,
12078                 //              for benefit of FilteringSelect.
12079                 displayedValue: "",
12080
12081                 getDisplayedValue: function(){
12082                         // summary:
12083                         //              Deprecated.   Use set('displayedValue') instead.
12084                         // tags:
12085                         //              deprecated
12086                         dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
12087                         return this.get('displayedValue');
12088                 },
12089
12090                 _getDisplayedValueAttr: function(){
12091                         // summary:
12092                         //              Hook so attr('displayedValue') works.
12093                         // description:
12094                         //              Returns the displayed value (what the user sees on the screen),
12095                         //              after filtering (ie, trimming spaces etc.).
12096                         //
12097                         //              For some subclasses of TextBox (like ComboBox), the displayed value
12098                         //              is different from the serialized value that's actually
12099                         //              sent to the server (see dijit.form.ValidationTextBox.serialize)
12100
12101                         return this.filter(this.textbox.value);
12102                 },
12103
12104                 setDisplayedValue: function(/*String*/value){
12105                         // summary:
12106                         //              Deprecated.   Use set('displayedValue', ...) instead.
12107                         // tags:
12108                         //              deprecated
12109                         dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
12110                         this.set('displayedValue', value);
12111                 },
12112
12113                 _setDisplayedValueAttr: function(/*String*/value){
12114                         // summary:
12115                         //              Hook so attr('displayedValue', ...) works.
12116                         // description:
12117                         //              Sets the value of the visual element to the string "value".
12118                         //              The widget value is also set to a corresponding,
12119                         //              but not necessarily the same, value.
12120
12121                         if(value === null || value === undefined){ value = '' }
12122                         else if(typeof value != "string"){ value = String(value) }
12123                         this.textbox.value = value;
12124                         this._setValueAttr(this.get('value'), undefined, value);
12125                 },
12126
12127                 format: function(/* String */ value, /* Object */ constraints){
12128                         // summary:
12129                         //              Replacable function to convert a value to a properly formatted string.
12130                         // tags:
12131                         //              protected extension
12132                         return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
12133                 },
12134
12135                 parse: function(/* String */ value, /* Object */ constraints){
12136                         // summary:
12137                         //              Replacable function to convert a formatted string to a value
12138                         // tags:
12139                         //              protected extension
12140
12141                         return value;   // String
12142                 },
12143
12144                 _refreshState: function(){
12145                         // summary:
12146                         //              After the user types some characters, etc., this method is
12147                         //              called to check the field for validity etc.  The base method
12148                         //              in `dijit.form.TextBox` does nothing, but subclasses override.
12149                         // tags:
12150                         //              protected
12151                 },
12152
12153                 _onInput: function(e){
12154                         if(e && e.type && /key/i.test(e.type) && e.keyCode){
12155                                 switch(e.keyCode){
12156                                         case dojo.keys.SHIFT:
12157                                         case dojo.keys.ALT:
12158                                         case dojo.keys.CTRL:
12159                                         case dojo.keys.TAB:
12160                                                 return;
12161                                 }
12162                         }
12163                         if(this.intermediateChanges){
12164                                 var _this = this;
12165                                 // the setTimeout allows the key to post to the widget input box
12166                                 setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
12167                         }
12168                         this._refreshState();
12169                 },
12170
12171                 postCreate: function(){
12172                         // setting the value here is needed since value="" in the template causes "undefined"
12173                         // and setting in the DOM (instead of the JS object) helps with form reset actions
12174                         if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
12175                                 var s = dojo.getComputedStyle(this.domNode);
12176                                 if(s){
12177                                         var ff = s.fontFamily;
12178                                         if(ff){
12179                                                 var inputs = this.domNode.getElementsByTagName("INPUT");
12180                                                 if(inputs){
12181                                                         for(var i=0; i < inputs.length; i++){
12182                                                                 inputs[i].style.fontFamily = ff;
12183                                                         }
12184                                                 }
12185                                         }
12186                                 }
12187                         }
12188                         this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values shuld be the same
12189                         this.inherited(arguments);
12190                         if(dojo.isMoz || dojo.isOpera){
12191                                 this.connect(this.textbox, "oninput", this._onInput);
12192                         }else{
12193                                 this.connect(this.textbox, "onkeydown", this._onInput);
12194                                 this.connect(this.textbox, "onkeyup", this._onInput);
12195                                 this.connect(this.textbox, "onpaste", this._onInput);
12196                                 this.connect(this.textbox, "oncut", this._onInput);
12197                         }
12198                 },
12199
12200                 _blankValue: '', // if the textbox is blank, what value should be reported
12201                 filter: function(val){
12202                         // summary:
12203                         //              Auto-corrections (such as trimming) that are applied to textbox
12204                         //              value on blur or form submit.
12205                         // description:
12206                         //              For MappedTextBox subclasses, this is called twice
12207                         //                      - once with the display value
12208                         //                      - once the value as set/returned by attr('value', ...)
12209                         //              and attr('value'), ex: a Number for NumberTextBox.
12210                         //
12211                         //              In the latter case it does corrections like converting null to NaN.  In
12212                         //              the former case the NumberTextBox.filter() method calls this.inherited()
12213                         //              to execute standard trimming code in TextBox.filter().
12214                         //
12215                         //              TODO: break this into two methods in 2.0
12216                         //
12217                         // tags:
12218                         //              protected extension
12219                         if(val === null){ return this._blankValue; }
12220                         if(typeof val != "string"){ return val; }
12221                         if(this.trim){
12222                                 val = dojo.trim(val);
12223                         }
12224                         if(this.uppercase){
12225                                 val = val.toUpperCase();
12226                         }
12227                         if(this.lowercase){
12228                                 val = val.toLowerCase();
12229                         }
12230                         if(this.propercase){
12231                                 val = val.replace(/[^\s]+/g, function(word){
12232                                         return word.substring(0,1).toUpperCase() + word.substring(1);
12233                                 });
12234                         }
12235                         return val;
12236                 },
12237
12238                 _setBlurValue: function(){
12239                         this._setValueAttr(this.get('value'), true);
12240                 },
12241
12242                 _onBlur: function(e){
12243                         if(this.disabled){ return; }
12244                         this._setBlurValue();
12245                         this.inherited(arguments);
12246
12247                         if(this._selectOnClickHandle){
12248                                 this.disconnect(this._selectOnClickHandle);
12249                         }
12250                         if(this.selectOnClick && dojo.isMoz){
12251                                 this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
12252                         }
12253                         
12254                         this._updatePlaceHolder();
12255                 },
12256
12257                 _onFocus: function(/*String*/ by){
12258                         if(this.disabled || this.readOnly){ return; }
12259
12260                         // Select all text on focus via click if nothing already selected.
12261                         // Since mouse-up will clear the selection need to defer selection until after mouse-up.
12262                         // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
12263                         if(this.selectOnClick && by == "mouse"){
12264                                 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
12265                                         // Only select all text on first click; otherwise users would have no way to clear
12266                                         // the selection.
12267                                         this.disconnect(this._selectOnClickHandle);
12268
12269                                         // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
12270                                         // and if not, then select all the text
12271                                         var textIsNotSelected;
12272                                         if(dojo.isIE){
12273                                                 var range = dojo.doc.selection.createRange();
12274                                                 var parent = range.parentElement();
12275                                                 textIsNotSelected = parent == this.textbox && range.text.length == 0;
12276                                         }else{
12277                                                 textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
12278                                         }
12279                                         if(textIsNotSelected){
12280                                                 dijit.selectInputText(this.textbox);
12281                                         }
12282                                 });
12283                         }
12284
12285                         this._updatePlaceHolder();
12286                         
12287                         this._refreshState();
12288                         this.inherited(arguments);
12289                 },
12290
12291                 reset: function(){
12292                         // Overrides dijit._FormWidget.reset().
12293                         // Additionally resets the displayed textbox value to ''
12294                         this.textbox.value = '';
12295                         this.inherited(arguments);
12296                 }
12297         }
12298 );
12299
12300 dijit.selectInputText = function(/*DomNode*/element, /*Number?*/ start, /*Number?*/ stop){
12301         // summary:
12302         //              Select text in the input element argument, from start (default 0), to stop (default end).
12303
12304         // TODO: use functions in _editor/selection.js?
12305         var _window = dojo.global;
12306         var _document = dojo.doc;
12307         element = dojo.byId(element);
12308         if(isNaN(start)){ start = 0; }
12309         if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
12310         dijit.focus(element);
12311         if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
12312                 if(element.createTextRange){
12313                         var range = element.createTextRange();
12314                         with(range){
12315                                 collapse(true);
12316                                 moveStart("character", -99999); // move to 0
12317                                 moveStart("character", start); // delta from 0 is the correct position
12318                                 moveEnd("character", stop-start);
12319                                 select();
12320                         }
12321                 }
12322         }else if(_window["getSelection"]){
12323                 if(element.setSelectionRange){
12324                         element.setSelectionRange(start, stop);
12325                 }
12326         }
12327 };
12328
12329 }
12330
12331 if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12332 dojo._hasResource["dijit.Tooltip"] = true;
12333 dojo.provide("dijit.Tooltip");
12334
12335
12336
12337
12338 dojo.declare(
12339         "dijit._MasterTooltip",
12340         [dijit._Widget, dijit._Templated],
12341         {
12342                 // summary:
12343                 //              Internal widget that holds the actual tooltip markup,
12344                 //              which occurs once per page.
12345                 //              Called by Tooltip widgets which are just containers to hold
12346                 //              the markup
12347                 // tags:
12348                 //              protected
12349
12350                 // duration: Integer
12351                 //              Milliseconds to fade in/fade out
12352                 duration: dijit.defaultDuration,
12353
12354                 templateString: dojo.cache("dijit", "templates/Tooltip.html", "<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"),
12355
12356                 postCreate: function(){
12357                         dojo.body().appendChild(this.domNode);
12358
12359                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
12360
12361                         // Setup fade-in and fade-out functions.
12362                         this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
12363                         this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
12364
12365                 },
12366
12367                 show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
12368                         // summary:
12369                         //              Display tooltip w/specified contents to right of specified node
12370                         //              (To left if there's no space on the right, or if rtl == true)
12371
12372                         if(this.aroundNode && this.aroundNode === aroundNode){
12373                                 return;
12374                         }
12375
12376                         if(this.fadeOut.status() == "playing"){
12377                                 // previous tooltip is being hidden; wait until the hide completes then show new one
12378                                 this._onDeck=arguments;
12379                                 return;
12380                         }
12381                         this.containerNode.innerHTML=innerHTML;
12382
12383                         var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
12384
12385                         // show it
12386                         dojo.style(this.domNode, "opacity", 0);
12387                         this.fadeIn.play();
12388                         this.isShowingNow = true;
12389                         this.aroundNode = aroundNode;
12390                 },
12391
12392                 orient: function(/* DomNode */ node, /* String */ aroundCorner, /* String */ tooltipCorner){
12393                         // summary:
12394                         //              Private function to set CSS for tooltip node based on which position it's in.
12395                         //              This is called by the dijit popup code.
12396                         // tags:
12397                         //              protected
12398
12399                         node.className = "dijitTooltip " +
12400                                 {
12401                                         "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
12402                                         "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
12403                                         "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
12404                                         "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
12405                                         "BR-BL": "dijitTooltipRight",
12406                                         "BL-BR": "dijitTooltipLeft"
12407                                 }[aroundCorner + "-" + tooltipCorner];
12408                 },
12409
12410                 _onShow: function(){
12411                         // summary:
12412                         //              Called at end of fade-in operation
12413                         // tags:
12414                         //              protected
12415                         if(dojo.isIE){
12416                                 // the arrow won't show up on a node w/an opacity filter
12417                                 this.domNode.style.filter="";
12418                         }
12419                 },
12420
12421                 hide: function(aroundNode){
12422                         // summary:
12423                         //              Hide the tooltip
12424                         if(this._onDeck && this._onDeck[1] == aroundNode){
12425                                 // this hide request is for a show() that hasn't even started yet;
12426                                 // just cancel the pending show()
12427                                 this._onDeck=null;
12428                         }else if(this.aroundNode === aroundNode){
12429                                 // this hide request is for the currently displayed tooltip
12430                                 this.fadeIn.stop();
12431                                 this.isShowingNow = false;
12432                                 this.aroundNode = null;
12433                                 this.fadeOut.play();
12434                         }else{
12435                                 // just ignore the call, it's for a tooltip that has already been erased
12436                         }
12437                 },
12438
12439                 _onHide: function(){
12440                         // summary:
12441                         //              Called at end of fade-out operation
12442                         // tags:
12443                         //              protected
12444
12445                         this.domNode.style.cssText="";  // to position offscreen again
12446                         this.containerNode.innerHTML="";
12447                         if(this._onDeck){
12448                                 // a show request has been queued up; do it now
12449                                 this.show.apply(this, this._onDeck);
12450                                 this._onDeck=null;
12451                         }
12452                 }
12453
12454         }
12455 );
12456
12457 dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
12458         // summary:
12459         //              Display tooltip w/specified contents in specified position.
12460         //              See description of dijit.Tooltip.defaultPosition for details on position parameter.
12461         //              If position is not specified then dijit.Tooltip.defaultPosition is used.
12462         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
12463         return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
12464 };
12465
12466 dijit.hideTooltip = function(aroundNode){
12467         // summary:
12468         //              Hide the tooltip
12469         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
12470         return dijit._masterTT.hide(aroundNode);
12471 };
12472
12473 dojo.declare(
12474         "dijit.Tooltip",
12475         dijit._Widget,
12476         {
12477                 // summary:
12478                 //              Pops up a tooltip (a help message) when you hover over a node.
12479
12480                 // label: String
12481                 //              Text to display in the tooltip.
12482                 //              Specified as innerHTML when creating the widget from markup.
12483                 label: "",
12484
12485                 // showDelay: Integer
12486                 //              Number of milliseconds to wait after hovering over/focusing on the object, before
12487                 //              the tooltip is displayed.
12488                 showDelay: 400,
12489
12490                 // connectId: [const] String[]
12491                 //              Id's of domNodes to attach the tooltip to.
12492                 //              When user hovers over any of the specified dom nodes, the tooltip will appear.
12493                 //
12494                 //              Note: Currently connectId can only be specified on initialization, it cannot
12495                 //              be changed via attr('connectId', ...)
12496                 //
12497                 //              Note: in 2.0 this will be renamed to connectIds for less confusion.
12498                 connectId: [],
12499
12500                 // position: String[]
12501                 //              See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
12502                 position: [],
12503
12504                 constructor: function(){
12505                         // Map id's of nodes I'm connected to to a list of the this.connect() handles
12506                         this._nodeConnectionsById = {};
12507                 },
12508
12509                 _setConnectIdAttr: function(newIds){
12510                         for(var oldId in this._nodeConnectionsById){
12511                                 this.removeTarget(oldId);
12512                         }
12513                         dojo.forEach(dojo.isArrayLike(newIds) ? newIds : [newIds], this.addTarget, this);
12514                 },
12515
12516                 _getConnectIdAttr: function(){
12517                         var ary = [];
12518                         for(var id in this._nodeConnectionsById){
12519                                 ary.push(id);
12520                         }
12521                         return ary;
12522                 },
12523
12524                 addTarget: function(/*DOMNODE || String*/ id){
12525                         // summary:
12526                         //              Attach tooltip to specified node, if it's not already connected
12527                         var node = dojo.byId(id);
12528                         if(!node){ return; }
12529                         if(node.id in this._nodeConnectionsById){ return; }//Already connected
12530
12531                         this._nodeConnectionsById[node.id] = [
12532                                 this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
12533                                 this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
12534                                 this.connect(node, "onfocus", "_onTargetFocus"),
12535                                 this.connect(node, "onblur", "_onTargetBlur")
12536                         ];
12537                 },
12538
12539                 removeTarget: function(/*DOMNODE || String*/ node){
12540                         // summary:
12541                         //              Detach tooltip from specified node
12542
12543                         // map from DOMNode back to plain id string
12544                         var id = node.id || node;
12545
12546                         if(id in this._nodeConnectionsById){
12547                                 dojo.forEach(this._nodeConnectionsById[id], this.disconnect, this);
12548                                 delete this._nodeConnectionsById[id];
12549                         }
12550                 },
12551
12552                 postCreate: function(){
12553                         dojo.addClass(this.domNode,"dijitTooltipData");
12554                 },
12555
12556                 startup: function(){
12557                         this.inherited(arguments);
12558
12559                         // If this tooltip was created in a template, or for some other reason the specified connectId[s]
12560                         // didn't exist during the widget's initialization, then connect now.
12561                         var ids = this.connectId;
12562                         dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
12563                 },
12564
12565                 _onTargetMouseEnter: function(/*Event*/ e){
12566                         // summary:
12567                         //              Handler for mouseenter event on the target node
12568                         // tags:
12569                         //              private
12570                         this._onHover(e);
12571                 },
12572
12573                 _onTargetMouseLeave: function(/*Event*/ e){
12574                         // summary:
12575                         //              Handler for mouseleave event on the target node
12576                         // tags:
12577                         //              private
12578                         this._onUnHover(e);
12579                 },
12580
12581                 _onTargetFocus: function(/*Event*/ e){
12582                         // summary:
12583                         //              Handler for focus event on the target node
12584                         // tags:
12585                         //              private
12586
12587                         this._focus = true;
12588                         this._onHover(e);
12589                 },
12590
12591                 _onTargetBlur: function(/*Event*/ e){
12592                         // summary:
12593                         //              Handler for blur event on the target node
12594                         // tags:
12595                         //              private
12596
12597                         this._focus = false;
12598                         this._onUnHover(e);
12599                 },
12600
12601                 _onHover: function(/*Event*/ e){
12602                         // summary:
12603                         //              Despite the name of this method, it actually handles both hover and focus
12604                         //              events on the target node, setting a timer to show the tooltip.
12605                         // tags:
12606                         //              private
12607                         if(!this._showTimer){
12608                                 var target = e.target;
12609                                 this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
12610                         }
12611                 },
12612
12613                 _onUnHover: function(/*Event*/ e){
12614                         // summary:
12615                         //              Despite the name of this method, it actually handles both mouseleave and blur
12616                         //              events on the target node, hiding the tooltip.
12617                         // tags:
12618                         //              private
12619
12620                         // keep a tooltip open if the associated element still has focus (even though the
12621                         // mouse moved away)
12622                         if(this._focus){ return; }
12623
12624                         if(this._showTimer){
12625                                 clearTimeout(this._showTimer);
12626                                 delete this._showTimer;
12627                         }
12628                         this.close();
12629                 },
12630
12631                 open: function(/*DomNode*/ target){
12632                         // summary:
12633                         //              Display the tooltip; usually not called directly.
12634                         // tags:
12635                         //              private
12636
12637                         if(this._showTimer){
12638                                 clearTimeout(this._showTimer);
12639                                 delete this._showTimer;
12640                         }
12641                         dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
12642
12643                         this._connectNode = target;
12644                         this.onShow(target, this.position);
12645                 },
12646
12647                 close: function(){
12648                         // summary:
12649                         //              Hide the tooltip or cancel timer for show of tooltip
12650                         // tags:
12651                         //              private
12652
12653                         if(this._connectNode){
12654                                 // if tooltip is currently shown
12655                                 dijit.hideTooltip(this._connectNode);
12656                                 delete this._connectNode;
12657                                 this.onHide();
12658                         }
12659                         if(this._showTimer){
12660                                 // if tooltip is scheduled to be shown (after a brief delay)
12661                                 clearTimeout(this._showTimer);
12662                                 delete this._showTimer;
12663                         }
12664                 },
12665
12666                 onShow: function(target, position){
12667                         // summary:
12668                         //              Called when the tooltip is shown
12669                         // tags:
12670                         //              callback
12671                 },
12672
12673                 onHide: function(){
12674                         // summary:
12675                         //              Called when the tooltip is hidden
12676                         // tags:
12677                         //              callback
12678                 },
12679
12680                 uninitialize: function(){
12681                         this.close();
12682                         this.inherited(arguments);
12683                 }
12684         }
12685 );
12686
12687 // dijit.Tooltip.defaultPosition: String[]
12688 //              This variable controls the position of tooltips, if the position is not specified to
12689 //              the Tooltip widget or *TextBox widget itself.  It's an array of strings with the following values:
12690 //
12691 //                      * before: places tooltip to the left of the target node/widget, or to the right in
12692 //                        the case of RTL scripts like Hebrew and Arabic
12693 //                      * after: places tooltip to the right of the target node/widget, or to the left in
12694 //                        the case of RTL scripts like Hebrew and Arabic
12695 //                      * above: tooltip goes above target node
12696 //                      * below: tooltip goes below target node
12697 //
12698 //              The list is positions is tried, in order, until a position is found where the tooltip fits
12699 //              within the viewport.
12700 //
12701 //              Be careful setting this parameter.  A value of "above" may work fine until the user scrolls
12702 //              the screen so that there's no room above the target node.   Nodes with drop downs, like
12703 //              DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
12704 //              that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
12705 //              is only room below (or above) the target node, but not both.
12706 dijit.Tooltip.defaultPosition = ["after", "before"];
12707
12708 }
12709
12710 if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12711 dojo._hasResource["dijit.form.ValidationTextBox"] = true;
12712 dojo.provide("dijit.form.ValidationTextBox");
12713
12714
12715
12716
12717
12718
12719
12720
12721 /*=====
12722         dijit.form.ValidationTextBox.__Constraints = function(){
12723                 // locale: String
12724                 //              locale used for validation, picks up value from this widget's lang attribute
12725                 // _flags_: anything
12726                 //              various flags passed to regExpGen function
12727                 this.locale = "";
12728                 this._flags_ = "";
12729         }
12730 =====*/
12731
12732 dojo.declare(
12733         "dijit.form.ValidationTextBox",
12734         dijit.form.TextBox,
12735         {
12736                 // summary:
12737                 //              Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
12738                 // tags:
12739                 //              protected
12740
12741                 templateString: dojo.cache("dijit.form", "templates/ValidationTextBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" waiRole=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&Chi; \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" dojoAttachPoint='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"),
12742                 baseClass: "dijitTextBox dijitValidationTextBox",
12743
12744                 // required: Boolean
12745                 //              User is required to enter data into this field.
12746                 required: false,
12747
12748                 // promptMessage: String
12749                 //              If defined, display this hint string immediately on focus to the textbox, if empty.
12750                 //              Think of this like a tooltip that tells the user what to do, not an error message
12751                 //              that tells the user what they've done wrong.
12752                 //
12753                 //              Message disappears when user starts typing.
12754                 promptMessage: "",
12755
12756                 // invalidMessage: String
12757                 //              The message to display if value is invalid.
12758                 //              The translated string value is read from the message file by default.
12759                 //              Set to "" to use the promptMessage instead.
12760                 invalidMessage: "$_unset_$",
12761
12762                 // missingMessage: String
12763                 //              The message to display if value is empty and the field is required.
12764                 //              The translated string value is read from the message file by default.
12765                 //              Set to "" to use the invalidMessage instead.
12766                 missingMessage: "$_unset_$",
12767
12768                 // constraints: dijit.form.ValidationTextBox.__Constraints
12769                 //              user-defined object needed to pass parameters to the validator functions
12770                 constraints: {},
12771
12772                 // regExp: [extension protected] String
12773                 //              regular expression string used to validate the input
12774                 //              Do not specify both regExp and regExpGen
12775                 regExp: ".*",
12776
12777                 regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){
12778                         // summary:
12779                         //              Overridable function used to generate regExp when dependent on constraints.
12780                         //              Do not specify both regExp and regExpGen.
12781                         // tags:
12782                         //              extension protected
12783                         return this.regExp; // String
12784                 },
12785
12786                 // state: [readonly] String
12787                 //              Shows current state (ie, validation result) of input (Normal, Warning, or Error)
12788                 state: "",
12789
12790                 // tooltipPosition: String[]
12791                 //              See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
12792                 tooltipPosition: [],
12793
12794                 _setValueAttr: function(){
12795                         // summary:
12796                         //              Hook so attr('value', ...) works.
12797                         this.inherited(arguments);
12798                         this.validate(this._focused);
12799                 },
12800
12801                 validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
12802                         // summary:
12803                         //              Overridable function used to validate the text input against the regular expression.
12804                         // tags:
12805                         //              protected
12806                         return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
12807                                 (!this.required || !this._isEmpty(value)) &&
12808                                 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
12809                 },
12810
12811                 _isValidSubset: function(){
12812                         // summary:
12813                         //              Returns true if the value is either already valid or could be made valid by appending characters.
12814                         //              This is used for validation while the user [may be] still typing.
12815                         return this.textbox.value.search(this._partialre) == 0;
12816                 },
12817
12818                 isValid: function(/*Boolean*/ isFocused){
12819                         // summary:
12820                         //              Tests if value is valid.
12821                         //              Can override with your own routine in a subclass.
12822                         // tags:
12823                         //              protected
12824                         return this.validator(this.textbox.value, this.constraints);
12825                 },
12826
12827                 _isEmpty: function(value){
12828                         // summary:
12829                         //              Checks for whitespace
12830                         return /^\s*$/.test(value); // Boolean
12831                 },
12832
12833                 getErrorMessage: function(/*Boolean*/ isFocused){
12834                         // summary:
12835                         //              Return an error message to show if appropriate
12836                         // tags:
12837                         //              protected
12838                         return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
12839                 },
12840
12841                 getPromptMessage: function(/*Boolean*/ isFocused){
12842                         // summary:
12843                         //              Return a hint message to show when widget is first focused
12844                         // tags:
12845                         //              protected
12846                         return this.promptMessage; // String
12847                 },
12848
12849                 _maskValidSubsetError: true,
12850                 validate: function(/*Boolean*/ isFocused){
12851                         // summary:
12852                         //              Called by oninit, onblur, and onkeypress.
12853                         // description:
12854                         //              Show missing or invalid messages if appropriate, and highlight textbox field.
12855                         // tags:
12856                         //              protected
12857                         var message = "";
12858                         var isValid = this.disabled || this.isValid(isFocused);
12859                         if(isValid){ this._maskValidSubsetError = true; }
12860                         var isEmpty = this._isEmpty(this.textbox.value);
12861                         var isValidSubset = !isValid && !isEmpty && isFocused && this._isValidSubset();
12862                         this.state = ((isValid || ((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "" : "Error";
12863                         if(this.state == "Error"){ this._maskValidSubsetError = isFocused; } // we want the error to show up afer a blur and refocus
12864                         this._setStateClass();
12865                         dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
12866                         if(isFocused){
12867                                 if(this.state == "Error"){
12868                                         message = this.getErrorMessage(true);
12869                                 }else{
12870                                         message = this.getPromptMessage(true); // show the prompt whever there's no error
12871                                 }
12872                                 this._maskValidSubsetError = true; // since we're focused, always mask warnings
12873                         }
12874                         this.displayMessage(message);
12875                         return isValid;
12876                 },
12877
12878                 // _message: String
12879                 //              Currently displayed message
12880                 _message: "",
12881
12882                 displayMessage: function(/*String*/ message){
12883                         // summary:
12884                         //              Overridable method to display validation errors/hints.
12885                         //              By default uses a tooltip.
12886                         // tags:
12887                         //              extension
12888                         if(this._message == message){ return; }
12889                         this._message = message;
12890                         dijit.hideTooltip(this.domNode);
12891                         if(message){
12892                                 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
12893                         }
12894                 },
12895
12896                 _refreshState: function(){
12897                         // Overrides TextBox._refreshState()
12898                         this.validate(this._focused);
12899                         this.inherited(arguments);
12900                 },
12901
12902                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
12903
12904                 constructor: function(){
12905                         this.constraints = {};
12906                 },
12907
12908                 _setConstraintsAttr: function(/* Object */ constraints){
12909                         if(!constraints.locale && this.lang){
12910                                 constraints.locale = this.lang;
12911                         }
12912                         this.constraints = constraints;
12913                         this._computePartialRE();
12914                 },
12915
12916                 _computePartialRE: function(){
12917                         var p = this.regExpGen(this.constraints);
12918                         this.regExp = p;
12919                         var partialre = "";
12920                         // parse the regexp and produce a new regexp that matches valid subsets
12921                         // if the regexp is .* then there's no use in matching subsets since everything is valid
12922                         if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
12923                                 function (re){
12924                                         switch(re.charAt(0)){
12925                                                 case '{':
12926                                                 case '+':
12927                                                 case '?':
12928                                                 case '*':
12929                                                 case '^':
12930                                                 case '$':
12931                                                 case '|':
12932                                                 case '(':
12933                                                         partialre += re;
12934                                                         break;
12935                                                 case ")":
12936                                                         partialre += "|$)";
12937                                                         break;
12938                                                  default:
12939                                                         partialre += "(?:"+re+"|$)";
12940                                                         break;
12941                                         }
12942                                 }
12943                         );}
12944                         try{ // this is needed for now since the above regexp parsing needs more test verification
12945                                 "".search(partialre);
12946                         }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
12947                                 partialre = this.regExp;
12948                                 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
12949                         } // should never be here unless the original RE is bad or the parsing is bad
12950                         this._partialre = "^(?:" + partialre + ")$";
12951                 },
12952
12953                 postMixInProperties: function(){
12954                         this.inherited(arguments);
12955                         this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
12956                         if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
12957                         if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
12958                         if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
12959                         if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
12960                         this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
12961                 },
12962
12963                 _setDisabledAttr: function(/*Boolean*/ value){
12964                         this.inherited(arguments);      // call FormValueWidget._setDisabledAttr()
12965                         this._refreshState();
12966                 },
12967
12968                 _setRequiredAttr: function(/*Boolean*/ value){
12969                         this.required = value;
12970                         dijit.setWaiState(this.focusNode, "required", value);
12971                         this._refreshState();
12972                 },
12973
12974                 reset:function(){
12975                         // Overrides dijit.form.TextBox.reset() by also
12976                         // hiding errors about partial matches
12977                         this._maskValidSubsetError = true;
12978                         this.inherited(arguments);
12979                 },
12980
12981                 _onBlur: function(){
12982                         this.displayMessage('');
12983                         this.inherited(arguments);
12984                 }
12985         }
12986 );
12987
12988 dojo.declare(
12989         "dijit.form.MappedTextBox",
12990         dijit.form.ValidationTextBox,
12991         {
12992                 // summary:
12993                 //              A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
12994                 //              a visible formatted display value, and a serializable
12995                 //              value in a hidden input field which is actually sent to the server.
12996                 // description:
12997                 //              The visible display may
12998                 //              be locale-dependent and interactive.  The value sent to the server is stored in a hidden
12999                 //              input field which uses the `name` attribute declared by the original widget.  That value sent
13000                 //              to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
13001                 //              locale-neutral.
13002                 // tags:
13003                 //              protected
13004
13005                 postMixInProperties: function(){
13006                         this.inherited(arguments);
13007
13008                         // we want the name attribute to go to the hidden <input>, not the displayed <input>,
13009                         // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
13010                         this.nameAttrSetting = "";
13011                 },
13012
13013                 serialize: function(/*anything*/val, /*Object?*/options){
13014                         // summary:
13015                         //              Overridable function used to convert the attr('value') result to a canonical
13016                         //              (non-localized) string.  For example, will print dates in ISO format, and
13017                         //              numbers the same way as they are represented in javascript.
13018                         // tags:
13019                         //              protected extension
13020                         return val.toString ? val.toString() : ""; // String
13021                 },
13022
13023                 toString: function(){
13024                         // summary:
13025                         //              Returns widget as a printable string using the widget's value
13026                         // tags:
13027                         //              protected
13028                         var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
13029                         return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
13030                 },
13031
13032                 validate: function(){
13033                         // Overrides `dijit.form.TextBox.validate`
13034                         this.valueNode.value = this.toString();
13035                         return this.inherited(arguments);
13036                 },
13037
13038                 buildRendering: function(){
13039                         // Overrides `dijit._Templated.buildRendering`
13040
13041                         this.inherited(arguments);
13042
13043                         // Create a hidden <input> node with the serialized value used for submit
13044                         // (as opposed to the displayed value).
13045                         // Passing in name as markup rather than calling dojo.create() with an attrs argument
13046                         // to make dojo.query(input[name=...]) work on IE. (see #8660)
13047                         this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name + "'" : "") + ">", this.textbox, "after");
13048                 },
13049
13050                 reset:function(){
13051                         // Overrides `dijit.form.ValidationTextBox.reset` to
13052                         // reset the hidden textbox value to ''
13053                         this.valueNode.value = '';
13054                         this.inherited(arguments);
13055                 }
13056         }
13057 );
13058
13059 /*=====
13060         dijit.form.RangeBoundTextBox.__Constraints = function(){
13061                 // min: Number
13062                 //              Minimum signed value.  Default is -Infinity
13063                 // max: Number
13064                 //              Maximum signed value.  Default is +Infinity
13065                 this.min = min;
13066                 this.max = max;
13067         }
13068 =====*/
13069
13070 dojo.declare(
13071         "dijit.form.RangeBoundTextBox",
13072         dijit.form.MappedTextBox,
13073         {
13074                 // summary:
13075                 //              Base class for textbox form widgets which defines a range of valid values.
13076
13077                 // rangeMessage: String
13078                 //              The message to display if value is out-of-range
13079                 rangeMessage: "",
13080
13081                 /*=====
13082                 // constraints: dijit.form.RangeBoundTextBox.__Constraints
13083                 constraints: {},
13084                 ======*/
13085
13086                 rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
13087                         // summary:
13088                         //              Overridable function used to validate the range of the numeric input value.
13089                         // tags:
13090                         //              protected
13091                         return  ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
13092                                 ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
13093                 },
13094
13095                 isInRange: function(/*Boolean*/ isFocused){
13096                         // summary:
13097                         //              Tests if the value is in the min/max range specified in constraints
13098                         // tags:
13099                         //              protected
13100                         return this.rangeCheck(this.get('value'), this.constraints);
13101                 },
13102
13103                 _isDefinitelyOutOfRange: function(){
13104                         // summary:
13105                         //              Returns true if the value is out of range and will remain
13106                         //              out of range even if the user types more characters
13107                         var val = this.get('value');
13108                         var isTooLittle = false;
13109                         var isTooMuch = false;
13110                         if("min" in this.constraints){
13111                                 var min = this.constraints.min;
13112                                 min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
13113                                 isTooLittle = (typeof min == "number") && min < 0;
13114                         }
13115                         if("max" in this.constraints){
13116                                 var max = this.constraints.max;
13117                                 max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
13118                                 isTooMuch = (typeof max == "number") && max > 0;
13119                         }
13120                         return isTooLittle || isTooMuch;
13121                 },
13122
13123                 _isValidSubset: function(){
13124                         // summary:
13125                         //              Overrides `dijit.form.ValidationTextBox._isValidSubset`.
13126                         //              Returns true if the input is syntactically valid, and either within
13127                         //              range or could be made in range by more typing.
13128                         return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
13129                 },
13130
13131                 isValid: function(/*Boolean*/ isFocused){
13132                         // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
13133                         return this.inherited(arguments) &&
13134                                 ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
13135                 },
13136
13137                 getErrorMessage: function(/*Boolean*/ isFocused){
13138                         // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
13139                         var v = this.get('value');
13140                         if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
13141                                 return this.rangeMessage; // String
13142                         }
13143                         return this.inherited(arguments);
13144                 },
13145
13146                 postMixInProperties: function(){
13147                         this.inherited(arguments);
13148                         if(!this.rangeMessage){
13149                                 this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
13150                                 this.rangeMessage = this.messages.rangeMessage;
13151                         }
13152                 },
13153
13154                 _setConstraintsAttr: function(/* Object */ constraints){
13155                         this.inherited(arguments);
13156                         if(this.focusNode){ // not set when called from postMixInProperties
13157                                 if(this.constraints.min !== undefined){
13158                                         dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
13159                                 }else{
13160                                         dijit.removeWaiState(this.focusNode, "valuemin");
13161                                 }
13162                                 if(this.constraints.max !== undefined){
13163                                         dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
13164                                 }else{
13165                                         dijit.removeWaiState(this.focusNode, "valuemax");
13166                                 }
13167                         }
13168                 },
13169
13170                 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
13171                         // summary:
13172                         //              Hook so attr('value', ...) works.
13173
13174                         dijit.setWaiState(this.focusNode, "valuenow", value);
13175                         this.inherited(arguments);
13176                 }
13177         }
13178 );
13179
13180 }
13181
13182 if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13183 dojo._hasResource["dijit.form.ComboBox"] = true;
13184 dojo.provide("dijit.form.ComboBox");
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194
13195
13196
13197 dojo.declare(
13198         "dijit.form.ComboBoxMixin",
13199         null,
13200         {
13201                 // summary:
13202                 //              Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
13203                 // description:
13204                 //              All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
13205                 // tags:
13206                 //              protected
13207
13208                 // item: Object
13209                 //              This is the item returned by the dojo.data.store implementation that
13210                 //              provides the data for this ComboBox, it's the currently selected item.
13211                 item: null,
13212
13213                 // pageSize: Integer
13214                 //              Argument to data provider.
13215                 //              Specifies number of search results per page (before hitting "next" button)
13216                 pageSize: Infinity,
13217
13218                 // store: Object
13219                 //              Reference to data provider object used by this ComboBox
13220                 store: null,
13221
13222                 // fetchProperties: Object
13223                 //              Mixin to the dojo.data store's fetch.
13224                 //              For example, to set the sort order of the ComboBox menu, pass:
13225                 //      |       { sort: {attribute:"name",descending: true} }
13226                 //              To override the default queryOptions so that deep=false, do:
13227                 //      |       { queryOptions: {ignoreCase: true, deep: false} }
13228                 fetchProperties:{},
13229
13230                 // query: Object
13231                 //              A query that can be passed to 'store' to initially filter the items,
13232                 //              before doing further filtering based on `searchAttr` and the key.
13233                 //              Any reference to the `searchAttr` is ignored.
13234                 query: {},
13235
13236                 // autoComplete: Boolean
13237                 //              If user types in a partial string, and then tab out of the `<input>` box,
13238                 //              automatically copy the first entry displayed in the drop down list to
13239                 //              the `<input>` field
13240                 autoComplete: true,
13241
13242                 // highlightMatch: String
13243                 //              One of: "first", "all" or "none".
13244                 //
13245                 //              If the ComboBox/FilteringSelect opens with the search results and the searched
13246                 //              string can be found, it will be highlighted.  If set to "all"
13247                 //              then will probably want to change `queryExpr` parameter to '*${0}*'
13248                 //
13249                 //              Highlighting is only performed when `labelType` is "text", so as to not
13250                 //              interfere with any HTML markup an HTML label might contain.
13251                 highlightMatch: "first",
13252
13253                 // searchDelay: Integer
13254                 //              Delay in milliseconds between when user types something and we start
13255                 //              searching based on that value
13256                 searchDelay: 100,
13257
13258                 // searchAttr: String
13259                 //              Search for items in the data store where this attribute (in the item)
13260                 //              matches what the user typed
13261                 searchAttr: "name",
13262
13263                 // labelAttr: String?
13264                 //              The entries in the drop down list come from this attribute in the
13265                 //              dojo.data items.
13266                 //              If not specified, the searchAttr attribute is used instead.
13267                 labelAttr: "",
13268
13269                 // labelType: String
13270                 //              Specifies how to interpret the labelAttr in the data store items.
13271                 //              Can be "html" or "text".
13272                 labelType: "text",
13273
13274                 // queryExpr: String
13275                 //              This specifies what query ComboBox/FilteringSelect sends to the data store,
13276                 //              based on what the user has typed.  Changing this expression will modify
13277                 //              whether the drop down shows only exact matches, a "starting with" match,
13278                 //              etc.   Use it in conjunction with highlightMatch.
13279                 //              dojo.data query expression pattern.
13280                 //              `${0}` will be substituted for the user text.
13281                 //              `*` is used for wildcards.
13282                 //              `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
13283                 queryExpr: "${0}*",
13284
13285                 // ignoreCase: Boolean
13286                 //              Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
13287                 ignoreCase: true,
13288
13289                 // hasDownArrow: [const] Boolean
13290                 //              Set this textbox to have a down arrow button, to display the drop down list.
13291                 //              Defaults to true.
13292                 hasDownArrow: true,
13293
13294                 templateString: dojo.cache("dijit.form", "templates/ComboBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\tdojoAttachPoint=\"comboNode\" waiRole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"downArrowNode\" waiRole=\"presentation\"\n\t\tdojoAttachEvent=\"onmousedown:_onArrowMouseDown\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&Chi; \" type=\"text\" tabIndex=\"-1\" readOnly waiRole=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdojoAttachEvent=\"onkeypress:_onKeyPress,compositionend\"\n\t\t\tdojoAttachPoint=\"textbox,focusNode\" waiRole=\"textbox\" waiState=\"haspopup-true,autocomplete-list\"\n\t/></div\n></div>\n"),
13295
13296                 baseClass: "dijitTextBox dijitComboBox",
13297
13298                 // Set classes like dijitDownArrowButtonHover depending on
13299                 // mouse action over button node
13300                 cssStateNodes: {
13301                         "downArrowNode": "dijitDownArrowButton"
13302                 },
13303
13304                 _getCaretPos: function(/*DomNode*/ element){
13305                         // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
13306                         var pos = 0;
13307                         if(typeof(element.selectionStart) == "number"){
13308                                 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
13309                                 pos = element.selectionStart;
13310                         }else if(dojo.isIE){
13311                                 // in the case of a mouse click in a popup being handled,
13312                                 // then the dojo.doc.selection is not the textarea, but the popup
13313                                 // var r = dojo.doc.selection.createRange();
13314                                 // hack to get IE 6 to play nice. What a POS browser.
13315                                 var tr = dojo.doc.selection.createRange().duplicate();
13316                                 var ntr = element.createTextRange();
13317                                 tr.move("character",0);
13318                                 ntr.move("character",0);
13319                                 try{
13320                                         // If control doesnt have focus, you get an exception.
13321                                         // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
13322                                         // There appears to be no workaround for this - googled for quite a while.
13323                                         ntr.setEndPoint("EndToEnd", tr);
13324                                         pos = String(ntr.text).replace(/\r/g,"").length;
13325                                 }catch(e){
13326                                         // If focus has shifted, 0 is fine for caret pos.
13327                                 }
13328                         }
13329                         return pos;
13330                 },
13331
13332                 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
13333                         location = parseInt(location);
13334                         dijit.selectInputText(element, location, location);
13335                 },
13336
13337                 _setDisabledAttr: function(/*Boolean*/ value){
13338                         // Additional code to set disabled state of ComboBox node.
13339                         // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
13340                         this.inherited(arguments);
13341                         dijit.setWaiState(this.comboNode, "disabled", value);
13342                 },
13343
13344                 _abortQuery: function(){
13345                         // stop in-progress query
13346                         if(this.searchTimer){
13347                                 clearTimeout(this.searchTimer);
13348                                 this.searchTimer = null;
13349                         }
13350                         if(this._fetchHandle){
13351                                 if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
13352                                 this._fetchHandle = null;
13353                         }
13354                 },
13355
13356                 _onInput: function(/*Event*/ evt){
13357                         // summary:
13358                         //              Handles paste events
13359                         if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
13360                                 this.searchTimer = setTimeout(dojo.hitch(this, function(){
13361                                         this._onKeyPress({charOrCode: 229}); // fake IME key to cause a search
13362                                 }), 100); // long delay that will probably be preempted by keyboard input
13363                         }
13364                         this.inherited(arguments);
13365                 },
13366
13367                 _onKeyPress: function(/*Event*/ evt){
13368                         // summary:
13369                         //              Handles keyboard events
13370                         var key = evt.charOrCode;
13371                         // except for cutting/pasting case - ctrl + x/v
13372                         if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
13373                                 return; // throw out weird key combinations and spurious events
13374                         }
13375                         var doSearch = false;
13376                         var searchFunction = "_startSearchFromInput";
13377                         var pw = this._popupWidget;
13378                         var dk = dojo.keys;
13379                         var highlighted = null;
13380                         this._prev_key_backspace = false;
13381                         this._abortQuery();
13382                         if(this._isShowingNow){
13383                                 pw.handleKey(key);
13384                                 highlighted = pw.getHighlightedOption();
13385                         }
13386                         switch(key){
13387                                 case dk.PAGE_DOWN:
13388                                 case dk.DOWN_ARROW:
13389                                 case dk.PAGE_UP:
13390                                 case dk.UP_ARROW:
13391                                         if(!this._isShowingNow){
13392                                                 doSearch = true;
13393                                                 searchFunction = "_startSearchAll";
13394                                         }else{
13395                                                 this._announceOption(highlighted);
13396                                         }
13397                                         dojo.stopEvent(evt);
13398                                         break;
13399
13400                                 case dk.ENTER:
13401                                         // prevent submitting form if user presses enter. Also
13402                                         // prevent accepting the value if either Next or Previous
13403                                         // are selected
13404                                         if(highlighted){
13405                                                 // only stop event on prev/next
13406                                                 if(highlighted == pw.nextButton){
13407                                                         this._nextSearch(1);
13408                                                         dojo.stopEvent(evt);
13409                                                         break;
13410                                                 }else if(highlighted == pw.previousButton){
13411                                                         this._nextSearch(-1);
13412                                                         dojo.stopEvent(evt);
13413                                                         break;
13414                                                 }
13415                                         }else{
13416                                                 // Update 'value' (ex: KY) according to currently displayed text
13417                                                 this._setBlurValue(); // set value if needed
13418                                                 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
13419                                         }
13420                                         // default case:
13421                                         // prevent submit, but allow event to bubble
13422                                         evt.preventDefault();
13423                                         // fall through
13424
13425                                 case dk.TAB:
13426                                         var newvalue = this.get('displayedValue');
13427                                         //      if the user had More Choices selected fall into the
13428                                         //      _onBlur handler
13429                                         if(pw && (
13430                                                 newvalue == pw._messages["previousMessage"] ||
13431                                                 newvalue == pw._messages["nextMessage"])
13432                                         ){
13433                                                 break;
13434                                         }
13435                                         if(highlighted){
13436                                                 this._selectOption();
13437                                         }
13438                                         if(this._isShowingNow){
13439                                                 this._lastQuery = null; // in case results come back later
13440                                                 this._hideResultList();
13441                                         }
13442                                         break;
13443
13444                                 case ' ':
13445                                         if(highlighted){
13446                                                 dojo.stopEvent(evt);
13447                                                 this._selectOption();
13448                                                 this._hideResultList();
13449                                         }else{
13450                                                 doSearch = true;
13451                                         }
13452                                         break;
13453
13454                                 case dk.ESCAPE:
13455                                         if(this._isShowingNow){
13456                                                 dojo.stopEvent(evt);
13457                                                 this._hideResultList();
13458                                         }
13459                                         break;
13460
13461                                 case dk.DELETE:
13462                                 case dk.BACKSPACE:
13463                                         this._prev_key_backspace = true;
13464                                         doSearch = true;
13465                                         break;
13466
13467                                 default:
13468                                         // Non char keys (F1-F12 etc..)  shouldn't open list.
13469                                         // Ascii characters and IME input (Chinese, Japanese etc.) should.
13470                                         // On IE and safari, IME input produces keycode == 229, and we simulate
13471                                         // it on firefox by attaching to compositionend event (see compositionend method)
13472                                         doSearch = typeof key == 'string' || key == 229;
13473                         }
13474                         if(doSearch){
13475                                 // need to wait a tad before start search so that the event
13476                                 // bubbles through DOM and we have value visible
13477                                 this.item = undefined; // undefined means item needs to be set
13478                                 this.searchTimer = setTimeout(dojo.hitch(this, searchFunction),1);
13479                         }
13480                 },
13481
13482                 _autoCompleteText: function(/*String*/ text){
13483                         // summary:
13484                         //              Fill in the textbox with the first item from the drop down
13485                         //              list, and highlight the characters that were
13486                         //              auto-completed. For example, if user typed "CA" and the
13487                         //              drop down list appeared, the textbox would be changed to
13488                         //              "California" and "ifornia" would be highlighted.
13489
13490                         var fn = this.focusNode;
13491
13492                         // IE7: clear selection so next highlight works all the time
13493                         dijit.selectInputText(fn, fn.value.length);
13494                         // does text autoComplete the value in the textbox?
13495                         var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
13496                         if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
13497                                 var cpos = this._getCaretPos(fn);
13498                                 // only try to extend if we added the last character at the end of the input
13499                                 if((cpos+1) > fn.value.length){
13500                                         // only add to input node as we would overwrite Capitalisation of chars
13501                                         // actually, that is ok
13502                                         fn.value = text;//.substr(cpos);
13503                                         // visually highlight the autocompleted characters
13504                                         dijit.selectInputText(fn, cpos);
13505                                 }
13506                         }else{
13507                                 // text does not autoComplete; replace the whole value and highlight
13508                                 fn.value = text;
13509                                 dijit.selectInputText(fn);
13510                         }
13511                 },
13512
13513                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
13514                         this._fetchHandle = null;
13515                         if(     this.disabled ||
13516                                 this.readOnly ||
13517                                 (dataObject.query[this.searchAttr] != this._lastQuery)
13518                         ){
13519                                 return;
13520                         }
13521                         this._popupWidget.clearResultList();
13522                         if(!results.length && !this._maxOptions){ // this condition needs to match !this._isvalid set in FilteringSelect::_openResultList
13523                                 this._hideResultList();
13524                                 return;
13525                         }
13526
13527
13528                         // Fill in the textbox with the first item from the drop down list,
13529                         // and highlight the characters that were auto-completed. For
13530                         // example, if user typed "CA" and the drop down list appeared, the
13531                         // textbox would be changed to "California" and "ifornia" would be
13532                         // highlighted.
13533
13534                         dataObject._maxOptions = this._maxOptions;
13535                         var nodes = this._popupWidget.createOptions(
13536                                 results,
13537                                 dataObject,
13538                                 dojo.hitch(this, "_getMenuLabelFromItem")
13539                         );
13540
13541                         // show our list (only if we have content, else nothing)
13542                         this._showResultList();
13543
13544                         // #4091:
13545                         //              tell the screen reader that the paging callback finished by
13546                         //              shouting the next choice
13547                         if(dataObject.direction){
13548                                 if(1 == dataObject.direction){
13549                                         this._popupWidget.highlightFirstOption();
13550                                 }else if(-1 == dataObject.direction){
13551                                         this._popupWidget.highlightLastOption();
13552                                 }
13553                                 this._announceOption(this._popupWidget.getHighlightedOption());
13554                         }else if(this.autoComplete && !this._prev_key_backspace /*&& !dataObject.direction*/
13555                                 // when the user clicks the arrow button to show the full list,
13556                                 // startSearch looks for "*".
13557                                 // it does not make sense to autocomplete
13558                                 // if they are just previewing the options available.
13559                                 && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
13560                                         this._announceOption(nodes[1]); // 1st real item
13561                         }
13562                 },
13563
13564                 _showResultList: function(){
13565                         this._hideResultList();
13566                         // hide the tooltip
13567                         this.displayMessage("");
13568
13569                         // Position the list and if it's too big to fit on the screen then
13570                         // size it to the maximum possible height
13571                         // Our dear friend IE doesnt take max-height so we need to
13572                         // calculate that on our own every time
13573
13574                         // TODO: want to redo this, see
13575                         //              http://trac.dojotoolkit.org/ticket/3272
13576                         //      and
13577                         //              http://trac.dojotoolkit.org/ticket/4108
13578
13579
13580                         // natural size of the list has changed, so erase old
13581                         // width/height settings, which were hardcoded in a previous
13582                         // call to this function (via dojo.marginBox() call)
13583                         dojo.style(this._popupWidget.domNode, {width: "", height: ""});
13584
13585                         var best = this.open();
13586                         // #3212:
13587                         //              only set auto scroll bars if necessary prevents issues with
13588                         //              scroll bars appearing when they shouldn't when node is made
13589                         //              wider (fractional pixels cause this)
13590                         var popupbox = dojo.marginBox(this._popupWidget.domNode);
13591                         this._popupWidget.domNode.style.overflow =
13592                                 ((best.h == popupbox.h) && (best.w == popupbox.w)) ? "hidden" : "auto";
13593                         // #4134:
13594                         //              borrow TextArea scrollbar test so content isn't covered by
13595                         //              scrollbar and horizontal scrollbar doesn't appear
13596                         var newwidth = best.w;
13597                         if(best.h < this._popupWidget.domNode.scrollHeight){
13598                                 newwidth += 16;
13599                         }
13600                         dojo.marginBox(this._popupWidget.domNode, {
13601                                 h: best.h,
13602                                 w: Math.max(newwidth, this.domNode.offsetWidth)
13603                         });
13604                         
13605                         // If we increased the width of drop down to match the width of ComboBox.domNode,
13606                         // then need to reposition the drop down (wrapper) so (all of) the drop down still
13607                         // appears underneath the ComboBox.domNode
13608                         if(newwidth < this.domNode.offsetWidth){
13609                                 this._popupWidget.domNode.parentNode.style.left = dojo.position(this.domNode, true).x + "px";
13610                         }
13611
13612                         dijit.setWaiState(this.comboNode, "expanded", "true");
13613                 },
13614
13615                 _hideResultList: function(){
13616                         this._abortQuery();
13617                         if(this._isShowingNow){
13618                                 dijit.popup.close(this._popupWidget);
13619                                 this._isShowingNow=false;
13620                                 dijit.setWaiState(this.comboNode, "expanded", "false");
13621                                 dijit.removeWaiState(this.focusNode,"activedescendant");
13622                         }
13623                 },
13624
13625                 _setBlurValue: function(){
13626                         // if the user clicks away from the textbox OR tabs away, set the
13627                         // value to the textbox value
13628                         // #4617:
13629                         //              if value is now more choices or previous choices, revert
13630                         //              the value
13631                         var newvalue = this.get('displayedValue');
13632                         var pw = this._popupWidget;
13633                         if(pw && (
13634                                 newvalue == pw._messages["previousMessage"] ||
13635                                 newvalue == pw._messages["nextMessage"]
13636                                 )
13637                         ){
13638                                 this._setValueAttr(this._lastValueReported, true);
13639                         }else if(typeof this.item == "undefined"){
13640                                 // Update 'value' (ex: KY) according to currently displayed text
13641                                 this.item = null;
13642                                 this.set('displayedValue', newvalue);
13643                         }else{
13644                                 if(this.value != this._lastValueReported){
13645                                         dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
13646                                 }
13647                                 this._refreshState();
13648                         }
13649                 },
13650
13651                 _onBlur: function(){
13652                         // summary:
13653                         //              Called magically when focus has shifted away from this widget and it's drop down
13654                         this._hideResultList();
13655                         this.inherited(arguments);
13656                 },
13657
13658                 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
13659                         // summary:
13660                         //              Set the displayed valued in the input box, and the hidden value
13661                         //              that gets submitted, based on a dojo.data store item.
13662                         // description:
13663                         //              Users shouldn't call this function; they should be calling
13664                         //              attr('item', value)
13665                         // tags:
13666                         //              private
13667                         if(!displayedValue){ displayedValue = this.labelFunc(item, this.store); }
13668                         this.value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
13669                         this.item = item;
13670                         dijit.form.ComboBox.superclass._setValueAttr.call(this, this.value, priorityChange, displayedValue);
13671                 },
13672
13673                 _announceOption: function(/*Node*/ node){
13674                         // summary:
13675                         //              a11y code that puts the highlighted option in the textbox.
13676                         //              This way screen readers will know what is happening in the
13677                         //              menu.
13678
13679                         if(!node){
13680                                 return;
13681                         }
13682                         // pull the text value from the item attached to the DOM node
13683                         var newValue;
13684                         if(node == this._popupWidget.nextButton ||
13685                                 node == this._popupWidget.previousButton){
13686                                 newValue = node.innerHTML;
13687                                 this.item = undefined;
13688                                 this.value = '';
13689                         }else{
13690                                 newValue = this.labelFunc(node.item, this.store);
13691                                 this.set('item', node.item, false, newValue);
13692                         }
13693                         // get the text that the user manually entered (cut off autocompleted text)
13694                         this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
13695                         // set up ARIA activedescendant
13696                         dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
13697                         // autocomplete the rest of the option to announce change
13698                         this._autoCompleteText(newValue);
13699                 },
13700
13701                 _selectOption: function(/*Event*/ evt){
13702                         // summary:
13703                         //              Menu callback function, called when an item in the menu is selected.
13704                         if(evt){
13705                                 this._announceOption(evt.target);
13706                         }
13707                         this._hideResultList();
13708                         this._setCaretPos(this.focusNode, this.focusNode.value.length);
13709                         dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
13710                 },
13711
13712                 _onArrowMouseDown: function(evt){
13713                         // summary:
13714                         //              Callback when arrow is clicked
13715                         if(this.disabled || this.readOnly){
13716                                 return;
13717                         }
13718                         dojo.stopEvent(evt);
13719                         this.focus();
13720                         if(this._isShowingNow){
13721                                 this._hideResultList();
13722                         }else{
13723                                 // forces full population of results, if they click
13724                                 // on the arrow it means they want to see more options
13725                                 this._startSearchAll();
13726                         }
13727                 },
13728
13729                 _startSearchAll: function(){
13730                         this._startSearch('');
13731                 },
13732
13733                 _startSearchFromInput: function(){
13734                         this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
13735                 },
13736
13737                 _getQueryString: function(/*String*/ text){
13738                         return dojo.string.substitute(this.queryExpr, [text]);
13739                 },
13740
13741                 _startSearch: function(/*String*/ key){
13742                         if(!this._popupWidget){
13743                                 var popupId = this.id + "_popup";
13744                                 this._popupWidget = new dijit.form._ComboBoxMenu({
13745                                         onChange: dojo.hitch(this, this._selectOption),
13746                                         id: popupId,
13747                                         dir: this.dir
13748                                 });
13749                                 dijit.removeWaiState(this.focusNode,"activedescendant");
13750                                 dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
13751                         }
13752                         // create a new query to prevent accidentally querying for a hidden
13753                         // value from FilteringSelect's keyField
13754                         var query = dojo.clone(this.query); // #5970
13755                         this._lastInput = key; // Store exactly what was entered by the user.
13756                         this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
13757                         // #5970: set _lastQuery, *then* start the timeout
13758                         // otherwise, if the user types and the last query returns before the timeout,
13759                         // _lastQuery won't be set and their input gets rewritten
13760                         this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
13761                                 this.searchTimer = null;
13762                                 var fetch = {
13763                                         queryOptions: {
13764                                                 ignoreCase: this.ignoreCase,
13765                                                 deep: true
13766                                         },
13767                                         query: query,
13768                                         onBegin: dojo.hitch(this, "_setMaxOptions"),
13769                                         onComplete: dojo.hitch(this, "_openResultList"),
13770                                         onError: function(errText){
13771                                                 _this._fetchHandle = null;
13772                                                 console.error('dijit.form.ComboBox: ' + errText);
13773                                                 dojo.hitch(_this, "_hideResultList")();
13774                                         },
13775                                         start: 0,
13776                                         count: this.pageSize
13777                                 };
13778                                 dojo.mixin(fetch, _this.fetchProperties);
13779                                 this._fetchHandle = _this.store.fetch(fetch);
13780
13781                                 var nextSearch = function(dataObject, direction){
13782                                         dataObject.start += dataObject.count*direction;
13783                                         // #4091:
13784                                         //              tell callback the direction of the paging so the screen
13785                                         //              reader knows which menu option to shout
13786                                         dataObject.direction = direction;
13787                                         this._fetchHandle = this.store.fetch(dataObject);
13788                                 };
13789                                 this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
13790                         }, query, this), this.searchDelay);
13791                 },
13792
13793                 _setMaxOptions: function(size, request){
13794                          this._maxOptions = size;
13795                 },
13796
13797                 _getValueField: function(){
13798                         // summmary:
13799                         //              Helper for postMixInProperties() to set this.value based on data inlined into the markup.
13800                         //              Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
13801                         return this.searchAttr;
13802                 },
13803
13804                 /////////////// Event handlers /////////////////////
13805
13806                 // FIXME: For 2.0, rename to "_compositionEnd"
13807                 compositionend: function(/*Event*/ evt){
13808                         // summary:
13809                         //              When inputting characters using an input method, such as
13810                         //              Asian languages, it will generate this event instead of
13811                         //              onKeyDown event.
13812                         //              Note: this event is only triggered in FF (not in IE/safari)
13813                         // tags:
13814                         //              private
13815
13816                         // 229 is the code produced by IE and safari while pressing keys during
13817                         // IME input mode
13818                         this._onKeyPress({charOrCode: 229});
13819                 },
13820
13821                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
13822
13823                 constructor: function(){
13824                         this.query={};
13825                         this.fetchProperties={};
13826                 },
13827
13828                 postMixInProperties: function(){
13829                         if(!this.store){
13830                                 var srcNodeRef = this.srcNodeRef;
13831
13832                                 // if user didn't specify store, then assume there are option tags
13833                                 this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
13834
13835                                 // if there is no value set and there is an option list, set
13836                                 // the value to the first value to be consistent with native
13837                                 // Select
13838
13839                                 // Firefox and Safari set value
13840                                 // IE6 and Opera set selectedIndex, which is automatically set
13841                                 // by the selected attribute of an option tag
13842                                 // IE6 does not set value, Opera sets value = selectedIndex
13843                                 if(!("value" in this.params)){
13844                                         var item = this.store.fetchSelectedItem();
13845                                         if(item){
13846                                                 var valueField = this._getValueField();
13847                                                 this.value = valueField != this.searchAttr? this.store.getValue(item, valueField) : this.labelFunc(item, this.store);
13848                                         }
13849                                 }
13850                         }
13851                         this.inherited(arguments);
13852                 },
13853
13854                 postCreate: function(){
13855                         // summary:
13856                         //              Subclasses must call this method from their postCreate() methods
13857                         // tags:
13858                         //              protected
13859
13860                         if(!this.hasDownArrow){
13861                                 this.downArrowNode.style.display = "none";
13862                         }
13863
13864                         // find any associated label element and add to ComboBox node.
13865                         var label=dojo.query('label[for="'+this.id+'"]');
13866                         if(label.length){
13867                                 label[0].id = (this.id+"_label");
13868                                 var cn=this.comboNode;
13869                                 dijit.setWaiState(cn, "labelledby", label[0].id);
13870
13871                         }
13872                         this.inherited(arguments);
13873                 },
13874
13875                 uninitialize: function(){
13876                         if(this._popupWidget && !this._popupWidget._destroyed){
13877                                 this._hideResultList();
13878                                 this._popupWidget.destroy();
13879                         }
13880                         this.inherited(arguments);
13881                 },
13882
13883                 _getMenuLabelFromItem: function(/*Item*/ item){
13884                         var label = this.labelAttr? this.store.getValue(item, this.labelAttr) : this.labelFunc(item, this.store);
13885                         var labelType = this.labelType;
13886                         // If labelType is not "text" we don't want to screw any markup ot whatever.
13887                         if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
13888                                 label = this.doHighlight(label, this._escapeHtml(this._lastInput));
13889                                 labelType = "html";
13890                         }
13891                         return {html: labelType == "html", label: label};
13892                 },
13893
13894                 doHighlight: function(/*String*/label, /*String*/find){
13895                         // summary:
13896                         //              Highlights the string entered by the user in the menu.  By default this
13897                         //              highlights the first occurence found. Override this method
13898                         //              to implement your custom highlighing.
13899                         // tags:
13900                         //              protected
13901
13902                         // Add greedy when this.highlightMatch == "all"
13903                         var modifiers = "i"+(this.highlightMatch == "all"?"g":"");
13904                         var escapedLabel = this._escapeHtml(label);
13905                         find = dojo.regexp.escapeString(find); // escape regexp special chars
13906                         var ret = escapedLabel.replace(new RegExp("(^|\\s)("+ find +")", modifiers),
13907                                         '$1<span class="dijitComboBoxHighlightMatch">$2</span>');
13908                         return ret;// returns String, (almost) valid HTML (entities encoded)
13909                 },
13910
13911                 _escapeHtml: function(/*string*/str){
13912                         // TODO Should become dojo.html.entities(), when exists use instead
13913                         // summary:
13914                         //              Adds escape sequences for special characters in XML: &<>"'
13915                         str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
13916                                 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
13917                         return str; // string
13918                 },
13919
13920                 open: function(){
13921                         // summary:
13922                         //              Opens the drop down menu.  TODO: rename to _open.
13923                         // tags:
13924                         //              private
13925                         this._isShowingNow=true;
13926                         return dijit.popup.open({
13927                                 popup: this._popupWidget,
13928                                 around: this.domNode,
13929                                 parent: this
13930                         });
13931                 },
13932
13933                 reset: function(){
13934                         // Overrides the _FormWidget.reset().
13935                         // Additionally reset the .item (to clean up).
13936                         this.item = null;
13937                         this.inherited(arguments);
13938                 },
13939
13940                 labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
13941                         // summary:
13942                         //              Computes the label to display based on the dojo.data store item.
13943                         // returns:
13944                         //              The label that the ComboBox should display
13945                         // tags:
13946                         //              private
13947
13948                         // Use toString() because XMLStore returns an XMLItem whereas this
13949                         // method is expected to return a String (#9354)
13950                         return store.getValue(item, this.searchAttr).toString(); // String
13951                 }
13952         }
13953 );
13954
13955 dojo.declare(
13956         "dijit.form._ComboBoxMenu",
13957         [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
13958         {
13959                 // summary:
13960                 //              Focus-less menu for internal use in `dijit.form.ComboBox`
13961                 // tags:
13962                 //              private
13963
13964                 templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' tabIndex='-1' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
13965                                 +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' waiRole='option'></li>"
13966                                 +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' waiRole='option'></li>"
13967                         +"</ul>",
13968
13969                 // _messages: Object
13970                 //              Holds "next" and "previous" text for paging buttons on drop down
13971                 _messages: null,
13972                 
13973                 baseClass: "dijitComboBoxMenu",
13974
13975                 postMixInProperties: function(){
13976                         this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
13977                         this.inherited(arguments);
13978                 },
13979
13980                 _setValueAttr: function(/*Object*/ value){
13981                         this.value = value;
13982                         this.onChange(value);
13983                 },
13984
13985                 // stubs
13986                 onChange: function(/*Object*/ value){
13987                         // summary:
13988                         //              Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
13989                         //              Probably should be called onSelect.
13990                         // tags:
13991                         //              callback
13992                 },
13993                 onPage: function(/*Number*/ direction){
13994                         // summary:
13995                         //              Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
13996                         // tags:
13997                         //              callback
13998                 },
13999
14000                 postCreate: function(){
14001                         // fill in template with i18n messages
14002                         this.previousButton.innerHTML = this._messages["previousMessage"];
14003                         this.nextButton.innerHTML = this._messages["nextMessage"];
14004                         this.inherited(arguments);
14005                 },
14006
14007                 onClose: function(){
14008                         // summary:
14009                         //              Callback from dijit.popup code to this widget, notifying it that it closed
14010                         // tags:
14011                         //              private
14012                         this._blurOptionNode();
14013                 },
14014
14015                 _createOption: function(/*Object*/ item, labelFunc){
14016                         // summary:
14017                         //              Creates an option to appear on the popup menu subclassed by
14018                         //              `dijit.form.FilteringSelect`.
14019
14020                         var labelObject = labelFunc(item);
14021                         var menuitem = dojo.doc.createElement("li");
14022                         dijit.setWaiRole(menuitem, "option");
14023                         if(labelObject.html){
14024                                 menuitem.innerHTML = labelObject.label;
14025                         }else{
14026                                 menuitem.appendChild(
14027                                         dojo.doc.createTextNode(labelObject.label)
14028                                 );
14029                         }
14030                         // #3250: in blank options, assign a normal height
14031                         if(menuitem.innerHTML == ""){
14032                                 menuitem.innerHTML = "&nbsp;";
14033                         }
14034                         menuitem.item=item;
14035                         return menuitem;
14036                 },
14037
14038                 createOptions: function(results, dataObject, labelFunc){
14039                         // summary:
14040                         //              Fills in the items in the drop down list
14041                         // results:
14042                         //              Array of dojo.data items
14043                         // dataObject:
14044                         //              dojo.data store
14045                         // labelFunc:
14046                         //              Function to produce a label in the drop down list from a dojo.data item
14047
14048                         //this._dataObject=dataObject;
14049                         //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
14050                         // display "Previous . . ." button
14051                         this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
14052                         dojo.attr(this.previousButton, "id", this.id + "_prev");
14053                         // create options using _createOption function defined by parent
14054                         // ComboBox (or FilteringSelect) class
14055                         // #2309:
14056                         //              iterate over cache nondestructively
14057                         dojo.forEach(results, function(item, i){
14058                                 var menuitem = this._createOption(item, labelFunc);
14059                                 menuitem.className = "dijitReset dijitMenuItem" +
14060                                         (this.isLeftToRight() ? "" : " dijitMenuItemRtl");
14061                                 dojo.attr(menuitem, "id", this.id + i);
14062                                 this.domNode.insertBefore(menuitem, this.nextButton);
14063                         }, this);
14064                         // display "Next . . ." button
14065                         var displayMore = false;
14066                         //Try to determine if we should show 'more'...
14067                         if(dataObject._maxOptions && dataObject._maxOptions != -1){
14068                                 if((dataObject.start + dataObject.count) < dataObject._maxOptions){
14069                                         displayMore = true;
14070                                 }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
14071                                         //Weird return from a datastore, where a start + count > maxOptions
14072                                         // implies maxOptions isn't really valid and we have to go into faking it.
14073                                         //And more or less assume more if count == results.length
14074                                         displayMore = true;
14075                                 }
14076                         }else if(dataObject.count == results.length){
14077                                 //Don't know the size, so we do the best we can based off count alone.
14078                                 //So, if we have an exact match to count, assume more.
14079                                 displayMore = true;
14080                         }
14081
14082                         this.nextButton.style.display = displayMore ? "" : "none";
14083                         dojo.attr(this.nextButton,"id", this.id + "_next");
14084                         return this.domNode.childNodes;
14085                 },
14086
14087                 clearResultList: function(){
14088                         // summary:
14089                         //              Clears the entries in the drop down list, but of course keeps the previous and next buttons.
14090                         while(this.domNode.childNodes.length>2){
14091                                 this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
14092                         }
14093                 },
14094
14095                 _onMouseDown: function(/*Event*/ evt){
14096                         dojo.stopEvent(evt);
14097                 },
14098
14099                 _onMouseUp: function(/*Event*/ evt){
14100                         if(evt.target === this.domNode || !this._highlighted_option){
14101                                 return;
14102                         }else if(evt.target == this.previousButton){
14103                                 this.onPage(-1);
14104                         }else if(evt.target == this.nextButton){
14105                                 this.onPage(1);
14106                         }else{
14107                                 var tgt = evt.target;
14108                                 // while the clicked node is inside the div
14109                                 while(!tgt.item){
14110                                         // recurse to the top
14111                                         tgt = tgt.parentNode;
14112                                 }
14113                                 this._setValueAttr({ target: tgt }, true);
14114                         }
14115                 },
14116
14117                 _onMouseOver: function(/*Event*/ evt){
14118                         if(evt.target === this.domNode){ return; }
14119                         var tgt = evt.target;
14120                         if(!(tgt == this.previousButton || tgt == this.nextButton)){
14121                                 // while the clicked node is inside the div
14122                                 while(!tgt.item){
14123                                         // recurse to the top
14124                                         tgt = tgt.parentNode;
14125                                 }
14126                         }
14127                         this._focusOptionNode(tgt);
14128                 },
14129
14130                 _onMouseOut: function(/*Event*/ evt){
14131                         if(evt.target === this.domNode){ return; }
14132                         this._blurOptionNode();
14133                 },
14134
14135                 _focusOptionNode: function(/*DomNode*/ node){
14136                         // summary:
14137                         //              Does the actual highlight.
14138                         if(this._highlighted_option != node){
14139                                 this._blurOptionNode();
14140                                 this._highlighted_option = node;
14141                                 dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
14142                         }
14143                 },
14144
14145                 _blurOptionNode: function(){
14146                         // summary:
14147                         //              Removes highlight on highlighted option.
14148                         if(this._highlighted_option){
14149                                 dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
14150                                 this._highlighted_option = null;
14151                         }
14152                 },
14153
14154                 _highlightNextOption: function(){
14155                         // summary:
14156                         //              Highlight the item just below the current selection.
14157                         //              If nothing selected, highlight first option.
14158
14159                         // because each press of a button clears the menu,
14160                         // the highlighted option sometimes becomes detached from the menu!
14161                         // test to see if the option has a parent to see if this is the case.
14162                         if(!this.getHighlightedOption()){
14163                                 var fc = this.domNode.firstChild;
14164                                 this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
14165                         }else{
14166                                 var ns = this._highlighted_option.nextSibling;
14167                                 if(ns && ns.style.display != "none"){
14168                                         this._focusOptionNode(ns);
14169                                 }else{
14170                                         this.highlightFirstOption();
14171                                 }
14172                         }
14173                         // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
14174                         dojo.window.scrollIntoView(this._highlighted_option);
14175                 },
14176
14177                 highlightFirstOption: function(){
14178                         // summary:
14179                         //              Highlight the first real item in the list (not Previous Choices).
14180                         var first = this.domNode.firstChild;
14181                         var second = first.nextSibling;
14182                         this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
14183                         dojo.window.scrollIntoView(this._highlighted_option);
14184                 },
14185
14186                 highlightLastOption: function(){
14187                         // summary:
14188                         //              Highlight the last real item in the list (not More Choices).
14189                         this._focusOptionNode(this.domNode.lastChild.previousSibling);
14190                         dojo.window.scrollIntoView(this._highlighted_option);
14191                 },
14192
14193                 _highlightPrevOption: function(){
14194                         // summary:
14195                         //              Highlight the item just above the current selection.
14196                         //              If nothing selected, highlight last option (if
14197                         //              you select Previous and try to keep scrolling up the list).
14198                         if(!this.getHighlightedOption()){
14199                                 var lc = this.domNode.lastChild;
14200                                 this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
14201                         }else{
14202                                 var ps = this._highlighted_option.previousSibling;
14203                                 if(ps && ps.style.display != "none"){
14204                                         this._focusOptionNode(ps);
14205                                 }else{
14206                                         this.highlightLastOption();
14207                                 }
14208                         }
14209                         dojo.window.scrollIntoView(this._highlighted_option);
14210                 },
14211
14212                 _page: function(/*Boolean*/ up){
14213                         // summary:
14214                         //              Handles page-up and page-down keypresses
14215
14216                         var scrollamount = 0;
14217                         var oldscroll = this.domNode.scrollTop;
14218                         var height = dojo.style(this.domNode, "height");
14219                         // if no item is highlighted, highlight the first option
14220                         if(!this.getHighlightedOption()){
14221                                 this._highlightNextOption();
14222                         }
14223                         while(scrollamount<height){
14224                                 if(up){
14225                                         // stop at option 1
14226                                         if(!this.getHighlightedOption().previousSibling ||
14227                                                 this._highlighted_option.previousSibling.style.display == "none"){
14228                                                 break;
14229                                         }
14230                                         this._highlightPrevOption();
14231                                 }else{
14232                                         // stop at last option
14233                                         if(!this.getHighlightedOption().nextSibling ||
14234                                                 this._highlighted_option.nextSibling.style.display == "none"){
14235                                                 break;
14236                                         }
14237                                         this._highlightNextOption();
14238                                 }
14239                                 // going backwards
14240                                 var newscroll=this.domNode.scrollTop;
14241                                 scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
14242                                 oldscroll=newscroll;
14243                         }
14244                 },
14245
14246                 pageUp: function(){
14247                         // summary:
14248                         //              Handles pageup keypress.
14249                         //              TODO: just call _page directly from handleKey().
14250                         // tags:
14251                         //              private
14252                         this._page(true);
14253                 },
14254
14255                 pageDown: function(){
14256                         // summary:
14257                         //              Handles pagedown keypress.
14258                         //              TODO: just call _page directly from handleKey().
14259                         // tags:
14260                         //              private
14261                         this._page(false);
14262                 },
14263
14264                 getHighlightedOption: function(){
14265                         // summary:
14266                         //              Returns the highlighted option.
14267                         var ho = this._highlighted_option;
14268                         return (ho && ho.parentNode) ? ho : null;
14269                 },
14270
14271                 handleKey: function(key){
14272                         switch(key){
14273                                 case dojo.keys.DOWN_ARROW:
14274                                         this._highlightNextOption();
14275                                         break;
14276                                 case dojo.keys.PAGE_DOWN:
14277                                         this.pageDown();
14278                                         break;
14279                                 case dojo.keys.UP_ARROW:
14280                                         this._highlightPrevOption();
14281                                         break;
14282                                 case dojo.keys.PAGE_UP:
14283                                         this.pageUp();
14284                                         break;
14285                         }
14286                 }
14287         }
14288 );
14289
14290 dojo.declare(
14291         "dijit.form.ComboBox",
14292         [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
14293         {
14294                 // summary:
14295                 //              Auto-completing text box, and base class for dijit.form.FilteringSelect.
14296                 //
14297                 // description:
14298                 //              The drop down box's values are populated from an class called
14299                 //              a data provider, which returns a list of values based on the characters
14300                 //              that the user has typed into the input box.
14301                 //              If OPTION tags are used as the data provider via markup,
14302                 //              then the OPTION tag's child text node is used as the widget value
14303                 //              when selected.  The OPTION tag's value attribute is ignored.
14304                 //              To set the default value when using OPTION tags, specify the selected
14305                 //              attribute on 1 of the child OPTION tags.
14306                 //
14307                 //              Some of the options to the ComboBox are actually arguments to the data
14308                 //              provider.
14309
14310                 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
14311                         // summary:
14312                         //              Hook so attr('value', value) works.
14313                         // description:
14314                         //              Sets the value of the select.
14315                         this.item = null; // value not looked up in store
14316                         if(!value){ value = ''; } // null translates to blank
14317                         dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
14318                 }
14319         }
14320 );
14321
14322 dojo.declare("dijit.form._ComboBoxDataStore", null, {
14323         // summary:
14324         //              Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
14325         //
14326         // description:
14327         //              Provides a store for inlined data like:
14328         //
14329         //      |       <select>
14330         //      |               <option value="AL">Alabama</option>
14331         //      |               ...
14332         //
14333         //              Actually. just implements the subset of dojo.data.Read/Notification
14334         //              needed for ComboBox and FilteringSelect to work.
14335         //
14336         //              Note that an item is just a pointer to the <option> DomNode.
14337
14338         constructor: function( /*DomNode*/ root){
14339                 this.root = root;
14340                 if(root.tagName != "SELECT" && root.firstChild){
14341                         root = dojo.query("select", root);
14342                         if(root.length > 0){ // SELECT is a child of srcNodeRef
14343                                 root = root[0];
14344                         }else{ // no select, so create 1 to parent the option tags to define selectedIndex
14345                                 this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
14346                                 root = this.root.firstChild;
14347                         }
14348                         this.root = root;
14349                 }
14350                 dojo.query("> option", root).forEach(function(node){
14351                         //      TODO: this was added in #3858 but unclear why/if it's needed;  doesn't seem to be.
14352                         //      If it is needed then can we just hide the select itself instead?
14353                         //node.style.display="none";
14354                         node.innerHTML = dojo.trim(node.innerHTML);
14355                 });
14356
14357         },
14358
14359         getValue: function(     /* item */ item,
14360                                                 /* attribute-name-string */ attribute,
14361                                                 /* value? */ defaultValue){
14362                 return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
14363         },
14364
14365         isItemLoaded: function(/* anything */ something){
14366                 return true;
14367         },
14368
14369         getFeatures: function(){
14370                 return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
14371         },
14372
14373         _fetchItems: function(  /* Object */ args,
14374                                                         /* Function */ findCallback,
14375                                                         /* Function */ errorCallback){
14376                 // summary:
14377                 //              See dojo.data.util.simpleFetch.fetch()
14378                 if(!args.query){ args.query = {}; }
14379                 if(!args.query.name){ args.query.name = ""; }
14380                 if(!args.queryOptions){ args.queryOptions = {}; }
14381                 var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
14382                         items = dojo.query("> option", this.root).filter(function(option){
14383                                 return (option.innerText || option.textContent || '').match(matcher);
14384                         } );
14385                 if(args.sort){
14386                         items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
14387                 }
14388                 findCallback(items, args);
14389         },
14390
14391         close: function(/*dojo.data.api.Request || args || null */ request){
14392                 return;
14393         },
14394
14395         getLabel: function(/* item */ item){
14396                 return item.innerHTML;
14397         },
14398
14399         getIdentity: function(/* item */ item){
14400                 return dojo.attr(item, "value");
14401         },
14402
14403         fetchItemByIdentity: function(/* Object */ args){
14404                 // summary:
14405                 //              Given the identity of an item, this method returns the item that has
14406                 //              that identity through the onItem callback.
14407                 //              Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
14408                 //
14409                 // description:
14410                 //              Given arguments like:
14411                 //
14412                 //      |               {identity: "CA", onItem: function(item){...}
14413                 //
14414                 //              Call `onItem()` with the DOM node `<option value="CA">California</option>`
14415                 var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
14416                 args.onItem(item);
14417         },
14418
14419         fetchSelectedItem: function(){
14420                 // summary:
14421                 //              Get the option marked as selected, like `<option selected>`.
14422                 //              Not part of dojo.data API.
14423                 var root = this.root,
14424                         si = root.selectedIndex;
14425                 return typeof si == "number"
14426                         ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
14427                         : null; // dojo.data.Item
14428         }
14429 });
14430 //Mix in the simple fetch implementation to this class.
14431 dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
14432
14433 }
14434
14435 if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14436 dojo._hasResource["dijit.form.FilteringSelect"] = true;
14437 dojo.provide("dijit.form.FilteringSelect");
14438
14439
14440
14441 dojo.declare(
14442         "dijit.form.FilteringSelect",
14443         [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
14444         {
14445                 // summary:
14446                 //              An enhanced version of the HTML SELECT tag, populated dynamically
14447                 //
14448                 // description:
14449                 //              An enhanced version of the HTML SELECT tag, populated dynamically. It works
14450                 //              very nicely with very large data sets because it can load and page data as needed.
14451                 //              It also resembles ComboBox, but does not allow values outside of the provided ones.
14452                 //              If OPTION tags are used as the data provider via markup, then the
14453                 //              OPTION tag's child text node is used as the displayed value when selected
14454                 //              while the OPTION tag's value attribute is used as the widget value on form submit.
14455                 //              To set the default value when using OPTION tags, specify the selected
14456                 //              attribute on 1 of the child OPTION tags.
14457                 //
14458                 //              Similar features:
14459                 //                      - There is a drop down list of possible values.
14460                 //                      - You can only enter a value from the drop down list.  (You can't
14461                 //                              enter an arbitrary value.)
14462                 //                      - The value submitted with the form is the hidden value (ex: CA),
14463                 //                              not the displayed value a.k.a. label (ex: California)
14464                 //
14465                 //              Enhancements over plain HTML version:
14466                 //                      - If you type in some text then it will filter down the list of
14467                 //                              possible values in the drop down list.
14468                 //                      - List can be specified either as a static list or via a javascript
14469                 //                              function (that can get the list from a server)
14470
14471                 _isvalid: true,
14472
14473                 // required: Boolean
14474                 //              True (default) if user is required to enter a value into this field.
14475                 required: true,
14476
14477                 _lastDisplayedValue: "",
14478
14479                 isValid: function(){
14480                         // Overrides ValidationTextBox.isValid()
14481                         return this._isvalid || (!this.required && this.get('displayedValue') == ""); // #5974
14482                 },
14483
14484                 _refreshState: function(){
14485                         if(!this.searchTimer){ // state will be refreshed after results are returned
14486                                 this.inherited(arguments);
14487                         }
14488                 },
14489
14490                 _callbackSetLabel: function(    /*Array*/ result,
14491                                                 /*Object*/ dataObject,
14492                                                 /*Boolean?*/ priorityChange){
14493                         // summary:
14494                         //              Callback function that dynamically sets the label of the
14495                         //              ComboBox
14496
14497                         // setValue does a synchronous lookup,
14498                         // so it calls _callbackSetLabel directly,
14499                         // and so does not pass dataObject
14500                         // still need to test against _lastQuery in case it came too late
14501                         if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
14502                                 return;
14503                         }
14504                         if(!result.length){
14505                                 //#3268: do nothing on bad input
14506                                 //#3285: change CSS to indicate error
14507                                 this.valueNode.value = "";
14508                                 dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
14509                                 this._isvalid = false;
14510                                 this.validate(this._focused);
14511                                 this.item = null;
14512                         }else{
14513                                 this.set('item', result[0], priorityChange);
14514                         }
14515                 },
14516
14517                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
14518                         // Overrides ComboBox._openResultList()
14519
14520                         // #3285: tap into search callback to see if user's query resembles a match
14521                         if(dataObject.query[this.searchAttr] != this._lastQuery){
14522                                 return;
14523                         }
14524                         if(this.item === undefined){ // item == undefined for keyboard search
14525                                 this._isvalid = results.length != 0 || this._maxOptions != 0; // result.length==0 && maxOptions != 0 implies the nextChoices item selected but then the datastore returned 0 more entries
14526                                 this.validate(true);
14527                         }
14528                         dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
14529                 },
14530
14531                 _getValueAttr: function(){
14532                         // summary:
14533                         //              Hook for attr('value') to work.
14534
14535                         // don't get the textbox value but rather the previously set hidden value.
14536                         // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
14537                         return this.valueNode.value;
14538                 },
14539
14540                 _getValueField: function(){
14541                         // Overrides ComboBox._getValueField()
14542                         return "value";
14543                 },
14544
14545                 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
14546                         // summary:
14547                         //              Hook so attr('value', value) works.
14548                         // description:
14549                         //              Sets the value of the select.
14550                         //              Also sets the label to the corresponding value by reverse lookup.
14551                         if(!this._onChangeActive){ priorityChange = null; }
14552                         this._lastQuery = value;
14553
14554                         if(value === null || value === ''){
14555                                 this._setDisplayedValueAttr('', priorityChange);
14556                                 return;
14557                         }
14558
14559                         //#3347: fetchItemByIdentity if no keyAttr specified
14560                         var self = this;
14561                         this.store.fetchItemByIdentity({
14562                                 identity: value,
14563                                 onItem: function(item){
14564                                         self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
14565                                 }
14566                         });
14567                 },
14568
14569                 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
14570                         // summary:
14571                         //              Set the displayed valued in the input box, and the hidden value
14572                         //              that gets submitted, based on a dojo.data store item.
14573                         // description:
14574                         //              Users shouldn't call this function; they should be calling
14575                         //              attr('item', value)
14576                         // tags:
14577                         //              private
14578                         this._isvalid = true;
14579                         this.inherited(arguments);
14580                         this.valueNode.value = this.value;
14581                         this._lastDisplayedValue = this.textbox.value;
14582                 },
14583
14584                 _getDisplayQueryString: function(/*String*/ text){
14585                         return text.replace(/([\\\*\?])/g, "\\$1");
14586                 },
14587
14588                 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
14589                         // summary:
14590                         //              Hook so attr('displayedValue', label) works.
14591                         // description:
14592                         //              Sets textbox to display label. Also performs reverse lookup
14593                         //              to set the hidden value.
14594
14595                         // When this is called during initialization it'll ping the datastore
14596                         // for reverse lookup, and when that completes (after an XHR request)
14597                         // will call setValueAttr()... but that shouldn't trigger an onChange()
14598                         // event, even when it happens after creation has finished
14599                         if(!this._created){
14600                                 priorityChange = false;
14601                         }
14602
14603                         if(this.store){
14604                                 this._hideResultList();
14605                                 var query = dojo.clone(this.query); // #6196: populate query with user-specifics
14606                                 // escape meta characters of dojo.data.util.filter.patternToRegExp().
14607                                 this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
14608                                 // if the label is not valid, the callback will never set it,
14609                                 // so the last valid value will get the warning textbox set the
14610                                 // textbox value now so that the impending warning will make
14611                                 // sense to the user
14612                                 this.textbox.value = label;
14613                                 this._lastDisplayedValue = label;
14614                                 var _this = this;
14615                                 var fetch = {
14616                                         query: query,
14617                                         queryOptions: {
14618                                                 ignoreCase: this.ignoreCase,
14619                                                 deep: true
14620                                         },
14621                                         onComplete: function(result, dataObject){
14622                                                 _this._fetchHandle = null;
14623                                                 dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
14624                                         },
14625                                         onError: function(errText){
14626                                                 _this._fetchHandle = null;
14627                                                 console.error('dijit.form.FilteringSelect: ' + errText);
14628                                                 dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
14629                                         }
14630                                 };
14631                                 dojo.mixin(fetch, this.fetchProperties);
14632                                 this._fetchHandle = this.store.fetch(fetch);
14633                         }
14634                 },
14635
14636                 postMixInProperties: function(){
14637                         this.inherited(arguments);
14638                         this._isvalid = !this.required;
14639                 },
14640
14641                 undo: function(){
14642                         this.set('displayedValue', this._lastDisplayedValue);
14643                 }
14644         }
14645 );
14646
14647 }
14648
14649 if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14650 dojo._hasResource["dijit.form.Form"] = true;
14651 dojo.provide("dijit.form.Form");
14652
14653
14654
14655
14656
14657 dojo.declare(
14658         "dijit.form.Form",
14659         [dijit._Widget, dijit._Templated, dijit.form._FormMixin],
14660         {
14661                 // summary:
14662                 //              Widget corresponding to HTML form tag, for validation and serialization
14663                 //
14664                 // example:
14665                 //      |       <form dojoType="dijit.form.Form" id="myForm">
14666                 //      |               Name: <input type="text" name="name" />
14667                 //      |       </form>
14668                 //      |       myObj = {name: "John Doe"};
14669                 //      |       dijit.byId('myForm').set('value', myObj);
14670                 //      |
14671                 //      |       myObj=dijit.byId('myForm').get('value');
14672
14673                 // HTML <FORM> attributes
14674
14675                 // name: String?
14676                 //              Name of form for scripting.
14677                 name: "",
14678
14679                 // action: String?
14680                 //              Server-side form handler.
14681                 action: "",
14682
14683                 // method: String?
14684                 //              HTTP method used to submit the form, either "GET" or "POST".
14685                 method: "",
14686
14687                 // encType: String?
14688                 //              Encoding type for the form, ex: application/x-www-form-urlencoded.
14689                 encType: "",
14690
14691                 // accept-charset: String?
14692                 //              List of supported charsets.
14693                 "accept-charset": "",
14694
14695                 // accept: String?
14696                 //              List of MIME types for file upload.
14697                 accept: "",
14698
14699                 // target: String?
14700                 //              Target frame for the document to be opened in.
14701                 target: "",
14702
14703                 templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
14704
14705                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
14706                         action: "",
14707                         method: "",
14708                         encType: "",
14709                         "accept-charset": "",
14710                         accept: "",
14711                         target: ""
14712                 }),
14713
14714                 postMixInProperties: function(){
14715                         // Setup name=foo string to be referenced from the template (but only if a name has been specified)
14716                         // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
14717                         this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
14718                         this.inherited(arguments);
14719                 },
14720
14721                 execute: function(/*Object*/ formContents){
14722                         // summary:
14723                         //              Deprecated: use submit()
14724                         // tags:
14725                         //              deprecated
14726                 },
14727
14728                 onExecute: function(){
14729                         // summary:
14730                         //              Deprecated: use onSubmit()
14731                         // tags:
14732                         //              deprecated
14733                 },
14734
14735                 _setEncTypeAttr: function(/*String*/ value){
14736                         this.encType = value;
14737                         dojo.attr(this.domNode, "encType", value);
14738                         if(dojo.isIE){ this.domNode.encoding = value; }
14739                 },
14740
14741                 postCreate: function(){
14742                         // IE tries to hide encType
14743                         // TODO: this code should be in parser, not here.
14744                         if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
14745                                 var item = this.srcNodeRef.attributes.getNamedItem('encType');
14746                                 if(item && !item.specified && (typeof item.value == "string")){
14747                                         this.set('encType', item.value);
14748                                 }
14749                         }
14750                         this.inherited(arguments);
14751                 },
14752
14753                 reset: function(/*Event?*/ e){
14754                         // summary:
14755                         //              restores all widget values back to their init values,
14756                         //              calls onReset() which can cancel the reset by returning false
14757
14758                         // create fake event so we can know if preventDefault() is called
14759                         var faux = {
14760                                 returnValue: true, // the IE way
14761                                 preventDefault: function(){ // not IE
14762                                                         this.returnValue = false;
14763                                                 },
14764                                 stopPropagation: function(){}, 
14765                                 currentTarget: e ? e.target : this.domNode, 
14766                                 target: e ? e.target : this.domNode
14767                         };
14768                         // if return value is not exactly false, and haven't called preventDefault(), then reset
14769                         if(!(this.onReset(faux) === false) && faux.returnValue){
14770                                 this.inherited(arguments, []);
14771                         }
14772                 },
14773
14774                 onReset: function(/*Event?*/ e){
14775                         // summary:
14776                         //              Callback when user resets the form. This method is intended
14777                         //              to be over-ridden. When the `reset` method is called
14778                         //              programmatically, the return value from `onReset` is used
14779                         //              to compute whether or not resetting should proceed
14780                         // tags:
14781                         //              callback
14782                         return true; // Boolean
14783                 },
14784
14785                 _onReset: function(e){
14786                         this.reset(e);
14787                         dojo.stopEvent(e);
14788                         return false;
14789                 },
14790
14791                 _onSubmit: function(e){
14792                         var fp = dijit.form.Form.prototype;
14793                         // TODO: remove this if statement beginning with 2.0
14794                         if(this.execute != fp.execute || this.onExecute != fp.onExecute){
14795                                 dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
14796                                 this.onExecute();
14797                                 this.execute(this.getValues());
14798                         }
14799                         if(this.onSubmit(e) === false){ // only exactly false stops submit
14800                                 dojo.stopEvent(e);
14801                         }
14802                 },
14803
14804                 onSubmit: function(/*Event?*/e){
14805                         // summary:
14806                         //              Callback when user submits the form.
14807                         // description:
14808                         //              This method is intended to be over-ridden, but by default it checks and
14809                         //              returns the validity of form elements. When the `submit`
14810                         //              method is called programmatically, the return value from
14811                         //              `onSubmit` is used to compute whether or not submission
14812                         //              should proceed
14813                         // tags:
14814                         //              extension
14815
14816                         return this.isValid(); // Boolean
14817                 },
14818
14819                 submit: function(){
14820                         // summary:
14821                         //              programmatically submit form if and only if the `onSubmit` returns true
14822                         if(!(this.onSubmit() === false)){
14823                                 this.containerNode.submit();
14824                         }
14825                 }
14826         }
14827 );
14828
14829 }
14830
14831 if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14832 dojo._hasResource["dijit.form.RadioButton"] = true;
14833 dojo.provide("dijit.form.RadioButton");
14834
14835
14836 // TODO: for 2.0, move the RadioButton code into this file
14837
14838 }
14839
14840 if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
14841 dojo._hasResource["dijit.form._FormSelectWidget"] = true;
14842 dojo.provide("dijit.form._FormSelectWidget");
14843
14844
14845
14846
14847 /*=====
14848 dijit.form.__SelectOption = function(){
14849         // value: String
14850         //              The value of the option.  Setting to empty (or missing) will
14851         //              place a separator at that location
14852         // label: String
14853         //              The label for our option.  It can contain html tags.
14854         //  selected: Boolean
14855         //              Whether or not we are a selected option
14856         // disabled: Boolean
14857         //              Whether or not this specific option is disabled
14858         this.value = value;
14859         this.label = label;
14860         this.selected = selected;
14861         this.disabled = disabled;
14862 }
14863 =====*/
14864
14865 dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
14866         // summary:
14867         //              Extends _FormValueWidget in order to provide "select-specific"
14868         //              values - i.e., those values that are unique to <select> elements.
14869         //              This also provides the mechanism for reading the elements from
14870         //              a store, if desired.
14871
14872         // multiple: Boolean
14873         //              Whether or not we are multi-valued
14874         multiple: false,
14875
14876         // options: dijit.form.__SelectOption[]
14877         //              The set of options for our select item.  Roughly corresponds to
14878         //      the html <option> tag.
14879         options: null,
14880
14881         // store: dojo.data.api.Identity
14882         //              A store which, at the very least impelements dojo.data.api.Identity
14883         //              to use for getting our list of options - rather than reading them
14884         //              from the <option> html tags.
14885         store: null,
14886
14887         // query: object
14888         //              A query to use when fetching items from our store
14889         query: null,
14890
14891         // queryOptions: object
14892         //              Query options to use when fetching from the store
14893         queryOptions: null,
14894
14895         // onFetch: Function
14896         //              A callback to do with an onFetch - but before any items are actually
14897         //              iterated over (i.e. to filter even futher what you want to add)
14898         onFetch: null,
14899
14900         // sortByLabel: boolean
14901         //              Flag to sort the options returned from a store by the label of
14902         //              the store.
14903         sortByLabel: true,
14904
14905
14906         // loadChildrenOnOpen: boolean
14907         //              By default loadChildren is called when the items are fetched from the
14908         //              store.  This property allows delaying loadChildren (and the creation
14909         //              of the options/menuitems) until the user opens the click the button.
14910         //              dropdown
14911         loadChildrenOnOpen: false,
14912
14913         getOptions: function(/* anything */ valueOrIdx){
14914                 // summary:
14915                 //              Returns a given option (or options).
14916                 // valueOrIdx:
14917                 //              If passed in as a string, that string is used to look up the option
14918                 //              in the array of options - based on the value property.
14919                 //              (See dijit.form.__SelectOption).
14920                 //
14921                 //              If passed in a number, then the option with the given index (0-based)
14922                 //              within this select will be returned.
14923                 //
14924                 //              If passed in a dijit.form.__SelectOption, the same option will be
14925                 //              returned if and only if it exists within this select.
14926                 //
14927                 //              If passed an array, then an array will be returned with each element
14928                 //              in the array being looked up.
14929                 //
14930                 //              If not passed a value, then all options will be returned
14931                 //
14932                 // returns:
14933                 //              The option corresponding with the given value or index.  null
14934                 //              is returned if any of the following are true:
14935                 //                      - A string value is passed in which doesn't exist
14936                 //                      - An index is passed in which is outside the bounds of the array of options
14937                 //                      - A dijit.form.__SelectOption is passed in which is not a part of the select
14938
14939                 // NOTE: the compare for passing in a dijit.form.__SelectOption checks
14940                 //              if the value property matches - NOT if the exact option exists
14941                 // NOTE: if passing in an array, null elements will be placed in the returned
14942                 //              array when a value is not found.
14943                 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
14944
14945                 if(lookupValue === undefined){
14946                         return opts; // dijit.form.__SelectOption[]
14947                 }
14948                 if(dojo.isArray(lookupValue)){
14949                         return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
14950                 }
14951                 if(dojo.isObject(valueOrIdx)){
14952                         // We were passed an option - so see if it's in our array (directly),
14953                         // and if it's not, try and find it by value.
14954                         if(!dojo.some(this.options, function(o, idx){
14955                                 if(o === lookupValue ||
14956                                         (o.value && o.value === lookupValue.value)){
14957                                         lookupValue = idx;
14958                                         return true;
14959                                 }
14960                                 return false;
14961                         })){
14962                                 lookupValue = -1;
14963                         }
14964                 }
14965                 if(typeof lookupValue == "string"){
14966                         for(var i=0; i<l; i++){
14967                                 if(opts[i].value === lookupValue){
14968                                         lookupValue = i;
14969                                         break;
14970                                 }
14971                         }
14972                 }
14973                 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
14974                         return this.options[lookupValue] // dijit.form.__SelectOption
14975                 }
14976                 return null; // null
14977         },
14978
14979         addOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ option){
14980                 // summary:
14981                 //              Adds an option or options to the end of the select.  If value
14982                 //              of the option is empty or missing, a separator is created instead.
14983                 //              Passing in an array of options will yield slightly better performance
14984                 //              since the children are only loaded once.
14985                 if(!dojo.isArray(option)){ option = [option]; }
14986                 dojo.forEach(option, function(i){
14987                         if(i && dojo.isObject(i)){
14988                                 this.options.push(i);
14989                         }
14990                 }, this);
14991                 this._loadChildren();
14992         },
14993
14994         removeOption: function(/* string, dijit.form.__SelectOption, number, or array */ valueOrIdx){
14995                 // summary:
14996                 //              Removes the given option or options.  You can remove by string
14997                 //              (in which case the value is removed), number (in which case the
14998                 //              index in the options array is removed), or select option (in
14999                 //              which case, the select option with a matching value is removed).
15000                 //              You can also pass in an array of those values for a slightly
15001                 //              better performance since the children are only loaded once.
15002                 if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
15003                 var oldOpts = this.getOptions(valueOrIdx);
15004                 dojo.forEach(oldOpts, function(i){
15005                         // We can get null back in our array - if our option was not found.  In
15006                         // that case, we don't want to blow up...
15007                         if(i){
15008                                 this.options = dojo.filter(this.options, function(node, idx){
15009                                         return (node.value !== i.value);
15010                                 });
15011                                 this._removeOptionItem(i);
15012                         }
15013                 }, this);
15014                 this._loadChildren();
15015         },
15016
15017         updateOption: function(/* dijit.form.__SelectOption, dijit.form.__SelectOption[] */ newOption){
15018                 // summary:
15019                 //              Updates the values of the given option.  The option to update
15020                 //              is matched based on the value of the entered option.  Passing
15021                 //              in an array of new options will yeild better performance since
15022                 //              the children will only be loaded once.
15023                 if(!dojo.isArray(newOption)){ newOption = [newOption]; }
15024                 dojo.forEach(newOption, function(i){
15025                         var oldOpt = this.getOptions(i), k;
15026                         if(oldOpt){
15027                                 for(k in i){ oldOpt[k] = i[k]; }
15028                         }
15029                 }, this);
15030                 this._loadChildren();
15031         },
15032
15033         setStore: function(/* dojo.data.api.Identity */ store,
15034                                                 /* anything? */ selectedValue,
15035                                                 /* Object? */ fetchArgs){
15036                 // summary:
15037                 //              Sets the store you would like to use with this select widget.
15038                 //              The selected value is the value of the new store to set.  This
15039                 //              function returns the original store, in case you want to reuse
15040                 //              it or something.
15041                 // store: dojo.data.api.Identity
15042                 //              The store you would like to use - it MUST implement Identity,
15043                 //              and MAY implement Notification.
15044                 // selectedValue: anything?
15045                 //              The value that this widget should set itself to *after* the store
15046                 //              has been loaded
15047                 // fetchArgs: Object?
15048                 //              The arguments that will be passed to the store's fetch() function
15049                 var oStore = this.store;
15050                 fetchArgs = fetchArgs || {};
15051                 if(oStore !== store){
15052                         // Our store has changed, so update our notifications
15053                         dojo.forEach(this._notifyConnections || [], dojo.disconnect);
15054                         delete this._notifyConnections;
15055                         if(store && store.getFeatures()["dojo.data.api.Notification"]){
15056                                 this._notifyConnections = [
15057                                         dojo.connect(store, "onNew", this, "_onNewItem"),
15058                                         dojo.connect(store, "onDelete", this, "_onDeleteItem"),
15059                                         dojo.connect(store, "onSet", this, "_onSetItem")
15060                                 ];
15061                         }
15062                         this.store = store;
15063                 }
15064
15065                 // Turn off change notifications while we make all these changes
15066                 this._onChangeActive = false;
15067
15068                 // Remove existing options (if there are any)
15069                 if(this.options && this.options.length){
15070                         this.removeOption(this.options);
15071                 }
15072
15073                 // Add our new options
15074                 if(store){
15075                         var cb = function(items){
15076                                 if(this.sortByLabel && !fetchArgs.sort && items.length){
15077                                         items.sort(dojo.data.util.sorter.createSortFunction([{
15078                                                 attribute: store.getLabelAttributes(items[0])[0]
15079                                         }], store));
15080                                 }
15081
15082                                 if(fetchArgs.onFetch){
15083                                         items = fetchArgs.onFetch(items);
15084                                 }
15085                                 // TODO: Add these guys as a batch, instead of separately
15086                                 dojo.forEach(items, function(i){
15087                                         this._addOptionForItem(i);
15088                                 }, this);
15089
15090                                 // Set our value (which might be undefined), and then tweak
15091                                 // it to send a change event with the real value
15092                                 this._loadingStore = false;
15093                                 this.set("value", (("_pendingValue" in this) ? this._pendingValue : selectedValue));
15094                                 delete this._pendingValue;
15095
15096                                 if(!this.loadChildrenOnOpen){
15097                                         this._loadChildren();
15098                                 }else{
15099                                         this._pseudoLoadChildren(items);
15100                                 }
15101                                 this._fetchedWith = opts;
15102                                 this._lastValueReported = this.multiple ? [] : null;
15103                                 this._onChangeActive = true;
15104                                 this.onSetStore();
15105                                 this._handleOnChange(this.value);
15106                         };
15107                         var opts = dojo.mixin({onComplete:cb, scope: this}, fetchArgs);
15108                         this._loadingStore = true;
15109                         store.fetch(opts);
15110                 }else{
15111                         delete this._fetchedWith;
15112                 }
15113                 return oStore;  // dojo.data.api.Identity
15114         },
15115
15116         _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
15117                 // summary:
15118                 //              set the value of the widget.
15119                 //              If a string is passed, then we set our value from looking it up.
15120                 if(this._loadingStore){
15121                         // Our store is loading - so save our value, and we'll set it when
15122                         // we're done
15123                         this._pendingValue = newValue;
15124                         return;
15125                 }
15126                 var opts = this.getOptions() || [];
15127                 if(!dojo.isArray(newValue)){
15128                         newValue = [newValue];
15129                 }
15130                 dojo.forEach(newValue, function(i, idx){
15131                         if(!dojo.isObject(i)){
15132                                 i = i + "";
15133                         }
15134                         if(typeof i === "string"){
15135                                 newValue[idx] = dojo.filter(opts, function(node){
15136                                         return node.value === i;
15137                                 })[0] || {value: "", label: ""};
15138                         }
15139                 }, this);
15140
15141                 // Make sure some sane default is set
15142                 newValue = dojo.filter(newValue, function(i){ return i && i.value; });
15143                 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
15144                         newValue[0] = opts[0];
15145                 }
15146                 dojo.forEach(opts, function(i){
15147                         i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
15148                 });
15149                 var val = dojo.map(newValue, function(i){ return i.value; }),
15150                         disp = dojo.map(newValue, function(i){ return i.label; });
15151
15152                 this.value = this.multiple ? val : val[0];
15153                 this._setDisplay(this.multiple ? disp : disp[0]);
15154                 this._updateSelection();
15155                 this._handleOnChange(this.value, priorityChange);
15156         },
15157
15158         _getDisplayedValueAttr: function(){
15159                 // summary:
15160                 //              returns the displayed value of the widget
15161                 var val = this.get("value");
15162                 if(!dojo.isArray(val)){
15163                         val = [val];
15164                 }
15165                 var ret = dojo.map(this.getOptions(val), function(v){
15166                         if(v && "label" in v){
15167                                 return v.label;
15168                         }else if(v){
15169                                 return v.value;
15170                         }
15171                         return null;
15172                 }, this);
15173                 return this.multiple ? ret : ret[0];
15174         },
15175
15176         _getValueDeprecated: false, // remove when _FormWidget:getValue is removed
15177         getValue: function(){
15178                 // summary:
15179                 //              get the value of the widget.
15180                 return this._lastValue;
15181         },
15182
15183         undo: function(){
15184                 // summary:
15185                 //              restore the value to the last value passed to onChange
15186                 this._setValueAttr(this._lastValueReported, false);
15187         },
15188
15189         _loadChildren: function(){
15190                 // summary:
15191                 //              Loads the children represented by this widget's options.
15192                 //              reset the menu to make it "populatable on the next click
15193                 if(this._loadingStore){ return; }
15194                 dojo.forEach(this._getChildren(), function(child){
15195                         child.destroyRecursive();
15196                 });
15197                 // Add each menu item
15198                 dojo.forEach(this.options, this._addOptionItem, this);
15199
15200                 // Update states
15201                 this._updateSelection();
15202         },
15203
15204         _updateSelection: function(){
15205                 // summary:
15206                 //              Sets the "selected" class on the item for styling purposes
15207                 this.value = this._getValueFromOpts();
15208                 var val = this.value;
15209                 if(!dojo.isArray(val)){
15210                         val = [val];
15211                 }
15212                 if(val && val[0]){
15213                         dojo.forEach(this._getChildren(), function(child){
15214                                 var isSelected = dojo.some(val, function(v){
15215                                         return child.option && (v === child.option.value);
15216                                 });
15217                                 dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
15218                                 dijit.setWaiState(child.domNode, "selected", isSelected);
15219                         }, this);
15220                 }
15221                 this._handleOnChange(this.value);
15222         },
15223
15224         _getValueFromOpts: function(){
15225                 // summary:
15226                 //              Returns the value of the widget by reading the options for
15227                 //              the selected flag
15228                 var opts = this.getOptions() || [];
15229                 if(!this.multiple && opts.length){
15230                         // Mirror what a select does - choose the first one
15231                         var opt = dojo.filter(opts, function(i){
15232                                 return i.selected;
15233                         })[0];
15234                         if(opt && opt.value){
15235                                 return opt.value
15236                         }else{
15237                                 opts[0].selected = true;
15238                                 return opts[0].value;
15239                         }
15240                 }else if(this.multiple){
15241                         // Set value to be the sum of all selected
15242                         return dojo.map(dojo.filter(opts, function(i){
15243                                 return i.selected;
15244                         }), function(i){
15245                                 return i.value;
15246                         }) || [];
15247                 }
15248                 return "";
15249         },
15250
15251         // Internal functions to call when we have store notifications come in
15252         _onNewItem: function(/* item */ item, /* Object? */ parentInfo){
15253                 if(!parentInfo || !parentInfo.parent){
15254                         // Only add it if we are top-level
15255                         this._addOptionForItem(item);
15256                 }
15257         },
15258         _onDeleteItem: function(/* item */ item){
15259                 var store = this.store;
15260                 this.removeOption(store.getIdentity(item));
15261         },
15262         _onSetItem: function(/* item */ item){
15263                 this.updateOption(this._getOptionObjForItem(item));
15264         },
15265
15266         _getOptionObjForItem: function(item){
15267                 // summary:
15268                 //              Returns an option object based off the given item.  The "value"
15269                 //              of the option item will be the identity of the item, the "label"
15270                 //              of the option will be the label of the item.  If the item contains
15271                 //              children, the children value of the item will be set
15272                 var store = this.store, label = store.getLabel(item),
15273                         value = (label ? store.getIdentity(item) : null);
15274                 return {value: value, label: label, item:item}; // dijit.form.__SelectOption
15275         },
15276
15277         _addOptionForItem: function(/* item */ item){
15278                 // summary:
15279                 //              Creates (and adds) the option for the given item
15280                 var store = this.store;
15281                 if(!store.isItemLoaded(item)){
15282                         // We are not loaded - so let's load it and add later
15283                         store.loadItem({item: item, onComplete: function(i){
15284                                 this._addOptionForItem(item);
15285                         },
15286                         scope: this});
15287                         return;
15288                 }
15289                 var newOpt = this._getOptionObjForItem(item);
15290                 this.addOption(newOpt);
15291         },
15292
15293         constructor: function(/* Object */ keywordArgs){
15294                 // summary:
15295                 //              Saves off our value, if we have an initial one set so we
15296                 //              can use it if we have a store as well (see startup())
15297                 this._oValue = (keywordArgs || {}).value || null;
15298         },
15299
15300         _fillContent: function(){
15301                 // summary:
15302                 //              Loads our options and sets up our dropdown correctly.  We
15303                 //              don't want any content, so we don't call any inherit chain
15304                 //              function.
15305                 var opts = this.options;
15306                 if(!opts){
15307                         opts = this.options = this.srcNodeRef ? dojo.query(">",
15308                                                 this.srcNodeRef).map(function(node){
15309                                                         if(node.getAttribute("type") === "separator"){
15310                                                                 return { value: "", label: "", selected: false, disabled: false };
15311                                                         }
15312                                                         return { value: node.getAttribute("value"),
15313                                                                                 label: String(node.innerHTML),
15314                                                                                 selected: node.getAttribute("selected") || false,
15315                                                                                 disabled: node.getAttribute("disabled") || false };
15316                                                 }, this) : [];
15317                 }
15318                 if(!this.value){
15319                         this.value = this._getValueFromOpts();
15320                 }else if(this.multiple && typeof this.value == "string"){
15321                         this.value = this.value.split(",");
15322                 }
15323         },
15324
15325         postCreate: function(){
15326                 // summary:
15327                 //              sets up our event handling that we need for functioning
15328                 //              as a select
15329                 dojo.setSelectable(this.focusNode, false);
15330                 this.inherited(arguments);
15331
15332                 // Make our event connections for updating state
15333                 this.connect(this, "onChange", "_updateSelection");
15334                 this.connect(this, "startup", "_loadChildren");
15335
15336                 this._setValueAttr(this.value, null);
15337         },
15338
15339         startup: function(){
15340                 // summary:
15341                 //              Connects in our store, if we have one defined
15342                 this.inherited(arguments);
15343                 var store = this.store, fetchArgs = {};
15344                 dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
15345                         if(this[i]){
15346                                 fetchArgs[i] = this[i];
15347                         }
15348                         delete this[i];
15349                 }, this);
15350                 if(store && store.getFeatures()["dojo.data.api.Identity"]){
15351                         // Temporarily set our store to null so that it will get set
15352                         // and connected appropriately
15353                         this.store = null;
15354                         this.setStore(store, this._oValue, fetchArgs);
15355                 }
15356         },
15357
15358         destroy: function(){
15359                 // summary:
15360                 //              Clean up our connections
15361                 dojo.forEach(this._notifyConnections || [], dojo.disconnect);
15362                 this.inherited(arguments);
15363         },
15364
15365         _addOptionItem: function(/* dijit.form.__SelectOption */ option){
15366                 // summary:
15367                 //              User-overridable function which, for the given option, adds an
15368                 //              item to the select.  If the option doesn't have a value, then a
15369                 //              separator is added in that place.  Make sure to store the option
15370                 //              in the created option widget.
15371         },
15372
15373         _removeOptionItem: function(/* dijit.form.__SelectOption */ option){
15374                 // summary:
15375                 //              User-overridable function which, for the given option, removes
15376                 //              its item from the select.
15377         },
15378
15379         _setDisplay: function(/*String or String[]*/ newDisplay){
15380                 // summary:
15381                 //              Overridable function which will set the display for the
15382                 //              widget.  newDisplay is either a string (in the case of
15383                 //              single selects) or array of strings (in the case of multi-selects)
15384         },
15385
15386         _getChildren: function(){
15387                 // summary:
15388                 //              Overridable function to return the children that this widget contains.
15389                 return [];
15390         },
15391
15392         _getSelectedOptionsAttr: function(){
15393                 // summary:
15394                 //              hooks into this.attr to provide a mechanism for getting the
15395                 //              option items for the current value of the widget.
15396                 return this.getOptions(this.get("value"));
15397         },
15398
15399         _pseudoLoadChildren: function(/* item[] */ items){
15400                 // summary:
15401                 //              a function that will "fake" loading children, if needed, and
15402                 //              if we have set to not load children until the widget opens.
15403                 // items:
15404                 //              An array of items that will be loaded, when needed
15405         },
15406
15407         onSetStore: function(){
15408                 // summary:
15409                 //              a function that can be connected to in order to receive a
15410                 //              notification that the store has finished loading and all options
15411                 //              from that store are available
15412         }
15413 });
15414
15415 }
15416
15417 if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15418 dojo._hasResource["dijit._KeyNavContainer"] = true;
15419 dojo.provide("dijit._KeyNavContainer");
15420
15421
15422 dojo.declare("dijit._KeyNavContainer",
15423         dijit._Container,
15424         {
15425
15426                 // summary:
15427                 //              A _Container with keyboard navigation of its children.
15428                 // description:
15429                 //              To use this mixin, call connectKeyNavHandlers() in
15430                 //              postCreate() and call startupKeyNavChildren() in startup().
15431                 //              It provides normalized keyboard and focusing code for Container
15432                 //              widgets.
15433 /*=====
15434                 // focusedChild: [protected] Widget
15435                 //              The currently focused child widget, or null if there isn't one
15436                 focusedChild: null,
15437 =====*/
15438
15439                 // tabIndex: Integer
15440                 //              Tab index of the container; same as HTML tabIndex attribute.
15441                 //              Note then when user tabs into the container, focus is immediately
15442                 //              moved to the first item in the container.
15443                 tabIndex: "0",
15444
15445                 _keyNavCodes: {},
15446
15447                 connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
15448                         // summary:
15449                         //              Call in postCreate() to attach the keyboard handlers
15450                         //              to the container.
15451                         // preKeyCodes: dojo.keys[]
15452                         //              Key codes for navigating to the previous child.
15453                         // nextKeyCodes: dojo.keys[]
15454                         //              Key codes for navigating to the next child.
15455                         // tags:
15456                         //              protected
15457
15458                         var keyCodes = (this._keyNavCodes = {});
15459                         var prev = dojo.hitch(this, this.focusPrev);
15460                         var next = dojo.hitch(this, this.focusNext);
15461                         dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
15462                         dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
15463                         this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
15464                         this.connect(this.domNode, "onfocus", "_onContainerFocus");
15465                 },
15466
15467                 startupKeyNavChildren: function(){
15468                         // summary:
15469                         //              Call in startup() to set child tabindexes to -1
15470                         // tags:
15471                         //              protected
15472                         dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
15473                 },
15474
15475                 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
15476                         // summary:
15477                         //              Add a child to our _Container
15478                         dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
15479                         this._startupChild(widget);
15480                 },
15481
15482                 focus: function(){
15483                         // summary:
15484                         //              Default focus() implementation: focus the first child.
15485                         this.focusFirstChild();
15486                 },
15487
15488                 focusFirstChild: function(){
15489                         // summary:
15490                         //              Focus the first focusable child in the container.
15491                         // tags:
15492                         //              protected
15493                         var child = this._getFirstFocusableChild();
15494                         if(child){ // edge case: Menu could be empty or hidden
15495                                 this.focusChild(child);
15496                         }
15497                 },
15498
15499                 focusNext: function(){
15500                         // summary:
15501                         //              Focus the next widget
15502                         // tags:
15503                         //              protected
15504                         var child = this._getNextFocusableChild(this.focusedChild, 1);
15505                         this.focusChild(child);
15506                 },
15507
15508                 focusPrev: function(){
15509                         // summary:
15510                         //              Focus the last focusable node in the previous widget
15511                         //              (ex: go to the ComboButton icon section rather than button section)
15512                         // tags:
15513                         //              protected
15514                         var child = this._getNextFocusableChild(this.focusedChild, -1);
15515                         this.focusChild(child, true);
15516                 },
15517
15518                 focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
15519                         // summary:
15520                         //              Focus widget.
15521                         // widget:
15522                         //              Reference to container's child widget
15523                         // last:
15524                         //              If true and if widget has multiple focusable nodes, focus the
15525                         //              last one instead of the first one
15526                         // tags:
15527                         //              protected
15528                         
15529                         if(this.focusedChild && widget !== this.focusedChild){
15530                                 this._onChildBlur(this.focusedChild);
15531                         }
15532                         widget.focus(last ? "end" : "start");
15533                         this.focusedChild = widget;
15534                 },
15535
15536                 _startupChild: function(/*dijit._Widget*/ widget){
15537                         // summary:
15538                         //              Setup for each child widget
15539                         // description:
15540                         //              Sets tabIndex=-1 on each child, so that the tab key will 
15541                         //              leave the container rather than visiting each child.
15542                         // tags:
15543                         //              private
15544                         
15545                         widget.set("tabIndex", "-1");
15546                         
15547                         this.connect(widget, "_onFocus", function(){
15548                                 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
15549                                 widget.set("tabIndex", this.tabIndex);
15550                         });
15551                         this.connect(widget, "_onBlur", function(){
15552                                 widget.set("tabIndex", "-1");
15553                         });
15554                 },
15555
15556                 _onContainerFocus: function(evt){
15557                         // summary:
15558                         //              Handler for when the container gets focus
15559                         // description:
15560                         //              Initially the container itself has a tabIndex, but when it gets
15561                         //              focus, switch focus to first child...
15562                         // tags:
15563                         //              private
15564
15565                         // Note that we can't use _onFocus() because switching focus from the
15566                         // _onFocus() handler confuses the focus.js code
15567                         // (because it causes _onFocusNode() to be called recursively)
15568
15569                         // focus bubbles on Firefox,
15570                         // so just make sure that focus has really gone to the container
15571                         if(evt.target !== this.domNode){ return; }
15572
15573                         this.focusFirstChild();
15574
15575                         // and then set the container's tabIndex to -1,
15576                         // (don't remove as that breaks Safari 4)
15577                         // so that tab or shift-tab will go to the fields after/before
15578                         // the container, rather than the container itself
15579                         dojo.attr(this.domNode, "tabIndex", "-1");
15580                 },
15581
15582                 _onBlur: function(evt){
15583                         // When focus is moved away the container, and its descendant (popup) widgets,
15584                         // then restore the container's tabIndex so that user can tab to it again.
15585                         // Note that using _onBlur() so that this doesn't happen when focus is shifted
15586                         // to one of my child widgets (typically a popup)
15587                         if(this.tabIndex){
15588                                 dojo.attr(this.domNode, "tabIndex", this.tabIndex);
15589                         }
15590                         this.inherited(arguments);
15591                 },
15592
15593                 _onContainerKeypress: function(evt){
15594                         // summary:
15595                         //              When a key is pressed, if it's an arrow key etc. then
15596                         //              it's handled here.
15597                         // tags:
15598                         //              private
15599                         if(evt.ctrlKey || evt.altKey){ return; }
15600                         var func = this._keyNavCodes[evt.charOrCode];
15601                         if(func){
15602                                 func();
15603                                 dojo.stopEvent(evt);
15604                         }
15605                 },
15606
15607                 _onChildBlur: function(/*dijit._Widget*/ widget){
15608                         // summary:
15609                         //              Called when focus leaves a child widget to go
15610                         //              to a sibling widget.
15611                         // tags:
15612                         //              protected
15613                 },
15614
15615                 _getFirstFocusableChild: function(){
15616                         // summary:
15617                         //              Returns first child that can be focused
15618                         return this._getNextFocusableChild(null, 1);    // dijit._Widget
15619                 },
15620
15621                 _getNextFocusableChild: function(child, dir){
15622                         // summary:
15623                         //              Returns the next or previous focusable child, compared
15624                         //              to "child"
15625                         // child: Widget
15626                         //              The current widget
15627                         // dir: Integer
15628                         //              * 1 = after
15629                         //              * -1 = before
15630                         if(child){
15631                                 child = this._getSiblingOfChild(child, dir);
15632                         }
15633                         var children = this.getChildren();
15634                         for(var i=0; i < children.length; i++){
15635                                 if(!child){
15636                                         child = children[(dir>0) ? 0 : (children.length-1)];
15637                                 }
15638                                 if(child.isFocusable()){
15639                                         return child;   // dijit._Widget
15640                                 }
15641                                 child = this._getSiblingOfChild(child, dir);
15642                         }
15643                         // no focusable child found
15644                         return null;    // dijit._Widget
15645                 }
15646         }
15647 );
15648
15649 }
15650
15651 if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15652 dojo._hasResource["dijit.MenuItem"] = true;
15653 dojo.provide("dijit.MenuItem");
15654
15655
15656
15657
15658
15659
15660 dojo.declare("dijit.MenuItem",
15661                 [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
15662                 {
15663                 // summary:
15664                 //              A line item in a Menu Widget
15665
15666                 // Make 3 columns
15667                 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15668                 templateString: dojo.cache("dijit", "templates/MenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\">\n\t\t<div dojoAttachPoint=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"),
15669
15670                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
15671                         label: { node: "containerNode", type: "innerHTML" },
15672                         iconClass: { node: "iconNode", type: "class" }
15673                 }),
15674
15675                 baseClass: "dijitMenuItem",
15676
15677                 // label: String
15678                 //              Menu text
15679                 label: '',
15680
15681                 // iconClass: String
15682                 //              Class to apply to DOMNode to make it display an icon.
15683                 iconClass: "",
15684
15685                 // accelKey: String
15686                 //              Text for the accelerator (shortcut) key combination.
15687                 //              Note that although Menu can display accelerator keys there
15688                 //              is no infrastructure to actually catch and execute these
15689                 //              accelerators.
15690                 accelKey: "",
15691
15692                 // disabled: Boolean
15693                 //              If true, the menu item is disabled.
15694                 //              If false, the menu item is enabled.
15695                 disabled: false,
15696
15697                 _fillContent: function(/*DomNode*/ source){
15698                         // If button label is specified as srcNodeRef.innerHTML rather than
15699                         // this.params.label, handle it here.
15700                         if(source && !("label" in this.params)){
15701                                 this.set('label', source.innerHTML);
15702                         }
15703                 },
15704
15705                 postCreate: function(){
15706                         this.inherited(arguments);
15707                         dojo.setSelectable(this.domNode, false);
15708                         var label = this.id+"_text";
15709                         dojo.attr(this.containerNode, "id", label);
15710                         if(this.accelKeyNode){
15711                                 dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
15712                                 label += " " + this.id + "_accel";
15713                         }
15714                         dijit.setWaiState(this.domNode, "labelledby", label);
15715                 },
15716
15717                 _onHover: function(){
15718                         // summary:
15719                         //              Handler when mouse is moved onto menu item
15720                         // tags:
15721                         //              protected
15722                         this.getParent().onItemHover(this);
15723                 },
15724
15725                 _onUnhover: function(){
15726                         // summary:
15727                         //              Handler when mouse is moved off of menu item,
15728                         //              possibly to a child menu, or maybe to a sibling
15729                         //              menuitem or somewhere else entirely.
15730                         // tags:
15731                         //              protected
15732
15733                         // if we are unhovering the currently selected item
15734                         // then unselect it
15735                         this.getParent().onItemUnhover(this);
15736
15737                         // _onUnhover() is called when the menu is hidden (collapsed), due to clicking
15738                         // a MenuItem and having it execut.  When that happens, FF and IE don't generate
15739                         // an onmouseout event for the MenuItem, so give _CssStateMixin some help
15740                         this._hovering = false;
15741                         this._setStateClass();
15742                 },
15743
15744                 _onClick: function(evt){
15745                         // summary:
15746                         //              Internal handler for click events on MenuItem.
15747                         // tags:
15748                         //              private
15749                         this.getParent().onItemClick(this, evt);
15750                         dojo.stopEvent(evt);
15751                 },
15752
15753                 onClick: function(/*Event*/ evt){
15754                         // summary:
15755                         //              User defined function to handle clicks
15756                         // tags:
15757                         //              callback
15758                 },
15759
15760                 focus: function(){
15761                         // summary:
15762                         //              Focus on this MenuItem
15763                         try{
15764                                 if(dojo.isIE == 8){
15765                                         // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15766                                         this.containerNode.focus();
15767                                 }
15768                                 dijit.focus(this.focusNode);
15769                         }catch(e){
15770                                 // this throws on IE (at least) in some scenarios
15771                         }
15772                 },
15773
15774                 _onFocus: function(){
15775                         // summary:
15776                         //              This is called by the focus manager when focus
15777                         //              goes to this MenuItem or a child menu.
15778                         // tags:
15779                         //              protected
15780                         this._setSelected(true);
15781                         this.getParent()._onItemFocus(this);
15782
15783                         this.inherited(arguments);
15784                 },
15785
15786                 _setSelected: function(selected){
15787                         // summary:
15788                         //              Indicate that this node is the currently selected one
15789                         // tags:
15790                         //              private
15791
15792                         /***
15793                          * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15794                          * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15795                          * That's not supposed to happen, but the problem is:
15796                          * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15797                          * points to the parent Menu, bypassing the parent MenuItem... thus the
15798                          * MenuItem is not in the chain of active widgets and gets a premature call to
15799                          * _onBlur()
15800                          */
15801
15802                         dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
15803                 },
15804
15805                 setLabel: function(/*String*/ content){
15806                         // summary:
15807                         //              Deprecated.   Use set('label', ...) instead.
15808                         // tags:
15809                         //              deprecated
15810                         dojo.deprecated("dijit.MenuItem.setLabel() is deprecated.  Use set('label', ...) instead.", "", "2.0");
15811                         this.set("label", content);
15812                 },
15813
15814                 setDisabled: function(/*Boolean*/ disabled){
15815                         // summary:
15816                         //              Deprecated.   Use set('disabled', bool) instead.
15817                         // tags:
15818                         //              deprecated
15819                         dojo.deprecated("dijit.Menu.setDisabled() is deprecated.  Use set('disabled', bool) instead.", "", "2.0");
15820                         this.set('disabled', disabled);
15821                 },
15822                 _setDisabledAttr: function(/*Boolean*/ value){
15823                         // summary:
15824                         //              Hook for attr('disabled', ...) to work.
15825                         //              Enable or disable this menu item.
15826                         this.disabled = value;
15827                         dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
15828                 },
15829                 _setAccelKeyAttr: function(/*String*/ value){
15830                         // summary:
15831                         //              Hook for attr('accelKey', ...) to work.
15832                         //              Set accelKey on this menu item.
15833                         this.accelKey=value;
15834
15835                         this.accelKeyNode.style.display=value?"":"none";
15836                         this.accelKeyNode.innerHTML=value;
15837                         //have to use colSpan to make it work in IE
15838                         dojo.attr(this.containerNode,'colSpan',value?"1":"2");
15839                 }
15840         });
15841
15842 }
15843
15844 if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15845 dojo._hasResource["dijit.PopupMenuItem"] = true;
15846 dojo.provide("dijit.PopupMenuItem");
15847
15848
15849
15850 dojo.declare("dijit.PopupMenuItem",
15851                 dijit.MenuItem,
15852                 {
15853                 _fillContent: function(){
15854                         // summary:
15855                         //              When Menu is declared in markup, this code gets the menu label and
15856                         //              the popup widget from the srcNodeRef.
15857                         // description:
15858                         //              srcNodeRefinnerHTML contains both the menu item text and a popup widget
15859                         //              The first part holds the menu item text and the second part is the popup
15860                         // example:
15861                         // |    <div dojoType="dijit.PopupMenuItem">
15862                         // |            <span>pick me</span>
15863                         // |            <popup> ... </popup>
15864                         // |    </div>
15865                         // tags:
15866                         //              protected
15867
15868                         if(this.srcNodeRef){
15869                                 var nodes = dojo.query("*", this.srcNodeRef);
15870                                 dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
15871
15872                                 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
15873                                 this.dropDownContainer = this.srcNodeRef;
15874                         }
15875                 },
15876
15877                 startup: function(){
15878                         if(this._started){ return; }
15879                         this.inherited(arguments);
15880
15881                         // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
15882                         // land now.  move it to dojo.doc.body.
15883                         if(!this.popup){
15884                                 var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
15885                                 this.popup = dijit.byNode(node);
15886                         }
15887                         dojo.body().appendChild(this.popup.domNode);
15888                         this.popup.startup();
15889
15890                         this.popup.domNode.style.display="none";
15891                         if(this.arrowWrapper){
15892                                 dojo.style(this.arrowWrapper, "visibility", "");
15893                         }
15894                         dijit.setWaiState(this.focusNode, "haspopup", "true");
15895                 },
15896
15897                 destroyDescendants: function(){
15898                         if(this.popup){
15899                                 // Destroy the popup, unless it's already been destroyed.  This can happen because
15900                                 // the popup is a direct child of <body> even though it's logically my child.
15901                                 if(!this.popup._destroyed){
15902                                         this.popup.destroyRecursive();
15903                                 }
15904                                 delete this.popup;
15905                         }
15906                         this.inherited(arguments);
15907                 }
15908         });
15909
15910
15911 }
15912
15913 if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15914 dojo._hasResource["dijit.CheckedMenuItem"] = true;
15915 dojo.provide("dijit.CheckedMenuItem");
15916
15917
15918
15919 dojo.declare("dijit.CheckedMenuItem",
15920                 dijit.MenuItem,
15921                 {
15922                 // summary:
15923                 //              A checkbox-like menu item for toggling on and off
15924
15925                 templateString: dojo.cache("dijit", "templates/CheckedMenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" waiRole=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" waiRole=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" dojoAttachPoint=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" dojoAttachPoint=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" waiRole=\"presentation\">&nbsp;</td>\n</tr>\n"),
15926
15927                 // checked: Boolean
15928                 //              Our checked state
15929                 checked: false,
15930                 _setCheckedAttr: function(/*Boolean*/ checked){
15931                         // summary:
15932                         //              Hook so attr('checked', bool) works.
15933                         //              Sets the class and state for the check box.
15934                         dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
15935                         dijit.setWaiState(this.domNode, "checked", checked);
15936                         this.checked = checked;
15937                 },
15938
15939                 onChange: function(/*Boolean*/ checked){
15940                         // summary:
15941                         //              User defined function to handle check/uncheck events
15942                         // tags:
15943                         //              callback
15944                 },
15945
15946                 _onClick: function(/*Event*/ e){
15947                         // summary:
15948                         //              Clicking this item just toggles its state
15949                         // tags:
15950                         //              private
15951                         if(!this.disabled){
15952                                 this.set("checked", !this.checked);
15953                                 this.onChange(this.checked);
15954                         }
15955                         this.inherited(arguments);
15956                 }
15957         });
15958
15959 }
15960
15961 if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15962 dojo._hasResource["dijit.MenuSeparator"] = true;
15963 dojo.provide("dijit.MenuSeparator");
15964
15965
15966
15967
15968
15969 dojo.declare("dijit.MenuSeparator",
15970                 [dijit._Widget, dijit._Templated, dijit._Contained],
15971                 {
15972                 // summary:
15973                 //              A line between two menu items
15974
15975                 templateString: dojo.cache("dijit", "templates/MenuSeparator.html", "<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>\n"),
15976
15977                 postCreate: function(){
15978                         dojo.setSelectable(this.domNode, false);
15979                 },
15980
15981                 isFocusable: function(){
15982                         // summary:
15983                         //              Override to always return false
15984                         // tags:
15985                         //              protected
15986
15987                         return false; // Boolean
15988                 }
15989         });
15990
15991
15992 }
15993
15994 if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15995 dojo._hasResource["dijit.Menu"] = true;
15996 dojo.provide("dijit.Menu");
15997
15998
15999
16000
16001
16002
16003
16004 dojo.declare("dijit._MenuBase",
16005         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
16006 {
16007         // summary:
16008         //              Base class for Menu and MenuBar
16009
16010         // parentMenu: [readonly] Widget
16011         //              pointer to menu that displayed me
16012         parentMenu: null,
16013
16014         // popupDelay: Integer
16015         //              number of milliseconds before hovering (without clicking) causes the popup to automatically open.
16016         popupDelay: 500,
16017
16018         startup: function(){
16019                 if(this._started){ return; }
16020
16021                 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
16022                 this.startupKeyNavChildren();
16023
16024                 this.inherited(arguments);
16025         },
16026
16027         onExecute: function(){
16028                 // summary:
16029                 //              Attach point for notification about when a menu item has been executed.
16030                 //              This is an internal mechanism used for Menus to signal to their parent to
16031                 //              close them, because they are about to execute the onClick handler.   In
16032                 //              general developers should not attach to or override this method.
16033                 // tags:
16034                 //              protected
16035         },
16036
16037         onCancel: function(/*Boolean*/ closeAll){
16038                 // summary:
16039                 //              Attach point for notification about when the user cancels the current menu
16040                 //              This is an internal mechanism used for Menus to signal to their parent to
16041                 //              close them.  In general developers should not attach to or override this method.
16042                 // tags:
16043                 //              protected
16044         },
16045
16046         _moveToPopup: function(/*Event*/ evt){
16047                 // summary:
16048                 //              This handles the right arrow key (left arrow key on RTL systems),
16049                 //              which will either open a submenu, or move to the next item in the
16050                 //              ancestor MenuBar
16051                 // tags:
16052                 //              private
16053
16054                 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
16055                         this.focusedChild._onClick(evt);
16056                 }else{
16057                         var topMenu = this._getTopMenu();
16058                         if(topMenu && topMenu._isMenuBar){
16059                                 topMenu.focusNext();
16060                         }
16061                 }
16062         },
16063
16064         _onPopupHover: function(/*Event*/ evt){
16065                 // summary:
16066                 //              This handler is called when the mouse moves over the popup.
16067                 // tags:
16068                 //              private
16069
16070                 // if the mouse hovers over a menu popup that is in pending-close state,
16071                 // then stop the close operation.
16072                 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
16073                 if(this.currentPopup && this.currentPopup._pendingClose_timer){
16074                         var parentMenu = this.currentPopup.parentMenu;
16075                         // highlight the parent menu item pointing to this popup
16076                         if(parentMenu.focusedChild){
16077                                 parentMenu.focusedChild._setSelected(false);
16078                         }
16079                         parentMenu.focusedChild = this.currentPopup.from_item;
16080                         parentMenu.focusedChild._setSelected(true);
16081                         // cancel the pending close
16082                         this._stopPendingCloseTimer(this.currentPopup);
16083                 }
16084         },
16085
16086         onItemHover: function(/*MenuItem*/ item){
16087                 // summary:
16088                 //              Called when cursor is over a MenuItem.
16089                 // tags:
16090                 //              protected
16091
16092                 // Don't do anything unless user has "activated" the menu by:
16093                 //              1) clicking it
16094                 //              2) opening it from a parent menu (which automatically focuses it)
16095                 if(this.isActive){
16096                         this.focusChild(item);
16097                         if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
16098                                 this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
16099                         }
16100                 }
16101                 // if the user is mixing mouse and keyboard navigation,
16102                 // then the menu may not be active but a menu item has focus,
16103                 // but it's not the item that the mouse just hovered over.
16104                 // To avoid both keyboard and mouse selections, use the latest.
16105                 if(this.focusedChild){
16106                         this.focusChild(item);
16107                 }
16108                 this._hoveredChild = item;
16109         },
16110
16111         _onChildBlur: function(item){
16112                 // summary:
16113                 //              Called when a child MenuItem becomes inactive because focus
16114                 //              has been removed from the MenuItem *and* it's descendant menus.
16115                 // tags:
16116                 //              private
16117                 this._stopPopupTimer();
16118                 item._setSelected(false);
16119                 // Close all popups that are open and descendants of this menu
16120                 var itemPopup = item.popup;
16121                 if(itemPopup){
16122                         this._stopPendingCloseTimer(itemPopup);
16123                         itemPopup._pendingClose_timer = setTimeout(function(){
16124                                 itemPopup._pendingClose_timer = null;
16125                                 if(itemPopup.parentMenu){
16126                                         itemPopup.parentMenu.currentPopup = null;
16127                                 }
16128                                 dijit.popup.close(itemPopup); // this calls onClose
16129                         }, this.popupDelay);
16130                 }
16131         },
16132
16133         onItemUnhover: function(/*MenuItem*/ item){
16134                 // summary:
16135                 //              Callback fires when mouse exits a MenuItem
16136                 // tags:
16137                 //              protected
16138
16139                 if(this.isActive){
16140                         this._stopPopupTimer();
16141                 }
16142                 if(this._hoveredChild == item){ this._hoveredChild = null; }
16143         },
16144
16145         _stopPopupTimer: function(){
16146                 // summary:
16147                 //              Cancels the popup timer because the user has stop hovering
16148                 //              on the MenuItem, etc.
16149                 // tags:
16150                 //              private
16151                 if(this.hover_timer){
16152                         clearTimeout(this.hover_timer);
16153                         this.hover_timer = null;
16154                 }
16155         },
16156
16157         _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
16158                 // summary:
16159                 //              Cancels the pending-close timer because the close has been preempted
16160                 // tags:
16161                 //              private
16162                 if(popup._pendingClose_timer){
16163                         clearTimeout(popup._pendingClose_timer);
16164                         popup._pendingClose_timer = null;
16165                 }
16166         },
16167
16168         _stopFocusTimer: function(){
16169                 // summary:
16170                 //              Cancels the pending-focus timer because the menu was closed before focus occured
16171                 // tags:
16172                 //              private
16173                 if(this._focus_timer){
16174                         clearTimeout(this._focus_timer);
16175                         this._focus_timer = null;
16176                 }
16177         },
16178
16179         _getTopMenu: function(){
16180                 // summary:
16181                 //              Returns the top menu in this chain of Menus
16182                 // tags:
16183                 //              private
16184                 for(var top=this; top.parentMenu; top=top.parentMenu);
16185                 return top;
16186         },
16187
16188         onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
16189                 // summary:
16190                 //              Handle clicks on an item.
16191                 // tags:
16192                 //              private
16193
16194                 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
16195                 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
16196                         this._markActive();
16197                 }
16198
16199                 this.focusChild(item);
16200
16201                 if(item.disabled){ return false; }
16202
16203                 if(item.popup){
16204                         this._openPopup();
16205                 }else{
16206                         // before calling user defined handler, close hierarchy of menus
16207                         // and restore focus to place it was when menu was opened
16208                         this.onExecute();
16209
16210                         // user defined handler for click
16211                         item.onClick(evt);
16212                 }
16213         },
16214
16215         _openPopup: function(){
16216                 // summary:
16217                 //              Open the popup to the side of/underneath the current menu item
16218                 // tags:
16219                 //              protected
16220
16221                 this._stopPopupTimer();
16222                 var from_item = this.focusedChild;
16223                 if(!from_item){ return; } // the focused child lost focus since the timer was started
16224                 var popup = from_item.popup;
16225                 if(popup.isShowingNow){ return; }
16226                 if(this.currentPopup){
16227                         this._stopPendingCloseTimer(this.currentPopup);
16228                         dijit.popup.close(this.currentPopup);
16229                 }
16230                 popup.parentMenu = this;
16231                 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
16232                 var self = this;
16233                 dijit.popup.open({
16234                         parent: this,
16235                         popup: popup,
16236                         around: from_item.domNode,
16237                         orient: this._orient || (this.isLeftToRight() ?
16238                                                                         {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
16239                                                                         {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
16240                         onCancel: function(){ // called when the child menu is canceled
16241                                 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
16242                                 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
16243                                 self.focusChild(from_item);     // put focus back on my node
16244                                 self._cleanUp();                        // close the submenu (be sure this is done _after_ focus is moved)
16245                                 from_item._setSelected(true); // oops, _cleanUp() deselected the item
16246                                 self.focusedChild = from_item;  // and unset focusedChild
16247                         },
16248                         onExecute: dojo.hitch(this, "_cleanUp")
16249                 });
16250
16251                 this.currentPopup = popup;
16252                 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
16253                 popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
16254
16255                 if(popup.focus){
16256                         // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
16257                         // if the cursor happens to collide with the popup, it will generate an onmouseover event
16258                         // even though the mouse wasn't moved.   Use a setTimeout() to call popup.focus so that
16259                         // our focus() call overrides the onmouseover event, rather than vice-versa.  (#8742)
16260                         popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
16261                                 this._focus_timer = null;
16262                                 this.focus();
16263                         }), 0);
16264                 }
16265         },
16266
16267         _markActive: function(){
16268                 // summary:
16269                 //              Mark this menu's state as active.
16270                 //              Called when this Menu gets focus from:
16271                 //                      1) clicking it (mouse or via space/arrow key)
16272                 //                      2) being opened by a parent menu.
16273                 //              This is not called just from mouse hover.
16274                 //              Focusing a menu via TAB does NOT automatically set isActive
16275                 //              since TAB is a navigation operation and not a selection one.
16276                 //              For Windows apps, pressing the ALT key focuses the menubar
16277                 //              menus (similar to TAB navigation) but the menu is not active
16278                 //              (ie no dropdown) until an item is clicked.
16279                 this.isActive = true;
16280                 dojo.addClass(this.domNode, "dijitMenuActive");
16281                 dojo.removeClass(this.domNode, "dijitMenuPassive");
16282         },
16283
16284         onOpen: function(/*Event*/ e){
16285                 // summary:
16286                 //              Callback when this menu is opened.
16287                 //              This is called by the popup manager as notification that the menu
16288                 //              was opened.
16289                 // tags:
16290                 //              private
16291
16292                 this.isShowingNow = true;
16293                 this._markActive();
16294         },
16295
16296         _markInactive: function(){
16297                 // summary:
16298                 //              Mark this menu's state as inactive.
16299                 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
16300                 dojo.removeClass(this.domNode, "dijitMenuActive");
16301                 dojo.addClass(this.domNode, "dijitMenuPassive");
16302         },
16303
16304         onClose: function(){
16305                 // summary:
16306                 //              Callback when this menu is closed.
16307                 //              This is called by the popup manager as notification that the menu
16308                 //              was closed.
16309                 // tags:
16310                 //              private
16311
16312                 this._stopFocusTimer();
16313                 this._markInactive();
16314                 this.isShowingNow = false;
16315                 this.parentMenu = null;
16316         },
16317
16318         _closeChild: function(){
16319                 // summary:
16320                 //              Called when submenu is clicked or focus is lost.  Close hierarchy of menus.
16321                 // tags:
16322                 //              private
16323                 this._stopPopupTimer();
16324                 if(this.focusedChild){ // unhighlight the focused item
16325                         this.focusedChild._setSelected(false);
16326                         this.focusedChild._onUnhover();
16327                         this.focusedChild = null;
16328                 }
16329                 if(this.currentPopup){
16330                         // Close all popups that are open and descendants of this menu
16331                         dijit.popup.close(this.currentPopup);
16332                         this.currentPopup = null;
16333                 }
16334         },
16335
16336         _onItemFocus: function(/*MenuItem*/ item){
16337                 // summary:
16338                 //              Called when child of this Menu gets focus from:
16339                 //                      1) clicking it
16340                 //                      2) tabbing into it
16341                 //                      3) being opened by a parent menu.
16342                 //              This is not called just from mouse hover.
16343                 if(this._hoveredChild && this._hoveredChild != item){
16344                         this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
16345                 }
16346         },
16347
16348         _onBlur: function(){
16349                 // summary:
16350                 //              Called when focus is moved away from this Menu and it's submenus.
16351                 // tags:
16352                 //              protected
16353                 this._cleanUp();
16354                 this.inherited(arguments);
16355         },
16356
16357         _cleanUp: function(){
16358                 // summary:
16359                 //              Called when the user is done with this menu.  Closes hierarchy of menus.
16360                 // tags:
16361                 //              private
16362
16363                 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
16364                 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
16365                         this._markInactive();
16366                 }
16367         }
16368 });
16369
16370 dojo.declare("dijit.Menu",
16371         dijit._MenuBase,
16372         {
16373         // summary
16374         //              A context menu you can assign to multiple elements
16375
16376         // TODO: most of the code in here is just for context menu (right-click menu)
16377         // support.  In retrospect that should have been a separate class (dijit.ContextMenu).
16378         // Split them for 2.0
16379
16380         constructor: function(){
16381                 this._bindings = [];
16382         },
16383
16384         templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" waiRole=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=0>\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
16385
16386         baseClass: "dijitMenu",
16387
16388         // targetNodeIds: [const] String[]
16389         //              Array of dom node ids of nodes to attach to.
16390         //              Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
16391         targetNodeIds: [],
16392
16393         // contextMenuForWindow: [const] Boolean
16394         //              If true, right clicking anywhere on the window will cause this context menu to open.
16395         //              If false, must specify targetNodeIds.
16396         contextMenuForWindow: false,
16397
16398         // leftClickToOpen: [const] Boolean
16399         //              If true, menu will open on left click instead of right click, similiar to a file menu.
16400         leftClickToOpen: false,
16401
16402         // refocus: Boolean
16403         //              When this menu closes, re-focus the element which had focus before it was opened.
16404         refocus: true,
16405
16406         postCreate: function(){
16407                 if(this.contextMenuForWindow){
16408                         this.bindDomNode(dojo.body());
16409                 }else{
16410                         // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
16411                         // later attr('targetNodeIds', ...) call.   There's also a problem that targetNodeIds[]
16412                         // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
16413                         dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
16414                 }
16415                 var k = dojo.keys, l = this.isLeftToRight();
16416                 this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
16417                 this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
16418                 this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
16419         },
16420
16421         _onKeyPress: function(/*Event*/ evt){
16422                 // summary:
16423                 //              Handle keyboard based menu navigation.
16424                 // tags:
16425                 //              protected
16426
16427                 if(evt.ctrlKey || evt.altKey){ return; }
16428
16429                 switch(evt.charOrCode){
16430                         case this._openSubMenuKey:
16431                                 this._moveToPopup(evt);
16432                                 dojo.stopEvent(evt);
16433                                 break;
16434                         case this._closeSubMenuKey:
16435                                 if(this.parentMenu){
16436                                         if(this.parentMenu._isMenuBar){
16437                                                 this.parentMenu.focusPrev();
16438                                         }else{
16439                                                 this.onCancel(false);
16440                                         }
16441                                 }else{
16442                                         dojo.stopEvent(evt);
16443                                 }
16444                                 break;
16445                 }
16446         },
16447
16448         // thanks burstlib!
16449         _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
16450                 // summary:
16451                 //              Returns the window reference of the passed iframe
16452                 // tags:
16453                 //              private
16454                 var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
16455                         // Moz. TODO: is this available when defaultView isn't?
16456                         this._iframeContentDocument(iframe_el)['__parent__'] ||
16457                         (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
16458                 return win;     //      Window
16459         },
16460
16461         _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
16462                 // summary:
16463                 //              Returns a reference to the document object inside iframe_el
16464                 // tags:
16465                 //              protected
16466                 var doc = iframe_el.contentDocument // W3
16467                         || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
16468                         || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
16469                         || null;
16470                 return doc;     //      HTMLDocument
16471         },
16472
16473         bindDomNode: function(/*String|DomNode*/ node){
16474                 // summary:
16475                 //              Attach menu to given node
16476                 node = dojo.byId(node);
16477
16478                 var cn; // Connect node
16479
16480                 // Support context menus on iframes.   Rather than binding to the iframe itself we need
16481                 // to bind to the <body> node inside the iframe.
16482                 if(node.tagName.toLowerCase() == "iframe"){
16483                         var iframe = node,
16484                                 win = this._iframeContentWindow(iframe);
16485                         cn = dojo.withGlobal(win, dojo.body);
16486                 }else{
16487                         
16488                         // To capture these events at the top level, attach to <html>, not <body>.
16489                         // Otherwise right-click context menu just doesn't work.
16490                         cn = (node == dojo.body() ? dojo.doc.documentElement : node);
16491                 }
16492
16493
16494                 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
16495                 var binding = {
16496                         node: node,
16497                         iframe: iframe
16498                 };
16499
16500                 // Save info about binding in _bindings[], and make node itself record index(+1) into
16501                 // _bindings[] array.   Prefix w/_dijitMenu to avoid setting an attribute that may
16502                 // start with a number, which fails on FF/safari.
16503                 dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
16504
16505                 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
16506                 // loading yet, in which case we need to wait for the onload event first, and then connect
16507                 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
16508                 // we need to monitor keyboard events in addition to the oncontextmenu event.
16509                 var doConnects = dojo.hitch(this, function(cn){
16510                         return [
16511                                 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
16512                                 // rather than shift-F10?
16513                                 dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
16514                                         // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
16515                                         dojo.stopEvent(evt);
16516                                         this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
16517                                 }),
16518                                 dojo.connect(cn, "onkeydown", this, function(evt){
16519                                         if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
16520                                                 dojo.stopEvent(evt);
16521                                                 this._scheduleOpen(evt.target, iframe); // no coords - open near target node
16522                                         }
16523                                 })
16524                         ];      
16525                 });
16526                 binding.connects = cn ? doConnects(cn) : [];
16527
16528                 if(iframe){
16529                         // Setup handler to [re]bind to the iframe when the contents are initially loaded,
16530                         // and every time the contents change.
16531                         // Need to do this b/c we are actually binding to the iframe's <body> node.
16532                         // Note: can't use dojo.connect(), see #9609.
16533
16534                         binding.onloadHandler = dojo.hitch(this, function(){
16535                                 // want to remove old connections, but IE throws exceptions when trying to
16536                                 // access the <body> node because it's already gone, or at least in a state of limbo
16537
16538                                 var win = this._iframeContentWindow(iframe);
16539                                         cn = dojo.withGlobal(win, dojo.body);
16540                                 binding.connects = doConnects(cn);
16541                         });
16542                         if(iframe.addEventListener){
16543                                 iframe.addEventListener("load", binding.onloadHandler, false);
16544                         }else{
16545                                 iframe.attachEvent("onload", binding.onloadHandler);
16546                         }
16547                 }
16548         },
16549
16550         unBindDomNode: function(/*String|DomNode*/ nodeName){
16551                 // summary:
16552                 //              Detach menu from given node
16553
16554                 var node;
16555                 try{
16556                         node = dojo.byId(nodeName);
16557                 }catch(e){
16558                         // On IE the dojo.byId() call will get an exception if the attach point was
16559                         // the <body> node of an <iframe> that has since been reloaded (and thus the
16560                         // <body> node is in a limbo state of destruction.
16561                         return;
16562                 }
16563
16564                 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
16565                 var attrName = "_dijitMenu" + this.id;
16566                 if(node && dojo.hasAttr(node, attrName)){
16567                         var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
16568                         dojo.forEach(b.connects, dojo.disconnect);
16569
16570                         // Remove listener for iframe onload events
16571                         var iframe = b.iframe;
16572                         if(iframe){
16573                                 if(iframe.removeEventListener){
16574                                         iframe.removeEventListener("load", b.onloadHandler, false);
16575                                 }else{
16576                                         iframe.detachEvent("onload", b.onloadHandler);
16577                                 }
16578                         }
16579
16580                         dojo.removeAttr(node, attrName);
16581                         delete this._bindings[bid];
16582                 }
16583         },
16584
16585         _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
16586                 // summary:
16587                 //              Set timer to display myself.  Using a timer rather than displaying immediately solves
16588                 //              two problems:
16589                 //
16590                 //              1. IE: without the delay, focus work in "open" causes the system
16591                 //              context menu to appear in spite of stopEvent.
16592                 //
16593                 //              2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
16594                 //              even after a dojo.stopEvent(e).  (Shift-F10 on windows doesn't generate the
16595                 //              oncontextmenu event.)
16596
16597                 if(!this._openTimer){
16598                         this._openTimer = setTimeout(dojo.hitch(this, function(){
16599                                 delete this._openTimer;
16600                                 this._openMyself({
16601                                         target: target,
16602                                         iframe: iframe,
16603                                         coords: coords
16604                                 });
16605                         }), 1);
16606                 }
16607         },
16608
16609         _openMyself: function(args){
16610                 // summary:
16611                 //              Internal function for opening myself when the user does a right-click or something similar.
16612                 // args:
16613                 //              This is an Object containing:
16614                 //              * target:
16615                 //                      The node that is being clicked
16616                 //              * iframe:
16617                 //                      If an <iframe> is being clicked, iframe points to that iframe
16618                 //              * coords:
16619                 //                      Put menu at specified x/y position in viewport, or if iframe is
16620                 //                      specified, then relative to iframe.
16621                 //
16622                 //              _openMyself() formerly took the event object, and since various code references
16623                 //              evt.target (after connecting to _openMyself()), using an Object for parameters
16624                 //              (so that old code still works).
16625
16626                 var target = args.target,
16627                         iframe = args.iframe,
16628                         coords = args.coords;
16629
16630                 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
16631                 // then near the node the menu is assigned to.
16632                 if(coords){
16633                         if(iframe){
16634                                 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
16635                                 var od = target.ownerDocument,
16636                                         ifc = dojo.position(iframe, true),
16637                                         win = this._iframeContentWindow(iframe),
16638                                         scroll = dojo.withGlobal(win, "_docScroll", dojo);
16639         
16640                                 var cs = dojo.getComputedStyle(iframe),
16641                                         tp = dojo._toPixelValue,
16642                                         left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
16643                                         top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
16644
16645                                 coords.x += ifc.x + left - scroll.x;
16646                                 coords.y += ifc.y + top - scroll.y;
16647                         }
16648                 }else{
16649                         coords = dojo.position(target, true);
16650                         coords.x += 10;
16651                         coords.y += 10;
16652                 }
16653
16654                 var self=this;
16655                 var savedFocus = dijit.getFocus(this);
16656                 function closeAndRestoreFocus(){
16657                         // user has clicked on a menu or popup
16658                         if(self.refocus){
16659                                 dijit.focus(savedFocus);
16660                         }
16661                         dijit.popup.close(self);
16662                 }
16663                 dijit.popup.open({
16664                         popup: this,
16665                         x: coords.x,
16666                         y: coords.y,
16667                         onExecute: closeAndRestoreFocus,
16668                         onCancel: closeAndRestoreFocus,
16669                         orient: this.isLeftToRight() ? 'L' : 'R'
16670                 });
16671                 this.focus();
16672
16673                 this._onBlur = function(){
16674                         this.inherited('_onBlur', arguments);
16675                         // Usually the parent closes the child widget but if this is a context
16676                         // menu then there is no parent
16677                         dijit.popup.close(this);
16678                         // don't try to restore focus; user has clicked another part of the screen
16679                         // and set focus there
16680                 };
16681         },
16682
16683         uninitialize: function(){
16684                 dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
16685                 this.inherited(arguments);
16686         }
16687 }
16688 );
16689
16690 // Back-compat (TODO: remove in 2.0)
16691
16692
16693
16694
16695
16696
16697 }
16698
16699 if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16700 dojo._hasResource["dijit.form.Select"] = true;
16701 dojo.provide("dijit.form.Select");
16702
16703
16704
16705
16706
16707
16708
16709
16710 dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
16711         // summary:
16712         //              An internally-used menu for dropdown that allows us a vertical scrollbar
16713         buildRendering: function(){
16714                 // summary:
16715                 //              Stub in our own changes, so that our domNode is not a table
16716                 //              otherwise, we won't respond correctly to heights/overflows
16717                 this.inherited(arguments);
16718                 var o = (this.menuTableNode = this.domNode);
16719                 var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
16720                 if(o.parentNode){
16721                         o.parentNode.replaceChild(n, o);
16722                 }
16723                 dojo.removeClass(o, "dijitMenuTable");
16724                 n.className = o.className + " dijitSelectMenu";
16725                 o.className = "dijitReset dijitMenuTable";
16726                 dijit.setWaiRole(o,"listbox");
16727                 dijit.setWaiRole(n,"presentation");
16728                 n.appendChild(o);
16729         },
16730         resize: function(/*Object*/ mb){
16731                 // summary:
16732                 //              Overridden so that we are able to handle resizing our
16733                 //              internal widget.  Note that this is not a "full" resize
16734                 //              implementation - it only works correctly if you pass it a
16735                 //              marginBox.
16736                 //
16737                 // mb: Object
16738                 //              The margin box to set this dropdown to.
16739                 if(mb){
16740                         dojo.marginBox(this.domNode, mb);
16741                         if("w" in mb){
16742                                 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
16743                                 // 100% is safer than a pixel value because there may be a scroll bar with
16744                                 // browser/OS specific width.
16745                                 this.menuTableNode.style.width = "100%";
16746                         }
16747                 }
16748         }
16749 });
16750
16751 dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
16752         // summary:
16753         //              This is a "styleable" select box - it is basically a DropDownButton which
16754         //              can take a <select> as its input.
16755
16756         baseClass: "dijitSelect",
16757
16758         templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\twaiRole=\"combobox\" waiState=\"haspopup-true\"\n\t><tbody waiRole=\"presentation\"><tr waiRole=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" waiRole=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"  dojoAttachPoint=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} dojoAttachPoint=\"valueNode\" value=\"${value}\" waiState=\"hidden-true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" waiRole=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" waiRole=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" waiRole=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
16759
16760         // attributeMap: Object
16761         //              Add in our style to be applied to the focus node
16762         attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
16763
16764         // required: Boolean
16765         //              Can be true or false, default is false.
16766         required: false,
16767
16768         // state: String
16769         //              Shows current state (ie, validation result) of input (Normal, Warning, or Error)
16770         state: "",
16771
16772         //      tooltipPosition: String[]
16773         //              See description of dijit.Tooltip.defaultPosition for details on this parameter.
16774         tooltipPosition: [],
16775
16776         // emptyLabel: string
16777         //              What to display in an "empty" dropdown
16778         emptyLabel: "",
16779
16780         // _isLoaded: Boolean
16781         //              Whether or not we have been loaded
16782         _isLoaded: false,
16783
16784         // _childrenLoaded: Boolean
16785         //              Whether or not our children have been loaded
16786         _childrenLoaded: false,
16787
16788         _fillContent: function(){
16789                 // summary:
16790                 //              Set the value to be the first, or the selected index
16791                 this.inherited(arguments);
16792                 if(this.options.length && !this.value && this.srcNodeRef){
16793                         var si = this.srcNodeRef.selectedIndex;
16794                         this.value = this.options[si != -1 ? si : 0].value;
16795                 }
16796
16797                 // Create the dropDown widget
16798                 this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
16799                 dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
16800         },
16801
16802         _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
16803                 // summary:
16804                 //              For the given option, return the menu item that should be
16805                 //              used to display it.  This can be overridden as needed
16806                 if(!option.value){
16807                         // We are a separator (no label set for it)
16808                         return new dijit.MenuSeparator();
16809                 }else{
16810                         // Just a regular menu option
16811                         var click = dojo.hitch(this, "_setValueAttr", option);
16812                         var item = new dijit.MenuItem({
16813                                 option: option,
16814                                 label: option.label,
16815                                 onClick: click,
16816                                 disabled: option.disabled || false
16817                         });
16818                         dijit.setWaiRole(item.focusNode, "listitem");
16819                         return item;
16820                 }
16821         },
16822
16823         _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
16824                 // summary:
16825                 //              For the given option, add an option to our dropdown.
16826                 //              If the option doesn't have a value, then a separator is added
16827                 //              in that place.
16828                 if(this.dropDown){
16829                         this.dropDown.addChild(this._getMenuItemForOption(option));
16830                 }
16831         },
16832
16833         _getChildren: function(){
16834                 if(!this.dropDown){
16835                         return [];
16836                 }
16837                 return this.dropDown.getChildren();
16838         },
16839
16840         _loadChildren: function(/*Boolean*/ loadMenuItems){
16841                 // summary:
16842                 //              Resets the menu and the length attribute of the button - and
16843                 //              ensures that the label is appropriately set.
16844                 //      loadMenuItems: Boolean
16845                 //              actually loads the child menu items - we only do this when we are
16846                 //              populating for showing the dropdown.
16847
16848                 if(loadMenuItems === true){
16849                         // this.inherited destroys this.dropDown's child widgets (MenuItems).
16850                         // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
16851                         // issues later in _setSelected). (see #10296)
16852                         if(this.dropDown){
16853                                 delete this.dropDown.focusedChild;
16854                         }
16855                         if(this.options.length){
16856                                 this.inherited(arguments);
16857                         }else{
16858                                 // Drop down menu is blank but add one blank entry just so something appears on the screen
16859                                 // to let users know that they are no choices (mimicing native select behavior)
16860                                 dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
16861                                 var item = new dijit.MenuItem({label: "&nbsp;"});
16862                                 this.dropDown.addChild(item);
16863                         }
16864                 }else{
16865                         this._updateSelection();
16866                 }
16867
16868                 var len = this.options.length;
16869                 this._isLoaded = false;
16870                 this._childrenLoaded = true;
16871
16872                 if(!this._loadingStore){
16873                         // Don't call this if we are loading - since we will handle it later
16874                         this._setValueAttr(this.value);
16875                 }
16876         },
16877
16878         _setValueAttr: function(value){
16879                 this.inherited(arguments);
16880                 dojo.attr(this.valueNode, "value", this.get("value"));
16881         },
16882
16883         _setDisplay: function(/*String*/ newDisplay){
16884                 // summary:
16885                 //              sets the display for the given value (or values)
16886                 this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' +
16887                                         (newDisplay || this.emptyLabel || "&nbsp;") +
16888                                         '</span>';
16889                 dijit.setWaiState(this.focusNode, "valuetext", (newDisplay || this.emptyLabel || "&nbsp;") );
16890         },
16891
16892         validate: function(/*Boolean*/ isFocused){
16893                 // summary:
16894                 //              Called by oninit, onblur, and onkeypress.
16895                 // description:
16896                 //              Show missing or invalid messages if appropriate, and highlight textbox field.
16897                 //              Used when a select is initially set to no value and the user is required to
16898                 //              set the value.
16899                 
16900                 var isValid = this.isValid(isFocused);
16901                 this.state = isValid ? "" : "Error";
16902                 this._setStateClass();
16903                 dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
16904                 var message = isValid ? "" : this._missingMsg;
16905                 if(this._message !== message){
16906                         this._message = message;
16907                         dijit.hideTooltip(this.domNode);
16908                         if(message){
16909                                 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
16910                         }
16911                 }
16912                 return isValid;
16913         },
16914
16915         isValid: function(/*Boolean*/ isFocused){
16916                 // summary:
16917                 //              Whether or not this is a valid value.   The only way a Select
16918                 //              can be invalid is when it's required but nothing is selected.
16919                 return (!this.required || !(/^\s*$/.test(this.value)));
16920         },
16921
16922         reset: function(){
16923                 // summary:
16924                 //              Overridden so that the state will be cleared.
16925                 this.inherited(arguments);
16926                 dijit.hideTooltip(this.domNode);
16927                 this.state = "";
16928                 this._setStateClass();
16929                 delete this._message;
16930         },
16931
16932         postMixInProperties: function(){
16933                 // summary:
16934                 //              set the missing message
16935                 this.inherited(arguments);
16936                 this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
16937                                                                         this.lang).missingMessage;
16938         },
16939
16940         postCreate: function(){
16941                 this.inherited(arguments);
16942                 if(this.tableNode.style.width){
16943                         dojo.addClass(this.domNode, this.baseClass + "FixedWidth");
16944                 }
16945         },
16946
16947         isLoaded: function(){
16948                 return this._isLoaded;
16949         },
16950
16951         loadDropDown: function(/*Function*/ loadCallback){
16952                 // summary:
16953                 //              populates the menu
16954                 this._loadChildren(true);
16955                 this._isLoaded = true;
16956                 loadCallback();
16957         },
16958
16959         closeDropDown: function(){
16960                 // overriding _HasDropDown.closeDropDown()
16961                 this.inherited(arguments);
16962
16963                 if(this.dropDown && this.dropDown.menuTableNode){
16964                         // Erase possible width: 100% setting from _SelectMenu.resize().
16965                         // Leaving it would interfere with the next openDropDown() call, which
16966                         // queries the natural size of the drop down.
16967                         this.dropDown.menuTableNode.style.width = "";
16968                 }
16969         },
16970
16971         uninitialize: function(preserveDom){
16972                 if(this.dropDown && !this.dropDown._destroyed){
16973                         this.dropDown.destroyRecursive(preserveDom);
16974                         delete this.dropDown;
16975                 }
16976                 this.inherited(arguments);
16977         }
16978 });
16979
16980 }
16981
16982 if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16983 dojo._hasResource["dijit.form.SimpleTextarea"] = true;
16984 dojo.provide("dijit.form.SimpleTextarea");
16985
16986
16987
16988 dojo.declare("dijit.form.SimpleTextarea",
16989         dijit.form.TextBox,
16990         {
16991         // summary:
16992         //              A simple textarea that degrades, and responds to
16993         //              minimal LayoutContainer usage, and works with dijit.form.Form.
16994         //              Doesn't automatically size according to input, like Textarea.
16995         //
16996         // example:
16997         //      |       <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
16998         //
16999         // example:
17000         //      |       new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
17001
17002         baseClass: "dijitTextBox dijitTextArea",
17003
17004         attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
17005                 rows:"textbox", cols: "textbox"
17006         }),
17007
17008         // rows: Number
17009         //              The number of rows of text.
17010         rows: "3",
17011
17012         // rows: Number
17013         //              The number of characters per line.
17014         cols: "20",
17015
17016         templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
17017
17018         postMixInProperties: function(){
17019                 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
17020                 if(!this.value && this.srcNodeRef){
17021                         this.value = this.srcNodeRef.value;
17022                 }
17023                 this.inherited(arguments);
17024         },
17025
17026         filter: function(/*String*/ value){
17027                 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
17028                 // as \r\n instead of just \n
17029                 if(value){
17030                         value = value.replace(/\r/g,"");
17031                 }
17032                 return this.inherited(arguments);
17033         },
17034
17035         postCreate: function(){
17036                 this.inherited(arguments);
17037                 if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
17038                         dojo.addClass(this.textbox, "dijitTextAreaCols");
17039                 }
17040         },
17041
17042         _previousValue: "",
17043         _onInput: function(/*Event?*/ e){
17044                 // Override TextBox._onInput() to enforce maxLength restriction
17045                 if(this.maxLength){
17046                         var maxLength = parseInt(this.maxLength);
17047                         var value = this.textbox.value.replace(/\r/g,'');
17048                         var overflow = value.length - maxLength;
17049                         if(overflow > 0){
17050                                 if(e){ dojo.stopEvent(e); }
17051                                 var textarea = this.textbox;
17052                                 if(textarea.selectionStart){
17053                                         var pos = textarea.selectionStart;
17054                                         var cr = 0;
17055                                         if(dojo.isOpera){
17056                                                 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
17057                                         }
17058                                         this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
17059                                         textarea.setSelectionRange(pos-overflow, pos-overflow);
17060                                 }else if(dojo.doc.selection){ //IE
17061                                         textarea.focus();
17062                                         var range = dojo.doc.selection.createRange();
17063                                         // delete overflow characters
17064                                         range.moveStart("character", -overflow);
17065                                         range.text = '';
17066                                         // show cursor
17067                                         range.select();
17068                                 }
17069                         }
17070                         this._previousValue = this.textbox.value;
17071                 }
17072                 this.inherited(arguments);
17073         }
17074 });
17075
17076 }
17077
17078 if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17079 dojo._hasResource["dijit.InlineEditBox"] = true;
17080 dojo.provide("dijit.InlineEditBox");
17081
17082
17083
17084
17085
17086
17087
17088
17089
17090
17091 dojo.declare("dijit.InlineEditBox",
17092         dijit._Widget,
17093         {
17094         // summary:
17095         //              An element with in-line edit capabilites
17096         //
17097         // description:
17098         //              Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
17099         //              when you click it, an editor shows up in place of the original
17100         //              text.  Optionally, Save and Cancel button are displayed below the edit widget.
17101         //              When Save is clicked, the text is pulled from the edit
17102         //              widget and redisplayed and the edit widget is again hidden.
17103         //              By default a plain Textarea widget is used as the editor (or for
17104         //              inline values a TextBox), but you can specify an editor such as
17105         //              dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
17106         //              An edit widget must support the following API to be used:
17107         //                      - displayedValue or value as initialization parameter,
17108         //                      and available through set('displayedValue') / set('value')
17109         //                      - void focus()
17110         //                      - DOM-node focusNode = node containing editable text
17111
17112         // editing: [readonly] Boolean
17113         //              Is the node currently in edit mode?
17114         editing: false,
17115
17116         // autoSave: Boolean
17117         //              Changing the value automatically saves it; don't have to push save button
17118         //              (and save button isn't even displayed)
17119         autoSave: true,
17120
17121         // buttonSave: String
17122         //              Save button label
17123         buttonSave: "",
17124
17125         // buttonCancel: String
17126         //              Cancel button label
17127         buttonCancel: "",
17128
17129         // renderAsHtml: Boolean
17130         //              Set this to true if the specified Editor's value should be interpreted as HTML
17131         //              rather than plain text (ex: `dijit.Editor`)
17132         renderAsHtml: false,
17133
17134         // editor: String
17135         //              Class name for Editor widget
17136         editor: "dijit.form.TextBox",
17137
17138         // editorWrapper: String
17139         //              Class name for widget that wraps the editor widget, displaying save/cancel
17140         //              buttons.
17141         editorWrapper: "dijit._InlineEditor",
17142
17143         // editorParams: Object
17144         //              Set of parameters for editor, like {required: true}
17145         editorParams: {},
17146
17147         onChange: function(value){
17148                 // summary:
17149                 //              Set this handler to be notified of changes to value.
17150                 // tags:
17151                 //              callback
17152         },
17153
17154         onCancel: function(){
17155                 // summary:
17156                 //              Set this handler to be notified when editing is cancelled.
17157                 // tags:
17158                 //              callback
17159         },
17160
17161         // width: String
17162         //              Width of editor.  By default it's width=100% (ie, block mode).
17163         width: "100%",
17164
17165         // value: String
17166         //              The display value of the widget in read-only mode
17167         value: "",
17168
17169         // noValueIndicator: [const] String
17170         //              The text that gets displayed when there is no value (so that the user has a place to click to edit)
17171         noValueIndicator: dojo.isIE <= 6 ?      // font-family needed on IE6 but it messes up IE8
17172                 "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>" :
17173                 "<span style='text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
17174
17175         constructor: function(){
17176                 // summary:
17177                 //              Sets up private arrays etc.
17178                 // tags:
17179                 //              private
17180                 this.editorParams = {};
17181         },
17182
17183         postMixInProperties: function(){
17184                 this.inherited(arguments);
17185
17186                 // save pointer to original source node, since Widget nulls-out srcNodeRef
17187                 this.displayNode = this.srcNodeRef;
17188
17189                 // connect handlers to the display node
17190                 var events = {
17191                         ondijitclick: "_onClick",
17192                         onmouseover: "_onMouseOver",
17193                         onmouseout: "_onMouseOut",
17194                         onfocus: "_onMouseOver",
17195                         onblur: "_onMouseOut"
17196                 };
17197                 for(var name in events){
17198                         this.connect(this.displayNode, name, events[name]);
17199                 }
17200                 dijit.setWaiRole(this.displayNode, "button");
17201                 if(!this.displayNode.getAttribute("tabIndex")){
17202                         this.displayNode.setAttribute("tabIndex", 0);
17203                 }
17204
17205                 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
17206                    this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML :
17207                       (this.displayNode.innerText||this.displayNode.textContent||""));
17208                 }
17209                 if(!this.value){
17210                     this.displayNode.innerHTML = this.noValueIndicator;
17211                 }
17212
17213                 dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode');
17214         },
17215
17216         setDisabled: function(/*Boolean*/ disabled){
17217                 // summary:
17218                 //              Deprecated.   Use set('disabled', ...) instead.
17219                 // tags:
17220                 //              deprecated
17221                 dojo.deprecated("dijit.InlineEditBox.setDisabled() is deprecated.  Use set('disabled', bool) instead.", "", "2.0");
17222                 this.set('disabled', disabled);
17223         },
17224
17225         _setDisabledAttr: function(/*Boolean*/ disabled){
17226                 // summary:
17227                 //              Hook to make set("disabled", ...) work.
17228                 //              Set disabled state of widget.
17229                 this.disabled = disabled;
17230                 dijit.setWaiState(this.domNode, "disabled", disabled);
17231                 if(disabled){
17232                         this.displayNode.removeAttribute("tabIndex");
17233                 }else{
17234                         this.displayNode.setAttribute("tabIndex", 0);
17235                 }
17236                 dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
17237         },
17238
17239         _onMouseOver: function(){
17240                 // summary:
17241                 //              Handler for onmouseover and onfocus event.
17242                 // tags:
17243                 //              private
17244                 if(!this.disabled){
17245                         dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
17246                 }
17247         },
17248
17249         _onMouseOut: function(){
17250                 // summary:
17251                 //              Handler for onmouseout and onblur event.
17252                 // tags:
17253                 //              private
17254                 dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
17255         },
17256
17257         _onClick: function(/*Event*/ e){
17258                 // summary:
17259                 //              Handler for onclick event.
17260                 // tags:
17261                 //              private
17262                 if(this.disabled){ return; }
17263                 if(e){ dojo.stopEvent(e); }
17264                 this._onMouseOut();
17265
17266                 // Since FF gets upset if you move a node while in an event handler for that node...
17267                 setTimeout(dojo.hitch(this, "edit"), 0);
17268         },
17269
17270         edit: function(){
17271                 // summary:
17272                 //              Display the editor widget in place of the original (read only) markup.
17273                 // tags:
17274                 //              private
17275
17276                 if(this.disabled || this.editing){ return; }
17277                 this.editing = true;
17278
17279                 // save some display node values that can be restored later
17280                 this._savedPosition = dojo.style(this.displayNode, "position") || "static";
17281                 this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1";
17282                 this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0";
17283
17284                 if(this.wrapperWidget){
17285                         var ew = this.wrapperWidget.editWidget;
17286                         ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
17287                 }else{
17288                         // Placeholder for edit widget
17289                         // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
17290                         // when Calendar dropdown appears, which happens automatically on focus.
17291                         var placeholder = dojo.create("span", null, this.domNode, "before");
17292
17293                         // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
17294                         var ewc = dojo.getObject(this.editorWrapper);
17295                         this.wrapperWidget = new ewc({
17296                                 value: this.value,
17297                                 buttonSave: this.buttonSave,
17298                                 buttonCancel: this.buttonCancel,
17299                                 dir: this.dir,
17300                                 lang: this.lang,
17301                                 tabIndex: this._savedTabIndex,
17302                                 editor: this.editor,
17303                                 inlineEditBox: this,
17304                                 sourceStyle: dojo.getComputedStyle(this.displayNode),
17305                                 save: dojo.hitch(this, "save"),
17306                                 cancel: dojo.hitch(this, "cancel")
17307                         }, placeholder);
17308                 }
17309                 var ww = this.wrapperWidget;
17310
17311                 if(dojo.isIE){
17312                         dijit.focus(dijit.getFocus()); // IE (at least 8) needs help with tab order changes
17313                 }
17314                 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
17315                 // and then when it's finished rendering, we switch from display mode to editor
17316                 // position:absolute releases screen space allocated to the display node
17317                 // opacity:0 is the same as visibility:hidden but is still focusable
17318                 // visiblity:hidden removes focus outline
17319
17320                 dojo.style(this.displayNode, { position: "absolute", opacity: "0", display: "none" }); // makes display node invisible, display style used for focus-ability
17321                 dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
17322                 dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
17323
17324                 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
17325                 // focus can be shifted without incident.  (browser may needs some time to render the editor.)
17326                 setTimeout(dojo.hitch(this, function(){
17327                         ww.focus(); // both nodes are showing, so we can switch focus safely
17328                         ww._resetValue = ww.getValue();
17329                 }), 0);
17330         },
17331
17332         _onBlur: function(){
17333                 // summary:
17334                 //              Called when focus moves outside the InlineEditBox.
17335                 //              Performs garbage collection.
17336                 // tags:
17337                 //              private
17338
17339                 this.inherited(arguments);
17340                 if(!this.editing){
17341                         /* causes IE focus problems, see TooltipDialog_a11y.html...
17342                         setTimeout(dojo.hitch(this, function(){
17343                                 if(this.wrapperWidget){
17344                                         this.wrapperWidget.destroy();
17345                                         delete this.wrapperWidget;
17346                                 }
17347                         }), 0);
17348                         */
17349                 }
17350         },
17351
17352         destroy: function(){
17353                 if(this.wrapperWidget){
17354                         this.wrapperWidget.destroy();
17355                         delete this.wrapperWidget;
17356                 }
17357                 this.inherited(arguments);
17358         },
17359
17360         _showText: function(/*Boolean*/ focus){
17361                 // summary:
17362                 //              Revert to display mode, and optionally focus on display node
17363                 // tags:
17364                 //              private
17365
17366                 var ww = this.wrapperWidget;
17367                 dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
17368                 dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity, display: "" }); // make the original text visible
17369                 dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex);
17370                 if(focus){
17371                         dijit.focus(this.displayNode);
17372                 }
17373         },
17374
17375         save: function(/*Boolean*/ focus){
17376                 // summary:
17377                 //              Save the contents of the editor and revert to display mode.
17378                 // focus: Boolean
17379                 //              Focus on the display mode text
17380                 // tags:
17381                 //              private
17382
17383                 if(this.disabled || !this.editing){ return; }
17384                 this.editing = false;
17385
17386                 var ww = this.wrapperWidget;
17387                 var value = ww.getValue();
17388                 this.set('value', value); // display changed, formatted value
17389
17390                 // tell the world that we have changed
17391                 setTimeout(dojo.hitch(this, "onChange", value), 0); // setTimeout prevents browser freeze for long-running event handlers
17392
17393                 this._showText(focus); // set focus as needed
17394         },
17395
17396         setValue: function(/*String*/ val){
17397                 // summary:
17398                 //              Deprecated.   Use set('value', ...) instead.
17399                 // tags:
17400                 //              deprecated
17401                 dojo.deprecated("dijit.InlineEditBox.setValue() is deprecated.  Use set('value', ...) instead.", "", "2.0");
17402                 return this.set("value", val);
17403         },
17404
17405         _setValueAttr: function(/*String*/ val){
17406                 // summary:
17407                 //              Hook to make set("value", ...) work.
17408                 //              Inserts specified HTML value into this node, or an "input needed" character if node is blank.
17409
17410                 this.value = val = dojo.trim(val);
17411                 if(!this.renderAsHtml){
17412                         val = val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
17413                 }
17414                 this.displayNode.innerHTML = val || this.noValueIndicator;
17415         },
17416
17417         getValue: function(){
17418                 // summary:
17419                 //              Deprecated.   Use get('value') instead.
17420                 // tags:
17421                 //              deprecated
17422                 dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated.  Use get('value') instead.", "", "2.0");
17423                 return this.get("value");
17424         },
17425
17426         cancel: function(/*Boolean*/ focus){
17427                 // summary:
17428                 //              Revert to display mode, discarding any changes made in the editor
17429                 // tags:
17430                 //              private
17431
17432                 if(this.disabled || !this.editing){ return; }
17433                 this.editing = false;
17434
17435                 // tell the world that we have no changes
17436                 setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
17437
17438                 this._showText(focus);
17439         }
17440 });
17441
17442 dojo.declare(
17443         "dijit._InlineEditor",
17444          [dijit._Widget, dijit._Templated],
17445 {
17446         // summary:
17447         //              Internal widget used by InlineEditBox, displayed when in editing mode
17448         //              to display the editor and maybe save/cancel buttons.  Calling code should
17449         //              connect to save/cancel methods to detect when editing is finished
17450         //
17451         //              Has mainly the same parameters as InlineEditBox, plus these values:
17452         //
17453         // style: Object
17454         //              Set of CSS attributes of display node, to replicate in editor
17455         //
17456         // value: String
17457         //              Value as an HTML string or plain text string, depending on renderAsHTML flag
17458
17459         templateString: dojo.cache("dijit", "templates/InlineEditBox.html", "<span dojoAttachPoint=\"editNode\" waiRole=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdojoAttachEvent=\"onkeypress: _onKeyPress\"\n\t><span dojoAttachPoint=\"editorPlaceholder\"></span\n\t><span dojoAttachPoint=\"buttonContainer\"\n\t\t><button class='saveButton' dojoAttachPoint=\"saveButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:save\" label=\"${buttonSave}\"></button\n\t\t><button class='cancelButton' dojoAttachPoint=\"cancelButton\" dojoType=\"dijit.form.Button\" dojoAttachEvent=\"onClick:cancel\" label=\"${buttonCancel}\"></button\n\t></span\n></span>\n"),
17460         widgetsInTemplate: true,
17461
17462         postMixInProperties: function(){
17463                 this.inherited(arguments);
17464                 this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
17465                 dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
17466                         if(!this[prop]){ this[prop] = this.messages[prop]; }
17467                 }, this);
17468         },
17469
17470         postCreate: function(){
17471                 // Create edit widget in place in the template
17472                 var cls = dojo.getObject(this.editor);
17473
17474                 // Copy the style from the source
17475                 // Don't copy ALL properties though, just the necessary/applicable ones.
17476                 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
17477                 // is a relative value like 200%, rather than an absolute value like 24px, and
17478                 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
17479                 var srcStyle = this.sourceStyle,
17480                         editStyle = "line-height:" + srcStyle.lineHeight + ";",
17481                         destStyle = dojo.getComputedStyle(this.domNode);
17482                 dojo.forEach(["Weight","Family","Size","Style"], function(prop){
17483                         var textStyle = srcStyle["font"+prop],
17484                                 wrapperStyle = destStyle["font"+prop];
17485                         if(wrapperStyle != textStyle){
17486                                 editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
17487                         }
17488                 }, this);
17489                 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
17490                         this.domNode.style[prop] = srcStyle[prop];
17491                 }, this);
17492                 var width = this.inlineEditBox.width;
17493                 if(width == "100%"){
17494                         // block mode
17495                         editStyle += "width:100%;";
17496                         this.domNode.style.display = "block";
17497                 }else{
17498                         // inline-block mode
17499                         editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
17500                 }
17501                 var editorParams = dojo.delegate(this.inlineEditBox.editorParams, {
17502                         style: editStyle,
17503                         dir: this.dir,
17504                         lang: this.lang
17505                 });
17506                 editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
17507                 var ew = (this.editWidget = new cls(editorParams, this.editorPlaceholder));
17508
17509                 if(this.inlineEditBox.autoSave){
17510                         // Remove the save/cancel buttons since saving is done by simply tabbing away or
17511                         // selecting a value from the drop down list
17512                         dojo.destroy(this.buttonContainer);
17513
17514                         // Selecting a value from a drop down list causes an onChange event and then we save
17515                         this.connect(ew, "onChange", "_onChange");
17516
17517                         // ESC and TAB should cancel and save.  Note that edit widgets do a stopEvent() on ESC key (to
17518                         // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
17519                         // so this is the only way we can see the key press event.
17520                         this.connect(ew, "onKeyPress", "_onKeyPress");
17521                 }else{
17522                         // If possible, enable/disable save button based on whether the user has changed the value
17523                         if("intermediateChanges" in cls.prototype){
17524                                 ew.set("intermediateChanges", true);
17525                                 this.connect(ew, "onChange", "_onIntermediateChange");
17526                                 this.saveButton.set("disabled", true);
17527                         }
17528                 }
17529         },
17530
17531         _onIntermediateChange: function(val){
17532                 // summary:
17533                 //              Called for editor widgets that support the intermediateChanges=true flag as a way
17534                 //              to detect when to enable/disabled the save button
17535                 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
17536         },
17537
17538         destroy: function(){
17539                 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
17540                 this.inherited(arguments);
17541         },
17542
17543         getValue: function(){
17544                 // summary:
17545                 //              Return the [display] value of the edit widget
17546                 var ew = this.editWidget;
17547                 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
17548         },
17549
17550         _onKeyPress: function(e){
17551                 // summary:
17552                 //              Handler for keypress in the edit box in autoSave mode.
17553                 // description:
17554                 //              For autoSave widgets, if Esc/Enter, call cancel/save.
17555                 // tags:
17556                 //              private
17557
17558                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
17559                         if(e.altKey || e.ctrlKey){ return; }
17560                         // If Enter/Esc pressed, treat as save/cancel.
17561                         if(e.charOrCode == dojo.keys.ESCAPE){
17562                                 dojo.stopEvent(e);
17563                                 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
17564                         }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){
17565                                 dojo.stopEvent(e);
17566                                 this._onChange(); // fire _onBlur and then save
17567                         }
17568
17569                         // _onBlur will handle TAB automatically by allowing
17570                         // the TAB to change focus before we mess with the DOM: #6227
17571                         // Expounding by request:
17572                         //      The current focus is on the edit widget input field.
17573                         //      save() will hide and destroy this widget.
17574                         //      We want the focus to jump from the currently hidden
17575                         //      displayNode, but since it's hidden, it's impossible to
17576                         //      unhide it, focus it, and then have the browser focus
17577                         //      away from it to the next focusable element since each
17578                         //      of these events is asynchronous and the focus-to-next-element
17579                         //      is already queued.
17580                         //      So we allow the browser time to unqueue the move-focus event
17581                         //      before we do all the hide/show stuff.
17582                 }
17583         },
17584
17585         _onBlur: function(){
17586                 // summary:
17587                 //              Called when focus moves outside the editor
17588                 // tags:
17589                 //              private
17590
17591                 this.inherited(arguments);
17592                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
17593                         if(this.getValue() == this._resetValue){
17594                                 this.cancel(false);
17595                         }else if(this.enableSave()){
17596                                 this.save(false);
17597                         }
17598                 }
17599         },
17600
17601         _onChange: function(){
17602                 // summary:
17603                 //              Called when the underlying widget fires an onChange event,
17604                 //              such as when the user selects a value from the drop down list of a ComboBox,
17605                 //              which means that the user has finished entering the value and we should save.
17606                 // tags:
17607                 //              private
17608
17609                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
17610                         dojo.style(this.inlineEditBox.displayNode, { display: "" });
17611                         dijit.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
17612                 }
17613         },
17614
17615         enableSave: function(){
17616                 // summary:
17617                 //              User overridable function returning a Boolean to indicate
17618                 //              if the Save button should be enabled or not - usually due to invalid conditions
17619                 // tags:
17620                 //              extension
17621                 return (
17622                         this.editWidget.isValid
17623                         ? this.editWidget.isValid()
17624                         : true
17625                 );
17626         },
17627
17628         focus: function(){
17629                 // summary:
17630                 //              Focus the edit widget.
17631                 // tags:
17632                 //              protected
17633
17634                 this.editWidget.focus();
17635                 setTimeout(dojo.hitch(this, function(){
17636                         if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
17637                                 dijit.selectInputText(this.editWidget.focusNode);
17638                         }
17639                 }), 0);
17640         }
17641 });
17642
17643 }
17644
17645 if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17646 dojo._hasResource["dojo.cookie"] = true;
17647 dojo.provide("dojo.cookie");
17648
17649
17650
17651 /*=====
17652 dojo.__cookieProps = function(){
17653         //      expires: Date|String|Number?
17654         //              If a number, the number of days from today at which the cookie
17655         //              will expire. If a date, the date past which the cookie will expire.
17656         //              If expires is in the past, the cookie will be deleted.
17657         //              If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
17658         //      path: String?
17659         //              The path to use for the cookie.
17660         //      domain: String?
17661         //              The domain to use for the cookie.
17662         //      secure: Boolean?
17663         //              Whether to only send the cookie on secure connections
17664         this.expires = expires;
17665         this.path = path;
17666         this.domain = domain;
17667         this.secure = secure;
17668 }
17669 =====*/
17670
17671
17672 dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
17673         //      summary: 
17674         //              Get or set a cookie.
17675         //      description:
17676         //              If one argument is passed, returns the value of the cookie
17677         //              For two or more arguments, acts as a setter.
17678         //      name:
17679         //              Name of the cookie
17680         //      value:
17681         //              Value for the cookie
17682         //      props: 
17683         //              Properties for the cookie
17684         //      example:
17685         //              set a cookie with the JSON-serialized contents of an object which
17686         //              will expire 5 days from now:
17687         //      |       dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
17688         //      
17689         //      example:
17690         //              de-serialize a cookie back into a JavaScript object:
17691         //      |       var config = dojo.fromJson(dojo.cookie("configObj"));
17692         //      
17693         //      example:
17694         //              delete a cookie:
17695         //      |       dojo.cookie("configObj", null, {expires: -1});
17696         var c = document.cookie;
17697         if(arguments.length == 1){
17698                 var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
17699                 return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
17700         }else{
17701                 props = props || {};
17702 // FIXME: expires=0 seems to disappear right away, not on close? (FF3)  Change docs?
17703                 var exp = props.expires;
17704                 if(typeof exp == "number"){ 
17705                         var d = new Date();
17706                         d.setTime(d.getTime() + exp*24*60*60*1000);
17707                         exp = props.expires = d;
17708                 }
17709                 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
17710
17711                 value = encodeURIComponent(value);
17712                 var updatedCookie = name + "=" + value, propName;
17713                 for(propName in props){
17714                         updatedCookie += "; " + propName;
17715                         var propValue = props[propName];
17716                         if(propValue !== true){ updatedCookie += "=" + propValue; }
17717                 }
17718                 document.cookie = updatedCookie;
17719         }
17720 };
17721
17722 dojo.cookie.isSupported = function(){
17723         //      summary:
17724         //              Use to determine if the current browser supports cookies or not.
17725         //              
17726         //              Returns true if user allows cookies.
17727         //              Returns false if user doesn't allow cookies.
17728
17729         if(!("cookieEnabled" in navigator)){
17730                 this("__djCookieTest__", "CookiesAllowed");
17731                 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
17732                 if(navigator.cookieEnabled){
17733                         this("__djCookieTest__", "", {expires: -1});
17734                 }
17735         }
17736         return navigator.cookieEnabled;
17737 };
17738
17739 }
17740
17741 if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17742 dojo._hasResource["dijit.layout.StackController"] = true;
17743 dojo.provide("dijit.layout.StackController");
17744
17745
17746
17747
17748
17749
17750
17751 dojo.declare(
17752                 "dijit.layout.StackController",
17753                 [dijit._Widget, dijit._Templated, dijit._Container],
17754                 {
17755                         // summary:
17756                         //              Set of buttons to select a page in a page list.
17757                         // description:
17758                         //              Monitors the specified StackContainer, and whenever a page is
17759                         //              added, deleted, or selected, updates itself accordingly.
17760
17761                         templateString: "<span wairole='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
17762
17763                         // containerId: [const] String
17764                         //              The id of the page container that I point to
17765                         containerId: "",
17766
17767                         // buttonWidget: [const] String
17768                         //              The name of the button widget to create to correspond to each page
17769                         buttonWidget: "dijit.layout._StackButton",
17770
17771                         postCreate: function(){
17772                                 dijit.setWaiRole(this.domNode, "tablist");
17773
17774                                 this.pane2button = {};          // mapping from pane id to buttons
17775                                 this.pane2handles = {};         // mapping from pane id to this.connect() handles
17776
17777                                 // Listen to notifications from StackContainer
17778                                 this.subscribe(this.containerId+"-startup", "onStartup");
17779                                 this.subscribe(this.containerId+"-addChild", "onAddChild");
17780                                 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
17781                                 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
17782                                 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
17783                         },
17784
17785                         onStartup: function(/*Object*/ info){
17786                                 // summary:
17787                                 //              Called after StackContainer has finished initializing
17788                                 // tags:
17789                                 //              private
17790                                 dojo.forEach(info.children, this.onAddChild, this);
17791                                 if(info.selected){
17792                                         // Show button corresponding to selected pane (unless selected
17793                                         // is null because there are no panes)
17794                                         this.onSelectChild(info.selected);
17795                                 }
17796                         },
17797
17798                         destroy: function(){
17799                                 for(var pane in this.pane2button){
17800                                         this.onRemoveChild(dijit.byId(pane));
17801                                 }
17802                                 this.inherited(arguments);
17803                         },
17804
17805                         onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
17806                                 // summary:
17807                                 //              Called whenever a page is added to the container.
17808                                 //              Create button corresponding to the page.
17809                                 // tags:
17810                                 //              private
17811
17812                                 // create an instance of the button widget
17813                                 var cls = dojo.getObject(this.buttonWidget);
17814                                 var button = new cls({
17815                                         id: this.id + "_" + page.id,
17816                                         label: page.title,
17817                                         dir: page.dir,
17818                                         lang: page.lang,
17819                                         showLabel: page.showTitle,
17820                                         iconClass: page.iconClass,
17821                                         closeButton: page.closable,
17822                                         title: page.tooltip
17823                                 });
17824                                 dijit.setWaiState(button.focusNode,"selected", "false");
17825                                 this.pane2handles[page.id] = [
17826                                         this.connect(page, 'set', function(name, value){
17827                                                 var buttonAttr = {
17828                                                         title: 'label',
17829                                                         showTitle: 'showLabel',
17830                                                         iconClass: 'iconClass',
17831                                                         closable: 'closeButton',
17832                                                         tooltip: 'title'
17833                                                 }[name];
17834                                                 if(buttonAttr){
17835                                                         button.set(buttonAttr, value);
17836                                                 }
17837                                         }),
17838                                         this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
17839                                         this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
17840                                 ];
17841                                 this.addChild(button, insertIndex);
17842                                 this.pane2button[page.id] = button;
17843                                 page.controlButton = button;    // this value might be overwritten if two tabs point to same container
17844                                 if(!this._currentChild){ // put the first child into the tab order
17845                                         button.focusNode.setAttribute("tabIndex", "0");
17846                                         dijit.setWaiState(button.focusNode, "selected", "true");
17847                                         this._currentChild = page;
17848                                 }
17849                                 // make sure all tabs have the same length
17850                                 if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
17851                                         this._rectifyRtlTabList();
17852                                 }
17853                         },
17854
17855                         onRemoveChild: function(/*dijit._Widget*/ page){
17856                                 // summary:
17857                                 //              Called whenever a page is removed from the container.
17858                                 //              Remove the button corresponding to the page.
17859                                 // tags:
17860                                 //              private
17861
17862                                 if(this._currentChild === page){ this._currentChild = null; }
17863                                 dojo.forEach(this.pane2handles[page.id], this.disconnect, this);
17864                                 delete this.pane2handles[page.id];
17865                                 var button = this.pane2button[page.id];
17866                                 if(button){
17867                                         this.removeChild(button);
17868                                         delete this.pane2button[page.id];
17869                                         button.destroy();
17870                                 }
17871                                 delete page.controlButton;
17872                         },
17873
17874                         onSelectChild: function(/*dijit._Widget*/ page){
17875                                 // summary:
17876                                 //              Called when a page has been selected in the StackContainer, either by me or by another StackController
17877                                 // tags:
17878                                 //              private
17879
17880                                 if(!page){ return; }
17881
17882                                 if(this._currentChild){
17883                                         var oldButton=this.pane2button[this._currentChild.id];
17884                                         oldButton.set('checked', false);
17885                                         dijit.setWaiState(oldButton.focusNode, "selected", "false");
17886                                         oldButton.focusNode.setAttribute("tabIndex", "-1");
17887                                 }
17888
17889                                 var newButton=this.pane2button[page.id];
17890                                 newButton.set('checked', true);
17891                                 dijit.setWaiState(newButton.focusNode, "selected", "true");
17892                                 this._currentChild = page;
17893                                 newButton.focusNode.setAttribute("tabIndex", "0");
17894                                 var container = dijit.byId(this.containerId);
17895                                 dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
17896                         },
17897
17898                         onButtonClick: function(/*dijit._Widget*/ page){
17899                                 // summary:
17900                                 //              Called whenever one of my child buttons is pressed in an attempt to select a page
17901                                 // tags:
17902                                 //              private
17903
17904                                 var container = dijit.byId(this.containerId);
17905                                 container.selectChild(page);
17906                         },
17907
17908                         onCloseButtonClick: function(/*dijit._Widget*/ page){
17909                                 // summary:
17910                                 //              Called whenever one of my child buttons [X] is pressed in an attempt to close a page
17911                                 // tags:
17912                                 //              private
17913
17914                                 var container = dijit.byId(this.containerId);
17915                                 container.closeChild(page);
17916                                 if(this._currentChild){
17917                                         var b = this.pane2button[this._currentChild.id];
17918                                         if(b){
17919                                                 dijit.focus(b.focusNode || b.domNode);
17920                                         }
17921                                 }
17922                         },
17923
17924                         // TODO: this is a bit redundant with forward, back api in StackContainer
17925                         adjacent: function(/*Boolean*/ forward){
17926                                 // summary:
17927                                 //              Helper for onkeypress to find next/previous button
17928                                 // tags:
17929                                 //              private
17930
17931                                 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
17932                                 // find currently focused button in children array
17933                                 var children = this.getChildren();
17934                                 var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
17935                                 // pick next button to focus on
17936                                 var offset = forward ? 1 : children.length - 1;
17937                                 return children[ (current + offset) % children.length ]; // dijit._Widget
17938                         },
17939
17940                         onkeypress: function(/*Event*/ e){
17941                                 // summary:
17942                                 //              Handle keystrokes on the page list, for advancing to next/previous button
17943                                 //              and closing the current page if the page is closable.
17944                                 // tags:
17945                                 //              private
17946
17947                                 if(this.disabled || e.altKey ){ return; }
17948                                 var forward = null;
17949                                 if(e.ctrlKey || !e._djpage){
17950                                         var k = dojo.keys;
17951                                         switch(e.charOrCode){
17952                                                 case k.LEFT_ARROW:
17953                                                 case k.UP_ARROW:
17954                                                         if(!e._djpage){ forward = false; }
17955                                                         break;
17956                                                 case k.PAGE_UP:
17957                                                         if(e.ctrlKey){ forward = false; }
17958                                                         break;
17959                                                 case k.RIGHT_ARROW:
17960                                                 case k.DOWN_ARROW:
17961                                                         if(!e._djpage){ forward = true; }
17962                                                         break;
17963                                                 case k.PAGE_DOWN:
17964                                                         if(e.ctrlKey){ forward = true; }
17965                                                         break;
17966                                                 case k.DELETE:
17967                                                         if(this._currentChild.closable){
17968                                                                 this.onCloseButtonClick(this._currentChild);
17969                                                         }
17970                                                         dojo.stopEvent(e);
17971                                                         break;
17972                                                 default:
17973                                                         if(e.ctrlKey){
17974                                                                 if(e.charOrCode === k.TAB){
17975                                                                         this.adjacent(!e.shiftKey).onClick();
17976                                                                         dojo.stopEvent(e);
17977                                                                 }else if(e.charOrCode == "w"){
17978                                                                         if(this._currentChild.closable){
17979                                                                                 this.onCloseButtonClick(this._currentChild);
17980                                                                         }
17981                                                                         dojo.stopEvent(e); // avoid browser tab closing.
17982                                                                 }
17983                                                         }
17984                                         }
17985                                         // handle page navigation
17986                                         if(forward !== null){
17987                                                 this.adjacent(forward).onClick();
17988                                                 dojo.stopEvent(e);
17989                                         }
17990                                 }
17991                         },
17992
17993                         onContainerKeyPress: function(/*Object*/ info){
17994                                 // summary:
17995                                 //              Called when there was a keypress on the container
17996                                 // tags:
17997                                 //              private
17998                                 info.e._djpage = info.page;
17999                                 this.onkeypress(info.e);
18000                         }
18001         });
18002
18003
18004 dojo.declare("dijit.layout._StackButton",
18005                 dijit.form.ToggleButton,
18006                 {
18007                 // summary:
18008                 //              Internal widget used by StackContainer.
18009                 // description:
18010                 //              The button-like or tab-like object you click to select or delete a page
18011                 // tags:
18012                 //              private
18013
18014                 // Override _FormWidget.tabIndex.
18015                 // StackContainer buttons are not in the tab order by default.
18016                 // Probably we should be calling this.startupKeyNavChildren() instead.
18017                 tabIndex: "-1",
18018
18019                 postCreate: function(/*Event*/ evt){
18020                         dijit.setWaiRole((this.focusNode || this.domNode), "tab");
18021                         this.inherited(arguments);
18022                 },
18023
18024                 onClick: function(/*Event*/ evt){
18025                         // summary:
18026                         //              This is for TabContainer where the tabs are <span> rather than button,
18027                         //              so need to set focus explicitly (on some browsers)
18028                         //              Note that you shouldn't override this method, but you can connect to it.
18029                         dijit.focus(this.focusNode);
18030
18031                         // ... now let StackController catch the event and tell me what to do
18032                 },
18033
18034                 onClickCloseButton: function(/*Event*/ evt){
18035                         // summary:
18036                         //              StackContainer connects to this function; if your widget contains a close button
18037                         //              then clicking it should call this function.
18038                         //              Note that you shouldn't override this method, but you can connect to it.
18039                         evt.stopPropagation();
18040                 }
18041         });
18042
18043
18044 }
18045
18046 if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18047 dojo._hasResource["dijit.layout.StackContainer"] = true;
18048 dojo.provide("dijit.layout.StackContainer");
18049
18050
18051
18052
18053
18054
18055 dojo.declare(
18056         "dijit.layout.StackContainer",
18057         dijit.layout._LayoutWidget,
18058         {
18059         // summary:
18060         //              A container that has multiple children, but shows only
18061         //              one child at a time
18062         //
18063         // description:
18064         //              A container for widgets (ContentPanes, for example) That displays
18065         //              only one Widget at a time.
18066         //
18067         //              Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
18068         //
18069         //              Can be base class for container, Wizard, Show, etc.
18070
18071         // doLayout: Boolean
18072         //              If true, change the size of my currently displayed child to match my size
18073         doLayout: true,
18074
18075         // persist: Boolean
18076         //              Remembers the selected child across sessions
18077         persist: false,
18078
18079         baseClass: "dijitStackContainer",
18080
18081 /*=====
18082         // selectedChildWidget: [readonly] dijit._Widget
18083         //              References the currently selected child widget, if any.
18084         //              Adjust selected child with selectChild() method.
18085         selectedChildWidget: null,
18086 =====*/
18087
18088         postCreate: function(){
18089                 this.inherited(arguments);
18090                 dojo.addClass(this.domNode, "dijitLayoutContainer");
18091                 dijit.setWaiRole(this.containerNode, "tabpanel");
18092                 this.connect(this.domNode, "onkeypress", this._onKeyPress);
18093         },
18094
18095         startup: function(){
18096                 if(this._started){ return; }
18097
18098                 var children = this.getChildren();
18099
18100                 // Setup each page panel to be initially hidden
18101                 dojo.forEach(children, this._setupChild, this);
18102
18103                 // Figure out which child to initially display, defaulting to first one
18104                 if(this.persist){
18105                         this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
18106                 }else{
18107                         dojo.some(children, function(child){
18108                                 if(child.selected){
18109                                         this.selectedChildWidget = child;
18110                                 }
18111                                 return child.selected;
18112                         }, this);
18113                 }
18114                 var selected = this.selectedChildWidget;
18115                 if(!selected && children[0]){
18116                         selected = this.selectedChildWidget = children[0];
18117                         selected.selected = true;
18118                 }
18119
18120                 // Publish information about myself so any StackControllers can initialize.
18121                 // This needs to happen before this.inherited(arguments) so that for
18122                 // TabContainer, this._contentBox doesn't include the space for the tab labels.
18123                 dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
18124
18125                 // Startup each child widget, and do initial layout like setting this._contentBox,
18126                 // then calls this.resize() which does the initial sizing on the selected child.
18127                 this.inherited(arguments);
18128         },
18129
18130         resize: function(){
18131                 // Resize is called when we are first made visible (it's called from startup()
18132                 // if we are initially visible).   If this is the first time we've been made
18133                 // visible then show our first child.
18134                 var selected = this.selectedChildWidget;
18135                 if(selected && !this._hasBeenShown){
18136                         this._hasBeenShown = true;
18137                         this._showChild(selected);
18138                 }
18139                 this.inherited(arguments);
18140         },
18141
18142         _setupChild: function(/*dijit._Widget*/ child){
18143                 // Overrides _LayoutWidget._setupChild()
18144
18145                 this.inherited(arguments);
18146
18147                 dojo.removeClass(child.domNode, "dijitVisible");
18148                 dojo.addClass(child.domNode, "dijitHidden");
18149
18150                 // remove the title attribute so it doesn't show up when i hover
18151                 // over a node
18152                 child.domNode.title = "";
18153         },
18154
18155         addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
18156                 // Overrides _Container.addChild() to do layout and publish events
18157
18158                 this.inherited(arguments);
18159
18160                 if(this._started){
18161                         dojo.publish(this.id+"-addChild", [child, insertIndex]);
18162
18163                         // in case the tab titles have overflowed from one line to two lines
18164                         // (or, if this if first child, from zero lines to one line)
18165                         // TODO: w/ScrollingTabController this is no longer necessary, although
18166                         // ScrollTabController.resize() does need to get called to show/hide
18167                         // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
18168                         this.layout();
18169
18170                         // if this is the first child, then select it
18171                         if(!this.selectedChildWidget){
18172                                 this.selectChild(child);
18173                         }
18174                 }
18175         },
18176
18177         removeChild: function(/*dijit._Widget*/ page){
18178                 // Overrides _Container.removeChild() to do layout and publish events
18179
18180                 this.inherited(arguments);
18181
18182                 if(this._started){
18183                         // this will notify any tablists to remove a button; do this first because it may affect sizing
18184                         dojo.publish(this.id + "-removeChild", [page]);
18185                 }
18186
18187                 // If we are being destroyed than don't run the code below (to select another page), because we are deleting
18188                 // every page one by one
18189                 if(this._beingDestroyed){ return; }
18190
18191                 // Select new page to display, also updating TabController to show the respective tab.
18192                 // Do this before layout call because it can affect the height of the TabController.
18193                 if(this.selectedChildWidget === page){
18194                         this.selectedChildWidget = undefined;
18195                         if(this._started){
18196                                 var children = this.getChildren();
18197                                 if(children.length){
18198                                         this.selectChild(children[0]);
18199                                 }
18200                         }
18201                 }
18202
18203                 if(this._started){
18204                         // In case the tab titles now take up one line instead of two lines
18205                         // (note though that ScrollingTabController never overflows to multiple lines),
18206                         // or the height has changed slightly because of addition/removal of tab which close icon
18207                         this.layout();
18208                 }
18209         },
18210
18211         selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
18212                 // summary:
18213                 //              Show the given widget (which must be one of my children)
18214                 // page:
18215                 //              Reference to child widget or id of child widget
18216
18217                 page = dijit.byId(page);
18218
18219                 if(this.selectedChildWidget != page){
18220                         // Deselect old page and select new one
18221                         this._transition(page, this.selectedChildWidget, animate);
18222                         this.selectedChildWidget = page;
18223                         dojo.publish(this.id+"-selectChild", [page]);
18224
18225                         if(this.persist){
18226                                 dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
18227                         }
18228                 }
18229         },
18230
18231         _transition: function(/*dijit._Widget*/newWidget, /*dijit._Widget*/oldWidget){
18232                 // summary:
18233                 //              Hide the old widget and display the new widget.
18234                 //              Subclasses should override this.
18235                 // tags:
18236                 //              protected extension
18237                 if(oldWidget){
18238                         this._hideChild(oldWidget);
18239                 }
18240                 this._showChild(newWidget);
18241
18242                 // Size the new widget, in case this is the first time it's being shown,
18243                 // or I have been resized since the last time it was shown.
18244                 // Note that page must be visible for resizing to work.
18245                 if(newWidget.resize){
18246                         if(this.doLayout){
18247                                 newWidget.resize(this._containerContentBox || this._contentBox);
18248                         }else{
18249                                 // the child should pick it's own size but we still need to call resize()
18250                                 // (with no arguments) to let the widget lay itself out
18251                                 newWidget.resize();
18252                         }
18253                 }
18254         },
18255
18256         _adjacent: function(/*Boolean*/ forward){
18257                 // summary:
18258                 //              Gets the next/previous child widget in this container from the current selection.
18259                 var children = this.getChildren();
18260                 var index = dojo.indexOf(children, this.selectedChildWidget);
18261                 index += forward ? 1 : children.length - 1;
18262                 return children[ index % children.length ]; // dijit._Widget
18263         },
18264
18265         forward: function(){
18266                 // summary:
18267                 //              Advance to next page.
18268                 this.selectChild(this._adjacent(true), true);
18269         },
18270
18271         back: function(){
18272                 // summary:
18273                 //              Go back to previous page.
18274                 this.selectChild(this._adjacent(false), true);
18275         },
18276
18277         _onKeyPress: function(e){
18278                 dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
18279         },
18280
18281         layout: function(){
18282                 // Implement _LayoutWidget.layout() virtual method.
18283                 if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
18284                         this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
18285                 }
18286         },
18287
18288         _showChild: function(/*dijit._Widget*/ page){
18289                 // summary:
18290                 //              Show the specified child by changing it's CSS, and call _onShow()/onShow() so
18291                 //              it can do any updates it needs regarding loading href's etc.
18292                 var children = this.getChildren();
18293                 page.isFirstChild = (page == children[0]);
18294                 page.isLastChild = (page == children[children.length-1]);
18295                 page.selected = true;
18296
18297                 dojo.removeClass(page.domNode, "dijitHidden");
18298                 dojo.addClass(page.domNode, "dijitVisible");
18299
18300                 page._onShow();
18301         },
18302
18303         _hideChild: function(/*dijit._Widget*/ page){
18304                 // summary:
18305                 //              Hide the specified child by changing it's CSS, and call _onHide() so
18306                 //              it's notified.
18307                 page.selected=false;
18308                 dojo.removeClass(page.domNode, "dijitVisible");
18309                 dojo.addClass(page.domNode, "dijitHidden");
18310
18311                 page.onHide();
18312         },
18313
18314         closeChild: function(/*dijit._Widget*/ page){
18315                 // summary:
18316                 //              Callback when user clicks the [X] to remove a page.
18317                 //              If onClose() returns true then remove and destroy the child.
18318                 // tags:
18319                 //              private
18320                 var remove = page.onClose(this, page);
18321                 if(remove){
18322                         this.removeChild(page);
18323                         // makes sure we can clean up executeScripts in ContentPane onUnLoad
18324                         page.destroyRecursive();
18325                 }
18326         },
18327
18328         destroyDescendants: function(/*Boolean*/preserveDom){
18329                 dojo.forEach(this.getChildren(), function(child){
18330                         this.removeChild(child);
18331                         child.destroyRecursive(preserveDom);
18332                 }, this);
18333         }
18334 });
18335
18336 // For back-compat, remove for 2.0
18337
18338
18339
18340 // These arguments can be specified for the children of a StackContainer.
18341 // Since any widget can be specified as a StackContainer child, mix them
18342 // into the base widget class.  (This is a hack, but it's effective.)
18343 dojo.extend(dijit._Widget, {
18344         // selected: Boolean
18345         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
18346         //              Specifies that this widget should be the initially displayed pane.
18347         //              Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
18348         selected: false,
18349
18350         // closable: Boolean
18351         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
18352         //              True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
18353         closable: false,
18354
18355         // iconClass: String
18356         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
18357         //              CSS Class specifying icon to use in label associated with this pane.
18358         iconClass: "",
18359
18360         // showTitle: Boolean
18361         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
18362         //              When true, display title of this widget as tab label etc., rather than just using
18363         //              icon specified in iconClass
18364         showTitle: true
18365 });
18366
18367 }
18368
18369 if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18370 dojo._hasResource["dijit.layout.AccordionPane"] = true;
18371 dojo.provide("dijit.layout.AccordionPane");
18372
18373
18374
18375 dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, {
18376         // summary:
18377         //              Deprecated widget.   Use `dijit.layout.ContentPane` instead.
18378         // tags:
18379         //              deprecated
18380
18381         constructor: function(){
18382                 dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
18383         },
18384
18385         onSelected: function(){
18386                 // summary:
18387                 //              called when this pane is selected
18388         }
18389 });
18390
18391 }
18392
18393 if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18394 dojo._hasResource["dijit.layout.AccordionContainer"] = true;
18395 dojo.provide("dijit.layout.AccordionContainer");
18396
18397
18398
18399
18400
18401
18402
18403
18404
18405         // for back compat, remove for 2.0
18406
18407 dojo.declare(
18408         "dijit.layout.AccordionContainer",
18409         dijit.layout.StackContainer,
18410         {
18411                 // summary:
18412                 //              Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
18413                 //              and switching between panes is visualized by sliding the other panes up/down.
18414                 // example:
18415                 //      |       <div dojoType="dijit.layout.AccordionContainer">
18416                 //      |               <div dojoType="dijit.layout.ContentPane" title="pane 1">
18417                 //      |               </div>
18418                 //      |               <div dojoType="dijit.layout.ContentPane" title="pane 2">
18419                 //      |                       <p>This is some text</p>
18420                 //      |               </div>
18421                 //      |       </div>
18422
18423                 // duration: Integer
18424                 //              Amount of time (in ms) it takes to slide panes
18425                 duration: dijit.defaultDuration,
18426
18427                 // buttonWidget: [const] String
18428                 //              The name of the widget used to display the title of each pane
18429                 buttonWidget: "dijit.layout._AccordionButton",
18430
18431                 // _verticalSpace: Number
18432                 //              Pixels of space available for the open pane
18433                 //              (my content box size minus the cumulative size of all the title bars)
18434                 _verticalSpace: 0,
18435
18436                 baseClass: "dijitAccordionContainer",
18437
18438                 postCreate: function(){
18439                         this.domNode.style.overflow = "hidden";
18440                         this.inherited(arguments);
18441                         dijit.setWaiRole(this.domNode, "tablist");
18442                 },
18443
18444                 startup: function(){
18445                         if(this._started){ return; }
18446                         this.inherited(arguments);
18447                         if(this.selectedChildWidget){
18448                                 var style = this.selectedChildWidget.containerNode.style;
18449                                 style.display = "";
18450                                 style.overflow = "auto";
18451                                 this.selectedChildWidget._wrapperWidget.set("selected", true);
18452                         }
18453                 },
18454
18455                 _getTargetHeight: function(/* Node */ node){
18456                         // summary:
18457                         //              For the given node, returns the height that should be
18458                         //              set to achieve our vertical space (subtract any padding
18459                         //              we may have).
18460                         //
18461                         //              This is used by the animations.
18462                         //
18463                         //              TODO: I don't think this works correctly in IE quirks when an elements
18464                         //              style.height including padding and borders
18465                         var cs = dojo.getComputedStyle(node);
18466                         return Math.max(this._verticalSpace - dojo._getPadBorderExtents(node, cs).h - dojo._getMarginExtents(node, cs).h, 0);
18467                 },
18468
18469                 layout: function(){
18470                         // Implement _LayoutWidget.layout() virtual method.
18471                         // Set the height of the open pane based on what room remains.
18472
18473                         var openPane = this.selectedChildWidget;
18474                         
18475                         if(!openPane){ return;}
18476
18477                         var openPaneContainer = openPane._wrapperWidget.domNode,
18478                                 openPaneContainerMargin = dojo._getMarginExtents(openPaneContainer),
18479                                 openPaneContainerPadBorder = dojo._getPadBorderExtents(openPaneContainer),
18480                                 mySize = this._contentBox;
18481
18482                         // get cumulative height of all the unselected title bars
18483                         var totalCollapsedHeight = 0;
18484                         dojo.forEach(this.getChildren(), function(child){
18485                     if(child != openPane){
18486                                         totalCollapsedHeight += dojo.marginBox(child._wrapperWidget.domNode).h;
18487                                 }
18488                         });
18489                         this._verticalSpace = mySize.h - totalCollapsedHeight - openPaneContainerMargin.h 
18490                                 - openPaneContainerPadBorder.h - openPane._buttonWidget.getTitleHeight();
18491
18492                         // Memo size to make displayed child
18493                         this._containerContentBox = {
18494                                 h: this._verticalSpace,
18495                                 w: this._contentBox.w - openPaneContainerMargin.w - openPaneContainerPadBorder.w
18496                         };
18497
18498                         if(openPane){
18499                                 openPane.resize(this._containerContentBox);
18500                         }
18501                 },
18502
18503                 _setupChild: function(child){
18504                         // Overrides _LayoutWidget._setupChild().
18505                         // Put wrapper widget around the child widget, showing title
18506
18507                         child._wrapperWidget = new dijit.layout._AccordionInnerContainer({
18508                                 contentWidget: child,
18509                                 buttonWidget: this.buttonWidget,
18510                                 id: child.id + "_wrapper",
18511                                 dir: child.dir,
18512                                 lang: child.lang,
18513                                 parent: this
18514                         });
18515
18516                         this.inherited(arguments);
18517                 },
18518
18519                 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){  
18520                         if(this._started){
18521                                 // Adding a child to a started Accordion is complicated because children have
18522                                 // wrapper widgets.  Default code path (calling this.inherited()) would add
18523                                 // the new child inside another child's wrapper.
18524
18525                                 // First add in child as a direct child of this AccordionContainer
18526                                 dojo.place(child.domNode, this.containerNode, insertIndex);
18527
18528                                 if(!child._started){
18529                                         child.startup();
18530                                 }
18531                                 
18532                                 // Then stick the wrapper widget around the child widget
18533                                 this._setupChild(child);
18534
18535                                 // Code below copied from StackContainer        
18536                                 dojo.publish(this.id+"-addChild", [child, insertIndex]);
18537                                 this.layout();
18538                                 if(!this.selectedChildWidget){
18539                                         this.selectChild(child);
18540                                 }
18541                         }else{
18542                                 // We haven't been started yet so just add in the child widget directly,
18543                                 // and the wrapper will be created on startup()
18544                                 this.inherited(arguments);
18545                         }
18546                 },
18547
18548                 removeChild: function(child){
18549                         // Overrides _LayoutWidget.removeChild().
18550
18551                         // destroy wrapper widget first, before StackContainer.getChildren() call
18552                         child._wrapperWidget.destroy();
18553                         delete child._wrapperWidget;
18554                         dojo.removeClass(child.domNode, "dijitHidden");
18555
18556                         this.inherited(arguments);
18557                 },
18558
18559                 getChildren: function(){
18560                         // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
18561                         return dojo.map(this.inherited(arguments), function(child){
18562                                 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
18563                         }, this);
18564                 },
18565
18566                 destroy: function(){
18567                         dojo.forEach(this.getChildren(), function(child){
18568                                 child._wrapperWidget.destroy();
18569                         });
18570                         this.inherited(arguments);
18571                 },
18572
18573                 _transition: function(/*dijit._Widget?*/newWidget, /*dijit._Widget?*/oldWidget, /*Boolean*/ animate){
18574                         // Overrides StackContainer._transition() to provide sliding of title bars etc.
18575
18576 //TODO: should be able to replace this with calls to slideIn/slideOut
18577                         if(this._inTransition){ return; }
18578                         var animations = [];
18579                         var paneHeight = this._verticalSpace;
18580                         if(newWidget){
18581                                 newWidget._wrapperWidget.set("selected", true);
18582
18583                                 this._showChild(newWidget);     // prepare widget to be slid in
18584
18585                                 // Size the new widget, in case this is the first time it's being shown,
18586                                 // or I have been resized since the last time it was shown.
18587                                 // Note that page must be visible for resizing to work.
18588                                 if(this.doLayout && newWidget.resize){
18589                                         newWidget.resize(this._containerContentBox);
18590                                 }
18591
18592                                 var newContents = newWidget.domNode;
18593                                 dojo.addClass(newContents, "dijitVisible");
18594                                 dojo.removeClass(newContents, "dijitHidden");
18595                                 
18596                                 if(animate){
18597                                         var newContentsOverflow = newContents.style.overflow;
18598                                         newContents.style.overflow = "hidden";
18599                                         animations.push(dojo.animateProperty({
18600                                                 node: newContents,
18601                                                 duration: this.duration,
18602                                                 properties: {
18603                                                         height: { start: 1, end: this._getTargetHeight(newContents) }
18604                                                 },
18605                                                 onEnd: function(){
18606                                                         newContents.style.overflow = newContentsOverflow;
18607
18608                                                         // Kick IE to workaround layout bug, see #11415
18609                                                         if(dojo.isIE){
18610                                                                 setTimeout(function(){
18611                                                                         dojo.removeClass(newContents.parentNode, "dijitAccordionInnerContainerFocused");
18612                                                                         setTimeout(function(){
18613                                                                                 dojo.addClass(newContents.parentNode, "dijitAccordionInnerContainerFocused");
18614                                                                         }, 0);
18615                                                                 }, 0);
18616                                                         }
18617                                                 }
18618                                         }));
18619                                 }
18620                         }
18621                         if(oldWidget){
18622                                 oldWidget._wrapperWidget.set("selected", false);
18623                                 var oldContents = oldWidget.domNode;
18624                                 if(animate){
18625                                         var oldContentsOverflow = oldContents.style.overflow;
18626                                         oldContents.style.overflow = "hidden";
18627                                         animations.push(dojo.animateProperty({
18628                                                 node: oldContents,
18629                                                 duration: this.duration,
18630                                                 properties: {
18631                                                         height: { start: this._getTargetHeight(oldContents), end: 1 }
18632                                                 },
18633                                                 onEnd: function(){
18634                                                         dojo.addClass(oldContents, "dijitHidden");
18635                                                         dojo.removeClass(oldContents, "dijitVisible");
18636                                                         oldContents.style.overflow = oldContentsOverflow;
18637                                                         if(oldWidget.onHide){
18638                                                                 oldWidget.onHide();
18639                                                         }
18640                                                 }
18641                                         }));
18642                                 }else{
18643                                         dojo.addClass(oldContents, "dijitHidden");
18644                                         dojo.removeClass(oldContents, "dijitVisible");
18645                                         if(oldWidget.onHide){
18646                                                 oldWidget.onHide();
18647                                         }
18648                                 }
18649                         }
18650
18651                         if(animate){
18652                                 this._inTransition = true;
18653                                 var combined = dojo.fx.combine(animations);
18654                                 combined.onEnd = dojo.hitch(this, function(){
18655                                         delete this._inTransition;
18656                                 });
18657                                 combined.play();
18658                         }                       
18659                 },
18660
18661                 // note: we are treating the container as controller here
18662                 _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
18663                         // summary:
18664                         //              Handle keypress events
18665                         // description:
18666                         //              This is called from a handler on AccordionContainer.domNode
18667                         //              (setup in StackContainer), and is also called directly from
18668                         //              the click handler for accordion labels
18669                         if(this._inTransition || this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
18670                                 if(this._inTransition){
18671                                         dojo.stopEvent(e);
18672                                 }
18673                                 return;
18674                         }
18675                         var k = dojo.keys,
18676                                 c = e.charOrCode;
18677                         if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
18678                                         (e.ctrlKey && c == k.PAGE_UP)){
18679                                 this._adjacent(false)._buttonWidget._onTitleClick();
18680                                 dojo.stopEvent(e);
18681                         }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
18682                                         (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
18683                                 this._adjacent(true)._buttonWidget._onTitleClick();
18684                                 dojo.stopEvent(e);
18685                         }
18686                 }
18687         }
18688 );
18689
18690 dojo.declare("dijit.layout._AccordionInnerContainer",
18691         [dijit._Widget, dijit._CssStateMixin], {
18692                 // summary:
18693                 //              Internal widget placed as direct child of AccordionContainer.containerNode.
18694                 //              When other widgets are added as children to an AccordionContainer they are wrapped in
18695                 //              this widget.
18696                 
18697                 // buttonWidget: String
18698                 //              Name of class to use to instantiate title
18699                 //              (Wish we didn't have a separate widget for just the title but maintaining it
18700                 //              for backwards compatibility, is it worth it?)
18701 /*=====
18702                  buttonWidget: null,
18703 =====*/
18704                 // contentWidget: dijit._Widget
18705                 //              Pointer to the real child widget
18706 /*=====
18707                 contentWidget: null,
18708 =====*/
18709
18710                 baseClass: "dijitAccordionInnerContainer",
18711
18712                 // tell nested layout widget that we will take care of sizing
18713                 isContainer: true,
18714                 isLayoutContainer: true,
18715
18716                 buildRendering: function(){                     
18717                         // Create wrapper div, placed where the child is now
18718                         this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after");
18719                         
18720                         // wrapper div's first child is the button widget (ie, the title bar)
18721                         var child = this.contentWidget,
18722                                 cls = dojo.getObject(this.buttonWidget);
18723                         this.button = child._buttonWidget = (new cls({
18724                                 contentWidget: child,
18725                                 label: child.title,
18726                                 title: child.tooltip,
18727                                 dir: child.dir,
18728                                 lang: child.lang,
18729                                 iconClass: child.iconClass,
18730                                 id: child.id + "_button",
18731                                 parent: this.parent
18732                         })).placeAt(this.domNode);
18733                         
18734                         // and then the actual content widget (changing it from prior-sibling to last-child)
18735                         dojo.place(this.contentWidget.domNode, this.domNode);
18736                 },
18737
18738                 postCreate: function(){
18739                         this.inherited(arguments);
18740                         this.connect(this.contentWidget, 'set', function(name, value){
18741                                 var mappedName = {title: "label", tooltip: "title", iconClass: "iconClass"}[name];
18742                                 if(mappedName){
18743                                         this.button.set(mappedName, value);
18744                                 }
18745                         }, this);
18746                 },
18747
18748                 _setSelectedAttr: function(/*Boolean*/ isSelected){
18749                         this.selected = isSelected;
18750                         this.button.set("selected", isSelected);
18751                         if(isSelected){
18752                                 var cw = this.contentWidget;
18753                                 if(cw.onSelected){ cw.onSelected(); }
18754                         }
18755                 },
18756
18757                 startup: function(){
18758                         // Called by _Container.addChild()
18759                         this.contentWidget.startup();
18760                 },
18761
18762                 destroy: function(){
18763                         this.button.destroyRecursive();
18764                         
18765                         delete this.contentWidget._buttonWidget;
18766                         delete this.contentWidget._wrapperWidget;
18767
18768                         this.inherited(arguments);
18769                 },
18770                 
18771                 destroyDescendants: function(){
18772                         // since getChildren isn't working for me, have to code this manually
18773                         this.contentWidget.destroyRecursive();
18774                 }
18775 });
18776
18777 dojo.declare("dijit.layout._AccordionButton",
18778         [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
18779         {
18780         // summary:
18781         //              The title bar to click to open up an accordion pane.
18782         //              Internal widget used by AccordionContainer.
18783         // tags:
18784         //              private
18785
18786         templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "<div dojoAttachEvent='onclick:_onTitleClick' class='dijitAccordionTitle'>\n\t<div dojoAttachPoint='titleNode,focusNode' dojoAttachEvent='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' wairole=\"tab\" waiState=\"expanded-false\"\n\t\t><span class='dijitInline dijitAccordionArrow' waiRole=\"presentation\"></span\n\t\t><span class='arrowTextUp' waiRole=\"presentation\">+</span\n\t\t><span class='arrowTextDown' waiRole=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" waiRole=\"presentation\"/>\n\t\t<span waiRole=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"),
18787         attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
18788                 label: {node: "titleTextNode", type: "innerHTML" },
18789                 title: {node: "titleTextNode", type: "attribute", attribute: "title"},
18790                 iconClass: { node: "iconNode", type: "class" }
18791         }),
18792
18793         baseClass: "dijitAccordionTitle",
18794
18795         getParent: function(){
18796                 // summary:
18797                 //              Returns the AccordionContainer parent.
18798                 // tags:
18799                 //              private
18800                 return this.parent;
18801         },
18802
18803         postCreate: function(){
18804                 this.inherited(arguments);
18805                 dojo.setSelectable(this.domNode, false);
18806                 var titleTextNodeId = dojo.attr(this.domNode,'id').replace(' ','_');
18807                 dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
18808                 dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
18809         },
18810
18811         getTitleHeight: function(){
18812                 // summary:
18813                 //              Returns the height of the title dom node.
18814                 return dojo.marginBox(this.domNode).h;  // Integer
18815         },
18816
18817         // TODO: maybe the parent should set these methods directly rather than forcing the code
18818         // into the button widget?
18819         _onTitleClick: function(){
18820                 // summary:
18821                 //              Callback when someone clicks my title.
18822                 var parent = this.getParent();
18823                 if(!parent._inTransition){
18824                         parent.selectChild(this.contentWidget, true);
18825                         dijit.focus(this.focusNode);
18826                 }
18827         },
18828
18829         _onTitleKeyPress: function(/*Event*/ evt){
18830                 return this.getParent()._onKeyPress(evt, this.contentWidget);
18831         },
18832
18833         _setSelectedAttr: function(/*Boolean*/ isSelected){
18834                 this.selected = isSelected;
18835                 dijit.setWaiState(this.focusNode, "expanded", isSelected);
18836                 dijit.setWaiState(this.focusNode, "selected", isSelected);
18837                 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
18838         }
18839 });
18840
18841 }
18842
18843 if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18844 dojo._hasResource["dijit.layout.BorderContainer"] = true;
18845 dojo.provide("dijit.layout.BorderContainer");
18846
18847
18848
18849
18850 dojo.declare(
18851         "dijit.layout.BorderContainer",
18852         dijit.layout._LayoutWidget,
18853 {
18854         // summary:
18855         //              Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
18856         //
18857         // description:
18858         //              A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
18859         //              that contains a child widget marked region="center" and optionally children widgets marked
18860         //              region equal to "top", "bottom", "leading", "trailing", "left" or "right".
18861         //              Children along the edges will be laid out according to width or height dimensions and may
18862         //              include optional splitters (splitter="true") to make them resizable by the user.  The remaining
18863         //              space is designated for the center region.
18864         //
18865         //              NOTE: Splitters must not be more than 50 pixels in width.
18866         //
18867         //              The outer size must be specified on the BorderContainer node.  Width must be specified for the sides
18868         //              and height for the top and bottom, respectively.  No dimensions should be specified on the center;
18869         //              it will fill the remaining space.  Regions named "leading" and "trailing" may be used just like
18870         //              "left" and "right" except that they will be reversed in right-to-left environments.
18871         //
18872         // example:
18873         // |    <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
18874         // |            style="width: 400px; height: 300px;">
18875         // |            <div dojoType="ContentPane" region="top">header text</div>
18876         // |            <div dojoType="ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
18877         // |            <div dojoType="ContentPane" region="center">client area</div>
18878         // |    </div>
18879
18880         // design: String
18881         //              Which design is used for the layout:
18882         //                      - "headline" (default) where the top and bottom extend
18883         //                              the full width of the container
18884         //                      - "sidebar" where the left and right sides extend from top to bottom.
18885         design: "headline",
18886
18887         // gutters: Boolean
18888         //              Give each pane a border and margin.
18889         //              Margin determined by domNode.paddingLeft.
18890         //              When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
18891         gutters: true,
18892
18893         // liveSplitters: Boolean
18894         //              Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
18895         liveSplitters: true,
18896
18897         // persist: Boolean
18898         //              Save splitter positions in a cookie.
18899         persist: false,
18900
18901         baseClass: "dijitBorderContainer",
18902
18903         // _splitterClass: String
18904         //              Optional hook to override the default Splitter widget used by BorderContainer
18905         _splitterClass: "dijit.layout._Splitter",
18906
18907         postMixInProperties: function(){
18908                 // change class name to indicate that BorderContainer is being used purely for
18909                 // layout (like LayoutContainer) rather than for pretty formatting.
18910                 if(!this.gutters){
18911                         this.baseClass += "NoGutter";
18912                 }
18913                 this.inherited(arguments);
18914         },
18915
18916         postCreate: function(){
18917                 this.inherited(arguments);
18918
18919                 this._splitters = {};
18920                 this._splitterThickness = {};
18921         },
18922
18923         startup: function(){
18924                 if(this._started){ return; }
18925                 dojo.forEach(this.getChildren(), this._setupChild, this);
18926                 this.inherited(arguments);
18927         },
18928
18929         _setupChild: function(/*dijit._Widget*/ child){
18930                 // Override _LayoutWidget._setupChild().
18931
18932                 var region = child.region;
18933                 if(region){
18934                         this.inherited(arguments);
18935
18936                         dojo.addClass(child.domNode, this.baseClass+"Pane");
18937
18938                         var ltr = this.isLeftToRight();
18939                         if(region == "leading"){ region = ltr ? "left" : "right"; }
18940                         if(region == "trailing"){ region = ltr ? "right" : "left"; }
18941
18942                         //FIXME: redundant?
18943                         this["_"+region] = child.domNode;
18944                         this["_"+region+"Widget"] = child;
18945
18946                         // Create draggable splitter for resizing pane,
18947                         // or alternately if splitter=false but BorderContainer.gutters=true then
18948                         // insert dummy div just for spacing
18949                         if((child.splitter || this.gutters) && !this._splitters[region]){
18950                                 var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
18951                                 var splitter = new _Splitter({
18952                                         id: child.id + "_splitter",
18953                                         container: this,
18954                                         child: child,
18955                                         region: region,
18956                                         live: this.liveSplitters
18957                                 });
18958                                 splitter.isSplitter = true;
18959                                 this._splitters[region] = splitter.domNode;
18960                                 dojo.place(this._splitters[region], child.domNode, "after");
18961
18962                                 // Splitters arent added as Contained children, so we need to call startup explicitly
18963                                 splitter.startup();
18964                         }
18965                         child.region = region;
18966                 }
18967         },
18968
18969         _computeSplitterThickness: function(region){
18970                 this._splitterThickness[region] = this._splitterThickness[region] ||
18971                         dojo.marginBox(this._splitters[region])[(/top|bottom/.test(region) ? 'h' : 'w')];
18972         },
18973
18974         layout: function(){
18975                 // Implement _LayoutWidget.layout() virtual method.
18976                 for(var region in this._splitters){ this._computeSplitterThickness(region); }
18977                 this._layoutChildren();
18978         },
18979
18980         addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
18981                 // Override _LayoutWidget.addChild().
18982                 this.inherited(arguments);
18983                 if(this._started){
18984                         this.layout(); //OPT
18985                 }
18986         },
18987
18988         removeChild: function(/*dijit._Widget*/ child){
18989                 // Override _LayoutWidget.removeChild().
18990                 var region = child.region;
18991                 var splitter = this._splitters[region];
18992                 if(splitter){
18993                         dijit.byNode(splitter).destroy();
18994                         delete this._splitters[region];
18995                         delete this._splitterThickness[region];
18996                 }
18997                 this.inherited(arguments);
18998                 delete this["_"+region];
18999                 delete this["_" +region+"Widget"];
19000                 if(this._started){
19001                         this._layoutChildren();
19002                 }
19003                 dojo.removeClass(child.domNode, this.baseClass+"Pane");
19004         },
19005
19006         getChildren: function(){
19007                 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
19008                 return dojo.filter(this.inherited(arguments), function(widget){
19009                         return !widget.isSplitter;
19010                 });
19011         },
19012
19013         getSplitter: function(/*String*/region){
19014                 // summary:
19015                 //              Returns the widget responsible for rendering the splitter associated with region
19016                 var splitter = this._splitters[region];
19017                 return splitter ? dijit.byNode(splitter) : null;
19018         },
19019
19020         resize: function(newSize, currentSize){
19021                 // Overrides _LayoutWidget.resize().
19022
19023                 // resetting potential padding to 0px to provide support for 100% width/height + padding
19024                 // TODO: this hack doesn't respect the box model and is a temporary fix
19025                 if(!this.cs || !this.pe){
19026                         var node = this.domNode;
19027                         this.cs = dojo.getComputedStyle(node);
19028                         this.pe = dojo._getPadExtents(node, this.cs);
19029                         this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
19030                         this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
19031
19032                         dojo.style(node, "padding", "0px");
19033                 }
19034
19035                 this.inherited(arguments);
19036         },
19037
19038         _layoutChildren: function(/*String?*/changedRegion, /*Number?*/ changedRegionSize){
19039                 // summary:
19040                 //              This is the main routine for setting size/position of each child.
19041                 // description:
19042                 //              With no arguments, measures the height of top/bottom panes, the width
19043                 //              of left/right panes, and then sizes all panes accordingly.
19044                 //
19045                 //              With changedRegion specified (as "left", "top", "bottom", or "right"),
19046                 //              it changes that region's width/height to changedRegionSize and
19047                 //              then resizes other regions that were affected.
19048                 // changedRegion:
19049                 //              The region should be changed because splitter was dragged.
19050                 //              "left", "right", "top", or "bottom".
19051                 // changedRegionSize:
19052                 //              The new width/height (in pixels) to make changedRegion
19053
19054                 if(!this._borderBox || !this._borderBox.h){
19055                         // We are currently hidden, or we haven't been sized by our parent yet.
19056                         // Abort.   Someone will resize us later.
19057                         return;
19058                 }
19059
19060                 var sidebarLayout = (this.design == "sidebar");
19061                 var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0;
19062                 var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {},
19063                         centerStyle = (this._center && this._center.style) || {};
19064
19065                 var changedSide = /left|right/.test(changedRegion);
19066
19067                 var layoutSides = !changedRegion || (!changedSide && !sidebarLayout);
19068                 var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout);
19069
19070                 // Ask browser for width/height of side panes.
19071                 // Would be nice to cache this but height can change according to width
19072                 // (because words wrap around).  I don't think width will ever change though
19073                 // (except when the user drags a splitter).
19074                 if(this._top){
19075                         topStyle = (changedRegion == "top" || layoutTopBottom) && this._top.style;
19076                         topHeight = changedRegion == "top" ? changedRegionSize : dojo.marginBox(this._top).h;
19077                 }
19078                 if(this._left){
19079                         leftStyle = (changedRegion == "left" || layoutSides) && this._left.style;
19080                         leftWidth = changedRegion == "left" ? changedRegionSize : dojo.marginBox(this._left).w;
19081                 }
19082                 if(this._right){
19083                         rightStyle = (changedRegion == "right" || layoutSides) && this._right.style;
19084                         rightWidth = changedRegion == "right" ? changedRegionSize : dojo.marginBox(this._right).w;
19085                 }
19086                 if(this._bottom){
19087                         bottomStyle = (changedRegion == "bottom" || layoutTopBottom) && this._bottom.style;
19088                         bottomHeight = changedRegion == "bottom" ? changedRegionSize : dojo.marginBox(this._bottom).h;
19089                 }
19090
19091                 var splitters = this._splitters;
19092                 var topSplitter = splitters.top, bottomSplitter = splitters.bottom,
19093                         leftSplitter = splitters.left, rightSplitter = splitters.right;
19094                 var splitterThickness = this._splitterThickness;
19095                 var topSplitterThickness = splitterThickness.top || 0,
19096                         leftSplitterThickness = splitterThickness.left || 0,
19097                         rightSplitterThickness = splitterThickness.right || 0,
19098                         bottomSplitterThickness = splitterThickness.bottom || 0;
19099
19100                 // Check for race condition where CSS hasn't finished loading, so
19101                 // the splitter width == the viewport width (#5824)
19102                 if(leftSplitterThickness > 50 || rightSplitterThickness > 50){
19103                         setTimeout(dojo.hitch(this, function(){
19104                                 // Results are invalid.  Clear them out.
19105                                 this._splitterThickness = {};
19106
19107                                 for(var region in this._splitters){
19108                                         this._computeSplitterThickness(region);
19109                                 }
19110                                 this._layoutChildren();
19111                         }), 50);
19112                         return false;
19113                 }
19114
19115                 var pe = this.pe;
19116
19117                 var splitterBounds = {
19118                         left: (sidebarLayout ? leftWidth + leftSplitterThickness: 0) + pe.l + "px",
19119                         right: (sidebarLayout ? rightWidth + rightSplitterThickness: 0) + pe.r + "px"
19120                 };
19121
19122                 if(topSplitter){
19123                         dojo.mixin(topSplitter.style, splitterBounds);
19124                         topSplitter.style.top = topHeight + pe.t + "px";
19125                 }
19126
19127                 if(bottomSplitter){
19128                         dojo.mixin(bottomSplitter.style, splitterBounds);
19129                         bottomSplitter.style.bottom = bottomHeight + pe.b + "px";
19130                 }
19131
19132                 splitterBounds = {
19133                         top: (sidebarLayout ? 0 : topHeight + topSplitterThickness) + pe.t + "px",
19134                         bottom: (sidebarLayout ? 0 : bottomHeight + bottomSplitterThickness) + pe.b + "px"
19135                 };
19136
19137                 if(leftSplitter){
19138                         dojo.mixin(leftSplitter.style, splitterBounds);
19139                         leftSplitter.style.left = leftWidth + pe.l + "px";
19140                 }
19141
19142                 if(rightSplitter){
19143                         dojo.mixin(rightSplitter.style, splitterBounds);
19144                         rightSplitter.style.right = rightWidth + pe.r + "px";
19145                 }
19146
19147                 dojo.mixin(centerStyle, {
19148                         top: pe.t + topHeight + topSplitterThickness + "px",
19149                         left: pe.l + leftWidth + leftSplitterThickness + "px",
19150                         right: pe.r + rightWidth + rightSplitterThickness + "px",
19151                         bottom: pe.b + bottomHeight + bottomSplitterThickness + "px"
19152                 });
19153
19154                 var bounds = {
19155                         top: sidebarLayout ? pe.t + "px" : centerStyle.top,
19156                         bottom: sidebarLayout ? pe.b + "px" : centerStyle.bottom
19157                 };
19158                 dojo.mixin(leftStyle, bounds);
19159                 dojo.mixin(rightStyle, bounds);
19160                 leftStyle.left = pe.l + "px"; rightStyle.right = pe.r + "px"; topStyle.top = pe.t + "px"; bottomStyle.bottom = pe.b + "px";
19161                 if(sidebarLayout){
19162                         topStyle.left = bottomStyle.left = leftWidth + leftSplitterThickness + pe.l + "px";
19163                         topStyle.right = bottomStyle.right = rightWidth + rightSplitterThickness + pe.r + "px";
19164                 }else{
19165                         topStyle.left = bottomStyle.left = pe.l + "px";
19166                         topStyle.right = bottomStyle.right = pe.r + "px";
19167                 }
19168
19169                 // More calculations about sizes of panes
19170                 var containerHeight = this._borderBox.h - pe.t - pe.b,
19171                         middleHeight = containerHeight - ( topHeight + topSplitterThickness + bottomHeight + bottomSplitterThickness),
19172                         sidebarHeight = sidebarLayout ? containerHeight : middleHeight;
19173
19174                 var containerWidth = this._borderBox.w - pe.l - pe.r,
19175                         middleWidth = containerWidth - (leftWidth + leftSplitterThickness + rightWidth + rightSplitterThickness),
19176                         sidebarWidth = sidebarLayout ? middleWidth : containerWidth;
19177
19178                 // New margin-box size of each pane
19179                 var dim = {
19180                         top:    { w: sidebarWidth, h: topHeight },
19181                         bottom: { w: sidebarWidth, h: bottomHeight },
19182                         left:   { w: leftWidth, h: sidebarHeight },
19183                         right:  { w: rightWidth, h: sidebarHeight },
19184                         center: { h: middleHeight, w: middleWidth }
19185                 };
19186
19187                 if(changedRegion){
19188                         // Respond to splitter drag event by changing changedRegion's width or height
19189                         var child = this["_" + changedRegion + "Widget"],
19190                                 mb = {};
19191                                 mb[ /top|bottom/.test(changedRegion) ? "h" : "w"] = changedRegionSize;
19192                         child.resize ? child.resize(mb, dim[child.region]) : dojo.marginBox(child.domNode, mb);
19193                 }
19194
19195                 // Nodes in IE<8 don't respond to t/l/b/r, and TEXTAREA doesn't respond in any browser
19196                 var janky = dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.some(this.getChildren(), function(child){
19197                         return child.domNode.tagName == "TEXTAREA" || child.domNode.tagName == "INPUT";
19198                 });
19199                 if(janky){
19200                         // Set the size of the children the old fashioned way, by setting
19201                         // CSS width and height
19202
19203                         var resizeWidget = function(widget, changes, result){
19204                                 if(widget){
19205                                         (widget.resize ? widget.resize(changes, result) : dojo.marginBox(widget.domNode, changes));
19206                                 }
19207                         };
19208
19209                         if(leftSplitter){ leftSplitter.style.height = sidebarHeight; }
19210                         if(rightSplitter){ rightSplitter.style.height = sidebarHeight; }
19211                         resizeWidget(this._leftWidget, {h: sidebarHeight}, dim.left);
19212                         resizeWidget(this._rightWidget, {h: sidebarHeight}, dim.right);
19213
19214                         if(topSplitter){ topSplitter.style.width = sidebarWidth; }
19215                         if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; }
19216                         resizeWidget(this._topWidget, {w: sidebarWidth}, dim.top);
19217                         resizeWidget(this._bottomWidget, {w: sidebarWidth}, dim.bottom);
19218
19219                         resizeWidget(this._centerWidget, dim.center);
19220                 }else{
19221                         // Calculate which panes need a notification that their size has been changed
19222                         // (we've already set style.top/bottom/left/right on those other panes).
19223                         var notifySides = !changedRegion || (/top|bottom/.test(changedRegion) && this.design != "sidebar"),
19224                                 notifyTopBottom = !changedRegion || (/left|right/.test(changedRegion) && this.design == "sidebar"),
19225                                 notifyList = {
19226                                         center: true,
19227                                         left: notifySides,
19228                                         right: notifySides,
19229                                         top: notifyTopBottom,
19230                                         bottom: notifyTopBottom
19231                                 };
19232                         
19233                         // Send notification to those panes that have changed size
19234                         dojo.forEach(this.getChildren(), function(child){
19235                                 if(child.resize && notifyList[child.region]){
19236                                         child.resize(null, dim[child.region]);
19237                                 }
19238                         }, this);
19239                 }
19240         },
19241
19242         destroy: function(){
19243                 for(var region in this._splitters){
19244                         var splitter = this._splitters[region];
19245                         dijit.byNode(splitter).destroy();
19246                         dojo.destroy(splitter);
19247                 }
19248                 delete this._splitters;
19249                 delete this._splitterThickness;
19250                 this.inherited(arguments);
19251         }
19252 });
19253
19254 // This argument can be specified for the children of a BorderContainer.
19255 // Since any widget can be specified as a LayoutContainer child, mix it
19256 // into the base widget class.  (This is a hack, but it's effective.)
19257 dojo.extend(dijit._Widget, {
19258         // region: [const] String
19259         //              Parameter for children of `dijit.layout.BorderContainer`.
19260         //              Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
19261         //              See the `dijit.layout.BorderContainer` description for details.
19262         region: '',
19263
19264         // splitter: [const] Boolean
19265         //              Parameter for child of `dijit.layout.BorderContainer` where region != "center".
19266         //              If true, enables user to resize the widget by putting a draggable splitter between
19267         //              this widget and the region=center widget.
19268         splitter: false,
19269
19270         // minSize: [const] Number
19271         //              Parameter for children of `dijit.layout.BorderContainer`.
19272         //              Specifies a minimum size (in pixels) for this widget when resized by a splitter.
19273         minSize: 0,
19274
19275         // maxSize: [const] Number
19276         //              Parameter for children of `dijit.layout.BorderContainer`.
19277         //              Specifies a maximum size (in pixels) for this widget when resized by a splitter.
19278         maxSize: Infinity
19279 });
19280
19281
19282
19283 dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
19284 {
19285         // summary:
19286         //              A draggable spacer between two items in a `dijit.layout.BorderContainer`.
19287         // description:
19288         //              This is instantiated by `dijit.layout.BorderContainer`.  Users should not
19289         //              create it directly.
19290         // tags:
19291         //              private
19292
19293 /*=====
19294         // container: [const] dijit.layout.BorderContainer
19295         //              Pointer to the parent BorderContainer
19296         container: null,
19297
19298         // child: [const] dijit.layout._LayoutWidget
19299         //              Pointer to the pane associated with this splitter
19300         child: null,
19301
19302         // region: String
19303         //              Region of pane associated with this splitter.
19304         //              "top", "bottom", "left", "right".
19305         region: null,
19306 =====*/
19307
19308         // live: [const] Boolean
19309         //              If true, the child's size changes and the child widget is redrawn as you drag the splitter;
19310         //              otherwise, the size doesn't change until you drop the splitter (by mouse-up)
19311         live: true,
19312
19313         templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" waiRole="separator"><div class="dijitSplitterThumb"></div></div>',
19314
19315         postCreate: function(){
19316                 this.inherited(arguments);
19317                 this.horizontal = /top|bottom/.test(this.region);
19318                 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
19319 //              dojo.addClass(this.child.domNode, "dijitSplitterPane");
19320 //              dojo.setSelectable(this.domNode, false); //TODO is this necessary?
19321
19322                 this._factor = /top|left/.test(this.region) ? 1 : -1;
19323
19324                 this._cookieName = this.container.id + "_" + this.region;
19325                 if(this.container.persist){
19326                         // restore old size
19327                         var persistSize = dojo.cookie(this._cookieName);
19328                         if(persistSize){
19329                                 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
19330                         }
19331                 }
19332         },
19333
19334         _computeMaxSize: function(){
19335                 // summary:
19336                 //              Compute the maximum size that my corresponding pane can be set to
19337
19338                 var dim = this.horizontal ? 'h' : 'w',
19339                         thickness = this.container._splitterThickness[this.region];
19340                         
19341                 // Get DOMNode of opposite pane, if an opposite pane exists.
19342                 // Ex: if I am the _Splitter for the left pane, then get the right pane.
19343                 var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'},
19344                         oppNode = this.container["_" + flip[this.region]];
19345                 
19346                 // I can expand up to the edge of the opposite pane, or if there's no opposite pane, then to
19347                 // edge of BorderContainer
19348                 var available = dojo.contentBox(this.container.domNode)[dim] -
19349                                 (oppNode ? dojo.marginBox(oppNode)[dim] : 0) -
19350                                 20 - thickness * 2;
19351
19352                 return Math.min(this.child.maxSize, available);
19353         },
19354
19355         _startDrag: function(e){
19356                 if(!this.cover){
19357                         this.cover = dojo.doc.createElement('div');
19358                         dojo.addClass(this.cover, "dijitSplitterCover");
19359                         dojo.place(this.cover, this.child.domNode, "after");
19360                 }
19361                 dojo.addClass(this.cover, "dijitSplitterCoverActive");
19362
19363                 // Safeguard in case the stop event was missed.  Shouldn't be necessary if we always get the mouse up.
19364                 if(this.fake){ dojo.destroy(this.fake); }
19365                 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
19366                         // create fake splitter to display at old position while we drag
19367                         (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
19368                         dojo.addClass(this.domNode, "dijitSplitterShadow");
19369                         dojo.place(this.fake, this.domNode, "after");
19370                 }
19371                 dojo.addClass(this.domNode, "dijitSplitterActive");
19372                 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
19373                 if(this.fake){
19374                         dojo.removeClass(this.fake, "dijitSplitterHover");
19375                         dojo.removeClass(this.fake, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
19376                 }
19377
19378                 //Performance: load data info local vars for onmousevent function closure
19379                 var factor = this._factor,
19380                         max = this._computeMaxSize(),
19381                         min = this.child.minSize || 20,
19382                         isHorizontal = this.horizontal,
19383                         axis = isHorizontal ? "pageY" : "pageX",
19384                         pageStart = e[axis],
19385                         splitterStyle = this.domNode.style,
19386                         dim = isHorizontal ? 'h' : 'w',
19387                         childStart = dojo.marginBox(this.child.domNode)[dim],
19388                         region = this.region,
19389                         splitterStart = parseInt(this.domNode.style[region], 10),
19390                         resize = this._resize,
19391                         childNode = this.child.domNode,
19392                         layoutFunc = dojo.hitch(this.container, this.container._layoutChildren),
19393                         de = dojo.doc;
19394
19395                 this._handlers = (this._handlers || []).concat([
19396                         dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
19397                                 var delta = e[axis] - pageStart,
19398                                         childSize = factor * delta + childStart,
19399                                         boundChildSize = Math.max(Math.min(childSize, max), min);
19400
19401                                 if(resize || forceResize){
19402                                         layoutFunc(region, boundChildSize);
19403                                 }
19404                                 splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px";
19405                         }),
19406                         dojo.connect(de, "ondragstart", dojo.stopEvent),
19407                         dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
19408                         dojo.connect(de, "onmouseup", this, "_stopDrag")
19409                 ]);
19410                 dojo.stopEvent(e);
19411         },
19412
19413         _onMouse: function(e){
19414                 var o = (e.type == "mouseover" || e.type == "mouseenter");
19415                 dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
19416                 dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
19417         },
19418
19419         _stopDrag: function(e){
19420                 try{
19421                         if(this.cover){
19422                                 dojo.removeClass(this.cover, "dijitSplitterCoverActive");
19423                         }
19424                         if(this.fake){ dojo.destroy(this.fake); }
19425                         dojo.removeClass(this.domNode, "dijitSplitterActive");
19426                         dojo.removeClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
19427                         dojo.removeClass(this.domNode, "dijitSplitterShadow");
19428                         this._drag(e); //TODO: redundant with onmousemove?
19429                         this._drag(e, true);
19430                 }finally{
19431                         this._cleanupHandlers();
19432                         delete this._drag;
19433                 }
19434
19435                 if(this.container.persist){
19436                         dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
19437                 }
19438         },
19439
19440         _cleanupHandlers: function(){
19441                 dojo.forEach(this._handlers, dojo.disconnect);
19442                 delete this._handlers;
19443         },
19444
19445         _onKeyPress: function(/*Event*/ e){
19446                 // should we apply typematic to this?
19447                 this._resize = true;
19448                 var horizontal = this.horizontal;
19449                 var tick = 1;
19450                 var dk = dojo.keys;
19451                 switch(e.charOrCode){
19452                         case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
19453                                 tick *= -1;
19454 //                              break;
19455                         case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
19456                                 break;
19457                         default:
19458 //                              this.inherited(arguments);
19459                                 return;
19460                 }
19461                 var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
19462                 this.container._layoutChildren(this.region, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
19463                 dojo.stopEvent(e);
19464         },
19465
19466         destroy: function(){
19467                 this._cleanupHandlers();
19468                 delete this.child;
19469                 delete this.container;
19470                 delete this.cover;
19471                 delete this.fake;
19472                 this.inherited(arguments);
19473         }
19474 });
19475
19476 dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated ],
19477 {
19478         // summary:
19479         //              Just a spacer div to separate side pane from center pane.
19480         //              Basically a trick to lookup the gutter/splitter width from the theme.
19481         // description:
19482         //              Instantiated by `dijit.layout.BorderContainer`.  Users should not
19483         //              create directly.
19484         // tags:
19485         //              private
19486
19487         templateString: '<div class="dijitGutter" waiRole="presentation"></div>',
19488
19489         postCreate: function(){
19490                 this.horizontal = /top|bottom/.test(this.region);
19491                 dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
19492         }
19493 });
19494
19495 }
19496
19497 if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19498 dojo._hasResource["dijit.layout._TabContainerBase"] = true;
19499 dojo.provide("dijit.layout._TabContainerBase");
19500
19501
19502
19503
19504 dojo.declare("dijit.layout._TabContainerBase",
19505         [dijit.layout.StackContainer, dijit._Templated],
19506         {
19507         // summary:
19508         //              Abstract base class for TabContainer.   Must define _makeController() to instantiate
19509         //              and return the widget that displays the tab labels
19510         // description:
19511         //              A TabContainer is a container that has multiple panes, but shows only
19512         //              one pane at a time.  There are a set of tabs corresponding to each pane,
19513         //              where each tab has the name (aka title) of the pane, and optionally a close button.
19514
19515         // tabPosition: String
19516         //              Defines where tabs go relative to tab content.
19517         //              "top", "bottom", "left-h", "right-h"
19518         tabPosition: "top",
19519
19520         baseClass: "dijitTabContainer",
19521
19522         // tabStrip: Boolean
19523         //              Defines whether the tablist gets an extra class for layouting, putting a border/shading
19524         //              around the set of tabs.
19525         tabStrip: false,
19526
19527         // nested: Boolean
19528         //              If true, use styling for a TabContainer nested inside another TabContainer.
19529         //              For tundra etc., makes tabs look like links, and hides the outer
19530         //              border since the outer TabContainer already has a border.
19531         nested: false,
19532
19533         templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" dojoAttachPoint=\"tablistNode\"></div>\n\t<div dojoAttachPoint=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" dojoAttachPoint=\"containerNode\"></div>\n</div>\n"),
19534
19535         postMixInProperties: function(){
19536                 // set class name according to tab position, ex: dijitTabContainerTop
19537                 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
19538
19539                 this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
19540
19541                 this.inherited(arguments);
19542         },
19543
19544         postCreate: function(){
19545                 this.inherited(arguments);
19546
19547                 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
19548                 this.tablist = this._makeController(this.tablistNode);
19549
19550                 if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
19551
19552                 if(this.nested){
19553                         /* workaround IE's lack of support for "a > b" selectors by
19554                          * tagging each node in the template.
19555                          */
19556                         dojo.addClass(this.domNode, "dijitTabContainerNested");
19557                         dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
19558                         dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
19559                         dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
19560                 }else{
19561                         dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
19562                 }
19563         },
19564
19565         _setupChild: function(/*dijit._Widget*/ tab){
19566                 // Overrides StackContainer._setupChild().
19567                 dojo.addClass(tab.domNode, "dijitTabPane");
19568                 this.inherited(arguments);
19569         },
19570
19571         startup: function(){
19572                 if(this._started){ return; }
19573
19574                 // wire up the tablist and its tabs
19575                 this.tablist.startup();
19576
19577                 this.inherited(arguments);
19578         },
19579
19580         layout: function(){
19581                 // Overrides StackContainer.layout().
19582                 // Configure the content pane to take up all the space except for where the tabs are
19583
19584                 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
19585
19586                 var sc = this.selectedChildWidget;
19587
19588                 if(this.doLayout){
19589                         // position and size the titles and the container node
19590                         var titleAlign = this.tabPosition.replace(/-h/, "");
19591                         this.tablist.layoutAlign = titleAlign;
19592                         var children = [this.tablist, {
19593                                 domNode: this.tablistSpacer,
19594                                 layoutAlign: titleAlign
19595                         }, {
19596                                 domNode: this.containerNode,
19597                                 layoutAlign: "client"
19598                         }];
19599                         dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
19600
19601                         // Compute size to make each of my children.
19602                         // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
19603                         this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
19604
19605                         if(sc && sc.resize){
19606                                 sc.resize(this._containerContentBox);
19607                         }
19608                 }else{
19609                         // just layout the tab controller, so it can position left/right buttons etc.
19610                         if(this.tablist.resize){
19611                                 this.tablist.resize({w: dojo.contentBox(this.domNode).w});
19612                         }
19613
19614                         // and call resize() on the selected pane just to tell it that it's been made visible
19615                         if(sc && sc.resize){
19616                                 sc.resize();
19617                         }
19618                 }
19619         },
19620
19621         destroy: function(){
19622                 if(this.tablist){
19623                         this.tablist.destroy();
19624                 }
19625                 this.inherited(arguments);
19626         }
19627 });
19628
19629
19630 }
19631
19632 if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19633 dojo._hasResource["dijit.layout.TabController"] = true;
19634 dojo.provide("dijit.layout.TabController");
19635
19636
19637
19638 // Menu is used for an accessible close button, would be nice to have a lighter-weight solution
19639
19640
19641
19642
19643
19644 dojo.declare("dijit.layout.TabController",
19645         dijit.layout.StackController,
19646 {
19647         // summary:
19648         //              Set of tabs (the things with titles and a close button, that you click to show a tab panel).
19649         //              Used internally by `dijit.layout.TabContainer`.
19650         // description:
19651         //              Lets the user select the currently shown pane in a TabContainer or StackContainer.
19652         //              TabController also monitors the TabContainer, and whenever a pane is
19653         //              added or deleted updates itself accordingly.
19654         // tags:
19655         //              private
19656
19657         templateString: "<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
19658
19659         // tabPosition: String
19660         //              Defines where tabs go relative to the content.
19661         //              "top", "bottom", "left-h", "right-h"
19662         tabPosition: "top",
19663
19664         // buttonWidget: String
19665         //              The name of the tab widget to create to correspond to each page
19666         buttonWidget: "dijit.layout._TabButton",
19667
19668         _rectifyRtlTabList: function(){
19669                 // summary:
19670                 //              For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
19671
19672                 if(0 >= this.tabPosition.indexOf('-h')){ return; }
19673                 if(!this.pane2button){ return; }
19674
19675                 var maxWidth = 0;
19676                 for(var pane in this.pane2button){
19677                         var ow = this.pane2button[pane].innerDiv.scrollWidth;
19678                         maxWidth = Math.max(maxWidth, ow);
19679                 }
19680                 //unify the length of all the tabs
19681                 for(pane in this.pane2button){
19682                         this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
19683                 }
19684         }
19685 });
19686
19687 dojo.declare("dijit.layout._TabButton",
19688         dijit.layout._StackButton,
19689         {
19690         // summary:
19691         //              A tab (the thing you click to select a pane).
19692         // description:
19693         //              Contains the title of the pane, and optionally a close-button to destroy the pane.
19694         //              This is an internal widget and should not be instantiated directly.
19695         // tags:
19696         //              private
19697
19698         // baseClass: String
19699         //              The CSS class applied to the domNode.
19700         baseClass: "dijitTab",
19701
19702         // Apply dijitTabCloseButtonHover when close button is hovered
19703         cssStateNodes: {
19704                 closeNode: "dijitTabCloseButton"
19705         },
19706
19707         templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div waiRole=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n    <div waiRole=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n        <div waiRole=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n        \t<div waiRole=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t        <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' />\n\t\t        <span dojoAttachPoint='containerNode' class='tabLabel'></span>\n\t\t        <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" dojoAttachPoint='closeNode'\n\t\t        \t\tdojoAttachEvent='onclick: onClickCloseButton' waiRole=\"presentation\">\n\t\t            <span dojoAttachPoint='closeText' class='dijitTabCloseText'>x</span\n\t\t        ></span>\n\t\t\t</div>\n        </div>\n    </div>\n</div>\n"),
19708
19709         // Override _FormWidget.scrollOnFocus.
19710         // Don't scroll the whole tab container into view when the button is focused.
19711         scrollOnFocus: false,
19712
19713         postMixInProperties: function(){
19714                 // Override blank iconClass from Button to do tab height adjustment on IE6,
19715                 // to make sure that tabs with and w/out close icons are same height
19716                 if(!this.iconClass){
19717                         this.iconClass = "dijitTabButtonIcon";
19718                 }
19719         },
19720
19721         postCreate: function(){
19722                 this.inherited(arguments);
19723                 dojo.setSelectable(this.containerNode, false);
19724
19725                 // If a custom icon class has not been set for the
19726                 // tab icon, set its width to one pixel. This ensures
19727                 // that the height styling of the tab is maintained,
19728                 // as it is based on the height of the icon.
19729                 // TODO: I still think we can just set dijitTabButtonIcon to 1px in CSS <Bill>
19730                 if(this.iconNode.className == "dijitTabButtonIcon"){
19731                         dojo.style(this.iconNode, "width", "1px");
19732                 }
19733         },
19734
19735         startup: function(){
19736                 this.inherited(arguments);
19737                 var n = this.domNode;
19738
19739                 // Required to give IE6 a kick, as it initially hides the
19740                 // tabs until they are focused on.
19741                 setTimeout(function(){
19742                         n.className = n.className;
19743                 }, 1);
19744         },
19745
19746         _setCloseButtonAttr: function(disp){
19747                 this.closeButton = disp;
19748                 dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
19749                 this.closeNode.style.display = disp ? "" : "none";
19750                 if(disp){
19751                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
19752                         if(this.closeNode){
19753                                 dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
19754                         }
19755                         // add context menu onto title button
19756                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
19757                         this._closeMenu = new dijit.Menu({
19758                                 id: this.id+"_Menu",
19759                                 dir: this.dir,
19760                                 lang: this.lang,
19761                                 targetNodeIds: [this.domNode]
19762                         });
19763
19764                         this._closeMenu.addChild(new dijit.MenuItem({
19765                                 label: _nlsResources.itemClose,
19766                                 dir: this.dir,
19767                                 lang: this.lang,
19768                                 onClick: dojo.hitch(this, "onClickCloseButton")
19769                         }));
19770                 }else{
19771                         if(this._closeMenu){
19772                                 this._closeMenu.destroyRecursive();
19773                                 delete this._closeMenu;
19774                         }
19775                 }
19776         },
19777         _setLabelAttr: function(/*String*/ content){
19778                 // summary:
19779                 //              Hook for attr('label', ...) to work.
19780                 // description:
19781                 //              takes an HTML string.
19782                 //              Inherited ToggleButton implementation will Set the label (text) of the button; 
19783                 //              Need to set the alt attribute of icon on tab buttons if no label displayed
19784                         this.inherited(arguments);
19785                         if(this.showLabel == false && !this.params.title){
19786                                 this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
19787                         }
19788                 },
19789
19790         destroy: function(){
19791                 if(this._closeMenu){
19792                         this._closeMenu.destroyRecursive();
19793                         delete this._closeMenu;
19794                 }
19795                 this.inherited(arguments);
19796         }
19797 });
19798
19799 }
19800
19801 if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19802 dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
19803 dojo.provide("dijit.layout.ScrollingTabController");
19804
19805
19806
19807
19808 dojo.declare("dijit.layout.ScrollingTabController",
19809         dijit.layout.TabController,
19810         {
19811         // summary:
19812         //              Set of tabs with left/right arrow keys and a menu to switch between tabs not
19813         //              all fitting on a single row.
19814         //              Works only for horizontal tabs (either above or below the content, not to the left
19815         //              or right).
19816         // tags:
19817         //              private
19818
19819         templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdojoAttachPoint=\"_menuBtn\" showLabel=false>&#9660;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\" iconClass=\"dijitTabStripSlideLeftIcon\"\n\t\t\tdojoAttachPoint=\"_leftBtn\" dojoAttachEvent=\"onClick: doSlideLeft\" showLabel=false>&#9664;</div>\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\" iconClass=\"dijitTabStripSlideRightIcon\"\n\t\t\tdojoAttachPoint=\"_rightBtn\" dojoAttachEvent=\"onClick: doSlideRight\" showLabel=false>&#9654;</div>\n\t<div class='dijitTabListWrapper' dojoAttachPoint='tablistWrapper'>\n\t\t<div wairole='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"),
19820
19821         // useMenu:[const] Boolean
19822         //              True if a menu should be used to select tabs when they are too
19823         //              wide to fit the TabContainer, false otherwise.
19824         useMenu: true,
19825
19826         // useSlider: [const] Boolean
19827         //              True if a slider should be used to select tabs when they are too
19828         //              wide to fit the TabContainer, false otherwise.
19829         useSlider: true,
19830
19831         // tabStripClass: String
19832         //              The css class to apply to the tab strip, if it is visible.
19833         tabStripClass: "",
19834
19835         widgetsInTemplate: true,
19836
19837         // _minScroll: Number
19838         //              The distance in pixels from the edge of the tab strip which,
19839         //              if a scroll animation is less than, forces the scroll to
19840         //              go all the way to the left/right.
19841         _minScroll: 5,
19842
19843         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
19844                 "class": "containerNode"
19845         }),
19846
19847         postCreate: function(){
19848                 this.inherited(arguments);
19849                 var n = this.domNode;
19850
19851                 this.scrollNode = this.tablistWrapper;
19852                 this._initButtons();
19853
19854                 if(!this.tabStripClass){
19855                         this.tabStripClass = "dijitTabContainer" +
19856                                 this.tabPosition.charAt(0).toUpperCase() +
19857                                 this.tabPosition.substr(1).replace(/-.*/, "") +
19858                                 "None";
19859                         dojo.addClass(n, "tabStrip-disabled")
19860                 }
19861
19862                 dojo.addClass(this.tablistWrapper, this.tabStripClass);
19863         },
19864
19865         onStartup: function(){
19866                 this.inherited(arguments);
19867
19868                 // Do not show the TabController until the related
19869                 // StackController has added it's children.  This gives
19870                 // a less visually jumpy instantiation.
19871                 dojo.style(this.domNode, "visibility", "visible");
19872                 this._postStartup = true;
19873         },
19874
19875         onAddChild: function(page, insertIndex){
19876                 this.inherited(arguments);
19877                 var menuItem;
19878                 if(this.useMenu){
19879                         var containerId = this.containerId;
19880                         menuItem = new dijit.MenuItem({
19881                                 id: page.id + "_stcMi",
19882                                 label: page.title,
19883                                 dir: page.dir,
19884                                 lang: page.lang,
19885                                 onClick: dojo.hitch(this, function(){
19886                                         var container = dijit.byId(containerId);
19887                                         container.selectChild(page);
19888                                 })
19889                         });
19890                         this._menuChildren[page.id] = menuItem;
19891                         this._menu.addChild(menuItem, insertIndex);
19892                 }
19893
19894                 // update the menuItem label when the button label is updated
19895                 this.pane2handles[page.id].push(
19896                         this.connect(this.pane2button[page.id], "set", function(name, value){
19897                                 if(this._postStartup){
19898                                         if(name == "label"){
19899                                                 if(menuItem){
19900                                                         menuItem.set(name, value);
19901                                                 }
19902         
19903                                                 // The changed label will have changed the width of the
19904                                                 // buttons, so do a resize
19905                                                 if(this._dim){
19906                                                         this.resize(this._dim);
19907                                                 }
19908                                         }
19909                                 }
19910                         })
19911                 );
19912
19913                 // Increment the width of the wrapper when a tab is added
19914                 // This makes sure that the buttons never wrap.
19915                 // The value 200 is chosen as it should be bigger than most
19916                 // Tab button widths.
19917                 dojo.style(this.containerNode, "width",
19918                         (dojo.style(this.containerNode, "width") + 200) + "px");
19919         },
19920
19921         onRemoveChild: function(page, insertIndex){
19922                 // null out _selectedTab because we are about to delete that dom node
19923                 var button = this.pane2button[page.id];
19924                 if(this._selectedTab === button.domNode){
19925                         this._selectedTab = null;
19926                 }
19927
19928                 // delete menu entry corresponding to pane that was removed from TabContainer
19929                 if(this.useMenu && page && page.id && this._menuChildren[page.id]){
19930                         this._menu.removeChild(this._menuChildren[page.id]);
19931                         this._menuChildren[page.id].destroy();
19932                         delete this._menuChildren[page.id];
19933                 }
19934
19935                 this.inherited(arguments);
19936         },
19937
19938         _initButtons: function(){
19939                 // summary:
19940                 //              Creates the buttons used to scroll to view tabs that
19941                 //              may not be visible if the TabContainer is too narrow.
19942                 this._menuChildren = {};
19943
19944                 // Make a list of the buttons to display when the tab labels become
19945                 // wider than the TabContainer, and hide the other buttons.
19946                 // Also gets the total width of the displayed buttons.
19947                 this._btnWidth = 0;
19948                 this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
19949                         if((this.useMenu && btn == this._menuBtn.domNode) ||
19950                                 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
19951                                 this._btnWidth += dojo.marginBox(btn).w;
19952                                 return true;
19953                         }else{
19954                                 dojo.style(btn, "display", "none");
19955                                 return false;
19956                         }
19957                 }, this);
19958
19959                 if(this.useMenu){
19960                         // Create the menu that is used to select tabs.
19961                         this._menu = new dijit.Menu({
19962                                 id: this.id + "_menu",
19963                                 dir: this.dir,
19964                                 lang: this.lang,
19965                                 targetNodeIds: [this._menuBtn.domNode],
19966                                 leftClickToOpen: true,
19967                                 refocus: false  // selecting a menu item sets focus to a TabButton
19968                         });
19969                         this._supportingWidgets.push(this._menu);
19970                 }
19971         },
19972
19973         _getTabsWidth: function(){
19974                 var children = this.getChildren();
19975                 if(children.length){
19976                         var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
19977                                 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
19978                         return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
19979                 }else{
19980                         return 0;
19981                 }
19982         },
19983
19984         _enableBtn: function(width){
19985                 // summary:
19986                 //              Determines if the tabs are wider than the width of the TabContainer, and
19987                 //              thus that we need to display left/right/menu navigation buttons.
19988                 var tabsWidth = this._getTabsWidth();
19989                 width = width || dojo.style(this.scrollNode, "width");
19990                 return tabsWidth > 0 && width < tabsWidth;
19991         },
19992
19993         resize: function(dim){
19994                 // summary:
19995                 //              Hides or displays the buttons used to scroll the tab list and launch the menu
19996                 //              that selects tabs.
19997
19998                 if(this.domNode.offsetWidth == 0){
19999                         return;
20000                 }
20001
20002                 // Save the dimensions to be used when a child is renamed.
20003                 this._dim = dim;
20004
20005                 // Set my height to be my natural height (tall enough for one row of tab labels),
20006                 // and my content-box width based on margin-box width specified in dim parameter.
20007                 // But first reset scrollNode.height in case it was set by layoutChildren() call
20008                 // in a previous run of this method.
20009                 this.scrollNode.style.height = "auto";
20010                 this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
20011                 this._contentBox.h = this.scrollNode.offsetHeight;
20012                 dojo.contentBox(this.domNode, this._contentBox);
20013
20014                 // Show/hide the left/right/menu navigation buttons depending on whether or not they
20015                 // are needed.
20016                 var enable = this._enableBtn(this._contentBox.w);
20017                 this._buttons.style("display", enable ? "" : "none");
20018
20019                 // Position and size the navigation buttons and the tablist
20020                 this._leftBtn.layoutAlign = "left";
20021                 this._rightBtn.layoutAlign = "right";
20022                 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
20023                 dijit.layout.layoutChildren(this.domNode, this._contentBox,
20024                         [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
20025
20026                 // set proper scroll so that selected tab is visible
20027                 if(this._selectedTab){
20028                         if(this._anim && this._anim.status() == "playing"){
20029                                 this._anim.stop();
20030                         }
20031                         var w = this.scrollNode,
20032                                 sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
20033                         w.scrollLeft = sl;
20034                 }
20035
20036                 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
20037                 this._setButtonClass(this._getScroll());
20038                 
20039                 this._postResize = true;
20040         },
20041
20042         _getScroll: function(){
20043                 // summary:
20044                 //              Returns the current scroll of the tabs where 0 means
20045                 //              "scrolled all the way to the left" and some positive number, based on #
20046                 //              of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
20047                 var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
20048                                 dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
20049                                          + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
20050                 return sl;
20051         },
20052
20053         _convertToScrollLeft: function(val){
20054                 // summary:
20055                 //              Given a scroll value where 0 means "scrolled all the way to the left"
20056                 //              and some positive number, based on # of pixels of possible scroll (ex: 1000)
20057                 //              means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
20058                 //              to achieve that scroll.
20059                 //
20060                 //              This method is to adjust for RTL funniness in various browsers and versions.
20061                 if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
20062                         return val;
20063                 }else{
20064                         var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
20065                         return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
20066                 }
20067         },
20068
20069         onSelectChild: function(/*dijit._Widget*/ page){
20070                 // summary:
20071                 //              Smoothly scrolls to a tab when it is selected.
20072
20073                 var tab = this.pane2button[page.id];
20074                 if(!tab || !page){return;}
20075
20076                 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
20077                 var node = tab.domNode;
20078                 if(this._postResize && node != this._selectedTab){
20079                         this._selectedTab = node;
20080
20081                         var sl = this._getScroll();
20082
20083                         if(sl > node.offsetLeft ||
20084                                         sl + dojo.style(this.scrollNode, "width") <
20085                                         node.offsetLeft + dojo.style(node, "width")){
20086                                 this.createSmoothScroll().play();
20087                         }
20088                 }
20089
20090                 this.inherited(arguments);
20091         },
20092
20093         _getScrollBounds: function(){
20094                 // summary:
20095                 //              Returns the minimum and maximum scroll setting to show the leftmost and rightmost
20096                 //              tabs (respectively)
20097                 var children = this.getChildren(),
20098                         scrollNodeWidth = dojo.style(this.scrollNode, "width"),         // about 500px
20099                         containerWidth = dojo.style(this.containerNode, "width"),       // 50,000px
20100                         maxPossibleScroll = containerWidth - scrollNodeWidth,   // scrolling until right edge of containerNode visible
20101                         tabsWidth = this._getTabsWidth();
20102
20103                 if(children.length && tabsWidth > scrollNodeWidth){
20104                         // Scrolling should happen
20105                         return {
20106                                 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
20107                                 max: this.isLeftToRight() ?
20108                                         (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
20109                                         maxPossibleScroll
20110                         };
20111                 }else{
20112                         // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
20113                         var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
20114                         return {
20115                                 min: onlyScrollPosition,
20116                                 max: onlyScrollPosition
20117                         };
20118                 }
20119         },
20120
20121         _getScrollForSelectedTab: function(){
20122                 // summary:
20123                 //              Returns the scroll value setting so that the selected tab
20124                 //              will appear in the center
20125                 var w = this.scrollNode,
20126                         n = this._selectedTab,
20127                         scrollNodeWidth = dojo.style(this.scrollNode, "width"),
20128                         scrollBounds = this._getScrollBounds();
20129
20130                 // TODO: scroll minimal amount (to either right or left) so that
20131                 // selected tab is fully visible, and just return if it's already visible?
20132                 var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
20133                 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
20134
20135                 // TODO:
20136                 // If scrolling close to the left side or right side, scroll
20137                 // all the way to the left or right.  See this._minScroll.
20138                 // (But need to make sure that doesn't scroll the tab out of view...)
20139                 return pos;
20140         },
20141
20142         createSmoothScroll : function(x){
20143                 // summary:
20144                 //              Creates a dojo._Animation object that smoothly scrolls the tab list
20145                 //              either to a fixed horizontal pixel value, or to the selected tab.
20146                 // description:
20147                 //              If an number argument is passed to the function, that horizontal
20148                 //              pixel position is scrolled to.  Otherwise the currently selected
20149                 //              tab is scrolled to.
20150                 // x: Integer?
20151                 //              An optional pixel value to scroll to, indicating distance from left.
20152
20153                 // Calculate position to scroll to
20154                 if(arguments.length > 0){
20155                         // position specified by caller, just make sure it's within bounds
20156                         var scrollBounds = this._getScrollBounds();
20157                         x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
20158                 }else{
20159                         // scroll to center the current tab
20160                         x = this._getScrollForSelectedTab();
20161                 }
20162
20163                 if(this._anim && this._anim.status() == "playing"){
20164                         this._anim.stop();
20165                 }
20166
20167                 var self = this,
20168                         w = this.scrollNode,
20169                         anim = new dojo._Animation({
20170                                 beforeBegin: function(){
20171                                         if(this.curve){ delete this.curve; }
20172                                         var oldS = w.scrollLeft,
20173                                                 newS = self._convertToScrollLeft(x);
20174                                         anim.curve = new dojo._Line(oldS, newS);
20175                                 },
20176                                 onAnimate: function(val){
20177                                         w.scrollLeft = val;
20178                                 }
20179                         });
20180                 this._anim = anim;
20181
20182                 // Disable/enable left/right buttons according to new scroll position
20183                 this._setButtonClass(x);
20184
20185                 return anim; // dojo._Animation
20186         },
20187
20188         _getBtnNode: function(e){
20189                 // summary:
20190                 //              Gets a button DOM node from a mouse click event.
20191                 // e:
20192                 //              The mouse click event.
20193                 var n = e.target;
20194                 while(n && !dojo.hasClass(n, "tabStripButton")){
20195                         n = n.parentNode;
20196                 }
20197                 return n;
20198         },
20199
20200         doSlideRight: function(e){
20201                 // summary:
20202                 //              Scrolls the menu to the right.
20203                 // e:
20204                 //              The mouse click event.
20205                 this.doSlide(1, this._getBtnNode(e));
20206         },
20207
20208         doSlideLeft: function(e){
20209                 // summary:
20210                 //              Scrolls the menu to the left.
20211                 // e:
20212                 //              The mouse click event.
20213                 this.doSlide(-1,this._getBtnNode(e));
20214         },
20215
20216         doSlide: function(direction, node){
20217                 // summary:
20218                 //              Scrolls the tab list to the left or right by 75% of the widget width.
20219                 // direction:
20220                 //              If the direction is 1, the widget scrolls to the right, if it is
20221                 //              -1, it scrolls to the left.
20222
20223                 if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
20224
20225                 var sWidth = dojo.style(this.scrollNode, "width");
20226                 var d = (sWidth * 0.75) * direction;
20227
20228                 var to = this._getScroll() + d;
20229
20230                 this._setButtonClass(to);
20231
20232                 this.createSmoothScroll(to).play();
20233         },
20234
20235         _setButtonClass: function(scroll){
20236                 // summary:
20237                 //              Disables the left scroll button if the tabs are scrolled all the way to the left,
20238                 //              or the right scroll button in the opposite case.
20239                 // scroll: Integer
20240                 //              amount of horizontal scroll
20241
20242                 var scrollBounds = this._getScrollBounds();
20243                 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
20244                 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
20245         }
20246 });
20247
20248 dojo.declare("dijit.layout._ScrollingTabControllerButton",
20249         dijit.form.Button,
20250         {
20251                 baseClass: "dijitTab tabStripButton",
20252
20253                 templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div waiRole=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div waiRole=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img waiRole=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" dojoAttachPoint=\"iconNode\"/>\n\t\t\t<span dojoAttachPoint=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>\n"),
20254
20255                 // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
20256                 // able to tab to the left/right/menu buttons
20257                 tabIndex: "-1"
20258         }
20259 );
20260
20261 }
20262
20263 if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20264 dojo._hasResource["dijit.layout.TabContainer"] = true;
20265 dojo.provide("dijit.layout.TabContainer");
20266
20267
20268
20269
20270
20271 dojo.declare("dijit.layout.TabContainer",
20272         dijit.layout._TabContainerBase,
20273         {
20274                 // summary:
20275                 //              A Container with tabs to select each child (only one of which is displayed at a time).
20276                 // description:
20277                 //              A TabContainer is a container that has multiple panes, but shows only
20278                 //              one pane at a time.  There are a set of tabs corresponding to each pane,
20279                 //              where each tab has the name (aka title) of the pane, and optionally a close button.
20280
20281                 // useMenu: [const] Boolean
20282                 //              True if a menu should be used to select tabs when they are too
20283                 //              wide to fit the TabContainer, false otherwise.
20284                 useMenu: true,
20285
20286                 // useSlider: [const] Boolean
20287                 //              True if a slider should be used to select tabs when they are too
20288                 //              wide to fit the TabContainer, false otherwise.
20289                 useSlider: true,
20290
20291                 // controllerWidget: String
20292                 //              An optional parameter to override the widget used to display the tab labels
20293                 controllerWidget: "",
20294
20295                 _makeController: function(/*DomNode*/ srcNode){
20296                         // summary:
20297                         //              Instantiate tablist controller widget and return reference to it.
20298                         //              Callback from _TabContainerBase.postCreate().
20299                         // tags:
20300                         //              protected extension
20301
20302                         var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
20303                                 TabController = dojo.getObject(this.controllerWidget);
20304
20305                         return new TabController({
20306                                 id: this.id + "_tablist",
20307                                 dir: this.dir,
20308                                 lang: this.lang,
20309                                 tabPosition: this.tabPosition,
20310                                 doLayout: this.doLayout,
20311                                 containerId: this.id,
20312                                 "class": cls,
20313                                 nested: this.nested,
20314                                 useMenu: this.useMenu,
20315                                 useSlider: this.useSlider,
20316                                 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
20317                         }, srcNode);
20318                 },
20319
20320                 postMixInProperties: function(){
20321                         this.inherited(arguments);
20322
20323                         // Scrolling controller only works for horizontal non-nested tabs
20324                         if(!this.controllerWidget){
20325                                 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
20326                                                         "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
20327                         }
20328                 }
20329 });
20330
20331
20332 }
20333
20334 if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20335 dojo._hasResource["dojo.number"] = true;
20336 dojo.provide("dojo.number");
20337
20338
20339
20340
20341
20342
20343
20344 /*=====
20345 dojo.number = {
20346         // summary: localized formatting and parsing routines for Number
20347 }
20348
20349 dojo.number.__FormatOptions = function(){
20350         //      pattern: String?
20351         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
20352         //              with this string.  Default value is based on locale.  Overriding this property will defeat
20353         //              localization.  Literal characters in patterns are not supported.
20354         //      type: String?
20355         //              choose a format type based on the locale from the following:
20356         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
20357         //      places: Number?
20358         //              fixed number of decimal places to show.  This overrides any
20359         //              information in the provided pattern.
20360         //      round: Number?
20361         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
20362         //              means do not round.
20363         //      locale: String?
20364         //              override the locale used to determine formatting rules
20365         //      fractional: Boolean?
20366         //              If false, show no decimal places, overriding places and pattern settings.
20367         this.pattern = pattern;
20368         this.type = type;
20369         this.places = places;
20370         this.round = round;
20371         this.locale = locale;
20372         this.fractional = fractional;
20373 }
20374 =====*/
20375
20376 dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
20377         // summary:
20378         //              Format a Number as a String, using locale-specific settings
20379         // description:
20380         //              Create a string from a Number using a known localized pattern.
20381         //              Formatting patterns appropriate to the locale are chosen from the
20382         //              [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
20383         //              delimiters.
20384         //              If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
20385         // value:
20386         //              the number to be formatted
20387
20388         options = dojo.mixin({}, options || {});
20389         var locale = dojo.i18n.normalizeLocale(options.locale),
20390                 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
20391         options.customs = bundle;
20392         var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
20393         if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
20394         return dojo.number._applyPattern(value, pattern, options); // String
20395 };
20396
20397 //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
20398 dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
20399
20400 dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
20401         // summary:
20402         //              Apply pattern to format value as a string using options. Gives no
20403         //              consideration to local customs.
20404         // value:
20405         //              the number to be formatted.
20406         // pattern:
20407         //              a pattern string as described by
20408         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
20409         // options: dojo.number.__FormatOptions?
20410         //              _applyPattern is usually called via `dojo.number.format()` which
20411         //              populates an extra property in the options parameter, "customs".
20412         //              The customs object specifies group and decimal parameters if set.
20413
20414         //TODO: support escapes
20415         options = options || {};
20416         var group = options.customs.group,
20417                 decimal = options.customs.decimal,
20418                 patternList = pattern.split(';'),
20419                 positivePattern = patternList[0];
20420         pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
20421
20422         //TODO: only test against unescaped
20423         if(pattern.indexOf('%') != -1){
20424                 value *= 100;
20425         }else if(pattern.indexOf('\u2030') != -1){
20426                 value *= 1000; // per mille
20427         }else if(pattern.indexOf('\u00a4') != -1){
20428                 group = options.customs.currencyGroup || group;//mixins instead?
20429                 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
20430                 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
20431                         var prop = ["symbol", "currency", "displayName"][match.length-1];
20432                         return options[prop] || options.currency || "";
20433                 });
20434         }else if(pattern.indexOf('E') != -1){
20435                 throw new Error("exponential notation not supported");
20436         }
20437         
20438         //TODO: support @ sig figs?
20439         var numberPatternRE = dojo.number._numberPatternRE;
20440         var numberPattern = positivePattern.match(numberPatternRE);
20441         if(!numberPattern){
20442                 throw new Error("unable to find a number expression in pattern: "+pattern);
20443         }
20444         if(options.fractional === false){ options.places = 0; }
20445         return pattern.replace(numberPatternRE,
20446                 dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
20447 }
20448
20449 dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
20450         //      summary:
20451         //              Rounds to the nearest value with the given number of decimal places, away from zero
20452         //      description:
20453         //              Rounds to the nearest value with the given number of decimal places, away from zero if equal.
20454         //              Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
20455         //              fractional increments also, such as the nearest quarter.
20456         //              NOTE: Subject to floating point errors.  See dojox.math.round for experimental workaround.
20457         //      value:
20458         //              The number to round
20459         //      places:
20460         //              The number of decimal places where rounding takes place.  Defaults to 0 for whole rounding.
20461         //              Must be non-negative.
20462         //      increment:
20463         //              Rounds next place to nearest value of increment/10.  10 by default.
20464         //      example:
20465         //              >>> dojo.number.round(-0.5)
20466         //              -1
20467         //              >>> dojo.number.round(162.295, 2)
20468         //              162.29  // note floating point error.  Should be 162.3
20469         //              >>> dojo.number.round(10.71, 0, 2.5)
20470         //              10.75
20471         var factor = 10 / (increment || 10);
20472         return (factor * +value).toFixed(places) / factor; // Number
20473 }
20474
20475 if((0.9).toFixed() == 0){
20476         // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
20477         // is just after the rounding place and is >=5
20478         (function(){
20479                 var round = dojo.number.round;
20480                 dojo.number.round = function(v, p, m){
20481                         var d = Math.pow(10, -p || 0), a = Math.abs(v);
20482                         if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
20483                                 d = 0;
20484                         }
20485                         return round(v, p, m) + (v > 0 ? d : -d);
20486                 }
20487         })();
20488 }
20489
20490 /*=====
20491 dojo.number.__FormatAbsoluteOptions = function(){
20492         //      decimal: String?
20493         //              the decimal separator
20494         //      group: String?
20495         //              the group separator
20496         //      places: Number?|String?
20497         //              number of decimal places.  the range "n,m" will format to m places.
20498         //      round: Number?
20499         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
20500         //              means don't round.
20501         this.decimal = decimal;
20502         this.group = group;
20503         this.places = places;
20504         this.round = round;
20505 }
20506 =====*/
20507
20508 dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
20509         // summary: 
20510         //              Apply numeric pattern to absolute value using options. Gives no
20511         //              consideration to local customs.
20512         // value:
20513         //              the number to be formatted, ignores sign
20514         // pattern:
20515         //              the number portion of a pattern (e.g. `#,##0.00`)
20516         options = options || {};
20517         if(options.places === true){options.places=0;}
20518         if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
20519
20520         var patternParts = pattern.split("."),
20521                 comma = typeof options.places == "string" && options.places.indexOf(","),
20522                 maxPlaces = options.places;
20523         if(comma){
20524                 maxPlaces = options.places.substring(comma + 1);
20525         }else if(!(maxPlaces >= 0)){
20526                 maxPlaces = (patternParts[1] || []).length;
20527         }
20528         if(!(options.round < 0)){
20529                 value = dojo.number.round(value, maxPlaces, options.round);
20530         }
20531
20532         var valueParts = String(Math.abs(value)).split("."),
20533                 fractional = valueParts[1] || "";
20534         if(patternParts[1] || options.places){
20535                 if(comma){
20536                         options.places = options.places.substring(0, comma);
20537                 }
20538                 // Pad fractional with trailing zeros
20539                 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
20540                 if(pad > fractional.length){
20541                         valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
20542                 }
20543
20544                 // Truncate fractional
20545                 if(maxPlaces < fractional.length){
20546                         valueParts[1] = fractional.substr(0, maxPlaces);
20547                 }
20548         }else{
20549                 if(valueParts[1]){ valueParts.pop(); }
20550         }
20551
20552         // Pad whole with leading zeros
20553         var patternDigits = patternParts[0].replace(',', '');
20554         pad = patternDigits.indexOf("0");
20555         if(pad != -1){
20556                 pad = patternDigits.length - pad;
20557                 if(pad > valueParts[0].length){
20558                         valueParts[0] = dojo.string.pad(valueParts[0], pad);
20559                 }
20560
20561                 // Truncate whole
20562                 if(patternDigits.indexOf("#") == -1){
20563                         valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
20564                 }
20565         }
20566
20567         // Add group separators
20568         var index = patternParts[0].lastIndexOf(','),
20569                 groupSize, groupSize2;
20570         if(index != -1){
20571                 groupSize = patternParts[0].length - index - 1;
20572                 var remainder = patternParts[0].substr(0, index);
20573                 index = remainder.lastIndexOf(',');
20574                 if(index != -1){
20575                         groupSize2 = remainder.length - index - 1;
20576                 }
20577         }
20578         var pieces = [];
20579         for(var whole = valueParts[0]; whole;){
20580                 var off = whole.length - groupSize;
20581                 pieces.push((off > 0) ? whole.substr(off) : whole);
20582                 whole = (off > 0) ? whole.slice(0, off) : "";
20583                 if(groupSize2){
20584                         groupSize = groupSize2;
20585                         delete groupSize2;
20586                 }
20587         }
20588         valueParts[0] = pieces.reverse().join(options.group || ",");
20589
20590         return valueParts.join(options.decimal || ".");
20591 };
20592
20593 /*=====
20594 dojo.number.__RegexpOptions = function(){
20595         //      pattern: String?
20596         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
20597         //              with this string.  Default value is based on locale.  Overriding this property will defeat
20598         //              localization.
20599         //      type: String?
20600         //              choose a format type based on the locale from the following:
20601         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
20602         //      locale: String?
20603         //              override the locale used to determine formatting rules
20604         //      strict: Boolean?
20605         //              strict parsing, false by default.  Strict parsing requires input as produced by the format() method.
20606         //              Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
20607         //      places: Number|String?
20608         //              number of decimal places to accept: Infinity, a positive number, or
20609         //              a range "n,m".  Defined by pattern or Infinity if pattern not provided.
20610         this.pattern = pattern;
20611         this.type = type;
20612         this.locale = locale;
20613         this.strict = strict;
20614         this.places = places;
20615 }
20616 =====*/
20617 dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
20618         //      summary:
20619         //              Builds the regular needed to parse a number
20620         //      description:
20621         //              Returns regular expression with positive and negative match, group
20622         //              and decimal separators
20623         return dojo.number._parseInfo(options).regexp; // String
20624 }
20625
20626 dojo.number._parseInfo = function(/*Object?*/options){
20627         options = options || {};
20628         var locale = dojo.i18n.normalizeLocale(options.locale),
20629                 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
20630                 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
20631 //TODO: memoize?
20632                 group = bundle.group,
20633                 decimal = bundle.decimal,
20634                 factor = 1;
20635
20636         if(pattern.indexOf('%') != -1){
20637                 factor /= 100;
20638         }else if(pattern.indexOf('\u2030') != -1){
20639                 factor /= 1000; // per mille
20640         }else{
20641                 var isCurrency = pattern.indexOf('\u00a4') != -1;
20642                 if(isCurrency){
20643                         group = bundle.currencyGroup || group;
20644                         decimal = bundle.currencyDecimal || decimal;
20645                 }
20646         }
20647
20648         //TODO: handle quoted escapes
20649         var patternList = pattern.split(';');
20650         if(patternList.length == 1){
20651                 patternList.push("-" + patternList[0]);
20652         }
20653
20654         var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
20655                 pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
20656                 return pattern.replace(dojo.number._numberPatternRE, function(format){
20657                         var flags = {
20658                                 signed: false,
20659                                 separator: options.strict ? group : [group,""],
20660                                 fractional: options.fractional,
20661                                 decimal: decimal,
20662                                 exponent: false
20663                                 },
20664
20665                                 parts = format.split('.'),
20666                                 places = options.places;
20667
20668                         // special condition for percent (factor != 1)
20669                         // allow decimal places even if not specified in pattern
20670                         if(parts.length == 1 && factor != 1){
20671                             parts[1] = "###";
20672                         }
20673                         if(parts.length == 1 || places === 0){
20674                                 flags.fractional = false;
20675                         }else{
20676                                 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
20677                                 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
20678                                 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
20679                                 flags.places = places;
20680                         }
20681                         var groups = parts[0].split(',');
20682                         if(groups.length > 1){
20683                                 flags.groupSize = groups.pop().length;
20684                                 if(groups.length > 1){
20685                                         flags.groupSize2 = groups.pop().length;
20686                                 }
20687                         }
20688                         return "("+dojo.number._realNumberRegexp(flags)+")";
20689                 });
20690         }, true);
20691
20692         if(isCurrency){
20693                 // substitute the currency symbol for the placeholder in the pattern
20694                 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
20695                         var prop = ["symbol", "currency", "displayName"][target.length-1],
20696                                 symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
20697                         before = before ? "[\\s\\xa0]" : "";
20698                         after = after ? "[\\s\\xa0]" : "";
20699                         if(!options.strict){
20700                                 if(before){before += "*";}
20701                                 if(after){after += "*";}
20702                                 return "(?:"+before+symbol+after+")?";
20703                         }
20704                         return before+symbol+after;
20705                 });
20706         }
20707
20708 //TODO: substitute localized sign/percent/permille/etc.?
20709
20710         // normalize whitespace and return
20711         return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
20712 }
20713
20714 /*=====
20715 dojo.number.__ParseOptions = function(){
20716         //      pattern: String?
20717         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
20718         //              with this string.  Default value is based on locale.  Overriding this property will defeat
20719         //              localization.  Literal characters in patterns are not supported.
20720         //      type: String?
20721         //              choose a format type based on the locale from the following:
20722         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
20723         //      locale: String?
20724         //              override the locale used to determine formatting rules
20725         //      strict: Boolean?
20726         //              strict parsing, false by default.  Strict parsing requires input as produced by the format() method.
20727         //              Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
20728         //      fractional: Boolean?|Array?
20729         //              Whether to include the fractional portion, where the number of decimal places are implied by pattern
20730         //              or explicit 'places' parameter.  The value [true,false] makes the fractional portion optional.
20731         this.pattern = pattern;
20732         this.type = type;
20733         this.locale = locale;
20734         this.strict = strict;
20735         this.fractional = fractional;
20736 }
20737 =====*/
20738 dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
20739         // summary:
20740         //              Convert a properly formatted string to a primitive Number, using
20741         //              locale-specific settings.
20742         // description:
20743         //              Create a Number from a string using a known localized pattern.
20744         //              Formatting patterns are chosen appropriate to the locale
20745         //              and follow the syntax described by
20746         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
20747         //              Note that literal characters in patterns are not supported.
20748         // expression:
20749         //              A string representation of a Number
20750         var info = dojo.number._parseInfo(options),
20751                 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
20752         if(!results){
20753                 return NaN; //NaN
20754         }
20755         var absoluteMatch = results[1]; // match for the positive expression
20756         if(!results[1]){
20757                 if(!results[2]){
20758                         return NaN; //NaN
20759                 }
20760                 // matched the negative pattern
20761                 absoluteMatch =results[2];
20762                 info.factor *= -1;
20763         }
20764
20765         // Transform it to something Javascript can parse as a number.  Normalize
20766         // decimal point and strip out group separators or alternate forms of whitespace
20767         absoluteMatch = absoluteMatch.
20768                 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
20769                 replace(info.decimal, ".");
20770         // Adjust for negative sign, percent, etc. as necessary
20771         return absoluteMatch * info.factor; //Number
20772 };
20773
20774 /*=====
20775 dojo.number.__RealNumberRegexpFlags = function(){
20776         //      places: Number?
20777         //              The integer number of decimal places or a range given as "n,m".  If
20778         //              not given, the decimal part is optional and the number of places is
20779         //              unlimited.
20780         //      decimal: String?
20781         //              A string for the character used as the decimal point.  Default
20782         //              is ".".
20783         //      fractional: Boolean?|Array?
20784         //              Whether decimal places are used.  Can be true, false, or [true,
20785         //              false].  Default is [true, false] which means optional.
20786         //      exponent: Boolean?|Array?
20787         //              Express in exponential notation.  Can be true, false, or [true,
20788         //              false]. Default is [true, false], (i.e. will match if the
20789         //              exponential part is present are not).
20790         //      eSigned: Boolean?|Array?
20791         //              The leading plus-or-minus sign on the exponent.  Can be true,
20792         //              false, or [true, false].  Default is [true, false], (i.e. will
20793         //              match if it is signed or unsigned).  flags in regexp.integer can be
20794         //              applied.
20795         this.places = places;
20796         this.decimal = decimal;
20797         this.fractional = fractional;
20798         this.exponent = exponent;
20799         this.eSigned = eSigned;
20800 }
20801 =====*/
20802
20803 dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
20804         // summary:
20805         //              Builds a regular expression to match a real number in exponential
20806         //              notation
20807
20808         // assign default values to missing parameters
20809         flags = flags || {};
20810         //TODO: use mixin instead?
20811         if(!("places" in flags)){ flags.places = Infinity; }
20812         if(typeof flags.decimal != "string"){ flags.decimal = "."; }
20813         if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
20814         if(!("exponent" in flags)){ flags.exponent = [true, false]; }
20815         if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
20816
20817         var integerRE = dojo.number._integerRegexp(flags),
20818                 decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
20819                 function(q){
20820                         var re = "";
20821                         if(q && (flags.places!==0)){
20822                                 re = "\\" + flags.decimal;
20823                                 if(flags.places == Infinity){ 
20824                                         re = "(?:" + re + "\\d+)?"; 
20825                                 }else{
20826                                         re += "\\d{" + flags.places + "}"; 
20827                                 }
20828                         }
20829                         return re;
20830                 },
20831                 true
20832         );
20833
20834         var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
20835                 function(q){ 
20836                         if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
20837                         return ""; 
20838                 }
20839         );
20840
20841         var realRE = integerRE + decimalRE;
20842         // allow for decimals without integers, e.g. .25
20843         if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
20844         return realRE + exponentRE; // String
20845 };
20846
20847 /*=====
20848 dojo.number.__IntegerRegexpFlags = function(){
20849         //      signed: Boolean?
20850         //              The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
20851         //              Default is `[true, false]`, (i.e. will match if it is signed
20852         //              or unsigned).
20853         //      separator: String?
20854         //              The character used as the thousands separator. Default is no
20855         //              separator. For more than one symbol use an array, e.g. `[",", ""]`,
20856         //              makes ',' optional.
20857         //      groupSize: Number?
20858         //              group size between separators
20859         //      groupSize2: Number?
20860         //              second grouping, where separators 2..n have a different interval than the first separator (for India)
20861         this.signed = signed;
20862         this.separator = separator;
20863         this.groupSize = groupSize;
20864         this.groupSize2 = groupSize2;
20865 }
20866 =====*/
20867
20868 dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
20869         // summary: 
20870         //              Builds a regular expression that matches an integer
20871
20872         // assign default values to missing parameters
20873         flags = flags || {};
20874         if(!("signed" in flags)){ flags.signed = [true, false]; }
20875         if(!("separator" in flags)){
20876                 flags.separator = "";
20877         }else if(!("groupSize" in flags)){
20878                 flags.groupSize = 3;
20879         }
20880
20881         var signRE = dojo.regexp.buildGroupRE(flags.signed,
20882                 function(q){ return q ? "[-+]" : ""; },
20883                 true
20884         );
20885
20886         var numberRE = dojo.regexp.buildGroupRE(flags.separator,
20887                 function(sep){
20888                         if(!sep){
20889                                 return "(?:\\d+)";
20890                         }
20891
20892                         sep = dojo.regexp.escapeString(sep);
20893                         if(sep == " "){ sep = "\\s"; }
20894                         else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
20895
20896                         var grp = flags.groupSize, grp2 = flags.groupSize2;
20897                         //TODO: should we continue to enforce that numbers with separators begin with 1-9?  See #6933
20898                         if(grp2){
20899                                 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
20900                                 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
20901                         }
20902                         return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
20903                 },
20904                 true
20905         );
20906
20907         return signRE + numberRE; // String
20908 }
20909
20910 }
20911
20912 if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20913 dojo._hasResource["dijit.ProgressBar"] = true;
20914 dojo.provide("dijit.ProgressBar");
20915
20916
20917
20918
20919
20920
20921
20922 dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
20923         // summary:
20924         //              A progress indication widget, showing the amount completed
20925         //              (often the percentage completed) of a task.
20926         //
20927         // example:
20928         // |    <div dojoType="ProgressBar"
20929         // |             places="0"
20930         // |             progress="..." maximum="...">
20931         // |    </div>
20932         //
20933         // description:
20934         //              Note that the progress bar is updated via (a non-standard)
20935         //              update() method, rather than via attr() like other widgets.
20936
20937         // progress: [const] String (Percentage or Number)
20938         //              Number or percentage indicating amount of task completed.
20939         //              With "%": percentage value, 0% <= progress <= 100%, or
20940         //              without "%": absolute value, 0 <= progress <= maximum
20941         // TODO: rename to value for 2.0
20942         progress: "0",
20943
20944         // maximum: [const] Float
20945         //              Max sample number
20946         maximum: 100,
20947
20948         // places: [const] Number
20949         //              Number of places to show in values; 0 by default
20950         places: 0,
20951
20952         // indeterminate: [const] Boolean
20953         //              If false: show progress value (number or percentage).
20954         //              If true: show that a process is underway but that the amount completed is unknown.
20955         indeterminate: false,
20956
20957         // name: String
20958         //              this is the field name (for a form) if set. This needs to be set if you want to use
20959         //              this widget in a dijit.form.Form widget (such as dijit.Dialog)
20960         name: '',
20961
20962         templateString: dojo.cache("dijit", "templates/ProgressBar.html", "<div class=\"dijitProgressBar dijitProgressBarEmpty\"\n\t><div waiRole=\"progressbar\" 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=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"),
20963
20964         // _indeterminateHighContrastImagePath: [private] dojo._URL
20965         //              URL to image to use for indeterminate progress bar when display is in high contrast mode
20966         _indeterminateHighContrastImagePath:
20967                 dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
20968
20969         // public functions
20970         postCreate: function(){
20971                 this.inherited(arguments);
20972                 this.indeterminateHighContrastImage.setAttribute("src",
20973                         this._indeterminateHighContrastImagePath.toString());
20974                 this.update();
20975         },
20976
20977         update: function(/*Object?*/attributes){
20978                 // summary:
20979                 //              Change attributes of ProgressBar, similar to attr(hash).
20980                 //
20981                 // attributes:
20982                 //              May provide progress and/or maximum properties on this parameter;
20983                 //              see attribute specs for details.
20984                 //
20985                 // example:
20986                 //      |       myProgressBar.update({'indeterminate': true});
20987                 //      |       myProgressBar.update({'progress': 80});
20988
20989                 // TODO: deprecate this method and use set() instead
20990
20991                 dojo.mixin(this, attributes || {});
20992                 var tip = this.internalProgress;
20993                 var percent = 1, classFunc;
20994                 if(this.indeterminate){
20995                         classFunc = "addClass";
20996                         dijit.removeWaiState(tip, "valuenow");
20997                         dijit.removeWaiState(tip, "valuemin");
20998                         dijit.removeWaiState(tip, "valuemax");
20999                 }else{
21000                         classFunc = "removeClass";
21001                         if(String(this.progress).indexOf("%") != -1){
21002                                 percent = Math.min(parseFloat(this.progress)/100, 1);
21003                                 this.progress = percent * this.maximum;
21004                         }else{
21005                                 this.progress = Math.min(this.progress, this.maximum);
21006                                 percent = this.progress / this.maximum;
21007                         }
21008                         var text = this.report(percent);
21009                         this.label.firstChild.nodeValue = text;
21010                         dijit.setWaiState(tip, "describedby", this.label.id);
21011                         dijit.setWaiState(tip, "valuenow", this.progress);
21012                         dijit.setWaiState(tip, "valuemin", 0);
21013                         dijit.setWaiState(tip, "valuemax", this.maximum);
21014                 }
21015                 dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate");
21016                 tip.style.width = (percent * 100) + "%";
21017                 this.onChange();
21018         },
21019
21020         _setValueAttr: function(v){
21021                 if(v == Infinity){
21022                         this.update({indeterminate:true});
21023                 }else{
21024                         this.update({indeterminate:false, progress:v});
21025                 }
21026         },
21027
21028         _getValueAttr: function(){
21029                 return this.progress;
21030         },
21031
21032         report: function(/*float*/percent){
21033                 // summary:
21034                 //              Generates message to show inside progress bar (normally indicating amount of task completed).
21035                 //              May be overridden.
21036                 // tags:
21037                 //              extension
21038
21039                 return dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang });
21040         },
21041
21042         onChange: function(){
21043                 // summary:
21044                 //              Callback fired when progress updates.
21045                 // tags:
21046                 //              progress
21047         }
21048 });
21049
21050 }
21051
21052 if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21053 dojo._hasResource["dijit.ToolbarSeparator"] = true;
21054 dojo.provide("dijit.ToolbarSeparator");
21055
21056
21057
21058
21059 dojo.declare("dijit.ToolbarSeparator",
21060                 [ dijit._Widget, dijit._Templated ],
21061                 {
21062                 // summary:
21063                 //              A spacer between two `dijit.Toolbar` items
21064                 templateString: '<div class="dijitToolbarSeparator dijitInline" waiRole="presentation"></div>',
21065                 postCreate: function(){ dojo.setSelectable(this.domNode, false); },
21066                 isFocusable: function(){
21067                         // summary:
21068                         //              This widget isn't focusable, so pass along that fact.
21069                         // tags:
21070                         //              protected
21071                         return false;
21072                 }
21073
21074         });
21075
21076
21077
21078 }
21079
21080 if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21081 dojo._hasResource["dijit.Toolbar"] = true;
21082 dojo.provide("dijit.Toolbar");
21083
21084
21085
21086
21087
21088 dojo.declare("dijit.Toolbar",
21089         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
21090         {
21091         // summary:
21092         //              A Toolbar widget, used to hold things like `dijit.Editor` buttons
21093
21094         templateString:
21095                 '<div class="dijit" waiRole="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
21096                 //      '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
21097                 //              '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
21098                 //      '</table>' +
21099                 '</div>',
21100
21101         baseClass: "dijitToolbar",
21102
21103         postCreate: function(){
21104                 this.connectKeyNavHandlers(
21105                         this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
21106                         this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
21107                 );
21108                 this.inherited(arguments);
21109         },
21110
21111         startup: function(){
21112                 if(this._started){ return; }
21113
21114                 this.startupKeyNavChildren();
21115
21116                 this.inherited(arguments);
21117         }
21118 }
21119 );
21120
21121 // For back-compat, remove for 2.0
21122
21123
21124 }
21125
21126 if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21127 dojo._hasResource["dojo.DeferredList"] = true;
21128 dojo.provide("dojo.DeferredList");
21129 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
21130         // summary:
21131         //              Provides event handling for a group of Deferred objects.
21132         // description:
21133         //              DeferredList takes an array of existing deferreds and returns a new deferred of its own
21134         //              this new deferred will typically have its callback fired when all of the deferreds in
21135         //              the given list have fired their own deferreds.  The parameters `fireOnOneCallback` and
21136         //              fireOnOneErrback, will fire before all the deferreds as appropriate
21137         //
21138         //      list:
21139         //              The list of deferreds to be synchronizied with this DeferredList
21140         //      fireOnOneCallback:
21141         //              Will cause the DeferredLists callback to be fired as soon as any
21142         //              of the deferreds in its list have been fired instead of waiting until
21143         //              the entire list has finished
21144         //      fireonOneErrback:
21145         //              Will cause the errback to fire upon any of the deferreds errback
21146         //      canceller:
21147         //              A deferred canceller function, see dojo.Deferred
21148         var resultList = [];
21149         dojo.Deferred.call(this);
21150         var self = this;
21151         if(list.length === 0 && !fireOnOneCallback){
21152                 this.resolve([0, []]);
21153         }
21154         var finished = 0;
21155         dojo.forEach(list, function(item, i){
21156                 item.then(function(result){
21157                         if(fireOnOneCallback){
21158                                 self.resolve([i, result]);
21159                         }else{
21160                                 addResult(true, result);
21161                         }
21162                 },function(error){
21163                         if(fireOnOneErrback){
21164                                 self.reject(error);
21165                         }else{
21166                                 addResult(false, error);
21167                         }
21168                         if(consumeErrors){
21169                                 return null;
21170                         }
21171                         throw error;
21172                 });
21173                 function addResult(succeeded, result){
21174                         resultList[i] = [succeeded, result];
21175                         finished++;
21176                         if(finished === list.length){
21177                                 self.resolve(resultList);
21178                         }
21179                         
21180                 }
21181         });
21182 };
21183 dojo.DeferredList.prototype = new dojo.Deferred();
21184
21185 dojo.DeferredList.prototype.gatherResults= function(deferredList){
21186         // summary:     
21187         //      Gathers the results of the deferreds for packaging
21188         //      as the parameters to the Deferred Lists' callback
21189
21190         var d = new dojo.DeferredList(deferredList, false, true, false);
21191         d.addCallback(function(results){
21192                 var ret = [];
21193                 dojo.forEach(results, function(result){
21194                         ret.push(result[1]);
21195                 });
21196                 return ret;
21197         });
21198         return d;
21199 };
21200
21201 }
21202
21203 if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21204 dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
21205 dojo.provide("dijit.tree.TreeStoreModel");
21206
21207 dojo.declare(
21208                 "dijit.tree.TreeStoreModel",
21209                 null,
21210         {
21211                 // summary:
21212                 //              Implements dijit.Tree.model connecting to a store with a single
21213                 //              root item.  Any methods passed into the constructor will override
21214                 //              the ones defined here.
21215
21216                 // store: dojo.data.Store
21217                 //              Underlying store
21218                 store: null,
21219
21220                 // childrenAttrs: String[]
21221                 //              One or more attribute names (attributes in the dojo.data item) that specify that item's children
21222                 childrenAttrs: ["children"],
21223
21224                 // newItemIdAttr: String
21225                 //              Name of attribute in the Object passed to newItem() that specifies the id.
21226                 //
21227                 //              If newItemIdAttr is set then it's used when newItem() is called to see if an
21228                 //              item with the same id already exists, and if so just links to the old item
21229                 //              (so that the old item ends up with two parents).
21230                 //
21231                 //              Setting this to null or "" will make every drop create a new item.
21232                 newItemIdAttr: "id",
21233
21234                 // labelAttr: String
21235                 //              If specified, get label for tree node from this attribute, rather
21236                 //              than by calling store.getLabel()
21237                 labelAttr: "",
21238
21239                 // root: [readonly] dojo.data.Item
21240                 //              Pointer to the root item (read only, not a parameter)
21241                 root: null,
21242
21243                 // query: anything
21244                 //              Specifies datastore query to return the root item for the tree.
21245                 //              Must only return a single item.   Alternately can just pass in pointer
21246                 //              to root item.
21247                 // example:
21248                 //      |       {id:'ROOT'}
21249                 query: null,
21250
21251                 // deferItemLoadingUntilExpand: Boolean
21252                 //              Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
21253                 //              until they are expanded. This allows for lazying loading where only one
21254                 //              loadItem (and generally one network call, consequently) per expansion
21255                 //              (rather than one for each child).
21256                 //              This relies on partial loading of the children items; each children item of a
21257                 //              fully loaded item should contain the label and info about having children.
21258                 deferItemLoadingUntilExpand: false,
21259
21260                 constructor: function(/* Object */ args){
21261                         // summary:
21262                         //              Passed the arguments listed above (store, etc)
21263                         // tags:
21264                         //              private
21265
21266                         dojo.mixin(this, args);
21267
21268                         this.connects = [];
21269
21270                         var store = this.store;
21271                         if(!store.getFeatures()['dojo.data.api.Identity']){
21272                                 throw new Error("dijit.Tree: store must support dojo.data.Identity");
21273                         }
21274
21275                         // if the store supports Notification, subscribe to the notification events
21276                         if(store.getFeatures()['dojo.data.api.Notification']){
21277                                 this.connects = this.connects.concat([
21278                                         dojo.connect(store, "onNew", this, "onNewItem"),
21279                                         dojo.connect(store, "onDelete", this, "onDeleteItem"),
21280                                         dojo.connect(store, "onSet", this, "onSetItem")
21281                                 ]);
21282                         }
21283                 },
21284
21285                 destroy: function(){
21286                         dojo.forEach(this.connects, dojo.disconnect);
21287                         // TODO: should cancel any in-progress processing of getRoot(), getChildren()
21288                 },
21289
21290                 // =======================================================================
21291                 // Methods for traversing hierarchy
21292
21293                 getRoot: function(onItem, onError){
21294                         // summary:
21295                         //              Calls onItem with the root item for the tree, possibly a fabricated item.
21296                         //              Calls onError on error.
21297                         if(this.root){
21298                                 onItem(this.root);
21299                         }else{
21300                                 this.store.fetch({
21301                                         query: this.query,
21302                                         onComplete: dojo.hitch(this, function(items){
21303                                                 if(items.length != 1){
21304                                                         throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
21305                                                                 " items, but must return exactly one item");
21306                                                 }
21307                                                 this.root = items[0];
21308                                                 onItem(this.root);
21309                                         }),
21310                                         onError: onError
21311                                 });
21312                         }
21313                 },
21314
21315                 mayHaveChildren: function(/*dojo.data.Item*/ item){
21316                         // summary:
21317                         //              Tells if an item has or may have children.  Implementing logic here
21318                         //              avoids showing +/- expando icon for nodes that we know don't have children.
21319                         //              (For efficiency reasons we may not want to check if an element actually
21320                         //              has children until user clicks the expando node)
21321                         return dojo.some(this.childrenAttrs, function(attr){
21322                                 return this.store.hasAttribute(item, attr);
21323                         }, this);
21324                 },
21325
21326                 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
21327                         // summary:
21328                         //              Calls onComplete() with array of child items of given parent item, all loaded.
21329
21330                         var store = this.store;
21331                         if(!store.isItemLoaded(parentItem)){
21332                                 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
21333                                 // mode, so we will load it and just return the children (without loading each
21334                                 // child item)
21335                                 var getChildren = dojo.hitch(this, arguments.callee);
21336                                 store.loadItem({
21337                                         item: parentItem,
21338                                         onItem: function(parentItem){
21339                                                 getChildren(parentItem, onComplete, onError);
21340                                         },
21341                                         onError: onError
21342                                 });
21343                                 return;
21344                         }
21345                         // get children of specified item
21346                         var childItems = [];
21347                         for(var i=0; i<this.childrenAttrs.length; i++){
21348                                 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
21349                                 childItems = childItems.concat(vals);
21350                         }
21351
21352                         // count how many items need to be loaded
21353                         var _waitCount = 0;
21354                         if(!this.deferItemLoadingUntilExpand){
21355                                 dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
21356                         }
21357
21358                         if(_waitCount == 0){
21359                                 // all items are already loaded (or we aren't loading them).  proceed...
21360                                 onComplete(childItems);
21361                         }else{
21362                                 // still waiting for some or all of the items to load
21363                                 dojo.forEach(childItems, function(item, idx){
21364                                         if(!store.isItemLoaded(item)){
21365                                                 store.loadItem({
21366                                                         item: item,
21367                                                         onItem: function(item){
21368                                                                 childItems[idx] = item;
21369                                                                 if(--_waitCount == 0){
21370                                                                         // all nodes have been loaded, send them to the tree
21371                                                                         onComplete(childItems);
21372                                                                 }
21373                                                         },
21374                                                         onError: onError
21375                                                 });
21376                                         }
21377                                 });
21378                         }
21379                 },
21380
21381                 // =======================================================================
21382                 // Inspecting items
21383
21384                 isItem: function(/* anything */ something){
21385                         return this.store.isItem(something);    // Boolean
21386                 },
21387
21388                 fetchItemByIdentity: function(/* object */ keywordArgs){
21389                         this.store.fetchItemByIdentity(keywordArgs);
21390                 },
21391
21392                 getIdentity: function(/* item */ item){
21393                         return this.store.getIdentity(item);    // Object
21394                 },
21395
21396                 getLabel: function(/*dojo.data.Item*/ item){
21397                         // summary:
21398                         //              Get the label for an item
21399                         if(this.labelAttr){
21400                                 return this.store.getValue(item,this.labelAttr);        // String
21401                         }else{
21402                                 return this.store.getLabel(item);       // String
21403                         }
21404                 },
21405
21406                 // =======================================================================
21407                 // Write interface
21408
21409                 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
21410                         // summary:
21411                         //              Creates a new item.   See `dojo.data.api.Write` for details on args.
21412                         //              Used in drag & drop when item from external source dropped onto tree.
21413                         // description:
21414                         //              Developers will need to override this method if new items get added
21415                         //              to parents with multiple children attributes, in order to define which
21416                         //              children attribute points to the new item.
21417
21418                         var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex};
21419
21420                         if(this.newItemIdAttr && args[this.newItemIdAttr]){
21421                                 // Maybe there's already a corresponding item in the store; if so, reuse it.
21422                                 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
21423                                         if(item){
21424                                                 // There's already a matching item in store, use it
21425                                                 this.pasteItem(item, null, parent, true, insertIndex);
21426                                         }else{
21427                                                 // Create new item in the tree, based on the drag source.
21428                                                 this.store.newItem(args, pInfo);
21429                                         }
21430                                 }});
21431                         }else{
21432                                 // [as far as we know] there is no id so we must assume this is a new item
21433                                 this.store.newItem(args, pInfo);
21434                         }
21435                 },
21436
21437                 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
21438                         // summary:
21439                         //              Move or copy an item from one parent item to another.
21440                         //              Used in drag & drop
21441                         var store = this.store,
21442                                 parentAttr = this.childrenAttrs[0];     // name of "children" attr in parent item
21443
21444                         // remove child from source item, and record the attribute that child occurred in
21445                         if(oldParentItem){
21446                                 dojo.forEach(this.childrenAttrs, function(attr){
21447                                         if(store.containsValue(oldParentItem, attr, childItem)){
21448                                                 if(!bCopy){
21449                                                         var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
21450                                                                 return x != childItem;
21451                                                         });
21452                                                         store.setValues(oldParentItem, attr, values);
21453                                                 }
21454                                                 parentAttr = attr;
21455                                         }
21456                                 });
21457                         }
21458
21459                         // modify target item's children attribute to include this item
21460                         if(newParentItem){
21461                                 if(typeof insertIndex == "number"){
21462                                         // call slice() to avoid modifying the original array, confusing the data store
21463                                         var childItems = store.getValues(newParentItem, parentAttr).slice();
21464                                         childItems.splice(insertIndex, 0, childItem);
21465                                         store.setValues(newParentItem, parentAttr, childItems);
21466                                 }else{
21467                                         store.setValues(newParentItem, parentAttr,
21468                                                 store.getValues(newParentItem, parentAttr).concat(childItem));
21469                                 }
21470                         }
21471                 },
21472
21473                 // =======================================================================
21474                 // Callbacks
21475
21476                 onChange: function(/*dojo.data.Item*/ item){
21477                         // summary:
21478                         //              Callback whenever an item has changed, so that Tree
21479                         //              can update the label, icon, etc.   Note that changes
21480                         //              to an item's children or parent(s) will trigger an
21481                         //              onChildrenChange() so you can ignore those changes here.
21482                         // tags:
21483                         //              callback
21484                 },
21485
21486                 onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
21487                         // summary:
21488                         //              Callback to do notifications about new, updated, or deleted items.
21489                         // tags:
21490                         //              callback
21491                 },
21492
21493                 onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
21494                         // summary:
21495                         //              Callback when an item has been deleted.
21496                         // description:
21497                         //              Note that there will also be an onChildrenChange() callback for the parent
21498                         //              of this item.
21499                         // tags:
21500                         //              callback
21501                 },
21502
21503                 // =======================================================================
21504                 // Events from data store
21505
21506                 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
21507                         // summary:
21508                         //              Handler for when new items appear in the store, either from a drop operation
21509                         //              or some other way.   Updates the tree view (if necessary).
21510                         // description:
21511                         //              If the new item is a child of an existing item,
21512                         //              calls onChildrenChange() with the new list of children
21513                         //              for that existing item.
21514                         //
21515                         // tags:
21516                         //              extension
21517
21518                         // We only care about the new item if it has a parent that corresponds to a TreeNode
21519                         // we are currently displaying
21520                         if(!parentInfo){
21521                                 return;
21522                         }
21523
21524                         // Call onChildrenChange() on parent (ie, existing) item with new list of children
21525                         // In the common case, the new list of children is simply parentInfo.newValue or
21526                         // [ parentInfo.newValue ], although if items in the store has multiple
21527                         // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
21528                         // so call getChildren() to be sure to get right answer.
21529                         this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
21530                                 this.onChildrenChange(parentInfo.item, children);
21531                         }));
21532                 },
21533
21534                 onDeleteItem: function(/*Object*/ item){
21535                         // summary:
21536                         //              Handler for delete notifications from underlying store
21537                         this.onDelete(item);
21538                 },
21539
21540                 onSetItem: function(/* item */ item,
21541                                                 /* attribute-name-string */ attribute,
21542                                                 /* object | array */ oldValue,
21543                                                 /* object | array */ newValue){
21544                         // summary:
21545                         //              Updates the tree view according to changes in the data store.
21546                         // description:
21547                         //              Handles updates to an item's children by calling onChildrenChange(), and
21548                         //              other updates to an item by calling onChange().
21549                         //
21550                         //              See `onNewItem` for more details on handling updates to an item's children.
21551                         // tags:
21552                         //              extension
21553
21554                         if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
21555                                 // item's children list changed
21556                                 this.getChildren(item, dojo.hitch(this, function(children){
21557                                         // See comments in onNewItem() about calling getChildren()
21558                                         this.onChildrenChange(item, children);
21559                                 }));
21560                         }else{
21561                                 // item's label/icon/etc. changed.
21562                                 this.onChange(item);
21563                         }
21564                 }
21565         });
21566
21567
21568
21569 }
21570
21571 if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21572 dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
21573 dojo.provide("dijit.tree.ForestStoreModel");
21574
21575
21576
21577 dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
21578         // summary:
21579         //              Interface between Tree and a dojo.store that doesn't have a root item,
21580         //              i.e. has multiple "top level" items.
21581         //
21582         // description
21583         //              Use this class to wrap a dojo.store, making all the items matching the specified query
21584         //              appear as children of a fabricated "root item".  If no query is specified then all the
21585         //              items returned by fetch() on the underlying store become children of the root item.
21586         //              It allows dijit.Tree to assume a single root item, even if the store doesn't have one.
21587
21588         // Parameters to constructor
21589
21590         // rootId: String
21591         //              ID of fabricated root item
21592         rootId: "$root$",
21593
21594         // rootLabel: String
21595         //              Label of fabricated root item
21596         rootLabel: "ROOT",
21597
21598         // query: String
21599         //              Specifies the set of children of the root item.
21600         // example:
21601         //      |       {type:'continent'}
21602         query: null,
21603
21604         // End of parameters to constructor
21605
21606         constructor: function(params){
21607                 // summary:
21608                 //              Sets up variables, etc.
21609                 // tags:
21610                 //              private
21611
21612                 // Make dummy root item
21613                 this.root = {
21614                         store: this,
21615                         root: true,
21616                         id: params.rootId,
21617                         label: params.rootLabel,
21618                         children: params.rootChildren   // optional param
21619                 };
21620         },
21621
21622         // =======================================================================
21623         // Methods for traversing hierarchy
21624
21625         mayHaveChildren: function(/*dojo.data.Item*/ item){
21626                 // summary:
21627                 //              Tells if an item has or may have children.  Implementing logic here
21628                 //              avoids showing +/- expando icon for nodes that we know don't have children.
21629                 //              (For efficiency reasons we may not want to check if an element actually
21630                 //              has children until user clicks the expando node)
21631                 // tags:
21632                 //              extension
21633                 return item === this.root || this.inherited(arguments);
21634         },
21635
21636         getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
21637                 // summary:
21638                 //              Calls onComplete() with array of child items of given parent item, all loaded.
21639                 if(parentItem === this.root){
21640                         if(this.root.children){
21641                                 // already loaded, just return
21642                                 callback(this.root.children);
21643                         }else{
21644                                 this.store.fetch({
21645                                         query: this.query,
21646                                         onComplete: dojo.hitch(this, function(items){
21647                                                 this.root.children = items;
21648                                                 callback(items);
21649                                         }),
21650                                         onError: onError
21651                                 });
21652                         }
21653                 }else{
21654                         this.inherited(arguments);
21655                 }
21656         },
21657
21658         // =======================================================================
21659         // Inspecting items
21660
21661         isItem: function(/* anything */ something){
21662                 return (something === this.root) ? true : this.inherited(arguments);
21663         },
21664
21665         fetchItemByIdentity: function(/* object */ keywordArgs){
21666                 if(keywordArgs.identity == this.root.id){
21667                         var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
21668                         if(keywordArgs.onItem){
21669                                 keywordArgs.onItem.call(scope, this.root);
21670                         }
21671                 }else{
21672                         this.inherited(arguments);
21673                 }
21674         },
21675
21676         getIdentity: function(/* item */ item){
21677                 return (item === this.root) ? this.root.id : this.inherited(arguments);
21678         },
21679
21680         getLabel: function(/* item */ item){
21681                 return  (item === this.root) ? this.root.label : this.inherited(arguments);
21682         },
21683
21684         // =======================================================================
21685         // Write interface
21686
21687         newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
21688                 // summary:
21689                 //              Creates a new item.   See dojo.data.api.Write for details on args.
21690                 //              Used in drag & drop when item from external source dropped onto tree.
21691                 if(parent === this.root){
21692                         this.onNewRootItem(args);
21693                         return this.store.newItem(args);
21694                 }else{
21695                         return this.inherited(arguments);
21696                 }
21697         },
21698
21699         onNewRootItem: function(args){
21700                 // summary:
21701                 //              User can override this method to modify a new element that's being
21702                 //              added to the root of the tree, for example to add a flag like root=true
21703         },
21704
21705         pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
21706                 // summary:
21707                 //              Move or copy an item from one parent item to another.
21708                 //              Used in drag & drop
21709                 if(oldParentItem === this.root){
21710                         if(!bCopy){
21711                                 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
21712                                 // this.query... thus triggering an onChildrenChange() event to notify the Tree
21713                                 // that this element is no longer a child of the root node
21714                                 this.onLeaveRoot(childItem);
21715                         }
21716                 }
21717                 dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
21718                         oldParentItem === this.root ? null : oldParentItem,
21719                         newParentItem === this.root ? null : newParentItem,
21720                         bCopy,
21721                         insertIndex
21722                 );
21723                 if(newParentItem === this.root){
21724                         // It's onAddToRoot()'s responsibility to modify the item so it matches
21725                         // this.query... thus triggering an onChildrenChange() event to notify the Tree
21726                         // that this element is now a child of the root node
21727                         this.onAddToRoot(childItem);
21728                 }
21729         },
21730
21731         // =======================================================================
21732         // Handling for top level children
21733
21734         onAddToRoot: function(/* item */ item){
21735                 // summary:
21736                 //              Called when item added to root of tree; user must override this method
21737                 //              to modify the item so that it matches the query for top level items
21738                 // example:
21739                 //      |       store.setValue(item, "root", true);
21740                 // tags:
21741                 //              extension
21742                 console.log(this, ": item ", item, " added to root");
21743         },
21744
21745         onLeaveRoot: function(/* item */ item){
21746                 // summary:
21747                 //              Called when item removed from root of tree; user must override this method
21748                 //              to modify the item so it doesn't match the query for top level items
21749                 // example:
21750                 //      |       store.unsetAttribute(item, "root");
21751                 // tags:
21752                 //              extension
21753                 console.log(this, ": item ", item, " removed from root");
21754         },
21755
21756         // =======================================================================
21757         // Events from data store
21758
21759         _requeryTop: function(){
21760                 // reruns the query for the children of the root node,
21761                 // sending out an onSet notification if those children have changed
21762                 var oldChildren = this.root.children || [];
21763                 this.store.fetch({
21764                         query: this.query,
21765                         onComplete: dojo.hitch(this, function(newChildren){
21766                                 this.root.children = newChildren;
21767
21768                                 // If the list of children or the order of children has changed...
21769                                 if(oldChildren.length != newChildren.length ||
21770                                         dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
21771                                         this.onChildrenChange(this.root, newChildren);
21772                                 }
21773                         })
21774                 });
21775         },
21776
21777         onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
21778                 // summary:
21779                 //              Handler for when new items appear in the store.  Developers should override this
21780                 //              method to be more efficient based on their app/data.
21781                 // description:
21782                 //              Note that the default implementation requeries the top level items every time
21783                 //              a new item is created, since any new item could be a top level item (even in
21784                 //              addition to being a child of another item, since items can have multiple parents).
21785                 //
21786                 //              Developers can override this function to do something more efficient if they can
21787                 //              detect which items are possible top level items (based on the item and the
21788                 //              parentInfo parameters).  Often all top level items have parentInfo==null, but
21789                 //              that will depend on which store you use and what your data is like.
21790                 // tags:
21791                 //              extension
21792                 this._requeryTop();
21793
21794                 this.inherited(arguments);
21795         },
21796
21797         onDeleteItem: function(/*Object*/ item){
21798                 // summary:
21799                 //              Handler for delete notifications from underlying store
21800
21801                 // check if this was a child of root, and if so send notification that root's children
21802                 // have changed
21803                 if(dojo.indexOf(this.root.children, item) != -1){
21804                         this._requeryTop();
21805                 }
21806
21807                 this.inherited(arguments);
21808         }
21809 });
21810
21811
21812
21813 }
21814
21815 if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21816 dojo._hasResource["dijit.Tree"] = true;
21817 dojo.provide("dijit.Tree");
21818
21819
21820
21821
21822
21823
21824
21825
21826
21827
21828
21829 dojo.declare(
21830         "dijit._TreeNode",
21831         [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
21832 {
21833         // summary:
21834         //              Single node within a tree.   This class is used internally
21835         //              by Tree and should not be accessed directly.
21836         // tags:
21837         //              private
21838
21839         // item: dojo.data.Item
21840         //              the dojo.data entry this tree represents
21841         item: null,
21842
21843         // isTreeNode: [protected] Boolean
21844         //              Indicates that this is a TreeNode.   Used by `dijit.Tree` only,
21845         //              should not be accessed directly.
21846         isTreeNode: true,
21847
21848         // label: String
21849         //              Text of this tree node
21850         label: "",
21851
21852         // isExpandable: [private] Boolean
21853         //              This node has children, so show the expando node (+ sign)
21854         isExpandable: null,
21855
21856         // isExpanded: [readonly] Boolean
21857         //              This node is currently expanded (ie, opened)
21858         isExpanded: false,
21859
21860         // state: [private] String
21861         //              Dynamic loading-related stuff.
21862         //              When an empty folder node appears, it is "UNCHECKED" first,
21863         //              then after dojo.data query it becomes "LOADING" and, finally "LOADED"
21864         state: "UNCHECKED",
21865
21866         templateString: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" waiRole=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" waiRole=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" waiRole=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" waiRole=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" waiRole=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" waiRole=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" wairole=\"treeitem\" tabindex=\"-1\" waiState=\"selected-false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" waiRole=\"presentation\" style=\"display: none;\"></div>\n</div>\n"),
21867
21868         baseClass: "dijitTreeNode",
21869
21870         // For hover effect for tree node, and focus effect for label
21871         cssStateNodes: {
21872                 rowNode: "dijitTreeRow",
21873                 labelNode: "dijitTreeLabel"
21874         },
21875
21876         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
21877                 label: {node: "labelNode", type: "innerText"},
21878                 tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
21879         }),
21880
21881         postCreate: function(){
21882                 this.inherited(arguments);
21883
21884                 // set expand icon for leaf
21885                 this._setExpando();
21886
21887                 // set icon and label class based on item
21888                 this._updateItemClasses(this.item);
21889
21890                 if(this.isExpandable){
21891                         dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
21892                 }
21893         },
21894
21895         _setIndentAttr: function(indent){
21896                 // summary:
21897                 //              Tell this node how many levels it should be indented
21898                 // description:
21899                 //              0 for top level nodes, 1 for their children, 2 for their
21900                 //              grandchildren, etc.
21901                 this.indent = indent;
21902
21903                 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
21904                 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
21905
21906                 dojo.style(this.domNode, "backgroundPosition",  pixels + " 0px");
21907                 dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
21908
21909                 dojo.forEach(this.getChildren(), function(child){
21910                         child.set("indent", indent+1);
21911                 });
21912         },
21913
21914         markProcessing: function(){
21915                 // summary:
21916                 //              Visually denote that tree is loading data, etc.
21917                 // tags:
21918                 //              private
21919                 this.state = "LOADING";
21920                 this._setExpando(true);
21921         },
21922
21923         unmarkProcessing: function(){
21924                 // summary:
21925                 //              Clear markup from markProcessing() call
21926                 // tags:
21927                 //              private
21928                 this._setExpando(false);
21929         },
21930
21931         _updateItemClasses: function(item){
21932                 // summary:
21933                 //              Set appropriate CSS classes for icon and label dom node
21934                 //              (used to allow for item updates to change respective CSS)
21935                 // tags:
21936                 //              private
21937                 var tree = this.tree, model = tree.model;
21938                 if(tree._v10Compat && item === model.root){
21939                         // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
21940                         item = null;
21941                 }
21942                 this._applyClassAndStyle(item, "icon", "Icon");
21943                 this._applyClassAndStyle(item, "label", "Label");
21944                 this._applyClassAndStyle(item, "row", "Row");
21945         },
21946
21947         _applyClassAndStyle: function(item, lower, upper){
21948                 // summary:
21949                 //              Set the appropriate CSS classes and styles for labels, icons and rows.
21950                 //
21951                 // item:
21952                 //              The data item.
21953                 //
21954                 // lower:
21955                 //              The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
21956                 //
21957                 // upper:
21958                 //              The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
21959                 //
21960                 // tags:
21961                 //              private
21962
21963                 var clsName = "_" + lower + "Class";
21964                 var nodeName = lower + "Node";
21965
21966                 if(this[clsName]){
21967                         dojo.removeClass(this[nodeName], this[clsName]);
21968                 }
21969                 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
21970                 if(this[clsName]){
21971                         dojo.addClass(this[nodeName], this[clsName]);
21972                 }
21973                 dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
21974         },
21975
21976         _updateLayout: function(){
21977                 // summary:
21978                 //              Set appropriate CSS classes for this.domNode
21979                 // tags:
21980                 //              private
21981                 var parent = this.getParent();
21982                 if(!parent || parent.rowNode.style.display == "none"){
21983                         /* if we are hiding the root node then make every first level child look like a root node */
21984                         dojo.addClass(this.domNode, "dijitTreeIsRoot");
21985                 }else{
21986                         dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
21987                 }
21988         },
21989
21990         _setExpando: function(/*Boolean*/ processing){
21991                 // summary:
21992                 //              Set the right image for the expando node
21993                 // tags:
21994                 //              private
21995
21996                 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
21997                                                 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
21998                         _a11yStates = ["*","-","+","*"],
21999                         idx = processing ? 0 : (this.isExpandable ?     (this.isExpanded ? 1 : 2) : 3);
22000
22001                 // apply the appropriate class to the expando node
22002                 dojo.removeClass(this.expandoNode, styles);
22003                 dojo.addClass(this.expandoNode, styles[idx]);
22004
22005                 // provide a non-image based indicator for images-off mode
22006                 this.expandoNodeText.innerHTML = _a11yStates[idx];
22007
22008         },
22009
22010         expand: function(){
22011                 // summary:
22012                 //              Show my children
22013                 // returns:
22014                 //              Deferred that fires when expansion is complete
22015
22016                 // If there's already an expand in progress or we are already expanded, just return
22017                 if(this._expandDeferred){
22018                         return this._expandDeferred;            // dojo.Deferred
22019                 }
22020
22021                 // cancel in progress collapse operation
22022                 this._wipeOut && this._wipeOut.stop();
22023
22024                 // All the state information for when a node is expanded, maybe this should be
22025                 // set when the animation completes instead
22026                 this.isExpanded = true;
22027                 dijit.setWaiState(this.labelNode, "expanded", "true");
22028                 dijit.setWaiRole(this.containerNode, "group");
22029                 dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
22030                 this._setExpando();
22031                 this._updateItemClasses(this.item);
22032                 if(this == this.tree.rootNode){
22033                         dijit.setWaiState(this.tree.domNode, "expanded", "true");
22034                 }
22035
22036                 var def,
22037                         wipeIn = dojo.fx.wipeIn({
22038                                 node: this.containerNode, duration: dijit.defaultDuration,
22039                                 onEnd: function(){
22040                                         def.callback(true);
22041                                 }
22042                         });
22043
22044                 // Deferred that fires when expand is complete
22045                 def = (this._expandDeferred = new dojo.Deferred(function(){
22046                         // Canceller
22047                         wipeIn.stop();
22048                 }));
22049
22050                 wipeIn.play();
22051
22052                 return def;             // dojo.Deferred
22053         },
22054
22055         collapse: function(){
22056                 // summary:
22057                 //              Collapse this node (if it's expanded)
22058
22059                 if(!this.isExpanded){ return; }
22060
22061                 // cancel in progress expand operation
22062                 if(this._expandDeferred){
22063                         this._expandDeferred.cancel();
22064                         delete this._expandDeferred;
22065                 }
22066
22067                 this.isExpanded = false;
22068                 dijit.setWaiState(this.labelNode, "expanded", "false");
22069                 if(this == this.tree.rootNode){
22070                         dijit.setWaiState(this.tree.domNode, "expanded", "false");
22071                 }
22072                 dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
22073                 this._setExpando();
22074                 this._updateItemClasses(this.item);
22075
22076                 if(!this._wipeOut){
22077                         this._wipeOut = dojo.fx.wipeOut({
22078                                 node: this.containerNode, duration: dijit.defaultDuration
22079                         });
22080                 }
22081                 this._wipeOut.play();
22082         },
22083
22084         // indent: Integer
22085         //              Levels from this node to the root node
22086         indent: 0,
22087
22088         setChildItems: function(/* Object[] */ items){
22089                 // summary:
22090                 //              Sets the child items of this node, removing/adding nodes
22091                 //              from current children to match specified items[] array.
22092                 //              Also, if this.persist == true, expands any children that were previously
22093                 //              opened.
22094                 // returns:
22095                 //              Deferred object that fires after all previously opened children
22096                 //              have been expanded again (or fires instantly if there are no such children).
22097
22098                 var tree = this.tree,
22099                         model = tree.model,
22100                         defs = [];      // list of deferreds that need to fire before I am complete
22101
22102
22103                 // Orphan all my existing children.
22104                 // If items contains some of the same items as before then we will reattach them.
22105                 // Don't call this.removeChild() because that will collapse the tree etc.
22106                 dojo.forEach(this.getChildren(), function(child){
22107                         dijit._Container.prototype.removeChild.call(this, child);
22108                 }, this);
22109
22110                 this.state = "LOADED";
22111
22112                 if(items && items.length > 0){
22113                         this.isExpandable = true;
22114
22115                         // Create _TreeNode widget for each specified tree node, unless one already
22116                         // exists and isn't being used (presumably it's from a DnD move and was recently
22117                         // released
22118                         dojo.forEach(items, function(item){
22119                                 var id = model.getIdentity(item),
22120                                         existingNodes = tree._itemNodesMap[id],
22121                                         node;
22122                                 if(existingNodes){
22123                                         for(var i=0;i<existingNodes.length;i++){
22124                                                 if(existingNodes[i] && !existingNodes[i].getParent()){
22125                                                         node = existingNodes[i];
22126                                                         node.set('indent', this.indent+1);
22127                                                         break;
22128                                                 }
22129                                         }
22130                                 }
22131                                 if(!node){
22132                                         node = this.tree._createTreeNode({
22133                                                         item: item,
22134                                                         tree: tree,
22135                                                         isExpandable: model.mayHaveChildren(item),
22136                                                         label: tree.getLabel(item),
22137                                                         tooltip: tree.getTooltip(item),
22138                                                         dir: tree.dir,
22139                                                         lang: tree.lang,
22140                                                         indent: this.indent + 1
22141                                                 });
22142                                         if(existingNodes){
22143                                                 existingNodes.push(node);
22144                                         }else{
22145                                                 tree._itemNodesMap[id] = [node];
22146                                         }
22147                                 }
22148                                 this.addChild(node);
22149
22150                                 // If node was previously opened then open it again now (this may trigger
22151                                 // more data store accesses, recursively)
22152                                 if(this.tree.autoExpand || this.tree._state(item)){
22153                                         defs.push(tree._expandNode(node));
22154                                 }
22155                         }, this);
22156
22157                         // note that updateLayout() needs to be called on each child after
22158                         // _all_ the children exist
22159                         dojo.forEach(this.getChildren(), function(child, idx){
22160                                 child._updateLayout();
22161                         });
22162                 }else{
22163                         this.isExpandable=false;
22164                 }
22165
22166                 if(this._setExpando){
22167                         // change expando to/from dot or + icon, as appropriate
22168                         this._setExpando(false);
22169                 }
22170
22171                 // Set leaf icon or folder icon, as appropriate
22172                 this._updateItemClasses(this.item);
22173
22174                 // On initial tree show, make the selected TreeNode as either the root node of the tree,
22175                 // or the first child, if the root node is hidden
22176                 if(this == tree.rootNode){
22177                         var fc = this.tree.showRoot ? this : this.getChildren()[0];
22178                         if(fc){
22179                                 fc.setFocusable(true);
22180                                 tree.lastFocused = fc;
22181                         }else{
22182                                 // fallback: no nodes in tree so focus on Tree <div> itself
22183                                 tree.domNode.setAttribute("tabIndex", "0");
22184                         }
22185                 }
22186
22187                 return new dojo.DeferredList(defs);     // dojo.Deferred
22188         },
22189
22190         removeChild: function(/* treeNode */ node){
22191                 this.inherited(arguments);
22192
22193                 var children = this.getChildren();
22194                 if(children.length == 0){
22195                         this.isExpandable = false;
22196                         this.collapse();
22197                 }
22198
22199                 dojo.forEach(children, function(child){
22200                                 child._updateLayout();
22201                 });
22202         },
22203
22204         makeExpandable: function(){
22205                 // summary:
22206                 //              if this node wasn't already showing the expando node,
22207                 //              turn it into one and call _setExpando()
22208
22209                 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
22210
22211                 this.isExpandable = true;
22212                 this._setExpando(false);
22213         },
22214
22215         _onLabelFocus: function(evt){
22216                 // summary:
22217                 //              Called when this row is focused (possibly programatically)
22218                 //              Note that we aren't using _onFocus() builtin to dijit
22219                 //              because it's called when focus is moved to a descendant TreeNode.
22220                 // tags:
22221                 //              private
22222                 this.tree._onNodeFocus(this);
22223         },
22224
22225         setSelected: function(/*Boolean*/ selected){
22226                 // summary:
22227                 //              A Tree has a (single) currently selected node.
22228                 //              Mark that this node is/isn't that currently selected node.
22229                 // description:
22230                 //              In particular, setting a node as selected involves setting tabIndex
22231                 //              so that when user tabs to the tree, focus will go to that node (only).
22232                 dijit.setWaiState(this.labelNode, "selected", selected);
22233                 dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
22234         },
22235
22236         setFocusable: function(/*Boolean*/ selected){
22237                 // summary:
22238                 //              A Tree has a (single) node that's focusable.
22239                 //              Mark that this node is/isn't that currently focsuable node.
22240                 // description:
22241                 //              In particular, setting a node as selected involves setting tabIndex
22242                 //              so that when user tabs to the tree, focus will go to that node (only).
22243
22244                 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
22245         },
22246
22247         _onClick: function(evt){
22248                 // summary:
22249                 //              Handler for onclick event on a node
22250                 // tags:
22251                 //              private
22252                 this.tree._onClick(this, evt);
22253         },
22254         _onDblClick: function(evt){
22255                 // summary:
22256                 //              Handler for ondblclick event on a node
22257                 // tags:
22258                 //              private
22259                 this.tree._onDblClick(this, evt);
22260         },
22261
22262         _onMouseEnter: function(evt){
22263                 // summary:
22264                 //              Handler for onmouseenter event on a node
22265                 // tags:
22266                 //              private
22267                 this.tree._onNodeMouseEnter(this, evt);
22268         },
22269
22270         _onMouseLeave: function(evt){
22271                 // summary:
22272                 //              Handler for onmouseenter event on a node
22273                 // tags:
22274                 //              private
22275                 this.tree._onNodeMouseLeave(this, evt);
22276         }
22277 });
22278
22279 dojo.declare(
22280         "dijit.Tree",
22281         [dijit._Widget, dijit._Templated],
22282 {
22283         // summary:
22284         //              This widget displays hierarchical data from a store.
22285
22286         // store: [deprecated] String||dojo.data.Store
22287         //              Deprecated.  Use "model" parameter instead.
22288         //              The store to get data to display in the tree.
22289         store: null,
22290
22291         // model: dijit.Tree.model
22292         //              Interface to read tree data, get notifications of changes to tree data,
22293         //              and for handling drop operations (i.e drag and drop onto the tree)
22294         model: null,
22295
22296         // query: [deprecated] anything
22297         //              Deprecated.  User should specify query to the model directly instead.
22298         //              Specifies datastore query to return the root item or top items for the tree.
22299         query: null,
22300
22301         // label: [deprecated] String
22302         //              Deprecated.  Use dijit.tree.ForestStoreModel directly instead.
22303         //              Used in conjunction with query parameter.
22304         //              If a query is specified (rather than a root node id), and a label is also specified,
22305         //              then a fake root node is created and displayed, with this label.
22306         label: "",
22307
22308         // showRoot: [const] Boolean
22309         //              Should the root node be displayed, or hidden?
22310         showRoot: true,
22311
22312         // childrenAttr: [deprecated] String[]
22313         //              Deprecated.   This information should be specified in the model.
22314         //              One ore more attributes that holds children of a tree node
22315         childrenAttr: ["children"],
22316
22317         // path: String[] or Item[]
22318         //              Full path from rootNode to selected node expressed as array of items or array of ids.
22319         //              Since setting the path may be asynchronous (because ofwaiting on dojo.data), set("path", ...)
22320         //              returns a Deferred to indicate when the set is complete.
22321         path: [],
22322
22323         // selectedItem: [readonly] Item
22324         //              The currently selected item in this tree.
22325         //              This property can only be set (via set('selectedItem', ...)) when that item is already
22326         //              visible in the tree.   (I.e. the tree has already been expanded to show that node.)
22327         //              Should generally use `path` attribute to set the selected item instead.
22328         selectedItem: null,
22329
22330         // openOnClick: Boolean
22331         //              If true, clicking a folder node's label will open it, rather than calling onClick()
22332         openOnClick: false,
22333
22334         // openOnDblClick: Boolean
22335         //              If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
22336         openOnDblClick: false,
22337
22338         templateString: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" waiRole=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"),
22339
22340         // persist: Boolean
22341         //              Enables/disables use of cookies for state saving.
22342         persist: true,
22343
22344         // autoExpand: Boolean
22345         //              Fully expand the tree on load.   Overrides `persist`
22346         autoExpand: false,
22347
22348         // dndController: [protected] String
22349         //              Class name to use as as the dnd controller.  Specifying this class enables DnD.
22350         //              Generally you should specify this as "dijit.tree.dndSource".
22351         dndController: null,
22352
22353         // parameters to pull off of the tree and pass on to the dndController as its params
22354         dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
22355
22356         //declare the above items so they can be pulled from the tree's markup
22357
22358         // onDndDrop: [protected] Function
22359         //              Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
22360         //              Generally this doesn't need to be set.
22361         onDndDrop: null,
22362
22363         /*=====
22364         itemCreator: function(nodes, target, source){
22365                 // summary:
22366                 //              Returns objects passed to `Tree.model.newItem()` based on DnD nodes
22367                 //              dropped onto the tree.   Developer must override this method to enable
22368                 //              dropping from external sources onto this Tree, unless the Tree.model's items
22369                 //              happen to look like {id: 123, name: "Apple" } with no other attributes.
22370                 // description:
22371                 //              For each node in nodes[], which came from source, create a hash of name/value
22372                 //              pairs to be passed to Tree.model.newItem().  Returns array of those hashes.
22373                 // nodes: DomNode[]
22374                 //              The DOMNodes dragged from the source container
22375                 // target: DomNode
22376                 //              The target TreeNode.rowNode
22377                 // source: dojo.dnd.Source
22378                 //              The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
22379                 // returns: Object[]
22380                 //              Array of name/value hashes for each new item to be added to the Tree, like:
22381                 // |    [
22382                 // |            { id: 123, label: "apple", foo: "bar" },
22383                 // |            { id: 456, label: "pear", zaz: "bam" }
22384                 // |    ]
22385                 // tags:
22386                 //              extension
22387                 return [{}];
22388         },
22389         =====*/
22390         itemCreator: null,
22391
22392         // onDndCancel: [protected] Function
22393         //              Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
22394         //              Generally this doesn't need to be set.
22395         onDndCancel: null,
22396
22397 /*=====
22398         checkAcceptance: function(source, nodes){
22399                 // summary:
22400                 //              Checks if the Tree itself can accept nodes from this source
22401                 // source: dijit.tree._dndSource
22402                 //              The source which provides items
22403                 // nodes: DOMNode[]
22404                 //              Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
22405                 //              source is a dijit.Tree.
22406                 // tags:
22407                 //              extension
22408                 return true;    // Boolean
22409         },
22410 =====*/
22411         checkAcceptance: null,
22412
22413 /*=====
22414         checkItemAcceptance: function(target, source, position){
22415                 // summary:
22416                 //              Stub function to be overridden if one wants to check for the ability to drop at the node/item level
22417                 // description:
22418                 //              In the base case, this is called to check if target can become a child of source.
22419                 //              When betweenThreshold is set, position="before" or "after" means that we
22420                 //              are asking if the source node can be dropped before/after the target node.
22421                 // target: DOMNode
22422                 //              The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
22423                 //              Use dijit.getEnclosingWidget(target) to get the TreeNode.
22424                 // source: dijit.tree.dndSource
22425                 //              The (set of) nodes we are dropping
22426                 // position: String
22427                 //              "over", "before", or "after"
22428                 // tags:
22429                 //              extension
22430                 return true;    // Boolean
22431         },
22432 =====*/
22433         checkItemAcceptance: null,
22434
22435         // dragThreshold: Integer
22436         //              Number of pixels mouse moves before it's considered the start of a drag operation
22437         dragThreshold: 5,
22438
22439         // betweenThreshold: Integer
22440         //              Set to a positive value to allow drag and drop "between" nodes.
22441         //
22442         //              If during DnD mouse is over a (target) node but less than betweenThreshold
22443         //              pixels from the bottom edge, dropping the the dragged node will make it
22444         //              the next sibling of the target node, rather than the child.
22445         //
22446         //              Similarly, if mouse is over a target node but less that betweenThreshold
22447         //              pixels from the top edge, dropping the dragged node will make it
22448         //              the target node's previous sibling rather than the target node's child.
22449         betweenThreshold: 0,
22450
22451         // _nodePixelIndent: Integer
22452         //              Number of pixels to indent tree nodes (relative to parent node).
22453         //              Default is 19 but can be overridden by setting CSS class dijitTreeIndent
22454         //              and calling resize() or startup() on tree after it's in the DOM.
22455         _nodePixelIndent: 19,
22456
22457         _publish: function(/*String*/ topicName, /*Object*/ message){
22458                 // summary:
22459                 //              Publish a message for this widget/topic
22460                 dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
22461         },
22462
22463         postMixInProperties: function(){
22464                 this.tree = this;
22465
22466                 if(this.autoExpand){
22467                         // There's little point in saving opened/closed state of nodes for a Tree
22468                         // that initially opens all it's nodes.
22469                         this.persist = false;
22470                 }
22471
22472                 this._itemNodesMap={};
22473
22474                 if(!this.cookieName){
22475                         this.cookieName = this.id + "SaveStateCookie";
22476                 }
22477
22478                 this._loadDeferred = new dojo.Deferred();
22479
22480                 this.inherited(arguments);
22481         },
22482
22483         postCreate: function(){
22484                 this._initState();
22485
22486                 // Create glue between store and Tree, if not specified directly by user
22487                 if(!this.model){
22488                         this._store2model();
22489                 }
22490
22491                 // monitor changes to items
22492                 this.connect(this.model, "onChange", "_onItemChange");
22493                 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
22494                 this.connect(this.model, "onDelete", "_onItemDelete");
22495
22496                 this._load();
22497
22498                 this.inherited(arguments);
22499
22500                 if(this.dndController){
22501                         if(dojo.isString(this.dndController)){
22502                                 this.dndController = dojo.getObject(this.dndController);
22503                         }
22504                         var params={};
22505                         for(var i=0; i<this.dndParams.length;i++){
22506                                 if(this[this.dndParams[i]]){
22507                                         params[this.dndParams[i]] = this[this.dndParams[i]];
22508                                 }
22509                         }
22510                         this.dndController = new this.dndController(this, params);
22511                 }
22512         },
22513
22514         _store2model: function(){
22515                 // summary:
22516                 //              User specified a store&query rather than model, so create model from store/query
22517                 this._v10Compat = true;
22518                 dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
22519
22520                 var modelParams = {
22521                         id: this.id + "_ForestStoreModel",
22522                         store: this.store,
22523                         query: this.query,
22524                         childrenAttrs: this.childrenAttr
22525                 };
22526
22527                 // Only override the model's mayHaveChildren() method if the user has specified an override
22528                 if(this.params.mayHaveChildren){
22529                         modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
22530                 }
22531
22532                 if(this.params.getItemChildren){
22533                         modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
22534                                 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
22535                         });
22536                 }
22537                 this.model = new dijit.tree.ForestStoreModel(modelParams);
22538
22539                 // For backwards compatibility, the visibility of the root node is controlled by
22540                 // whether or not the user has specified a label
22541                 this.showRoot = Boolean(this.label);
22542         },
22543
22544         onLoad: function(){
22545                 // summary:
22546                 //              Called when tree finishes loading and expanding.
22547                 // description:
22548                 //              If persist == true the loading may encompass many levels of fetches
22549                 //              from the data store, each asynchronous.   Waits for all to finish.
22550                 // tags:
22551                 //              callback
22552         },
22553
22554         _load: function(){
22555                 // summary:
22556                 //              Initial load of the tree.
22557                 //              Load root node (possibly hidden) and it's children.
22558                 this.model.getRoot(
22559                         dojo.hitch(this, function(item){
22560                                 var rn = (this.rootNode = this.tree._createTreeNode({
22561                                         item: item,
22562                                         tree: this,
22563                                         isExpandable: true,
22564                                         label: this.label || this.getLabel(item),
22565                                         indent: this.showRoot ? 0 : -1
22566                                 }));
22567                                 if(!this.showRoot){
22568                                         rn.rowNode.style.display="none";
22569                                 }
22570                                 this.domNode.appendChild(rn.domNode);
22571                                 var identity = this.model.getIdentity(item);
22572                                 if(this._itemNodesMap[identity]){
22573                                         this._itemNodesMap[identity].push(rn);
22574                                 }else{
22575                                         this._itemNodesMap[identity] = [rn];
22576                                 }
22577
22578                                 rn._updateLayout();             // sets "dijitTreeIsRoot" CSS classname
22579
22580                                 // load top level children and then fire onLoad() event
22581                                 this._expandNode(rn).addCallback(dojo.hitch(this, function(){
22582                                         this._loadDeferred.callback(true);
22583                                         this.onLoad();
22584                                 }));
22585                         }),
22586                         function(err){
22587                                 console.error(this, ": error loading root: ", err);
22588                         }
22589                 );
22590         },
22591
22592         getNodesByItem: function(/*dojo.data.Item or id*/ item){
22593                 // summary:
22594                 //              Returns all tree nodes that refer to an item
22595                 // returns:
22596                 //              Array of tree nodes that refer to passed item
22597
22598                 if(!item){ return []; }
22599                 var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
22600                 // return a copy so widget don't get messed up by changes to returned array
22601                 return [].concat(this._itemNodesMap[identity]);
22602         },
22603
22604         _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
22605                 // summary:
22606                 //              Select a tree node related to passed item.
22607                 //              WARNING: if model use multi-parented items or desired tree node isn't already loaded
22608                 //              behavior is undefined. Use set('path', ...) instead.
22609
22610                 var oldValue = this.get("selectedItem");
22611                 var identity = (!item || dojo.isString(item)) ? item : this.model.getIdentity(item);
22612                 if(identity == oldValue ? this.model.getIdentity(oldValue) : null){ return; }
22613                 var nodes = this._itemNodesMap[identity];
22614                 this._selectNode((nodes && nodes[0]) || null);  //select the first item
22615         },
22616
22617         _getSelectedItemAttr: function(){
22618                 // summary:
22619                 //              Return item related to selected tree node.
22620                 return this.selectedNode && this.selectedNode.item;
22621         },
22622
22623         _setPathAttr: function(/*Item[] || String[]*/ path){
22624                 // summary:
22625                 //              Select the tree node identified by passed path.
22626                 // path:
22627                 //              Array of items or item id's
22628                 // returns:
22629                 //              Deferred to indicate when the set is complete
22630
22631                 var d = new dojo.Deferred();
22632
22633                 this._selectNode(null);
22634                 if(!path || !path.length){
22635                         d.resolve(true);
22636                         return d;
22637                 }
22638
22639                 // If this is called during initialization, defer running until Tree has finished loading
22640                 this._loadDeferred.addCallback(dojo.hitch(this, function(){
22641                         if(!this.rootNode){
22642                                 d.reject(new Error("!this.rootNode"));
22643                                 return;
22644                         }
22645                         if(path[0] !== this.rootNode.item && (dojo.isString(path[0]) && path[0] != this.model.getIdentity(this.rootNode.item))){
22646                                 d.reject(new Error(this.id + ":path[0] doesn't match this.rootNode.item.  Maybe you are using the wrong tree."));
22647                                 return;
22648                         }
22649                         path.shift();
22650
22651                         var node = this.rootNode;
22652
22653                         function advance(){
22654                                 // summary:
22655                                 //              Called when "node" has completed loading and expanding.   Pop the next item from the path
22656                                 //              (which must be a child of "node") and advance to it, and then recurse.
22657
22658                                 // Set item and identity to next item in path (node is pointing to the item that was popped
22659                                 // from the path _last_ time.
22660                                 var item = path.shift(),
22661                                         identity = dojo.isString(item) ? item : this.model.getIdentity(item);
22662
22663                                 // Change "node" from previous item in path to the item we just popped from path
22664                                 dojo.some(this._itemNodesMap[identity], function(n){
22665                                         if(n.getParent() == node){
22666                                                 node = n;
22667                                                 return true;
22668                                         }
22669                                         return false;
22670                                 });
22671
22672                                 if(path.length){
22673                                         // Need to do more expanding
22674                                         this._expandNode(node).addCallback(dojo.hitch(this, advance));
22675                                 }else{
22676                                         // Final destination node, select it
22677                                         this._selectNode(node);
22678                                         
22679                                         // signal that path setting is finished
22680                                         d.resolve(true);
22681                                 }
22682                         }
22683
22684                         this._expandNode(node).addCallback(dojo.hitch(this, advance));
22685                 }));
22686                         
22687                 return d;
22688         },
22689
22690         _getPathAttr: function(){
22691                 // summary:
22692                 //              Return an array of items that is the path to selected tree node.
22693                 if(!this.selectedNode){ return; }
22694                 var res = [];
22695                 var treeNode = this.selectedNode;
22696                 while(treeNode && treeNode !== this.rootNode){
22697                         res.unshift(treeNode.item);
22698                         treeNode = treeNode.getParent();
22699                 }
22700                 res.unshift(this.rootNode.item);
22701                 return res;
22702         },
22703
22704         ////////////// Data store related functions //////////////////////
22705         // These just get passed to the model; they are here for back-compat
22706
22707         mayHaveChildren: function(/*dojo.data.Item*/ item){
22708                 // summary:
22709                 //              Deprecated.   This should be specified on the model itself.
22710                 //
22711                 //              Overridable function to tell if an item has or may have children.
22712                 //              Controls whether or not +/- expando icon is shown.
22713                 //              (For efficiency reasons we may not want to check if an element actually
22714                 //              has children until user clicks the expando node)
22715                 // tags:
22716                 //              deprecated
22717         },
22718
22719         getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
22720                 // summary:
22721                 //              Deprecated.   This should be specified on the model itself.
22722                 //
22723                 //              Overridable function that return array of child items of given parent item,
22724                 //              or if parentItem==null then return top items in tree
22725                 // tags:
22726                 //              deprecated
22727         },
22728
22729         ///////////////////////////////////////////////////////
22730         // Functions for converting an item to a TreeNode
22731         getLabel: function(/*dojo.data.Item*/ item){
22732                 // summary:
22733                 //              Overridable function to get the label for a tree node (given the item)
22734                 // tags:
22735                 //              extension
22736                 return this.model.getLabel(item);       // String
22737         },
22738
22739         getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22740                 // summary:
22741                 //              Overridable function to return CSS class name to display icon
22742                 // tags:
22743                 //              extension
22744                 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
22745         },
22746
22747         getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22748                 // summary:
22749                 //              Overridable function to return CSS class name to display label
22750                 // tags:
22751                 //              extension
22752         },
22753
22754         getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22755                 // summary:
22756                 //              Overridable function to return CSS class name to display row
22757                 // tags:
22758                 //              extension
22759         },
22760
22761         getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22762                 // summary:
22763                 //              Overridable function to return CSS styles to display icon
22764                 // returns:
22765                 //              Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
22766                 // tags:
22767                 //              extension
22768         },
22769
22770         getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22771                 // summary:
22772                 //              Overridable function to return CSS styles to display label
22773                 // returns:
22774                 //              Object suitable for input to dojo.style() like {color: "red", background: "green"}
22775                 // tags:
22776                 //              extension
22777         },
22778
22779         getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
22780                 // summary:
22781                 //              Overridable function to return CSS styles to display row
22782                 // returns:
22783                 //              Object suitable for input to dojo.style() like {background-color: "#bbb"}
22784                 // tags:
22785                 //              extension
22786         },
22787
22788         getTooltip: function(/*dojo.data.Item*/ item){
22789                 // summary:
22790                 //              Overridable function to get the tooltip for a tree node (given the item)
22791                 // tags:
22792                 //              extension
22793                 return "";      // String
22794         },
22795
22796         /////////// Keyboard and Mouse handlers ////////////////////
22797
22798         _onKeyPress: function(/*Event*/ e){
22799                 // summary:
22800                 //              Translates keypress events into commands for the controller
22801                 if(e.altKey){ return; }
22802                 var dk = dojo.keys;
22803                 var treeNode = dijit.getEnclosingWidget(e.target);
22804                 if(!treeNode){ return; }
22805
22806                 var key = e.charOrCode;
22807                 if(typeof key == "string"){     // handle printables (letter navigation)
22808                         // Check for key navigation.
22809                         if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
22810                                 this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
22811                                 dojo.stopEvent(e);
22812                         }
22813                 }else{  // handle non-printables (arrow keys)
22814                         // clear record of recent printables (being saved for multi-char letter navigation),
22815                         // because "a", down-arrow, "b" shouldn't search for "ab"
22816                         if(this._curSearch){
22817                                 clearTimeout(this._curSearch.timer);
22818                                 delete this._curSearch;
22819                         }
22820
22821                         var map = this._keyHandlerMap;
22822                         if(!map){
22823                                 // setup table mapping keys to events
22824                                 map = {};
22825                                 map[dk.ENTER]="_onEnterKey";
22826                                 map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
22827                                 map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
22828                                 map[dk.UP_ARROW]="_onUpArrow";
22829                                 map[dk.DOWN_ARROW]="_onDownArrow";
22830                                 map[dk.HOME]="_onHomeKey";
22831                                 map[dk.END]="_onEndKey";
22832                                 this._keyHandlerMap = map;
22833                         }
22834                         if(this._keyHandlerMap[key]){
22835                                 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
22836                                 dojo.stopEvent(e);
22837                         }
22838                 }
22839         },
22840
22841         _onEnterKey: function(/*Object*/ message, /*Event*/ evt){
22842                 this._publish("execute", { item: message.item, node: message.node } );
22843                 this._selectNode(message.node);
22844                 this.onClick(message.item, message.node, evt);
22845         },
22846
22847         _onDownArrow: function(/*Object*/ message){
22848                 // summary:
22849                 //              down arrow pressed; get next visible node, set focus there
22850                 var node = this._getNextNode(message.node);
22851                 if(node && node.isTreeNode){
22852                         this.focusNode(node);
22853                 }
22854         },
22855
22856         _onUpArrow: function(/*Object*/ message){
22857                 // summary:
22858                 //              Up arrow pressed; move to previous visible node
22859
22860                 var node = message.node;
22861
22862                 // if younger siblings
22863                 var previousSibling = node.getPreviousSibling();
22864                 if(previousSibling){
22865                         node = previousSibling;
22866                         // if the previous node is expanded, dive in deep
22867                         while(node.isExpandable && node.isExpanded && node.hasChildren()){
22868                                 // move to the last child
22869                                 var children = node.getChildren();
22870                                 node = children[children.length-1];
22871                         }
22872                 }else{
22873                         // if this is the first child, return the parent
22874                         // unless the parent is the root of a tree with a hidden root
22875                         var parent = node.getParent();
22876                         if(!(!this.showRoot && parent === this.rootNode)){
22877                                 node = parent;
22878                         }
22879                 }
22880
22881                 if(node && node.isTreeNode){
22882                         this.focusNode(node);
22883                 }
22884         },
22885
22886         _onRightArrow: function(/*Object*/ message){
22887                 // summary:
22888                 //              Right arrow pressed; go to child node
22889                 var node = message.node;
22890
22891                 // if not expanded, expand, else move to 1st child
22892                 if(node.isExpandable && !node.isExpanded){
22893                         this._expandNode(node);
22894                 }else if(node.hasChildren()){
22895                         node = node.getChildren()[0];
22896                         if(node && node.isTreeNode){
22897                                 this.focusNode(node);
22898                         }
22899                 }
22900         },
22901
22902         _onLeftArrow: function(/*Object*/ message){
22903                 // summary:
22904                 //              Left arrow pressed.
22905                 //              If not collapsed, collapse, else move to parent.
22906
22907                 var node = message.node;
22908
22909                 if(node.isExpandable && node.isExpanded){
22910                         this._collapseNode(node);
22911                 }else{
22912                         var parent = node.getParent();
22913                         if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
22914                                 this.focusNode(parent);
22915                         }
22916                 }
22917         },
22918
22919         _onHomeKey: function(){
22920                 // summary:
22921                 //              Home key pressed; get first visible node, and set focus there
22922                 var node = this._getRootOrFirstNode();
22923                 if(node){
22924                         this.focusNode(node);
22925                 }
22926         },
22927
22928         _onEndKey: function(/*Object*/ message){
22929                 // summary:
22930                 //              End key pressed; go to last visible node.
22931
22932                 var node = this.rootNode;
22933                 while(node.isExpanded){
22934                         var c = node.getChildren();
22935                         node = c[c.length - 1];
22936                 }
22937
22938                 if(node && node.isTreeNode){
22939                         this.focusNode(node);
22940                 }
22941         },
22942
22943         // multiCharSearchDuration: Number
22944         //              If multiple characters are typed where each keystroke happens within
22945         //              multiCharSearchDuration of the previous keystroke,
22946         //              search for nodes matching all the keystrokes.
22947         //
22948         //              For example, typing "ab" will search for entries starting with
22949         //              "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
22950         multiCharSearchDuration: 250,
22951
22952         _onLetterKeyNav: function(message){
22953                 // summary:
22954                 //              Called when user presses a prinatable key; search for node starting with recently typed letters.
22955                 // message: Object
22956                 //              Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
22957
22958                 // Branch depending on whether this key starts a new search, or modifies an existing search
22959                 var cs = this._curSearch;
22960                 if(cs){
22961                         // We are continuing a search.  Ex: user has pressed 'a', and now has pressed
22962                         // 'b', so we want to search for nodes starting w/"ab".
22963                         cs.pattern = cs.pattern + message.key;
22964                         clearTimeout(cs.timer);
22965                 }else{
22966                         // We are starting a new search
22967                         cs = this._curSearch = {
22968                                         pattern: message.key,
22969                                         startNode: message.node
22970                         };
22971                 }
22972
22973                 // set/reset timer to forget recent keystrokes
22974                 var self = this;
22975                 cs.timer = setTimeout(function(){
22976                         delete self._curSearch;
22977                 }, this.multiCharSearchDuration);
22978
22979                 // Navigate to TreeNode matching keystrokes [entered so far].
22980                 var node = cs.startNode;
22981                 do{
22982                         node = this._getNextNode(node);
22983                         //check for last node, jump to first node if necessary
22984                         if(!node){
22985                                 node = this._getRootOrFirstNode();
22986                         }
22987                 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
22988                 if(node && node.isTreeNode){
22989                         // no need to set focus if back where we started
22990                         if(node !== cs.startNode){
22991                                 this.focusNode(node);
22992                         }
22993                 }
22994         },
22995
22996         _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
22997                 // summary:
22998                 //              Translates click events into commands for the controller to process
22999
23000                 var domElement = e.target,
23001                         isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
23002
23003                 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
23004                         // expando node was clicked, or label of a folder node was clicked; open it
23005                         if(nodeWidget.isExpandable){
23006                                 this._onExpandoClick({node:nodeWidget});
23007                         }
23008                 }else{
23009                         this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
23010                         this.onClick(nodeWidget.item, nodeWidget, e);
23011                         this.focusNode(nodeWidget);
23012                 }
23013                 if(!isExpandoClick){
23014                         this._selectNode(nodeWidget);
23015                 }
23016                 dojo.stopEvent(e);
23017         },
23018         _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
23019                 // summary:
23020                 //              Translates double-click events into commands for the controller to process
23021
23022                 var domElement = e.target,
23023                         isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
23024
23025                 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
23026                         // expando node was clicked, or label of a folder node was clicked; open it
23027                         if(nodeWidget.isExpandable){
23028                                 this._onExpandoClick({node:nodeWidget});
23029                         }
23030                 }else{
23031                         this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
23032                         this.onDblClick(nodeWidget.item, nodeWidget, e);
23033                         this.focusNode(nodeWidget);
23034                 }
23035                 if(!isExpandoClick){
23036                         this._selectNode(nodeWidget);
23037                 }
23038                 dojo.stopEvent(e);
23039         },
23040
23041         _onExpandoClick: function(/*Object*/ message){
23042                 // summary:
23043                 //              User clicked the +/- icon; expand or collapse my children.
23044                 var node = message.node;
23045
23046                 // If we are collapsing, we might be hiding the currently focused node.
23047                 // Also, clicking the expando node might have erased focus from the current node.
23048                 // For simplicity's sake just focus on the node with the expando.
23049                 this.focusNode(node);
23050
23051                 if(node.isExpanded){
23052                         this._collapseNode(node);
23053                 }else{
23054                         this._expandNode(node);
23055                 }
23056         },
23057
23058         onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
23059                 // summary:
23060                 //              Callback when a tree node is clicked
23061                 // tags:
23062                 //              callback
23063         },
23064         onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
23065                 // summary:
23066                 //              Callback when a tree node is double-clicked
23067                 // tags:
23068                 //              callback
23069         },
23070         onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
23071                 // summary:
23072                 //              Callback when a node is opened
23073                 // tags:
23074                 //              callback
23075         },
23076         onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
23077                 // summary:
23078                 //              Callback when a node is closed
23079                 // tags:
23080                 //              callback
23081         },
23082
23083         _getNextNode: function(node){
23084                 // summary:
23085                 //              Get next visible node
23086
23087                 if(node.isExpandable && node.isExpanded && node.hasChildren()){
23088                         // if this is an expanded node, get the first child
23089                         return node.getChildren()[0];           // _TreeNode
23090                 }else{
23091                         // find a parent node with a sibling
23092                         while(node && node.isTreeNode){
23093                                 var returnNode = node.getNextSibling();
23094                                 if(returnNode){
23095                                         return returnNode;              // _TreeNode
23096                                 }
23097                                 node = node.getParent();
23098                         }
23099                         return null;
23100                 }
23101         },
23102
23103         _getRootOrFirstNode: function(){
23104                 // summary:
23105                 //              Get first visible node
23106                 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
23107         },
23108
23109         _collapseNode: function(/*_TreeNode*/ node){
23110                 // summary:
23111                 //              Called when the user has requested to collapse the node
23112
23113                 if(node._expandNodeDeferred){
23114                         delete node._expandNodeDeferred;
23115                 }
23116
23117                 if(node.isExpandable){
23118                         if(node.state == "LOADING"){
23119                                 // ignore clicks while we are in the process of loading data
23120                                 return;
23121                         }
23122
23123                         node.collapse();
23124                         this.onClose(node.item, node);
23125
23126                         if(node.item){
23127                                 this._state(node.item,false);
23128                                 this._saveState();
23129                         }
23130                 }
23131         },
23132
23133         _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
23134                 // summary:
23135                 //              Called when the user has requested to expand the node
23136                 // recursive:
23137                 //              Internal flag used when _expandNode() calls itself, don't set.
23138                 // returns:
23139                 //              Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
23140                 //              that were previously opened too
23141
23142                 if(node._expandNodeDeferred && !recursive){
23143                         // there's already an expand in progress (or completed), so just return
23144                         return node._expandNodeDeferred;        // dojo.Deferred
23145                 }
23146
23147                 var model = this.model,
23148                         item = node.item,
23149                         _this = this;
23150
23151                 switch(node.state){
23152                         case "UNCHECKED":
23153                                 // need to load all the children, and then expand
23154                                 node.markProcessing();
23155
23156                                 // Setup deferred to signal when the load and expand are finished.
23157                                 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
23158                                 var def = (node._expandNodeDeferred = new dojo.Deferred());
23159
23160                                 // Get the children
23161                                 model.getChildren(
23162                                         item,
23163                                         function(items){
23164                                                 node.unmarkProcessing();
23165
23166                                                 // Display the children and also start expanding any children that were previously expanded
23167                                                 // (if this.persist == true).   The returned Deferred will fire when those expansions finish.
23168                                                 var scid = node.setChildItems(items);
23169
23170                                                 // Call _expandNode() again but this time it will just to do the animation (default branch).
23171                                                 // The returned Deferred will fire when the animation completes.
23172                                                 // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
23173                                                 var ed = _this._expandNode(node, true);
23174
23175                                                 // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
23176                                                 // signal that I am done.
23177                                                 scid.addCallback(function(){
23178                                                         ed.addCallback(function(){
23179                                                                 def.callback();
23180                                                         })
23181                                                 });
23182                                         },
23183                                         function(err){
23184                                                 console.error(_this, ": error loading root children: ", err);
23185                                         }
23186                                 );
23187                                 break;
23188
23189                         default:        // "LOADED"
23190                                 // data is already loaded; just expand node
23191                                 def = (node._expandNodeDeferred = node.expand());
23192
23193                                 this.onOpen(node.item, node);
23194
23195                                 if(item){
23196                                         this._state(item, true);
23197                                         this._saveState();
23198                                 }
23199                 }
23200
23201                 return def;     // dojo.Deferred
23202         },
23203
23204         ////////////////// Miscellaneous functions ////////////////
23205
23206         focusNode: function(/* _tree.Node */ node){
23207                 // summary:
23208                 //              Focus on the specified node (which must be visible)
23209                 // tags:
23210                 //              protected
23211
23212                 // set focus so that the label will be voiced using screen readers
23213                 dijit.focus(node.labelNode);
23214         },
23215
23216         _selectNode: function(/*_tree.Node*/ node){
23217                 // summary:
23218                 //              Mark specified node as select, and unmark currently selected node.
23219                 // tags:
23220                 //              protected
23221
23222                 if(this.selectedNode && !this.selectedNode._destroyed){
23223                         this.selectedNode.setSelected(false);
23224                 }
23225                 if(node){
23226                         node.setSelected(true);
23227                 }
23228                 this.selectedNode = node;
23229         },
23230
23231         _onNodeFocus: function(/*dijit._Widget*/ node){
23232                 // summary:
23233                 //              Called when a TreeNode gets focus, either by user clicking
23234                 //              it, or programatically by arrow key handling code.
23235                 // description:
23236                 //              It marks that the current node is the selected one, and the previously
23237                 //              selected node no longer is.
23238
23239                 if(node && node != this.lastFocused){
23240                         if(this.lastFocused && !this.lastFocused._destroyed){
23241                                 // mark that the previously focsable node is no longer focusable
23242                                 this.lastFocused.setFocusable(false);
23243                         }
23244
23245                         // mark that the new node is the currently selected one
23246                         node.setFocusable(true);
23247                         this.lastFocused = node;
23248                 }
23249         },
23250
23251         _onNodeMouseEnter: function(/*dijit._Widget*/ node){
23252                 // summary:
23253                 //              Called when mouse is over a node (onmouseenter event),
23254                 //              this is monitored by the DND code
23255         },
23256
23257         _onNodeMouseLeave: function(/*dijit._Widget*/ node){
23258                 // summary:
23259                 //              Called when mouse leaves a node (onmouseleave event),
23260                 //              this is monitored by the DND code
23261         },
23262
23263         //////////////// Events from the model //////////////////////////
23264
23265         _onItemChange: function(/*Item*/ item){
23266                 // summary:
23267                 //              Processes notification of a change to an item's scalar values like label
23268                 var model = this.model,
23269                         identity = model.getIdentity(item),
23270                         nodes = this._itemNodesMap[identity];
23271
23272                 if(nodes){
23273                         var label = this.getLabel(item),
23274                                 tooltip = this.getTooltip(item);
23275                         dojo.forEach(nodes, function(node){
23276                                 node.set({
23277                                         item: item,             // theoretically could be new JS Object representing same item
23278                                         label: label,
23279                                         tooltip: tooltip
23280                                 });
23281                                 node._updateItemClasses(item);
23282                         });
23283                 }
23284         },
23285
23286         _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
23287                 // summary:
23288                 //              Processes notification of a change to an item's children
23289                 var model = this.model,
23290                         identity = model.getIdentity(parent),
23291                         parentNodes = this._itemNodesMap[identity];
23292
23293                 if(parentNodes){
23294                         dojo.forEach(parentNodes,function(parentNode){
23295                                 parentNode.setChildItems(newChildrenList);
23296                         });
23297                 }
23298         },
23299
23300         _onItemDelete: function(/*Item*/ item){
23301                 // summary:
23302                 //              Processes notification of a deletion of an item
23303                 var model = this.model,
23304                         identity = model.getIdentity(item),
23305                         nodes = this._itemNodesMap[identity];
23306
23307                 if(nodes){
23308                         dojo.forEach(nodes,function(node){
23309                                 var parent = node.getParent();
23310                                 if(parent){
23311                                         // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
23312                                         parent.removeChild(node);
23313                                 }
23314                                 node.destroyRecursive();
23315                         });
23316                         delete this._itemNodesMap[identity];
23317                 }
23318         },
23319
23320         /////////////// Miscellaneous funcs
23321
23322         _initState: function(){
23323                 // summary:
23324                 //              Load in which nodes should be opened automatically
23325                 if(this.persist){
23326                         var cookie = dojo.cookie(this.cookieName);
23327                         this._openedItemIds = {};
23328                         if(cookie){
23329                                 dojo.forEach(cookie.split(','), function(item){
23330                                         this._openedItemIds[item] = true;
23331                                 }, this);
23332                         }
23333                 }
23334         },
23335         _state: function(item,expanded){
23336                 // summary:
23337                 //              Query or set expanded state for an item,
23338                 if(!this.persist){
23339                         return false;
23340                 }
23341                 var id=this.model.getIdentity(item);
23342                 if(arguments.length === 1){
23343                         return this._openedItemIds[id];
23344                 }
23345                 if(expanded){
23346                         this._openedItemIds[id] = true;
23347                 }else{
23348                         delete this._openedItemIds[id];
23349                 }
23350         },
23351         _saveState: function(){
23352                 // summary:
23353                 //              Create and save a cookie with the currently expanded nodes identifiers
23354                 if(!this.persist){
23355                         return;
23356                 }
23357                 var ary = [];
23358                 for(var id in this._openedItemIds){
23359                         ary.push(id);
23360                 }
23361                 dojo.cookie(this.cookieName, ary.join(","), {expires:365});
23362         },
23363
23364         destroy: function(){
23365                 if(this._curSearch){
23366                         clearTimeout(this._curSearch.timer);
23367                         delete this._curSearch;
23368                 }
23369                 if(this.rootNode){
23370                         this.rootNode.destroyRecursive();
23371                 }
23372                 if(this.dndController && !dojo.isString(this.dndController)){
23373                         this.dndController.destroy();
23374                 }
23375                 this.rootNode = null;
23376                 this.inherited(arguments);
23377         },
23378
23379         destroyRecursive: function(){
23380                 // A tree is treated as a leaf, not as a node with children (like a grid),
23381                 // but defining destroyRecursive for back-compat.
23382                 this.destroy();
23383         },
23384
23385         resize: function(changeSize){
23386                 if(changeSize){
23387                         dojo.marginBox(this.domNode, changeSize);
23388                         dojo.style(this.domNode, "overflow", "auto");   // for scrollbars
23389                 }
23390
23391                 // The only JS sizing involved w/tree is the indentation, which is specified
23392                 // in CSS and read in through this dummy indentDetector node (tree must be
23393                 // visible and attached to the DOM to read this)
23394                 this._nodePixelIndent = dojo.marginBox(this.tree.indentDetector).w;
23395
23396                 if(this.tree.rootNode){
23397                         // If tree has already loaded, then reset indent for all the nodes
23398                         this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
23399                 }
23400         },
23401
23402         _createTreeNode: function(/*Object*/ args){
23403                 // summary:
23404                 //              creates a TreeNode
23405                 // description:
23406                 //              Developers can override this method to define their own TreeNode class;
23407                 //              However it will probably be removed in a future release in favor of a way
23408                 //              of just specifying a widget for the label, rather than one that contains
23409                 //              the children too.
23410                 return new dijit._TreeNode(args);
23411         }
23412 });
23413
23414 // For back-compat.  TODO: remove in 2.0
23415
23416
23417
23418 }
23419
23420 if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23421 dojo._hasResource["dojo.dnd.Container"] = true;
23422 dojo.provide("dojo.dnd.Container");
23423
23424
23425
23426
23427 /*
23428         Container states:
23429                 ""              - normal state
23430                 "Over"  - mouse over a container
23431         Container item states:
23432                 ""              - normal state
23433                 "Over"  - mouse over a container item
23434 */
23435
23436 /*=====
23437 dojo.declare("dojo.dnd.__ContainerArgs", [], {
23438         creator: function(){
23439                 // summary:
23440                 //              a creator function, which takes a data item, and returns an object like that:
23441                 //              {node: newNode, data: usedData, type: arrayOfStrings}
23442         },
23443
23444         // skipForm: Boolean
23445         //              don't start the drag operation, if clicked on form elements
23446         skipForm: false,
23447
23448         // dropParent: Node||String
23449         //              node or node's id to use as the parent node for dropped items
23450         //              (must be underneath the 'node' parameter in the DOM)
23451         dropParent: null,
23452
23453         // _skipStartup: Boolean
23454         //              skip startup(), which collects children, for deferred initialization
23455         //              (this is used in the markup mode)
23456         _skipStartup: false
23457 });
23458
23459 dojo.dnd.Item = function(){
23460         // summary:
23461         //              Represents (one of) the source node(s) being dragged.
23462         //              Contains (at least) the "type" and "data" attributes.
23463         // type: String[]
23464         //              Type(s) of this item, by default this is ["text"]
23465         // data: Object
23466         //              Logical representation of the object being dragged.
23467         //              If the drag object's type is "text" then data is a String,
23468         //              if it's another type then data could be a different Object,
23469         //              perhaps a name/value hash.
23470         
23471         this.type = type;
23472         this.data = data;
23473 }
23474 =====*/
23475
23476 dojo.declare("dojo.dnd.Container", null, {
23477         // summary:
23478         //              a Container object, which knows when mouse hovers over it, 
23479         //              and over which element it hovers
23480         
23481         // object attributes (for markup)
23482         skipForm: false,
23483         
23484         /*=====
23485         // current: DomNode
23486         //              The DOM node the mouse is currently hovered over
23487         current: null,
23488         
23489         // map: Hash<String, dojo.dnd.Item>
23490         //              Map from an item's id (which is also the DOMNode's id) to
23491         //              the dojo.dnd.Item itself.
23492         map: {},
23493         =====*/
23494         
23495         constructor: function(node, params){
23496                 // summary:
23497                 //              a constructor of the Container
23498                 // node: Node
23499                 //              node or node's id to build the container on
23500                 // params: dojo.dnd.__ContainerArgs
23501                 //              a dictionary of parameters
23502                 this.node = dojo.byId(node);
23503                 if(!params){ params = {}; }
23504                 this.creator = params.creator || null;
23505                 this.skipForm = params.skipForm;
23506                 this.parent = params.dropParent && dojo.byId(params.dropParent);
23507                 
23508                 // class-specific variables
23509                 this.map = {};
23510                 this.current = null;
23511
23512                 // states
23513                 this.containerState = "";
23514                 dojo.addClass(this.node, "dojoDndContainer");
23515                 
23516                 // mark up children
23517                 if(!(params && params._skipStartup)){
23518                         this.startup();
23519                 }
23520
23521                 // set up events
23522                 this.events = [
23523                         dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
23524                         dojo.connect(this.node, "onmouseout",  this, "onMouseOut"),
23525                         // cancel text selection and text dragging
23526                         dojo.connect(this.node, "ondragstart",   this, "onSelectStart"),
23527                         dojo.connect(this.node, "onselectstart", this, "onSelectStart")
23528                 ];
23529         },
23530         
23531         // object attributes (for markup)
23532         creator: function(){
23533                 // summary:
23534                 //              creator function, dummy at the moment
23535         },
23536         
23537         // abstract access to the map
23538         getItem: function(/*String*/ key){
23539                 // summary:
23540                 //              returns a data item by its key (id)
23541                 return this.map[key];   // dojo.dnd.Item
23542         },
23543         setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
23544                 // summary:
23545                 //              associates a data item with its key (id)
23546                 this.map[key] = data;
23547         },
23548         delItem: function(/*String*/ key){
23549                 // summary:
23550                 //              removes a data item from the map by its key (id)
23551                 delete this.map[key];
23552         },
23553         forInItems: function(/*Function*/ f, /*Object?*/ o){
23554                 // summary:
23555                 //              iterates over a data map skipping members that 
23556                 //              are present in the empty object (IE and/or 3rd-party libraries).
23557                 o = o || dojo.global;
23558                 var m = this.map, e = dojo.dnd._empty;
23559                 for(var i in m){
23560                         if(i in e){ continue; }
23561                         f.call(o, m[i], i, this);
23562                 }
23563                 return o;       // Object
23564         },
23565         clearItems: function(){
23566                 // summary:
23567                 //              removes all data items from the map
23568                 this.map = {};
23569         },
23570         
23571         // methods
23572         getAllNodes: function(){
23573                 // summary:
23574                 //              returns a list (an array) of all valid child nodes
23575                 return dojo.query("> .dojoDndItem", this.parent);       // NodeList
23576         },
23577         sync: function(){
23578                 // summary:
23579                 //              sync up the node list with the data map
23580                 var map = {};
23581                 this.getAllNodes().forEach(function(node){
23582                         if(node.id){
23583                                 var item = this.getItem(node.id);
23584                                 if(item){
23585                                         map[node.id] = item;
23586                                         return;
23587                                 }
23588                         }else{
23589                                 node.id = dojo.dnd.getUniqueId();
23590                         }
23591                         var type = node.getAttribute("dndType"),
23592                                 data = node.getAttribute("dndData");
23593                         map[node.id] = {
23594                                 data: data || node.innerHTML,
23595                                 type: type ? type.split(/\s*,\s*/) : ["text"]
23596                         };
23597                 }, this);
23598                 this.map = map;
23599                 return this;    // self
23600         },
23601         insertNodes: function(data, before, anchor){
23602                 // summary:
23603                 //              inserts an array of new nodes before/after an anchor node
23604                 // data: Array
23605                 //              a list of data items, which should be processed by the creator function
23606                 // before: Boolean
23607                 //              insert before the anchor, if true, and after the anchor otherwise
23608                 // anchor: Node
23609                 //              the anchor node to be used as a point of insertion
23610                 if(!this.parent.firstChild){
23611                         anchor = null;
23612                 }else if(before){
23613                         if(!anchor){
23614                                 anchor = this.parent.firstChild;
23615                         }
23616                 }else{
23617                         if(anchor){
23618                                 anchor = anchor.nextSibling;
23619                         }
23620                 }
23621                 if(anchor){
23622                         for(var i = 0; i < data.length; ++i){
23623                                 var t = this._normalizedCreator(data[i]);
23624                                 this.setItem(t.node.id, {data: t.data, type: t.type});
23625                                 this.parent.insertBefore(t.node, anchor);
23626                         }
23627                 }else{
23628                         for(var i = 0; i < data.length; ++i){
23629                                 var t = this._normalizedCreator(data[i]);
23630                                 this.setItem(t.node.id, {data: t.data, type: t.type});
23631                                 this.parent.appendChild(t.node);
23632                         }
23633                 }
23634                 return this;    // self
23635         },
23636         destroy: function(){
23637                 // summary:
23638                 //              prepares this object to be garbage-collected
23639                 dojo.forEach(this.events, dojo.disconnect);
23640                 this.clearItems();
23641                 this.node = this.parent = this.current = null;
23642         },
23643
23644         // markup methods
23645         markupFactory: function(params, node){
23646                 params._skipStartup = true;
23647                 return new dojo.dnd.Container(node, params);
23648         },
23649         startup: function(){
23650                 // summary:
23651                 //              collects valid child items and populate the map
23652                 
23653                 // set up the real parent node
23654                 if(!this.parent){
23655                         // use the standard algorithm, if not assigned
23656                         this.parent = this.node;
23657                         if(this.parent.tagName.toLowerCase() == "table"){
23658                                 var c = this.parent.getElementsByTagName("tbody");
23659                                 if(c && c.length){ this.parent = c[0]; }
23660                         }
23661                 }
23662                 this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
23663
23664                 // process specially marked children
23665                 this.sync();
23666         },
23667
23668         // mouse events
23669         onMouseOver: function(e){
23670                 // summary:
23671                 //              event processor for onmouseover
23672                 // e: Event
23673                 //              mouse event
23674                 var n = e.relatedTarget;
23675                 while(n){
23676                         if(n == this.node){ break; }
23677                         try{
23678                                 n = n.parentNode;
23679                         }catch(x){
23680                                 n = null;
23681                         }
23682                 }
23683                 if(!n){
23684                         this._changeState("Container", "Over");
23685                         this.onOverEvent();
23686                 }
23687                 n = this._getChildByEvent(e);
23688                 if(this.current == n){ return; }
23689                 if(this.current){ this._removeItemClass(this.current, "Over"); }
23690                 if(n){ this._addItemClass(n, "Over"); }
23691                 this.current = n;
23692         },
23693         onMouseOut: function(e){
23694                 // summary:
23695                 //              event processor for onmouseout
23696                 // e: Event
23697                 //              mouse event
23698                 for(var n = e.relatedTarget; n;){
23699                         if(n == this.node){ return; }
23700                         try{
23701                                 n = n.parentNode;
23702                         }catch(x){
23703                                 n = null;
23704                         }
23705                 }
23706                 if(this.current){
23707                         this._removeItemClass(this.current, "Over");
23708                         this.current = null;
23709                 }
23710                 this._changeState("Container", "");
23711                 this.onOutEvent();
23712         },
23713         onSelectStart: function(e){
23714                 // summary:
23715                 //              event processor for onselectevent and ondragevent
23716                 // e: Event
23717                 //              mouse event
23718                 if(!this.skipForm || !dojo.dnd.isFormElement(e)){
23719                         dojo.stopEvent(e);
23720                 }
23721         },
23722         
23723         // utilities
23724         onOverEvent: function(){
23725                 // summary:
23726                 //              this function is called once, when mouse is over our container
23727         },
23728         onOutEvent: function(){
23729                 // summary:
23730                 //              this function is called once, when mouse is out of our container
23731         },
23732         _changeState: function(type, newState){
23733                 // summary:
23734                 //              changes a named state to new state value
23735                 // type: String
23736                 //              a name of the state to change
23737                 // newState: String
23738                 //              new state
23739                 var prefix = "dojoDnd" + type;
23740                 var state  = type.toLowerCase() + "State";
23741                 //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
23742                 dojo.removeClass(this.node, prefix + this[state]);
23743                 dojo.addClass(this.node, prefix + newState);
23744                 this[state] = newState;
23745         },
23746         _addItemClass: function(node, type){
23747                 // summary:
23748                 //              adds a class with prefix "dojoDndItem"
23749                 // node: Node
23750                 //              a node
23751                 // type: String
23752                 //              a variable suffix for a class name
23753                 dojo.addClass(node, "dojoDndItem" + type);
23754         },
23755         _removeItemClass: function(node, type){
23756                 // summary:
23757                 //              removes a class with prefix "dojoDndItem"
23758                 // node: Node
23759                 //              a node
23760                 // type: String
23761                 //              a variable suffix for a class name
23762                 dojo.removeClass(node, "dojoDndItem" + type);
23763         },
23764         _getChildByEvent: function(e){
23765                 // summary:
23766                 //              gets a child, which is under the mouse at the moment, or null
23767                 // e: Event
23768                 //              a mouse event
23769                 var node = e.target;
23770                 if(node){
23771                         for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
23772                                 if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
23773                         }
23774                 }
23775                 return null;
23776         },
23777         _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
23778                 // summary:
23779                 //              adds all necessary data to the output of the user-supplied creator function
23780                 var t = (this.creator || this.defaultCreator).call(this, item, hint);
23781                 if(!dojo.isArray(t.type)){ t.type = ["text"]; }
23782                 if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
23783                 dojo.addClass(t.node, "dojoDndItem");
23784                 return t;
23785         }
23786 });
23787
23788 dojo.dnd._createNode = function(tag){
23789         // summary:
23790         //              returns a function, which creates an element of given tag 
23791         //              (SPAN by default) and sets its innerHTML to given text
23792         // tag: String
23793         //              a tag name or empty for SPAN
23794         if(!tag){ return dojo.dnd._createSpan; }
23795         return function(text){  // Function
23796                 return dojo.create(tag, {innerHTML: text});     // Node
23797         };
23798 };
23799
23800 dojo.dnd._createTrTd = function(text){
23801         // summary:
23802         //              creates a TR/TD structure with given text as an innerHTML of TD
23803         // text: String
23804         //              a text for TD
23805         var tr = dojo.create("tr");
23806         dojo.create("td", {innerHTML: text}, tr);
23807         return tr;      // Node
23808 };
23809
23810 dojo.dnd._createSpan = function(text){
23811         // summary:
23812         //              creates a SPAN element with given text as its innerHTML
23813         // text: String
23814         //              a text for SPAN
23815         return dojo.create("span", {innerHTML: text});  // Node
23816 };
23817
23818 // dojo.dnd._defaultCreatorNodes: Object
23819 //              a dictionary that maps container tag names to child tag names
23820 dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
23821
23822 dojo.dnd._defaultCreator = function(node){
23823         // summary:
23824         //              takes a parent node, and returns an appropriate creator function
23825         // node: Node
23826         //              a container node
23827         var tag = node.tagName.toLowerCase();
23828         var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
23829                         dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
23830         return function(item, hint){    // Function
23831                 var isObj = item && dojo.isObject(item), data, type, n;
23832                 if(isObj && item.tagName && item.nodeType && item.getAttribute){
23833                         // process a DOM node
23834                         data = item.getAttribute("dndData") || item.innerHTML;
23835                         type = item.getAttribute("dndType");
23836                         type = type ? type.split(/\s*,\s*/) : ["text"];
23837                         n = item;       // this node is going to be moved rather than copied
23838                 }else{
23839                         // process a DnD item object or a string
23840                         data = (isObj && item.data) ? item.data : item;
23841                         type = (isObj && item.type) ? item.type : ["text"];
23842                         n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
23843                 }
23844                 if(!n.id){
23845                         n.id = dojo.dnd.getUniqueId();
23846                 }
23847                 return {node: n, data: data, type: type};
23848         };
23849 };
23850
23851 }
23852
23853 if(!dojo._hasResource["dijit.tree._dndContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23854 dojo._hasResource["dijit.tree._dndContainer"] = true;
23855 dojo.provide("dijit.tree._dndContainer");
23856
23857
23858
23859 dojo.declare("dijit.tree._dndContainer",
23860         null,
23861         {
23862
23863                 // summary:
23864                 //              This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
23865                 //              It's modeled after `dojo.dnd.Container`.
23866                 // tags:
23867                 //              protected
23868
23869                 /*=====
23870                 // current: DomNode
23871                 //              The currently hovered TreeNode.rowNode (which is the DOM node
23872                 //              associated w/a given node in the tree, excluding it's descendants)
23873                 current: null,
23874                 =====*/
23875
23876                 constructor: function(tree, params){
23877                         // summary:
23878                         //              A constructor of the Container
23879                         // tree: Node
23880                         //              Node or node's id to build the container on
23881                         // params: dijit.tree.__SourceArgs
23882                         //              A dict of parameters, which gets mixed into the object
23883                         // tags:
23884                         //              private
23885                         this.tree = tree;
23886                         this.node = tree.domNode;       // TODO: rename; it's not a TreeNode but the whole Tree
23887                         dojo.mixin(this, params);
23888
23889                         // class-specific variables
23890                         this.map = {};
23891                         this.current = null;    // current TreeNode's DOM node
23892
23893                         // states
23894                         this.containerState = "";
23895                         dojo.addClass(this.node, "dojoDndContainer");
23896
23897                         // set up events
23898                         this.events = [
23899                                 // container level events
23900                                 dojo.connect(this.node, "onmouseenter", this, "onOverEvent"),
23901                                 dojo.connect(this.node, "onmouseleave", this, "onOutEvent"),
23902
23903                                 // switching between TreeNodes
23904                                 dojo.connect(this.tree, "_onNodeMouseEnter", this, "onMouseOver"),
23905                                 dojo.connect(this.tree, "_onNodeMouseLeave", this, "onMouseOut"),
23906
23907                                 // cancel text selection and text dragging
23908                                 dojo.connect(this.node, "ondragstart", dojo, "stopEvent"),
23909                                 dojo.connect(this.node, "onselectstart", dojo, "stopEvent")
23910                         ];
23911                 },
23912
23913                 getItem: function(/*String*/ key){
23914                         // summary:
23915                         //              Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
23916                         //              Called by dojo.dnd.Source.checkAcceptance().
23917                         // tags:
23918                         //              protected
23919
23920                         var node = this.selection[key],
23921                                 ret = {
23922                                         data: dijit.getEnclosingWidget(node),
23923                                         type: ["treeNode"]
23924                                 };
23925
23926                         return ret;     // dojo.dnd.Item
23927                 },
23928
23929                 destroy: function(){
23930                         // summary:
23931                         //              Prepares this object to be garbage-collected
23932
23933                         dojo.forEach(this.events, dojo.disconnect);
23934                         // this.clearItems();
23935                         this.node = this.parent = null;
23936                 },
23937
23938                 // mouse events
23939                 onMouseOver: function(/*TreeNode*/ widget, /*Event*/ evt){
23940                         // summary:
23941                         //              Called when mouse is moved over a TreeNode
23942                         // tags:
23943                         //              protected
23944                         this.current = widget.rowNode;
23945                         this.currentWidget = widget;
23946                 },
23947
23948                 onMouseOut: function(/*TreeNode*/ widget, /*Event*/ evt){
23949                         // summary:
23950                         //              Called when mouse is moved away from a TreeNode
23951                         // tags:
23952                         //              protected
23953                         this.current = null;
23954                         this.currentWidget = null;
23955                 },
23956
23957                 _changeState: function(type, newState){
23958                         // summary:
23959                         //              Changes a named state to new state value
23960                         // type: String
23961                         //              A name of the state to change
23962                         // newState: String
23963                         //              new state
23964                         var prefix = "dojoDnd" + type;
23965                         var state = type.toLowerCase() + "State";
23966                         //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
23967                         dojo.removeClass(this.node, prefix + this[state]);
23968                         dojo.addClass(this.node, prefix + newState);
23969                         this[state] = newState;
23970                 },
23971
23972                 _addItemClass: function(node, type){
23973                         // summary:
23974                         //              Adds a class with prefix "dojoDndItem"
23975                         // node: Node
23976                         //              A node
23977                         // type: String
23978                         //              A variable suffix for a class name
23979                         dojo.addClass(node, "dojoDndItem" + type);
23980                 },
23981
23982                 _removeItemClass: function(node, type){
23983                         // summary:
23984                         //              Removes a class with prefix "dojoDndItem"
23985                         // node: Node
23986                         //              A node
23987                         // type: String
23988                         //              A variable suffix for a class name
23989                         dojo.removeClass(node, "dojoDndItem" + type);
23990                 },
23991
23992                 onOverEvent: function(){
23993                         // summary:
23994                         //              This function is called once, when mouse is over our container
23995                         // tags:
23996                         //              protected
23997                         this._changeState("Container", "Over");
23998                 },
23999
24000                 onOutEvent: function(){
24001                         // summary:
24002                         //              This function is called once, when mouse is out of our container
24003                         // tags:
24004                         //              protected
24005                         this._changeState("Container", "");
24006                 }
24007 });
24008
24009 }
24010
24011 if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24012 dojo._hasResource["dijit.tree._dndSelector"] = true;
24013 dojo.provide("dijit.tree._dndSelector");
24014
24015
24016
24017 dojo.declare("dijit.tree._dndSelector",
24018         dijit.tree._dndContainer,
24019         {
24020                 // summary:
24021                 //              This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
24022                 //              It's based on `dojo.dnd.Selector`.
24023                 // tags:
24024                 //              protected
24025
24026                 /*=====
24027                 // selection: Hash<String, DomNode>
24028                 //              (id, DomNode) map for every TreeNode that's currently selected.
24029                 //              The DOMNode is the TreeNode.rowNode.
24030                 selection: {},
24031                 =====*/
24032
24033                 constructor: function(tree, params){
24034                         // summary:
24035                         //              Initialization
24036                         // tags:
24037                         //              private
24038
24039                         this.selection={};
24040                         this.anchor = null;
24041                         this.simpleSelection=false;
24042
24043                         this.events.push(
24044                                 dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"),
24045                                 dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"),
24046                                 dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove")
24047                         );
24048                 },
24049
24050                 //      singular: Boolean
24051                 //              Allows selection of only one element, if true.
24052                 //              Tree hasn't been tested in singular=true mode, unclear if it works.
24053                 singular: false,
24054
24055                 // methods
24056
24057                 getSelectedNodes: function(){
24058                         // summary:
24059                         //              Returns the set of selected nodes.
24060                         //              Used by dndSource on the start of a drag.
24061                         // tags:
24062                         //              protected
24063                         return this.selection;
24064                 },
24065
24066                 selectNone: function(){
24067                         // summary:
24068                         //              Unselects all items
24069                         // tags:
24070                         //              private
24071
24072                         return this._removeSelection()._removeAnchor(); // self
24073                 },
24074
24075                 destroy: function(){
24076                         // summary:
24077                         //              Prepares the object to be garbage-collected
24078                         this.inherited(arguments);
24079                         this.selection = this.anchor = null;
24080                 },
24081
24082                 // mouse events
24083                 onMouseDown: function(e){
24084                         // summary:
24085                         //              Event processor for onmousedown
24086                         // e: Event
24087                         //              mouse event
24088                         // tags:
24089                         //              protected
24090
24091                         if(!this.current){ return; }
24092
24093                         if(e.button == dojo.mouseButtons.RIGHT){ return; }      // ignore right-click
24094
24095                         var treeNode = dijit.getEnclosingWidget(this.current),
24096                                 id = treeNode.id + "-dnd"       // so id doesn't conflict w/widget
24097
24098                         if(!dojo.hasAttr(this.current, "id")){
24099                                 dojo.attr(this.current, "id", id);
24100                         }
24101
24102                         if(!this.singular && !dojo.isCopyKey(e) && !e.shiftKey && (this.current.id in this.selection)){
24103                                 this.simpleSelection = true;
24104                                 dojo.stopEvent(e);
24105                                 return;
24106                         }
24107                         if(this.singular){
24108                                 if(this.anchor == this.current){
24109                                         if(dojo.isCopyKey(e)){
24110                                                 this.selectNone();
24111                                         }
24112                                 }else{
24113                                         this.selectNone();
24114                                         this.anchor = this.current;
24115                                         this._addItemClass(this.anchor, "Anchor");
24116
24117                                         this.selection[this.current.id] = this.current;
24118                                 }
24119                         }else{
24120                                 if(!this.singular && e.shiftKey){
24121                                         if(dojo.isCopyKey(e)){
24122                                                 //TODO add range to selection
24123                                         }else{
24124                                                 //TODO select new range from anchor
24125                                         }
24126                                 }else{
24127                                         if(dojo.isCopyKey(e)){
24128                                                 if(this.anchor == this.current){
24129                                                         delete this.selection[this.anchor.id];
24130                                                         this._removeAnchor();
24131                                                 }else{
24132                                                         if(this.current.id in this.selection){
24133                                                                 this._removeItemClass(this.current, "Selected");
24134                                                                 delete this.selection[this.current.id];
24135                                                         }else{
24136                                                                 if(this.anchor){
24137                                                                         this._removeItemClass(this.anchor, "Anchor");
24138                                                                         this._addItemClass(this.anchor, "Selected");
24139                                                                 }
24140                                                                 this.anchor = this.current;
24141                                                                 this._addItemClass(this.current, "Anchor");
24142                                                                 this.selection[this.current.id] = this.current;
24143                                                         }
24144                                                 }
24145                                         }else{
24146                                                 if(!(id in this.selection)){
24147                                                         this.selectNone();
24148                                                         this.anchor = this.current;
24149                                                         this._addItemClass(this.current, "Anchor");
24150                                                         this.selection[id] = this.current;
24151                                                 }
24152                                         }
24153                                 }
24154                         }
24155
24156                         dojo.stopEvent(e);
24157                 },
24158
24159                 onMouseUp: function(e){
24160                         // summary:
24161                         //              Event processor for onmouseup
24162                         // e: Event
24163                         //              mouse event
24164                         // tags:
24165                         //              protected
24166
24167                         // TODO: this code is apparently for handling an edge case when the user is selecting
24168                         // multiple nodes and then mousedowns on a node by accident... it lets the user keep the
24169                         // current selection by moving the mouse away (or something like that).   It doesn't seem
24170                         // to work though and requires a lot of plumbing (including this code, the onmousemove
24171                         // handler, and the this.simpleSelection attribute.   Consider getting rid of all of it.
24172
24173                         if(!this.simpleSelection){ return; }
24174                         this.simpleSelection = false;
24175                         this.selectNone();
24176                         if(this.current){
24177                                 this.anchor = this.current;
24178                                 this._addItemClass(this.anchor, "Anchor");
24179                                 this.selection[this.current.id] = this.current;
24180                         }
24181                 },
24182                 onMouseMove: function(e){
24183                         // summary
24184                         //              event processor for onmousemove
24185                         // e: Event
24186                         //              mouse event
24187                         this.simpleSelection = false;
24188                 },
24189
24190                 _removeSelection: function(){
24191                         // summary:
24192                         //              Unselects all items
24193                         // tags:
24194                         //              private
24195                         var e = dojo.dnd._empty;
24196                         for(var i in this.selection){
24197                                 if(i in e){ continue; }
24198                                 var node = dojo.byId(i);
24199                                 if(node){ this._removeItemClass(node, "Selected"); }
24200                         }
24201                         this.selection = {};
24202                         return this;    // self
24203                 },
24204
24205                 _removeAnchor: function(){
24206                         // summary:
24207                         //              Removes the Anchor CSS class from a node.
24208                         //              According to `dojo.dnd.Selector`, anchor means that
24209                         //              "an item is selected, and is an anchor for a 'shift' selection".
24210                         //              It's not relevant for Tree at this point, since we don't support multiple selection.
24211                         // tags:
24212                         //              private
24213                         if(this.anchor){
24214                                 this._removeItemClass(this.anchor, "Anchor");
24215                                 this.anchor = null;
24216                         }
24217                         return this;    // self
24218                 },
24219
24220                 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
24221                         // summary:
24222                         //              Iterates over selected items;
24223                         //              see `dojo.dnd.Container.forInItems()` for details
24224                         o = o || dojo.global;
24225                         for(var id in this.selection){
24226                                 console.log("selected item id: " + id);
24227                                 f.call(o, this.getItem(id), id, this);
24228                         }
24229                 }
24230 });
24231
24232 }
24233
24234 if(!dojo._hasResource["dojo.dnd.Avatar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24235 dojo._hasResource["dojo.dnd.Avatar"] = true;
24236 dojo.provide("dojo.dnd.Avatar");
24237
24238
24239
24240 dojo.declare("dojo.dnd.Avatar", null, {
24241         // summary:
24242         //              Object that represents transferred DnD items visually
24243         // manager: Object
24244         //              a DnD manager object
24245
24246         constructor: function(manager){
24247                 this.manager = manager;
24248                 this.construct();
24249         },
24250
24251         // methods
24252         construct: function(){
24253                 // summary:
24254                 //              constructor function;
24255                 //              it is separate so it can be (dynamically) overwritten in case of need
24256                 this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
24257                 var a = dojo.create("table", {
24258                                 "class": "dojoDndAvatar",
24259                                 style: {
24260                                         position: "absolute",
24261                                         zIndex:   "1999",
24262                                         margin:   "0px"
24263                                 }
24264                         }),
24265                         source = this.manager.source, node,
24266                         b = dojo.create("tbody", null, a),
24267                         tr = dojo.create("tr", null, b),
24268                         td = dojo.create("td", null, tr),
24269                         icon = this.isA11y ? dojo.create("span", {
24270                                                 id : "a11yIcon",
24271                                                 innerHTML : this.manager.copy ? '+' : "<"
24272                                         }, td) : null,
24273                         span = dojo.create("span", {
24274                                 innerHTML: source.generateText ? this._generateText() : ""
24275                         }, td),
24276                         k = Math.min(5, this.manager.nodes.length), i = 0;
24277                 // we have to set the opacity on IE only after the node is live
24278                 dojo.attr(tr, {
24279                         "class": "dojoDndAvatarHeader",
24280                         style: {opacity: 0.9}
24281                 });
24282                 for(; i < k; ++i){
24283                         if(source.creator){
24284                                 // create an avatar representation of the node
24285                                 node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
24286                         }else{
24287                                 // or just clone the node and hope it works
24288                                 node = this.manager.nodes[i].cloneNode(true);
24289                                 if(node.tagName.toLowerCase() == "tr"){
24290                                         // insert extra table nodes
24291                                         var table = dojo.create("table"),
24292                                                 tbody = dojo.create("tbody", null, table);
24293                                         tbody.appendChild(node);
24294                                         node = table;
24295                                 }
24296                         }
24297                         node.id = "";
24298                         tr = dojo.create("tr", null, b);
24299                         td = dojo.create("td", null, tr);
24300                         td.appendChild(node);
24301                         dojo.attr(tr, {
24302                                 "class": "dojoDndAvatarItem",
24303                                 style: {opacity: (9 - i) / 10}
24304                         });
24305                 }
24306                 this.node = a;
24307         },
24308         destroy: function(){
24309                 // summary:
24310                 //              destructor for the avatar; called to remove all references so it can be garbage-collected
24311                 dojo.destroy(this.node);
24312                 this.node = false;
24313         },
24314         update: function(){
24315                 // summary:
24316                 //              updates the avatar to reflect the current DnD state
24317                 dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
24318                 if (this.isA11y){
24319                         var icon = dojo.byId("a11yIcon");
24320                         var text = '+';   // assume canDrop && copy
24321                         if (this.manager.canDropFlag && !this.manager.copy) {
24322                                 text = '< '; // canDrop && move 
24323                         }else if (!this.manager.canDropFlag && !this.manager.copy) {
24324                                 text = "o"; //!canDrop && move
24325                         }else if(!this.manager.canDropFlag){
24326                                 text = 'x';  // !canDrop && copy
24327                         }
24328                         icon.innerHTML=text;
24329                 }
24330                 // replace text
24331                 dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
24332                         function(node){
24333                                 node.innerHTML = this._generateText();
24334                         }, this);
24335         },
24336         _generateText: function(){
24337                 // summary: generates a proper text to reflect copying or moving of items
24338                 return this.manager.nodes.length.toString();
24339         }
24340 });
24341
24342 }
24343
24344 if(!dojo._hasResource["dojo.dnd.Manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24345 dojo._hasResource["dojo.dnd.Manager"] = true;
24346 dojo.provide("dojo.dnd.Manager");
24347
24348
24349
24350
24351
24352 dojo.declare("dojo.dnd.Manager", null, {
24353         // summary:
24354         //              the manager of DnD operations (usually a singleton)
24355         constructor: function(){
24356                 this.avatar  = null;
24357                 this.source = null;
24358                 this.nodes = [];
24359                 this.copy  = true;
24360                 this.target = null;
24361                 this.canDropFlag = false;
24362                 this.events = [];
24363         },
24364
24365         // avatar's offset from the mouse
24366         OFFSET_X: 16,
24367         OFFSET_Y: 16,
24368         
24369         // methods
24370         overSource: function(source){
24371                 // summary:
24372                 //              called when a source detected a mouse-over condition
24373                 // source: Object
24374                 //              the reporter
24375                 if(this.avatar){
24376                         this.target = (source && source.targetState != "Disabled") ? source : null;
24377                         this.canDropFlag = Boolean(this.target);
24378                         this.avatar.update();
24379                 }
24380                 dojo.publish("/dnd/source/over", [source]);
24381         },
24382         outSource: function(source){
24383                 // summary:
24384                 //              called when a source detected a mouse-out condition
24385                 // source: Object
24386                 //              the reporter
24387                 if(this.avatar){
24388                         if(this.target == source){
24389                                 this.target = null;
24390                                 this.canDropFlag = false;
24391                                 this.avatar.update();
24392                                 dojo.publish("/dnd/source/over", [null]);
24393                         }
24394                 }else{
24395                         dojo.publish("/dnd/source/over", [null]);
24396                 }
24397         },
24398         startDrag: function(source, nodes, copy){
24399                 // summary:
24400                 //              called to initiate the DnD operation
24401                 // source: Object
24402                 //              the source which provides items
24403                 // nodes: Array
24404                 //              the list of transferred items
24405                 // copy: Boolean
24406                 //              copy items, if true, move items otherwise
24407                 this.source = source;
24408                 this.nodes  = nodes;
24409                 this.copy   = Boolean(copy); // normalizing to true boolean
24410                 this.avatar = this.makeAvatar();
24411                 dojo.body().appendChild(this.avatar.node);
24412                 dojo.publish("/dnd/start", [source, nodes, this.copy]);
24413                 this.events = [
24414                         dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
24415                         dojo.connect(dojo.doc, "onmouseup",   this, "onMouseUp"),
24416                         dojo.connect(dojo.doc, "onkeydown",   this, "onKeyDown"),
24417                         dojo.connect(dojo.doc, "onkeyup",     this, "onKeyUp"),
24418                         // cancel text selection and text dragging
24419                         dojo.connect(dojo.doc, "ondragstart",   dojo.stopEvent),
24420                         dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
24421                 ];
24422                 var c = "dojoDnd" + (copy ? "Copy" : "Move");
24423                 dojo.addClass(dojo.body(), c); 
24424         },
24425         canDrop: function(flag){
24426                 // summary:
24427                 //              called to notify if the current target can accept items
24428                 var canDropFlag = Boolean(this.target && flag);
24429                 if(this.canDropFlag != canDropFlag){
24430                         this.canDropFlag = canDropFlag;
24431                         this.avatar.update();
24432                 }
24433         },
24434         stopDrag: function(){
24435                 // summary:
24436                 //              stop the DnD in progress
24437                 dojo.removeClass(dojo.body(), "dojoDndCopy");
24438                 dojo.removeClass(dojo.body(), "dojoDndMove");
24439                 dojo.forEach(this.events, dojo.disconnect);
24440                 this.events = [];
24441                 this.avatar.destroy();
24442                 this.avatar = null;
24443                 this.source = this.target = null;
24444                 this.nodes = [];
24445         },
24446         makeAvatar: function(){
24447                 // summary:
24448                 //              makes the avatar; it is separate to be overwritten dynamically, if needed
24449                 return new dojo.dnd.Avatar(this);
24450         },
24451         updateAvatar: function(){
24452                 // summary:
24453                 //              updates the avatar; it is separate to be overwritten dynamically, if needed
24454                 this.avatar.update();
24455         },
24456         
24457         // mouse event processors
24458         onMouseMove: function(e){
24459                 // summary:
24460                 //              event processor for onmousemove
24461                 // e: Event
24462                 //              mouse event
24463                 var a = this.avatar;
24464                 if(a){
24465                         dojo.dnd.autoScrollNodes(e);
24466                         //dojo.dnd.autoScroll(e);
24467                         var s = a.node.style;
24468                         s.left = (e.pageX + this.OFFSET_X) + "px";
24469                         s.top  = (e.pageY + this.OFFSET_Y) + "px";
24470                         var copy = Boolean(this.source.copyState(dojo.isCopyKey(e)));
24471                         if(this.copy != copy){ 
24472                                 this._setCopyStatus(copy);
24473                         }
24474                 }
24475         },
24476         onMouseUp: function(e){
24477                 // summary:
24478                 //              event processor for onmouseup
24479                 // e: Event
24480                 //              mouse event
24481                 if(this.avatar){
24482                         if(this.target && this.canDropFlag){
24483                                 var copy = Boolean(this.source.copyState(dojo.isCopyKey(e))),
24484                                 params = [this.source, this.nodes, copy, this.target, e];
24485                                 dojo.publish("/dnd/drop/before", params);
24486                                 dojo.publish("/dnd/drop", params);
24487                         }else{
24488                                 dojo.publish("/dnd/cancel");
24489                         }
24490                         this.stopDrag();
24491                 }
24492         },
24493         
24494         // keyboard event processors
24495         onKeyDown: function(e){
24496                 // summary:
24497                 //              event processor for onkeydown:
24498                 //              watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
24499                 // e: Event
24500                 //              keyboard event
24501                 if(this.avatar){
24502                         switch(e.keyCode){
24503                                 case dojo.keys.CTRL:
24504                                         var copy = Boolean(this.source.copyState(true));
24505                                         if(this.copy != copy){ 
24506                                                 this._setCopyStatus(copy);
24507                                         }
24508                                         break;
24509                                 case dojo.keys.ESCAPE:
24510                                         dojo.publish("/dnd/cancel");
24511                                         this.stopDrag();
24512                                         break;
24513                         }
24514                 }
24515         },
24516         onKeyUp: function(e){
24517                 // summary:
24518                 //              event processor for onkeyup, watching for CTRL for copy/move status
24519                 // e: Event
24520                 //              keyboard event
24521                 if(this.avatar && e.keyCode == dojo.keys.CTRL){
24522                         var copy = Boolean(this.source.copyState(false));
24523                         if(this.copy != copy){ 
24524                                 this._setCopyStatus(copy);
24525                         }
24526                 }
24527         },
24528         
24529         // utilities
24530         _setCopyStatus: function(copy){
24531                 // summary:
24532                 //              changes the copy status
24533                 // copy: Boolean
24534                 //              the copy status
24535                 this.copy = copy;
24536                 this.source._markDndStatus(this.copy);
24537                 this.updateAvatar();
24538                 dojo.removeClass(dojo.body(), "dojoDnd" + (this.copy ? "Move" : "Copy"));
24539                 dojo.addClass(dojo.body(), "dojoDnd" + (this.copy ? "Copy" : "Move"));
24540         }
24541 });
24542
24543 // dojo.dnd._manager:
24544 //              The manager singleton variable. Can be overwritten if needed.
24545 dojo.dnd._manager = null;
24546
24547 dojo.dnd.manager = function(){
24548         // summary:
24549         //              Returns the current DnD manager.  Creates one if it is not created yet.
24550         if(!dojo.dnd._manager){
24551                 dojo.dnd._manager = new dojo.dnd.Manager();
24552         }
24553         return dojo.dnd._manager;       // Object
24554 };
24555
24556 }
24557
24558 if(!dojo._hasResource["dijit.tree.dndSource"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
24559 dojo._hasResource["dijit.tree.dndSource"] = true;
24560 dojo.provide("dijit.tree.dndSource");
24561
24562
24563
24564
24565 /*=====
24566 dijit.tree.__SourceArgs = function(){
24567         // summary:
24568         //              A dict of parameters for Tree source configuration.
24569         // isSource: Boolean?
24570         //              Can be used as a DnD source. Defaults to true.
24571         // accept: String[]
24572         //              List of accepted types (text strings) for a target; defaults to
24573         //              ["text", "treeNode"]
24574         // copyOnly: Boolean?
24575         //              Copy items, if true, use a state of Ctrl key otherwise,
24576         // dragThreshold: Number
24577         //              The move delay in pixels before detecting a drag; 0 by default
24578         // betweenThreshold: Integer
24579         //              Distance from upper/lower edge of node to allow drop to reorder nodes
24580         this.isSource = isSource;
24581         this.accept = accept;
24582         this.autoSync = autoSync;
24583         this.copyOnly = copyOnly;
24584         this.dragThreshold = dragThreshold;
24585         this.betweenThreshold = betweenThreshold;
24586 }
24587 =====*/
24588
24589 dojo.declare("dijit.tree.dndSource", dijit.tree._dndSelector, {
24590         // summary:
24591         //              Handles drag and drop operations (as a source or a target) for `dijit.Tree`
24592
24593         // isSource: [private] Boolean
24594         //              Can be used as a DnD source.
24595         isSource: true,
24596
24597         // accept: String[]
24598         //              List of accepted types (text strings) for the Tree; defaults to
24599         //              ["text"]
24600         accept: ["text", "treeNode"],
24601
24602         // copyOnly: [private] Boolean
24603         //              Copy items, if true, use a state of Ctrl key otherwise
24604         copyOnly: false,
24605
24606         // dragThreshold: Number
24607         //              The move delay in pixels before detecting a drag; 5 by default
24608         dragThreshold: 5,
24609
24610         // betweenThreshold: Integer
24611         //              Distance from upper/lower edge of node to allow drop to reorder nodes
24612         betweenThreshold: 0,
24613
24614         constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
24615                 // summary:
24616                 //              a constructor of the Tree DnD Source
24617                 // tags:
24618                 //              private
24619                 if(!params){ params = {}; }
24620                 dojo.mixin(this, params);
24621                 this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
24622                 var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
24623                 this.accept = null;
24624                 if(type.length){
24625                         this.accept = {};
24626                         for(var i = 0; i < type.length; ++i){
24627                                 this.accept[type[i]] = 1;
24628                         }
24629                 }
24630
24631                 // class-specific variables
24632                 this.isDragging = false;
24633                 this.mouseDown = false;
24634                 this.targetAnchor = null;       // DOMNode corresponding to the currently moused over TreeNode
24635                 this.targetBox = null;  // coordinates of this.targetAnchor
24636                 this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
24637                 this._lastX = 0;
24638                 this._lastY = 0;
24639
24640                 // states
24641                 this.sourceState = "";
24642                 if(this.isSource){
24643                         dojo.addClass(this.node, "dojoDndSource");
24644                 }
24645                 this.targetState = "";
24646                 if(this.accept){
24647                         dojo.addClass(this.node, "dojoDndTarget");
24648                 }
24649
24650                 // set up events
24651                 this.topics = [
24652                         dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
24653                         dojo.subscribe("/dnd/start", this, "onDndStart"),
24654                         dojo.subscribe("/dnd/drop", this, "onDndDrop"),
24655                         dojo.subscribe("/dnd/cancel", this, "onDndCancel")
24656                 ];
24657         },
24658
24659         // methods
24660         checkAcceptance: function(source, nodes){
24661                 // summary:
24662                 //              Checks if the target can accept nodes from this source
24663                 // source: dijit.tree.dndSource
24664                 //              The source which provides items
24665                 // nodes: DOMNode[]
24666                 //              Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
24667                 //              source is a dijit.Tree.
24668                 // tags:
24669                 //              extension
24670                 return true;    // Boolean
24671         },
24672
24673         copyState: function(keyPressed){
24674                 // summary:
24675                 //              Returns true, if we need to copy items, false to move.
24676                 //              It is separated to be overwritten dynamically, if needed.
24677                 // keyPressed: Boolean
24678                 //              The "copy" control key was pressed
24679                 // tags:
24680                 //              protected
24681                 return this.copyOnly || keyPressed;     // Boolean
24682         },
24683         destroy: function(){
24684                 // summary:
24685                 //              Prepares the object to be garbage-collected.
24686                 this.inherited("destroy",arguments);
24687                 dojo.forEach(this.topics, dojo.unsubscribe);
24688                 this.targetAnchor = null;
24689         },
24690
24691         _onDragMouse: function(e){
24692                 // summary:
24693                 //              Helper method for processing onmousemove/onmouseover events while drag is in progress.
24694                 //              Keeps track of current drop target.
24695
24696                 var m = dojo.dnd.manager(),
24697                         oldTarget = this.targetAnchor,                  // the DOMNode corresponding to TreeNode mouse was previously over
24698                         newTarget = this.current,                               // DOMNode corresponding to TreeNode mouse is currently over
24699                         newTargetWidget = this.currentWidget,   // the TreeNode itself
24700                         oldDropPosition = this.dropPosition;    // the previous drop position (over/before/after)
24701
24702                 // calculate if user is indicating to drop the dragged node before, after, or over
24703                 // (i.e., to become a child of) the target node
24704                 var newDropPosition = "Over";
24705                 if(newTarget && this.betweenThreshold > 0){
24706                         // If mouse is over a new TreeNode, then get new TreeNode's position and size
24707                         if(!this.targetBox || oldTarget != newTarget){
24708                                 this.targetBox = dojo.position(newTarget, true);
24709                         }
24710                         if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
24711                                 newDropPosition = "Before";
24712                         }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
24713                                 newDropPosition = "After";
24714                         }
24715                 }
24716
24717                 if(newTarget != oldTarget || newDropPosition != oldDropPosition){
24718                         if(oldTarget){
24719                                 this._removeItemClass(oldTarget, oldDropPosition);
24720                         }
24721                         if(newTarget){
24722                                 this._addItemClass(newTarget, newDropPosition);
24723                         }
24724
24725                         // Check if it's ok to drop the dragged node on/before/after the target node.
24726                         if(!newTarget){
24727                                 m.canDrop(false);
24728                         }else if(newTargetWidget == this.tree.rootNode && newDropPosition != "Over"){
24729                                 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
24730                                 m.canDrop(false);
24731                         }else if(m.source == this && (newTarget.id in this.selection)){
24732                                 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
24733                                 m.canDrop(false);
24734                         }else if(this.checkItemAcceptance(newTarget, m.source, newDropPosition.toLowerCase())
24735                                         && !this._isParentChildDrop(m.source, newTarget)){
24736                                 m.canDrop(true);
24737                         }else{
24738                                 m.canDrop(false);
24739                         }
24740
24741                         this.targetAnchor = newTarget;
24742                         this.dropPosition = newDropPosition;
24743                 }
24744         },
24745
24746         onMouseMove: function(e){
24747                 // summary:
24748                 //              Called for any onmousemove events over the Tree
24749                 // e: Event
24750                 //              onmousemouse event
24751                 // tags:
24752                 //              private
24753                 if(this.isDragging && this.targetState == "Disabled"){ return; }
24754                 this.inherited(arguments);
24755                 var m = dojo.dnd.manager();
24756                 if(this.isDragging){
24757                         this._onDragMouse(e);
24758                 }else{
24759                         if(this.mouseDown && this.isSource &&
24760                                  (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
24761                                 var n = this.getSelectedNodes();
24762                                 var nodes=[];
24763                                 for(var i in n){
24764                                         nodes.push(n[i]);
24765                                 }
24766                                 if(nodes.length){
24767                                         m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
24768                                 }
24769                         }
24770                 }
24771         },
24772
24773         onMouseDown: function(e){
24774                 // summary:
24775                 //              Event processor for onmousedown
24776                 // e: Event
24777                 //              onmousedown event
24778                 // tags:
24779                 //              private
24780                 this.mouseDown = true;
24781                 this.mouseButton = e.button;
24782                 this._lastX = e.pageX;
24783                 this._lastY = e.pageY;
24784                 this.inherited("onMouseDown",arguments);
24785         },
24786
24787         onMouseUp: function(e){
24788                 // summary:
24789                 //              Event processor for onmouseup
24790                 // e: Event
24791                 //              onmouseup event
24792                 // tags:
24793                 //              private
24794                 if(this.mouseDown){
24795                         this.mouseDown = false;
24796                         this.inherited("onMouseUp",arguments);
24797                 }
24798         },
24799
24800         onMouseOut: function(){
24801                 // summary:
24802                 //              Event processor for when mouse is moved away from a TreeNode
24803                 // tags:
24804                 //              private
24805                 this.inherited(arguments);
24806                 this._unmarkTargetAnchor();
24807         },
24808
24809         checkItemAcceptance: function(target, source, position){
24810                 // summary:
24811                 //              Stub function to be overridden if one wants to check for the ability to drop at the node/item level
24812                 // description:
24813                 //              In the base case, this is called to check if target can become a child of source.
24814                 //              When betweenThreshold is set, position="before" or "after" means that we
24815                 //              are asking if the source node can be dropped before/after the target node.
24816                 // target: DOMNode
24817                 //              The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
24818                 //              Use dijit.getEnclosingWidget(target) to get the TreeNode.
24819                 // source: dijit.tree.dndSource
24820                 //              The (set of) nodes we are dropping
24821                 // position: String
24822                 //              "over", "before", or "after"
24823                 // tags:
24824                 //              extension
24825                 return true;
24826         },
24827
24828         // topic event processors
24829         onDndSourceOver: function(source){
24830                 // summary:
24831                 //              Topic event processor for /dnd/source/over, called when detected a current source.
24832                 // source: Object
24833                 //              The dijit.tree.dndSource / dojo.dnd.Source which has the mouse over it
24834                 // tags:
24835                 //              private
24836                 if(this != source){
24837                         this.mouseDown = false;
24838                         this._unmarkTargetAnchor();
24839                 }else if(this.isDragging){
24840                         var m = dojo.dnd.manager();
24841                         m.canDrop(false);
24842                 }
24843         },
24844         onDndStart: function(source, nodes, copy){
24845                 // summary:
24846                 //              Topic event processor for /dnd/start, called to initiate the DnD operation
24847                 // source: Object
24848                 //              The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
24849                 // nodes: DomNode[]
24850                 //              The list of transferred items, dndTreeNode nodes if dragging from a Tree
24851                 // copy: Boolean
24852                 //              Copy items, if true, move items otherwise
24853                 // tags:
24854                 //              private
24855
24856                 if(this.isSource){
24857                         this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
24858                 }
24859                 var accepted = this.checkAcceptance(source, nodes);
24860
24861                 this._changeState("Target", accepted ? "" : "Disabled");
24862
24863                 if(this == source){
24864                         dojo.dnd.manager().overSource(this);
24865                 }
24866
24867                 this.isDragging = true;
24868         },
24869
24870         itemCreator: function(/*DomNode[]*/ nodes, target, /*dojo.dnd.Source*/ source){
24871                 // summary:
24872                 //              Returns objects passed to `Tree.model.newItem()` based on DnD nodes
24873                 //              dropped onto the tree.   Developer must override this method to enable
24874                 //              dropping from external sources onto this Tree, unless the Tree.model's items
24875                 //              happen to look like {id: 123, name: "Apple" } with no other attributes.
24876                 // description:
24877                 //              For each node in nodes[], which came from source, create a hash of name/value
24878                 //              pairs to be passed to Tree.model.newItem().  Returns array of those hashes.
24879                 // returns: Object[]
24880                 //              Array of name/value hashes for each new item to be added to the Tree, like:
24881                 // |    [
24882                 // |            { id: 123, label: "apple", foo: "bar" },
24883                 // |            { id: 456, label: "pear", zaz: "bam" }
24884                 // |    ]
24885                 // tags:
24886                 //              extension
24887
24888                 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
24889                 // make signature itemCreator(sourceItem, node, target) (or similar).
24890
24891                 return dojo.map(nodes, function(node){
24892                         return {
24893                                 "id": node.id,
24894                                 "name": node.textContent || node.innerText || ""
24895                         };
24896                 }); // Object[]
24897         },
24898
24899         onDndDrop: function(source, nodes, copy){
24900                 // summary:
24901                 //              Topic event processor for /dnd/drop, called to finish the DnD operation.
24902                 // description:
24903                 //              Updates data store items according to where node was dragged from and dropped
24904                 //              to.   The tree will then respond to those data store updates and redraw itself.
24905                 // source: Object
24906                 //              The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
24907                 // nodes: DomNode[]
24908                 //              The list of transferred items, dndTreeNode nodes if dragging from a Tree
24909                 // copy: Boolean
24910                 //              Copy items, if true, move items otherwise
24911                 // tags:
24912                 //              protected
24913                 if(this.containerState == "Over"){
24914                         var tree = this.tree,
24915                                 model = tree.model,
24916                                 target = this.targetAnchor,
24917                                 requeryRoot = false;    // set to true iff top level items change
24918
24919                         this.isDragging = false;
24920
24921                         // Compute the new parent item
24922                         var targetWidget = dijit.getEnclosingWidget(target);
24923                         var newParentItem;
24924                         var insertIndex;
24925                         newParentItem = (targetWidget && targetWidget.item) || tree.item;
24926                         if(this.dropPosition == "Before" || this.dropPosition == "After"){
24927                                 // TODO: if there is no parent item then disallow the drop.
24928                                 // Actually this should be checked during onMouseMove too, to make the drag icon red.
24929                                 newParentItem = (targetWidget.getParent() && targetWidget.getParent().item) || tree.item;
24930                                 // Compute the insert index for reordering
24931                                 insertIndex = targetWidget.getIndexInParent();
24932                                 if(this.dropPosition == "After"){
24933                                         insertIndex = targetWidget.getIndexInParent() + 1;
24934                                 }
24935                         }else{
24936                                 newParentItem = (targetWidget && targetWidget.item) || tree.item;
24937                         }
24938
24939                         // If necessary, use this variable to hold array of hashes to pass to model.newItem()
24940                         // (one entry in the array for each dragged node).
24941                         var newItemsParams;
24942
24943                         dojo.forEach(nodes, function(node, idx){
24944                                 // dojo.dnd.Item representing the thing being dropped.
24945                                 // Don't confuse the use of item here (meaning a DnD item) with the
24946                                 // uses below where item means dojo.data item.
24947                                 var sourceItem = source.getItem(node.id);
24948
24949                                 // Information that's available if the source is another Tree
24950                                 // (possibly but not necessarily this tree, possibly but not
24951                                 // necessarily the same model as this Tree)
24952                                 if(dojo.indexOf(sourceItem.type, "treeNode") != -1){
24953                                         var childTreeNode = sourceItem.data,
24954                                                 childItem = childTreeNode.item,
24955                                                 oldParentItem = childTreeNode.getParent().item;
24956                                 }
24957
24958                                 if(source == this){
24959                                         // This is a node from my own tree, and we are moving it, not copying.
24960                                         // Remove item from old parent's children attribute.
24961                                         // TODO: dijit.tree.dndSelector should implement deleteSelectedNodes()
24962                                         // and this code should go there.
24963
24964                                         if(typeof insertIndex == "number"){
24965                                                 if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
24966                                                         insertIndex -= 1;
24967                                                 }
24968                                         }
24969                                         model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
24970                                 }else if(model.isItem(childItem)){
24971                                         // Item from same model
24972                                         // (maybe we should only do this branch if the source is a tree?)
24973                                         model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
24974                                 }else{
24975                                         // Get the hash to pass to model.newItem().  A single call to
24976                                         // itemCreator() returns an array of hashes, one for each drag source node.
24977                                         if(!newItemsParams){
24978                                                 newItemsParams = this.itemCreator(nodes, target, source);
24979                                         }
24980
24981                                         // Create new item in the tree, based on the drag source.
24982                                         model.newItem(newItemsParams[idx], newParentItem, insertIndex);
24983                                 }
24984                         }, this);
24985
24986                         // Expand the target node (if it's currently collapsed) so the user can see
24987                         // where their node was dropped.   In particular since that node is still selected.
24988                         this.tree._expandNode(targetWidget);
24989                 }
24990                 this.onDndCancel();
24991         },
24992
24993         onDndCancel: function(){
24994                 // summary:
24995                 //              Topic event processor for /dnd/cancel, called to cancel the DnD operation
24996                 // tags:
24997                 //              private
24998                 this._unmarkTargetAnchor();
24999                 this.isDragging = false;
25000                 this.mouseDown = false;
25001                 delete this.mouseButton;
25002                 this._changeState("Source", "");
25003                 this._changeState("Target", "");
25004         },
25005
25006         // When focus moves in/out of the entire Tree
25007         onOverEvent: function(){
25008                 // summary:
25009                 //              This method is called when mouse is moved over our container (like onmouseenter)
25010                 // tags:
25011                 //              private
25012                 this.inherited(arguments);
25013                 dojo.dnd.manager().overSource(this);
25014         },
25015         onOutEvent: function(){
25016                 // summary:
25017                 //              This method is called when mouse is moved out of our container (like onmouseleave)
25018                 // tags:
25019                 //              private
25020                 this._unmarkTargetAnchor();
25021                 var m = dojo.dnd.manager();
25022                 if(this.isDragging){
25023                         m.canDrop(false);
25024                 }
25025                 m.outSource(this);
25026
25027                 this.inherited(arguments);
25028         },
25029
25030         _isParentChildDrop: function(source, targetRow){
25031                 // summary:
25032                 //              Checks whether the dragged items are parent rows in the tree which are being
25033                 //              dragged into their own children.
25034                 //
25035                 // source:
25036                 //              The DragSource object.
25037                 //
25038                 // targetRow:
25039                 //              The tree row onto which the dragged nodes are being dropped.
25040                 //
25041                 // tags:
25042                 //              private
25043
25044                 // If the dragged object is not coming from the tree this widget belongs to,
25045                 // it cannot be invalid.
25046                 if(!source.tree || source.tree != this.tree){
25047                         return false;
25048                 }
25049
25050
25051                 var root = source.tree.domNode;
25052                 var ids = {};
25053                 for(var x in source.selection){
25054                         ids[source.selection[x].parentNode.id] = true;
25055                 }
25056
25057                 var node = targetRow.parentNode;
25058
25059                 // Iterate up the DOM hierarchy from the target drop row,
25060                 // checking of any of the dragged nodes have the same ID.
25061                 while(node != root && (!node.id || !ids[node.id])){
25062                         node = node.parentNode;
25063                 }
25064
25065                 return node.id && ids[node.id];
25066         },
25067
25068         _unmarkTargetAnchor: function(){
25069                 // summary:
25070                 //              Removes hover class of the current target anchor
25071                 // tags:
25072                 //              private
25073                 if(!this.targetAnchor){ return; }
25074                 this._removeItemClass(this.targetAnchor, this.dropPosition);
25075                 this.targetAnchor = null;
25076                 this.targetBox = null;
25077                 this.dropPosition = null;
25078         },
25079
25080         _markDndStatus: function(copy){
25081                 // summary:
25082                 //              Changes source's state based on "copy" status
25083                 this._changeState("Source", copy ? "Copied" : "Moved");
25084         }
25085 });
25086
25087 }
25088
25089 if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25090 dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
25091 dojo.provide("dojo.data.ItemFileReadStore");
25092
25093
25094
25095
25096
25097 dojo.declare("dojo.data.ItemFileReadStore", null,{
25098         //      summary:
25099         //              The ItemFileReadStore implements the dojo.data.api.Read API and reads
25100         //              data from JSON files that have contents in this format --
25101         //              { items: [
25102         //                      { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
25103         //                      { name:'Fozzie Bear', wears:['hat', 'tie']},
25104         //                      { name:'Miss Piggy', pets:'Foo-Foo'}
25105         //              ]}
25106         //              Note that it can also contain an 'identifer' property that specified which attribute on the items 
25107         //              in the array of items that acts as the unique identifier for that item.
25108         //
25109         constructor: function(/* Object */ keywordParameters){
25110                 //      summary: constructor
25111                 //      keywordParameters: {url: String}
25112                 //      keywordParameters: {data: jsonObject}
25113                 //      keywordParameters: {typeMap: object)
25114                 //              The structure of the typeMap object is as follows:
25115                 //              {
25116                 //                      type0: function || object,
25117                 //                      type1: function || object,
25118                 //                      ...
25119                 //                      typeN: function || object
25120                 //              }
25121                 //              Where if it is a function, it is assumed to be an object constructor that takes the 
25122                 //              value of _value as the initialization parameters.  If it is an object, then it is assumed
25123                 //              to be an object of general form:
25124                 //              {
25125                 //                      type: function, //constructor.
25126                 //                      deserialize:    function(value) //The function that parses the value and constructs the object defined by type appropriately.
25127                 //              }
25128         
25129                 this._arrayOfAllItems = [];
25130                 this._arrayOfTopLevelItems = [];
25131                 this._loadFinished = false;
25132                 this._jsonFileUrl = keywordParameters.url;
25133                 this._ccUrl = keywordParameters.url;
25134                 this.url = keywordParameters.url;
25135                 this._jsonData = keywordParameters.data;
25136                 this.data = null;
25137                 this._datatypeMap = keywordParameters.typeMap || {};
25138                 if(!this._datatypeMap['Date']){
25139                         //If no default mapping for dates, then set this as default.
25140                         //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
25141                         //of generically representing dates.
25142                         this._datatypeMap['Date'] = {
25143                                                                                         type: Date,
25144                                                                                         deserialize: function(value){
25145                                                                                                 return dojo.date.stamp.fromISOString(value);
25146                                                                                         }
25147                                                                                 };
25148                 }
25149                 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
25150                 this._itemsByIdentity = null;
25151                 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
25152                 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
25153                 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
25154                 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
25155                 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
25156                 this._queuedFetches = [];
25157                 if(keywordParameters.urlPreventCache !== undefined){
25158                         this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
25159                 }
25160                 if(keywordParameters.hierarchical !== undefined){
25161                         this.hierarchical = keywordParameters.hierarchical?true:false;
25162                 }
25163                 if(keywordParameters.clearOnClose){
25164                         this.clearOnClose = true;
25165                 }
25166                 if("failOk" in keywordParameters){
25167                         this.failOk = keywordParameters.failOk?true:false;
25168                 }
25169         },
25170         
25171         url: "",        // use "" rather than undefined for the benefit of the parser (#3539)
25172
25173         //Internal var, crossCheckUrl.  Used so that setting either url or _jsonFileUrl, can still trigger a reload
25174         //when clearOnClose and close is used.
25175         _ccUrl: "",
25176
25177         data: null,     // define this so that the parser can populate it
25178
25179         typeMap: null, //Define so parser can populate.
25180         
25181         //Parameter to allow users to specify if a close call should force a reload or not.
25182         //By default, it retains the old behavior of not clearing if close is called.  But
25183         //if set true, the store will be reset to default state.  Note that by doing this,
25184         //all item handles will become invalid and a new fetch must be issued.
25185         clearOnClose: false,
25186
25187         //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.  
25188         //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
25189         //Added for tracker: #6072
25190         urlPreventCache: false,
25191         
25192         //Parameter for specifying that it is OK for the xhrGet call to fail silently.
25193         failOk: false,
25194
25195         //Parameter to indicate to process data from the url as hierarchical 
25196         //(data items can contain other data items in js form).  Default is true 
25197         //for backwards compatibility.  False means only root items are processed 
25198         //as items, all child objects outside of type-mapped objects and those in 
25199         //specific reference format, are left straight JS data objects.
25200         hierarchical: true,
25201
25202         _assertIsItem: function(/* item */ item){
25203                 //      summary:
25204                 //              This function tests whether the item passed in is indeed an item in the store.
25205                 //      item: 
25206                 //              The item to test for being contained by the store.
25207                 if(!this.isItem(item)){ 
25208                         throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
25209                 }
25210         },
25211
25212         _assertIsAttribute: function(/* attribute-name-string */ attribute){
25213                 //      summary:
25214                 //              This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
25215                 //      attribute: 
25216                 //              The attribute to test for being contained by the store.
25217                 if(typeof attribute !== "string"){ 
25218                         throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
25219                 }
25220         },
25221
25222         getValue: function(     /* item */ item, 
25223                                                 /* attribute-name-string */ attribute, 
25224                                                 /* value? */ defaultValue){
25225                 //      summary: 
25226                 //              See dojo.data.api.Read.getValue()
25227                 var values = this.getValues(item, attribute);
25228                 return (values.length > 0)?values[0]:defaultValue; // mixed
25229         },
25230
25231         getValues: function(/* item */ item, 
25232                                                 /* attribute-name-string */ attribute){
25233                 //      summary: 
25234                 //              See dojo.data.api.Read.getValues()
25235
25236                 this._assertIsItem(item);
25237                 this._assertIsAttribute(attribute);
25238                 // Clone it before returning.  refs: #10474
25239                 return (item[attribute] || []).slice(0); // Array
25240         },
25241
25242         getAttributes: function(/* item */ item){
25243                 //      summary: 
25244                 //              See dojo.data.api.Read.getAttributes()
25245                 this._assertIsItem(item);
25246                 var attributes = [];
25247                 for(var key in item){
25248                         // Save off only the real item attributes, not the special id marks for O(1) isItem.
25249                         if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
25250                                 attributes.push(key);
25251                         }
25252                 }
25253                 return attributes; // Array
25254         },
25255
25256         hasAttribute: function( /* item */ item,
25257                                                         /* attribute-name-string */ attribute){
25258                 //      summary: 
25259                 //              See dojo.data.api.Read.hasAttribute()
25260                 this._assertIsItem(item);
25261                 this._assertIsAttribute(attribute);
25262                 return (attribute in item);
25263         },
25264
25265         containsValue: function(/* item */ item, 
25266                                                         /* attribute-name-string */ attribute, 
25267                                                         /* anything */ value){
25268                 //      summary: 
25269                 //              See dojo.data.api.Read.containsValue()
25270                 var regexp = undefined;
25271                 if(typeof value === "string"){
25272                         regexp = dojo.data.util.filter.patternToRegExp(value, false);
25273                 }
25274                 return this._containsValue(item, attribute, value, regexp); //boolean.
25275         },
25276
25277         _containsValue: function(       /* item */ item, 
25278                                                                 /* attribute-name-string */ attribute, 
25279                                                                 /* anything */ value,
25280                                                                 /* RegExp?*/ regexp){
25281                 //      summary: 
25282                 //              Internal function for looking at the values contained by the item.
25283                 //      description: 
25284                 //              Internal function for looking at the values contained by the item.  This 
25285                 //              function allows for denoting if the comparison should be case sensitive for
25286                 //              strings or not (for handling filtering cases where string case should not matter)
25287                 //      
25288                 //      item:
25289                 //              The data item to examine for attribute values.
25290                 //      attribute:
25291                 //              The attribute to inspect.
25292                 //      value:  
25293                 //              The value to match.
25294                 //      regexp:
25295                 //              Optional regular expression generated off value if value was of string type to handle wildcarding.
25296                 //              If present and attribute values are string, then it can be used for comparison instead of 'value'
25297                 return dojo.some(this.getValues(item, attribute), function(possibleValue){
25298                         if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
25299                                 if(possibleValue.toString().match(regexp)){
25300                                         return true; // Boolean
25301                                 }
25302                         }else if(value === possibleValue){
25303                                 return true; // Boolean
25304                         }
25305                 });
25306         },
25307
25308         isItem: function(/* anything */ something){
25309                 //      summary: 
25310                 //              See dojo.data.api.Read.isItem()
25311                 if(something && something[this._storeRefPropName] === this){
25312                         if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
25313                                 return true;
25314                         }
25315                 }
25316                 return false; // Boolean
25317         },
25318
25319         isItemLoaded: function(/* anything */ something){
25320                 //      summary: 
25321                 //              See dojo.data.api.Read.isItemLoaded()
25322                 return this.isItem(something); //boolean
25323         },
25324
25325         loadItem: function(/* object */ keywordArgs){
25326                 //      summary: 
25327                 //              See dojo.data.api.Read.loadItem()
25328                 this._assertIsItem(keywordArgs.item);
25329         },
25330
25331         getFeatures: function(){
25332                 //      summary: 
25333                 //              See dojo.data.api.Read.getFeatures()
25334                 return this._features; //Object
25335         },
25336
25337         getLabel: function(/* item */ item){
25338                 //      summary: 
25339                 //              See dojo.data.api.Read.getLabel()
25340                 if(this._labelAttr && this.isItem(item)){
25341                         return this.getValue(item,this._labelAttr); //String
25342                 }
25343                 return undefined; //undefined
25344         },
25345
25346         getLabelAttributes: function(/* item */ item){
25347                 //      summary: 
25348                 //              See dojo.data.api.Read.getLabelAttributes()
25349                 if(this._labelAttr){
25350                         return [this._labelAttr]; //array
25351                 }
25352                 return null; //null
25353         },
25354
25355         _fetchItems: function(  /* Object */ keywordArgs, 
25356                                                         /* Function */ findCallback, 
25357                                                         /* Function */ errorCallback){
25358                 //      summary: 
25359                 //              See dojo.data.util.simpleFetch.fetch()
25360                 var self = this,
25361                     filter = function(requestArgs, arrayOfItems){
25362                         var items = [],
25363                             i, key;
25364                         if(requestArgs.query){
25365                                 var value,
25366                                     ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
25367
25368                                 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
25369                                 //same value for each item examined.  Much more efficient.
25370                                 var regexpList = {};
25371                                 for(key in requestArgs.query){
25372                                         value = requestArgs.query[key];
25373                                         if(typeof value === "string"){
25374                                                 regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
25375                                         }else if(value instanceof RegExp){
25376                                                 regexpList[key] = value;
25377                                         }
25378                                 }
25379                                 for(i = 0; i < arrayOfItems.length; ++i){
25380                                         var match = true;
25381                                         var candidateItem = arrayOfItems[i];
25382                                         if(candidateItem === null){
25383                                                 match = false;
25384                                         }else{
25385                                                 for(key in requestArgs.query){
25386                                                         value = requestArgs.query[key];
25387                                                         if(!self._containsValue(candidateItem, key, value, regexpList[key])){
25388                                                                 match = false;
25389                                                         }
25390                                                 }
25391                                         }
25392                                         if(match){
25393                                                 items.push(candidateItem);
25394                                         }
25395                                 }
25396                                 findCallback(items, requestArgs);
25397                         }else{
25398                                 // We want a copy to pass back in case the parent wishes to sort the array. 
25399                                 // We shouldn't allow resort of the internal list, so that multiple callers 
25400                                 // can get lists and sort without affecting each other.  We also need to
25401                                 // filter out any null values that have been left as a result of deleteItem()
25402                                 // calls in ItemFileWriteStore.
25403                                 for(i = 0; i < arrayOfItems.length; ++i){
25404                                         var item = arrayOfItems[i];
25405                                         if(item !== null){
25406                                                 items.push(item);
25407                                         }
25408                                 }
25409                                 findCallback(items, requestArgs);
25410                         }
25411                 };
25412
25413                 if(this._loadFinished){
25414                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
25415                 }else{
25416                         //Do a check on the JsonFileUrl and crosscheck it.
25417                         //If it doesn't match the cross-check, it needs to be updated
25418                         //This allows for either url or _jsonFileUrl to he changed to
25419                         //reset the store load location.  Done this way for backwards 
25420                         //compatibility.  People use _jsonFileUrl (even though officially
25421                         //private.
25422                         if(this._jsonFileUrl !== this._ccUrl){
25423                                 dojo.deprecated("dojo.data.ItemFileReadStore: ", 
25424                                         "To change the url, set the url property of the store," +
25425                                         " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
25426                                 this._ccUrl = this._jsonFileUrl;
25427                                 this.url = this._jsonFileUrl;
25428                         }else if(this.url !== this._ccUrl){
25429                                 this._jsonFileUrl = this.url;
25430                                 this._ccUrl = this.url;
25431                         }
25432
25433                         //See if there was any forced reset of data.
25434                         if(this.data != null && this._jsonData == null){
25435                                 this._jsonData = this.data;
25436                                 this.data = null;
25437                         }
25438
25439                         if(this._jsonFileUrl){
25440                                 //If fetches come in before the loading has finished, but while
25441                                 //a load is in progress, we have to defer the fetching to be 
25442                                 //invoked in the callback.
25443                                 if(this._loadInProgress){
25444                                         this._queuedFetches.push({args: keywordArgs, filter: filter});
25445                                 }else{
25446                                         this._loadInProgress = true;
25447                                         var getArgs = {
25448                                                         url: self._jsonFileUrl, 
25449                                                         handleAs: "json-comment-optional",
25450                                                         preventCache: this.urlPreventCache,
25451                                                         failOk: this.failOk
25452                                                 };
25453                                         var getHandler = dojo.xhrGet(getArgs);
25454                                         getHandler.addCallback(function(data){
25455                                                 try{
25456                                                         self._getItemsFromLoadedData(data);
25457                                                         self._loadFinished = true;
25458                                                         self._loadInProgress = false;
25459                                                         
25460                                                         filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
25461                                                         self._handleQueuedFetches();
25462                                                 }catch(e){
25463                                                         self._loadFinished = true;
25464                                                         self._loadInProgress = false;
25465                                                         errorCallback(e, keywordArgs);
25466                                                 }
25467                                         });
25468                                         getHandler.addErrback(function(error){
25469                                                 self._loadInProgress = false;
25470                                                 errorCallback(error, keywordArgs);
25471                                         });
25472
25473                                         //Wire up the cancel to abort of the request
25474                                         //This call cancel on the deferred if it hasn't been called
25475                                         //yet and then will chain to the simple abort of the
25476                                         //simpleFetch keywordArgs
25477                                         var oldAbort = null;
25478                                         if(keywordArgs.abort){
25479                                                 oldAbort = keywordArgs.abort;
25480                                         }
25481                                         keywordArgs.abort = function(){
25482                                                 var df = getHandler;
25483                                                 if(df && df.fired === -1){
25484                                                         df.cancel();
25485                                                         df = null;
25486                                                 }
25487                                                 if(oldAbort){
25488                                                         oldAbort.call(keywordArgs);
25489                                                 }
25490                                         };
25491                                 }
25492                         }else if(this._jsonData){
25493                                 try{
25494                                         this._loadFinished = true;
25495                                         this._getItemsFromLoadedData(this._jsonData);
25496                                         this._jsonData = null;
25497                                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
25498                                 }catch(e){
25499                                         errorCallback(e, keywordArgs);
25500                                 }
25501                         }else{
25502                                 errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
25503                         }
25504                 }
25505         },
25506
25507         _handleQueuedFetches: function(){
25508                 //      summary: 
25509                 //              Internal function to execute delayed request in the store.
25510                 //Execute any deferred fetches now.
25511                 if(this._queuedFetches.length > 0){
25512                         for(var i = 0; i < this._queuedFetches.length; i++){
25513                                 var fData = this._queuedFetches[i],
25514                                     delayedQuery = fData.args,
25515                                     delayedFilter = fData.filter;
25516                                 if(delayedFilter){
25517                                         delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions)); 
25518                                 }else{
25519                                         this.fetchItemByIdentity(delayedQuery);
25520                                 }
25521                         }
25522                         this._queuedFetches = [];
25523                 }
25524         },
25525
25526         _getItemsArray: function(/*object?*/queryOptions){
25527                 //      summary: 
25528                 //              Internal function to determine which list of items to search over.
25529                 //      queryOptions: The query options parameter, if any.
25530                 if(queryOptions && queryOptions.deep){
25531                         return this._arrayOfAllItems; 
25532                 }
25533                 return this._arrayOfTopLevelItems;
25534         },
25535
25536         close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
25537                  //     summary: 
25538                  //             See dojo.data.api.Read.close()
25539                  if(this.clearOnClose && 
25540                         this._loadFinished && 
25541                         !this._loadInProgress){
25542                          //Reset all internalsback to default state.  This will force a reload
25543                          //on next fetch.  This also checks that the data or url param was set 
25544                          //so that the store knows it can get data.  Without one of those being set,
25545                          //the next fetch will trigger an error.
25546
25547                          if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) && 
25548                                  (this.url == "" || this.url == null)
25549                                 ) && this.data == null){
25550                                  console.debug("dojo.data.ItemFileReadStore: WARNING!  Data reload " +
25551                                         " information has not been provided." + 
25552                                         "  Please set 'url' or 'data' to the appropriate value before" +
25553                                         " the next fetch");
25554                          }
25555                          this._arrayOfAllItems = [];
25556                          this._arrayOfTopLevelItems = [];
25557                          this._loadFinished = false;
25558                          this._itemsByIdentity = null;
25559                          this._loadInProgress = false;
25560                          this._queuedFetches = [];
25561                  }
25562         },
25563
25564         _getItemsFromLoadedData: function(/* Object */ dataObject){
25565                 //      summary:
25566                 //              Function to parse the loaded data into item format and build the internal items array.
25567                 //      description:
25568                 //              Function to parse the loaded data into item format and build the internal items array.
25569                 //
25570                 //      dataObject:
25571                 //              The JS data object containing the raw data to convery into item format.
25572                 //
25573                 //      returns: array
25574                 //              Array of items in store item format.
25575                 
25576                 // First, we define a couple little utility functions...
25577                 var addingArrays = false,
25578                     self = this;
25579                 
25580                 function valueIsAnItem(/* anything */ aValue){
25581                         // summary:
25582                         //              Given any sort of value that could be in the raw json data,
25583                         //              return true if we should interpret the value as being an
25584                         //              item itself, rather than a literal value or a reference.
25585                         // example:
25586                         //      |       false == valueIsAnItem("Kermit");
25587                         //      |       false == valueIsAnItem(42);
25588                         //      |       false == valueIsAnItem(new Date());
25589                         //      |       false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
25590                         //      |       false == valueIsAnItem({_reference:'Kermit'});
25591                         //      |       true == valueIsAnItem({name:'Kermit', color:'green'});
25592                         //      |       true == valueIsAnItem({iggy:'pop'});
25593                         //      |       true == valueIsAnItem({foo:42});
25594                         var isItem = (
25595                                 (aValue !== null) &&
25596                                 (typeof aValue === "object") &&
25597                                 (!dojo.isArray(aValue) || addingArrays) &&
25598                                 (!dojo.isFunction(aValue)) &&
25599                                 (aValue.constructor == Object || dojo.isArray(aValue)) &&
25600                                 (typeof aValue._reference === "undefined") && 
25601                                 (typeof aValue._type === "undefined") && 
25602                                 (typeof aValue._value === "undefined") &&
25603                                 self.hierarchical
25604                         );
25605                         return isItem;
25606                 }
25607                 
25608                 function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
25609                         self._arrayOfAllItems.push(anItem);
25610                         for(var attribute in anItem){
25611                                 var valueForAttribute = anItem[attribute];
25612                                 if(valueForAttribute){
25613                                         if(dojo.isArray(valueForAttribute)){
25614                                                 var valueArray = valueForAttribute;
25615                                                 for(var k = 0; k < valueArray.length; ++k){
25616                                                         var singleValue = valueArray[k];
25617                                                         if(valueIsAnItem(singleValue)){
25618                                                                 addItemAndSubItemsToArrayOfAllItems(singleValue);
25619                                                         }
25620                                                 }
25621                                         }else{
25622                                                 if(valueIsAnItem(valueForAttribute)){
25623                                                         addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
25624                                                 }
25625                                         }
25626                                 }
25627                         }
25628                 }
25629
25630                 this._labelAttr = dataObject.label;
25631
25632                 // We need to do some transformations to convert the data structure
25633                 // that we read from the file into a format that will be convenient
25634                 // to work with in memory.
25635
25636                 // Step 1: Walk through the object hierarchy and build a list of all items
25637                 var i,
25638                     item;
25639                 this._arrayOfAllItems = [];
25640                 this._arrayOfTopLevelItems = dataObject.items;
25641
25642                 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
25643                         item = this._arrayOfTopLevelItems[i];
25644                         if(dojo.isArray(item)){
25645                                 addingArrays = true;
25646                         }
25647                         addItemAndSubItemsToArrayOfAllItems(item);
25648                         item[this._rootItemPropName]=true;
25649                 }
25650
25651                 // Step 2: Walk through all the attribute values of all the items, 
25652                 // and replace single values with arrays.  For example, we change this:
25653                 //              { name:'Miss Piggy', pets:'Foo-Foo'}
25654                 // into this:
25655                 //              { name:['Miss Piggy'], pets:['Foo-Foo']}
25656                 // 
25657                 // We also store the attribute names so we can validate our store  
25658                 // reference and item id special properties for the O(1) isItem
25659                 var allAttributeNames = {},
25660                     key;
25661
25662                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
25663                         item = this._arrayOfAllItems[i];
25664                         for(key in item){
25665                                 if(key !== this._rootItemPropName){
25666                                         var value = item[key];
25667                                         if(value !== null){
25668                                                 if(!dojo.isArray(value)){
25669                                                         item[key] = [value];
25670                                                 }
25671                                         }else{
25672                                                 item[key] = [null];
25673                                         }
25674                                 }
25675                                 allAttributeNames[key]=key;
25676                         }
25677                 }
25678
25679                 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
25680                 // This should go really fast, it will generally never even run the loop.
25681                 while(allAttributeNames[this._storeRefPropName]){
25682                         this._storeRefPropName += "_";
25683                 }
25684                 while(allAttributeNames[this._itemNumPropName]){
25685                         this._itemNumPropName += "_";
25686                 }
25687                 while(allAttributeNames[this._reverseRefMap]){
25688                         this._reverseRefMap += "_";
25689                 }
25690
25691                 // Step 4: Some data files specify an optional 'identifier', which is 
25692                 // the name of an attribute that holds the identity of each item. 
25693                 // If this data file specified an identifier attribute, then build a 
25694                 // hash table of items keyed by the identity of the items.
25695                 var arrayOfValues;
25696
25697                 var identifier = dataObject.identifier;
25698                 if(identifier){
25699                         this._itemsByIdentity = {};
25700                         this._features['dojo.data.api.Identity'] = identifier;
25701                         for(i = 0; i < this._arrayOfAllItems.length; ++i){
25702                                 item = this._arrayOfAllItems[i];
25703                                 arrayOfValues = item[identifier];
25704                                 var identity = arrayOfValues[0];
25705                                 if(!this._itemsByIdentity[identity]){
25706                                         this._itemsByIdentity[identity] = item;
25707                                 }else{
25708                                         if(this._jsonFileUrl){
25709                                                 throw new Error("dojo.data.ItemFileReadStore:  The json data as specified by: [" + this._jsonFileUrl + "] is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
25710                                         }else if(this._jsonData){
25711                                                 throw new Error("dojo.data.ItemFileReadStore:  The json data provided by the creation arguments is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
25712                                         }
25713                                 }
25714                         }
25715                 }else{
25716                         this._features['dojo.data.api.Identity'] = Number;
25717                 }
25718
25719                 // Step 5: Walk through all the items, and set each item's properties 
25720                 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
25721                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
25722                         item = this._arrayOfAllItems[i];
25723                         item[this._storeRefPropName] = this;
25724                         item[this._itemNumPropName] = i;
25725                 }
25726
25727                 // Step 6: We walk through all the attribute values of all the items,
25728                 // looking for type/value literals and item-references.
25729                 //
25730                 // We replace item-references with pointers to items.  For example, we change:
25731                 //              { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
25732                 // into this:
25733                 //              { name:['Kermit'], friends:[miss_piggy] } 
25734                 // (where miss_piggy is the object representing the 'Miss Piggy' item).
25735                 //
25736                 // We replace type/value pairs with typed-literals.  For example, we change:
25737                 //              { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
25738                 // into this:
25739                 //              { name:['Kermit'], born:(new Date('July 18, 1918')) } 
25740                 //
25741                 // We also generate the associate map for all items for the O(1) isItem function.
25742                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
25743                         item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
25744                         for(key in item){
25745                                 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
25746                                 for(var j = 0; j < arrayOfValues.length; ++j){
25747                                         value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
25748                                         if(value !== null && typeof value == "object"){
25749                                                 if(("_type" in value) && ("_value" in value)){
25750                                                         var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
25751                                                         var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
25752                                                         if(!mappingObj){ 
25753                                                                 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
25754                                                         }else if(dojo.isFunction(mappingObj)){
25755                                                                 arrayOfValues[j] = new mappingObj(value._value);
25756                                                         }else if(dojo.isFunction(mappingObj.deserialize)){
25757                                                                 arrayOfValues[j] = mappingObj.deserialize(value._value);
25758                                                         }else{
25759                                                                 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
25760                                                         }
25761                                                 }
25762                                                 if(value._reference){
25763                                                         var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
25764                                                         if(!dojo.isObject(referenceDescription)){
25765                                                                 // example: 'Miss Piggy'
25766                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
25767                                                                 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
25768                                                         }else{
25769                                                                 // example: {name:'Miss Piggy'}
25770                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
25771                                                                 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
25772                                                                         var candidateItem = this._arrayOfAllItems[k],
25773                                                                             found = true;
25774                                                                         for(var refKey in referenceDescription){
25775                                                                                 if(candidateItem[refKey] != referenceDescription[refKey]){ 
25776                                                                                         found = false; 
25777                                                                                 }
25778                                                                         }
25779                                                                         if(found){ 
25780                                                                                 arrayOfValues[j] = candidateItem; 
25781                                                                         }
25782                                                                 }
25783                                                         }
25784                                                         if(this.referenceIntegrity){
25785                                                                 var refItem = arrayOfValues[j];
25786                                                                 if(this.isItem(refItem)){
25787                                                                         this._addReferenceToMap(refItem, item, key);
25788                                                                 }
25789                                                         }
25790                                                 }else if(this.isItem(value)){
25791                                                         //It's a child item (not one referenced through _reference).  
25792                                                         //We need to treat this as a referenced item, so it can be cleaned up
25793                                                         //in a write store easily.
25794                                                         if(this.referenceIntegrity){
25795                                                                 this._addReferenceToMap(value, item, key);
25796                                                         }
25797                                                 }
25798                                         }
25799                                 }
25800                         }
25801                 }
25802         },
25803
25804         _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
25805                  //     summary:
25806                  //             Method to add an reference map entry for an item and attribute.
25807                  //     description:
25808                  //             Method to add an reference map entry for an item and attribute.                  //
25809                  //     refItem:
25810                  //             The item that is referenced.
25811                  //     parentItem:
25812                  //             The item that holds the new reference to refItem.
25813                  //     attribute:
25814                  //             The attribute on parentItem that contains the new reference.
25815                  
25816                  //Stub function, does nothing.  Real processing is in ItemFileWriteStore.
25817         },
25818
25819         getIdentity: function(/* item */ item){
25820                 //      summary: 
25821                 //              See dojo.data.api.Identity.getIdentity()
25822                 var identifier = this._features['dojo.data.api.Identity'];
25823                 if(identifier === Number){
25824                         return item[this._itemNumPropName]; // Number
25825                 }else{
25826                         var arrayOfValues = item[identifier];
25827                         if(arrayOfValues){
25828                                 return arrayOfValues[0]; // Object || String
25829                         }
25830                 }
25831                 return null; // null
25832         },
25833
25834         fetchItemByIdentity: function(/* Object */ keywordArgs){
25835                 //      summary: 
25836                 //              See dojo.data.api.Identity.fetchItemByIdentity()
25837
25838                 // Hasn't loaded yet, we have to trigger the load.
25839                 var item,
25840                     scope;
25841                 if(!this._loadFinished){
25842                         var self = this;
25843                         //Do a check on the JsonFileUrl and crosscheck it.
25844                         //If it doesn't match the cross-check, it needs to be updated
25845                         //This allows for either url or _jsonFileUrl to he changed to
25846                         //reset the store load location.  Done this way for backwards 
25847                         //compatibility.  People use _jsonFileUrl (even though officially
25848                         //private.
25849                         if(this._jsonFileUrl !== this._ccUrl){
25850                                 dojo.deprecated("dojo.data.ItemFileReadStore: ", 
25851                                         "To change the url, set the url property of the store," +
25852                                         " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
25853                                 this._ccUrl = this._jsonFileUrl;
25854                                 this.url = this._jsonFileUrl;
25855                         }else if(this.url !== this._ccUrl){
25856                                 this._jsonFileUrl = this.url;
25857                                 this._ccUrl = this.url;
25858                         }
25859                         
25860                         //See if there was any forced reset of data.
25861                         if(this.data != null && this._jsonData == null){
25862                                 this._jsonData = this.data;
25863                                 this.data = null;
25864                         }
25865
25866                         if(this._jsonFileUrl){
25867
25868                                 if(this._loadInProgress){
25869                                         this._queuedFetches.push({args: keywordArgs});
25870                                 }else{
25871                                         this._loadInProgress = true;
25872                                         var getArgs = {
25873                                                         url: self._jsonFileUrl, 
25874                                                         handleAs: "json-comment-optional",
25875                                                         preventCache: this.urlPreventCache,
25876                                                         failOk: this.failOk
25877                                         };
25878                                         var getHandler = dojo.xhrGet(getArgs);
25879                                         getHandler.addCallback(function(data){
25880                                                 var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
25881                                                 try{
25882                                                         self._getItemsFromLoadedData(data);
25883                                                         self._loadFinished = true;
25884                                                         self._loadInProgress = false;
25885                                                         item = self._getItemByIdentity(keywordArgs.identity);
25886                                                         if(keywordArgs.onItem){
25887                                                                 keywordArgs.onItem.call(scope, item);
25888                                                         }
25889                                                         self._handleQueuedFetches();
25890                                                 }catch(error){
25891                                                         self._loadInProgress = false;
25892                                                         if(keywordArgs.onError){
25893                                                                 keywordArgs.onError.call(scope, error);
25894                                                         }
25895                                                 }
25896                                         });
25897                                         getHandler.addErrback(function(error){
25898                                                 self._loadInProgress = false;
25899                                                 if(keywordArgs.onError){
25900                                                         var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
25901                                                         keywordArgs.onError.call(scope, error);
25902                                                 }
25903                                         });
25904                                 }
25905
25906                         }else if(this._jsonData){
25907                                 // Passed in data, no need to xhr.
25908                                 self._getItemsFromLoadedData(self._jsonData);
25909                                 self._jsonData = null;
25910                                 self._loadFinished = true;
25911                                 item = self._getItemByIdentity(keywordArgs.identity);
25912                                 if(keywordArgs.onItem){
25913                                         scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
25914                                         keywordArgs.onItem.call(scope, item);
25915                                 }
25916                         } 
25917                 }else{
25918                         // Already loaded.  We can just look it up and call back.
25919                         item = this._getItemByIdentity(keywordArgs.identity);
25920                         if(keywordArgs.onItem){
25921                                 scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
25922                                 keywordArgs.onItem.call(scope, item);
25923                         }
25924                 }
25925         },
25926
25927         _getItemByIdentity: function(/* Object */ identity){
25928                 //      summary:
25929                 //              Internal function to look an item up by its identity map.
25930                 var item = null;
25931                 if(this._itemsByIdentity){
25932                         item = this._itemsByIdentity[identity];
25933                 }else{
25934                         item = this._arrayOfAllItems[identity];
25935                 }
25936                 if(item === undefined){
25937                         item = null;
25938                 }
25939                 return item; // Object
25940         },
25941
25942         getIdentityAttributes: function(/* item */ item){
25943                 //      summary: 
25944                 //              See dojo.data.api.Identity.getIdentifierAttributes()
25945                  
25946                 var identifier = this._features['dojo.data.api.Identity'];
25947                 if(identifier === Number){
25948                         // If (identifier === Number) it means getIdentity() just returns
25949                         // an integer item-number for each item.  The dojo.data.api.Identity
25950                         // spec says we need to return null if the identity is not composed 
25951                         // of attributes 
25952                         return null; // null
25953                 }else{
25954                         return [identifier]; // Array
25955                 }
25956         },
25957         
25958         _forceLoad: function(){
25959                 //      summary: 
25960                 //              Internal function to force a load of the store if it hasn't occurred yet.  This is required
25961                 //              for specific functions to work properly.  
25962                 var self = this;
25963                 //Do a check on the JsonFileUrl and crosscheck it.
25964                 //If it doesn't match the cross-check, it needs to be updated
25965                 //This allows for either url or _jsonFileUrl to he changed to
25966                 //reset the store load location.  Done this way for backwards 
25967                 //compatibility.  People use _jsonFileUrl (even though officially
25968                 //private.
25969                 if(this._jsonFileUrl !== this._ccUrl){
25970                         dojo.deprecated("dojo.data.ItemFileReadStore: ", 
25971                                 "To change the url, set the url property of the store," +
25972                                 " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
25973                         this._ccUrl = this._jsonFileUrl;
25974                         this.url = this._jsonFileUrl;
25975                 }else if(this.url !== this._ccUrl){
25976                         this._jsonFileUrl = this.url;
25977                         this._ccUrl = this.url;
25978                 }
25979
25980                 //See if there was any forced reset of data.
25981                 if(this.data != null && this._jsonData == null){
25982                         this._jsonData = this.data;
25983                         this.data = null;
25984                 }
25985
25986                 if(this._jsonFileUrl){
25987                                 var getArgs = {
25988                                         url: this._jsonFileUrl, 
25989                                         handleAs: "json-comment-optional",
25990                                         preventCache: this.urlPreventCache,
25991                                         failOk: this.failOk,
25992                                         sync: true
25993                                 };
25994                         var getHandler = dojo.xhrGet(getArgs);
25995                         getHandler.addCallback(function(data){
25996                                 try{
25997                                         //Check to be sure there wasn't another load going on concurrently 
25998                                         //So we don't clobber data that comes in on it.  If there is a load going on
25999                                         //then do not save this data.  It will potentially clobber current data.
26000                                         //We mainly wanted to sync/wait here.
26001                                         //TODO:  Revisit the loading scheme of this store to improve multi-initial
26002                                         //request handling.
26003                                         if(self._loadInProgress !== true && !self._loadFinished){
26004                                                 self._getItemsFromLoadedData(data);
26005                                                 self._loadFinished = true;
26006                                         }else if(self._loadInProgress){
26007                                                 //Okay, we hit an error state we can't recover from.  A forced load occurred
26008                                                 //while an async load was occurring.  Since we cannot block at this point, the best
26009                                                 //that can be managed is to throw an error.
26010                                                 throw new Error("dojo.data.ItemFileReadStore:  Unable to perform a synchronous load, an async load is in progress."); 
26011                                         }
26012                                 }catch(e){
26013                                         console.log(e);
26014                                         throw e;
26015                                 }
26016                         });
26017                         getHandler.addErrback(function(error){
26018                                 throw error;
26019                         });
26020                 }else if(this._jsonData){
26021                         self._getItemsFromLoadedData(self._jsonData);
26022                         self._jsonData = null;
26023                         self._loadFinished = true;
26024                 } 
26025         }
26026 });
26027 //Mix in the simple fetch implementation to this class.
26028 dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
26029
26030 }
26031
26032 if(!dojo._hasResource["dojo.data.ItemFileWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
26033 dojo._hasResource["dojo.data.ItemFileWriteStore"] = true;
26034 dojo.provide("dojo.data.ItemFileWriteStore");
26035
26036
26037 dojo.declare("dojo.data.ItemFileWriteStore", dojo.data.ItemFileReadStore, {
26038         constructor: function(/* object */ keywordParameters){
26039                 //      keywordParameters: {typeMap: object)
26040                 //              The structure of the typeMap object is as follows:
26041                 //              {
26042                 //                      type0: function || object,
26043                 //                      type1: function || object,
26044                 //                      ...
26045                 //                      typeN: function || object
26046                 //              }
26047                 //              Where if it is a function, it is assumed to be an object constructor that takes the 
26048                 //              value of _value as the initialization parameters.  It is serialized assuming object.toString()
26049                 //              serialization.  If it is an object, then it is assumed
26050                 //              to be an object of general form:
26051                 //              {
26052                 //                      type: function, //constructor.
26053                 //                      deserialize:    function(value) //The function that parses the value and constructs the object defined by type appropriately.
26054                 //                      serialize:      function(object) //The function that converts the object back into the proper file format form.
26055                 //              }
26056
26057                 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
26058                 this._features['dojo.data.api.Write'] = true;
26059                 this._features['dojo.data.api.Notification'] = true;
26060                 
26061                 // For keeping track of changes so that we can implement isDirty and revert
26062                 this._pending = {
26063                         _newItems:{}, 
26064                         _modifiedItems:{}, 
26065                         _deletedItems:{}
26066                 };
26067
26068                 if(!this._datatypeMap['Date'].serialize){
26069                         this._datatypeMap['Date'].serialize = function(obj){
26070                                 return dojo.date.stamp.toISOString(obj, {zulu:true});
26071                         };
26072                 }
26073                 //Disable only if explicitly set to false.
26074                 if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
26075                         this.referenceIntegrity = false;
26076                 }
26077
26078                 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
26079                 this._saveInProgress = false;
26080         },
26081
26082         referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking.  This way it can also be disabled pogrammatially or declaratively.
26083
26084         _assert: function(/* boolean */ condition){
26085                 if(!condition){
26086                         throw new Error("assertion failed in ItemFileWriteStore");
26087                 }
26088         },
26089
26090         _getIdentifierAttribute: function(){
26091                 var identifierAttribute = this.getFeatures()['dojo.data.api.Identity'];
26092                 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
26093                 return identifierAttribute;
26094         },
26095         
26096         
26097 /* dojo.data.api.Write */
26098
26099         newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
26100                 // summary: See dojo.data.api.Write.newItem()
26101
26102                 this._assert(!this._saveInProgress);
26103
26104                 if(!this._loadFinished){
26105                         // We need to do this here so that we'll be able to find out what
26106                         // identifierAttribute was specified in the data file.
26107                         this._forceLoad();
26108                 }
26109
26110                 if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
26111                         throw new Error("newItem() was passed something other than an object");
26112                 }
26113                 var newIdentity = null;
26114                 var identifierAttribute = this._getIdentifierAttribute();
26115                 if(identifierAttribute === Number){
26116                         newIdentity = this._arrayOfAllItems.length;
26117                 }else{
26118                         newIdentity = keywordArgs[identifierAttribute];
26119                         if(typeof newIdentity === "undefined"){
26120                                 throw new Error("newItem() was not passed an identity for the new item");
26121                         }
26122                         if(dojo.isArray(newIdentity)){
26123                                 throw new Error("newItem() was not passed an single-valued identity");
26124                         }
26125                 }
26126                 
26127                 // make sure this identity is not already in use by another item, if identifiers were 
26128                 // defined in the file.  Otherwise it would be the item count, 
26129                 // which should always be unique in this case.
26130                 if(this._itemsByIdentity){
26131                         this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
26132                 }
26133                 this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
26134                 this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
26135                 
26136                 var newItem = {};
26137                 newItem[this._storeRefPropName] = this;         
26138                 newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
26139                 if(this._itemsByIdentity){
26140                         this._itemsByIdentity[newIdentity] = newItem;
26141                         //We have to set the identifier now, otherwise we can't look it
26142                         //up at calls to setValueorValues in parentInfo handling.
26143                         newItem[identifierAttribute] = [newIdentity];
26144                 }
26145                 this._arrayOfAllItems.push(newItem);
26146
26147                 //We need to construct some data for the onNew call too...
26148                 var pInfo = null;
26149                 
26150                 // Now we need to check to see where we want to assign this thingm if any.
26151                 if(parentInfo && parentInfo.parent && parentInfo.attribute){
26152                         pInfo = {
26153                                 item: parentInfo.parent,
26154                                 attribute: parentInfo.attribute,
26155                                 oldValue: undefined
26156                         };
26157
26158                         //See if it is multi-valued or not and handle appropriately
26159                         //Generally, all attributes are multi-valued for this store
26160                         //So, we only need to append if there are already values present.
26161                         var values = this.getValues(parentInfo.parent, parentInfo.attribute);
26162                         if(values && values.length > 0){
26163                                 var tempValues = values.slice(0, values.length);
26164                                 if(values.length === 1){
26165                                         pInfo.oldValue = values[0];
26166                                 }else{
26167                                         pInfo.oldValue = values.slice(0, values.length);
26168                                 }
26169                                 tempValues.push(newItem);
26170                                 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
26171                                 pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
26172                         }else{
26173                                 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
26174                                 pInfo.newValue = newItem;
26175                         }
26176                 }else{
26177                         //Toplevel item, add to both top list as well as all list.
26178                         newItem[this._rootItemPropName]=true;
26179                         this._arrayOfTopLevelItems.push(newItem);
26180                 }
26181                 
26182                 this._pending._newItems[newIdentity] = newItem;
26183                 
26184                 //Clone over the properties to the new item
26185                 for(var key in keywordArgs){
26186                         if(key === this._storeRefPropName || key === this._itemNumPropName){
26187                                 // Bummer, the user is trying to do something like
26188                                 // newItem({_S:"foo"}).  Unfortunately, our superclass,
26189                                 // ItemFileReadStore, is already using _S in each of our items
26190                                 // to hold private info.  To avoid a naming collision, we 
26191                                 // need to move all our private info to some other property 
26192                                 // of all the items/objects.  So, we need to iterate over all
26193                                 // the items and do something like: 
26194                                 //    item.__S = item._S;
26195                                 //    item._S = undefined;
26196                                 // But first we have to make sure the new "__S" variable is 
26197                                 // not in use, which means we have to iterate over all the 
26198                                 // items checking for that.
26199                                 throw new Error("encountered bug in ItemFileWriteStore.newItem");
26200                         }
26201                         var value = keywordArgs[key];
26202                         if(!dojo.isArray(value)){
26203                                 value = [value];
26204                         }
26205                         newItem[key] = value;
26206                         if(this.referenceIntegrity){
26207                                 for(var i = 0; i < value.length; i++){
26208                                         var val = value[i];
26209                                         if(this.isItem(val)){
26210                                                 this._addReferenceToMap(val, newItem, key);
26211                                         }
26212                                 }
26213                         }
26214                 }
26215                 this.onNew(newItem, pInfo); // dojo.data.api.Notification call
26216                 return newItem; // item
26217         },
26218         
26219         _removeArrayElement: function(/* Array */ array, /* anything */ element){
26220                 var index = dojo.indexOf(array, element);
26221                 if(index != -1){
26222                         array.splice(index, 1);
26223                         return true;
26224                 }
26225                 return false;
26226         },
26227         
26228         deleteItem: function(/* item */ item){
26229                 // summary: See dojo.data.api.Write.deleteItem()
26230                 this._assert(!this._saveInProgress);
26231                 this._assertIsItem(item);
26232
26233                 // Remove this item from the _arrayOfAllItems, but leave a null value in place
26234                 // of the item, so as not to change the length of the array, so that in newItem() 
26235                 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
26236                 var indexInArrayOfAllItems = item[this._itemNumPropName];
26237                 var identity = this.getIdentity(item);
26238
26239                 //If we have reference integrity on, we need to do reference cleanup for the deleted item
26240                 if(this.referenceIntegrity){
26241                         //First scan all the attributes of this items for references and clean them up in the map 
26242                         //As this item is going away, no need to track its references anymore.
26243
26244                         //Get the attributes list before we generate the backup so it 
26245                         //doesn't pollute the attributes list.
26246                         var attributes = this.getAttributes(item);
26247
26248                         //Backup the map, we'll have to restore it potentially, in a revert.
26249                         if(item[this._reverseRefMap]){
26250                                 item["backup_" + this._reverseRefMap] = dojo.clone(item[this._reverseRefMap]);
26251                         }
26252                         
26253                         //TODO:  This causes a reversion problem.  This list won't be restored on revert since it is
26254                         //attached to the 'value'. item, not ours.  Need to back tese up somehow too.
26255                         //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
26256                         //later.  Or just record them and call _addReferenceToMap on them in revert.
26257                         dojo.forEach(attributes, function(attribute){
26258                                 dojo.forEach(this.getValues(item, attribute), function(value){
26259                                         if(this.isItem(value)){
26260                                                 //We have to back up all the references we had to others so they can be restored on a revert.
26261                                                 if(!item["backupRefs_" + this._reverseRefMap]){
26262                                                         item["backupRefs_" + this._reverseRefMap] = [];
26263                                                 }
26264                                                 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
26265                                                 this._removeReferenceFromMap(value, item, attribute);
26266                                         }
26267                                 }, this);
26268                         }, this);
26269
26270                         //Next, see if we have references to this item, if we do, we have to clean them up too.
26271                         var references = item[this._reverseRefMap];
26272                         if(references){
26273                                 //Look through all the items noted as references to clean them up.
26274                                 for(var itemId in references){
26275                                         var containingItem = null;
26276                                         if(this._itemsByIdentity){
26277                                                 containingItem = this._itemsByIdentity[itemId];
26278                                         }else{
26279                                                 containingItem = this._arrayOfAllItems[itemId];
26280                                         }
26281                                         //We have a reference to a containing item, now we have to process the
26282                                         //attributes and clear all references to the item being deleted.
26283                                         if(containingItem){
26284                                                 for(var attribute in references[itemId]){
26285                                                         var oldValues = this.getValues(containingItem, attribute) || [];
26286                                                         var newValues = dojo.filter(oldValues, function(possibleItem){
26287                                                                 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
26288                                                         }, this);
26289                                                         //Remove the note of the reference to the item and set the values on the modified attribute.
26290                                                         this._removeReferenceFromMap(item, containingItem, attribute); 
26291                                                         if(newValues.length < oldValues.length){
26292                                                                 this._setValueOrValues(containingItem, attribute, newValues, true);
26293                                                         }
26294                                                 }
26295                                         }
26296                                 }
26297                         }
26298                 }
26299
26300                 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
26301
26302                 item[this._storeRefPropName] = null;
26303                 if(this._itemsByIdentity){
26304                         delete this._itemsByIdentity[identity];
26305                 }
26306                 this._pending._deletedItems[identity] = item;
26307                 
26308                 //Remove from the toplevel items, if necessary...
26309                 if(item[this._rootItemPropName]){
26310                         this._removeArrayElement(this._arrayOfTopLevelItems, item);
26311                 }
26312                 this.onDelete(item); // dojo.data.api.Notification call
26313                 return true;
26314         },
26315
26316         setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
26317                 // summary: See dojo.data.api.Write.set()
26318                 return this._setValueOrValues(item, attribute, value, true); // boolean
26319         },
26320         
26321         setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
26322                 // summary: See dojo.data.api.Write.setValues()
26323                 return this._setValueOrValues(item, attribute, values, true); // boolean
26324         },
26325         
26326         unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
26327                 // summary: See dojo.data.api.Write.unsetAttribute()
26328                 return this._setValueOrValues(item, attribute, [], true);
26329         },
26330         
26331         _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
26332                 this._assert(!this._saveInProgress);
26333                 
26334                 // Check for valid arguments
26335                 this._assertIsItem(item);
26336                 this._assert(dojo.isString(attribute));
26337                 this._assert(typeof newValueOrValues !== "undefined");
26338
26339                 // Make sure the user isn't trying to change the item's identity
26340                 var identifierAttribute = this._getIdentifierAttribute();
26341                 if(attribute == identifierAttribute){
26342                         throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
26343                 }
26344
26345                 // To implement the Notification API, we need to make a note of what
26346                 // the old attribute value was, so that we can pass that info when
26347                 // we call the onSet method.
26348                 var oldValueOrValues = this._getValueOrValues(item, attribute);
26349
26350                 var identity = this.getIdentity(item);
26351                 if(!this._pending._modifiedItems[identity]){
26352                         // Before we actually change the item, we make a copy of it to 
26353                         // record the original state, so that we'll be able to revert if 
26354                         // the revert method gets called.  If the item has already been
26355                         // modified then there's no need to do this now, since we already
26356                         // have a record of the original state.                                         
26357                         var copyOfItemState = {};
26358                         for(var key in item){
26359                                 if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
26360                                         copyOfItemState[key] = item[key];
26361                                 }else if(key === this._reverseRefMap){
26362                                         copyOfItemState[key] = dojo.clone(item[key]);
26363                                 }else{
26364                                         copyOfItemState[key] = item[key].slice(0, item[key].length);
26365                                 }
26366                         }
26367                         // Now mark the item as dirty, and save the copy of the original state
26368                         this._pending._modifiedItems[identity] = copyOfItemState;
26369                 }
26370                 
26371                 // Okay, now we can actually change this attribute on the item
26372                 var success = false;
26373                 
26374                 if(dojo.isArray(newValueOrValues) && newValueOrValues.length === 0){
26375                         
26376                         // If we were passed an empty array as the value, that counts
26377                         // as "unsetting" the attribute, so we need to remove this 
26378                         // attribute from the item.
26379                         success = delete item[attribute];
26380                         newValueOrValues = undefined; // used in the onSet Notification call below
26381
26382                         if(this.referenceIntegrity && oldValueOrValues){
26383                                 var oldValues = oldValueOrValues;
26384                                 if(!dojo.isArray(oldValues)){
26385                                         oldValues = [oldValues];
26386                                 }
26387                                 for(var i = 0; i < oldValues.length; i++){
26388                                         var value = oldValues[i];
26389                                         if(this.isItem(value)){
26390                                                 this._removeReferenceFromMap(value, item, attribute);
26391                                         }
26392                                 }
26393                         }
26394                 }else{
26395                         var newValueArray;
26396                         if(dojo.isArray(newValueOrValues)){
26397                                 var newValues = newValueOrValues;
26398                                 // Unfortunately, it's not safe to just do this:
26399                                 //    newValueArray = newValues;
26400                                 // Instead, we need to copy the array, which slice() does very nicely.
26401                                 // This is so that our internal data structure won't  
26402                                 // get corrupted if the user mucks with the values array *after*
26403                                 // calling setValues().
26404                                 newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
26405                         }else{
26406                                 newValueArray = [newValueOrValues];
26407                         }
26408
26409                         //We need to handle reference integrity if this is on. 
26410                         //In the case of set, we need to see if references were added or removed
26411                         //and update the reference tracking map accordingly.
26412                         if(this.referenceIntegrity){
26413                                 if(oldValueOrValues){
26414                                         var oldValues = oldValueOrValues;
26415                                         if(!dojo.isArray(oldValues)){
26416                                                 oldValues = [oldValues];
26417                                         }
26418                                         //Use an associative map to determine what was added/removed from the list.
26419                                         //Should be O(n) performant.  First look at all the old values and make a list of them
26420                                         //Then for any item not in the old list, we add it.  If it was already present, we remove it.
26421                                         //Then we pass over the map and any references left it it need to be removed (IE, no match in
26422                                         //the new values list).
26423                                         var map = {};
26424                                         dojo.forEach(oldValues, function(possibleItem){
26425                                                 if(this.isItem(possibleItem)){
26426                                                         var id = this.getIdentity(possibleItem);
26427                                                         map[id.toString()] = true;
26428                                                 }
26429                                         }, this);
26430                                         dojo.forEach(newValueArray, function(possibleItem){
26431                                                 if(this.isItem(possibleItem)){
26432                                                         var id = this.getIdentity(possibleItem);
26433                                                         if(map[id.toString()]){
26434                                                                 delete map[id.toString()];
26435                                                         }else{
26436                                                                 this._addReferenceToMap(possibleItem, item, attribute); 
26437                                                         }
26438                                                 }
26439                                         }, this);
26440                                         for(var rId in map){
26441                                                 var removedItem;
26442                                                 if(this._itemsByIdentity){
26443                                                         removedItem = this._itemsByIdentity[rId];
26444                                                 }else{
26445                                                         removedItem = this._arrayOfAllItems[rId];
26446                                                 }
26447                                                 this._removeReferenceFromMap(removedItem, item, attribute);
26448                                         }
26449                                 }else{
26450                                         //Everything is new (no old values) so we have to just
26451                                         //insert all the references, if any.
26452                                         for(var i = 0; i < newValueArray.length; i++){
26453                                                 var value = newValueArray[i];
26454                                                 if(this.isItem(value)){
26455                                                         this._addReferenceToMap(value, item, attribute);
26456                                                 }
26457                                         }
26458                                 }
26459                         }
26460                         item[attribute] = newValueArray;
26461                         success = true;
26462                 }
26463
26464                 // Now we make the dojo.data.api.Notification call
26465                 if(callOnSet){
26466                         this.onSet(item, attribute, oldValueOrValues, newValueOrValues); 
26467                 }
26468                 return success; // boolean
26469         },
26470
26471         _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
26472                 //      summary:
26473                 //              Method to add an reference map entry for an item and attribute.
26474                 //      description:
26475                 //              Method to add an reference map entry for an item and attribute.                  //
26476                 //      refItem:
26477                 //              The item that is referenced.
26478                 //      parentItem:
26479                 //              The item that holds the new reference to refItem.
26480                 //      attribute:
26481                 //              The attribute on parentItem that contains the new reference.
26482                  
26483                 var parentId = this.getIdentity(parentItem);
26484                 var references = refItem[this._reverseRefMap];
26485
26486                 if(!references){
26487                         references = refItem[this._reverseRefMap] = {};
26488                 }
26489                 var itemRef = references[parentId];
26490                 if(!itemRef){
26491                         itemRef = references[parentId] = {};
26492                 }
26493                 itemRef[attribute] = true;
26494         },
26495
26496         _removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /*strin*/ attribute){
26497                 //      summary:
26498                 //              Method to remove an reference map entry for an item and attribute.
26499                 //      description:
26500                 //              Method to remove an reference map entry for an item and attribute.  This will
26501                 //              also perform cleanup on the map such that if there are no more references at all to 
26502                 //              the item, its reference object and entry are removed.
26503                 //
26504                 //      refItem:
26505                 //              The item that is referenced.
26506                 //      parentItem:
26507                 //              The item holding a reference to refItem.
26508                 //      attribute:
26509                 //              The attribute on parentItem that contains the reference.
26510                 var identity = this.getIdentity(parentItem);
26511                 var references = refItem[this._reverseRefMap];
26512                 var itemId;
26513                 if(references){
26514                         for(itemId in references){
26515                                 if(itemId == identity){
26516                                         delete references[itemId][attribute];
26517                                         if(this._isEmpty(references[itemId])){
26518                                                 delete references[itemId];
26519                                         }
26520                                 }
26521                         }
26522                         if(this._isEmpty(references)){
26523                                 delete refItem[this._reverseRefMap];
26524                         }
26525                 }
26526         },
26527
26528         _dumpReferenceMap: function(){
26529                 //      summary:
26530                 //              Function to dump the reverse reference map of all items in the store for debug purposes.
26531                 //      description:
26532                 //              Function to dump the reverse reference map of all items in the store for debug purposes.
26533                 var i;
26534                 for(i = 0; i < this._arrayOfAllItems.length; i++){
26535                         var item = this._arrayOfAllItems[i];
26536                         if(item && item[this._reverseRefMap]){
26537                                 console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + dojo.toJson(item[this._reverseRefMap]));
26538                         }
26539                 }
26540         },
26541         
26542         _getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
26543                 var valueOrValues = undefined;
26544                 if(this.hasAttribute(item, attribute)){
26545                         var valueArray = this.getValues(item, attribute);
26546                         if(valueArray.length == 1){
26547                                 valueOrValues = valueArray[0];
26548                         }else{
26549                                 valueOrValues = valueArray;
26550                         }
26551                 }
26552                 return valueOrValues;
26553         },
26554         
26555         _flatten: function(/* anything */ value){
26556                 if(this.isItem(value)){
26557                         var item = value;
26558                         // Given an item, return an serializable object that provides a 
26559                         // reference to the item.
26560                         // For example, given kermit:
26561                         //    var kermit = store.newItem({id:2, name:"Kermit"});
26562                         // we want to return
26563                         //    {_reference:2}
26564                         var identity = this.getIdentity(item);
26565                         var referenceObject = {_reference: identity};
26566                         return referenceObject;
26567                 }else{
26568                         if(typeof value === "object"){
26569                                 for(var type in this._datatypeMap){
26570                                         var typeMap = this._datatypeMap[type];
26571                                         if(dojo.isObject(typeMap) && !dojo.isFunction(typeMap)){
26572                                                 if(value instanceof typeMap.type){
26573                                                         if(!typeMap.serialize){
26574                                                                 throw new Error("ItemFileWriteStore:  No serializer defined for type mapping: [" + type + "]");
26575                                                         }
26576                                                         return {_type: type, _value: typeMap.serialize(value)};
26577                                                 }
26578                                         } else if(value instanceof typeMap){
26579                                                 //SImple mapping, therefore, return as a toString serialization.
26580                                                 return {_type: type, _value: value.toString()};
26581                                         }
26582                                 }
26583                         }
26584                         return value;
26585                 }
26586         },
26587         
26588         _getNewFileContentString: function(){
26589                 // summary: 
26590                 //              Generate a string that can be saved to a file.
26591                 //              The result should look similar to:
26592                 //              http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
26593                 var serializableStructure = {};
26594                 
26595                 var identifierAttribute = this._getIdentifierAttribute();
26596                 if(identifierAttribute !== Number){
26597                         serializableStructure.identifier = identifierAttribute;
26598                 }
26599                 if(this._labelAttr){
26600                         serializableStructure.label = this._labelAttr;
26601                 }
26602                 serializableStructure.items = [];
26603                 for(var i = 0; i < this._arrayOfAllItems.length; ++i){
26604                         var item = this._arrayOfAllItems[i];
26605                         if(item !== null){
26606                                 var serializableItem = {};
26607                                 for(var key in item){
26608                                         if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
26609                                                 var attribute = key;
26610                                                 var valueArray = this.getValues(item, attribute);
26611                                                 if(valueArray.length == 1){
26612                                                         serializableItem[attribute] = this._flatten(valueArray[0]);
26613                                                 }else{
26614                                                         var serializableArray = [];
26615                                                         for(var j = 0; j < valueArray.length; ++j){
26616                                                                 serializableArray.push(this._flatten(valueArray[j]));
26617                                                                 serializableItem[attribute] = serializableArray;
26618                                                         }
26619                                                 }
26620                                         }
26621                                 }
26622                                 serializableStructure.items.push(serializableItem);
26623                         }
26624                 }
26625                 var prettyPrint = true;
26626                 return dojo.toJson(serializableStructure, prettyPrint);
26627         },
26628
26629         _isEmpty: function(something){
26630                 //      summary: 
26631                 //              Function to determine if an array or object has no properties or values.
26632                 //      something:
26633                 //              The array or object to examine.
26634                 var empty = true;
26635                 if(dojo.isObject(something)){
26636                         var i;
26637                         for(i in something){
26638                                 empty = false;
26639                                 break;
26640                         }
26641                 }else if(dojo.isArray(something)){
26642                         if(something.length > 0){
26643                                 empty = false;
26644                         }
26645                 }
26646                 return empty; //boolean
26647         },
26648         
26649         save: function(/* object */ keywordArgs){
26650                 // summary: See dojo.data.api.Write.save()
26651                 this._assert(!this._saveInProgress);
26652                 
26653                 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
26654                 this._saveInProgress = true;
26655                 
26656                 var self = this;
26657                 var saveCompleteCallback = function(){
26658                         self._pending = {
26659                                 _newItems:{}, 
26660                                 _modifiedItems:{},
26661                                 _deletedItems:{}
26662                         };
26663
26664                         self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
26665                         if(keywordArgs && keywordArgs.onComplete){
26666                                 var scope = keywordArgs.scope || dojo.global;
26667                                 keywordArgs.onComplete.call(scope);
26668                         }
26669                 };
26670                 var saveFailedCallback = function(err){
26671                         self._saveInProgress = false;
26672                         if(keywordArgs && keywordArgs.onError){
26673                                 var scope = keywordArgs.scope || dojo.global;
26674                                 keywordArgs.onError.call(scope, err);
26675                         }
26676                 };
26677                 
26678                 if(this._saveEverything){
26679                         var newFileContentString = this._getNewFileContentString();
26680                         this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
26681                 }
26682                 if(this._saveCustom){
26683                         this._saveCustom(saveCompleteCallback, saveFailedCallback);
26684                 }
26685                 if(!this._saveEverything && !this._saveCustom){
26686                         // Looks like there is no user-defined save-handler function.
26687                         // That's fine, it just means the datastore is acting as a "mock-write"
26688                         // store -- changes get saved in memory but don't get saved to disk.
26689                         saveCompleteCallback();
26690                 }
26691         },
26692         
26693         revert: function(){
26694                 // summary: See dojo.data.api.Write.revert()
26695                 this._assert(!this._saveInProgress);
26696
26697                 var identity;
26698                 for(identity in this._pending._modifiedItems){
26699                         // find the original item and the modified item that replaced it
26700                         var copyOfItemState = this._pending._modifiedItems[identity];
26701                         var modifiedItem = null;
26702                         if(this._itemsByIdentity){
26703                                 modifiedItem = this._itemsByIdentity[identity];
26704                         }else{
26705                                 modifiedItem = this._arrayOfAllItems[identity];
26706                         }
26707         
26708                         // Restore the original item into a full-fledged item again, we want to try to 
26709                         // keep the same object instance as if we don't it, causes bugs like #9022.
26710                         copyOfItemState[this._storeRefPropName] = this;
26711                         for(key in modifiedItem){
26712                                 delete modifiedItem[key];
26713                         }
26714                         dojo.mixin(modifiedItem, copyOfItemState);
26715                 }
26716                 var deletedItem;
26717                 for(identity in this._pending._deletedItems){
26718                         deletedItem = this._pending._deletedItems[identity];
26719                         deletedItem[this._storeRefPropName] = this;
26720                         var index = deletedItem[this._itemNumPropName];
26721
26722                         //Restore the reverse refererence map, if any.
26723                         if(deletedItem["backup_" + this._reverseRefMap]){
26724                                 deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
26725                                 delete deletedItem["backup_" + this._reverseRefMap];
26726                         }
26727                         this._arrayOfAllItems[index] = deletedItem;
26728                         if(this._itemsByIdentity){
26729                                 this._itemsByIdentity[identity] = deletedItem;
26730                         }
26731                         if(deletedItem[this._rootItemPropName]){
26732                                 this._arrayOfTopLevelItems.push(deletedItem);
26733                         }
26734                 }
26735                 //We have to pass through it again and restore the reference maps after all the
26736                 //undeletes have occurred.
26737                 for(identity in this._pending._deletedItems){
26738                         deletedItem = this._pending._deletedItems[identity];
26739                         if(deletedItem["backupRefs_" + this._reverseRefMap]){
26740                                 dojo.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
26741                                         var refItem;
26742                                         if(this._itemsByIdentity){
26743                                                 refItem = this._itemsByIdentity[reference.id];
26744                                         }else{
26745                                                 refItem = this._arrayOfAllItems[reference.id];
26746                                         }
26747                                         this._addReferenceToMap(refItem, deletedItem, reference.attr);
26748                                 }, this);
26749                                 delete deletedItem["backupRefs_" + this._reverseRefMap]; 
26750                         }
26751                 }
26752
26753                 for(identity in this._pending._newItems){
26754                         var newItem = this._pending._newItems[identity];
26755                         newItem[this._storeRefPropName] = null;
26756                         // null out the new item, but don't change the array index so
26757                         // so we can keep using _arrayOfAllItems.length.
26758                         this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
26759                         if(newItem[this._rootItemPropName]){
26760                                 this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
26761                         }
26762                         if(this._itemsByIdentity){
26763                                 delete this._itemsByIdentity[identity];
26764                         }
26765                 }
26766
26767                 this._pending = {
26768                         _newItems:{}, 
26769                         _modifiedItems:{}, 
26770                         _deletedItems:{}
26771                 };
26772                 return true; // boolean
26773         },
26774         
26775         isDirty: function(/* item? */ item){
26776                 // summary: See dojo.data.api.Write.isDirty()
26777                 if(item){
26778                         // return true if the item is dirty
26779                         var identity = this.getIdentity(item);
26780                         return new Boolean(this._pending._newItems[identity] || 
26781                                 this._pending._modifiedItems[identity] ||
26782                                 this._pending._deletedItems[identity]).valueOf(); // boolean
26783                 }else{
26784                         // return true if the store is dirty -- which means return true
26785                         // if there are any new items, dirty items, or modified items
26786                         if(!this._isEmpty(this._pending._newItems) || 
26787                                 !this._isEmpty(this._pending._modifiedItems) ||
26788                                 !this._isEmpty(this._pending._deletedItems)){
26789                                 return true;
26790                         }
26791                         return false; // boolean
26792                 }
26793         },
26794
26795 /* dojo.data.api.Notification */
26796
26797         onSet: function(/* item */ item, 
26798                                         /*attribute-name-string*/ attribute, 
26799                                         /*object | array*/ oldValue,
26800                                         /*object | array*/ newValue){
26801                 // summary: See dojo.data.api.Notification.onSet()
26802                 
26803                 // No need to do anything. This method is here just so that the 
26804                 // client code can connect observers to it.
26805         },
26806
26807         onNew: function(/* item */ newItem, /*object?*/ parentInfo){
26808                 // summary: See dojo.data.api.Notification.onNew()
26809                 
26810                 // No need to do anything. This method is here just so that the 
26811                 // client code can connect observers to it. 
26812         },
26813
26814         onDelete: function(/* item */ deletedItem){
26815                 // summary: See dojo.data.api.Notification.onDelete()
26816                 
26817                 // No need to do anything. This method is here just so that the 
26818                 // client code can connect observers to it. 
26819         },
26820
26821         close: function(/* object? */ request){
26822                  // summary:
26823                  //             Over-ride of base close function of ItemFileReadStore to add in check for store state.
26824                  // description:
26825                  //             Over-ride of base close function of ItemFileReadStore to add in check for store state.
26826                  //             If the store is still dirty (unsaved changes), then an error will be thrown instead of
26827                  //             clearing the internal state for reload from the url.
26828
26829                  //Clear if not dirty ... or throw an error
26830                  if(this.clearOnClose){
26831                          if(!this.isDirty()){
26832                                  this.inherited(arguments);
26833                          }else{
26834                                  //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
26835                                  throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store.  Please save or revert the changes before invoking close.");
26836                          }
26837                  }
26838         }
26839 });
26840
26841 }
26842
26843
26844 dojo.i18n._preloadLocalizations("dojo.nls.tt-rss-layer", ["ROOT","ar","ca","cs","da","de","de-de","el","en","en-gb","en-us","es","es-es","fi","fi-fi","fr","fr-fr","he","he-il","hu","it","it-it","ja","ja-jp","ko","ko-kr","nb","nl","nl-nl","pl","pt","pt-br","pt-pt","ru","sk","sl","sv","th","tr","xx","zh","zh-cn","zh-tw"]);