]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/tt-rss-layer.js.uncompressed.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dojo / tt-rss-layer.js.uncompressed.js
1 /*
2         Copyright (c) 2004-2011, 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 dojo.getObject("date.stamp", true, dojo);
20
21 // Methods to convert dates to or from a wire (string) format using well-known conventions
22
23 dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
24         //      summary:
25         //              Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
26         //
27         //      description:
28         //              Accepts a string formatted according to a profile of ISO8601 as defined by
29         //              [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
30         //              Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
31         //              The following combinations are valid:
32         //
33         //                      * dates only
34         //                      |       * yyyy
35         //                      |       * yyyy-MM
36         //                      |       * yyyy-MM-dd
37         //                      * times only, with an optional time zone appended
38         //                      |       * THH:mm
39         //                      |       * THH:mm:ss
40         //                      |       * THH:mm:ss.SSS
41         //                      * and "datetimes" which could be any combination of the above
42         //
43         //              timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
44         //              Assumes the local time zone if not specified.  Does not validate.  Improperly formatted
45         //              input may return null.  Arguments which are out of bounds will be handled
46         //              by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
47         //              Only years between 100 and 9999 are supported.
48         //
49         //      formattedString:
50         //              A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
51         //
52         //      defaultTime:
53         //              Used for defaults for fields omitted in the formattedString.
54         //              Uses 1970-01-01T00:00:00.0Z by default.
55
56         if(!dojo.date.stamp._isoRegExp){
57                 dojo.date.stamp._isoRegExp =
58 //TODO: could be more restrictive and check for 00-59, etc.
59                         /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
60         }
61
62         var match = dojo.date.stamp._isoRegExp.exec(formattedString),
63                 result = null;
64
65         if(match){
66                 match.shift();
67                 if(match[1]){match[1]--;} // Javascript Date months are 0-based
68                 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
69
70                 if(defaultTime){
71                         // mix in defaultTime.  Relatively expensive, so use || operators for the fast path of defaultTime === 0
72                         defaultTime = new Date(defaultTime);
73                         dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
74                                 return defaultTime["get" + prop]();
75                         }), function(value, index){
76                                 match[index] = match[index] || value;
77                         });
78                 }
79                 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
80                 if(match[0] < 100){
81                         result.setFullYear(match[0] || 1970);
82                 }
83
84                 var offset = 0,
85                         zoneSign = match[7] && match[7].charAt(0);
86                 if(zoneSign != 'Z'){
87                         offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
88                         if(zoneSign != '-'){ offset *= -1; }
89                 }
90                 if(zoneSign){
91                         offset -= result.getTimezoneOffset();
92                 }
93                 if(offset){
94                         result.setTime(result.getTime() + offset * 60000);
95                 }
96         }
97
98         return result; // Date or null
99 };
100
101 /*=====
102         dojo.date.stamp.__Options = function(){
103                 //      selector: String
104                 //              "date" or "time" for partial formatting of the Date object.
105                 //              Both date and time will be formatted by default.
106                 //      zulu: Boolean
107                 //              if true, UTC/GMT is used for a timezone
108                 //      milliseconds: Boolean
109                 //              if true, output milliseconds
110                 this.selector = selector;
111                 this.zulu = zulu;
112                 this.milliseconds = milliseconds;
113         }
114 =====*/
115
116 dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
117         //      summary:
118         //              Format a Date object as a string according a subset of the ISO-8601 standard
119         //
120         //      description:
121         //              When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
122         //              The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
123         //              Does not check bounds.  Only years between 100 and 9999 are supported.
124         //
125         //      dateObject:
126         //              A Date object
127
128         var _ = function(n){ return (n < 10) ? "0" + n : n; };
129         options = options || {};
130         var formattedDate = [],
131                 getter = options.zulu ? "getUTC" : "get",
132                 date = "";
133         if(options.selector != "time"){
134                 var year = dateObject[getter+"FullYear"]();
135                 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
136         }
137         formattedDate.push(date);
138         if(options.selector != "date"){
139                 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
140                 var millis = dateObject[getter+"Milliseconds"]();
141                 if(options.milliseconds){
142                         time += "."+ (millis < 100 ? "0" : "") + _(millis);
143                 }
144                 if(options.zulu){
145                         time += "Z";
146                 }else if(options.selector != "time"){
147                         var timezoneOffset = dateObject.getTimezoneOffset();
148                         var absOffset = Math.abs(timezoneOffset);
149                         time += (timezoneOffset > 0 ? "-" : "+") +
150                                 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
151                 }
152                 formattedDate.push(time);
153         }
154         return formattedDate.join('T'); // String
155 };
156
157 }
158
159 if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
160 dojo._hasResource["dojo.parser"] = true;
161 dojo.provide("dojo.parser");
162
163
164
165 new Date("X"); // workaround for #11279, new Date("") == NaN
166
167 dojo.parser = new function(){
168         // summary:
169         //              The Dom/Widget parsing package
170
171         var d = dojo;
172
173         function val2type(/*Object*/ value){
174                 // summary:
175                 //              Returns name of type of given value.
176
177                 if(d.isString(value)){ return "string"; }
178                 if(typeof value == "number"){ return "number"; }
179                 if(typeof value == "boolean"){ return "boolean"; }
180                 if(d.isFunction(value)){ return "function"; }
181                 if(d.isArray(value)){ return "array"; } // typeof [] == "object"
182                 if(value instanceof Date) { return "date"; } // assume timestamp
183                 if(value instanceof d._Url){ return "url"; }
184                 return "object";
185         }
186
187         function str2obj(/*String*/ value, /*String*/ type){
188                 // summary:
189                 //              Convert given string value to given type
190                 switch(type){
191                         case "string":
192                                 return value;
193                         case "number":
194                                 return value.length ? Number(value) : NaN;
195                         case "boolean":
196                                 // for checked/disabled value might be "" or "checked".  interpret as true.
197                                 return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
198                         case "function":
199                                 if(d.isFunction(value)){
200                                         // IE gives us a function, even when we say something like onClick="foo"
201                                         // (in which case it gives us an invalid function "function(){ foo }").
202                                         //      Therefore, convert to string
203                                         value=value.toString();
204                                         value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
205                                 }
206                                 try{
207                                         if(value === "" || value.search(/[^\w\.]+/i) != -1){
208                                                 // The user has specified some text for a function like "return x+5"
209                                                 return new Function(value);
210                                         }else{
211                                                 // The user has specified the name of a function like "myOnClick"
212                                                 // or a single word function "return"
213                                                 return d.getObject(value, false) || new Function(value);
214                                         }
215                                 }catch(e){ return new Function(); }
216                         case "array":
217                                 return value ? value.split(/\s*,\s*/) : [];
218                         case "date":
219                                 switch(value){
220                                         case "": return new Date("");   // the NaN of dates
221                                         case "now": return new Date();  // current date
222                                         default: return d.date.stamp.fromISOString(value);
223                                 }
224                         case "url":
225                                 return d.baseUrl + value;
226                         default:
227                                 return d.fromJson(value);
228                 }
229         }
230
231         var dummyClass = {}, instanceClasses = {
232                 // map from fully qualified name (like "dijit.Button") to structure like
233                 // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
234         };
235
236         // Widgets like BorderContainer add properties to _Widget via dojo.extend().
237         // If BorderContainer is loaded after _Widget's parameter list has been cached,
238         // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
239         // TODO: remove this in 2.0, when we stop caching parameters.
240         d.connect(d, "extend", function(){
241                 instanceClasses = {};
242         });
243
244         function getProtoInfo(cls, params){
245                 // cls: A prototype
246                 //              The prototype of the class to check props on
247                 // params: Object
248                 //              The parameters object to mix found parameters onto.
249                 for(var name in cls){
250                         if(name.charAt(0)=="_"){ continue; }    // skip internal properties
251                         if(name in dummyClass){ continue; }             // skip "constructor" and "toString"
252                         params[name] = val2type(cls[name]);
253                 }
254                 return params;
255         }
256
257         function getClassInfo(/*String*/ className, /*Boolean*/ skipParamsLookup){
258                 // summary:
259                 //              Maps a widget name string like "dijit.form.Button" to the widget constructor itself,
260                 //              and a list of that widget's parameters and their types
261                 // className:
262                 //              fully qualified name (like "dijit.form.Button")
263                 // returns:
264                 //              structure like
265                 //                      {
266                 //                              cls: dijit.Button,
267                 //                              params: { label: "string", disabled: "boolean"}
268                 //                      }
269
270                 var c = instanceClasses[className];
271                 if(!c){
272                         // get pointer to widget class
273                         var cls = d.getObject(className), params = null;
274                         if(!cls){ return null; }                // class not defined [yet]
275                         if(!skipParamsLookup){ // from fastpath, we don't need to lookup the attrs on the proto because they are explicit
276                                 params = getProtoInfo(cls.prototype, {})
277                         }
278                         c = { cls: cls, params: params };
279                         
280                 }else if(!skipParamsLookup && !c.params){
281                         // if we're calling getClassInfo and have a cls proto, but no params info, scan that cls for params now
282                         // and update the pointer in instanceClasses[className]. This happens when a widget appears in another
283                         // widget's template which still uses dojoType, but an instance of the widget appears prior with a data-dojo-type,
284                         // skipping this lookup the first time.
285                         c.params = getProtoInfo(c.cls.prototype, {});
286                 }
287                 
288                 return c;
289         }
290
291         this._functionFromScript = function(script, attrData){
292                 // summary:
293                 //              Convert a <script type="dojo/method" args="a, b, c"> ... </script>
294                 //              into a function
295                 // script: DOMNode
296                 //              The <script> DOMNode
297                 // attrData: String
298                 //              For HTML5 compliance, searches for attrData + "args" (typically
299                 //              "data-dojo-args") instead of "args"
300                 var preamble = "";
301                 var suffix = "";
302                 var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args"));
303                 if(argsStr){
304                         d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
305                                 preamble += "var "+part+" = arguments["+idx+"]; ";
306                         });
307                 }
308                 var withStr = script.getAttribute("with");
309                 if(withStr && withStr.length){
310                         d.forEach(withStr.split(/\s*,\s*/), function(part){
311                                 preamble += "with("+part+"){";
312                                 suffix += "}";
313                         });
314                 }
315                 return new Function(preamble+script.innerHTML+suffix);
316         };
317
318         this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){
319                 // summary:
320                 //              Takes array of nodes, and turns them into class instances and
321                 //              potentially calls a startup method to allow them to connect with
322                 //              any children.
323                 // nodes: Array
324                 //              Array of nodes or objects like
325                 //      |               {
326                 //      |                       type: "dijit.form.Button",
327                 //      |                       node: DOMNode,
328                 //      |                       scripts: [ ... ],       // array of <script type="dojo/..."> children of node
329                 //      |                       inherited: { ... }      // settings inherited from ancestors like dir, theme, etc.
330                 //      |               }
331                 // mixin: Object?
332                 //              An object that will be mixed in with each node in the array.
333                 //              Values in the mixin will override values in the node, if they
334                 //              exist.
335                 // args: Object?
336                 //              An object used to hold kwArgs for instantiation.
337                 //              See parse.args argument for details.
338
339                 var thelist = [],
340                 mixin = mixin||{};
341                 args = args||{};
342
343                 // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
344                 var attrName = (args.scope || d._scopeName) + "Type",   // typically "dojoType"
345                         attrData = "data-" + (args.scope || d._scopeName) + "-";        // typically "data-dojo-"
346
347                 d.forEach(nodes, function(obj){
348                         if(!obj){ return; }
349
350                         // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.
351                         var node, type, clsInfo, clazz, scripts, fastpath;
352                         if(obj.node){
353                                 // new format of nodes[] array, object w/lots of properties pre-computed for me
354                                 node = obj.node;
355                                 type = obj.type;
356                                 fastpath = obj.fastpath;
357                                 clsInfo = obj.clsInfo || (type && getClassInfo(type, fastpath));
358                                 clazz = clsInfo && clsInfo.cls;
359                                 scripts = obj.scripts;
360                         }else{
361                                 // old (backwards compatible) format of nodes[] array, simple array of DOMNodes. no fastpath/data-dojo-type support here.
362                                 node = obj;
363                                 type = attrName in mixin ? mixin[attrName] : node.getAttribute(attrName);
364                                 clsInfo = type && getClassInfo(type);
365                                 clazz = clsInfo && clsInfo.cls;
366                                 scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] :
367                                                         d.query("> script[type^='dojo/']", node));
368                         }
369                         if(!clsInfo){
370                                 throw new Error("Could not load class '" + type);
371                         }
372
373                         // Setup hash to hold parameter settings for this widget.       Start with the parameter
374                         // settings inherited from ancestors ("dir" and "lang").
375                         // Inherited setting may later be overridden by explicit settings on node itself.
376                         var params = {};
377                                 
378                         if(args.defaults){
379                                 // settings for the document itself (or whatever subtree is being parsed)
380                                 d._mixin(params, args.defaults);
381                         }
382                         if(obj.inherited){
383                                 // settings from dir=rtl or lang=... on a node above this node
384                                 d._mixin(params, obj.inherited);
385                         }
386                         
387                         // mix things found in data-dojo-props into the params
388                         if(fastpath){
389                                 var extra = node.getAttribute(attrData + "props");
390                                 if(extra && extra.length){
391                                         try{
392                                                 extra = d.fromJson.call(args.propsThis, "{" + extra + "}");
393                                                 d._mixin(params, extra);
394                                         }catch(e){
395                                                 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
396                                                 throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
397                                         }
398                                 }
399
400                                 // For the benefit of _Templated, check if node has data-dojo-attach-point/data-dojo-attach-event
401                                 // and mix those in as though they were parameters
402                                 var attachPoint = node.getAttribute(attrData + "attach-point");
403                                 if(attachPoint){
404                                         params.dojoAttachPoint = attachPoint;
405                                 }
406                                 var attachEvent = node.getAttribute(attrData + "attach-event");
407                                 if(attachEvent){
408                                         params.dojoAttachEvent = attachEvent;
409                                 }
410                                 dojo.mixin(params, mixin);
411                         }else{
412                                 // FIXME: we need something like "deprecateOnce()" to throw dojo.deprecation for something.
413                                 // remove this logic in 2.0
414                                 // read parameters (ie, attributes) specified on DOMNode
415
416                                 var attributes = node.attributes;
417
418                                 // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
419                                 for(var name in clsInfo.params){
420                                         var item = name in mixin ? { value:mixin[name], specified:true } : attributes.getNamedItem(name);
421                                         if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
422                                         var value = item.value;
423                                         // Deal with IE quirks for 'class' and 'style'
424                                         switch(name){
425                                         case "class":
426                                                 value = "className" in mixin ? mixin.className : node.className;
427                                                 break;
428                                         case "style":
429                                                 value = "style" in mixin ? mixin.style : (node.style && node.style.cssText); // FIXME: Opera?
430                                         }
431                                         var _type = clsInfo.params[name];
432                                         if(typeof value == "string"){
433                                                 params[name] = str2obj(value, _type);
434                                         }else{
435                                                 params[name] = value;
436                                         }
437                                 }
438                         }
439
440                         // Process <script type="dojo/*"> script tags
441                         // <script type="dojo/method" event="foo"> tags are added to params, and passed to
442                         // the widget on instantiation.
443                         // <script type="dojo/method"> tags (with no event) are executed after instantiation
444                         // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
445                         // note: dojo/* script tags cannot exist in self closing widgets, like <input />
446                         var connects = [],      // functions to connect after instantiation
447                                 calls = [];             // functions to call after instantiation
448
449                         d.forEach(scripts, function(script){
450                                 node.removeChild(script);
451                                 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
452                                 var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
453                                         type = script.getAttribute("type"),
454                                         nf = d.parser._functionFromScript(script, attrData);
455                                 if(event){
456                                         if(type == "dojo/connect"){
457                                                 connects.push({event: event, func: nf});
458                                         }else{
459                                                 params[event] = nf;
460                                         }
461                                 }else{
462                                         calls.push(nf);
463                                 }
464                         });
465
466                         var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory;
467                         // create the instance
468                         var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
469                         thelist.push(instance);
470
471                         // map it to the JS namespace if that makes sense
472                         // FIXME: in 2.0, drop jsId support. use data-dojo-id instead
473                         var jsname = (node.getAttribute(attrData + "id") || node.getAttribute("jsId"));
474                         if(jsname){
475                                 d.setObject(jsname, instance);
476                         }
477
478                         // process connections and startup functions
479                         d.forEach(connects, function(connect){
480                                 d.connect(instance, connect.event, null, connect.func);
481                         });
482                         d.forEach(calls, function(func){
483                                 func.call(instance);
484                         });
485                 });
486
487                 // Call startup on each top level instance if it makes sense (as for
488                 // widgets).  Parent widgets will recursively call startup on their
489                 // (non-top level) children
490                 if(!mixin._started){
491                         // TODO: for 2.0, when old instantiate() API is desupported, store parent-child
492                         // relationships in the nodes[] array so that no getParent() call is needed.
493                         // Note that will  require a parse() call from ContentPane setting a param that the
494                         // ContentPane is the parent widget (so that the parse doesn't call startup() on the
495                         // ContentPane's children)
496                         d.forEach(thelist, function(instance){
497                                 if( !args.noStart && instance  &&
498                                         dojo.isFunction(instance.startup) &&
499                                         !instance._started &&
500                                         (!instance.getParent || !instance.getParent())
501                                 ){
502                                         instance.startup();
503                                 }
504                         });
505                 }
506                 return thelist;
507         };
508
509         this.parse = function(rootNode, args){
510                 // summary:
511                 //              Scan the DOM for class instances, and instantiate them.
512                 //
513                 // description:
514                 //              Search specified node (or root node) recursively for class instances,
515                 //              and instantiate them. Searches for either data-dojo-type="Class" or
516                 //              dojoType="Class" where "Class" is a a fully qualified class name,
517                 //              like `dijit.form.Button`
518                 //
519                 //              Using `data-dojo-type`:
520                 //              Attributes using can be mixed into the parameters used to instantitate the
521                 //              Class by using a `data-dojo-props` attribute on the node being converted.
522                 //              `data-dojo-props` should be a string attribute to be converted from JSON.
523                 //
524                 //              Using `dojoType`:
525                 //              Attributes are read from the original domNode and converted to appropriate
526                 //              types by looking up the Class prototype values. This is the default behavior
527                 //              from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
528                 //              go away in Dojo 2.0.
529                 //
530                 // rootNode: DomNode?
531                 //              A default starting root node from which to start the parsing. Can be
532                 //              omitted, defaulting to the entire document. If omitted, the `args`
533                 //              object can be passed in this place. If the `args` object has a
534                 //              `rootNode` member, that is used.
535                 //
536                 // args: Object
537                 //              a kwArgs object passed along to instantiate()
538                 //
539                 //                      * noStart: Boolean?
540                 //                              when set will prevent the parser from calling .startup()
541                 //                              when locating the nodes.
542                 //                      * rootNode: DomNode?
543                 //                              identical to the function's `rootNode` argument, though
544                 //                              allowed to be passed in via this `args object.
545                 //                      * template: Boolean
546                 //                              If true, ignores ContentPane's stopParser flag and parses contents inside of
547                 //                              a ContentPane inside of a template.   This allows dojoAttachPoint on widgets/nodes
548                 //                              nested inside the ContentPane to work.
549                 //                      * inherited: Object
550                 //                              Hash possibly containing dir and lang settings to be applied to
551                 //                              parsed widgets, unless there's another setting on a sub-node that overrides
552                 //                      * scope: String
553                 //                              Root for attribute names to search for.   If scopeName is dojo,
554                 //                              will search for data-dojo-type (or dojoType).   For backwards compatibility
555                 //                              reasons defaults to dojo._scopeName (which is "dojo" except when
556                 //                              multi-version support is used, when it will be something like dojo16, dojo20, etc.)
557                 //                      * propsThis: Object
558                 //                              If specified, "this" referenced from data-dojo-props will refer to propsThis.
559                 //                              Intended for use from the widgets-in-template feature of `dijit._Templated`
560                 //
561                 // example:
562                 //              Parse all widgets on a page:
563                 //      |               dojo.parser.parse();
564                 //
565                 // example:
566                 //              Parse all classes within the node with id="foo"
567                 //      |               dojo.parser.parse(dojo.byId('foo'));
568                 //
569                 // example:
570                 //              Parse all classes in a page, but do not call .startup() on any
571                 //              child
572                 //      |               dojo.parser.parse({ noStart: true })
573                 //
574                 // example:
575                 //              Parse all classes in a node, but do not call .startup()
576                 //      |               dojo.parser.parse(someNode, { noStart:true });
577                 //      |               // or
578                 //      |               dojo.parser.parse({ noStart:true, rootNode: someNode });
579
580                 // determine the root node based on the passed arguments.
581                 var root;
582                 if(!args && rootNode && rootNode.rootNode){
583                         args = rootNode;
584                         root = args.rootNode;
585                 }else{
586                         root = rootNode;
587                 }
588                 root = root ? dojo.byId(root) : dojo.body();
589                 args = args || {};
590
591                 var attrName = (args.scope || d._scopeName) + "Type",           // typically "dojoType"
592                         attrData = "data-" + (args.scope || d._scopeName) + "-";        // typically "data-dojo-"
593
594                 function scan(parent, list){
595                         // summary:
596                         //              Parent is an Object representing a DOMNode, with or without a dojoType specified.
597                         //              Scan parent's children looking for nodes with dojoType specified, storing in list[].
598                         //              If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[].
599                         // parent: Object
600                         //              Object representing the parent node, like
601                         //      |       {
602                         //      |               node: DomNode,                  // scan children of this node
603                         //      |               inherited: {dir: "rtl"},        // dir/lang setting inherited from above node
604                         //      |
605                         //      |               // attributes only set if node has dojoType specified
606                         //      |               scripts: [],                    // empty array, put <script type=dojo/*> in here
607                         //      |               clsInfo: { cls: dijit.form.Button, ...}
608                         //      |       }
609                         // list: DomNode[]
610                         //              Output array of objects (same format as parent) representing nodes to be turned into widgets
611
612                         // Effective dir and lang settings on parent node, either set directly or inherited from grandparent
613                         var inherited = dojo.clone(parent.inherited);
614                         dojo.forEach(["dir", "lang"], function(name){
615                                 // TODO: what if this is a widget and dir/lang are declared in data-dojo-props?
616                                 var val = parent.node.getAttribute(name);
617                                 if(val){
618                                         inherited[name] = val;
619                                 }
620                         });
621
622                         // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[].
623                         var scripts = parent.clsInfo && !parent.clsInfo.cls.prototype._noScript ? parent.scripts : null;
624
625                         // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively
626                         var recurse = (!parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser) || (args && args.template);
627
628                         // scan parent's children looking for dojoType and <script type=dojo/*>
629                         for(var child = parent.node.firstChild; child; child = child.nextSibling){
630                                 if(child.nodeType == 1){
631                                         // FIXME: desupport dojoType in 2.0. use data-dojo-type instead
632                                         var type, html5 = recurse && child.getAttribute(attrData + "type");
633                                         if(html5){
634                                                 type = html5;
635                                         }else{
636                                                 // fallback to backward compatible mode, using dojoType. remove in 2.0
637                                                 type = recurse && child.getAttribute(attrName);
638                                         }
639                                         
640                                         var fastpath = html5 == type;
641
642                                         if(type){
643                                                 // if dojoType/data-dojo-type specified, add to output array of nodes to instantiate
644                                                 var params = {
645                                                         "type": type,
646                                                         fastpath: fastpath,
647                                                         clsInfo: getClassInfo(type, fastpath), // note: won't find classes declared via dojo.Declaration
648                                                         node: child,
649                                                         scripts: [], // <script> nodes that are parent's children
650                                                         inherited: inherited // dir & lang attributes inherited from parent
651                                                 };
652                                                 list.push(params);
653
654                                                 // Recurse, collecting <script type="dojo/..."> children, and also looking for
655                                                 // descendant nodes with dojoType specified (unless the widget has the stopParser flag),
656                                                 scan(params, list);
657                                         }else if(scripts && child.nodeName.toLowerCase() == "script"){
658                                                 // if <script type="dojo/...">, save in scripts[]
659                                                 type = child.getAttribute("type");
660                                                 if (type && /^dojo\/\w/i.test(type)) {
661                                                         scripts.push(child);
662                                                 }
663                                         }else if(recurse){
664                                                 // Recurse, looking for grandchild nodes with dojoType specified
665                                                 scan({
666                                                         node: child,
667                                                         inherited: inherited
668                                                 }, list);
669                                         }
670                                 }
671                         }
672                 }
673
674                 // Ignore bogus entries in inherited hash like {dir: ""}
675                 var inherited = {};
676                 if(args && args.inherited){
677                         for(var key in args.inherited){
678                                 if(args.inherited[key]){ inherited[key] = args.inherited[key]; }
679                         }
680                 }
681
682                 // Make list of all nodes on page w/dojoType specified
683                 var list = [];
684                 scan({
685                         node: root,
686                         inherited: inherited
687                 }, list);
688
689                 // go build the object instances
690                 var mixin = args && args.template ? {template: true} : null;
691                 return this.instantiate(list, mixin, args); // Array
692         };
693 }();
694
695 //Register the parser callback. It should be the first callback
696 //after the a11y test.
697
698 (function(){
699         var parseRunner = function(){
700                 if(dojo.config.parseOnLoad){
701                         dojo.parser.parse();
702                 }
703         };
704
705         // FIXME: need to clobber cross-dependency!!
706         if(dojo.getObject("dijit.wai.onload") === dojo._loaders[0]){
707                 dojo._loaders.splice(1, 0, parseRunner);
708         }else{
709                 dojo._loaders.unshift(parseRunner);
710         }
711 })();
712
713 }
714
715 if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
716 dojo._hasResource["dojo.window"] = true;
717 dojo.provide("dojo.window");
718
719 dojo.getObject("window", true, dojo);
720
721 dojo.window.getBox = function(){
722         // summary:
723         //              Returns the dimensions and scroll position of the viewable area of a browser window
724
725         var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement;
726
727         // get scroll position
728         var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work
729         return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
730 };
731
732 dojo.window.get = function(doc){
733         // summary:
734         //              Get window object associated with document doc
735
736         // In some IE versions (at least 6.0), document.parentWindow does not return a
737         // reference to the real window object (maybe a copy), so we must fix it as well
738         // We use IE specific execScript to attach the real window reference to
739         // document._parentWindow for later use
740         if(dojo.isIE && window !== document.parentWindow){
741                 /*
742                 In IE 6, only the variable "window" can be used to connect events (others
743                 may be only copies).
744                 */
745                 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
746                 //to prevent memory leak, unset it after use
747                 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
748                 var win = doc._parentWindow;
749                 doc._parentWindow = null;
750                 return win;     //      Window
751         }
752
753         return doc.parentWindow || doc.defaultView;     //      Window
754 };
755
756 dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
757         // summary:
758         //              Scroll the passed node into view, if it is not already.
759         
760         // don't rely on node.scrollIntoView working just because the function is there
761
762         try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
763                 node = dojo.byId(node);
764                 var doc = node.ownerDocument || dojo.doc,
765                         body = doc.body || dojo.body(),
766                         html = doc.documentElement || body.parentNode,
767                         isIE = dojo.isIE, isWK = dojo.isWebKit;
768                 // if an untested browser, then use the native method
769                 if((!(dojo.isMoz || isIE || isWK || dojo.isOpera) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
770                         node.scrollIntoView(false); // short-circuit to native if possible
771                         return;
772                 }
773                 var backCompat = doc.compatMode == 'BackCompat',
774                         clientAreaRoot = (isIE >= 9 && node.ownerDocument.parentWindow.frameElement)
775                                 ? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body)
776                                 : (backCompat ? body : html),
777                         scrollRoot = isWK ? body : clientAreaRoot,
778                         rootWidth = clientAreaRoot.clientWidth,
779                         rootHeight = clientAreaRoot.clientHeight,
780                         rtl = !dojo._isBodyLtr(),
781                         nodePos = pos || dojo.position(node),
782                         el = node.parentNode,
783                         isFixed = function(el){
784                                 return ((isIE <= 6 || (isIE && backCompat))? false : (dojo.style(el, 'position').toLowerCase() == "fixed"));
785                         };
786                 if(isFixed(node)){ return; } // nothing to do
787
788                 while(el){
789                         if(el == body){ el = scrollRoot; }
790                         var elPos = dojo.position(el),
791                                 fixedPos = isFixed(el);
792         
793                         if(el == scrollRoot){
794                                 elPos.w = rootWidth; elPos.h = rootHeight;
795                                 if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
796                                 if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
797                                 if(elPos.y < 0 || !isIE){ elPos.y = 0; }
798                         }else{
799                                 var pb = dojo._getPadBorderExtents(el);
800                                 elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
801                                 var clientSize = el.clientWidth,
802                                         scrollBarSize = elPos.w - clientSize;
803                                 if(clientSize > 0 && scrollBarSize > 0){
804                                         elPos.w = clientSize;
805                                         elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0;
806                                 }
807                                 clientSize = el.clientHeight;
808                                 scrollBarSize = elPos.h - clientSize;
809                                 if(clientSize > 0 && scrollBarSize > 0){
810                                         elPos.h = clientSize;
811                                 }
812                         }
813                         if(fixedPos){ // bounded by viewport, not parents
814                                 if(elPos.y < 0){
815                                         elPos.h += elPos.y; elPos.y = 0;
816                                 }
817                                 if(elPos.x < 0){
818                                         elPos.w += elPos.x; elPos.x = 0;
819                                 }
820                                 if(elPos.y + elPos.h > rootHeight){
821                                         elPos.h = rootHeight - elPos.y;
822                                 }
823                                 if(elPos.x + elPos.w > rootWidth){
824                                         elPos.w = rootWidth - elPos.x;
825                                 }
826                         }
827                         // calculate overflow in all 4 directions
828                         var l = nodePos.x - elPos.x, // beyond left: < 0
829                                 t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
830                                 r = l + nodePos.w - elPos.w, // beyond right: > 0
831                                 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
832                         if(r * l > 0){
833                                 var s = Math[l < 0? "max" : "min"](l, r);
834                                 if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
835                                 nodePos.x += el.scrollLeft;
836                                 el.scrollLeft += s;
837                                 nodePos.x -= el.scrollLeft;
838                         }
839                         if(bot * t > 0){
840                                 nodePos.y += el.scrollTop;
841                                 el.scrollTop += Math[t < 0? "max" : "min"](t, bot);
842                                 nodePos.y -= el.scrollTop;
843                         }
844                         el = (el != scrollRoot) && !fixedPos && el.parentNode;
845                 }
846         }catch(error){
847                 console.error('scrollIntoView: ' + error);
848                 node.scrollIntoView(false);
849         }
850 };
851
852 }
853
854 if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
855 dojo._hasResource["dijit._base.manager"] = true;
856 dojo.provide("dijit._base.manager");
857
858
859 dojo.declare("dijit.WidgetSet", null, {
860         // summary:
861         //              A set of widgets indexed by id. A default instance of this class is
862         //              available as `dijit.registry`
863         //
864         // example:
865         //              Create a small list of widgets:
866         //              |       var ws = new dijit.WidgetSet();
867         //              |       ws.add(dijit.byId("one"));
868         //              |       ws.add(dijit.byId("two"));
869         //              |       // destroy both:
870         //              |       ws.forEach(function(w){ w.destroy(); });
871         //
872         // example:
873         //              Using dijit.registry:
874         //              |       dijit.registry.forEach(function(w){ /* do something */ });
875
876         constructor: function(){
877                 this._hash = {};
878                 this.length = 0;
879         },
880
881         add: function(/*dijit._Widget*/ widget){
882                 // summary:
883                 //              Add a widget to this list. If a duplicate ID is detected, a error is thrown.
884                 //
885                 // widget: dijit._Widget
886                 //              Any dijit._Widget subclass.
887                 if(this._hash[widget.id]){
888                         throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
889                 }
890                 this._hash[widget.id] = widget;
891                 this.length++;
892         },
893
894         remove: function(/*String*/ id){
895                 // summary:
896                 //              Remove a widget from this WidgetSet. Does not destroy the widget; simply
897                 //              removes the reference.
898                 if(this._hash[id]){
899                         delete this._hash[id];
900                         this.length--;
901                 }
902         },
903
904         forEach: function(/*Function*/ func, /* Object? */thisObj){
905                 // summary:
906                 //              Call specified function for each widget in this set.
907                 //
908                 // func:
909                 //              A callback function to run for each item. Is passed the widget, the index
910                 //              in the iteration, and the full hash, similar to `dojo.forEach`.
911                 //
912                 // thisObj:
913                 //              An optional scope parameter
914                 //
915                 // example:
916                 //              Using the default `dijit.registry` instance:
917                 //              |       dijit.registry.forEach(function(widget){
918                 //              |               console.log(widget.declaredClass);
919                 //              |       });
920                 //
921                 // returns:
922                 //              Returns self, in order to allow for further chaining.
923
924                 thisObj = thisObj || dojo.global;
925                 var i = 0, id;
926                 for(id in this._hash){
927                         func.call(thisObj, this._hash[id], i++, this._hash);
928                 }
929                 return this;    // dijit.WidgetSet
930         },
931
932         filter: function(/*Function*/ filter, /* Object? */thisObj){
933                 // summary:
934                 //              Filter down this WidgetSet to a smaller new WidgetSet
935                 //              Works the same as `dojo.filter` and `dojo.NodeList.filter`
936                 //
937                 // filter:
938                 //              Callback function to test truthiness. Is passed the widget
939                 //              reference and the pseudo-index in the object.
940                 //
941                 // thisObj: Object?
942                 //              Option scope to use for the filter function.
943                 //
944                 // example:
945                 //              Arbitrary: select the odd widgets in this list
946                 //              |       dijit.registry.filter(function(w, i){
947                 //              |               return i % 2 == 0;
948                 //              |       }).forEach(function(w){ /* odd ones */ });
949
950                 thisObj = thisObj || dojo.global;
951                 var res = new dijit.WidgetSet(), i = 0, id;
952                 for(id in this._hash){
953                         var w = this._hash[id];
954                         if(filter.call(thisObj, w, i++, this._hash)){
955                                 res.add(w);
956                         }
957                 }
958                 return res; // dijit.WidgetSet
959         },
960
961         byId: function(/*String*/ id){
962                 // summary:
963                 //              Find a widget in this list by it's id.
964                 // example:
965                 //              Test if an id is in a particular WidgetSet
966                 //              | var ws = new dijit.WidgetSet();
967                 //              | ws.add(dijit.byId("bar"));
968                 //              | var t = ws.byId("bar") // returns a widget
969                 //              | var x = ws.byId("foo"); // returns undefined
970
971                 return this._hash[id];  // dijit._Widget
972         },
973
974         byClass: function(/*String*/ cls){
975                 // summary:
976                 //              Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
977                 //
978                 // cls: String
979                 //              The Class to scan for. Full dot-notated string.
980                 //
981                 // example:
982                 //              Find all `dijit.TitlePane`s in a page:
983                 //              |       dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
984
985                 var res = new dijit.WidgetSet(), id, widget;
986                 for(id in this._hash){
987                         widget = this._hash[id];
988                         if(widget.declaredClass == cls){
989                                 res.add(widget);
990                         }
991                  }
992                  return res; // dijit.WidgetSet
993 },
994
995         toArray: function(){
996                 // summary:
997                 //              Convert this WidgetSet into a true Array
998                 //
999                 // example:
1000                 //              Work with the widget .domNodes in a real Array
1001                 //              |       dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; });
1002
1003                 var ar = [];
1004                 for(var id in this._hash){
1005                         ar.push(this._hash[id]);
1006                 }
1007                 return ar;      // dijit._Widget[]
1008 },
1009
1010         map: function(/* Function */func, /* Object? */thisObj){
1011                 // summary:
1012                 //              Create a new Array from this WidgetSet, following the same rules as `dojo.map`
1013                 // example:
1014                 //              |       var nodes = dijit.registry.map(function(w){ return w.domNode; });
1015                 //
1016                 // returns:
1017                 //              A new array of the returned values.
1018                 return dojo.map(this.toArray(), func, thisObj); // Array
1019         },
1020
1021         every: function(func, thisObj){
1022                 // summary:
1023                 //              A synthetic clone of `dojo.every` acting explicitly on this WidgetSet
1024                 //
1025                 // func: Function
1026                 //              A callback function run for every widget in this list. Exits loop
1027                 //              when the first false return is encountered.
1028                 //
1029                 // thisObj: Object?
1030                 //              Optional scope parameter to use for the callback
1031
1032                 thisObj = thisObj || dojo.global;
1033                 var x = 0, i;
1034                 for(i in this._hash){
1035                         if(!func.call(thisObj, this._hash[i], x++, this._hash)){
1036                                 return false; // Boolean
1037                         }
1038                 }
1039                 return true; // Boolean
1040         },
1041
1042         some: function(func, thisObj){
1043                 // summary:
1044                 //              A synthetic clone of `dojo.some` acting explictly on this WidgetSet
1045                 //
1046                 // func: Function
1047                 //              A callback function run for every widget in this list. Exits loop
1048                 //              when the first true return is encountered.
1049                 //
1050                 // thisObj: Object?
1051                 //              Optional scope parameter to use for the callback
1052
1053                 thisObj = thisObj || dojo.global;
1054                 var x = 0, i;
1055                 for(i in this._hash){
1056                         if(func.call(thisObj, this._hash[i], x++, this._hash)){
1057                                 return true; // Boolean
1058                         }
1059                 }
1060                 return false; // Boolean
1061         }
1062
1063 });
1064
1065 (function(){
1066
1067         /*=====
1068         dijit.registry = {
1069                 // summary:
1070                 //              A list of widgets on a page.
1071                 // description:
1072                 //              Is an instance of `dijit.WidgetSet`
1073         };
1074         =====*/
1075         dijit.registry = new dijit.WidgetSet();
1076
1077         var hash = dijit.registry._hash,
1078                 attr = dojo.attr,
1079                 hasAttr = dojo.hasAttr,
1080                 style = dojo.style;
1081
1082         dijit.byId = function(/*String|dijit._Widget*/ id){
1083                 // summary:
1084                 //              Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId())
1085                 return typeof id == "string" ? hash[id] : id; // dijit._Widget
1086         };
1087
1088         var _widgetTypeCtr = {};
1089         dijit.getUniqueId = function(/*String*/widgetType){
1090                 // summary:
1091                 //              Generates a unique id for a given widgetType
1092         
1093                 var id;
1094                 do{
1095                         id = widgetType + "_" +
1096                                 (widgetType in _widgetTypeCtr ?
1097                                         ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
1098                 }while(hash[id]);
1099                 return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
1100         };
1101         
1102         dijit.findWidgets = function(/*DomNode*/ root){
1103                 // summary:
1104                 //              Search subtree under root returning widgets found.
1105                 //              Doesn't search for nested widgets (ie, widgets inside other widgets).
1106         
1107                 var outAry = [];
1108         
1109                 function getChildrenHelper(root){
1110                         for(var node = root.firstChild; node; node = node.nextSibling){
1111                                 if(node.nodeType == 1){
1112                                         var widgetId = node.getAttribute("widgetId");
1113                                         if(widgetId){
1114                                                 var widget = hash[widgetId];
1115                                                 if(widget){     // may be null on page w/multiple dojo's loaded
1116                                                         outAry.push(widget);
1117                                                 }
1118                                         }else{
1119                                                 getChildrenHelper(node);
1120                                         }
1121                                 }
1122                         }
1123                 }
1124         
1125                 getChildrenHelper(root);
1126                 return outAry;
1127         };
1128         
1129         dijit._destroyAll = function(){
1130                 // summary:
1131                 //              Code to destroy all widgets and do other cleanup on page unload
1132         
1133                 // Clean up focus manager lingering references to widgets and nodes
1134                 dijit._curFocus = null;
1135                 dijit._prevFocus = null;
1136                 dijit._activeStack = [];
1137         
1138                 // Destroy all the widgets, top down
1139                 dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){
1140                         // Avoid double destroy of widgets like Menu that are attached to <body>
1141                         // even though they are logically children of other widgets.
1142                         if(!widget._destroyed){
1143                                 if(widget.destroyRecursive){
1144                                         widget.destroyRecursive();
1145                                 }else if(widget.destroy){
1146                                         widget.destroy();
1147                                 }
1148                         }
1149                 });
1150         };
1151         
1152         if(dojo.isIE){
1153                 // Only run _destroyAll() for IE because we think it's only necessary in that case,
1154                 // and because it causes problems on FF.  See bug #3531 for details.
1155                 dojo.addOnWindowUnload(function(){
1156                         dijit._destroyAll();
1157                 });
1158         }
1159         
1160         dijit.byNode = function(/*DOMNode*/ node){
1161                 // summary:
1162                 //              Returns the widget corresponding to the given DOMNode
1163                 return hash[node.getAttribute("widgetId")]; // dijit._Widget
1164         };
1165         
1166         dijit.getEnclosingWidget = function(/*DOMNode*/ node){
1167                 // summary:
1168                 //              Returns the widget whose DOM tree contains the specified DOMNode, or null if
1169                 //              the node is not contained within the DOM tree of any widget
1170                 while(node){
1171                         var id = node.getAttribute && node.getAttribute("widgetId");
1172                         if(id){
1173                                 return hash[id];
1174                         }
1175                         node = node.parentNode;
1176                 }
1177                 return null;
1178         };
1179
1180         var shown = (dijit._isElementShown = function(/*Element*/ elem){
1181                 var s = style(elem);
1182                 return (s.visibility != "hidden")
1183                         && (s.visibility != "collapsed")
1184                         && (s.display != "none")
1185                         && (attr(elem, "type") != "hidden");
1186         });
1187         
1188         dijit.hasDefaultTabStop = function(/*Element*/ elem){
1189                 // summary:
1190                 //              Tests if element is tab-navigable even without an explicit tabIndex setting
1191         
1192                 // No explicit tabIndex setting, need to investigate node type
1193                 switch(elem.nodeName.toLowerCase()){
1194                         case "a":
1195                                 // An <a> w/out a tabindex is only navigable if it has an href
1196                                 return hasAttr(elem, "href");
1197                         case "area":
1198                         case "button":
1199                         case "input":
1200                         case "object":
1201                         case "select":
1202                         case "textarea":
1203                                 // These are navigable by default
1204                                 return true;
1205                         case "iframe":
1206                                 // If it's an editor <iframe> then it's tab navigable.
1207                                 var body;
1208                                 try{
1209                                         // non-IE
1210                                         var contentDocument = elem.contentDocument;
1211                                         if("designMode" in contentDocument && contentDocument.designMode == "on"){
1212                                                 return true;
1213                                         }
1214                                         body = contentDocument.body;
1215                                 }catch(e1){
1216                                         // contentWindow.document isn't accessible within IE7/8
1217                                         // if the iframe.src points to a foreign url and this
1218                                         // page contains an element, that could get focus
1219                                         try{
1220                                                 body = elem.contentWindow.document.body;
1221                                         }catch(e2){
1222                                                 return false;
1223                                         }
1224                                 }
1225                                 return body.contentEditable == 'true' || (body.firstChild && body.firstChild.contentEditable == 'true');
1226                         default:
1227                                 return elem.contentEditable == 'true';
1228                 }
1229         };
1230         
1231         var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
1232                 // summary:
1233                 //              Tests if an element is tab-navigable
1234         
1235                 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
1236                 if(attr(elem, "disabled")){
1237                         return false;
1238                 }else if(hasAttr(elem, "tabIndex")){
1239                         // Explicit tab index setting
1240                         return attr(elem, "tabIndex") >= 0; // boolean
1241                 }else{
1242                         // No explicit tabIndex setting, so depends on node type
1243                         return dijit.hasDefaultTabStop(elem);
1244                 }
1245         });
1246
1247         dijit._getTabNavigable = function(/*DOMNode*/ root){
1248                 // summary:
1249                 //              Finds descendants of the specified root node.
1250                 //
1251                 // description:
1252                 //              Finds the following descendants of the specified root node:
1253                 //              * the first tab-navigable element in document order
1254                 //                without a tabIndex or with tabIndex="0"
1255                 //              * the last tab-navigable element in document order
1256                 //                without a tabIndex or with tabIndex="0"
1257                 //              * the first element in document order with the lowest
1258                 //                positive tabIndex value
1259                 //              * the last element in document order with the highest
1260                 //                positive tabIndex value
1261                 var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
1262                 function radioName(node) {
1263                         // If this element is part of a radio button group, return the name for that group.
1264                         return node && node.tagName.toLowerCase() == "input" &&
1265                                 node.type && node.type.toLowerCase() == "radio" &&
1266                                 node.name && node.name.toLowerCase();
1267                 }
1268                 var walkTree = function(/*DOMNode*/parent){
1269                         dojo.query("> *", parent).forEach(function(child){
1270                                 // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
1271                                 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
1272                                 if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){
1273                                         return;
1274                                 }
1275
1276                                 if(isTabNavigable(child)){
1277                                         var tabindex = attr(child, "tabIndex");
1278                                         if(!hasAttr(child, "tabIndex") || tabindex == 0){
1279                                                 if(!first){ first = child; }
1280                                                 last = child;
1281                                         }else if(tabindex > 0){
1282                                                 if(!lowest || tabindex < lowestTabindex){
1283                                                         lowestTabindex = tabindex;
1284                                                         lowest = child;
1285                                                 }
1286                                                 if(!highest || tabindex >= highestTabindex){
1287                                                         highestTabindex = tabindex;
1288                                                         highest = child;
1289                                                 }
1290                                         }
1291                                         var rn = radioName(child);
1292                                         if(dojo.attr(child, "checked") && rn) {
1293                                                 radioSelected[rn] = child;
1294                                         }
1295                                 }
1296                                 if(child.nodeName.toUpperCase() != 'SELECT'){
1297                                         walkTree(child);
1298                                 }
1299                         });
1300                 };
1301                 if(shown(root)){ walkTree(root) }
1302                 function rs(node) {
1303                         // substitute checked radio button for unchecked one, if there is a checked one with the same name.
1304                         return radioSelected[radioName(node)] || node;
1305                 }
1306                 return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
1307         }
1308         dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
1309                 // summary:
1310                 //              Finds the descendant of the specified root node
1311                 //              that is first in the tabbing order
1312                 var elems = dijit._getTabNavigable(dojo.byId(root));
1313                 return elems.lowest ? elems.lowest : elems.first; // DomNode
1314         };
1315         
1316         dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
1317                 // summary:
1318                 //              Finds the descendant of the specified root node
1319                 //              that is last in the tabbing order
1320                 var elems = dijit._getTabNavigable(dojo.byId(root));
1321                 return elems.last ? elems.last : elems.highest; // DomNode
1322         };
1323         
1324         /*=====
1325         dojo.mixin(dijit, {
1326                 // defaultDuration: Integer
1327                 //              The default animation speed (in ms) to use for all Dijit
1328                 //              transitional animations, unless otherwise specified
1329                 //              on a per-instance basis. Defaults to 200, overrided by
1330                 //              `djConfig.defaultDuration`
1331                 defaultDuration: 200
1332         });
1333         =====*/
1334         
1335         dijit.defaultDuration = dojo.config["defaultDuration"] || 200;
1336
1337 })();
1338
1339 }
1340
1341 if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1342 dojo._hasResource["dijit._base.focus"] = true;
1343 dojo.provide("dijit._base.focus");
1344
1345
1346
1347
1348 // summary:
1349 //              These functions are used to query or set the focus and selection.
1350 //
1351 //              Also, they trace when widgets become activated/deactivated,
1352 //              so that the widget can fire _onFocus/_onBlur events.
1353 //              "Active" here means something similar to "focused", but
1354 //              "focus" isn't quite the right word because we keep track of
1355 //              a whole stack of "active" widgets.  Example: ComboButton --> Menu -->
1356 //              MenuItem.  The onBlur event for ComboButton doesn't fire due to focusing
1357 //              on the Menu or a MenuItem, since they are considered part of the
1358 //              ComboButton widget.  It only happens when focus is shifted
1359 //              somewhere completely different.
1360
1361 dojo.mixin(dijit, {
1362         // _curFocus: DomNode
1363         //              Currently focused item on screen
1364         _curFocus: null,
1365
1366         // _prevFocus: DomNode
1367         //              Previously focused item on screen
1368         _prevFocus: null,
1369
1370         isCollapsed: function(){
1371                 // summary:
1372                 //              Returns true if there is no text selected
1373                 return dijit.getBookmark().isCollapsed;
1374         },
1375
1376         getBookmark: function(){
1377                 // summary:
1378                 //              Retrieves a bookmark that can be used with moveToBookmark to return to the same range
1379                 var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus;
1380
1381                 if(dojo.global.getSelection){
1382                         //W3C Range API for selections.
1383                         sel = dojo.global.getSelection();
1384                         if(sel){
1385                                 if(sel.isCollapsed){
1386                                         tg = cf? cf.tagName : "";
1387                                         if(tg){
1388                                                 //Create a fake rangelike item to restore selections.
1389                                                 tg = tg.toLowerCase();
1390                                                 if(tg == "textarea" ||
1391                                                                 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
1392                                                         sel = {
1393                                                                 start: cf.selectionStart,
1394                                                                 end: cf.selectionEnd,
1395                                                                 node: cf,
1396                                                                 pRange: true
1397                                                         };
1398                                                         return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
1399                                                 }
1400                                         }
1401                                         bm = {isCollapsed:true};
1402                                         if(sel.rangeCount){
1403                                                 bm.mark = sel.getRangeAt(0).cloneRange();
1404                                         }
1405                                 }else{
1406                                         rg = sel.getRangeAt(0);
1407                                         bm = {isCollapsed: false, mark: rg.cloneRange()};
1408                                 }
1409                         }
1410                 }else if(sel){
1411                         // If the current focus was a input of some sort and no selection, don't bother saving
1412                         // a native bookmark.  This is because it causes issues with dialog/page selection restore.
1413                         // So, we need to create psuedo bookmarks to work with.
1414                         tg = cf ? cf.tagName : "";
1415                         tg = tg.toLowerCase();
1416                         if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
1417                                 if(sel.type && sel.type.toLowerCase() == "none"){
1418                                         return {
1419                                                 isCollapsed: true,
1420                                                 mark: null
1421                                         }
1422                                 }else{
1423                                         rg = sel.createRange();
1424                                         return {
1425                                                 isCollapsed: rg.text && rg.text.length?false:true,
1426                                                 mark: {
1427                                                         range: rg,
1428                                                         pRange: true
1429                                                 }
1430                                         };
1431                                 }
1432                         }
1433                         bm = {};
1434
1435                         //'IE' way for selections.
1436                         try{
1437                                 // createRange() throws exception when dojo in iframe
1438                                 //and nothing selected, see #9632
1439                                 rg = sel.createRange();
1440                                 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
1441                         }catch(e){
1442                                 bm.isCollapsed = true;
1443                                 return bm;
1444                         }
1445                         if(sel.type.toUpperCase() == 'CONTROL'){
1446                                 if(rg.length){
1447                                         bm.mark=[];
1448                                         var i=0,len=rg.length;
1449                                         while(i<len){
1450                                                 bm.mark.push(rg.item(i++));
1451                                         }
1452                                 }else{
1453                                         bm.isCollapsed = true;
1454                                         bm.mark = null;
1455                                 }
1456                         }else{
1457                                 bm.mark = rg.getBookmark();
1458                         }
1459                 }else{
1460                         console.warn("No idea how to store the current selection for this browser!");
1461                 }
1462                 return bm; // Object
1463         },
1464
1465         moveToBookmark: function(/*Object*/bookmark){
1466                 // summary:
1467                 //              Moves current selection to a bookmark
1468                 // bookmark:
1469                 //              This should be a returned object from dijit.getBookmark()
1470
1471                 var _doc = dojo.doc,
1472                         mark = bookmark.mark;
1473                 if(mark){
1474                         if(dojo.global.getSelection){
1475                                 //W3C Rangi API (FF, WebKit, Opera, etc)
1476                                 var sel = dojo.global.getSelection();
1477                                 if(sel && sel.removeAllRanges){
1478                                         if(mark.pRange){
1479                                                 var r = mark;
1480                                                 var n = r.node;
1481                                                 n.selectionStart = r.start;
1482                                                 n.selectionEnd = r.end;
1483                                         }else{
1484                                                 sel.removeAllRanges();
1485                                                 sel.addRange(mark);
1486                                         }
1487                                 }else{
1488                                         console.warn("No idea how to restore selection for this browser!");
1489                                 }
1490                         }else if(_doc.selection && mark){
1491                                 //'IE' way.
1492                                 var rg;
1493                                 if(mark.pRange){
1494                                         rg = mark.range;
1495                                 }else if(dojo.isArray(mark)){
1496                                         rg = _doc.body.createControlRange();
1497                                         //rg.addElement does not have call/apply method, so can not call it directly
1498                                         //rg is not available in "range.addElement(item)", so can't use that either
1499                                         dojo.forEach(mark, function(n){
1500                                                 rg.addElement(n);
1501                                         });
1502                                 }else{
1503                                         rg = _doc.body.createTextRange();
1504                                         rg.moveToBookmark(mark);
1505                                 }
1506                                 rg.select();
1507                         }
1508                 }
1509         },
1510
1511         getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
1512                 // summary:
1513                 //              Called as getFocus(), this returns an Object showing the current focus
1514                 //              and selected text.
1515                 //
1516                 //              Called as getFocus(widget), where widget is a (widget representing) a button
1517                 //              that was just pressed, it returns where focus was before that button
1518                 //              was pressed.   (Pressing the button may have either shifted focus to the button,
1519                 //              or removed focus altogether.)   In this case the selected text is not returned,
1520                 //              since it can't be accurately determined.
1521                 //
1522                 // menu: dijit._Widget or {domNode: DomNode} structure
1523                 //              The button that was just pressed.  If focus has disappeared or moved
1524                 //              to this button, returns the previous focus.  In this case the bookmark
1525                 //              information is already lost, and null is returned.
1526                 //
1527                 // openedForWindow:
1528                 //              iframe in which menu was opened
1529                 //
1530                 // returns:
1531                 //              A handle to restore focus/selection, to be passed to `dijit.focus`
1532                 var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus;
1533                 return {
1534                         node: node,
1535                         bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark),
1536                         openedForWindow: openedForWindow
1537                 }; // Object
1538         },
1539
1540         focus: function(/*Object || DomNode */ handle){
1541                 // summary:
1542                 //              Sets the focused node and the selection according to argument.
1543                 //              To set focus to an iframe's content, pass in the iframe itself.
1544                 // handle:
1545                 //              object returned by get(), or a DomNode
1546
1547                 if(!handle){ return; }
1548
1549                 var node = "node" in handle ? handle.node : handle,             // because handle is either DomNode or a composite object
1550                         bookmark = handle.bookmark,
1551                         openedForWindow = handle.openedForWindow,
1552                         collapsed = bookmark ? bookmark.isCollapsed : false;
1553
1554                 // Set the focus
1555                 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
1556                 // but we need to set focus to iframe.contentWindow
1557                 if(node){
1558                         var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
1559                         if(focusNode && focusNode.focus){
1560                                 try{
1561                                         // Gecko throws sometimes if setting focus is impossible,
1562                                         // node not displayed or something like that
1563                                         focusNode.focus();
1564                                 }catch(e){/*quiet*/}
1565                         }
1566                         dijit._onFocusNode(node);
1567                 }
1568
1569                 // set the selection
1570                 // do not need to restore if current selection is not empty
1571                 // (use keyboard to select a menu item) or if previous selection was collapsed
1572                 // as it may cause focus shift (Esp in IE).
1573                 if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){
1574                         if(openedForWindow){
1575                                 openedForWindow.focus();
1576                         }
1577                         try{
1578                                 dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]);
1579                         }catch(e2){
1580                                 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
1581                         }
1582                 }
1583         },
1584
1585         // _activeStack: dijit._Widget[]
1586         //              List of currently active widgets (focused widget and it's ancestors)
1587         _activeStack: [],
1588
1589         registerIframe: function(/*DomNode*/ iframe){
1590                 // summary:
1591                 //              Registers listeners on the specified iframe so that any click
1592                 //              or focus event on that iframe (or anything in it) is reported
1593                 //              as a focus/click event on the <iframe> itself.
1594                 // description:
1595                 //              Currently only used by editor.
1596                 // returns:
1597                 //              Handle to pass to unregisterIframe()
1598                 return dijit.registerWin(iframe.contentWindow, iframe);
1599         },
1600
1601         unregisterIframe: function(/*Object*/ handle){
1602                 // summary:
1603                 //              Unregisters listeners on the specified iframe created by registerIframe.
1604                 //              After calling be sure to delete or null out the handle itself.
1605                 // handle:
1606                 //              Handle returned by registerIframe()
1607
1608                 dijit.unregisterWin(handle);
1609         },
1610
1611         registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
1612                 // summary:
1613                 //              Registers listeners on the specified window (either the main
1614                 //              window or an iframe's window) to detect when the user has clicked somewhere
1615                 //              or focused somewhere.
1616                 // description:
1617                 //              Users should call registerIframe() instead of this method.
1618                 // targetWindow:
1619                 //              If specified this is the window associated with the iframe,
1620                 //              i.e. iframe.contentWindow.
1621                 // effectiveNode:
1622                 //              If specified, report any focus events inside targetWindow as
1623                 //              an event on effectiveNode, rather than on evt.target.
1624                 // returns:
1625                 //              Handle to pass to unregisterWin()
1626
1627                 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
1628
1629                 var mousedownListener = function(evt){
1630                         dijit._justMouseDowned = true;
1631                         setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
1632                         
1633                         // workaround weird IE bug where the click is on an orphaned node
1634                         // (first time clicking a Select/DropDownButton inside a TooltipDialog)
1635                         if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){
1636                                 return;
1637                         }
1638
1639                         dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
1640                 };
1641                 //dojo.connect(targetWindow, "onscroll", ???);
1642
1643                 // Listen for blur and focus events on targetWindow's document.
1644                 // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
1645                 // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
1646                 // fire.
1647                 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
1648                 // (at least for FF) the focus event doesn't fire on <html> or <body>.
1649                 var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document;
1650                 if(doc){
1651                         if(dojo.isIE){
1652                                 targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
1653                                 var activateListener = function(evt){
1654                                         // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
1655                                         // Should consider those more like a mouse-click than a focus....
1656                                         if(evt.srcElement.tagName.toLowerCase() != "#document" &&
1657                                                 dijit.isTabNavigable(evt.srcElement)){
1658                                                 dijit._onFocusNode(effectiveNode || evt.srcElement);
1659                                         }else{
1660                                                 dijit._onTouchNode(effectiveNode || evt.srcElement);
1661                                         }
1662                                 };
1663                                 doc.attachEvent('onactivate', activateListener);
1664                                 var deactivateListener =  function(evt){
1665                                         dijit._onBlurNode(effectiveNode || evt.srcElement);
1666                                 };
1667                                 doc.attachEvent('ondeactivate', deactivateListener);
1668
1669                                 return function(){
1670                                         targetWindow.document.detachEvent('onmousedown', mousedownListener);
1671                                         doc.detachEvent('onactivate', activateListener);
1672                                         doc.detachEvent('ondeactivate', deactivateListener);
1673                                         doc = null;     // prevent memory leak (apparent circular reference via closure)
1674                                 };
1675                         }else{
1676                                 doc.body.addEventListener('mousedown', mousedownListener, true);
1677                                 var focusListener = function(evt){
1678                                         dijit._onFocusNode(effectiveNode || evt.target);
1679                                 };
1680                                 doc.addEventListener('focus', focusListener, true);
1681                                 var blurListener = function(evt){
1682                                         dijit._onBlurNode(effectiveNode || evt.target);
1683                                 };
1684                                 doc.addEventListener('blur', blurListener, true);
1685
1686                                 return function(){
1687                                         doc.body.removeEventListener('mousedown', mousedownListener, true);
1688                                         doc.removeEventListener('focus', focusListener, true);
1689                                         doc.removeEventListener('blur', blurListener, true);
1690                                         doc = null;     // prevent memory leak (apparent circular reference via closure)
1691                                 };
1692                         }
1693                 }
1694         },
1695
1696         unregisterWin: function(/*Handle*/ handle){
1697                 // summary:
1698                 //              Unregisters listeners on the specified window (either the main
1699                 //              window or an iframe's window) according to handle returned from registerWin().
1700                 //              After calling be sure to delete or null out the handle itself.
1701
1702                 // Currently our handle is actually a function
1703                 handle && handle();
1704         },
1705
1706         _onBlurNode: function(/*DomNode*/ node){
1707                 // summary:
1708                 //              Called when focus leaves a node.
1709                 //              Usually ignored, _unless_ it *isn't* follwed by touching another node,
1710                 //              which indicates that we tabbed off the last field on the page,
1711                 //              in which case every widget is marked inactive
1712                 dijit._prevFocus = dijit._curFocus;
1713                 dijit._curFocus = null;
1714
1715                 if(dijit._justMouseDowned){
1716                         // the mouse down caused a new widget to be marked as active; this blur event
1717                         // is coming late, so ignore it.
1718                         return;
1719                 }
1720
1721                 // if the blur event isn't followed by a focus event then mark all widgets as inactive.
1722                 if(dijit._clearActiveWidgetsTimer){
1723                         clearTimeout(dijit._clearActiveWidgetsTimer);
1724                 }
1725                 dijit._clearActiveWidgetsTimer = setTimeout(function(){
1726                         delete dijit._clearActiveWidgetsTimer;
1727                         dijit._setStack([]);
1728                         dijit._prevFocus = null;
1729                 }, 100);
1730         },
1731
1732         _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
1733                 // summary:
1734                 //              Callback when node is focused or mouse-downed
1735                 // node:
1736                 //              The node that was touched.
1737                 // by:
1738                 //              "mouse" if the focus/touch was caused by a mouse down event
1739
1740                 // ignore the recent blurNode event
1741                 if(dijit._clearActiveWidgetsTimer){
1742                         clearTimeout(dijit._clearActiveWidgetsTimer);
1743                         delete dijit._clearActiveWidgetsTimer;
1744                 }
1745
1746                 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
1747                 var newStack=[];
1748                 try{
1749                         while(node){
1750                                 var popupParent = dojo.attr(node, "dijitPopupParent");
1751                                 if(popupParent){
1752                                         node=dijit.byId(popupParent).domNode;
1753                                 }else if(node.tagName && node.tagName.toLowerCase() == "body"){
1754                                         // is this the root of the document or just the root of an iframe?
1755                                         if(node === dojo.body()){
1756                                                 // node is the root of the main document
1757                                                 break;
1758                                         }
1759                                         // otherwise, find the iframe this node refers to (can't access it via parentNode,
1760                                         // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
1761                                         node=dojo.window.get(node.ownerDocument).frameElement;
1762                                 }else{
1763                                         // if this node is the root node of a widget, then add widget id to stack,
1764                                         // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
1765                                         // to support MenuItem)
1766                                         var id = node.getAttribute && node.getAttribute("widgetId"),
1767                                                 widget = id && dijit.byId(id);
1768                                         if(widget && !(by == "mouse" && widget.get("disabled"))){
1769                                                 newStack.unshift(id);
1770                                         }
1771                                         node=node.parentNode;
1772                                 }
1773                         }
1774                 }catch(e){ /* squelch */ }
1775
1776                 dijit._setStack(newStack, by);
1777         },
1778
1779         _onFocusNode: function(/*DomNode*/ node){
1780                 // summary:
1781                 //              Callback when node is focused
1782
1783                 if(!node){
1784                         return;
1785                 }
1786
1787                 if(node.nodeType == 9){
1788                         // Ignore focus events on the document itself.  This is here so that
1789                         // (for example) clicking the up/down arrows of a spinner
1790                         // (which don't get focus) won't cause that widget to blur. (FF issue)
1791                         return;
1792                 }
1793
1794                 dijit._onTouchNode(node);
1795
1796                 if(node == dijit._curFocus){ return; }
1797                 if(dijit._curFocus){
1798                         dijit._prevFocus = dijit._curFocus;
1799                 }
1800                 dijit._curFocus = node;
1801                 dojo.publish("focusNode", [node]);
1802         },
1803
1804         _setStack: function(/*String[]*/ newStack, /*String*/ by){
1805                 // summary:
1806                 //              The stack of active widgets has changed.  Send out appropriate events and records new stack.
1807                 // newStack:
1808                 //              array of widget id's, starting from the top (outermost) widget
1809                 // by:
1810                 //              "mouse" if the focus/touch was caused by a mouse down event
1811
1812                 var oldStack = dijit._activeStack;
1813                 dijit._activeStack = newStack;
1814
1815                 // compare old stack to new stack to see how many elements they have in common
1816                 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
1817                         if(oldStack[nCommon] != newStack[nCommon]){
1818                                 break;
1819                         }
1820                 }
1821
1822                 var widget;
1823                 // for all elements that have gone out of focus, send blur event
1824                 for(var i=oldStack.length-1; i>=nCommon; i--){
1825                         widget = dijit.byId(oldStack[i]);
1826                         if(widget){
1827                                 widget._focused = false;
1828                                 widget.set("focused", false);
1829                                 widget._hasBeenBlurred = true;
1830                                 if(widget._onBlur){
1831                                         widget._onBlur(by);
1832                                 }
1833                                 dojo.publish("widgetBlur", [widget, by]);
1834                         }
1835                 }
1836
1837                 // for all element that have come into focus, send focus event
1838                 for(i=nCommon; i<newStack.length; i++){
1839                         widget = dijit.byId(newStack[i]);
1840                         if(widget){
1841                                 widget._focused = true;
1842                                 widget.set("focused", true);
1843                                 if(widget._onFocus){
1844                                         widget._onFocus(by);
1845                                 }
1846                                 dojo.publish("widgetFocus", [widget, by]);
1847                         }
1848                 }
1849         }
1850 });
1851
1852 // register top window and all the iframes it contains
1853 dojo.addOnLoad(function(){
1854         var handle = dijit.registerWin(window);
1855         if(dojo.isIE){
1856                 dojo.addOnWindowUnload(function(){
1857                         dijit.unregisterWin(handle);
1858                         handle = null;
1859                 })
1860         }
1861 });
1862
1863 }
1864
1865 if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1866 dojo._hasResource["dojo.AdapterRegistry"] = true;
1867 dojo.provide("dojo.AdapterRegistry");
1868
1869
1870 dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){
1871         //      summary:
1872         //              A registry to make contextual calling/searching easier.
1873         //      description:
1874         //              Objects of this class keep list of arrays in the form [name, check,
1875         //              wrap, directReturn] that are used to determine what the contextual
1876         //              result of a set of checked arguments is. All check/wrap functions
1877         //              in this registry should be of the same arity.
1878         //      example:
1879         //      |       // create a new registry
1880         //      |       var reg = new dojo.AdapterRegistry();
1881         //      |       reg.register("handleString",
1882         //      |               dojo.isString,
1883         //      |               function(str){
1884         //      |                       // do something with the string here
1885         //      |               }
1886         //      |       );
1887         //      |       reg.register("handleArr",
1888         //      |               dojo.isArray,
1889         //      |               function(arr){
1890         //      |                       // do something with the array here
1891         //      |               }
1892         //      |       );
1893         //      |
1894         //      |       // now we can pass reg.match() *either* an array or a string and
1895         //      |       // the value we pass will get handled by the right function
1896         //      |       reg.match("someValue"); // will call the first function
1897         //      |       reg.match(["someValue"]); // will call the second
1898
1899         this.pairs = [];
1900         this.returnWrappers = returnWrappers || false; // Boolean
1901 };
1902
1903 dojo.extend(dojo.AdapterRegistry, {
1904         register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){
1905                 //      summary:
1906                 //              register a check function to determine if the wrap function or
1907                 //              object gets selected
1908                 //      name:
1909                 //              a way to identify this matcher.
1910                 //      check:
1911                 //              a function that arguments are passed to from the adapter's
1912                 //              match() function.  The check function should return true if the
1913                 //              given arguments are appropriate for the wrap function.
1914                 //      directReturn:
1915                 //              If directReturn is true, the value passed in for wrap will be
1916                 //              returned instead of being called. Alternately, the
1917                 //              AdapterRegistry can be set globally to "return not call" using
1918                 //              the returnWrappers property. Either way, this behavior allows
1919                 //              the registry to act as a "search" function instead of a
1920                 //              function interception library.
1921                 //      override:
1922                 //              If override is given and true, the check function will be given
1923                 //              highest priority. Otherwise, it will be the lowest priority
1924                 //              adapter.
1925                 this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]);
1926         },
1927
1928         match: function(/* ... */){
1929                 // summary:
1930                 //              Find an adapter for the given arguments. If no suitable adapter
1931                 //              is found, throws an exception. match() accepts any number of
1932                 //              arguments, all of which are passed to all matching functions
1933                 //              from the registered pairs.
1934                 for(var i = 0; i < this.pairs.length; i++){
1935                         var pair = this.pairs[i];
1936                         if(pair[1].apply(this, arguments)){
1937                                 if((pair[3])||(this.returnWrappers)){
1938                                         return pair[2];
1939                                 }else{
1940                                         return pair[2].apply(this, arguments);
1941                                 }
1942                         }
1943                 }
1944                 throw new Error("No match found");
1945         },
1946
1947         unregister: function(name){
1948                 // summary: Remove a named adapter from the registry
1949
1950                 // FIXME: this is kind of a dumb way to handle this. On a large
1951                 // registry this will be slow-ish and we can use the name as a lookup
1952                 // should we choose to trade memory for speed.
1953                 for(var i = 0; i < this.pairs.length; i++){
1954                         var pair = this.pairs[i];
1955                         if(pair[0] == name){
1956                                 this.pairs.splice(i, 1);
1957                                 return true;
1958                         }
1959                 }
1960                 return false;
1961         }
1962 });
1963
1964 }
1965
1966 if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1967 dojo._hasResource["dijit._base.place"] = true;
1968 dojo.provide("dijit._base.place");
1969
1970
1971
1972
1973 dijit.getViewport = function(){
1974         // summary:
1975         //              Returns the dimensions and scroll position of the viewable area of a browser window
1976
1977         return dojo.window.getBox();
1978 };
1979
1980 /*=====
1981 dijit.__Position = function(){
1982         // x: Integer
1983         //              horizontal coordinate in pixels, relative to document body
1984         // y: Integer
1985         //              vertical coordinate in pixels, relative to document body
1986
1987         thix.x = x;
1988         this.y = y;
1989 }
1990 =====*/
1991
1992
1993 dijit.placeOnScreen = function(
1994         /* DomNode */                   node,
1995         /* dijit.__Position */  pos,
1996         /* String[] */                  corners,
1997         /* dijit.__Position? */ padding){
1998         // summary:
1999         //              Positions one of the node's corners at specified position
2000         //              such that node is fully visible in viewport.
2001         // description:
2002         //              NOTE: node is assumed to be absolutely or relatively positioned.
2003         //      pos:
2004         //              Object like {x: 10, y: 20}
2005         //      corners:
2006         //              Array of Strings representing order to try corners in, like ["TR", "BL"].
2007         //              Possible values are:
2008         //                      * "BL" - bottom left
2009         //                      * "BR" - bottom right
2010         //                      * "TL" - top left
2011         //                      * "TR" - top right
2012         //      padding:
2013         //              set padding to put some buffer around the element you want to position.
2014         // example:
2015         //              Try to place node's top right corner at (10,20).
2016         //              If that makes node go (partially) off screen, then try placing
2017         //              bottom left corner at (10,20).
2018         //      |       placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
2019
2020         var choices = dojo.map(corners, function(corner){
2021                 var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
2022                 if(padding){
2023                         c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
2024                         c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
2025                 }
2026                 return c;
2027         });
2028
2029         return dijit._place(node, choices);
2030 }
2031
2032 dijit._place = function(/*DomNode*/ node, choices, layoutNode, /*Object*/ aroundNodeCoords){
2033         // summary:
2034         //              Given a list of spots to put node, put it at the first spot where it fits,
2035         //              of if it doesn't fit anywhere then the place with the least overflow
2036         // choices: Array
2037         //              Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
2038         //              Above example says to put the top-left corner of the node at (10,20)
2039         // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
2040         //              for things like tooltip, they are displayed differently (and have different dimensions)
2041         //              based on their orientation relative to the parent.   This adjusts the popup based on orientation.
2042         //              It also passes in the available size for the popup, which is useful for tooltips to
2043         //              tell them that their width is limited to a certain amount.   layoutNode() may return a value expressing
2044         //              how much the popup had to be modified to fit into the available space.   This is used to determine
2045         //              what the best placement is.
2046         // aroundNodeCoords: Object
2047         //              Size of aroundNode, ex: {w: 200, h: 50}
2048
2049         // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
2050         // viewport over document
2051         var view = dojo.window.getBox();
2052
2053         // This won't work if the node is inside a <div style="position: relative">,
2054         // so reattach it to dojo.doc.body.   (Otherwise, the positioning will be wrong
2055         // and also it might get cutoff)
2056         if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
2057                 dojo.body().appendChild(node);
2058         }
2059
2060         var best = null;
2061         dojo.some(choices, function(choice){
2062                 var corner = choice.corner;
2063                 var pos = choice.pos;
2064                 var overflow = 0;
2065
2066                 // calculate amount of space available given specified position of node
2067                 var spaceAvailable = {
2068                         w: corner.charAt(1) == 'L' ? (view.l + view.w) - pos.x : pos.x - view.l,
2069                         h: corner.charAt(1) == 'T' ? (view.t + view.h) - pos.y : pos.y - view.t
2070                 };
2071
2072                 // configure node to be displayed in given position relative to button
2073                 // (need to do this in order to get an accurate size for the node, because
2074                 // a tooltip's size changes based on position, due to triangle)
2075                 if(layoutNode){
2076                         var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
2077                         overflow = typeof res == "undefined" ? 0 : res;
2078                 }
2079
2080                 // get node's size
2081                 var style = node.style;
2082                 var oldDisplay = style.display;
2083                 var oldVis = style.visibility;
2084                 style.visibility = "hidden";
2085                 style.display = "";
2086                 var mb = dojo.marginBox(node);
2087                 style.display = oldDisplay;
2088                 style.visibility = oldVis;
2089
2090                 // coordinates and size of node with specified corner placed at pos,
2091                 // and clipped by viewport
2092                 var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)),
2093                         startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)),
2094                         endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x),
2095                         endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y),
2096                         width = endX - startX,
2097                         height = endY - startY;
2098
2099                 overflow += (mb.w - width) + (mb.h - height);
2100
2101                 if(best == null || overflow < best.overflow){
2102                         best = {
2103                                 corner: corner,
2104                                 aroundCorner: choice.aroundCorner,
2105                                 x: startX,
2106                                 y: startY,
2107                                 w: width,
2108                                 h: height,
2109                                 overflow: overflow,
2110                                 spaceAvailable: spaceAvailable
2111                         };
2112                 }
2113                 
2114                 return !overflow;
2115         });
2116
2117         // In case the best position is not the last one we checked, need to call
2118         // layoutNode() again.
2119         if(best.overflow && layoutNode){
2120                 layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
2121         }
2122
2123         // And then position the node.   Do this last, after the layoutNode() above
2124         // has sized the node, due to browser quirks when the viewport is scrolled
2125         // (specifically that a Tooltip will shrink to fit as though the window was
2126         // scrolled to the left).
2127         //
2128         // In RTL mode, set style.right rather than style.left so in the common case,
2129         // window resizes move the popup along with the aroundNode.
2130         var l = dojo._isBodyLtr(),
2131                 s = node.style;
2132         s.top = best.y + "px";
2133         s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
2134         
2135         return best;
2136 }
2137
2138 dijit.placeOnScreenAroundNode = function(
2139         /* DomNode */           node,
2140         /* DomNode */           aroundNode,
2141         /* Object */            aroundCorners,
2142         /* Function? */         layoutNode){
2143
2144         // summary:
2145         //              Position node adjacent or kitty-corner to aroundNode
2146         //              such that it's fully visible in viewport.
2147         //
2148         // description:
2149         //              Place node such that corner of node touches a corner of
2150         //              aroundNode, and that node is fully visible.
2151         //
2152         // aroundCorners:
2153         //              Ordered list of pairs of corners to try matching up.
2154         //              Each pair of corners is represented as a key/value in the hash,
2155         //              where the key corresponds to the aroundNode's corner, and
2156         //              the value corresponds to the node's corner:
2157         //
2158         //      |       { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...}
2159         //
2160         //              The following strings are used to represent the four corners:
2161         //                      * "BL" - bottom left
2162         //                      * "BR" - bottom right
2163         //                      * "TL" - top left
2164         //                      * "TR" - top right
2165         //
2166         // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
2167         //              For things like tooltip, they are displayed differently (and have different dimensions)
2168         //              based on their orientation relative to the parent.   This adjusts the popup based on orientation.
2169         //
2170         // example:
2171         //      |       dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
2172         //              This will try to position node such that node's top-left corner is at the same position
2173         //              as the bottom left corner of the aroundNode (ie, put node below
2174         //              aroundNode, with left edges aligned).  If that fails it will try to put
2175         //              the bottom-right corner of node where the top right corner of aroundNode is
2176         //              (ie, put node above aroundNode, with right edges aligned)
2177         //
2178
2179         // get coordinates of aroundNode
2180         aroundNode = dojo.byId(aroundNode);
2181         var aroundNodePos = dojo.position(aroundNode, true);
2182
2183         // place the node around the calculated rectangle
2184         return dijit._placeOnScreenAroundRect(node,
2185                 aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h,     // rectangle
2186                 aroundCorners, layoutNode);
2187 };
2188
2189 /*=====
2190 dijit.__Rectangle = function(){
2191         // x: Integer
2192         //              horizontal offset in pixels, relative to document body
2193         // y: Integer
2194         //              vertical offset in pixels, relative to document body
2195         // width: Integer
2196         //              width in pixels
2197         // height: Integer
2198         //              height in pixels
2199
2200         this.x = x;
2201         this.y = y;
2202         this.width = width;
2203         this.height = height;
2204 }
2205 =====*/
2206
2207
2208 dijit.placeOnScreenAroundRectangle = function(
2209         /* DomNode */                   node,
2210         /* dijit.__Rectangle */ aroundRect,
2211         /* Object */                    aroundCorners,
2212         /* Function */                  layoutNode){
2213
2214         // summary:
2215         //              Like dijit.placeOnScreenAroundNode(), except that the "around"
2216         //              parameter is an arbitrary rectangle on the screen (x, y, width, height)
2217         //              instead of a dom node.
2218
2219         return dijit._placeOnScreenAroundRect(node,
2220                 aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height,        // rectangle
2221                 aroundCorners, layoutNode);
2222 };
2223
2224 dijit._placeOnScreenAroundRect = function(
2225         /* DomNode */           node,
2226         /* Number */            x,
2227         /* Number */            y,
2228         /* Number */            width,
2229         /* Number */            height,
2230         /* Object */            aroundCorners,
2231         /* Function */          layoutNode){
2232
2233         // summary:
2234         //              Like dijit.placeOnScreenAroundNode(), except it accepts coordinates
2235         //              of a rectangle to place node adjacent to.
2236
2237         // TODO: combine with placeOnScreenAroundRectangle()
2238
2239         // Generate list of possible positions for node
2240         var choices = [];
2241         for(var nodeCorner in aroundCorners){
2242                 choices.push( {
2243                         aroundCorner: nodeCorner,
2244                         corner: aroundCorners[nodeCorner],
2245                         pos: {
2246                                 x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width),
2247                                 y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height)
2248                         }
2249                 });
2250         }
2251
2252         return dijit._place(node, choices, layoutNode, {w: width, h: height});
2253 };
2254
2255 dijit.placementRegistry= new dojo.AdapterRegistry();
2256 dijit.placementRegistry.register("node",
2257         function(n, x){
2258                 return typeof x == "object" &&
2259                         typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined";
2260         },
2261         dijit.placeOnScreenAroundNode);
2262 dijit.placementRegistry.register("rect",
2263         function(n, x){
2264                 return typeof x == "object" &&
2265                         "x" in x && "y" in x && "width" in x && "height" in x;
2266         },
2267         dijit.placeOnScreenAroundRectangle);
2268
2269 dijit.placeOnScreenAroundElement = function(
2270         /* DomNode */           node,
2271         /* Object */            aroundElement,
2272         /* Object */            aroundCorners,
2273         /* Function */          layoutNode){
2274
2275         // summary:
2276         //              Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
2277         //              for the "around" argument and finds a proper processor to place a node.
2278
2279         return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments);
2280 };
2281
2282 dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
2283         // summary:
2284         //              Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
2285         //
2286         // position: String[]
2287         //              This variable controls the position of the drop down.
2288         //              It's an array of strings with the following values:
2289         //
2290         //                      * before: places drop down to the left of the target node/widget, or to the right in
2291         //                        the case of RTL scripts like Hebrew and Arabic
2292         //                      * after: places drop down to the right of the target node/widget, or to the left in
2293         //                        the case of RTL scripts like Hebrew and Arabic
2294         //                      * above: drop down goes above target node
2295         //                      * below: drop down goes below target node
2296         //
2297         //              The list is positions is tried, in order, until a position is found where the drop down fits
2298         //              within the viewport.
2299         //
2300         // leftToRight: Boolean
2301         //              Whether the popup will be displaying in leftToRight mode.
2302         //
2303         var align = {};
2304         dojo.forEach(position, function(pos){
2305                 switch(pos){
2306                         case "after":
2307                                 align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
2308                                 break;
2309                         case "before":
2310                                 align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
2311                                 break;
2312                         case "below-alt":
2313                                 leftToRight = !leftToRight;
2314                                 // fall through
2315                         case "below":
2316                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
2317                                 align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR";
2318                                 align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL";
2319                                 break;
2320                         case "above-alt":
2321                                 leftToRight = !leftToRight;
2322                                 // fall through
2323                         case "above":
2324                         default:
2325                                 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
2326                                 align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR";
2327                                 align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL";
2328                                 break;
2329                 }
2330         });
2331         return align;
2332 };
2333
2334 }
2335
2336 if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2337 dojo._hasResource["dijit._base.window"] = true;
2338 dojo.provide("dijit._base.window");
2339
2340
2341
2342 dijit.getDocumentWindow = function(doc){
2343         return dojo.window.get(doc);
2344 };
2345
2346 }
2347
2348 if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2349 dojo._hasResource["dijit._base.popup"] = true;
2350 dojo.provide("dijit._base.popup");
2351
2352
2353
2354
2355
2356 /*=====
2357 dijit.popup.__OpenArgs = function(){
2358         // popup: Widget
2359         //              widget to display
2360         // parent: Widget
2361         //              the button etc. that is displaying this popup
2362         // around: DomNode
2363         //              DOM node (typically a button); place popup relative to this node.  (Specify this *or* "x" and "y" parameters.)
2364         // x: Integer
2365         //              Absolute horizontal position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
2366         // y: Integer
2367         //              Absolute vertical position (in pixels) to place node at.  (Specify this *or* "around" parameter.)
2368         // orient: Object|String
2369         //              When the around parameter is specified, orient should be an
2370         //              ordered list of tuples of the form (around-node-corner, popup-node-corner).
2371         //              dijit.popup.open() tries to position the popup according to each tuple in the list, in order,
2372         //              until the popup appears fully within the viewport.
2373         //
2374         //              The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples:
2375         //                      1. (BL, TL)
2376         //                      2. (TL, BL)
2377         //              where BL means "bottom left" and "TL" means "top left".
2378         //              So by default, it first tries putting the popup below the around node, left-aligning them,
2379         //              and then tries to put it above the around node, still left-aligning them.   Note that the
2380         //              default is horizontally reversed when in RTL mode.
2381         //
2382         //              When an (x,y) position is specified rather than an around node, orient is either
2383         //              "R" or "L".  R (for right) means that it tries to put the popup to the right of the mouse,
2384         //              specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
2385         //              fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
2386         //              and the top-right corner.
2387         // onCancel: Function
2388         //              callback when user has canceled the popup by
2389         //                      1. hitting ESC or
2390         //                      2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
2391         //                         i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
2392         // onClose: Function
2393         //              callback whenever this popup is closed
2394         // onExecute: Function
2395         //              callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
2396         // padding: dijit.__Position
2397         //              adding a buffer around the opening position. This is only useful when around is not set.
2398         this.popup = popup;
2399         this.parent = parent;
2400         this.around = around;
2401         this.x = x;
2402         this.y = y;
2403         this.orient = orient;
2404         this.onCancel = onCancel;
2405         this.onClose = onClose;
2406         this.onExecute = onExecute;
2407         this.padding = padding;
2408 }
2409 =====*/
2410
2411 dijit.popup = {
2412         // summary:
2413         //              This singleton is used to show/hide widgets as popups.
2414
2415         // _stack: dijit._Widget[]
2416         //              Stack of currently popped up widgets.
2417         //              (someone opened _stack[0], and then it opened _stack[1], etc.)
2418         _stack: [],
2419         
2420         // _beginZIndex: Number
2421         //              Z-index of the first popup.   (If first popup opens other
2422         //              popups they get a higher z-index.)
2423         _beginZIndex: 1000,
2424
2425         _idGen: 1,
2426
2427         _createWrapper: function(/*Widget || DomNode*/ widget){
2428                 // summary:
2429                 //              Initialization for widgets that will be used as popups.
2430                 //              Puts widget inside a wrapper DIV (if not already in one),
2431                 //              and returns pointer to that wrapper DIV.
2432
2433                 var wrapper = widget.declaredClass ? widget._popupWrapper : (widget.parentNode && dojo.hasClass(widget.parentNode, "dijitPopup")),
2434                         node = widget.domNode || widget;
2435
2436                 if(!wrapper){
2437                         // Create wrapper <div> for when this widget [in the future] will be used as a popup.
2438                         // This is done early because of IE bugs where creating/moving DOM nodes causes focus
2439                         // to go wonky, see tests/robot/Toolbar.html to reproduce
2440                         wrapper = dojo.create("div",{
2441                                 "class":"dijitPopup",
2442                                 style:{ display: "none"},
2443                                 role: "presentation"
2444                         }, dojo.body());
2445                         wrapper.appendChild(node);
2446
2447                         var s = node.style;
2448                         s.display = "";
2449                         s.visibility = "";
2450                         s.position = "";
2451                         s.top = "0px";
2452
2453                         if(widget.declaredClass){               // TODO: in 2.0 change signature to always take widget, then remove if()
2454                                 widget._popupWrapper = wrapper;
2455                                 dojo.connect(widget, "destroy", function(){
2456                                         dojo.destroy(wrapper);
2457                                         delete widget._popupWrapper;
2458                                 });
2459                         }
2460                 }
2461                 
2462                 return wrapper;
2463         },
2464
2465         moveOffScreen: function(/*Widget || DomNode*/ widget){
2466                 // summary:
2467                 //              Moves the popup widget off-screen.
2468                 //              Do not use this method to hide popups when not in use, because
2469                 //              that will create an accessibility issue: the offscreen popup is
2470                 //              still in the tabbing order.
2471
2472                 // Create wrapper if not already there
2473                 var wrapper = this._createWrapper(widget);
2474
2475                 dojo.style(wrapper, {
2476                         visibility: "hidden",
2477                         top: "-9999px",         // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
2478                         display: ""
2479                 });
2480         },
2481
2482         hide: function(/*dijit._Widget*/ widget){
2483                 // summary:
2484                 //              Hide this popup widget (until it is ready to be shown).
2485                 //              Initialization for widgets that will be used as popups
2486                 //
2487                 //              Also puts widget inside a wrapper DIV (if not already in one)
2488                 //
2489                 //              If popup widget needs to layout it should
2490                 //              do so when it is made visible, and popup._onShow() is called.
2491
2492                 // Create wrapper if not already there
2493                 var wrapper = this._createWrapper(widget);
2494
2495                 dojo.style(wrapper, "display", "none");
2496         },
2497                 
2498         getTopPopup: function(){
2499                 // summary:
2500                 //              Compute the closest ancestor popup that's *not* a child of another popup.
2501                 //              Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
2502                 var stack = this._stack;
2503                 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
2504                         /* do nothing, just trying to get right value for pi */
2505                 }
2506                 return stack[pi];
2507         },
2508
2509         open: function(/*dijit.popup.__OpenArgs*/ args){
2510                 // summary:
2511                 //              Popup the widget at the specified position
2512                 //
2513                 // example:
2514                 //              opening at the mouse position
2515                 //              |               dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
2516                 //
2517                 // example:
2518                 //              opening the widget as a dropdown
2519                 //              |               dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
2520                 //
2521                 //              Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
2522                 //              (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
2523
2524                 var stack = this._stack,
2525                         widget = args.popup,
2526                         orient = args.orient || (
2527                                 (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ?
2528                                 {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} :
2529                                 {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'}
2530                         ),
2531                         around = args.around,
2532                         id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
2533
2534                 // If we are opening a new popup that isn't a child of a currently opened popup, then
2535                 // close currently opened popup(s).   This should happen automatically when the old popups
2536                 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
2537                 while(stack.length && (!args.parent || !dojo.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
2538                         dijit.popup.close(stack[stack.length-1].widget);
2539                 }
2540
2541                 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
2542                 var wrapper = this._createWrapper(widget);
2543
2544
2545                 dojo.attr(wrapper, {
2546                         id: id,
2547                         style: {
2548                                 zIndex: this._beginZIndex + stack.length
2549                         },
2550                         "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
2551                         dijitPopupParent: args.parent ? args.parent.id : ""
2552                 });
2553
2554                 if(dojo.isIE || dojo.isMoz){
2555                         if(!widget.bgIframe){
2556                                 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
2557                                 widget.bgIframe = new dijit.BackgroundIframe(wrapper);
2558                         }
2559                 }
2560
2561                 // position the wrapper node and make it visible
2562                 var best = around ?
2563                         dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
2564                         dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
2565
2566                 wrapper.style.display = "";
2567                 wrapper.style.visibility = "visible";
2568                 widget.domNode.style.visibility = "visible";    // counteract effects from _HasDropDown
2569
2570                 var handlers = [];
2571
2572                 // provide default escape and tab key handling
2573                 // (this will work for any widget, not just menu)
2574                 handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
2575                         if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){
2576                                 dojo.stopEvent(evt);
2577                                 args.onCancel();
2578                         }else if(evt.charOrCode === dojo.keys.TAB){
2579                                 dojo.stopEvent(evt);
2580                                 var topPopup = this.getTopPopup();
2581                                 if(topPopup && topPopup.onCancel){
2582                                         topPopup.onCancel();
2583                                 }
2584                         }
2585                 }));
2586
2587                 // watch for cancel/execute events on the popup and notify the caller
2588                 // (for a menu, "execute" means clicking an item)
2589                 if(widget.onCancel){
2590                         handlers.push(dojo.connect(widget, "onCancel", args.onCancel));
2591                 }
2592
2593                 handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){
2594                         var topPopup = this.getTopPopup();
2595                         if(topPopup && topPopup.onExecute){
2596                                 topPopup.onExecute();
2597                         }
2598                 }));
2599
2600                 stack.push({
2601                         widget: widget,
2602                         parent: args.parent,
2603                         onExecute: args.onExecute,
2604                         onCancel: args.onCancel,
2605                         onClose: args.onClose,
2606                         handlers: handlers
2607                 });
2608
2609                 if(widget.onOpen){
2610                         // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
2611                         widget.onOpen(best);
2612                 }
2613
2614                 return best;
2615         },
2616
2617         close: function(/*dijit._Widget?*/ popup){
2618                 // summary:
2619                 //              Close specified popup and any popups that it parented.
2620                 //              If no popup is specified, closes all popups.
2621
2622                 var stack = this._stack;
2623
2624                 // Basically work backwards from the top of the stack closing popups
2625                 // until we hit the specified popup, but IIRC there was some issue where closing
2626                 // a popup would cause others to close too.  Thus if we are trying to close B in [A,B,C]
2627                 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
2628                 // so the while condition is constructed defensively.
2629                 while((popup && dojo.some(stack, function(elem){return elem.widget == popup;})) ||
2630                         (!popup && stack.length)){
2631                         var top = stack.pop(),
2632                                 widget = top.widget,
2633                                 onClose = top.onClose;
2634
2635                         if(widget.onClose){
2636                                 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
2637                                 widget.onClose();
2638                         }
2639                         dojo.forEach(top.handlers, dojo.disconnect);
2640
2641                         // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
2642                         if(widget && widget.domNode){
2643                                 this.hide(widget);
2644                         }
2645                         
2646                         if(onClose){
2647                                 onClose();
2648                         }
2649                 }
2650         }
2651 };
2652
2653 // TODO: remove dijit._frames, it isn't being used much, since popups never release their
2654 // iframes (see [22236])
2655 dijit._frames = new function(){
2656         // summary:
2657         //              cache of iframes
2658
2659         var queue = [];
2660
2661         this.pop = function(){
2662                 var iframe;
2663                 if(queue.length){
2664                         iframe = queue.pop();
2665                         iframe.style.display="";
2666                 }else{
2667                         if(dojo.isIE < 9){
2668                                 var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\"";
2669                                 var html="<iframe src='" + burl + "'"
2670                                         + " style='position: absolute; left: 0px; top: 0px;"
2671                                         + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
2672                                 iframe = dojo.doc.createElement(html);
2673                         }else{
2674                                 iframe = dojo.create("iframe");
2675                                 iframe.src = 'javascript:""';
2676                                 iframe.className = "dijitBackgroundIframe";
2677                                 dojo.style(iframe, "opacity", 0.1);
2678                         }
2679                         iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
2680                         dijit.setWaiRole(iframe,"presentation");
2681                 }
2682                 return iframe;
2683         };
2684
2685         this.push = function(iframe){
2686                 iframe.style.display="none";
2687                 queue.push(iframe);
2688         }
2689 }();
2690
2691
2692 dijit.BackgroundIframe = function(/*DomNode*/ node){
2693         // summary:
2694         //              For IE/FF z-index schenanigans. id attribute is required.
2695         //
2696         // description:
2697         //              new dijit.BackgroundIframe(node)
2698         //                      Makes a background iframe as a child of node, that fills
2699         //                      area (and position) of node
2700
2701         if(!node.id){ throw new Error("no id"); }
2702         if(dojo.isIE || dojo.isMoz){
2703                 var iframe = (this.iframe = dijit._frames.pop());
2704                 node.appendChild(iframe);
2705                 if(dojo.isIE<7 || dojo.isQuirks){
2706                         this.resize(node);
2707                         this._conn = dojo.connect(node, 'onresize', this, function(){
2708                                 this.resize(node);
2709                         });
2710                 }else{
2711                         dojo.style(iframe, {
2712                                 width: '100%',
2713                                 height: '100%'
2714                         });
2715                 }
2716         }
2717 };
2718
2719 dojo.extend(dijit.BackgroundIframe, {
2720         resize: function(node){
2721                 // summary:
2722                 //              Resize the iframe so it's the same size as node.
2723                 //              Needed on IE6 and IE/quirks because height:100% doesn't work right.
2724                 if(this.iframe){
2725                         dojo.style(this.iframe, {
2726                                 width: node.offsetWidth + 'px',
2727                                 height: node.offsetHeight + 'px'
2728                         });
2729                 }
2730         },
2731         destroy: function(){
2732                 // summary:
2733                 //              destroy the iframe
2734                 if(this._conn){
2735                         dojo.disconnect(this._conn);
2736                         this._conn = null;
2737                 }
2738                 if(this.iframe){
2739                         dijit._frames.push(this.iframe);
2740                         delete this.iframe;
2741                 }
2742         }
2743 });
2744
2745 }
2746
2747 if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2748 dojo._hasResource["dijit._base.scroll"] = true;
2749 dojo.provide("dijit._base.scroll");
2750
2751
2752
2753 dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
2754         // summary:
2755         //              Scroll the passed node into view, if it is not already.
2756         //              Deprecated, use `dojo.window.scrollIntoView` instead.
2757         
2758         dojo.window.scrollIntoView(node, pos);
2759 };
2760
2761 }
2762
2763 if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2764 dojo._hasResource["dojo.uacss"] = true;
2765 dojo.provide("dojo.uacss");
2766
2767
2768 (function(){
2769         // summary:
2770         //              Applies pre-set CSS classes to the top-level HTML node, based on:
2771         //                      - browser (ex: dj_ie)
2772         //                      - browser version (ex: dj_ie6)
2773         //                      - box model (ex: dj_contentBox)
2774         //                      - text direction (ex: dijitRtl)
2775         //
2776         //              In addition, browser, browser version, and box model are
2777         //              combined with an RTL flag when browser text is RTL.  ex: dj_ie-rtl.
2778
2779         var d = dojo,
2780                 html = d.doc.documentElement,
2781                 ie = d.isIE,
2782                 opera = d.isOpera,
2783                 maj = Math.floor,
2784                 ff = d.isFF,
2785                 boxModel = d.boxModel.replace(/-/,''),
2786
2787                 classes = {
2788                         dj_ie: ie,
2789                         dj_ie6: maj(ie) == 6,
2790                         dj_ie7: maj(ie) == 7,
2791                         dj_ie8: maj(ie) == 8,
2792                         dj_ie9: maj(ie) == 9,
2793                         dj_quirks: d.isQuirks,
2794                         dj_iequirks: ie && d.isQuirks,
2795
2796                         // NOTE: Opera not supported by dijit
2797                         dj_opera: opera,
2798
2799                         dj_khtml: d.isKhtml,
2800
2801                         dj_webkit: d.isWebKit,
2802                         dj_safari: d.isSafari,
2803                         dj_chrome: d.isChrome,
2804
2805                         dj_gecko: d.isMozilla,
2806                         dj_ff3: maj(ff) == 3
2807                 }; // no dojo unsupported browsers
2808
2809         classes["dj_" + boxModel] = true;
2810
2811         // apply browser, browser version, and box model class names
2812         var classStr = "";
2813         for(var clz in classes){
2814                 if(classes[clz]){
2815                         classStr += clz + " ";
2816                 }
2817         }
2818         html.className = d.trim(html.className + " " + classStr);
2819
2820         // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
2821         // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
2822         // Unshift() is to run sniff code before the parser.
2823         dojo._loaders.unshift(function(){
2824                 if(!dojo._isBodyLtr()){
2825                         var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")
2826                         html.className = d.trim(html.className + " " + rtlClassStr);
2827                 }
2828         });
2829 })();
2830
2831 }
2832
2833 if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2834 dojo._hasResource["dijit._base.sniff"] = true;
2835 dojo.provide("dijit._base.sniff");
2836
2837
2838
2839 // summary:
2840 //              Applies pre-set CSS classes to the top-level HTML node, see
2841 //              `dojo.uacss` for details.
2842 //
2843 //              Simply doing a require on this module will
2844 //              establish this CSS.  Modified version of Morris' CSS hack.
2845
2846 }
2847
2848 if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2849 dojo._hasResource["dijit._base.typematic"] = true;
2850 dojo.provide("dijit._base.typematic");
2851
2852
2853 dijit.typematic = {
2854         // summary:
2855         //              These functions are used to repetitively call a user specified callback
2856         //              method when a specific key or mouse click over a specific DOM node is
2857         //              held down for a specific amount of time.
2858         //              Only 1 such event is allowed to occur on the browser page at 1 time.
2859
2860         _fireEventAndReload: function(){
2861                 this._timer = null;
2862                 this._callback(++this._count, this._node, this._evt);
2863                 
2864                 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
2865                 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
2866                 this._currentTimeout = Math.max(
2867                         this._currentTimeout < 0 ? this._initialDelay :
2868                                 (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
2869                         this._minDelay);
2870                 this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout);
2871         },
2872
2873         trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2874                 // summary:
2875                 //              Start a timed, repeating callback sequence.
2876                 //              If already started, the function call is ignored.
2877                 //              This method is not normally called by the user but can be
2878                 //              when the normal listener code is insufficient.
2879                 // evt:
2880                 //              key or mouse event object to pass to the user callback
2881                 // _this:
2882                 //              pointer to the user's widget space.
2883                 // node:
2884                 //              the DOM node object to pass the the callback function
2885                 // callback:
2886                 //              function to call until the sequence is stopped called with 3 parameters:
2887                 // count:
2888                 //              integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
2889                 // node:
2890                 //              the DOM node object passed in
2891                 // evt:
2892                 //              key or mouse event object
2893                 // obj:
2894                 //              user space object used to uniquely identify each typematic sequence
2895                 // subsequentDelay (optional):
2896                 //              if > 1, the number of milliseconds until the 3->n events occur
2897                 //              or else the fractional time multiplier for the next event's delay, default=0.9
2898                 // initialDelay (optional):
2899                 //              the number of milliseconds until the 2nd event occurs, default=500ms
2900                 // minDelay (optional):
2901                 //              the maximum delay in milliseconds for event to fire, default=10ms
2902                 if(obj != this._obj){
2903                         this.stop();
2904                         this._initialDelay = initialDelay || 500;
2905                         this._subsequentDelay = subsequentDelay || 0.90;
2906                         this._minDelay = minDelay || 10;
2907                         this._obj = obj;
2908                         this._evt = evt;
2909                         this._node = node;
2910                         this._currentTimeout = -1;
2911                         this._count = -1;
2912                         this._callback = dojo.hitch(_this, callback);
2913                         this._fireEventAndReload();
2914                         this._evt = dojo.mixin({faux: true}, evt);
2915                 }
2916         },
2917
2918         stop: function(){
2919                 // summary:
2920                 //              Stop an ongoing timed, repeating callback sequence.
2921                 if(this._timer){
2922                         clearTimeout(this._timer);
2923                         this._timer = null;
2924                 }
2925                 if(this._obj){
2926                         this._callback(-1, this._node, this._evt);
2927                         this._obj = null;
2928                 }
2929         },
2930
2931         addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2932                 // summary:
2933                 //              Start listening for a specific typematic key.
2934                 //              See also the trigger method for other parameters.
2935                 // keyObject:
2936                 //              an object defining the key to listen for:
2937                 //              charOrCode:
2938                 //                      the printable character (string) or keyCode (number) to listen for.
2939                 //              keyCode:
2940                 //                      (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
2941                 //              charCode:
2942                 //                      (deprecated - use charOrCode) the charCode (number) to listen for.
2943                 //              ctrlKey:
2944                 //                      desired ctrl key state to initiate the callback sequence:
2945                 //                      - pressed (true)
2946                 //                      - released (false)
2947                 //                      - either (unspecified)
2948                 //              altKey:
2949                 //                      same as ctrlKey but for the alt key
2950                 //              shiftKey:
2951                 //                      same as ctrlKey but for the shift key
2952                 // returns:
2953                 //              an array of dojo.connect handles
2954                 if(keyObject.keyCode){
2955                         keyObject.charOrCode = keyObject.keyCode;
2956                         dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
2957                 }else if(keyObject.charCode){
2958                         keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
2959                         dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
2960                 }
2961                 return [
2962                         dojo.connect(node, "onkeypress", this, function(evt){
2963                                 if(evt.charOrCode == keyObject.charOrCode &&
2964                                 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
2965                                 (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
2966                                 (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
2967                                 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
2968                                         dojo.stopEvent(evt);
2969                                         dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
2970                                 }else if(dijit.typematic._obj == keyObject){
2971                                         dijit.typematic.stop();
2972                                 }
2973                         }),
2974                         dojo.connect(node, "onkeyup", this, function(evt){
2975                                 if(dijit.typematic._obj == keyObject){
2976                                         dijit.typematic.stop();
2977                                 }
2978                         })
2979                 ];
2980         },
2981
2982         addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
2983                 // summary:
2984                 //              Start listening for a typematic mouse click.
2985                 //              See the trigger method for other parameters.
2986                 // returns:
2987                 //              an array of dojo.connect handles
2988                 var dc = dojo.connect;
2989                 return [
2990                         dc(node, "mousedown", this, function(evt){
2991                                 dojo.stopEvent(evt);
2992                                 dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
2993                         }),
2994                         dc(node, "mouseup", this, function(evt){
2995                                 dojo.stopEvent(evt);
2996                                 dijit.typematic.stop();
2997                         }),
2998                         dc(node, "mouseout", this, function(evt){
2999                                 dojo.stopEvent(evt);
3000                                 dijit.typematic.stop();
3001                         }),
3002                         dc(node, "mousemove", this, function(evt){
3003                                 evt.preventDefault();
3004                         }),
3005                         dc(node, "dblclick", this, function(evt){
3006                                 dojo.stopEvent(evt);
3007                                 if(dojo.isIE){
3008                                         dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
3009                                         setTimeout(dojo.hitch(this, dijit.typematic.stop), 50);
3010                                 }
3011                         })
3012                 ];
3013         },
3014
3015         addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
3016                 // summary:
3017                 //              Start listening for a specific typematic key and mouseclick.
3018                 //              This is a thin wrapper to addKeyListener and addMouseListener.
3019                 //              See the addMouseListener and addKeyListener methods for other parameters.
3020                 // mouseNode:
3021                 //              the DOM node object to listen on for mouse events.
3022                 // keyNode:
3023                 //              the DOM node object to listen on for key events.
3024                 // returns:
3025                 //              an array of dojo.connect handles
3026                 return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat(
3027                         this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay));
3028         }
3029 };
3030
3031 }
3032
3033 if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3034 dojo._hasResource["dijit._base.wai"] = true;
3035 dojo.provide("dijit._base.wai");
3036
3037
3038 dijit.wai = {
3039         onload: function(){
3040                 // summary:
3041                 //              Detects if we are in high-contrast mode or not
3042
3043                 // This must be a named function and not an anonymous
3044                 // function, so that the widget parsing code can make sure it
3045                 // registers its onload function after this function.
3046                 // DO NOT USE "this" within this function.
3047
3048                 // create div for testing if high contrast mode is on or images are turned off
3049                 var div = dojo.create("div",{
3050                         id: "a11yTestNode",
3051                         style:{
3052                                 cssText:'border: 1px solid;'
3053                                         + 'border-color:red green;'
3054                                         + 'position: absolute;'
3055                                         + 'height: 5px;'
3056                                         + 'top: -999px;'
3057                                         + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");'
3058                         }
3059                 }, dojo.body());
3060
3061                 // test it
3062                 var cs = dojo.getComputedStyle(div);
3063                 if(cs){
3064                         var bkImg = cs.backgroundImage;
3065                         var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
3066                         dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
3067                         if(dojo.isIE){
3068                                 div.outerHTML = "";             // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
3069                         }else{
3070                                 dojo.body().removeChild(div);
3071                         }
3072                 }
3073         }
3074 };
3075
3076 // Test if computer is in high contrast mode.
3077 // Make sure the a11y test runs first, before widgets are instantiated.
3078 if(dojo.isIE || dojo.isMoz){    // NOTE: checking in Safari messes things up
3079         dojo._loaders.unshift(dijit.wai.onload);
3080 }
3081
3082 dojo.mixin(dijit, {
3083         hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
3084                 // summary:
3085                 //              Determines if an element has a particular role.
3086                 // returns:
3087                 //              True if elem has the specific role attribute and false if not.
3088                 //              For backwards compatibility if role parameter not provided,
3089                 //              returns true if has a role
3090                 var waiRole = this.getWaiRole(elem);
3091                 return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
3092         },
3093
3094         getWaiRole: function(/*Element*/ elem){
3095                 // summary:
3096                 //              Gets the role for an element (which should be a wai role).
3097                 // returns:
3098                 //              The role of elem or an empty string if elem
3099                 //              does not have a role.
3100                  return dojo.trim((dojo.attr(elem, "role") || "").replace("wairole:",""));
3101         },
3102
3103         setWaiRole: function(/*Element*/ elem, /*String*/ role){
3104                 // summary:
3105                 //              Sets the role on an element.
3106                 // description:
3107                 //              Replace existing role attribute with new role.
3108
3109                         dojo.attr(elem, "role", role);
3110         },
3111
3112         removeWaiRole: function(/*Element*/ elem, /*String*/ role){
3113                 // summary:
3114                 //              Removes the specified role from an element.
3115                 //              Removes role attribute if no specific role provided (for backwards compat.)
3116
3117                 var roleValue = dojo.attr(elem, "role");
3118                 if(!roleValue){ return; }
3119                 if(role){
3120                         var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
3121                         dojo.attr(elem, "role", t);
3122                 }else{
3123                         elem.removeAttribute("role");
3124                 }
3125         },
3126
3127         hasWaiState: function(/*Element*/ elem, /*String*/ state){
3128                 // summary:
3129                 //              Determines if an element has a given state.
3130                 // description:
3131                 //              Checks for an attribute called "aria-"+state.
3132                 // returns:
3133                 //              true if elem has a value for the given state and
3134                 //              false if it does not.
3135
3136                 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
3137         },
3138
3139         getWaiState: function(/*Element*/ elem, /*String*/ state){
3140                 // summary:
3141                 //              Gets the value of a state on an element.
3142                 // description:
3143                 //              Checks for an attribute called "aria-"+state.
3144                 // returns:
3145                 //              The value of the requested state on elem
3146                 //              or an empty string if elem has no value for state.
3147
3148                 return elem.getAttribute("aria-"+state) || "";
3149         },
3150
3151         setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
3152                 // summary:
3153                 //              Sets a state on an element.
3154                 // description:
3155                 //              Sets an attribute called "aria-"+state.
3156
3157                 elem.setAttribute("aria-"+state, value);
3158         },
3159
3160         removeWaiState: function(/*Element*/ elem, /*String*/ state){
3161                 // summary:
3162                 //              Removes a state from an element.
3163                 // description:
3164                 //              Sets an attribute called "aria-"+state.
3165
3166                 elem.removeAttribute("aria-"+state);
3167         }
3168 });
3169
3170 }
3171
3172 if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3173 dojo._hasResource["dijit._base"] = true;
3174 dojo.provide("dijit._base");
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187 }
3188
3189 if(!dojo._hasResource["dojo.Stateful"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3190 dojo._hasResource["dojo.Stateful"] = true;
3191 dojo.provide("dojo.Stateful");
3192
3193
3194 dojo.declare("dojo.Stateful", null, {
3195         // summary:
3196         //              Base class for objects that provide named properties with optional getter/setter
3197         //              control and the ability to watch for property changes
3198         // example:
3199         //      |       var obj = new dojo.Stateful();
3200         //      |       obj.watch("foo", function(){
3201         //      |               console.log("foo changed to " + this.get("foo"));
3202         //      |       });
3203         //      |       obj.set("foo","bar");
3204         postscript: function(mixin){
3205                 if(mixin){
3206                         dojo.mixin(this, mixin);
3207                 }
3208         },
3209         
3210         get: function(/*String*/name){
3211                 // summary:
3212                 //              Get a property on a Stateful instance.
3213                 //      name:
3214                 //              The property to get.
3215                 // description:
3216                 //              Get a named property on a Stateful object. The property may
3217                 //              potentially be retrieved via a getter method in subclasses. In the base class
3218                 //              this just retrieves the object's property.
3219                 //              For example:
3220                 //      |       stateful = new dojo.Stateful({foo: 3});
3221                 //      |       stateful.get("foo") // returns 3
3222                 //      |       stateful.foo // returns 3
3223                 
3224                 return this[name];
3225         },
3226         set: function(/*String*/name, /*Object*/value){
3227                 // summary:
3228                 //              Set a property on a Stateful instance
3229                 //      name:
3230                 //              The property to set.
3231                 //      value:
3232                 //              The value to set in the property.
3233                 // description:
3234                 //              Sets named properties on a stateful object and notifies any watchers of
3235                 //              the property. A programmatic setter may be defined in subclasses.
3236                 //              For example:
3237                 //      |       stateful = new dojo.Stateful();
3238                 //      |       stateful.watch(function(name, oldValue, value){
3239                 //      |               // this will be called on the set below
3240                 //      |       }
3241                 //      |       stateful.set(foo, 5);
3242                 //
3243                 //      set() may also be called with a hash of name/value pairs, ex:
3244                 //      |       myObj.set({
3245                 //      |               foo: "Howdy",
3246                 //      |               bar: 3
3247                 //      |       })
3248                 //      This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
3249                 if(typeof name === "object"){
3250                         for(var x in name){
3251                                 this.set(x, name[x]);
3252                         }
3253                         return this;
3254                 }
3255                 var oldValue = this[name];
3256                 this[name] = value;
3257                 if(this._watchCallbacks){
3258                         this._watchCallbacks(name, oldValue, value);
3259                 }
3260                 return this;
3261         },
3262         watch: function(/*String?*/name, /*Function*/callback){
3263                 // summary:
3264                 //              Watches a property for changes
3265                 //      name:
3266                 //              Indicates the property to watch. This is optional (the callback may be the
3267                 //              only parameter), and if omitted, all the properties will be watched
3268                 // returns:
3269                 //              An object handle for the watch. The unwatch method of this object
3270                 //              can be used to discontinue watching this property:
3271                 //              |       var watchHandle = obj.watch("foo", callback);
3272                 //              |       watchHandle.unwatch(); // callback won't be called now
3273                 //      callback:
3274                 //              The function to execute when the property changes. This will be called after
3275                 //              the property has been changed. The callback will be called with the |this|
3276                 //              set to the instance, the first argument as the name of the property, the
3277                 //              second argument as the old value and the third argument as the new value.
3278                 
3279                 var callbacks = this._watchCallbacks;
3280                 if(!callbacks){
3281                         var self = this;
3282                         callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
3283                                 var notify = function(propertyCallbacks){
3284                                         if(propertyCallbacks){
3285                         propertyCallbacks = propertyCallbacks.slice();
3286                                                 for(var i = 0, l = propertyCallbacks.length; i < l; i++){
3287                                                         try{
3288                                                                 propertyCallbacks[i].call(self, name, oldValue, value);
3289                                                         }catch(e){
3290                                                                 console.error(e);
3291                                                         }
3292                                                 }
3293                                         }
3294                                 };
3295                                 notify(callbacks['_' + name]);
3296                                 if(!ignoreCatchall){
3297                                         notify(callbacks["*"]); // the catch-all
3298                                 }
3299                         }; // we use a function instead of an object so it will be ignored by JSON conversion
3300                 }
3301                 if(!callback && typeof name === "function"){
3302                         callback = name;
3303                         name = "*";
3304                 }else{
3305                         // prepend with dash to prevent name conflicts with function (like "name" property)
3306                         name = '_' + name;
3307                 }
3308                 var propertyCallbacks = callbacks[name];
3309                 if(typeof propertyCallbacks !== "object"){
3310                         propertyCallbacks = callbacks[name] = [];
3311                 }
3312                 propertyCallbacks.push(callback);
3313                 return {
3314                         unwatch: function(){
3315                                 propertyCallbacks.splice(dojo.indexOf(propertyCallbacks, callback), 1);
3316                         }
3317                 };
3318         }
3319         
3320 });
3321
3322 }
3323
3324 if(!dojo._hasResource["dijit._WidgetBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3325 dojo._hasResource["dijit._WidgetBase"] = true;
3326 dojo.provide("dijit._WidgetBase");
3327
3328
3329
3330
3331 (function(){
3332
3333 dojo.declare("dijit._WidgetBase", dojo.Stateful, {
3334         // summary:
3335         //              Future base class for all Dijit widgets.
3336         //              _Widget extends this class adding support for various features needed by desktop.
3337
3338         // id: [const] String
3339         //              A unique, opaque ID string that can be assigned by users or by the
3340         //              system. If the developer passes an ID which is known not to be
3341         //              unique, the specified ID is ignored and the system-generated ID is
3342         //              used instead.
3343         id: "",
3344
3345         // lang: [const] String
3346         //              Rarely used.  Overrides the default Dojo locale used to render this widget,
3347         //              as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
3348         //              Value must be among the list of locales specified during by the Dojo bootstrap,
3349         //              formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
3350         lang: "",
3351
3352         // dir: [const] String
3353         //              Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
3354         //              attribute. Either left-to-right "ltr" or right-to-left "rtl".  If undefined, widgets renders in page's
3355         //              default direction.
3356         dir: "",
3357
3358         // class: String
3359         //              HTML class attribute
3360         "class": "",
3361
3362         // style: String||Object
3363         //              HTML style attributes as cssText string or name/value hash
3364         style: "",
3365
3366         // title: String
3367         //              HTML title attribute.
3368         //
3369         //              For form widgets this specifies a tooltip to display when hovering over
3370         //              the widget (just like the native HTML title attribute).
3371         //
3372         //              For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
3373         //              etc., it's used to specify the tab label, accordion pane title, etc.
3374         title: "",
3375
3376         // tooltip: String
3377         //              When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
3378         //              this specifies the tooltip to appear when the mouse is hovered over that text.
3379         tooltip: "",
3380
3381         // baseClass: [protected] String
3382         //              Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
3383         //              widget state.
3384         baseClass: "",
3385
3386         // srcNodeRef: [readonly] DomNode
3387         //              pointer to original DOM node
3388         srcNodeRef: null,
3389
3390         // domNode: [readonly] DomNode
3391         //              This is our visible representation of the widget! Other DOM
3392         //              Nodes may by assigned to other properties, usually through the
3393         //              template system's dojoAttachPoint syntax, but the domNode
3394         //              property is the canonical "top level" node in widget UI.
3395         domNode: null,
3396
3397         // containerNode: [readonly] DomNode
3398         //              Designates where children of the source DOM node will be placed.
3399         //              "Children" in this case refers to both DOM nodes and widgets.
3400         //              For example, for myWidget:
3401         //
3402         //              |       <div dojoType=myWidget>
3403         //              |               <b> here's a plain DOM node
3404         //              |               <span dojoType=subWidget>and a widget</span>
3405         //              |               <i> and another plain DOM node </i>
3406         //              |       </div>
3407         //
3408         //              containerNode would point to:
3409         //
3410         //              |               <b> here's a plain DOM node
3411         //              |               <span dojoType=subWidget>and a widget</span>
3412         //              |               <i> and another plain DOM node </i>
3413         //
3414         //              In templated widgets, "containerNode" is set via a
3415         //              dojoAttachPoint assignment.
3416         //
3417         //              containerNode must be defined for any widget that accepts innerHTML
3418         //              (like ContentPane or BorderContainer or even Button), and conversely
3419         //              is null for widgets that don't, like TextBox.
3420         containerNode: null,
3421
3422 /*=====
3423         // _started: Boolean
3424         //              startup() has completed.
3425         _started: false,
3426 =====*/
3427
3428         // attributeMap: [protected] Object
3429         //              attributeMap sets up a "binding" between attributes (aka properties)
3430         //              of the widget and the widget's DOM.
3431         //              Changes to widget attributes listed in attributeMap will be
3432         //              reflected into the DOM.
3433         //
3434         //              For example, calling set('title', 'hello')
3435         //              on a TitlePane will automatically cause the TitlePane's DOM to update
3436         //              with the new title.
3437         //
3438         //              attributeMap is a hash where the key is an attribute of the widget,
3439         //              and the value reflects a binding to a:
3440         //
3441         //              - DOM node attribute
3442         // |            focus: {node: "focusNode", type: "attribute"}
3443         //              Maps this.focus to this.focusNode.focus
3444         //
3445         //              - DOM node innerHTML
3446         //      |               title: { node: "titleNode", type: "innerHTML" }
3447         //              Maps this.title to this.titleNode.innerHTML
3448         //
3449         //              - DOM node innerText
3450         //      |               title: { node: "titleNode", type: "innerText" }
3451         //              Maps this.title to this.titleNode.innerText
3452         //
3453         //              - DOM node CSS class
3454         // |            myClass: { node: "domNode", type: "class" }
3455         //              Maps this.myClass to this.domNode.className
3456         //
3457         //              If the value is an array, then each element in the array matches one of the
3458         //              formats of the above list.
3459         //
3460         //              There are also some shorthands for backwards compatibility:
3461         //              - string --> { node: string, type: "attribute" }, for example:
3462         //      |       "focusNode" ---> { node: "focusNode", type: "attribute" }
3463         //              - "" --> { node: "domNode", type: "attribute" }
3464         attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},
3465
3466         // _blankGif: [protected] String
3467         //              Path to a blank 1x1 image.
3468         //              Used by <img> nodes in templates that really get their image via CSS background-image.
3469         _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(),
3470
3471         //////////// INITIALIZATION METHODS ///////////////////////////////////////
3472
3473         postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
3474                 // summary:
3475                 //              Kicks off widget instantiation.  See create() for details.
3476                 // tags:
3477                 //              private
3478                 this.create(params, srcNodeRef);
3479         },
3480
3481         create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
3482                 // summary:
3483                 //              Kick off the life-cycle of a widget
3484                 // params:
3485                 //              Hash of initialization parameters for widget, including
3486                 //              scalar values (like title, duration etc.) and functions,
3487                 //              typically callbacks like onClick.
3488                 // srcNodeRef:
3489                 //              If a srcNodeRef (DOM node) is specified:
3490                 //                      - use srcNodeRef.innerHTML as my contents
3491                 //                      - if this is a behavioral widget then apply behavior
3492                 //                        to that srcNodeRef
3493                 //                      - otherwise, replace srcNodeRef with my generated DOM
3494                 //                        tree
3495                 // description:
3496                 //              Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
3497                 //              etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget
3498                 //              for a discussion of the widget creation lifecycle.
3499                 //
3500                 //              Of course, adventurous developers could override create entirely, but this should
3501                 //              only be done as a last resort.
3502                 // tags:
3503                 //              private
3504
3505                 // store pointer to original DOM tree
3506                 this.srcNodeRef = dojo.byId(srcNodeRef);
3507
3508                 // For garbage collection.  An array of handles returned by Widget.connect()
3509                 // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
3510                 this._connects = [];
3511
3512                 // For garbage collection.  An array of handles returned by Widget.subscribe()
3513                 // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe()
3514                 this._subscribes = [];
3515
3516                 // mix in our passed parameters
3517                 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
3518                 if(params){
3519                         this.params = params;
3520                         dojo._mixin(this, params);
3521                 }
3522                 this.postMixInProperties();
3523
3524                 // generate an id for the widget if one wasn't specified
3525                 // (be sure to do this before buildRendering() because that function might
3526                 // expect the id to be there.)
3527                 if(!this.id){
3528                         this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
3529                 }
3530                 dijit.registry.add(this);
3531
3532                 this.buildRendering();
3533
3534                 if(this.domNode){
3535                         // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
3536                         // Also calls custom setters for all attributes with custom setters.
3537                         this._applyAttributes();
3538
3539                         // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
3540                         // For 2.0, move this after postCreate().  postCreate() shouldn't depend on the
3541                         // widget being attached to the DOM since it isn't when a widget is created programmatically like
3542                         // new MyWidget({}).   See #11635.
3543                         var source = this.srcNodeRef;
3544                         if(source && source.parentNode && this.domNode !== source){
3545                                 source.parentNode.replaceChild(this.domNode, source);
3546                         }
3547                 }
3548
3549                 if(this.domNode){
3550                         // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
3551                         // assuming that dojo._scopeName even exists in 2.0
3552                         this.domNode.setAttribute("widgetId", this.id);
3553                 }
3554                 this.postCreate();
3555
3556                 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
3557                 if(this.srcNodeRef && !this.srcNodeRef.parentNode){
3558                         delete this.srcNodeRef;
3559                 }
3560
3561                 this._created = true;
3562         },
3563
3564         _applyAttributes: function(){
3565                 // summary:
3566                 //              Step during widget creation to copy all widget attributes to the
3567                 //              DOM as per attributeMap and _setXXXAttr functions.
3568                 // description:
3569                 //              Skips over blank/false attribute values, unless they were explicitly specified
3570                 //              as parameters to the widget, since those are the default anyway,
3571                 //              and setting tabIndex="" is different than not setting tabIndex at all.
3572                 //
3573                 //              It processes the attributes in the attribute map first, and then
3574                 //              it goes through and processes the attributes for the _setXXXAttr
3575                 //              functions that have been specified
3576                 // tags:
3577                 //              private
3578                 var condAttrApply = function(attr, scope){
3579                         if((scope.params && attr in scope.params) || scope[attr]){
3580                                 scope.set(attr, scope[attr]);
3581                         }
3582                 };
3583
3584                 // Do the attributes in attributeMap
3585                 for(var attr in this.attributeMap){
3586                         condAttrApply(attr, this);
3587                 }
3588
3589                 // And also any attributes with custom setters
3590                 dojo.forEach(this._getSetterAttributes(), function(a){
3591                         if(!(a in this.attributeMap)){
3592                                 condAttrApply(a, this);
3593                         }
3594                 }, this);
3595         },
3596
3597         _getSetterAttributes: function(){
3598                 // summary:
3599                 //              Returns list of attributes with custom setters for this widget
3600                 var ctor = this.constructor;
3601                 if(!ctor._setterAttrs){
3602                         var r = (ctor._setterAttrs = []),
3603                                 attrs,
3604                                 proto = ctor.prototype;
3605                         for(var fxName in proto){
3606                                 if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){
3607                                         r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1));
3608                                 }
3609                         }
3610                 }
3611                 return ctor._setterAttrs;       // String[]
3612         },
3613
3614         postMixInProperties: function(){
3615                 // summary:
3616                 //              Called after the parameters to the widget have been read-in,
3617                 //              but before the widget template is instantiated. Especially
3618                 //              useful to set properties that are referenced in the widget
3619                 //              template.
3620                 // tags:
3621                 //              protected
3622         },
3623
3624         buildRendering: function(){
3625                 // summary:
3626                 //              Construct the UI for this widget, setting this.domNode
3627                 // description:
3628                 //              Most widgets will mixin `dijit._Templated`, which implements this
3629                 //              method.
3630                 // tags:
3631                 //              protected
3632
3633                 if(!this.domNode){
3634                         // Create root node if it wasn't created by _Templated
3635                         this.domNode = this.srcNodeRef || dojo.create('div');
3636                 }
3637
3638                 // baseClass is a single class name or occasionally a space-separated list of names.
3639                 // Add those classes to the DOMNode.  If RTL mode then also add with Rtl suffix.
3640                 // TODO: make baseClass custom setter
3641                 if(this.baseClass){
3642                         var classes = this.baseClass.split(" ");
3643                         if(!this.isLeftToRight()){
3644                                 classes = classes.concat( dojo.map(classes, function(name){ return name+"Rtl"; }));
3645                         }
3646                         dojo.addClass(this.domNode, classes);
3647                 }
3648         },
3649
3650         postCreate: function(){
3651                 // summary:
3652                 //              Processing after the DOM fragment is created
3653                 // description:
3654                 //              Called after the DOM fragment has been created, but not necessarily
3655                 //              added to the document.  Do not include any operations which rely on
3656                 //              node dimensions or placement.
3657                 // tags:
3658                 //              protected
3659         },
3660
3661         startup: function(){
3662                 // summary:
3663                 //              Processing after the DOM fragment is added to the document
3664                 // description:
3665                 //              Called after a widget and its children have been created and added to the page,
3666                 //              and all related widgets have finished their create() cycle, up through postCreate().
3667                 //              This is useful for composite widgets that need to control or layout sub-widgets.
3668                 //              Many layout widgets can use this as a wiring phase.
3669                 this._started = true;
3670         },
3671
3672         //////////// DESTROY FUNCTIONS ////////////////////////////////
3673
3674         destroyRecursive: function(/*Boolean?*/ preserveDom){
3675                 // summary:
3676                 //              Destroy this widget and its descendants
3677                 // description:
3678                 //              This is the generic "destructor" function that all widget users
3679                 //              should call to cleanly discard with a widget. Once a widget is
3680                 //              destroyed, it is removed from the manager object.
3681                 // preserveDom:
3682                 //              If true, this method will leave the original DOM structure
3683                 //              alone of descendant Widgets. Note: This will NOT work with
3684                 //              dijit._Templated widgets.
3685
3686                 this._beingDestroyed = true;
3687                 this.destroyDescendants(preserveDom);
3688                 this.destroy(preserveDom);
3689         },
3690
3691         destroy: function(/*Boolean*/ preserveDom){
3692                 // summary:
3693                 //              Destroy this widget, but not its descendants.
3694                 //              This method will, however, destroy internal widgets such as those used within a template.
3695                 // preserveDom: Boolean
3696                 //              If true, this method will leave the original DOM structure alone.
3697                 //              Note: This will not yet work with _Templated widgets
3698
3699                 this._beingDestroyed = true;
3700                 this.uninitialize();
3701                 var d = dojo,
3702                         dfe = d.forEach,
3703                         dun = d.unsubscribe;
3704                 dfe(this._connects, function(array){
3705                         dfe(array, d.disconnect);
3706                 });
3707                 dfe(this._subscribes, function(handle){
3708                         dun(handle);
3709                 });
3710
3711                 // destroy widgets created as part of template, etc.
3712                 dfe(this._supportingWidgets || [], function(w){
3713                         if(w.destroyRecursive){
3714                                 w.destroyRecursive();
3715                         }else if(w.destroy){
3716                                 w.destroy();
3717                         }
3718                 });
3719
3720                 this.destroyRendering(preserveDom);
3721                 dijit.registry.remove(this.id);
3722                 this._destroyed = true;
3723         },
3724
3725         destroyRendering: function(/*Boolean?*/ preserveDom){
3726                 // summary:
3727                 //              Destroys the DOM nodes associated with this widget
3728                 // preserveDom:
3729                 //              If true, this method will leave the original DOM structure alone
3730                 //              during tear-down. Note: this will not work with _Templated
3731                 //              widgets yet.
3732                 // tags:
3733                 //              protected
3734
3735                 if(this.bgIframe){
3736                         this.bgIframe.destroy(preserveDom);
3737                         delete this.bgIframe;
3738                 }
3739
3740                 if(this.domNode){
3741                         if(preserveDom){
3742                                 dojo.removeAttr(this.domNode, "widgetId");
3743                         }else{
3744                                 dojo.destroy(this.domNode);
3745                         }
3746                         delete this.domNode;
3747                 }
3748
3749                 if(this.srcNodeRef){
3750                         if(!preserveDom){
3751                                 dojo.destroy(this.srcNodeRef);
3752                         }
3753                         delete this.srcNodeRef;
3754                 }
3755         },
3756
3757         destroyDescendants: function(/*Boolean?*/ preserveDom){
3758                 // summary:
3759                 //              Recursively destroy the children of this widget and their
3760                 //              descendants.
3761                 // preserveDom:
3762                 //              If true, the preserveDom attribute is passed to all descendant
3763                 //              widget's .destroy() method. Not for use with _Templated
3764                 //              widgets.
3765
3766                 // get all direct descendants and destroy them recursively
3767                 dojo.forEach(this.getChildren(), function(widget){
3768                         if(widget.destroyRecursive){
3769                                 widget.destroyRecursive(preserveDom);
3770                         }
3771                 });
3772         },
3773
3774         uninitialize: function(){
3775                 // summary:
3776                 //              Stub function. Override to implement custom widget tear-down
3777                 //              behavior.
3778                 // tags:
3779                 //              protected
3780                 return false;
3781         },
3782
3783         ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
3784
3785         _setClassAttr: function(/*String*/ value){
3786                 // summary:
3787                 //              Custom setter for the CSS "class" attribute
3788                 // tags:
3789                 //              protected
3790                 var mapNode = this[this.attributeMap["class"] || 'domNode'];
3791                 dojo.replaceClass(mapNode, value, this["class"]);
3792                 this._set("class", value);
3793         },
3794
3795         _setStyleAttr: function(/*String||Object*/ value){
3796                 // summary:
3797                 //              Sets the style attribute of the widget according to value,
3798                 //              which is either a hash like {height: "5px", width: "3px"}
3799                 //              or a plain string
3800                 // description:
3801                 //              Determines which node to set the style on based on style setting
3802                 //              in attributeMap.
3803                 // tags:
3804                 //              protected
3805
3806                 var mapNode = this[this.attributeMap.style || 'domNode'];
3807
3808                 // Note: technically we should revert any style setting made in a previous call
3809                 // to his method, but that's difficult to keep track of.
3810
3811                 if(dojo.isObject(value)){
3812                         dojo.style(mapNode, value);
3813                 }else{
3814                         if(mapNode.style.cssText){
3815                                 mapNode.style.cssText += "; " + value;
3816                         }else{
3817                                 mapNode.style.cssText = value;
3818                         }
3819                 }
3820
3821                 this._set("style", value);
3822         },
3823
3824         _attrToDom: function(/*String*/ attr, /*String*/ value){
3825                 // summary:
3826                 //              Reflect a widget attribute (title, tabIndex, duration etc.) to
3827                 //              the widget DOM, as specified in attributeMap.
3828                 //              Note some attributes like "type"
3829                 //              cannot be processed this way as they are not mutable.
3830                 //
3831                 // tags:
3832                 //              private
3833
3834                 var commands = this.attributeMap[attr];
3835                 dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){
3836
3837                         // Get target node and what we are doing to that node
3838                         var mapNode = this[command.node || command || "domNode"];       // DOM node
3839                         var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
3840
3841                         switch(type){
3842                                 case "attribute":
3843                                         if(dojo.isFunction(value)){ // functions execute in the context of the widget
3844                                                 value = dojo.hitch(this, value);
3845                                         }
3846
3847                                         // Get the name of the DOM node attribute; usually it's the same
3848                                         // as the name of the attribute in the widget (attr), but can be overridden.
3849                                         // Also maps handler names to lowercase, like onSubmit --> onsubmit
3850                                         var attrName = command.attribute ? command.attribute :
3851                                                 (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
3852
3853                                         dojo.attr(mapNode, attrName, value);
3854                                         break;
3855                                 case "innerText":
3856                                         mapNode.innerHTML = "";
3857                                         mapNode.appendChild(dojo.doc.createTextNode(value));
3858                                         break;
3859                                 case "innerHTML":
3860                                         mapNode.innerHTML = value;
3861                                         break;
3862                                 case "class":
3863                                         dojo.replaceClass(mapNode, value, this[attr]);
3864                                         break;
3865                         }
3866                 }, this);
3867         },
3868
3869         get: function(name){
3870                 // summary:
3871                 //              Get a property from a widget.
3872                 //      name:
3873                 //              The property to get.
3874                 // description:
3875                 //              Get a named property from a widget. The property may
3876                 //              potentially be retrieved via a getter method. If no getter is defined, this
3877                 //              just retrieves the object's property.
3878                 //              For example, if the widget has a properties "foo"
3879                 //              and "bar" and a method named "_getFooAttr", calling:
3880                 //      |       myWidget.get("foo");
3881                 //              would be equivalent to writing:
3882                 //      |       widget._getFooAttr();
3883                 //              and:
3884                 //      |       myWidget.get("bar");
3885                 //              would be equivalent to writing:
3886                 //      |       widget.bar;
3887                 var names = this._getAttrNames(name);
3888                 return this[names.g] ? this[names.g]() : this[name];
3889         },
3890         
3891         set: function(name, value){
3892                 // summary:
3893                 //              Set a property on a widget
3894                 //      name:
3895                 //              The property to set.
3896                 //      value:
3897                 //              The value to set in the property.
3898                 // description:
3899                 //              Sets named properties on a widget which may potentially be handled by a
3900                 //              setter in the widget.
3901                 //              For example, if the widget has a properties "foo"
3902                 //              and "bar" and a method named "_setFooAttr", calling:
3903                 //      |       myWidget.set("foo", "Howdy!");
3904                 //              would be equivalent to writing:
3905                 //      |       widget._setFooAttr("Howdy!");
3906                 //              and:
3907                 //      |       myWidget.set("bar", 3);
3908                 //              would be equivalent to writing:
3909                 //      |       widget.bar = 3;
3910                 //
3911                 //      set() may also be called with a hash of name/value pairs, ex:
3912                 //      |       myWidget.set({
3913                 //      |               foo: "Howdy",
3914                 //      |               bar: 3
3915                 //      |       })
3916                 //      This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
3917
3918                 if(typeof name === "object"){
3919                         for(var x in name){
3920                                 this.set(x, name[x]);
3921                         }
3922                         return this;
3923                 }
3924                 var names = this._getAttrNames(name);
3925                 if(this[names.s]){
3926                         // use the explicit setter
3927                         var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1));
3928                 }else{
3929                         // if param is specified as DOM node attribute, copy it
3930                         if(name in this.attributeMap){
3931                                 this._attrToDom(name, value);
3932                         }
3933                         this._set(name, value);
3934                 }
3935                 return result || this;
3936         },
3937         
3938         _attrPairNames: {},             // shared between all widgets
3939         _getAttrNames: function(name){
3940                 // summary:
3941                 //              Helper function for get() and set().
3942                 //              Caches attribute name values so we don't do the string ops every time.
3943                 // tags:
3944                 //              private
3945
3946                 var apn = this._attrPairNames;
3947                 if(apn[name]){ return apn[name]; }
3948                 var uc = name.charAt(0).toUpperCase() + name.substr(1);
3949                 return (apn[name] = {
3950                         n: name+"Node",
3951                         s: "_set"+uc+"Attr",
3952                         g: "_get"+uc+"Attr"
3953                 });
3954         },
3955
3956         _set: function(/*String*/ name, /*anything*/ value){
3957                 // summary:
3958                 //              Helper function to set new value for specified attribute, and call handlers
3959                 //              registered with watch() if the value has changed.
3960                 var oldValue = this[name];
3961                 this[name] = value;
3962                 if(this._watchCallbacks && this._created && value !== oldValue){
3963                         this._watchCallbacks(name, oldValue, value);
3964                 }
3965         },
3966
3967         toString: function(){
3968                 // summary:
3969                 //              Returns a string that represents the widget
3970                 // description:
3971                 //              When a widget is cast to a string, this method will be used to generate the
3972                 //              output. Currently, it does not implement any sort of reversible
3973                 //              serialization.
3974                 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
3975         },
3976
3977         getDescendants: function(){
3978                 // summary:
3979                 //              Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
3980                 //              This method should generally be avoided as it returns widgets declared in templates, which are
3981                 //              supposed to be internal/hidden, but it's left here for back-compat reasons.
3982
3983                 return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[]
3984         },
3985
3986         getChildren: function(){
3987                 // summary:
3988                 //              Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
3989                 //              Does not return nested widgets, nor widgets that are part of this widget's template.
3990                 return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[]
3991         },
3992
3993         connect: function(
3994                         /*Object|null*/ obj,
3995                         /*String|Function*/ event,
3996                         /*String|Function*/ method){
3997                 // summary:
3998                 //              Connects specified obj/event to specified method of this object
3999                 //              and registers for disconnect() on widget destroy.
4000                 // description:
4001                 //              Provide widget-specific analog to dojo.connect, except with the
4002                 //              implicit use of this widget as the target object.
4003                 //              Events connected with `this.connect` are disconnected upon
4004                 //              destruction.
4005                 // returns:
4006                 //              A handle that can be passed to `disconnect` in order to disconnect before
4007                 //              the widget is destroyed.
4008                 // example:
4009                 //      |       var btn = new dijit.form.Button();
4010                 //      |       // when foo.bar() is called, call the listener we're going to
4011                 //      |       // provide in the scope of btn
4012                 //      |       btn.connect(foo, "bar", function(){
4013                 //      |               console.debug(this.toString());
4014                 //      |       });
4015                 // tags:
4016                 //              protected
4017
4018                 var handles = [dojo._connect(obj, event, this, method)];
4019                 this._connects.push(handles);
4020                 return handles;         // _Widget.Handle
4021         },
4022
4023         disconnect: function(/* _Widget.Handle */ handles){
4024                 // summary:
4025                 //              Disconnects handle created by `connect`.
4026                 //              Also removes handle from this widget's list of connects.
4027                 // tags:
4028                 //              protected
4029                 for(var i=0; i<this._connects.length; i++){
4030                         if(this._connects[i] == handles){
4031                                 dojo.forEach(handles, dojo.disconnect);
4032                                 this._connects.splice(i, 1);
4033                                 return;
4034                         }
4035                 }
4036         },
4037
4038         subscribe: function(
4039                         /*String*/ topic,
4040                         /*String|Function*/ method){
4041                 // summary:
4042                 //              Subscribes to the specified topic and calls the specified method
4043                 //              of this object and registers for unsubscribe() on widget destroy.
4044                 // description:
4045                 //              Provide widget-specific analog to dojo.subscribe, except with the
4046                 //              implicit use of this widget as the target object.
4047                 // example:
4048                 //      |       var btn = new dijit.form.Button();
4049                 //      |       // when /my/topic is published, this button changes its label to
4050                 //      |   // be the parameter of the topic.
4051                 //      |       btn.subscribe("/my/topic", function(v){
4052                 //      |               this.set("label", v);
4053                 //      |       });
4054                 var handle = dojo.subscribe(topic, this, method);
4055
4056                 // return handles for Any widget that may need them
4057                 this._subscribes.push(handle);
4058                 return handle;
4059         },
4060
4061         unsubscribe: function(/*Object*/ handle){
4062                 // summary:
4063                 //              Unsubscribes handle created by this.subscribe.
4064                 //              Also removes handle from this widget's list of subscriptions
4065                 for(var i=0; i<this._subscribes.length; i++){
4066                         if(this._subscribes[i] == handle){
4067                                 dojo.unsubscribe(handle);
4068                                 this._subscribes.splice(i, 1);
4069                                 return;
4070                         }
4071                 }
4072         },
4073
4074         isLeftToRight: function(){
4075                 // summary:
4076                 //              Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
4077                 // tags:
4078                 //              protected
4079                 return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean
4080         },
4081
4082         placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
4083                 // summary:
4084                 //              Place this widget's domNode reference somewhere in the DOM based
4085                 //              on standard dojo.place conventions, or passing a Widget reference that
4086                 //              contains and addChild member.
4087                 //
4088                 // description:
4089                 //              A convenience function provided in all _Widgets, providing a simple
4090                 //              shorthand mechanism to put an existing (or newly created) Widget
4091                 //              somewhere in the dom, and allow chaining.
4092                 //
4093                 // reference:
4094                 //              The String id of a domNode, a domNode reference, or a reference to a Widget posessing
4095                 //              an addChild method.
4096                 //
4097                 // position:
4098                 //              If passed a string or domNode reference, the position argument
4099                 //              accepts a string just as dojo.place does, one of: "first", "last",
4100                 //              "before", or "after".
4101                 //
4102                 //              If passed a _Widget reference, and that widget reference has an ".addChild" method,
4103                 //              it will be called passing this widget instance into that method, supplying the optional
4104                 //              position index passed.
4105                 //
4106                 // returns:
4107                 //              dijit._Widget
4108                 //              Provides a useful return of the newly created dijit._Widget instance so you
4109                 //              can "chain" this function by instantiating, placing, then saving the return value
4110                 //              to a variable.
4111                 //
4112                 // example:
4113                 // |    // create a Button with no srcNodeRef, and place it in the body:
4114                 // |    var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body());
4115                 // |    // now, 'button' is still the widget reference to the newly created button
4116                 // |    dojo.connect(button, "onClick", function(e){ console.log('click'); });
4117                 //
4118                 // example:
4119                 // |    // create a button out of a node with id="src" and append it to id="wrapper":
4120                 // |    var button = new dijit.form.Button({},"src").placeAt("wrapper");
4121                 //
4122                 // example:
4123                 // |    // place a new button as the first element of some div
4124                 // |    var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
4125                 //
4126                 // example:
4127                 // |    // create a contentpane and add it to a TabContainer
4128                 // |    var tc = dijit.byId("myTabs");
4129                 // |    new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
4130
4131                 if(reference.declaredClass && reference.addChild){
4132                         reference.addChild(this, position);
4133                 }else{
4134                         dojo.place(this.domNode, reference, position);
4135                 }
4136                 return this;
4137         }
4138 });
4139
4140 })();
4141
4142 }
4143
4144 if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4145 dojo._hasResource["dijit._Widget"] = true;
4146 dojo.provide("dijit._Widget");
4147
4148
4149
4150
4151
4152 ////////////////// DEFERRED CONNECTS ///////////////////
4153
4154 // This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets'
4155 // DOM nodes) until someone actually needs to monitor that event.
4156 dojo.connect(dojo, "_connect",
4157         function(/*dijit._Widget*/ widget, /*String*/ event){
4158                 if(widget && dojo.isFunction(widget._onConnect)){
4159                         widget._onConnect(event);
4160                 }
4161         });
4162
4163 dijit._connectOnUseEventHandler = function(/*Event*/ event){};
4164
4165 ////////////////// ONDIJITCLICK SUPPORT ///////////////////
4166
4167 // Keep track of where the last keydown event was, to help avoid generating
4168 // spurious ondijitclick events when:
4169 // 1. focus is on a <button> or <a>
4170 // 2. user presses then releases the ENTER key
4171 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
4172 // 4. onkeyup event fires, causing the ondijitclick handler to fire
4173 dijit._lastKeyDownNode = null;
4174 if(dojo.isIE){
4175         (function(){
4176                 var keydownCallback = function(evt){
4177                         dijit._lastKeyDownNode = evt.srcElement;
4178                 };
4179                 dojo.doc.attachEvent('onkeydown', keydownCallback);
4180                 dojo.addOnWindowUnload(function(){
4181                         dojo.doc.detachEvent('onkeydown', keydownCallback);
4182                 });
4183         })();
4184 }else{
4185         dojo.doc.addEventListener('keydown', function(evt){
4186                 dijit._lastKeyDownNode = evt.target;
4187         }, true);
4188 }
4189
4190 (function(){
4191
4192 dojo.declare("dijit._Widget", dijit._WidgetBase, {
4193         // summary:
4194         //              Base class for all Dijit widgets.
4195         //
4196         //              Extends _WidgetBase, adding support for:
4197         //                      - deferred connections
4198         //                              A call like dojo.connect(myWidget, "onMouseMove", func)
4199         //                              will essentially do a dojo.connect(myWidget.domNode, "onMouseMove", func)
4200         //                      - ondijitclick
4201         //                              Support new dojoAttachEvent="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
4202         //                      - focus related functions
4203         //                              In particular, the onFocus()/onBlur() callbacks.   Driven internally by
4204         //                              dijit/_base/focus.js.
4205         //                      - deprecated methods
4206         //                      - onShow(), onHide(), onClose()
4207         //
4208         //              Also, by loading code in dijit/_base, turns on:
4209         //                      - browser sniffing (putting browser id like .dj_ie on <html> node)
4210         //                      - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
4211         
4212
4213         ////////////////// DEFERRED CONNECTS ///////////////////
4214
4215         // _deferredConnects: [protected] Object
4216         //              attributeMap addendum for event handlers that should be connected only on first use
4217         _deferredConnects: {
4218                 onClick: "",
4219                 onDblClick: "",
4220                 onKeyDown: "",
4221                 onKeyPress: "",
4222                 onKeyUp: "",
4223                 onMouseMove: "",
4224                 onMouseDown: "",
4225                 onMouseOut: "",
4226                 onMouseOver: "",
4227                 onMouseLeave: "",
4228                 onMouseEnter: "",
4229                 onMouseUp: ""
4230         },
4231
4232         onClick: dijit._connectOnUseEventHandler,
4233         /*=====
4234         onClick: function(event){
4235                 // summary:
4236                 //              Connect to this function to receive notifications of mouse click events.
4237                 // event:
4238                 //              mouse Event
4239                 // tags:
4240                 //              callback
4241         },
4242         =====*/
4243         onDblClick: dijit._connectOnUseEventHandler,
4244         /*=====
4245         onDblClick: function(event){
4246                 // summary:
4247                 //              Connect to this function to receive notifications of mouse double click events.
4248                 // event:
4249                 //              mouse Event
4250                 // tags:
4251                 //              callback
4252         },
4253         =====*/
4254         onKeyDown: dijit._connectOnUseEventHandler,
4255         /*=====
4256         onKeyDown: function(event){
4257                 // summary:
4258                 //              Connect to this function to receive notifications of keys being pressed down.
4259                 // event:
4260                 //              key Event
4261                 // tags:
4262                 //              callback
4263         },
4264         =====*/
4265         onKeyPress: dijit._connectOnUseEventHandler,
4266         /*=====
4267         onKeyPress: function(event){
4268                 // summary:
4269                 //              Connect to this function to receive notifications of printable keys being typed.
4270                 // event:
4271                 //              key Event
4272                 // tags:
4273                 //              callback
4274         },
4275         =====*/
4276         onKeyUp: dijit._connectOnUseEventHandler,
4277         /*=====
4278         onKeyUp: function(event){
4279                 // summary:
4280                 //              Connect to this function to receive notifications of keys being released.
4281                 // event:
4282                 //              key Event
4283                 // tags:
4284                 //              callback
4285         },
4286         =====*/
4287         onMouseDown: dijit._connectOnUseEventHandler,
4288         /*=====
4289         onMouseDown: function(event){
4290                 // summary:
4291                 //              Connect to this function to receive notifications of when the mouse button is pressed down.
4292                 // event:
4293                 //              mouse Event
4294                 // tags:
4295                 //              callback
4296         },
4297         =====*/
4298         onMouseMove: dijit._connectOnUseEventHandler,
4299         /*=====
4300         onMouseMove: function(event){
4301                 // summary:
4302                 //              Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
4303                 // event:
4304                 //              mouse Event
4305                 // tags:
4306                 //              callback
4307         },
4308         =====*/
4309         onMouseOut: dijit._connectOnUseEventHandler,
4310         /*=====
4311         onMouseOut: function(event){
4312                 // summary:
4313                 //              Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
4314                 // event:
4315                 //              mouse Event
4316                 // tags:
4317                 //              callback
4318         },
4319         =====*/
4320         onMouseOver: dijit._connectOnUseEventHandler,
4321         /*=====
4322         onMouseOver: function(event){
4323                 // summary:
4324                 //              Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
4325                 // event:
4326                 //              mouse Event
4327                 // tags:
4328                 //              callback
4329         },
4330         =====*/
4331         onMouseLeave: dijit._connectOnUseEventHandler,
4332         /*=====
4333         onMouseLeave: function(event){
4334                 // summary:
4335                 //              Connect to this function to receive notifications of when the mouse moves off of this widget.
4336                 // event:
4337                 //              mouse Event
4338                 // tags:
4339                 //              callback
4340         },
4341         =====*/
4342         onMouseEnter: dijit._connectOnUseEventHandler,
4343         /*=====
4344         onMouseEnter: function(event){
4345                 // summary:
4346                 //              Connect to this function to receive notifications of when the mouse moves onto this widget.
4347                 // event:
4348                 //              mouse Event
4349                 // tags:
4350                 //              callback
4351         },
4352         =====*/
4353         onMouseUp: dijit._connectOnUseEventHandler,
4354         /*=====
4355         onMouseUp: function(event){
4356                 // summary:
4357                 //              Connect to this function to receive notifications of when the mouse button is released.
4358                 // event:
4359                 //              mouse Event
4360                 // tags:
4361                 //              callback
4362         },
4363         =====*/
4364
4365         create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
4366                 // To avoid double-connects, remove entries from _deferredConnects
4367                 // that have been setup manually by a subclass (ex, by dojoAttachEvent).
4368                 // If a subclass has redefined a callback (ex: onClick) then assume it's being
4369                 // connected to manually.
4370                 this._deferredConnects = dojo.clone(this._deferredConnects);
4371                 for(var attr in this.attributeMap){
4372                         delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects
4373                 }
4374                 for(attr in this._deferredConnects){
4375                         if(this[attr] !== dijit._connectOnUseEventHandler){
4376                                 delete this._deferredConnects[attr];    // redefined, probably dojoAttachEvent exists
4377                         }
4378                 }
4379
4380                 this.inherited(arguments);
4381
4382                 if(this.domNode){
4383                         // If the developer has specified a handler as a widget parameter
4384                         // (ex: new Button({onClick: ...})
4385                         // then naturally need to connect from DOM node to that handler immediately,
4386                         for(attr in this.params){
4387                                 this._onConnect(attr);
4388                         }
4389                 }
4390         },
4391
4392         _onConnect: function(/*String*/ event){
4393                 // summary:
4394                 //              Called when someone connects to one of my handlers.
4395                 //              "Turn on" that handler if it isn't active yet.
4396                 //
4397                 //              This is also called for every single initialization parameter
4398                 //              so need to do nothing for parameters like "id".
4399                 // tags:
4400                 //              private
4401                 if(event in this._deferredConnects){
4402                         var mapNode = this[this._deferredConnects[event] || 'domNode'];
4403                         this.connect(mapNode, event.toLowerCase(), event);
4404                         delete this._deferredConnects[event];
4405                 }
4406         },
4407
4408         ////////////////// FOCUS RELATED ///////////////////
4409         // _onFocus() and _onBlur() are called by the focus manager
4410
4411         // focused: [readonly] Boolean
4412         //              This widget or a widget it contains has focus, or is "active" because
4413         //              it was recently clicked.
4414         focused: false,
4415
4416         isFocusable: function(){
4417                 // summary:
4418                 //              Return true if this widget can currently be focused
4419                 //              and false if not
4420                 return this.focus && (dojo.style(this.domNode, "display") != "none");
4421         },
4422
4423         onFocus: function(){
4424                 // summary:
4425                 //              Called when the widget becomes "active" because
4426                 //              it or a widget inside of it either has focus, or has recently
4427                 //              been clicked.
4428                 // tags:
4429                 //              callback
4430         },
4431
4432         onBlur: function(){
4433                 // summary:
4434                 //              Called when the widget stops being "active" because
4435                 //              focus moved to something outside of it, or the user
4436                 //              clicked somewhere outside of it, or the widget was
4437                 //              hidden.
4438                 // tags:
4439                 //              callback
4440         },
4441
4442         _onFocus: function(e){
4443                 // summary:
4444                 //              This is where widgets do processing for when they are active,
4445                 //              such as changing CSS classes.  See onFocus() for more details.
4446                 // tags:
4447                 //              protected
4448                 this.onFocus();
4449         },
4450
4451         _onBlur: function(){
4452                 // summary:
4453                 //              This is where widgets do processing for when they stop being active,
4454                 //              such as changing CSS classes.  See onBlur() for more details.
4455                 // tags:
4456                 //              protected
4457                 this.onBlur();
4458         },
4459
4460         ////////////////// DEPRECATED METHODS ///////////////////
4461
4462         setAttribute: function(/*String*/ attr, /*anything*/ value){
4463                 // summary:
4464                 //              Deprecated.  Use set() instead.
4465                 // tags:
4466                 //              deprecated
4467                 dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
4468                 this.set(attr, value);
4469         },
4470
4471         attr: function(/*String|Object*/name, /*Object?*/value){
4472                 // summary:
4473                 //              Set or get properties on a widget instance.
4474                 //      name:
4475                 //              The property to get or set. If an object is passed here and not
4476                 //              a string, its keys are used as names of attributes to be set
4477                 //              and the value of the object as values to set in the widget.
4478                 //      value:
4479                 //              Optional. If provided, attr() operates as a setter. If omitted,
4480                 //              the current value of the named property is returned.
4481                 // description:
4482                 //              This method is deprecated, use get() or set() directly.
4483
4484                 // Print deprecation warning but only once per calling function
4485                 if(dojo.config.isDebug){
4486                         var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
4487                                 caller = (arguments.callee.caller || "unknown caller").toString();
4488                         if(!alreadyCalledHash[caller]){
4489                                 dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
4490                                 caller, "", "2.0");
4491                                 alreadyCalledHash[caller] = true;
4492                         }
4493                 }
4494
4495                 var args = arguments.length;
4496                 if(args >= 2 || typeof name === "object"){ // setter
4497                         return this.set.apply(this, arguments);
4498                 }else{ // getter
4499                         return this.get(name);
4500                 }
4501         },
4502         
4503         ////////////////// ONDIJITCLICK SUPPORT ///////////////////
4504
4505         // nodesWithKeyClick: [private] String[]
4506         //              List of nodes that correctly handle click events via native browser support,
4507         //              and don't need dijit's help
4508         nodesWithKeyClick: ["input", "button"],
4509
4510         connect: function(
4511                         /*Object|null*/ obj,
4512                         /*String|Function*/ event,
4513                         /*String|Function*/ method){
4514                 // summary:
4515                 //              Connects specified obj/event to specified method of this object
4516                 //              and registers for disconnect() on widget destroy.
4517                 // description:
4518                 //              Provide widget-specific analog to dojo.connect, except with the
4519                 //              implicit use of this widget as the target object.
4520                 //              This version of connect also provides a special "ondijitclick"
4521                 //              event which triggers on a click or space or enter keyup.
4522                 //              Events connected with `this.connect` are disconnected upon
4523                 //              destruction.
4524                 // returns:
4525                 //              A handle that can be passed to `disconnect` in order to disconnect before
4526                 //              the widget is destroyed.
4527                 // example:
4528                 //      |       var btn = new dijit.form.Button();
4529                 //      |       // when foo.bar() is called, call the listener we're going to
4530                 //      |       // provide in the scope of btn
4531                 //      |       btn.connect(foo, "bar", function(){
4532                 //      |               console.debug(this.toString());
4533                 //      |       });
4534                 // tags:
4535                 //              protected
4536
4537                 var d = dojo,
4538                         dc = d._connect,
4539                         handles = this.inherited(arguments, [obj, event == "ondijitclick" ? "onclick" : event, method]);
4540
4541                 if(event == "ondijitclick"){
4542                         // add key based click activation for unsupported nodes.
4543                         // do all processing onkey up to prevent spurious clicks
4544                         // for details see comments at top of this file where _lastKeyDownNode is defined
4545                         if(d.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button
4546                                 var m = d.hitch(this, method);
4547                                 handles.push(
4548                                         dc(obj, "onkeydown", this, function(e){
4549                                                 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
4550                                                 if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
4551                                                         !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
4552                                                         // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
4553                                                         dijit._lastKeyDownNode = e.target;
4554                                                         
4555                                                         // Stop event to prevent scrolling on space key in IE.
4556                                                         // But don't do this for _HasDropDown because it surpresses the onkeypress
4557                                                         // event needed to open the drop down when the user presses the SPACE key.
4558                                                         if(!("openDropDown" in this && obj == this._buttonNode)){
4559                                                                 e.preventDefault();
4560                                                         }
4561                                                 }
4562                                         }),
4563                                         dc(obj, "onkeyup", this, function(e){
4564                                                 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode));
4565                                                 if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) &&
4566                                                         e.target == dijit._lastKeyDownNode &&   // === breaks greasemonkey
4567                                                         !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){
4568                                                                 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
4569                                                                 dijit._lastKeyDownNode = null;
4570                                                                 return m(e);
4571                                                 }
4572                                         })
4573                                 );
4574                         }
4575                 }
4576
4577                 return handles;         // _Widget.Handle
4578         },
4579
4580         ////////////////// MISCELLANEOUS METHODS ///////////////////
4581
4582         _onShow: function(){
4583                 // summary:
4584                 //              Internal method called when this widget is made visible.
4585                 //              See `onShow` for details.
4586                 this.onShow();
4587         },
4588
4589         onShow: function(){
4590                 // summary:
4591                 //              Called when this widget becomes the selected pane in a
4592                 //              `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
4593                 //              `dijit.layout.AccordionContainer`, etc.
4594                 //
4595                 //              Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
4596                 // tags:
4597                 //              callback
4598         },
4599
4600         onHide: function(){
4601                 // summary:
4602                         //              Called when another widget becomes the selected pane in a
4603                         //              `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
4604                         //              `dijit.layout.AccordionContainer`, etc.
4605                         //
4606                         //              Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
4607                         // tags:
4608                         //              callback
4609         },
4610
4611         onClose: function(){
4612                 // summary:
4613                 //              Called when this widget is being displayed as a popup (ex: a Calendar popped
4614                 //              up from a DateTextBox), and it is hidden.
4615                 //              This is called from the dijit.popup code, and should not be called directly.
4616                 //
4617                 //              Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
4618                 //              Callback if a user tries to close the child.   Child will be closed if this function returns true.
4619                 // tags:
4620                 //              extension
4621
4622                 return true;            // Boolean
4623         }
4624 });
4625
4626 })();
4627
4628 }
4629
4630 if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4631 dojo._hasResource["dojo.string"] = true;
4632 dojo.provide("dojo.string");
4633
4634 dojo.getObject("string", true, dojo);
4635
4636 /*=====
4637 dojo.string = {
4638         // summary: String utilities for Dojo
4639 };
4640 =====*/
4641
4642 dojo.string.rep = function(/*String*/str, /*Integer*/num){
4643         //      summary:
4644         //              Efficiently replicate a string `n` times.
4645         //      str:
4646         //              the string to replicate
4647         //      num:
4648         //              number of times to replicate the string
4649         
4650         if(num <= 0 || !str){ return ""; }
4651         
4652         var buf = [];
4653         for(;;){
4654                 if(num & 1){
4655                         buf.push(str);
4656                 }
4657                 if(!(num >>= 1)){ break; }
4658                 str += str;
4659         }
4660         return buf.join("");    // String
4661 };
4662
4663 dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
4664         //      summary:
4665         //              Pad a string to guarantee that it is at least `size` length by
4666         //              filling with the character `ch` at either the start or end of the
4667         //              string. Pads at the start, by default.
4668         //      text:
4669         //              the string to pad
4670         //      size:
4671         //              length to provide padding
4672         //      ch:
4673         //              character to pad, defaults to '0'
4674         //      end:
4675         //              adds padding at the end if true, otherwise pads at start
4676         //      example:
4677         //      |       // Fill the string to length 10 with "+" characters on the right.  Yields "Dojo++++++".
4678         //      |       dojo.string.pad("Dojo", 10, "+", true);
4679
4680         if(!ch){
4681                 ch = '0';
4682         }
4683         var out = String(text),
4684                 pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
4685         return end ? out + pad : pad + out;     // String
4686 };
4687
4688 dojo.string.substitute = function(      /*String*/              template,
4689                                                                         /*Object|Array*/map,
4690                                                                         /*Function?*/   transform,
4691                                                                         /*Object?*/             thisObject){
4692         //      summary:
4693         //              Performs parameterized substitutions on a string. Throws an
4694         //              exception if any parameter is unmatched.
4695         //      template:
4696         //              a string with expressions in the form `${key}` to be replaced or
4697         //              `${key:format}` which specifies a format function. keys are case-sensitive.
4698         //      map:
4699         //              hash to search for substitutions
4700         //      transform:
4701         //              a function to process all parameters before substitution takes
4702         //              place, e.g. mylib.encodeXML
4703         //      thisObject:
4704         //              where to look for optional format function; default to the global
4705         //              namespace
4706         //      example:
4707         //              Substitutes two expressions in a string from an Array or Object
4708         //      |       // returns "File 'foo.html' is not found in directory '/temp'."
4709         //      |       // by providing substitution data in an Array
4710         //      |       dojo.string.substitute(
4711         //      |               "File '${0}' is not found in directory '${1}'.",
4712         //      |               ["foo.html","/temp"]
4713         //      |       );
4714         //      |
4715         //      |       // also returns "File 'foo.html' is not found in directory '/temp'."
4716         //      |       // but provides substitution data in an Object structure.  Dotted
4717         //      |       // notation may be used to traverse the structure.
4718         //      |       dojo.string.substitute(
4719         //      |               "File '${name}' is not found in directory '${info.dir}'.",
4720         //      |               { name: "foo.html", info: { dir: "/temp" } }
4721         //      |       );
4722         //      example:
4723         //              Use a transform function to modify the values:
4724         //      |       // returns "file 'foo.html' is not found in directory '/temp'."
4725         //      |       dojo.string.substitute(
4726         //      |               "${0} is not found in ${1}.",
4727         //      |               ["foo.html","/temp"],
4728         //      |               function(str){
4729         //      |                       // try to figure out the type
4730         //      |                       var prefix = (str.charAt(0) == "/") ? "directory": "file";
4731         //      |                       return prefix + " '" + str + "'";
4732         //      |               }
4733         //      |       );
4734         //      example:
4735         //              Use a formatter
4736         //      |       // returns "thinger -- howdy"
4737         //      |       dojo.string.substitute(
4738         //      |               "${0:postfix}", ["thinger"], null, {
4739         //      |                       postfix: function(value, key){
4740         //      |                               return value + " -- howdy";
4741         //      |                       }
4742         //      |               }
4743         //      |       );
4744
4745         thisObject = thisObject || dojo.global;
4746         transform = transform ?
4747                 dojo.hitch(thisObject, transform) : function(v){ return v; };
4748
4749         return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
4750                 function(match, key, format){
4751                         var value = dojo.getObject(key, false, map);
4752                         if(format){
4753                                 value = dojo.getObject(format, false, thisObject).call(thisObject, value, key);
4754                         }
4755                         return transform(value, key).toString();
4756                 }); // String
4757 };
4758
4759 /*=====
4760 dojo.string.trim = function(str){
4761         //      summary:
4762         //              Trims whitespace from both sides of the string
4763         //      str: String
4764         //              String to be trimmed
4765         //      returns: String
4766         //              Returns the trimmed string
4767         //      description:
4768         //              This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
4769         //              The short yet performant version of this function is dojo.trim(),
4770         //              which is part of Dojo base.  Uses String.prototype.trim instead, if available.
4771         return "";      // String
4772 }
4773 =====*/
4774
4775 dojo.string.trim = String.prototype.trim ?
4776         dojo.trim : // aliasing to the native function
4777         function(str){
4778                 str = str.replace(/^\s+/, '');
4779                 for(var i = str.length - 1; i >= 0; i--){
4780                         if(/\S/.test(str.charAt(i))){
4781                                 str = str.substring(0, i + 1);
4782                                 break;
4783                         }
4784                 }
4785                 return str;
4786         };
4787
4788 }
4789
4790 if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4791 dojo._hasResource["dojo.cache"] = true;
4792 dojo.provide("dojo.cache");
4793
4794
4795 /*=====
4796 dojo.cache = {
4797         // summary:
4798         //              A way to cache string content that is fetchable via `dojo.moduleUrl`.
4799 };
4800 =====*/
4801
4802         var cache = {};
4803         dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
4804                 // summary:
4805                 //              A getter and setter for storing the string content associated with the
4806                 //              module and url arguments.
4807                 // description:
4808                 //              module and url are used to call `dojo.moduleUrl()` to generate a module URL.
4809                 //              If value is specified, the cache value for the moduleUrl will be set to
4810                 //              that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
4811                 //              in its internal cache and return that cached value for the URL. To clear
4812                 //              a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
4813                 //              the URL contents, only modules on the same domain of the page can use this capability.
4814                 //              The build system can inline the cache values though, to allow for xdomain hosting.
4815                 // module: String||Object
4816                 //              If a String, the module name to use for the base part of the URL, similar to module argument
4817                 //              to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
4818                 //              generates a valid path for the cache item. For example, a dojo._Url object.
4819                 // url: String
4820                 //              The rest of the path to append to the path derived from the module argument. If
4821                 //              module is an object, then this second argument should be the "value" argument instead.
4822                 // value: String||Object?
4823                 //              If a String, the value to use in the cache for the module/url combination.
4824                 //              If an Object, it can have two properties: value and sanitize. The value property
4825                 //              should be the value to use in the cache, and sanitize can be set to true or false,
4826                 //              to indicate if XML declarations should be removed from the value and if the HTML
4827                 //              inside a body tag in the value should be extracted as the real value. The value argument
4828                 //              or the value property on the value argument are usually only used by the build system
4829                 //              as it inlines cache content.
4830                 //      example:
4831                 //              To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
4832                 //              of call is used to avoid an issue with the build system erroneously trying to intern
4833                 //              this example. To get the build system to intern your dojo.cache calls, use the
4834                 //              "dojo.cache" style of call):
4835                 //              |       //If template.html contains "<h1>Hello</h1>" that will be
4836                 //              |       //the value for the text variable.
4837                 //              |       var text = dojo["cache"]("my.module", "template.html");
4838                 //      example:
4839                 //              To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
4840                 //               (the dojo["cache"] style of call is used to avoid an issue with the build system
4841                 //              erroneously trying to intern this example. To get the build system to intern your
4842                 //              dojo.cache calls, use the "dojo.cache" style of call):
4843                 //              |       //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
4844                 //              |       //text variable will contain just "<h1>Hello</h1>".
4845                 //              |       var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
4846                 //      example:
4847                 //              Same example as previous, but demostrates how an object can be passed in as
4848                 //              the first argument, then the value argument can then be the second argument.
4849                 //              |       //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
4850                 //              |       //text variable will contain just "<h1>Hello</h1>".
4851                 //              |       var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
4852
4853                 //Module could be a string, or an object that has a toString() method
4854                 //that will return a useful path. If it is an object, then the "url" argument
4855                 //will actually be the value argument.
4856                 if(typeof module == "string"){
4857                         var pathObj = dojo.moduleUrl(module, url);
4858                 }else{
4859                         pathObj = module;
4860                         value = url;
4861                 }
4862                 var key = pathObj.toString();
4863
4864                 var val = value;
4865                 if(value != undefined && !dojo.isString(value)){
4866                         val = ("value" in value ? value.value : undefined);
4867                 }
4868
4869                 var sanitize = value && value.sanitize ? true : false;
4870
4871                 if(typeof val == "string"){
4872                         //We have a string, set cache value
4873                         val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
4874                 }else if(val === null){
4875                         //Remove cached value
4876                         delete cache[key];
4877                 }else{
4878                         //Allow cache values to be empty strings. If key property does
4879                         //not exist, fetch it.
4880                         if(!(key in cache)){
4881                                 val = dojo._getText(key);
4882                                 cache[key] = sanitize ? dojo.cache._sanitize(val) : val;
4883                         }
4884                         val = cache[key];
4885                 }
4886                 return val; //String
4887         };
4888
4889         dojo.cache._sanitize = function(/*String*/val){
4890                 // summary:
4891                 //              Strips <?xml ...?> declarations so that external SVG and XML
4892                 //              documents can be added to a document without worry. Also, if the string
4893                 //              is an HTML document, only the part inside the body tag is returned.
4894                 // description:
4895                 //              Copied from dijit._Templated._sanitizeTemplateString.
4896                 if(val){
4897                         val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
4898                         var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
4899                         if(matches){
4900                                 val = matches[1];
4901                         }
4902                 }else{
4903                         val = "";
4904                 }
4905                 return val; //String
4906         };
4907
4908 }
4909
4910 if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
4911 dojo._hasResource["dijit._Templated"] = true;
4912 dojo.provide("dijit._Templated");
4913
4914
4915
4916
4917
4918
4919 dojo.declare("dijit._Templated",
4920         null,
4921         {
4922                 // summary:
4923                 //              Mixin for widgets that are instantiated from a template
4924
4925                 // templateString: [protected] String
4926                 //              A string that represents the widget template. Pre-empts the
4927                 //              templatePath. In builds that have their strings "interned", the
4928                 //              templatePath is converted to an inline templateString, thereby
4929                 //              preventing a synchronous network call.
4930                 //
4931                 //              Use in conjunction with dojo.cache() to load from a file.
4932                 templateString: null,
4933
4934                 // templatePath: [protected deprecated] String
4935                 //              Path to template (HTML file) for this widget relative to dojo.baseUrl.
4936                 //              Deprecated: use templateString with dojo.cache() instead.
4937                 templatePath: null,
4938
4939                 // widgetsInTemplate: [protected] Boolean
4940                 //              Should we parse the template to find widgets that might be
4941                 //              declared in markup inside it?  False by default.
4942                 widgetsInTemplate: false,
4943
4944                 // skipNodeCache: [protected] Boolean
4945                 //              If using a cached widget template node poses issues for a
4946                 //              particular widget class, it can set this property to ensure
4947                 //              that its template is always re-built from a string
4948                 _skipNodeCache: false,
4949
4950                 // _earlyTemplatedStartup: Boolean
4951                 //              A fallback to preserve the 1.0 - 1.3 behavior of children in
4952                 //              templates having their startup called before the parent widget
4953                 //              fires postCreate. Defaults to 'false', causing child widgets to
4954                 //              have their .startup() called immediately before a parent widget
4955                 //              .startup(), but always after the parent .postCreate(). Set to
4956                 //              'true' to re-enable to previous, arguably broken, behavior.
4957                 _earlyTemplatedStartup: false,
4958
4959 /*=====
4960                 // _attachPoints: [private] String[]
4961                 //              List of widget attribute names associated with dojoAttachPoint=... in the
4962                 //              template, ex: ["containerNode", "labelNode"]
4963                 _attachPoints: [],
4964  =====*/
4965
4966 /*=====
4967                 // _attachEvents: [private] Handle[]
4968                 //              List of connections associated with dojoAttachEvent=... in the
4969                 //              template
4970                 _attachEvents: [],
4971  =====*/
4972
4973                 constructor: function(){
4974                         this._attachPoints = [];
4975                         this._attachEvents = [];
4976                 },
4977
4978                 _stringRepl: function(tmpl){
4979                         // summary:
4980                         //              Does substitution of ${foo} type properties in template string
4981                         // tags:
4982                         //              private
4983                         var className = this.declaredClass, _this = this;
4984                         // Cache contains a string because we need to do property replacement
4985                         // do the property replacement
4986                         return dojo.string.substitute(tmpl, this, function(value, key){
4987                                 if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); }
4988                                 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
4989                                 if(value == null){ return ""; }
4990
4991                                 // Substitution keys beginning with ! will skip the transform step,
4992                                 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
4993                                 return key.charAt(0) == "!" ? value :
4994                                         // Safer substitution, see heading "Attribute values" in
4995                                         // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
4996                                         value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
4997                         }, this);
4998                 },
4999
5000                 buildRendering: function(){
5001                         // summary:
5002                         //              Construct the UI for this widget from a template, setting this.domNode.
5003                         // tags:
5004                         //              protected
5005
5006                         // Lookup cached version of template, and download to cache if it
5007                         // isn't there already.  Returns either a DomNode or a string, depending on
5008                         // whether or not the template contains ${foo} replacement parameters.
5009                         var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
5010
5011                         var node;
5012                         if(dojo.isString(cached)){
5013                                 node = dojo._toDom(this._stringRepl(cached));
5014                                 if(node.nodeType != 1){
5015                                         // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
5016                                         throw new Error("Invalid template: " + cached);
5017                                 }
5018                         }else{
5019                                 // if it's a node, all we have to do is clone it
5020                                 node = cached.cloneNode(true);
5021                         }
5022
5023                         this.domNode = node;
5024
5025                         // Call down to _Widget.buildRendering() to get base classes assigned
5026                         // TODO: change the baseClass assignment to attributeMap
5027                         this.inherited(arguments);
5028
5029                         // recurse through the node, looking for, and attaching to, our
5030                         // attachment points and events, which should be defined on the template node.
5031                         this._attachTemplateNodes(node);
5032
5033                         if(this.widgetsInTemplate){
5034                                 // Store widgets that we need to start at a later point in time
5035                                 var cw = (this._startupWidgets = dojo.parser.parse(node, {
5036                                         noStart: !this._earlyTemplatedStartup,
5037                                         template: true,
5038                                         inherited: {dir: this.dir, lang: this.lang},
5039                                         propsThis: this,        // so data-dojo-props of widgets in the template can reference "this" to refer to me
5040                                         scope: "dojo"   // even in multi-version mode templates use dojoType/data-dojo-type
5041                                 }));
5042
5043                                 this._supportingWidgets = dijit.findWidgets(node);
5044
5045                                 this._attachTemplateNodes(cw, function(n,p){
5046                                         return n[p];
5047                                 });
5048                         }
5049
5050                         this._fillContent(this.srcNodeRef);
5051                 },
5052
5053                 _fillContent: function(/*DomNode*/ source){
5054                         // summary:
5055                         //              Relocate source contents to templated container node.
5056                         //              this.containerNode must be able to receive children, or exceptions will be thrown.
5057                         // tags:
5058                         //              protected
5059                         var dest = this.containerNode;
5060                         if(source && dest){
5061                                 while(source.hasChildNodes()){
5062                                         dest.appendChild(source.firstChild);
5063                                 }
5064                         }
5065                 },
5066
5067                 _attachTemplateNodes: function(rootNode, getAttrFunc){
5068                         // summary:
5069                         //              Iterate through the template and attach functions and nodes accordingly.
5070                         //              Alternately, if rootNode is an array of widgets, then will process dojoAttachPoint
5071                         //              etc. for those widgets.
5072                         // description:
5073                         //              Map widget properties and functions to the handlers specified in
5074                         //              the dom node and it's descendants. This function iterates over all
5075                         //              nodes and looks for these properties:
5076                         //                      * dojoAttachPoint
5077                         //                      * dojoAttachEvent
5078                         //                      * waiRole
5079                         //                      * waiState
5080                         // rootNode: DomNode|Array[Widgets]
5081                         //              the node to search for properties. All children will be searched.
5082                         // getAttrFunc: Function?
5083                         //              a function which will be used to obtain property for a given
5084                         //              DomNode/Widget
5085                         // tags:
5086                         //              private
5087
5088                         getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
5089
5090                         var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
5091                         var x = dojo.isArray(rootNode) ? 0 : -1;
5092                         for(; x<nodes.length; x++){
5093                                 var baseNode = (x == -1) ? rootNode : nodes[x];
5094                                 if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
5095                                         continue;
5096                                 }
5097                                 // Process dojoAttachPoint
5098                                 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
5099                                 if(attachPoint){
5100                                         var point, points = attachPoint.split(/\s*,\s*/);
5101                                         while((point = points.shift())){
5102                                                 if(dojo.isArray(this[point])){
5103                                                         this[point].push(baseNode);
5104                                                 }else{
5105                                                         this[point]=baseNode;
5106                                                 }
5107                                                 this._attachPoints.push(point);
5108                                         }
5109                                 }
5110
5111                                 // Process dojoAttachEvent
5112                                 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");;
5113                                 if(attachEvent){
5114                                         // NOTE: we want to support attributes that have the form
5115                                         // "domEvent: nativeEvent; ..."
5116                                         var event, events = attachEvent.split(/\s*,\s*/);
5117                                         var trim = dojo.trim;
5118                                         while((event = events.shift())){
5119                                                 if(event){
5120                                                         var thisFunc = null;
5121                                                         if(event.indexOf(":") != -1){
5122                                                                 // oh, if only JS had tuple assignment
5123                                                                 var funcNameArr = event.split(":");
5124                                                                 event = trim(funcNameArr[0]);
5125                                                                 thisFunc = trim(funcNameArr[1]);
5126                                                         }else{
5127                                                                 event = trim(event);
5128                                                         }
5129                                                         if(!thisFunc){
5130                                                                 thisFunc = event;
5131                                                         }
5132                                                         this._attachEvents.push(this.connect(baseNode, event, thisFunc));
5133                                                 }
5134                                         }
5135                                 }
5136
5137                                 // waiRole, waiState
5138                                 // TODO: remove this in 2.0, templates are now using role=... and aria-XXX=... attributes directicly
5139                                 var role = getAttrFunc(baseNode, "waiRole");
5140                                 if(role){
5141                                         dijit.setWaiRole(baseNode, role);
5142                                 }
5143                                 var values = getAttrFunc(baseNode, "waiState");
5144                                 if(values){
5145                                         dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
5146                                                 if(stateValue.indexOf('-') != -1){
5147                                                         var pair = stateValue.split('-');
5148                                                         dijit.setWaiState(baseNode, pair[0], pair[1]);
5149                                                 }
5150                                         });
5151                                 }
5152                         }
5153                 },
5154
5155                 startup: function(){
5156                         dojo.forEach(this._startupWidgets, function(w){
5157                                 if(w && !w._started && w.startup){
5158                                         w.startup();
5159                                 }
5160                         });
5161                         this.inherited(arguments);
5162                 },
5163
5164                 destroyRendering: function(){
5165                         // Delete all attach points to prevent IE6 memory leaks.
5166                         dojo.forEach(this._attachPoints, function(point){
5167                                 delete this[point];
5168                         }, this);
5169                         this._attachPoints = [];
5170
5171                         // And same for event handlers
5172                         dojo.forEach(this._attachEvents, this.disconnect, this);
5173                         this._attachEvents = [];
5174                         
5175                         this.inherited(arguments);
5176                 }
5177         }
5178 );
5179
5180 // key is either templatePath or templateString; object is either string or DOM tree
5181 dijit._Templated._templateCache = {};
5182
5183 dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
5184         // summary:
5185         //              Static method to get a template based on the templatePath or
5186         //              templateString key
5187         // templatePath: String||dojo.uri.Uri
5188         //              The URL to get the template from.
5189         // templateString: String?
5190         //              a string to use in lieu of fetching the template from a URL. Takes precedence
5191         //              over templatePath
5192         // returns: Mixed
5193         //              Either string (if there are ${} variables that need to be replaced) or just
5194         //              a DOM tree (if the node can be cloned directly)
5195
5196         // is it already cached?
5197         var tmplts = dijit._Templated._templateCache;
5198         var key = templateString || templatePath;
5199         var cached = tmplts[key];
5200         if(cached){
5201                 try{
5202                         // 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
5203                         if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){
5204                                 // string or node of the same document
5205                                 return cached;
5206                         }
5207                 }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
5208                 dojo.destroy(cached);
5209         }
5210
5211         // If necessary, load template string from template path
5212         if(!templateString){
5213                 templateString = dojo.cache(templatePath, {sanitize: true});
5214         }
5215         templateString = dojo.string.trim(templateString);
5216
5217         if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
5218                 // there are variables in the template so all we can do is cache the string
5219                 return (tmplts[key] = templateString); //String
5220         }else{
5221                 // there are no variables in the template so we can cache the DOM tree
5222                 var node = dojo._toDom(templateString);
5223                 if(node.nodeType != 1){
5224                         throw new Error("Invalid template: " + templateString);
5225                 }
5226                 return (tmplts[key] = node); //Node
5227         }
5228 };
5229
5230 if(dojo.isIE){
5231         dojo.addOnWindowUnload(function(){
5232                 var cache = dijit._Templated._templateCache;
5233                 for(var key in cache){
5234                         var value = cache[key];
5235                         if(typeof value == "object"){ // value is either a string or a DOM node template
5236                                 dojo.destroy(value);
5237                         }
5238                         delete cache[key];
5239                 }
5240         });
5241 }
5242
5243 // These arguments can be specified for widgets which are used in templates.
5244 // Since any widget can be specified as sub widgets in template, mix it
5245 // into the base widget class.  (This is a hack, but it's effective.)
5246 dojo.extend(dijit._Widget,{
5247         dojoAttachEvent: "",
5248         dojoAttachPoint: "",
5249         waiRole: "",
5250         waiState:""
5251 });
5252
5253 }
5254
5255 if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5256 dojo._hasResource["dijit._Container"] = true;
5257 dojo.provide("dijit._Container");
5258
5259
5260 dojo.declare("dijit._Container",
5261         null,
5262         {
5263                 // summary:
5264                 //              Mixin for widgets that contain a set of widget children.
5265                 // description:
5266                 //              Use this mixin for widgets that needs to know about and
5267                 //              keep track of their widget children. Suitable for widgets like BorderContainer
5268                 //              and TabContainer which contain (only) a set of child widgets.
5269                 //
5270                 //              It's not suitable for widgets like ContentPane
5271                 //              which contains mixed HTML (plain DOM nodes in addition to widgets),
5272                 //              and where contained widgets are not necessarily directly below
5273                 //              this.containerNode.   In that case calls like addChild(node, position)
5274                 //              wouldn't make sense.
5275
5276                 // isContainer: [protected] Boolean
5277                 //              Indicates that this widget acts as a "parent" to the descendant widgets.
5278                 //              When the parent is started it will call startup() on the child widgets.
5279                 //              See also `isLayoutContainer`.
5280                 isContainer: true,
5281
5282                 buildRendering: function(){
5283                         this.inherited(arguments);
5284                         if(!this.containerNode){
5285                                 // all widgets with descendants must set containerNode
5286                                         this.containerNode = this.domNode;
5287                         }
5288                 },
5289
5290                 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
5291                         // summary:
5292                         //              Makes the given widget a child of this widget.
5293                         // description:
5294                         //              Inserts specified child widget's dom node as a child of this widget's
5295                         //              container node, and possibly does other processing (such as layout).
5296
5297                         var refNode = this.containerNode;
5298                         if(insertIndex && typeof insertIndex == "number"){
5299                                 var children = this.getChildren();
5300                                 if(children && children.length >= insertIndex){
5301                                         refNode = children[insertIndex-1].domNode;
5302                                         insertIndex = "after";
5303                                 }
5304                         }
5305                         dojo.place(widget.domNode, refNode, insertIndex);
5306
5307                         // If I've been started but the child widget hasn't been started,
5308                         // start it now.  Make sure to do this after widget has been
5309                         // inserted into the DOM tree, so it can see that it's being controlled by me,
5310                         // so it doesn't try to size itself.
5311                         if(this._started && !widget._started){
5312                                 widget.startup();
5313                         }
5314                 },
5315
5316                 removeChild: function(/*Widget or int*/ widget){
5317                         // summary:
5318                         //              Removes the passed widget instance from this widget but does
5319                         //              not destroy it.  You can also pass in an integer indicating
5320                         //              the index within the container to remove
5321
5322                         if(typeof widget == "number"){
5323                                 widget = this.getChildren()[widget];
5324                         }
5325
5326                         if(widget){
5327                                 var node = widget.domNode;
5328                                 if(node && node.parentNode){
5329                                         node.parentNode.removeChild(node); // detach but don't destroy
5330                                 }
5331                         }
5332                 },
5333
5334                 hasChildren: function(){
5335                         // summary:
5336                         //              Returns true if widget has children, i.e. if this.containerNode contains something.
5337                         return this.getChildren().length > 0;   // Boolean
5338                 },
5339
5340                 destroyDescendants: function(/*Boolean*/ preserveDom){
5341                         // summary:
5342                         //      Destroys all the widgets inside this.containerNode,
5343                         //      but not this widget itself
5344                         dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); });
5345                 },
5346
5347                 _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
5348                         // summary:
5349                         //              Get the next or previous widget sibling of child
5350                         // dir:
5351                         //              if 1, get the next sibling
5352                         //              if -1, get the previous sibling
5353                         // tags:
5354                         //      private
5355                         var node = child.domNode,
5356                                 which = (dir>0 ? "nextSibling" : "previousSibling");
5357                         do{
5358                                 node = node[which];
5359                         }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
5360                         return node && dijit.byNode(node);      // dijit._Widget
5361                 },
5362
5363                 getIndexOfChild: function(/*dijit._Widget*/ child){
5364                         // summary:
5365                         //              Gets the index of the child in this container or -1 if not found
5366                         return dojo.indexOf(this.getChildren(), child); // int
5367                 },
5368
5369                 startup: function(){
5370                         // summary:
5371                         //              Called after all the widgets have been instantiated and their
5372                         //              dom nodes have been inserted somewhere under dojo.doc.body.
5373                         //
5374                         //              Widgets should override this method to do any initialization
5375                         //              dependent on other widgets existing, and then call
5376                         //              this superclass method to finish things off.
5377                         //
5378                         //              startup() in subclasses shouldn't do anything
5379                         //              size related because the size of the widget hasn't been set yet.
5380
5381                         if(this._started){ return; }
5382
5383                         // Startup all children of this widget
5384                         dojo.forEach(this.getChildren(), function(child){ child.startup(); });
5385
5386                         this.inherited(arguments);
5387                 }
5388         }
5389 );
5390
5391 }
5392
5393 if(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5394 dojo._hasResource["dijit._Contained"] = true;
5395 dojo.provide("dijit._Contained");
5396
5397
5398 dojo.declare("dijit._Contained",
5399                 null,
5400                 {
5401                         // summary:
5402                         //              Mixin for widgets that are children of a container widget
5403                         //
5404                         // example:
5405                         // |    // make a basic custom widget that knows about it's parents
5406                         // |    dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
5407
5408                         getParent: function(){
5409                                 // summary:
5410                                 //              Returns the parent widget of this widget, assuming the parent
5411                                 //              specifies isContainer
5412                                 var parent = dijit.getEnclosingWidget(this.domNode.parentNode);
5413                                 return parent && parent.isContainer ? parent : null;
5414                         },
5415
5416                         _getSibling: function(/*String*/ which){
5417                                 // summary:
5418                                 //      Returns next or previous sibling
5419                                 // which:
5420                                 //      Either "next" or "previous"
5421                                 // tags:
5422                                 //      private
5423                                 var node = this.domNode;
5424                                 do{
5425                                         node = node[which+"Sibling"];
5426                                 }while(node && node.nodeType != 1);
5427                                 return node && dijit.byNode(node);      // dijit._Widget
5428                         },
5429
5430                         getPreviousSibling: function(){
5431                                 // summary:
5432                                 //              Returns null if this is the first child of the parent,
5433                                 //              otherwise returns the next element sibling to the "left".
5434
5435                                 return this._getSibling("previous"); // dijit._Widget
5436                         },
5437
5438                         getNextSibling: function(){
5439                                 // summary:
5440                                 //              Returns null if this is the last child of the parent,
5441                                 //              otherwise returns the next element sibling to the "right".
5442
5443                                 return this._getSibling("next"); // dijit._Widget
5444                         },
5445
5446                         getIndexInParent: function(){
5447                                 // summary:
5448                                 //              Returns the index of this widget within its container parent.
5449                                 //              It returns -1 if the parent does not exist, or if the parent
5450                                 //              is not a dijit._Container
5451
5452                                 var p = this.getParent();
5453                                 if(!p || !p.getIndexOfChild){
5454                                         return -1; // int
5455                                 }
5456                                 return p.getIndexOfChild(this); // int
5457                         }
5458                 }
5459         );
5460
5461 }
5462
5463 if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5464 dojo._hasResource["dijit.layout._LayoutWidget"] = true;
5465 dojo.provide("dijit.layout._LayoutWidget");
5466
5467
5468
5469
5470
5471 dojo.declare("dijit.layout._LayoutWidget",
5472         [dijit._Widget, dijit._Container, dijit._Contained],
5473         {
5474                 // summary:
5475                 //              Base class for a _Container widget which is responsible for laying out its children.
5476                 //              Widgets which mixin this code must define layout() to manage placement and sizing of the children.
5477
5478                 // baseClass: [protected extension] String
5479                 //              This class name is applied to the widget's domNode
5480                 //              and also may be used to generate names for sub nodes,
5481                 //              for example dijitTabContainer-content.
5482                 baseClass: "dijitLayoutContainer",
5483
5484                 // isLayoutContainer: [protected] Boolean
5485                 //              Indicates that this widget is going to call resize() on its
5486                 //              children widgets, setting their size, when they become visible.
5487                 isLayoutContainer: true,
5488
5489                 buildRendering: function(){
5490                         this.inherited(arguments);
5491                         dojo.addClass(this.domNode, "dijitContainer");
5492                 },
5493
5494                 startup: function(){
5495                         // summary:
5496                         //              Called after all the widgets have been instantiated and their
5497                         //              dom nodes have been inserted somewhere under dojo.doc.body.
5498                         //
5499                         //              Widgets should override this method to do any initialization
5500                         //              dependent on other widgets existing, and then call
5501                         //              this superclass method to finish things off.
5502                         //
5503                         //              startup() in subclasses shouldn't do anything
5504                         //              size related because the size of the widget hasn't been set yet.
5505
5506                         if(this._started){ return; }
5507
5508                         // Need to call inherited first - so that child widgets get started
5509                         // up correctly
5510                         this.inherited(arguments);
5511
5512                         // If I am a not being controlled by a parent layout widget...
5513                         var parent = this.getParent && this.getParent()
5514                         if(!(parent && parent.isLayoutContainer)){
5515                                 // Do recursive sizing and layout of all my descendants
5516                                 // (passing in no argument to resize means that it has to glean the size itself)
5517                                 this.resize();
5518
5519                                 // Since my parent isn't a layout container, and my style *may be* width=height=100%
5520                                 // or something similar (either set directly or via a CSS class),
5521                                 // monitor when my size changes so that I can re-layout.
5522                                 // For browsers where I can't directly monitor when my size changes,
5523                                 // monitor when the viewport changes size, which *may* indicate a size change for me.
5524                                 this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
5525                                         // Using function(){} closure to ensure no arguments to resize.
5526                                         this.resize();
5527                                 });
5528                         }
5529                 },
5530
5531                 resize: function(changeSize, resultSize){
5532                         // summary:
5533                         //              Call this to resize a widget, or after its size has changed.
5534                         // description:
5535                         //              Change size mode:
5536                         //                      When changeSize is specified, changes the marginBox of this widget
5537                         //                      and forces it to relayout its contents accordingly.
5538                         //                      changeSize may specify height, width, or both.
5539                         //
5540                         //                      If resultSize is specified it indicates the size the widget will
5541                         //                      become after changeSize has been applied.
5542                         //
5543                         //              Notification mode:
5544                         //                      When changeSize is null, indicates that the caller has already changed
5545                         //                      the size of the widget, or perhaps it changed because the browser
5546                         //                      window was resized.  Tells widget to relayout its contents accordingly.
5547                         //
5548                         //                      If resultSize is also specified it indicates the size the widget has
5549                         //                      become.
5550                         //
5551                         //              In either mode, this method also:
5552                         //                      1. Sets this._borderBox and this._contentBox to the new size of
5553                         //                              the widget.  Queries the current domNode size if necessary.
5554                         //                      2. Calls layout() to resize contents (and maybe adjust child widgets).
5555                         //
5556                         // changeSize: Object?
5557                         //              Sets the widget to this margin-box size and position.
5558                         //              May include any/all of the following properties:
5559                         //      |       {w: int, h: int, l: int, t: int}
5560                         //
5561                         // resultSize: Object?
5562                         //              The margin-box size of this widget after applying changeSize (if
5563                         //              changeSize is specified).  If caller knows this size and
5564                         //              passes it in, we don't need to query the browser to get the size.
5565                         //      |       {w: int, h: int}
5566
5567                         var node = this.domNode;
5568
5569                         // set margin box size, unless it wasn't specified, in which case use current size
5570                         if(changeSize){
5571                                 dojo.marginBox(node, changeSize);
5572
5573                                 // set offset of the node
5574                                 if(changeSize.t){ node.style.top = changeSize.t + "px"; }
5575                                 if(changeSize.l){ node.style.left = changeSize.l + "px"; }
5576                         }
5577
5578                         // If either height or width wasn't specified by the user, then query node for it.
5579                         // But note that setting the margin box and then immediately querying dimensions may return
5580                         // inaccurate results, so try not to depend on it.
5581                         var mb = resultSize || {};
5582                         dojo.mixin(mb, changeSize || {});       // changeSize overrides resultSize
5583                         if( !("h" in mb) || !("w" in mb) ){
5584                                 mb = dojo.mixin(dojo.marginBox(node), mb);      // just use dojo.marginBox() to fill in missing values
5585                         }
5586
5587                         // Compute and save the size of my border box and content box
5588                         // (w/out calling dojo.contentBox() since that may fail if size was recently set)
5589                         var cs = dojo.getComputedStyle(node);
5590                         var me = dojo._getMarginExtents(node, cs);
5591                         var be = dojo._getBorderExtents(node, cs);
5592                         var bb = (this._borderBox = {
5593                                 w: mb.w - (me.w + be.w),
5594                                 h: mb.h - (me.h + be.h)
5595                         });
5596                         var pe = dojo._getPadExtents(node, cs);
5597                         this._contentBox = {
5598                                 l: dojo._toPixelValue(node, cs.paddingLeft),
5599                                 t: dojo._toPixelValue(node, cs.paddingTop),
5600                                 w: bb.w - pe.w,
5601                                 h: bb.h - pe.h
5602                         };
5603
5604                         // Callback for widget to adjust size of its children
5605                         this.layout();
5606                 },
5607
5608                 layout: function(){
5609                         // summary:
5610                         //              Widgets override this method to size and position their contents/children.
5611                         //              When this is called this._contentBox is guaranteed to be set (see resize()).
5612                         //
5613                         //              This is called after startup(), and also when the widget's size has been
5614                         //              changed.
5615                         // tags:
5616                         //              protected extension
5617                 },
5618
5619                 _setupChild: function(/*dijit._Widget*/child){
5620                         // summary:
5621                         //              Common setup for initial children and children which are added after startup
5622                         // tags:
5623                         //              protected extension
5624
5625                         var cls = this.baseClass + "-child "
5626                                 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
5627                         dojo.addClass(child.domNode, cls);
5628                 },
5629
5630                 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
5631                         // Overrides _Container.addChild() to call _setupChild()
5632                         this.inherited(arguments);
5633                         if(this._started){
5634                                 this._setupChild(child);
5635                         }
5636                 },
5637
5638                 removeChild: function(/*dijit._Widget*/ child){
5639                         // Overrides _Container.removeChild() to remove class added by _setupChild()
5640                         var cls = this.baseClass + "-child"
5641                                         + (child.baseClass ?
5642                                                 " " + this.baseClass + "-" + child.baseClass : "");
5643                         dojo.removeClass(child.domNode, cls);
5644                         
5645                         this.inherited(arguments);
5646                 }
5647         }
5648 );
5649
5650 dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
5651         // summary:
5652         //              Given the margin-box size of a node, return its content box size.
5653         //              Functions like dojo.contentBox() but is more reliable since it doesn't have
5654         //              to wait for the browser to compute sizes.
5655         var cs = dojo.getComputedStyle(node);
5656         var me = dojo._getMarginExtents(node, cs);
5657         var pb = dojo._getPadBorderExtents(node, cs);
5658         return {
5659                 l: dojo._toPixelValue(node, cs.paddingLeft),
5660                 t: dojo._toPixelValue(node, cs.paddingTop),
5661                 w: mb.w - (me.w + pb.w),
5662                 h: mb.h - (me.h + pb.h)
5663         };
5664 };
5665
5666 (function(){
5667         var capitalize = function(word){
5668                 return word.substring(0,1).toUpperCase() + word.substring(1);
5669         };
5670
5671         var size = function(widget, dim){
5672                 // size the child
5673                 var newSize = widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
5674
5675                 // record child's size
5676                 if(newSize){
5677                         // if the child returned it's new size then use that
5678                         dojo.mixin(widget, newSize);
5679                 }else{
5680                         // otherwise, call marginBox(), but favor our own numbers when we have them.
5681                         // the browser lies sometimes
5682                         dojo.mixin(widget, dojo.marginBox(widget.domNode));
5683                         dojo.mixin(widget, dim);
5684                 }
5685         };
5686
5687         dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
5688                         /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
5689                 // summary
5690                 //              Layout a bunch of child dom nodes within a parent dom node
5691                 // container:
5692                 //              parent node
5693                 // dim:
5694                 //              {l, t, w, h} object specifying dimensions of container into which to place children
5695                 // children:
5696                 //              an array of Widgets or at least objects containing:
5697                 //                      * domNode: pointer to DOM node to position
5698                 //                      * region or layoutAlign: position to place DOM node
5699                 //                      * resize(): (optional) method to set size of node
5700                 //                      * id: (optional) Id of widgets, referenced from resize object, below.
5701                 // changedRegionId:
5702                 //              If specified, the slider for the region with the specified id has been dragged, and thus
5703                 //              the region's height or width should be adjusted according to changedRegionSize
5704                 // changedRegionSize:
5705                 //              See changedRegionId.
5706
5707                 // copy dim because we are going to modify it
5708                 dim = dojo.mixin({}, dim);
5709
5710                 dojo.addClass(container, "dijitLayoutContainer");
5711
5712                 // Move "client" elements to the end of the array for layout.  a11y dictates that the author
5713                 // needs to be able to put them in the document in tab-order, but this algorithm requires that
5714                 // client be last.    TODO: move these lines to LayoutContainer?   Unneeded other places I think.
5715                 children = dojo.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
5716                         .concat(dojo.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
5717
5718                 // set positions/sizes
5719                 dojo.forEach(children, function(child){
5720                         var elm = child.domNode,
5721                                 pos = (child.region || child.layoutAlign);
5722
5723                         // set elem to upper left corner of unused space; may move it later
5724                         var elmStyle = elm.style;
5725                         elmStyle.left = dim.l+"px";
5726                         elmStyle.top = dim.t+"px";
5727                         elmStyle.position = "absolute";
5728
5729                         dojo.addClass(elm, "dijitAlign" + capitalize(pos));
5730
5731                         // Size adjustments to make to this child widget
5732                         var sizeSetting = {};
5733
5734                         // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
5735                         // panes and width adjustment for left/right align panes.
5736                         if(changedRegionId && changedRegionId == child.id){
5737                                 sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
5738                         }
5739
5740                         // set size && adjust record of remaining space.
5741                         // note that setting the width of a <div> may affect its height.
5742                         if(pos == "top" || pos == "bottom"){
5743                                 sizeSetting.w = dim.w;
5744                                 size(child, sizeSetting);
5745                                 dim.h -= child.h;
5746                                 if(pos == "top"){
5747                                         dim.t += child.h;
5748                                 }else{
5749                                         elmStyle.top = dim.t + dim.h + "px";
5750                                 }
5751                         }else if(pos == "left" || pos == "right"){
5752                                 sizeSetting.h = dim.h;
5753                                 size(child, sizeSetting);
5754                                 dim.w -= child.w;
5755                                 if(pos == "left"){
5756                                         dim.l += child.w;
5757                                 }else{
5758                                         elmStyle.left = dim.l + dim.w + "px";
5759                                 }
5760                         }else if(pos == "client" || pos == "center"){
5761                                 size(child, dim);
5762                         }
5763                 });
5764         };
5765
5766 })();
5767
5768 }
5769
5770 if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5771 dojo._hasResource["dijit._CssStateMixin"] = true;
5772 dojo.provide("dijit._CssStateMixin");
5773
5774
5775 dojo.declare("dijit._CssStateMixin", [], {
5776         // summary:
5777         //              Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
5778         //              state changes, and also higher-level state changes such becoming disabled or selected.
5779         //
5780         // description:
5781         //              By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
5782         //              maintain CSS classes on the widget root node (this.domNode) depending on hover,
5783         //              active, focus, etc. state.   Ex: with a baseClass of dijitButton, it will apply the classes
5784         //              dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
5785         //
5786         //              It also sets CSS like dijitButtonDisabled based on widget semantic state.
5787         //
5788         //              By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
5789         //              within the widget).
5790
5791         // cssStateNodes: [protected] Object
5792         //              List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
5793         //.
5794         //              Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
5795         //              (like "dijitUpArrowButton"). Example:
5796         //      |               {
5797         //      |                       "upArrowButton": "dijitUpArrowButton",
5798         //      |                       "downArrowButton": "dijitDownArrowButton"
5799         //      |               }
5800         //              The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
5801         //              is hovered, etc.
5802         cssStateNodes: {},
5803
5804         // hovering: [readonly] Boolean
5805         //              True if cursor is over this widget
5806         hovering: false,
5807         
5808         // active: [readonly] Boolean
5809         //              True if mouse was pressed while over this widget, and hasn't been released yet
5810         active: false,
5811
5812         _applyAttributes: function(){
5813                 // This code would typically be in postCreate(), but putting in _applyAttributes() for
5814                 // performance: so the class changes happen before DOM is inserted into the document.
5815                 // Change back to postCreate() in 2.0.  See #11635.
5816
5817                 this.inherited(arguments);
5818
5819                 // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
5820                 dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){
5821                         this.connect(this.domNode, e, "_cssMouseEvent");
5822                 }, this);
5823                 
5824                 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
5825                 dojo.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
5826                         this.watch(attr, dojo.hitch(this, "_setStateClass"));
5827                 }, this);
5828
5829                 // Events on sub nodes within the widget
5830                 for(var ap in this.cssStateNodes){
5831                         this._trackMouseState(this[ap], this.cssStateNodes[ap]);
5832                 }
5833                 // Set state initially; there's probably no hover/active/focus state but widget might be
5834                 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
5835                 this._setStateClass();
5836         },
5837
5838         _cssMouseEvent: function(/*Event*/ event){
5839                 // summary:
5840                 //      Sets hovering and active properties depending on mouse state,
5841                 //      which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
5842
5843                 if(!this.disabled){
5844                         switch(event.type){
5845                                 case "mouseenter":
5846                                 case "mouseover":       // generated on non-IE browsers even though we connected to mouseenter
5847                                         this._set("hovering", true);
5848                                         this._set("active", this._mouseDown);
5849                                         break;
5850
5851                                 case "mouseleave":
5852                                 case "mouseout":        // generated on non-IE browsers even though we connected to mouseleave
5853                                         this._set("hovering", false);
5854                                         this._set("active", false);
5855                                         break;
5856
5857                                 case "mousedown" :
5858                                         this._set("active", true);
5859                                         this._mouseDown = true;
5860                                         // Set a global event to handle mouseup, so it fires properly
5861                                         // even if the cursor leaves this.domNode before the mouse up event.
5862                                         // Alternately could set active=false on mouseout.
5863                                         var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
5864                                                 this._mouseDown = false;
5865                                                 this._set("active", false);
5866                                                 this.disconnect(mouseUpConnector);
5867                                         });
5868                                         break;
5869                         }
5870                 }
5871         },
5872
5873         _setStateClass: function(){
5874                 // summary:
5875                 //              Update the visual state of the widget by setting the css classes on this.domNode
5876                 //              (or this.stateNode if defined) by combining this.baseClass with
5877                 //              various suffixes that represent the current widget state(s).
5878                 //
5879                 // description:
5880                 //              In the case where a widget has multiple
5881                 //              states, it sets the class based on all possible
5882                 //              combinations.  For example, an invalid form widget that is being hovered
5883                 //              will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
5884                 //
5885                 //              The widget may have one or more of the following states, determined
5886                 //              by this.state, this.checked, this.valid, and this.selected:
5887                 //                      - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
5888                 //                      - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
5889                 //                      - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
5890                 //                      - Selected - ex: currently selected tab will have this.selected==true
5891                 //
5892                 //              In addition, it may have one or more of the following states,
5893                 //              based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
5894                 //                      - Disabled      - if the widget is disabled
5895                 //                      - Active                - if the mouse (or space/enter key?) is being pressed down
5896                 //                      - Focused               - if the widget has focus
5897                 //                      - Hover         - if the mouse is over the widget
5898
5899                 // Compute new set of classes
5900                 var newStateClasses = this.baseClass.split(" ");
5901
5902                 function multiply(modifier){
5903                         newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
5904                 }
5905
5906                 if(!this.isLeftToRight()){
5907                         // For RTL mode we need to set an addition class like dijitTextBoxRtl.
5908                         multiply("Rtl");
5909                 }
5910
5911                 if(this.checked){
5912                         multiply("Checked");
5913                 }
5914                 if(this.state){
5915                         multiply(this.state);
5916                 }
5917                 if(this.selected){
5918                         multiply("Selected");
5919                 }
5920
5921                 if(this.disabled){
5922                         multiply("Disabled");
5923                 }else if(this.readOnly){
5924                         multiply("ReadOnly");
5925                 }else{
5926                         if(this.active){
5927                                 multiply("Active");
5928                         }else if(this.hovering){
5929                                 multiply("Hover");
5930                         }
5931                 }
5932
5933                 if(this._focused){
5934                         multiply("Focused");
5935                 }
5936
5937                 // Remove old state classes and add new ones.
5938                 // For performance concerns we only write into domNode.className once.
5939                 var tn = this.stateNode || this.domNode,
5940                         classHash = {}; // set of all classes (state and otherwise) for node
5941
5942                 dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
5943
5944                 if("_stateClasses" in this){
5945                         dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; });
5946                 }
5947
5948                 dojo.forEach(newStateClasses, function(c){ classHash[c] = true; });
5949
5950                 var newClasses = [];
5951                 for(var c in classHash){
5952                         newClasses.push(c);
5953                 }
5954                 tn.className = newClasses.join(" ");
5955
5956                 this._stateClasses = newStateClasses;
5957         },
5958
5959         _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
5960                 // summary:
5961                 //              Track mouse/focus events on specified node and set CSS class on that node to indicate
5962                 //              current state.   Usually not called directly, but via cssStateNodes attribute.
5963                 // description:
5964                 //              Given class=foo, will set the following CSS class on the node
5965                 //                      - fooActive: if the user is currently pressing down the mouse button while over the node
5966                 //                      - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
5967                 //                      - fooFocus: if the node is focused
5968                 //
5969                 //              Note that it won't set any classes if the widget is disabled.
5970                 // node: DomNode
5971                 //              Should be a sub-node of the widget, not the top node (this.domNode), since the top node
5972                 //              is handled specially and automatically just by mixing in this class.
5973                 // clazz: String
5974                 //              CSS class name (ex: dijitSliderUpArrow).
5975
5976                 // Current state of node (initially false)
5977                 // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg
5978                 var hovering=false, active=false, focused=false;
5979
5980                 var self = this,
5981                         cn = dojo.hitch(this, "connect", node);
5982
5983                 function setClass(){
5984                         var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
5985                         dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled);
5986                         dojo.toggleClass(node, clazz+"Active", active && !disabled);
5987                         dojo.toggleClass(node, clazz+"Focused", focused && !disabled);
5988                 }
5989
5990                 // Mouse
5991                 cn("onmouseenter", function(){
5992                         hovering = true;
5993                         setClass();
5994                 });
5995                 cn("onmouseleave", function(){
5996                         hovering = false;
5997                         active = false;
5998                         setClass();
5999                 });
6000                 cn("onmousedown", function(){
6001                         active = true;
6002                         setClass();
6003                 });
6004                 cn("onmouseup", function(){
6005                         active = false;
6006                         setClass();
6007                 });
6008
6009                 // Focus
6010                 cn("onfocus", function(){
6011                         focused = true;
6012                         setClass();
6013                 });
6014                 cn("onblur", function(){
6015                         focused = false;
6016                         setClass();
6017                 });
6018
6019                 // Just in case widget is enabled/disabled while it has focus/hover/active state.
6020                 // Maybe this is overkill.
6021                 this.watch("disabled", setClass);
6022                 this.watch("readOnly", setClass);
6023         }
6024 });
6025
6026 }
6027
6028 if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6029 dojo._hasResource["dijit.form._FormWidget"] = true;
6030 dojo.provide("dijit.form._FormWidget");
6031
6032
6033
6034
6035
6036
6037 dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
6038         {
6039         // summary:
6040         //              Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
6041         //              which can be children of a <form> node or a `dijit.form.Form` widget.
6042         //
6043         // description:
6044         //              Represents a single HTML element.
6045         //              All these widgets should have these attributes just like native HTML input elements.
6046         //              You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
6047         //
6048         //              They also share some common methods.
6049
6050         // name: [const] String
6051         //              Name used when submitting form; same as "name" attribute or plain HTML elements
6052         name: "",
6053
6054         // alt: String
6055         //              Corresponds to the native HTML <input> element's attribute.
6056         alt: "",
6057
6058         // value: String
6059         //              Corresponds to the native HTML <input> element's attribute.
6060         value: "",
6061
6062         // type: String
6063         //              Corresponds to the native HTML <input> element's attribute.
6064         type: "text",
6065
6066         // tabIndex: Integer
6067         //              Order fields are traversed when user hits the tab key
6068         tabIndex: "0",
6069
6070         // disabled: Boolean
6071         //              Should this widget respond to user input?
6072         //              In markup, this is specified as "disabled='disabled'", or just "disabled".
6073         disabled: false,
6074
6075         // intermediateChanges: Boolean
6076         //              Fires onChange for each value change or only on demand
6077         intermediateChanges: false,
6078
6079         // scrollOnFocus: Boolean
6080         //              On focus, should this widget scroll into view?
6081         scrollOnFocus: true,
6082
6083         // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
6084         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
6085                 value: "focusNode",
6086                 id: "focusNode",
6087                 tabIndex: "focusNode",
6088                 alt: "focusNode",
6089                 title: "focusNode"
6090         }),
6091
6092         postMixInProperties: function(){
6093                 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
6094                 // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
6095                 // Regarding escaping, see heading "Attribute values" in
6096                 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
6097                 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
6098                 this.inherited(arguments);
6099         },
6100
6101         postCreate: function(){
6102                 this.inherited(arguments);
6103                 this.connect(this.domNode, "onmousedown", "_onMouseDown");
6104         },
6105
6106         _setDisabledAttr: function(/*Boolean*/ value){
6107                 this._set("disabled", value);
6108                 dojo.attr(this.focusNode, 'disabled', value);
6109                 if(this.valueNode){
6110                         dojo.attr(this.valueNode, 'disabled', value);
6111                 }
6112                 dijit.setWaiState(this.focusNode, "disabled", value);
6113
6114                 if(value){
6115                         // reset these, because after the domNode is disabled, we can no longer receive
6116                         // mouse related events, see #4200
6117                         this._set("hovering", false);
6118                         this._set("active", false);
6119
6120                         // clear tab stop(s) on this widget's focusable node(s)  (ComboBox has two focusable nodes)
6121                         var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode";
6122                         dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
6123                                 var node = this[attachPointName];
6124                                 // complex code because tabIndex=-1 on a <div> doesn't work on FF
6125                                 if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){     // see #11064 about webkit bug
6126                                         node.setAttribute('tabIndex', "-1");
6127                                 }else{
6128                                         node.removeAttribute('tabIndex');
6129                                 }
6130                         }, this);
6131                 }else{
6132                         if(this.tabIndex != ""){
6133                                 this.focusNode.setAttribute('tabIndex', this.tabIndex);
6134                         }
6135                 }
6136         },
6137
6138         setDisabled: function(/*Boolean*/ disabled){
6139                 // summary:
6140                 //              Deprecated.  Use set('disabled', ...) instead.
6141                 dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
6142                 this.set('disabled', disabled);
6143         },
6144
6145         _onFocus: function(e){
6146                 if(this.scrollOnFocus){
6147                         dojo.window.scrollIntoView(this.domNode);
6148                 }
6149                 this.inherited(arguments);
6150         },
6151
6152         isFocusable: function(){
6153                 // summary:
6154                 //              Tells if this widget is focusable or not.  Used internally by dijit.
6155                 // tags:
6156                 //              protected
6157                 return !this.disabled && this.focusNode && (dojo.style(this.domNode, "display") != "none");
6158         },
6159
6160         focus: function(){
6161                 // summary:
6162                 //              Put focus on this widget
6163                 if(!this.disabled){
6164                         dijit.focus(this.focusNode);
6165                 }
6166         },
6167
6168         compare: function(/*anything*/ val1, /*anything*/ val2){
6169                 // summary:
6170                 //              Compare 2 values (as returned by get('value') for this widget).
6171                 // tags:
6172                 //              protected
6173                 if(typeof val1 == "number" && typeof val2 == "number"){
6174                         return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
6175                 }else if(val1 > val2){
6176                         return 1;
6177                 }else if(val1 < val2){
6178                         return -1;
6179                 }else{
6180                         return 0;
6181                 }
6182         },
6183
6184         onChange: function(newValue){
6185                 // summary:
6186                 //              Callback when this widget's value is changed.
6187                 // tags:
6188                 //              callback
6189         },
6190
6191         // _onChangeActive: [private] Boolean
6192         //              Indicates that changes to the value should call onChange() callback.
6193         //              This is false during widget initialization, to avoid calling onChange()
6194         //              when the initial value is set.
6195         _onChangeActive: false,
6196
6197         _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
6198                 // summary:
6199                 //              Called when the value of the widget is set.  Calls onChange() if appropriate
6200                 // newValue:
6201                 //              the new value
6202                 // priorityChange:
6203                 //              For a slider, for example, dragging the slider is priorityChange==false,
6204                 //              but on mouse up, it's priorityChange==true.  If intermediateChanges==false,
6205                 //              onChange is only called form priorityChange=true events.
6206                 // tags:
6207                 //              private
6208                 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
6209                         // this block executes not for a change, but during initialization,
6210                         // and is used to store away the original value (or for ToggleButton, the original checked state)
6211                         this._resetValue = this._lastValueReported = newValue;
6212                 }
6213                 this._pendingOnChange = this._pendingOnChange
6214                         || (typeof newValue != typeof this._lastValueReported)
6215                         || (this.compare(newValue, this._lastValueReported) != 0);
6216                 if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
6217                         this._lastValueReported = newValue;
6218                         this._pendingOnChange = false;
6219                         if(this._onChangeActive){
6220                                 if(this._onChangeHandle){
6221                                         clearTimeout(this._onChangeHandle);
6222                                 }
6223                                 // setTimout allows hidden value processing to run and
6224                                 // also the onChange handler can safely adjust focus, etc
6225                                 this._onChangeHandle = setTimeout(dojo.hitch(this,
6226                                         function(){
6227                                                 this._onChangeHandle = null;
6228                                                 this.onChange(newValue);
6229                                         }), 0); // try to collapse multiple onChange's fired faster than can be processed
6230                         }
6231                 }
6232         },
6233
6234         create: function(){
6235                 // Overrides _Widget.create()
6236                 this.inherited(arguments);
6237                 this._onChangeActive = true;
6238         },
6239
6240         destroy: function(){
6241                 if(this._onChangeHandle){ // destroy called before last onChange has fired
6242                         clearTimeout(this._onChangeHandle);
6243                         this.onChange(this._lastValueReported);
6244                 }
6245                 this.inherited(arguments);
6246         },
6247
6248         setValue: function(/*String*/ value){
6249                 // summary:
6250                 //              Deprecated.  Use set('value', ...) instead.
6251                 dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated.  Use set('value',"+value+") instead.", "", "2.0");
6252                 this.set('value', value);
6253         },
6254
6255         getValue: function(){
6256                 // summary:
6257                 //              Deprecated.  Use get('value') instead.
6258                 dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
6259                 return this.get('value');
6260         },
6261         
6262         _onMouseDown: function(e){
6263                 // If user clicks on the button, even if the mouse is released outside of it,
6264                 // this button should get focus (to mimics native browser buttons).
6265                 // This is also needed on chrome because otherwise buttons won't get focus at all,
6266                 // which leads to bizarre focus restore on Dialog close etc.
6267                 if(!e.ctrlKey && dojo.mouseButtons.isLeft(e) && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac
6268                         // Set a global event to handle mouseup, so it fires properly
6269                         // even if the cursor leaves this.domNode before the mouse up event.
6270                         var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
6271                                 if (this.isFocusable()) {
6272                                         this.focus();
6273                                 }
6274                                 this.disconnect(mouseUpConnector);
6275                         });
6276                 }
6277         }
6278 });
6279
6280 dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
6281 {
6282         // summary:
6283         //              Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
6284         // description:
6285         //              Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
6286         //              to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
6287         //              works as expected.
6288
6289         // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
6290         // directly in the template as read by the parser in order to function. IE is known to specifically
6291         // require the 'name' attribute at element creation time.  See #8484, #8660.
6292         // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode,
6293         // so maybe {value: ""} is so the value *doesn't* get copied to focusNode?
6294         // Seems like we really want value removed from attributeMap altogether
6295         // (although there's no easy way to do that now)
6296
6297         // readOnly: Boolean
6298         //              Should this widget respond to user input?
6299         //              In markup, this is specified as "readOnly".
6300         //              Similar to disabled except readOnly form values are submitted.
6301         readOnly: false,
6302
6303         attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
6304                 value: "",
6305                 readOnly: "focusNode"
6306         }),
6307
6308         _setReadOnlyAttr: function(/*Boolean*/ value){
6309                 dojo.attr(this.focusNode, 'readOnly', value);
6310                 dijit.setWaiState(this.focusNode, "readonly", value);
6311                 this._set("readOnly", value);
6312         },
6313
6314         postCreate: function(){
6315                 this.inherited(arguments);
6316
6317                 if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ // IE won't stop the event with keypress
6318                         this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
6319                 }
6320                 // Update our reset value if it hasn't yet been set (because this.set()
6321                 // is only called when there *is* a value)
6322                 if(this._resetValue === undefined){
6323                         this._lastValueReported = this._resetValue = this.value;
6324                 }
6325         },
6326
6327         _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
6328                 // summary:
6329                 //              Hook so set('value', value) works.
6330                 // description:
6331                 //              Sets the value of the widget.
6332                 //              If the value has changed, then fire onChange event, unless priorityChange
6333                 //              is specified as null (or false?)
6334                 this._handleOnChange(newValue, priorityChange);
6335         },
6336
6337         _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
6338                 // summary:
6339                 //              Called when the value of the widget has changed.  Saves the new value in this.value,
6340                 //              and calls onChange() if appropriate.   See _FormWidget._handleOnChange() for details.
6341                 this._set("value", newValue);
6342                 this.inherited(arguments);
6343         },
6344
6345         undo: function(){
6346                 // summary:
6347                 //              Restore the value to the last value passed to onChange
6348                 this._setValueAttr(this._lastValueReported, false);
6349         },
6350
6351         reset: function(){
6352                 // summary:
6353                 //              Reset the widget's value to what it was at initialization time
6354                 this._hasBeenBlurred = false;
6355                 this._setValueAttr(this._resetValue, true);
6356         },
6357
6358         _onKeyDown: function(e){
6359                 if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
6360                         var te;
6361                         if(dojo.isIE){
6362                                 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
6363                                 te = document.createEventObject();
6364                                 te.keyCode = dojo.keys.ESCAPE;
6365                                 te.shiftKey = e.shiftKey;
6366                                 e.srcElement.fireEvent('onkeypress', te);
6367                         }
6368                 }
6369         },
6370
6371         _layoutHackIE7: function(){
6372                 // summary:
6373                 //              Work around table sizing bugs on IE7 by forcing redraw
6374
6375                 if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
6376                         var domNode = this.domNode;
6377                         var parent = domNode.parentNode;
6378                         var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
6379                         var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
6380                         var _this = this;
6381                         while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
6382                                 (function ping(){
6383                                         var disconnectHandle = _this.connect(parent, "onscroll",
6384                                                 function(e){
6385                                                         _this.disconnect(disconnectHandle); // only call once
6386                                                         pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
6387                                                         setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
6388                                                 }
6389                                         );
6390                                 })();
6391                                 parent = parent.parentNode;
6392                         }
6393                 }
6394         }
6395 });
6396
6397 }
6398
6399 if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6400 dojo._hasResource["dijit.dijit"] = true;
6401 dojo.provide("dijit.dijit");
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411 /*=====
6412 dijit.dijit = {
6413         // summary:
6414         //              A roll-up for common dijit methods
6415         // description:
6416         //      A rollup file for the build system including the core and common
6417         //      dijit files.
6418         //
6419         // example:
6420         // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script>
6421         //
6422 };
6423 =====*/
6424
6425 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
6426
6427 // And some other stuff that we tend to pull in all the time anyway
6428
6429 }
6430
6431 if(!dojo._hasResource["dojo.fx.Toggler"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6432 dojo._hasResource["dojo.fx.Toggler"] = true;
6433 dojo.provide("dojo.fx.Toggler");
6434
6435
6436 dojo.declare("dojo.fx.Toggler", null, {
6437         // summary:
6438         //              A simple `dojo.Animation` toggler API.
6439         //
6440         // description:
6441         //              class constructor for an animation toggler. It accepts a packed
6442         //              set of arguments about what type of animation to use in each
6443         //              direction, duration, etc. All available members are mixed into
6444         //              these animations from the constructor (for example, `node`,
6445         //              `showDuration`, `hideDuration`).
6446         //
6447         // example:
6448         //      |       var t = new dojo.fx.Toggler({
6449         //      |               node: "nodeId",
6450         //      |               showDuration: 500,
6451         //      |               // hideDuration will default to "200"
6452         //      |               showFunc: dojo.fx.wipeIn,
6453         //      |               // hideFunc will default to "fadeOut"
6454         //      |       });
6455         //      |       t.show(100); // delay showing for 100ms
6456         //      |       // ...time passes...
6457         //      |       t.hide();
6458
6459         // node: DomNode
6460         //              the node to target for the showing and hiding animations
6461         node: null,
6462
6463         // showFunc: Function
6464         //              The function that returns the `dojo.Animation` to show the node
6465         showFunc: dojo.fadeIn,
6466
6467         // hideFunc: Function
6468         //              The function that returns the `dojo.Animation` to hide the node
6469         hideFunc: dojo.fadeOut,
6470
6471         // showDuration:
6472         //              Time in milliseconds to run the show Animation
6473         showDuration: 200,
6474
6475         // hideDuration:
6476         //              Time in milliseconds to run the hide Animation
6477         hideDuration: 200,
6478
6479         // FIXME: need a policy for where the toggler should "be" the next
6480         // time show/hide are called if we're stopped somewhere in the
6481         // middle.
6482         // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
6483         // each animation individually.
6484         // FIXME: also would be nice to have events from the animations exposed/bridged
6485
6486         /*=====
6487         _showArgs: null,
6488         _showAnim: null,
6489
6490         _hideArgs: null,
6491         _hideAnim: null,
6492
6493         _isShowing: false,
6494         _isHiding: false,
6495         =====*/
6496
6497         constructor: function(args){
6498                 var _t = this;
6499
6500                 dojo.mixin(_t, args);
6501                 _t.node = args.node;
6502                 _t._showArgs = dojo.mixin({}, args);
6503                 _t._showArgs.node = _t.node;
6504                 _t._showArgs.duration = _t.showDuration;
6505                 _t.showAnim = _t.showFunc(_t._showArgs);
6506
6507                 _t._hideArgs = dojo.mixin({}, args);
6508                 _t._hideArgs.node = _t.node;
6509                 _t._hideArgs.duration = _t.hideDuration;
6510                 _t.hideAnim = _t.hideFunc(_t._hideArgs);
6511
6512                 dojo.connect(_t.showAnim, "beforeBegin", dojo.hitch(_t.hideAnim, "stop", true));
6513                 dojo.connect(_t.hideAnim, "beforeBegin", dojo.hitch(_t.showAnim, "stop", true));
6514         },
6515
6516         show: function(delay){
6517                 // summary: Toggle the node to showing
6518                 // delay: Integer?
6519                 //              Ammount of time to stall playing the show animation
6520                 return this.showAnim.play(delay || 0);
6521         },
6522
6523         hide: function(delay){
6524                 // summary: Toggle the node to hidden
6525                 // delay: Integer?
6526                 //              Ammount of time to stall playing the hide animation
6527                 return this.hideAnim.play(delay || 0);
6528         }
6529 });
6530
6531 }
6532
6533 if(!dojo._hasResource["dojo.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6534 dojo._hasResource["dojo.fx"] = true;
6535 dojo.provide("dojo.fx");
6536
6537
6538
6539 /*=====
6540 dojo.fx = {
6541         // summary: Effects library on top of Base animations
6542 };
6543 =====*/
6544 (function(){
6545         
6546         var d = dojo,
6547                 _baseObj = {
6548                         _fire: function(evt, args){
6549                                 if(this[evt]){
6550                                         this[evt].apply(this, args||[]);
6551                                 }
6552                                 return this;
6553                         }
6554                 };
6555
6556         var _chain = function(animations){
6557                 this._index = -1;
6558                 this._animations = animations||[];
6559                 this._current = this._onAnimateCtx = this._onEndCtx = null;
6560
6561                 this.duration = 0;
6562                 d.forEach(this._animations, function(a){
6563                         this.duration += a.duration;
6564                         if(a.delay){ this.duration += a.delay; }
6565                 }, this);
6566         };
6567         d.extend(_chain, {
6568                 _onAnimate: function(){
6569                         this._fire("onAnimate", arguments);
6570                 },
6571                 _onEnd: function(){
6572                         d.disconnect(this._onAnimateCtx);
6573                         d.disconnect(this._onEndCtx);
6574                         this._onAnimateCtx = this._onEndCtx = null;
6575                         if(this._index + 1 == this._animations.length){
6576                                 this._fire("onEnd");
6577                         }else{
6578                                 // switch animations
6579                                 this._current = this._animations[++this._index];
6580                                 this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
6581                                 this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
6582                                 this._current.play(0, true);
6583                         }
6584                 },
6585                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
6586                         if(!this._current){ this._current = this._animations[this._index = 0]; }
6587                         if(!gotoStart && this._current.status() == "playing"){ return this; }
6588                         var beforeBegin = d.connect(this._current, "beforeBegin", this, function(){
6589                                         this._fire("beforeBegin");
6590                                 }),
6591                                 onBegin = d.connect(this._current, "onBegin", this, function(arg){
6592                                         this._fire("onBegin", arguments);
6593                                 }),
6594                                 onPlay = d.connect(this._current, "onPlay", this, function(arg){
6595                                         this._fire("onPlay", arguments);
6596                                         d.disconnect(beforeBegin);
6597                                         d.disconnect(onBegin);
6598                                         d.disconnect(onPlay);
6599                                 });
6600                         if(this._onAnimateCtx){
6601                                 d.disconnect(this._onAnimateCtx);
6602                         }
6603                         this._onAnimateCtx = d.connect(this._current, "onAnimate", this, "_onAnimate");
6604                         if(this._onEndCtx){
6605                                 d.disconnect(this._onEndCtx);
6606                         }
6607                         this._onEndCtx = d.connect(this._current, "onEnd", this, "_onEnd");
6608                         this._current.play.apply(this._current, arguments);
6609                         return this;
6610                 },
6611                 pause: function(){
6612                         if(this._current){
6613                                 var e = d.connect(this._current, "onPause", this, function(arg){
6614                                                 this._fire("onPause", arguments);
6615                                                 d.disconnect(e);
6616                                         });
6617                                 this._current.pause();
6618                         }
6619                         return this;
6620                 },
6621                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
6622                         this.pause();
6623                         var offset = this.duration * percent;
6624                         this._current = null;
6625                         d.some(this._animations, function(a){
6626                                 if(a.duration <= offset){
6627                                         this._current = a;
6628                                         return true;
6629                                 }
6630                                 offset -= a.duration;
6631                                 return false;
6632                         });
6633                         if(this._current){
6634                                 this._current.gotoPercent(offset / this._current.duration, andPlay);
6635                         }
6636                         return this;
6637                 },
6638                 stop: function(/*boolean?*/ gotoEnd){
6639                         if(this._current){
6640                                 if(gotoEnd){
6641                                         for(; this._index + 1 < this._animations.length; ++this._index){
6642                                                 this._animations[this._index].stop(true);
6643                                         }
6644                                         this._current = this._animations[this._index];
6645                                 }
6646                                 var e = d.connect(this._current, "onStop", this, function(arg){
6647                                                 this._fire("onStop", arguments);
6648                                                 d.disconnect(e);
6649                                         });
6650                                 this._current.stop();
6651                         }
6652                         return this;
6653                 },
6654                 status: function(){
6655                         return this._current ? this._current.status() : "stopped";
6656                 },
6657                 destroy: function(){
6658                         if(this._onAnimateCtx){ d.disconnect(this._onAnimateCtx); }
6659                         if(this._onEndCtx){ d.disconnect(this._onEndCtx); }
6660                 }
6661         });
6662         d.extend(_chain, _baseObj);
6663
6664         dojo.fx.chain = function(/*dojo.Animation[]*/ animations){
6665                 // summary:
6666                 //              Chain a list of `dojo.Animation`s to run in sequence
6667                 //
6668                 // description:
6669                 //              Return a `dojo.Animation` which will play all passed
6670                 //              `dojo.Animation` instances in sequence, firing its own
6671                 //              synthesized events simulating a single animation. (eg:
6672                 //              onEnd of this animation means the end of the chain,
6673                 //              not the individual animations within)
6674                 //
6675                 // example:
6676                 //      Once `node` is faded out, fade in `otherNode`
6677                 //      |       dojo.fx.chain([
6678                 //      |               dojo.fadeIn({ node:node }),
6679                 //      |               dojo.fadeOut({ node:otherNode })
6680                 //      |       ]).play();
6681                 //
6682                 return new _chain(animations) // dojo.Animation
6683         };
6684
6685         var _combine = function(animations){
6686                 this._animations = animations||[];
6687                 this._connects = [];
6688                 this._finished = 0;
6689
6690                 this.duration = 0;
6691                 d.forEach(animations, function(a){
6692                         var duration = a.duration;
6693                         if(a.delay){ duration += a.delay; }
6694                         if(this.duration < duration){ this.duration = duration; }
6695                         this._connects.push(d.connect(a, "onEnd", this, "_onEnd"));
6696                 }, this);
6697                 
6698                 this._pseudoAnimation = new d.Animation({curve: [0, 1], duration: this.duration});
6699                 var self = this;
6700                 d.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
6701                         function(evt){
6702                                 self._connects.push(d.connect(self._pseudoAnimation, evt,
6703                                         function(){ self._fire(evt, arguments); }
6704                                 ));
6705                         }
6706                 );
6707         };
6708         d.extend(_combine, {
6709                 _doAction: function(action, args){
6710                         d.forEach(this._animations, function(a){
6711                                 a[action].apply(a, args);
6712                         });
6713                         return this;
6714                 },
6715                 _onEnd: function(){
6716                         if(++this._finished > this._animations.length){
6717                                 this._fire("onEnd");
6718                         }
6719                 },
6720                 _call: function(action, args){
6721                         var t = this._pseudoAnimation;
6722                         t[action].apply(t, args);
6723                 },
6724                 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
6725                         this._finished = 0;
6726                         this._doAction("play", arguments);
6727                         this._call("play", arguments);
6728                         return this;
6729                 },
6730                 pause: function(){
6731                         this._doAction("pause", arguments);
6732                         this._call("pause", arguments);
6733                         return this;
6734                 },
6735                 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
6736                         var ms = this.duration * percent;
6737                         d.forEach(this._animations, function(a){
6738                                 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
6739                         });
6740                         this._call("gotoPercent", arguments);
6741                         return this;
6742                 },
6743                 stop: function(/*boolean?*/ gotoEnd){
6744                         this._doAction("stop", arguments);
6745                         this._call("stop", arguments);
6746                         return this;
6747                 },
6748                 status: function(){
6749                         return this._pseudoAnimation.status();
6750                 },
6751                 destroy: function(){
6752                         d.forEach(this._connects, dojo.disconnect);
6753                 }
6754         });
6755         d.extend(_combine, _baseObj);
6756
6757         dojo.fx.combine = function(/*dojo.Animation[]*/ animations){
6758                 // summary:
6759                 //              Combine a list of `dojo.Animation`s to run in parallel
6760                 //
6761                 // description:
6762                 //              Combine an array of `dojo.Animation`s to run in parallel,
6763                 //              providing a new `dojo.Animation` instance encompasing each
6764                 //              animation, firing standard animation events.
6765                 //
6766                 // example:
6767                 //      Fade out `node` while fading in `otherNode` simultaneously
6768                 //      |       dojo.fx.combine([
6769                 //      |               dojo.fadeIn({ node:node }),
6770                 //      |               dojo.fadeOut({ node:otherNode })
6771                 //      |       ]).play();
6772                 //
6773                 // example:
6774                 //      When the longest animation ends, execute a function:
6775                 //      |       var anim = dojo.fx.combine([
6776                 //      |               dojo.fadeIn({ node: n, duration:700 }),
6777                 //      |               dojo.fadeOut({ node: otherNode, duration: 300 })
6778                 //      |       ]);
6779                 //      |       dojo.connect(anim, "onEnd", function(){
6780                 //      |               // overall animation is done.
6781                 //      |       });
6782                 //      |       anim.play(); // play the animation
6783                 //
6784                 return new _combine(animations); // dojo.Animation
6785         };
6786
6787         dojo.fx.wipeIn = function(/*Object*/ args){
6788                 // summary:
6789                 //              Expand a node to it's natural height.
6790                 //
6791                 // description:
6792                 //              Returns an animation that will expand the
6793                 //              node defined in 'args' object from it's current height to
6794                 //              it's natural height (with no scrollbar).
6795                 //              Node must have no margin/border/padding.
6796                 //
6797                 // args: Object
6798                 //              A hash-map of standard `dojo.Animation` constructor properties
6799                 //              (such as easing: node: duration: and so on)
6800                 //
6801                 // example:
6802                 //      |       dojo.fx.wipeIn({
6803                 //      |               node:"someId"
6804                 //      |       }).play()
6805                 var node = args.node = d.byId(args.node), s = node.style, o;
6806
6807                 var anim = d.animateProperty(d.mixin({
6808                         properties: {
6809                                 height: {
6810                                         // wrapped in functions so we wait till the last second to query (in case value has changed)
6811                                         start: function(){
6812                                                 // start at current [computed] height, but use 1px rather than 0
6813                                                 // because 0 causes IE to display the whole panel
6814                                                 o = s.overflow;
6815                                                 s.overflow = "hidden";
6816                                                 if(s.visibility == "hidden" || s.display == "none"){
6817                                                         s.height = "1px";
6818                                                         s.display = "";
6819                                                         s.visibility = "";
6820                                                         return 1;
6821                                                 }else{
6822                                                         var height = d.style(node, "height");
6823                                                         return Math.max(height, 1);
6824                                                 }
6825                                         },
6826                                         end: function(){
6827                                                 return node.scrollHeight;
6828                                         }
6829                                 }
6830                         }
6831                 }, args));
6832
6833                 d.connect(anim, "onEnd", function(){
6834                         s.height = "auto";
6835                         s.overflow = o;
6836                 });
6837
6838                 return anim; // dojo.Animation
6839         };
6840
6841         dojo.fx.wipeOut = function(/*Object*/ args){
6842                 // summary:
6843                 //              Shrink a node to nothing and hide it.
6844                 //
6845                 // description:
6846                 //              Returns an animation that will shrink node defined in "args"
6847                 //              from it's current height to 1px, and then hide it.
6848                 //
6849                 // args: Object
6850                 //              A hash-map of standard `dojo.Animation` constructor properties
6851                 //              (such as easing: node: duration: and so on)
6852                 //
6853                 // example:
6854                 //      |       dojo.fx.wipeOut({ node:"someId" }).play()
6855                 
6856                 var node = args.node = d.byId(args.node), s = node.style, o;
6857                 
6858                 var anim = d.animateProperty(d.mixin({
6859                         properties: {
6860                                 height: {
6861                                         end: 1 // 0 causes IE to display the whole panel
6862                                 }
6863                         }
6864                 }, args));
6865
6866                 d.connect(anim, "beforeBegin", function(){
6867                         o = s.overflow;
6868                         s.overflow = "hidden";
6869                         s.display = "";
6870                 });
6871                 d.connect(anim, "onEnd", function(){
6872                         s.overflow = o;
6873                         s.height = "auto";
6874                         s.display = "none";
6875                 });
6876
6877                 return anim; // dojo.Animation
6878         };
6879
6880         dojo.fx.slideTo = function(/*Object*/ args){
6881                 // summary:
6882                 //              Slide a node to a new top/left position
6883                 //
6884                 // description:
6885                 //              Returns an animation that will slide "node"
6886                 //              defined in args Object from its current position to
6887                 //              the position defined by (args.left, args.top).
6888                 //
6889                 // args: Object
6890                 //              A hash-map of standard `dojo.Animation` constructor properties
6891                 //              (such as easing: node: duration: and so on). Special args members
6892                 //              are `top` and `left`, which indicate the new position to slide to.
6893                 //
6894                 // example:
6895                 //      |       dojo.fx.slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
6896
6897                 var node = args.node = d.byId(args.node),
6898                         top = null, left = null;
6899
6900                 var init = (function(n){
6901                         return function(){
6902                                 var cs = d.getComputedStyle(n);
6903                                 var pos = cs.position;
6904                                 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
6905                                 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
6906                                 if(pos != 'absolute' && pos != 'relative'){
6907                                         var ret = d.position(n, true);
6908                                         top = ret.y;
6909                                         left = ret.x;
6910                                         n.style.position="absolute";
6911                                         n.style.top=top+"px";
6912                                         n.style.left=left+"px";
6913                                 }
6914                         };
6915                 })(node);
6916                 init();
6917
6918                 var anim = d.animateProperty(d.mixin({
6919                         properties: {
6920                                 top: args.top || 0,
6921                                 left: args.left || 0
6922                         }
6923                 }, args));
6924                 d.connect(anim, "beforeBegin", anim, init);
6925
6926                 return anim; // dojo.Animation
6927         };
6928
6929 })();
6930
6931 }
6932
6933 if(!dojo._hasResource["dojo.NodeList-fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6934 dojo._hasResource["dojo.NodeList-fx"] = true;
6935 dojo.provide("dojo.NodeList-fx");
6936
6937
6938
6939 /*=====
6940 dojo["NodeList-fx"] = {
6941         // summary: Adds dojo.fx animation support to dojo.query()
6942 };
6943 =====*/
6944
6945 dojo.extend(dojo.NodeList, {
6946         _anim: function(obj, method, args){
6947                 args = args||{};
6948                 var a = dojo.fx.combine(
6949                         this.map(function(item){
6950                                 var tmpArgs = { node: item };
6951                                 dojo.mixin(tmpArgs, args);
6952                                 return obj[method](tmpArgs);
6953                         })
6954                 );
6955                 return args.auto ? a.play() && this : a; // dojo.Animation|dojo.NodeList
6956         },
6957
6958         wipeIn: function(args){
6959                 //      summary:
6960                 //              wipe in all elements of this NodeList via `dojo.fx.wipeIn`
6961                 //
6962                 //      args: Object?
6963                 //              Additional dojo.Animation arguments to mix into this set with the addition of
6964                 //              an `auto` parameter.
6965                 //
6966                 //      returns: dojo.Animation|dojo.NodeList
6967                 //              A special args member `auto` can be passed to automatically play the animation.
6968                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6969                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6970                 //
6971                 //      example:
6972                 //              Fade in all tables with class "blah":
6973                 //              |       dojo.query("table.blah").wipeIn().play();
6974                 //
6975                 //      example:
6976                 //              Utilizing `auto` to get the NodeList back:
6977                 //              |       dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
6978                 //
6979                 return this._anim(dojo.fx, "wipeIn", args); // dojo.Animation|dojo.NodeList
6980         },
6981
6982         wipeOut: function(args){
6983                 //      summary:
6984                 //              wipe out all elements of this NodeList via `dojo.fx.wipeOut`
6985                 //
6986                 //      args: Object?
6987                 //              Additional dojo.Animation arguments to mix into this set with the addition of
6988                 //              an `auto` parameter.
6989                 //
6990                 //      returns: dojo.Animation|dojo.NodeList
6991                 //              A special args member `auto` can be passed to automatically play the animation.
6992                 //              If args.auto is present, the original dojo.NodeList will be returned for further
6993                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
6994                 //
6995                 //      example:
6996                 //              Wipe out all tables with class "blah":
6997                 //              |       dojo.query("table.blah").wipeOut().play();
6998                 return this._anim(dojo.fx, "wipeOut", args); // dojo.Animation|dojo.NodeList
6999         },
7000
7001         slideTo: function(args){
7002                 //      summary:
7003                 //              slide all elements of the node list to the specified place via `dojo.fx.slideTo`
7004                 //
7005                 //      args: Object?
7006                 //              Additional dojo.Animation arguments to mix into this set with the addition of
7007                 //              an `auto` parameter.
7008                 //
7009                 //      returns: dojo.Animation|dojo.NodeList
7010                 //              A special args member `auto` can be passed to automatically play the animation.
7011                 //              If args.auto is present, the original dojo.NodeList will be returned for further
7012                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
7013                 //
7014                 //      example:
7015                 //              |       Move all tables with class "blah" to 300/300:
7016                 //              |       dojo.query("table.blah").slideTo({
7017                 //              |               left: 40,
7018                 //              |               top: 50
7019                 //              |       }).play();
7020                 return this._anim(dojo.fx, "slideTo", args); // dojo.Animation|dojo.NodeList
7021         },
7022
7023
7024         fadeIn: function(args){
7025                 //      summary:
7026                 //              fade in all elements of this NodeList via `dojo.fadeIn`
7027                 //
7028                 //      args: Object?
7029                 //              Additional dojo.Animation arguments to mix into this set with the addition of
7030                 //              an `auto` parameter.
7031                 //
7032                 //      returns: dojo.Animation|dojo.NodeList
7033                 //              A special args member `auto` can be passed to automatically play the animation.
7034                 //              If args.auto is present, the original dojo.NodeList will be returned for further
7035                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
7036                 //
7037                 //      example:
7038                 //              Fade in all tables with class "blah":
7039                 //              |       dojo.query("table.blah").fadeIn().play();
7040                 return this._anim(dojo, "fadeIn", args); // dojo.Animation|dojo.NodeList
7041         },
7042
7043         fadeOut: function(args){
7044                 //      summary:
7045                 //              fade out all elements of this NodeList via `dojo.fadeOut`
7046                 //
7047                 //      args: Object?
7048                 //              Additional dojo.Animation arguments to mix into this set with the addition of
7049                 //              an `auto` parameter.
7050                 //
7051                 //      returns: dojo.Animation|dojo.NodeList
7052                 //              A special args member `auto` can be passed to automatically play the animation.
7053                 //              If args.auto is present, the original dojo.NodeList will be returned for further
7054                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
7055                 //
7056                 //      example:
7057                 //              Fade out all elements with class "zork":
7058                 //              |       dojo.query(".zork").fadeOut().play();
7059                 //      example:
7060                 //              Fade them on a delay and do something at the end:
7061                 //              |       var fo = dojo.query(".zork").fadeOut();
7062                 //              |       dojo.connect(fo, "onEnd", function(){ /*...*/ });
7063                 //              |       fo.play();
7064                 //      example:
7065                 //              Using `auto`:
7066                 //              |       dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
7067                 //
7068                 return this._anim(dojo, "fadeOut", args); // dojo.Animation|dojo.NodeList
7069         },
7070
7071         animateProperty: function(args){
7072                 //      summary:
7073                 //              Animate all elements of this NodeList across the properties specified.
7074                 //              syntax identical to `dojo.animateProperty`
7075                 //
7076                 // returns: dojo.Animation|dojo.NodeList
7077                 //              A special args member `auto` can be passed to automatically play the animation.
7078                 //              If args.auto is present, the original dojo.NodeList will be returned for further
7079                 //              chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
7080                 //
7081                 //      example:
7082                 //      |       dojo.query(".zork").animateProperty({
7083                 //      |               duration: 500,
7084                 //      |               properties: {
7085                 //      |                       color:          { start: "black", end: "white" },
7086                 //      |                       left:           { end: 300 }
7087                 //      |               }
7088                 //      |       }).play();
7089                 //
7090                 //      example:
7091                 //      |       dojo.query(".grue").animateProperty({
7092                 //      |               auto:true,
7093                 //      |               properties: {
7094                 //      |                       height:240
7095                 //      |               }
7096                 //      |       }).onclick(handler);
7097                 return this._anim(dojo, "animateProperty", args); // dojo.Animation|dojo.NodeList
7098         },
7099
7100         anim: function( /*Object*/                      properties,
7101                                         /*Integer?*/            duration,
7102                                         /*Function?*/           easing,
7103                                         /*Function?*/           onEnd,
7104                                         /*Integer?*/            delay){
7105                 //      summary:
7106                 //              Animate one or more CSS properties for all nodes in this list.
7107                 //              The returned animation object will already be playing when it
7108                 //              is returned. See the docs for `dojo.anim` for full details.
7109                 //      properties: Object
7110                 //              the properties to animate. does NOT support the `auto` parameter like other
7111                 //              NodeList-fx methods.
7112                 //      duration: Integer?
7113                 //              Optional. The time to run the animations for
7114                 //      easing: Function?
7115                 //              Optional. The easing function to use.
7116                 //      onEnd: Function?
7117                 //              A function to be called when the animation ends
7118                 //      delay:
7119                 //              how long to delay playing the returned animation
7120                 //      example:
7121                 //              Another way to fade out:
7122                 //      |       dojo.query(".thinger").anim({ opacity: 0 });
7123                 //      example:
7124                 //              animate all elements with the "thigner" class to a width of 500
7125                 //              pixels over half a second
7126                 //      |       dojo.query(".thinger").anim({ width: 500 }, 700);
7127                 var canim = dojo.fx.combine(
7128                         this.map(function(item){
7129                                 return dojo.animateProperty({
7130                                         node: item,
7131                                         properties: properties,
7132                                         duration: duration||350,
7133                                         easing: easing
7134                                 });
7135                         })
7136                 );
7137                 if(onEnd){
7138                         dojo.connect(canim, "onEnd", onEnd);
7139                 }
7140                 return canim.play(delay||0); // dojo.Animation
7141         }
7142 });
7143
7144 }
7145
7146 if(!dojo._hasResource["dojo.colors"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7147 dojo._hasResource["dojo.colors"] = true;
7148 dojo.provide("dojo.colors");
7149
7150 dojo.getObject("colors", true, dojo);
7151
7152 //TODO: this module appears to break naming conventions
7153
7154 /*=====
7155 dojo.colors = {
7156         // summary: Color utilities
7157 }
7158 =====*/
7159
7160 (function(){
7161         // this is a standard conversion prescribed by the CSS3 Color Module
7162         var hue2rgb = function(m1, m2, h){
7163                 if(h < 0){ ++h; }
7164                 if(h > 1){ --h; }
7165                 var h6 = 6 * h;
7166                 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
7167                 if(2 * h < 1){ return m2; }
7168                 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
7169                 return m1;
7170         };
7171         
7172         dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
7173                 // summary:
7174                 //              get rgb(a) array from css-style color declarations
7175                 // description:
7176                 //              this function can handle all 4 CSS3 Color Module formats: rgb,
7177                 //              rgba, hsl, hsla, including rgb(a) with percentage values.
7178                 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
7179                 if(m){
7180                         var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
7181                         if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
7182                                 var r = c[0];
7183                                 if(r.charAt(r.length - 1) == "%"){
7184                                         // 3 rgb percentage values
7185                                         a = dojo.map(c, function(x){
7186                                                 return parseFloat(x) * 2.56;
7187                                         });
7188                                         if(l == 4){ a[3] = c[3]; }
7189                                         return dojo.colorFromArray(a, obj);     // dojo.Color
7190                                 }
7191                                 return dojo.colorFromArray(c, obj);     // dojo.Color
7192                         }
7193                         if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
7194                                 // normalize hsl values
7195                                 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
7196                                         S = parseFloat(c[1]) / 100,
7197                                         L = parseFloat(c[2]) / 100,
7198                                         // calculate rgb according to the algorithm
7199                                         // recommended by the CSS3 Color Module
7200                                         m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
7201                                         m1 = 2 * L - m2;
7202                                 a = [
7203                                         hue2rgb(m1, m2, H + 1 / 3) * 256,
7204                                         hue2rgb(m1, m2, H) * 256,
7205                                         hue2rgb(m1, m2, H - 1 / 3) * 256,
7206                                         1
7207                                 ];
7208                                 if(l == 4){ a[3] = c[3]; }
7209                                 return dojo.colorFromArray(a, obj);     // dojo.Color
7210                         }
7211                 }
7212                 return null;    // dojo.Color
7213         };
7214         
7215         var confine = function(c, low, high){
7216                 // summary:
7217                 //              sanitize a color component by making sure it is a number,
7218                 //              and clamping it to valid values
7219                 c = Number(c);
7220                 return isNaN(c) ? high : c < low ? low : c > high ? high : c;   // Number
7221         };
7222         
7223         dojo.Color.prototype.sanitize = function(){
7224                 // summary: makes sure that the object has correct attributes
7225                 var t = this;
7226                 t.r = Math.round(confine(t.r, 0, 255));
7227                 t.g = Math.round(confine(t.g, 0, 255));
7228                 t.b = Math.round(confine(t.b, 0, 255));
7229                 t.a = confine(t.a, 0, 1);
7230                 return this;    // dojo.Color
7231         };
7232 })();
7233
7234
7235 dojo.colors.makeGrey = function(/*Number*/ g, /*Number?*/ a){
7236         // summary: creates a greyscale color with an optional alpha
7237         return dojo.colorFromArray([g, g, g, a]);
7238 };
7239
7240 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
7241 dojo.mixin(dojo.Color.named, {
7242         aliceblue:      [240,248,255],
7243         antiquewhite:   [250,235,215],
7244         aquamarine:     [127,255,212],
7245         azure:  [240,255,255],
7246         beige:  [245,245,220],
7247         bisque: [255,228,196],
7248         blanchedalmond: [255,235,205],
7249         blueviolet:     [138,43,226],
7250         brown:  [165,42,42],
7251         burlywood:      [222,184,135],
7252         cadetblue:      [95,158,160],
7253         chartreuse:     [127,255,0],
7254         chocolate:      [210,105,30],
7255         coral:  [255,127,80],
7256         cornflowerblue: [100,149,237],
7257         cornsilk:       [255,248,220],
7258         crimson:        [220,20,60],
7259         cyan:   [0,255,255],
7260         darkblue:       [0,0,139],
7261         darkcyan:       [0,139,139],
7262         darkgoldenrod:  [184,134,11],
7263         darkgray:       [169,169,169],
7264         darkgreen:      [0,100,0],
7265         darkgrey:       [169,169,169],
7266         darkkhaki:      [189,183,107],
7267         darkmagenta:    [139,0,139],
7268         darkolivegreen: [85,107,47],
7269         darkorange:     [255,140,0],
7270         darkorchid:     [153,50,204],
7271         darkred:        [139,0,0],
7272         darksalmon:     [233,150,122],
7273         darkseagreen:   [143,188,143],
7274         darkslateblue:  [72,61,139],
7275         darkslategray:  [47,79,79],
7276         darkslategrey:  [47,79,79],
7277         darkturquoise:  [0,206,209],
7278         darkviolet:     [148,0,211],
7279         deeppink:       [255,20,147],
7280         deepskyblue:    [0,191,255],
7281         dimgray:        [105,105,105],
7282         dimgrey:        [105,105,105],
7283         dodgerblue:     [30,144,255],
7284         firebrick:      [178,34,34],
7285         floralwhite:    [255,250,240],
7286         forestgreen:    [34,139,34],
7287         gainsboro:      [220,220,220],
7288         ghostwhite:     [248,248,255],
7289         gold:   [255,215,0],
7290         goldenrod:      [218,165,32],
7291         greenyellow:    [173,255,47],
7292         grey:   [128,128,128],
7293         honeydew:       [240,255,240],
7294         hotpink:        [255,105,180],
7295         indianred:      [205,92,92],
7296         indigo: [75,0,130],
7297         ivory:  [255,255,240],
7298         khaki:  [240,230,140],
7299         lavender:       [230,230,250],
7300         lavenderblush:  [255,240,245],
7301         lawngreen:      [124,252,0],
7302         lemonchiffon:   [255,250,205],
7303         lightblue:      [173,216,230],
7304         lightcoral:     [240,128,128],
7305         lightcyan:      [224,255,255],
7306         lightgoldenrodyellow:   [250,250,210],
7307         lightgray:      [211,211,211],
7308         lightgreen:     [144,238,144],
7309         lightgrey:      [211,211,211],
7310         lightpink:      [255,182,193],
7311         lightsalmon:    [255,160,122],
7312         lightseagreen:  [32,178,170],
7313         lightskyblue:   [135,206,250],
7314         lightslategray: [119,136,153],
7315         lightslategrey: [119,136,153],
7316         lightsteelblue: [176,196,222],
7317         lightyellow:    [255,255,224],
7318         limegreen:      [50,205,50],
7319         linen:  [250,240,230],
7320         magenta:        [255,0,255],
7321         mediumaquamarine:       [102,205,170],
7322         mediumblue:     [0,0,205],
7323         mediumorchid:   [186,85,211],
7324         mediumpurple:   [147,112,219],
7325         mediumseagreen: [60,179,113],
7326         mediumslateblue:        [123,104,238],
7327         mediumspringgreen:      [0,250,154],
7328         mediumturquoise:        [72,209,204],
7329         mediumvioletred:        [199,21,133],
7330         midnightblue:   [25,25,112],
7331         mintcream:      [245,255,250],
7332         mistyrose:      [255,228,225],
7333         moccasin:       [255,228,181],
7334         navajowhite:    [255,222,173],
7335         oldlace:        [253,245,230],
7336         olivedrab:      [107,142,35],
7337         orange: [255,165,0],
7338         orangered:      [255,69,0],
7339         orchid: [218,112,214],
7340         palegoldenrod:  [238,232,170],
7341         palegreen:      [152,251,152],
7342         paleturquoise:  [175,238,238],
7343         palevioletred:  [219,112,147],
7344         papayawhip:     [255,239,213],
7345         peachpuff:      [255,218,185],
7346         peru:   [205,133,63],
7347         pink:   [255,192,203],
7348         plum:   [221,160,221],
7349         powderblue:     [176,224,230],
7350         rosybrown:      [188,143,143],
7351         royalblue:      [65,105,225],
7352         saddlebrown:    [139,69,19],
7353         salmon: [250,128,114],
7354         sandybrown:     [244,164,96],
7355         seagreen:       [46,139,87],
7356         seashell:       [255,245,238],
7357         sienna: [160,82,45],
7358         skyblue:        [135,206,235],
7359         slateblue:      [106,90,205],
7360         slategray:      [112,128,144],
7361         slategrey:      [112,128,144],
7362         snow:   [255,250,250],
7363         springgreen:    [0,255,127],
7364         steelblue:      [70,130,180],
7365         tan:    [210,180,140],
7366         thistle:        [216,191,216],
7367         tomato: [255,99,71],
7368         transparent: [0, 0, 0, 0],
7369         turquoise:      [64,224,208],
7370         violet: [238,130,238],
7371         wheat:  [245,222,179],
7372         whitesmoke:     [245,245,245],
7373         yellowgreen:    [154,205,50]
7374 });
7375
7376 }
7377
7378 if(!dojo._hasResource["dojo.i18n"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7379 dojo._hasResource["dojo.i18n"] = true;
7380 dojo.provide("dojo.i18n");
7381
7382 dojo.getObject("i18n", true, dojo);
7383
7384 /*=====
7385 dojo.i18n = {
7386         // summary: Utility classes to enable loading of resources for internationalization (i18n)
7387 };
7388 =====*/
7389
7390 // when using a real AMD loader, dojo.i18n.getLocalization is already defined by dojo/lib/backCompat
7391 dojo.i18n.getLocalization = dojo.i18n.getLocalization || function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){
7392         //      summary:
7393         //              Returns an Object containing the localization for a given resource
7394         //              bundle in a package, matching the specified locale.
7395         //      description:
7396         //              Returns a hash containing name/value pairs in its prototypesuch
7397         //              that values can be easily overridden.  Throws an exception if the
7398         //              bundle is not found.  Bundle must have already been loaded by
7399         //              `dojo.requireLocalization()` or by a build optimization step.  NOTE:
7400         //              try not to call this method as part of an object property
7401         //              definition (`var foo = { bar: dojo.i18n.getLocalization() }`).  In
7402         //              some loading situations, the bundle may not be available in time
7403         //              for the object definition.  Instead, call this method inside a
7404         //              function that is run after all modules load or the page loads (like
7405         //              in `dojo.addOnLoad()`), or in a widget lifecycle method.
7406         //      packageName:
7407         //              package which is associated with this resource
7408         //      bundleName:
7409         //              the base filename of the resource bundle (without the ".js" suffix)
7410         //      locale:
7411         //              the variant to load (optional).  By default, the locale defined by
7412         //              the host environment: dojo.locale
7413
7414         locale = dojo.i18n.normalizeLocale(locale);
7415
7416         // look for nearest locale match
7417         var elements = locale.split('-');
7418         var module = [packageName,"nls",bundleName].join('.');
7419                 var bundle = dojo._loadedModules[module];
7420         if(bundle){
7421                 var localization;
7422                 for(var i = elements.length; i > 0; i--){
7423                         var loc = elements.slice(0, i).join('_');
7424                         if(bundle[loc]){
7425                                 localization = bundle[loc];
7426                                 break;
7427                         }
7428                 }
7429                 if(!localization){
7430                         localization = bundle.ROOT;
7431                 }
7432
7433                 // make a singleton prototype so that the caller won't accidentally change the values globally
7434                 if(localization){
7435                         var clazz = function(){};
7436                         clazz.prototype = localization;
7437                         return new clazz(); // Object
7438                 }
7439         }
7440
7441         throw new Error("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale);
7442 };
7443
7444 dojo.i18n.normalizeLocale = function(/*String?*/locale){
7445         //      summary:
7446         //              Returns canonical form of locale, as used by Dojo.
7447         //
7448         //  description:
7449         //              All variants are case-insensitive and are separated by '-' as specified in [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
7450         //              If no locale is specified, the dojo.locale is returned.  dojo.locale is defined by
7451         //              the user agent's locale unless overridden by djConfig.
7452
7453         var result = locale ? locale.toLowerCase() : dojo.locale;
7454         if(result == "root"){
7455                 result = "ROOT";
7456         }
7457         return result; // String
7458 };
7459
7460 dojo.i18n._requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
7461         //      summary:
7462         //              See dojo.requireLocalization()
7463         //      description:
7464         //              Called by the bootstrap, but factored out so that it is only
7465         //              included in the build when needed.
7466
7467         var targetLocale = dojo.i18n.normalizeLocale(locale);
7468         var bundlePackage = [moduleName, "nls", bundleName].join(".");
7469         // NOTE:
7470         //              When loading these resources, the packaging does not match what is
7471         //              on disk.  This is an implementation detail, as this is just a
7472         //              private data structure to hold the loaded resources.  e.g.
7473         //              `tests/hello/nls/en-us/salutations.js` is loaded as the object
7474         //              `tests.hello.nls.salutations.en_us={...}` The structure on disk is
7475         //              intended to be most convenient for developers and translators, but
7476         //              in memory it is more logical and efficient to store in a different
7477         //              order.  Locales cannot use dashes, since the resulting path will
7478         //              not evaluate as valid JS, so we translate them to underscores.
7479
7480         //Find the best-match locale to load if we have available flat locales.
7481         var bestLocale = "";
7482         if(availableFlatLocales){
7483                 var flatLocales = availableFlatLocales.split(",");
7484                 for(var i = 0; i < flatLocales.length; i++){
7485                         //Locale must match from start of string.
7486                         //Using ["indexOf"] so customBase builds do not see
7487                         //this as a dojo._base.array dependency.
7488                         if(targetLocale["indexOf"](flatLocales[i]) == 0){
7489                                 if(flatLocales[i].length > bestLocale.length){
7490                                         bestLocale = flatLocales[i];
7491                                 }
7492                         }
7493                 }
7494                 if(!bestLocale){
7495                         bestLocale = "ROOT";
7496                 }
7497         }
7498
7499         //See if the desired locale is already loaded.
7500         var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
7501         var bundle = dojo._loadedModules[bundlePackage];
7502         var localizedBundle = null;
7503         if(bundle){
7504                 if(dojo.config.localizationComplete && bundle._built){return;}
7505                 var jsLoc = tempLocale.replace(/-/g, '_');
7506                 var translationPackage = bundlePackage+"."+jsLoc;
7507                 localizedBundle = dojo._loadedModules[translationPackage];
7508         }
7509
7510         if(!localizedBundle){
7511                 bundle = dojo["provide"](bundlePackage);
7512                 var syms = dojo._getModuleSymbols(moduleName);
7513                 var modpath = syms.concat("nls").join("/");
7514                 var parent;
7515
7516                 dojo.i18n._searchLocalePath(tempLocale, availableFlatLocales, function(loc){
7517                         var jsLoc = loc.replace(/-/g, '_');
7518                         var translationPackage = bundlePackage + "." + jsLoc;
7519                         var loaded = false;
7520                         if(!dojo._loadedModules[translationPackage]){
7521                                 // Mark loaded whether it's found or not, so that further load attempts will not be made
7522                                 dojo["provide"](translationPackage);
7523                                 var module = [modpath];
7524                                 if(loc != "ROOT"){module.push(loc);}
7525                                 module.push(bundleName);
7526                                 var filespec = module.join("/") + '.js';
7527                                 loaded = dojo._loadPath(filespec, null, function(hash){
7528                                         hash = hash.root || hash;
7529                                         // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
7530                                         var clazz = function(){};
7531                                         clazz.prototype = parent;
7532                                         bundle[jsLoc] = new clazz();
7533                                         for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
7534                                 });
7535                         }else{
7536                                 loaded = true;
7537                         }
7538                         if(loaded && bundle[jsLoc]){
7539                                 parent = bundle[jsLoc];
7540                         }else{
7541                                 bundle[jsLoc] = parent;
7542                         }
7543
7544                         if(availableFlatLocales){
7545                                 //Stop the locale path searching if we know the availableFlatLocales, since
7546                                 //the first call to this function will load the only bundle that is needed.
7547                                 return true;
7548                         }
7549                 });
7550         }
7551
7552         //Save the best locale bundle as the target locale bundle when we know the
7553         //the available bundles.
7554         if(availableFlatLocales && targetLocale != bestLocale){
7555                 bundle[targetLocale.replace(/-/g, '_')] = bundle[bestLocale.replace(/-/g, '_')];
7556         }
7557 };
7558
7559 (function(){
7560         // If other locales are used, dojo.requireLocalization should load them as
7561         // well, by default.
7562         //
7563         // Override dojo.requireLocalization to do load the default bundle, then
7564         // iterate through the extraLocale list and load those translations as
7565         // well, unless a particular locale was requested.
7566
7567         var extra = dojo.config.extraLocale;
7568         if(extra){
7569                 if(!extra instanceof Array){
7570                         extra = [extra];
7571                 }
7572
7573                 var req = dojo.i18n._requireLocalization;
7574                 dojo.i18n._requireLocalization = function(m, b, locale, availableFlatLocales){
7575                         req(m,b,locale, availableFlatLocales);
7576                         if(locale){return;}
7577                         for(var i=0; i<extra.length; i++){
7578                                 req(m,b,extra[i], availableFlatLocales);
7579                         }
7580                 };
7581         }
7582 })();
7583
7584 dojo.i18n._searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
7585         //      summary:
7586         //              A helper method to assist in searching for locale-based resources.
7587         //              Will iterate through the variants of a particular locale, either up
7588         //              or down, executing a callback function.  For example, "en-us" and
7589         //              true will try "en-us" followed by "en" and finally "ROOT".
7590
7591         locale = dojo.i18n.normalizeLocale(locale);
7592
7593         var elements = locale.split('-');
7594         var searchlist = [];
7595         for(var i = elements.length; i > 0; i--){
7596                 searchlist.push(elements.slice(0, i).join('-'));
7597         }
7598         searchlist.push(false);
7599         if(down){searchlist.reverse();}
7600
7601         for(var j = searchlist.length - 1; j >= 0; j--){
7602                 var loc = searchlist[j] || "ROOT";
7603                 var stop = searchFunc(loc);
7604                 if(stop){ break; }
7605         }
7606 };
7607
7608 dojo.i18n._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated){
7609         //      summary:
7610         //              Load built, flattened resource bundles, if available for all
7611         //              locales used in the page. Only called by built layer files.
7612
7613         function preload(locale){
7614                 locale = dojo.i18n.normalizeLocale(locale);
7615                 dojo.i18n._searchLocalePath(locale, true, function(loc){
7616                         for(var i=0; i<localesGenerated.length;i++){
7617                                 if(localesGenerated[i] == loc){
7618                                         dojo["require"](bundlePrefix+"_"+loc);
7619                                         return true; // Boolean
7620                                 }
7621                         }
7622                         return false; // Boolean
7623                 });
7624         }
7625         preload();
7626         var extra = dojo.config.extraLocale||[];
7627         for(var i=0; i<extra.length; i++){
7628                 preload(extra[i]);
7629         }
7630 };
7631
7632 }
7633
7634 if(!dojo._hasResource["dijit._PaletteMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7635 dojo._hasResource["dijit._PaletteMixin"] = true;
7636 dojo.provide("dijit._PaletteMixin");
7637
7638
7639
7640 dojo.declare("dijit._PaletteMixin",
7641         [dijit._CssStateMixin],
7642         {
7643         // summary:
7644         //              A keyboard accessible palette, for picking a color/emoticon/etc.
7645         // description:
7646         //              A mixin for a grid showing various entities, so the user can pick a certain entity.
7647
7648         // defaultTimeout: Number
7649         //              Number of milliseconds before a held key or button becomes typematic
7650         defaultTimeout: 500,
7651
7652         // timeoutChangeRate: Number
7653         //              Fraction of time used to change the typematic timer between events
7654         //              1.0 means that each typematic event fires at defaultTimeout intervals
7655         //              < 1.0 means that each typematic event fires at an increasing faster rate
7656         timeoutChangeRate: 0.90,
7657
7658         // value: String
7659         //              Currently selected color/emoticon/etc.
7660         value: null,
7661         
7662         // _selectedCell: [private] Integer
7663         //              Index of the currently selected cell. Initially, none selected
7664         _selectedCell: -1,
7665
7666 /*=====
7667         // _currentFocus: [private] DomNode
7668         //              The currently focused cell (if the palette itself has focus), or otherwise
7669         //              the cell to be focused when the palette itself gets focus.
7670         //              Different from value, which represents the selected (i.e. clicked) cell.
7671         _currentFocus: null,
7672 =====*/
7673
7674 /*=====
7675         // _xDim: [protected] Integer
7676         //              This is the number of cells horizontally across.
7677         _xDim: null,
7678 =====*/
7679
7680 /*=====
7681         // _yDim: [protected] Integer
7682         //              This is the number of cells vertically down.
7683         _yDim: null,
7684 =====*/
7685
7686         // tabIndex: String
7687         //              Widget tab index.
7688         tabIndex: "0",
7689
7690         // cellClass: [protected] String
7691         //              CSS class applied to each cell in the palette
7692         cellClass: "dijitPaletteCell",
7693
7694         // dyeClass: [protected] String
7695         //       Name of javascript class for Object created for each cell of the palette.
7696         //       dyeClass should implements dijit.Dye interface
7697         dyeClass: '',
7698
7699         _preparePalette: function(choices, titles, dyeClassObj) {
7700                 // summary:
7701                 //              Subclass must call _preparePalette() from postCreate(), passing in the tooltip
7702                 //              for each cell
7703                 // choices: String[][]
7704                 //              id's for each cell of the palette, used to create Dye JS object for each cell
7705                 // titles: String[]
7706                 //              Localized tooltip for each cell
7707                 // dyeClassObj: Constructor?
7708                 //              If specified, use this constructor rather than this.dyeClass
7709
7710                 this._cells = [];
7711                 var url = this._blankGif;
7712                 
7713                 dyeClassObj = dyeClassObj || dojo.getObject(this.dyeClass);
7714
7715                 for(var row=0; row < choices.length; row++){
7716                         var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode);
7717                         for(var col=0; col < choices[row].length; col++){
7718                                 var value = choices[row][col];
7719                                 if(value){
7720                                         var cellObject = new dyeClassObj(value, row, col);
7721                                         
7722                                         var cellNode = dojo.create("td", {
7723                                                 "class": this.cellClass,
7724                                                 tabIndex: "-1",
7725                                                 title: titles[value]
7726                                         });
7727
7728                                         // prepare cell inner structure
7729                                         cellObject.fillCell(cellNode, url);
7730
7731                                         this.connect(cellNode, "ondijitclick", "_onCellClick");
7732                                         this._trackMouseState(cellNode, this.cellClass);
7733
7734                                         dojo.place(cellNode, rowNode);
7735
7736                                         cellNode.index = this._cells.length;
7737
7738                                         // save cell info into _cells
7739                                         this._cells.push({node:cellNode, dye:cellObject});
7740                                 }
7741                         }
7742                 }
7743                 this._xDim = choices[0].length;
7744                 this._yDim = choices.length;
7745
7746                 // Now set all events
7747                 // The palette itself is navigated to with the tab key on the keyboard
7748                 // Keyboard navigation within the Palette is with the arrow keys
7749                 // Spacebar selects the cell.
7750                 // For the up key the index is changed by negative the x dimension.
7751
7752                 var keyIncrementMap = {
7753                         UP_ARROW: -this._xDim,
7754                         // The down key the index is increase by the x dimension.
7755                         DOWN_ARROW: this._xDim,
7756                         // Right and left move the index by 1.
7757                         RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
7758                         LEFT_ARROW: this.isLeftToRight() ? -1 : 1
7759                 };
7760                 for(var key in keyIncrementMap){
7761                         this._connects.push(
7762                                 dijit.typematic.addKeyListener(
7763                                         this.domNode,
7764                                         {charOrCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
7765                                         this,
7766                                         function(){
7767                                                 var increment = keyIncrementMap[key];
7768                                                 return function(count){ this._navigateByKey(increment, count); };
7769                                         }(),
7770                                         this.timeoutChangeRate,
7771                                         this.defaultTimeout
7772                                 )
7773                         );
7774                 }
7775         },
7776
7777         postCreate: function(){
7778                 this.inherited(arguments);
7779
7780                 // Set initial navigable node.
7781                 this._setCurrent(this._cells[0].node);
7782         },
7783
7784         focus: function(){
7785                 // summary:
7786                 //              Focus this widget.  Puts focus on the most recently focused cell.
7787
7788                 // The cell already has tabIndex set, just need to set CSS and focus it
7789                 dijit.focus(this._currentFocus);
7790         },
7791
7792         _onCellClick: function(/*Event*/ evt){
7793                 // summary:
7794                 //              Handler for click, enter key & space key. Selects the cell.
7795                 // evt:
7796                 //              The event.
7797                 // tags:
7798                 //              private
7799
7800                 var target = evt.currentTarget,
7801                         value = this._getDye(target).getValue();
7802
7803                 // First focus the clicked cell, and then send onChange() notification.
7804                 // onChange() (via _setValueAttr) must be after the focus call, because
7805                 // it may trigger a refocus to somewhere else (like the Editor content area), and that
7806                 // second focus should win.
7807                 // Use setTimeout because IE doesn't like changing focus inside of an event handler.
7808                 this._setCurrent(target);
7809                 setTimeout(dojo.hitch(this, function(){
7810                         dijit.focus(target);
7811                         this._setValueAttr(value, true);
7812                 }));
7813
7814                 // workaround bug where hover class is not removed on popup because the popup is
7815                 // closed and then there's no onblur event on the cell
7816                 dojo.removeClass(target, "dijitPaletteCellHover");
7817
7818                 dojo.stopEvent(evt);
7819         },
7820
7821         _setCurrent: function(/*DomNode*/ node){
7822                 // summary:
7823                 //              Sets which node is the focused cell.
7824                 // description:
7825                 //              At any point in time there's exactly one
7826                 //              cell with tabIndex != -1.   If focus is inside the palette then
7827                 //              focus is on that cell.
7828                 //
7829                 //              After calling this method, arrow key handlers and mouse click handlers
7830                 //              should focus the cell in a setTimeout().
7831                 // tags:
7832                 //              protected
7833                 if("_currentFocus" in this){
7834                         // Remove tabIndex on old cell
7835                         dojo.attr(this._currentFocus, "tabIndex", "-1");
7836                 }
7837
7838                 // Set tabIndex of new cell
7839                 this._currentFocus = node;
7840                 if(node){
7841                         dojo.attr(node, "tabIndex", this.tabIndex);
7842                 }
7843         },
7844
7845         _setValueAttr: function(value, priorityChange){
7846                 // summary:
7847                 //              This selects a cell. It triggers the onChange event.
7848                 // value: String value of the cell to select
7849                 // tags:
7850                 //              protected
7851                 // priorityChange:
7852                 //              Optional parameter used to tell the select whether or not to fire
7853                 //              onChange event.
7854                 
7855                 // clear old selected cell
7856                 if(this._selectedCell >= 0){
7857                         dojo.removeClass(this._cells[this._selectedCell].node, "dijitPaletteCellSelected");
7858                 }
7859                 this._selectedCell = -1;
7860
7861                 // search for cell matching specified value
7862                 if(value){
7863                         for(var i = 0; i < this._cells.length; i++){
7864                                 if(value == this._cells[i].dye.getValue()){
7865                                         this._selectedCell = i;
7866                                         dojo.addClass(this._cells[i].node, "dijitPaletteCellSelected");
7867                                         break;
7868                                 }
7869                         }
7870                 }
7871                 
7872                 // record new value, or null if no matching cell
7873                 this._set("value", this._selectedCell >= 0 ? value : null);
7874
7875                 if(priorityChange || priorityChange === undefined){
7876                         this.onChange(value);
7877                 }
7878         },
7879
7880         onChange: function(value){
7881                 // summary:
7882                 //              Callback when a cell is selected.
7883                 // value: String
7884                 //              Value corresponding to cell.
7885         },
7886
7887         _navigateByKey: function(increment, typeCount){
7888                 // summary:
7889                 //              This is the callback for typematic.
7890                 //              It changes the focus and the highlighed cell.
7891                 // increment:
7892                 //              How much the key is navigated.
7893                 // typeCount:
7894                 //              How many times typematic has fired.
7895                 // tags:
7896                 //              private
7897
7898                 // typecount == -1 means the key is released.
7899                 if(typeCount == -1){ return; }
7900
7901                 var newFocusIndex = this._currentFocus.index + increment;
7902                 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
7903                         var focusNode = this._cells[newFocusIndex].node;
7904                         this._setCurrent(focusNode);
7905
7906                         // Actually focus the node, for the benefit of screen readers.
7907                         // Use setTimeout because IE doesn't like changing focus inside of an event handler
7908                         setTimeout(dojo.hitch(dijit, "focus", focusNode), 0);
7909                 }
7910         },
7911
7912         _getDye: function(/*DomNode*/ cell){
7913                 // summary:
7914                 //              Get JS object for given cell DOMNode
7915
7916                 return this._cells[cell.index].dye;
7917         }
7918 });
7919
7920 /*=====
7921 dojo.declare("dijit.Dye",
7922         null,
7923         {
7924                 // summary:
7925                 //              Interface for the JS Object associated with a palette cell (i.e. DOMNode)
7926
7927                 constructor: function(alias, row, col){
7928                         // summary:
7929                         //              Initialize according to value or alias like "white"
7930                         // alias: String
7931                 },
7932
7933                 getValue: function(){
7934                         // summary:
7935                         //              Return "value" of cell; meaning of "value" varies by subclass.
7936                         // description:
7937                         //              For example color hex value, emoticon ascii value etc, entity hex value.
7938                 },
7939
7940                 fillCell: function(cell, blankGif){
7941                         // summary:
7942                         //              Add cell DOMNode inner structure
7943                         //      cell: DomNode
7944                         //              The surrounding cell
7945                         //      blankGif: String
7946                         //              URL for blank cell image
7947                 }
7948         }
7949 );
7950 =====*/
7951
7952 }
7953
7954 if(!dojo._hasResource["dijit.ColorPalette"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7955 dojo._hasResource["dijit.ColorPalette"] = true;
7956 dojo.provide("dijit.ColorPalette");
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966 dojo.declare("dijit.ColorPalette",
7967         [dijit._Widget, dijit._Templated, dijit._PaletteMixin],
7968         {
7969         // summary:
7970         //              A keyboard accessible color-picking widget
7971         // description:
7972         //              Grid showing various colors, so the user can pick a certain color.
7973         //              Can be used standalone, or as a popup.
7974         //
7975         // example:
7976         // |    <div dojoType="dijit.ColorPalette"></div>
7977         //
7978         // example:
7979         // |    var picker = new dijit.ColorPalette({ },srcNode);
7980         // |    picker.startup();
7981
7982
7983         // palette: [const] String
7984         //              Size of grid, either "7x10" or "3x4".
7985         palette: "7x10",
7986
7987         // _palettes: [protected] Map
7988         //              This represents the value of the colors.
7989         //              The first level is a hashmap of the different palettes available.
7990         //              The next two dimensions represent the columns and rows of colors.
7991         _palettes: {
7992                 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan",     "lavender", "plum"],
7993                                 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
7994                                 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise",      "skyblue", "mediumslateblue","orchid"],
7995                                 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen",       "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
7996                                 ["dimgray", "crimson",  "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
7997                                 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
7998                                 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo",        "purple"]],
7999
8000                 "3x4": [["white", "lime", "green", "blue"],
8001                         ["silver", "yellow", "fuchsia", "navy"],
8002                         ["gray", "red", "purple", "black"]]
8003         },
8004
8005         // templateString: String
8006         //              The template of this widget.
8007         templateString: dojo.cache("dijit", "templates/ColorPalette.html", "<div class=\"dijitInline dijitColorPalette\">\n\t<table class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\">\n\t\t<tbody dojoAttachPoint=\"gridNode\"></tbody>\n\t</table>\n</div>\n"),
8008
8009         baseClass: "dijitColorPalette",
8010
8011         buildRendering: function(){
8012                 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
8013                 // <img> nodes
8014                 this.inherited(arguments);
8015
8016                 // Creates <img> nodes in each cell of the template.
8017                 // Pass in "customized" dijit._Color constructor for specified palette and high-contrast vs. normal mode
8018                 this._preparePalette(
8019                         this._palettes[this.palette],
8020                         dojo.i18n.getLocalization("dojo", "colors", this.lang),
8021                         dojo.declare(dijit._Color, {
8022                                 hc: dojo.hasClass(dojo.body(), "dijit_a11y"),
8023                                 palette: this.palette
8024                         })
8025                 );
8026         }
8027 });
8028
8029 dojo.declare("dijit._Color", dojo.Color, {
8030         // summary:
8031         //              Object associated with each cell in a ColorPalette palette.
8032         //              Implements dijit.Dye.
8033
8034         // Template for each cell in normal (non-high-contrast mode).  Each cell contains a wrapper
8035         // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
8036         // for showing the color.
8037         template:
8038                 "<span class='dijitInline dijitPaletteImg'>" +
8039                         "<img src='${blankGif}' alt='${alt}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
8040                 "</span>",
8041
8042         // Template for each cell in high contrast mode.  Each cell contains an image with the whole palette,
8043         // but scrolled and clipped to show the correct color only
8044         hcTemplate:
8045                 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
8046                         "<img src='${image}' alt='${alt}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
8047                 "</span>",
8048
8049         // _imagePaths: [protected] Map
8050         //              This is stores the path to the palette images used for high-contrast mode display
8051         _imagePaths: {
8052                 "7x10": dojo.moduleUrl("dijit.themes", "a11y/colors7x10.png"),
8053                 "3x4": dojo.moduleUrl("dijit.themes", "a11y/colors3x4.png")
8054         },
8055
8056         constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
8057                 this._alias = alias;
8058                 this._row = row;
8059                 this._col = col;
8060                 this.setColor(dojo.Color.named[alias]);
8061         },
8062
8063         getValue: function(){
8064                 // summary:
8065                 //              Note that although dijit._Color is initialized with a value like "white" getValue() always
8066                 //              returns a hex value
8067                 return this.toHex();
8068         },
8069
8070         fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
8071                 var html = dojo.string.substitute(this.hc ? this.hcTemplate : this.template, {
8072                         // substitution variables for normal mode
8073                         color: this.toHex(),
8074                         blankGif: blankGif,
8075                         alt: this._alias,
8076                         
8077                         // variables used for high contrast mode
8078                         image: this._imagePaths[this.palette].toString(),
8079                         left: this._col * -20 - 5,
8080                         top: this._row * -20 - 5,
8081                         size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
8082                 });
8083
8084                 dojo.place(html, cell);
8085         }
8086 });
8087
8088 }
8089
8090 if(!dojo._hasResource["dojo.dnd.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8091 dojo._hasResource["dojo.dnd.common"] = true;
8092 dojo.provide("dojo.dnd.common");
8093
8094 dojo.getObject("dnd", true, dojo);
8095
8096 dojo.dnd.getCopyKeyState = dojo.isCopyKey;
8097
8098 dojo.dnd._uniqueId = 0;
8099 dojo.dnd.getUniqueId = function(){
8100         // summary:
8101         //              returns a unique string for use with any DOM element
8102         var id;
8103         do{
8104                 id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
8105         }while(dojo.byId(id));
8106         return id;
8107 };
8108
8109 dojo.dnd._empty = {};
8110
8111 dojo.dnd.isFormElement = function(/*Event*/ e){
8112         // summary:
8113         //              returns true if user clicked on a form element
8114         var t = e.target;
8115         if(t.nodeType == 3 /*TEXT_NODE*/){
8116                 t = t.parentNode;
8117         }
8118         return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0;       // Boolean
8119 };
8120
8121 }
8122
8123 if(!dojo._hasResource["dojo.dnd.autoscroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8124 dojo._hasResource["dojo.dnd.autoscroll"] = true;
8125 dojo.provide("dojo.dnd.autoscroll");
8126
8127
8128 dojo.getObject("dnd", true, dojo);
8129
8130 dojo.dnd.getViewport = dojo.window.getBox;
8131
8132 dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
8133 dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
8134
8135 dojo.dnd.V_AUTOSCROLL_VALUE = 16;
8136 dojo.dnd.H_AUTOSCROLL_VALUE = 16;
8137
8138 dojo.dnd.autoScroll = function(e){
8139         // summary:
8140         //              a handler for onmousemove event, which scrolls the window, if
8141         //              necesary
8142         // e: Event
8143         //              onmousemove event
8144
8145         // FIXME: needs more docs!
8146         var v = dojo.window.getBox(), dx = 0, dy = 0;
8147         if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
8148                 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
8149         }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
8150                 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
8151         }
8152         if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
8153                 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
8154         }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
8155                 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
8156         }
8157         window.scrollBy(dx, dy);
8158 };
8159
8160 dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
8161 dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
8162
8163 dojo.dnd.autoScrollNodes = function(e){
8164         // summary:
8165         //              a handler for onmousemove event, which scrolls the first avaialble
8166         //              Dom element, it falls back to dojo.dnd.autoScroll()
8167         // e: Event
8168         //              onmousemove event
8169
8170         // FIXME: needs more docs!
8171         for(var n = e.target; n;){
8172                 if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
8173                         var s = dojo.getComputedStyle(n);
8174                         if(s.overflow.toLowerCase() in dojo.dnd._validOverflow){
8175                                 var b = dojo._getContentBox(n, s), t = dojo.position(n, true);
8176                                 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
8177                                 var w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2),
8178                                         h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2),
8179                                         rx = e.pageX - t.x, ry = e.pageY - t.y, dx = 0, dy = 0;
8180                                 if(dojo.isWebKit || dojo.isOpera){
8181                                         // FIXME: this code should not be here, it should be taken into account
8182                                         // either by the event fixing code, or the dojo.position()
8183                                         // FIXME: this code doesn't work on Opera 9.5 Beta
8184                                         rx += dojo.body().scrollLeft;
8185                                         ry += dojo.body().scrollTop;
8186                                 }
8187                                 if(rx > 0 && rx < b.w){
8188                                         if(rx < w){
8189                                                 dx = -w;
8190                                         }else if(rx > b.w - w){
8191                                                 dx = w;
8192                                         }
8193                                 }
8194                                 //console.log("ry =", ry, "b.h =", b.h, "h =", h);
8195                                 if(ry > 0 && ry < b.h){
8196                                         if(ry < h){
8197                                                 dy = -h;
8198                                         }else if(ry > b.h - h){
8199                                                 dy = h;
8200                                         }
8201                                 }
8202                                 var oldLeft = n.scrollLeft, oldTop = n.scrollTop;
8203                                 n.scrollLeft = n.scrollLeft + dx;
8204                                 n.scrollTop  = n.scrollTop  + dy;
8205                                 if(oldLeft != n.scrollLeft || oldTop != n.scrollTop){ return; }
8206                         }
8207                 }
8208                 try{
8209                         n = n.parentNode;
8210                 }catch(x){
8211                         n = null;
8212                 }
8213         }
8214         dojo.dnd.autoScroll(e);
8215 };
8216
8217 }
8218
8219 if(!dojo._hasResource["dojo.dnd.Mover"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8220 dojo._hasResource["dojo.dnd.Mover"] = true;
8221 dojo.provide("dojo.dnd.Mover");
8222
8223
8224
8225
8226 dojo.declare("dojo.dnd.Mover", null, {
8227         constructor: function(node, e, host){
8228                 // summary:
8229                 //              an object which makes a node follow the mouse, or touch-drag on touch devices.
8230                 //              Used as a default mover, and as a base class for custom movers.
8231                 // node: Node
8232                 //              a node (or node's id) to be moved
8233                 // e: Event
8234                 //              a mouse event, which started the move;
8235                 //              only pageX and pageY properties are used
8236                 // host: Object?
8237                 //              object which implements the functionality of the move,
8238                 //              and defines proper events (onMoveStart and onMoveStop)
8239                 this.node = dojo.byId(node);
8240                 var pos = e.touches ? e.touches[0] : e;
8241                 this.marginBox = {l: pos.pageX, t: pos.pageY};
8242                 this.mouseButton = e.button;
8243                 var h = (this.host = host), d = node.ownerDocument;
8244                 this.events = [
8245                         // At the start of a drag, onFirstMove is called, and then the following two
8246                         // connects are disconnected
8247                         dojo.connect(d, "onmousemove", this, "onFirstMove"),
8248                         dojo.connect(d, "ontouchmove", this, "onFirstMove"),
8249
8250                         // These are called continually during the drag
8251                         dojo.connect(d, "onmousemove", this, "onMouseMove"),
8252                         dojo.connect(d, "ontouchmove", this, "onMouseMove"),
8253
8254                         // And these are called at the end of the drag
8255                         dojo.connect(d, "onmouseup",   this, "onMouseUp"),
8256                         dojo.connect(d, "ontouchend", this, "onMouseUp"),
8257
8258                         // cancel text selection and text dragging
8259                         dojo.connect(d, "ondragstart",   dojo.stopEvent),
8260                         dojo.connect(d.body, "onselectstart", dojo.stopEvent)
8261                 ];
8262                 // notify that the move has started
8263                 if(h && h.onMoveStart){
8264                         h.onMoveStart(this);
8265                 }
8266         },
8267         // mouse event processors
8268         onMouseMove: function(e){
8269                 // summary:
8270                 //              event processor for onmousemove/ontouchmove
8271                 // e: Event
8272                 //              mouse/touch event
8273                 dojo.dnd.autoScroll(e);
8274                 var m = this.marginBox,
8275                         pos = e.touches ? e.touches[0] : e;
8276                 this.host.onMove(this, {l: m.l + pos.pageX, t: m.t + pos.pageY}, e);
8277                 dojo.stopEvent(e);
8278         },
8279         onMouseUp: function(e){
8280                 if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
8281                                 e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
8282                         this.destroy();
8283                 }
8284                 dojo.stopEvent(e);
8285         },
8286         // utilities
8287         onFirstMove: function(e){
8288                 // summary:
8289                 //              makes the node absolute; it is meant to be called only once.
8290                 //              relative and absolutely positioned nodes are assumed to use pixel units
8291                 var s = this.node.style, l, t, h = this.host;
8292                 switch(s.position){
8293                         case "relative":
8294                         case "absolute":
8295                                 // assume that left and top values are in pixels already
8296                                 l = Math.round(parseFloat(s.left)) || 0;
8297                                 t = Math.round(parseFloat(s.top)) || 0;
8298                                 break;
8299                         default:
8300                                 s.position = "absolute";        // enforcing the absolute mode
8301                                 var m = dojo.marginBox(this.node);
8302                                 // event.pageX/pageY (which we used to generate the initial
8303                                 // margin box) includes padding and margin set on the body.
8304                                 // However, setting the node's position to absolute and then
8305                                 // doing dojo.marginBox on it *doesn't* take that additional
8306                                 // space into account - so we need to subtract the combined
8307                                 // padding and margin.  We use getComputedStyle and
8308                                 // _getMarginBox/_getContentBox to avoid the extra lookup of
8309                                 // the computed style.
8310                                 var b = dojo.doc.body;
8311                                 var bs = dojo.getComputedStyle(b);
8312                                 var bm = dojo._getMarginBox(b, bs);
8313                                 var bc = dojo._getContentBox(b, bs);
8314                                 l = m.l - (bc.l - bm.l);
8315                                 t = m.t - (bc.t - bm.t);
8316                                 break;
8317                 }
8318                 this.marginBox.l = l - this.marginBox.l;
8319                 this.marginBox.t = t - this.marginBox.t;
8320                 if(h && h.onFirstMove){
8321                         h.onFirstMove(this, e);
8322                 }
8323                 
8324                 // Disconnect onmousemove and ontouchmove events that call this function
8325                 dojo.disconnect(this.events.shift());
8326                 dojo.disconnect(this.events.shift());
8327         },
8328         destroy: function(){
8329                 // summary:
8330                 //              stops the move, deletes all references, so the object can be garbage-collected
8331                 dojo.forEach(this.events, dojo.disconnect);
8332                 // undo global settings
8333                 var h = this.host;
8334                 if(h && h.onMoveStop){
8335                         h.onMoveStop(this);
8336                 }
8337                 // destroy objects
8338                 this.events = this.node = this.host = null;
8339         }
8340 });
8341
8342 }
8343
8344 if(!dojo._hasResource["dojo.dnd.Moveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8345 dojo._hasResource["dojo.dnd.Moveable"] = true;
8346 dojo.provide("dojo.dnd.Moveable");
8347
8348
8349
8350 /*=====
8351 dojo.declare("dojo.dnd.__MoveableArgs", [], {
8352         // handle: Node||String
8353         //              A node (or node's id), which is used as a mouse handle.
8354         //              If omitted, the node itself is used as a handle.
8355         handle: null,
8356
8357         // delay: Number
8358         //              delay move by this number of pixels
8359         delay: 0,
8360
8361         // skip: Boolean
8362         //              skip move of form elements
8363         skip: false,
8364
8365         // mover: Object
8366         //              a constructor of custom Mover
8367         mover: dojo.dnd.Mover
8368 });
8369 =====*/
8370
8371 dojo.declare("dojo.dnd.Moveable", null, {
8372         // object attributes (for markup)
8373         handle: "",
8374         delay: 0,
8375         skip: false,
8376         
8377         constructor: function(node, params){
8378                 // summary:
8379                 //              an object, which makes a node moveable
8380                 // node: Node
8381                 //              a node (or node's id) to be moved
8382                 // params: dojo.dnd.__MoveableArgs?
8383                 //              optional parameters
8384                 this.node = dojo.byId(node);
8385                 if(!params){ params = {}; }
8386                 this.handle = params.handle ? dojo.byId(params.handle) : null;
8387                 if(!this.handle){ this.handle = this.node; }
8388                 this.delay = params.delay > 0 ? params.delay : 0;
8389                 this.skip  = params.skip;
8390                 this.mover = params.mover ? params.mover : dojo.dnd.Mover;
8391                 this.events = [
8392                         dojo.connect(this.handle, "onmousedown", this, "onMouseDown"),
8393                         dojo.connect(this.handle, "ontouchstart", this, "onMouseDown"),
8394                         // cancel text selection and text dragging
8395                         dojo.connect(this.handle, "ondragstart",   this, "onSelectStart"),
8396                         dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
8397                 ];
8398         },
8399
8400         // markup methods
8401         markupFactory: function(params, node){
8402                 return new dojo.dnd.Moveable(node, params);
8403         },
8404
8405         // methods
8406         destroy: function(){
8407                 // summary:
8408                 //              stops watching for possible move, deletes all references, so the object can be garbage-collected
8409                 dojo.forEach(this.events, dojo.disconnect);
8410                 this.events = this.node = this.handle = null;
8411         },
8412         
8413         // mouse event processors
8414         onMouseDown: function(e){
8415                 // summary:
8416                 //              event processor for onmousedown/ontouchstart, creates a Mover for the node
8417                 // e: Event
8418                 //              mouse/touch event
8419                 if(this.skip && dojo.dnd.isFormElement(e)){ return; }
8420                 if(this.delay){
8421                         this.events.push(
8422                                 dojo.connect(this.handle, "onmousemove", this, "onMouseMove"),
8423                                 dojo.connect(this.handle, "ontouchmove", this, "onMouseMove"),
8424                                 dojo.connect(this.handle, "onmouseup", this, "onMouseUp"),
8425                                 dojo.connect(this.handle, "ontouchend", this, "onMouseUp")
8426                         );
8427                         var pos = e.touches ? e.touches[0] : e;
8428                         this._lastX = pos.pageX;
8429                         this._lastY = pos.pageY;
8430                 }else{
8431                         this.onDragDetected(e);
8432                 }
8433                 dojo.stopEvent(e);
8434         },
8435         onMouseMove: function(e){
8436                 // summary:
8437                 //              event processor for onmousemove/ontouchmove, used only for delayed drags
8438                 // e: Event
8439                 //              mouse/touch event
8440                 var pos = e.touches ? e.touches[0] : e;
8441                 if(Math.abs(pos.pageX - this._lastX) > this.delay || Math.abs(pos.pageY - this._lastY) > this.delay){
8442                         this.onMouseUp(e);
8443                         this.onDragDetected(e);
8444                 }
8445                 dojo.stopEvent(e);
8446         },
8447         onMouseUp: function(e){
8448                 // summary:
8449                 //              event processor for onmouseup, used only for delayed drags
8450                 // e: Event
8451                 //              mouse event
8452                 for(var i = 0; i < 2; ++i){
8453                         dojo.disconnect(this.events.pop());
8454                 }
8455                 dojo.stopEvent(e);
8456         },
8457         onSelectStart: function(e){
8458                 // summary:
8459                 //              event processor for onselectevent and ondragevent
8460                 // e: Event
8461                 //              mouse event
8462                 if(!this.skip || !dojo.dnd.isFormElement(e)){
8463                         dojo.stopEvent(e);
8464                 }
8465         },
8466         
8467         // local events
8468         onDragDetected: function(/* Event */ e){
8469                 // summary:
8470                 //              called when the drag is detected;
8471                 //              responsible for creation of the mover
8472                 new this.mover(this.node, e, this);
8473         },
8474         onMoveStart: function(/* dojo.dnd.Mover */ mover){
8475                 // summary:
8476                 //              called before every move operation
8477                 dojo.publish("/dnd/move/start", [mover]);
8478                 dojo.addClass(dojo.body(), "dojoMove");
8479                 dojo.addClass(this.node, "dojoMoveItem");
8480         },
8481         onMoveStop: function(/* dojo.dnd.Mover */ mover){
8482                 // summary:
8483                 //              called after every move operation
8484                 dojo.publish("/dnd/move/stop", [mover]);
8485                 dojo.removeClass(dojo.body(), "dojoMove");
8486                 dojo.removeClass(this.node, "dojoMoveItem");
8487         },
8488         onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
8489                 // summary:
8490                 //              called during the very first move notification;
8491                 //              can be used to initialize coordinates, can be overwritten.
8492                 
8493                 // default implementation does nothing
8494         },
8495         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
8496                 // summary:
8497                 //              called during every move notification;
8498                 //              should actually move the node; can be overwritten.
8499                 this.onMoving(mover, leftTop);
8500                 var s = mover.node.style;
8501                 s.left = leftTop.l + "px";
8502                 s.top  = leftTop.t + "px";
8503                 this.onMoved(mover, leftTop);
8504         },
8505         onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8506                 // summary:
8507                 //              called before every incremental move; can be overwritten.
8508                 
8509                 // default implementation does nothing
8510         },
8511         onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8512                 // summary:
8513                 //              called after every incremental move; can be overwritten.
8514                 
8515                 // default implementation does nothing
8516         }
8517 });
8518
8519 }
8520
8521 if(!dojo._hasResource["dojo.dnd.move"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8522 dojo._hasResource["dojo.dnd.move"] = true;
8523 dojo.provide("dojo.dnd.move");
8524
8525
8526
8527
8528 /*=====
8529 dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
8530         // constraints: Function
8531         //              Calculates a constraint box.
8532         //              It is called in a context of the moveable object.
8533         constraints: function(){},
8534
8535         // within: Boolean
8536         //              restrict move within boundaries.
8537         within: false
8538 });
8539 =====*/
8540
8541 dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
8542         // object attributes (for markup)
8543         constraints: function(){},
8544         within: false,
8545         
8546         // markup methods
8547         markupFactory: function(params, node){
8548                 return new dojo.dnd.move.constrainedMoveable(node, params);
8549         },
8550
8551         constructor: function(node, params){
8552                 // summary:
8553                 //              an object that makes a node moveable
8554                 // node: Node
8555                 //              a node (or node's id) to be moved
8556                 // params: dojo.dnd.move.__constrainedMoveableArgs?
8557                 //              an optional object with additional parameters;
8558                 //              the rest is passed to the base class
8559                 if(!params){ params = {}; }
8560                 this.constraints = params.constraints;
8561                 this.within = params.within;
8562         },
8563         onFirstMove: function(/* dojo.dnd.Mover */ mover){
8564                 // summary:
8565                 //              called during the very first move notification;
8566                 //              can be used to initialize coordinates, can be overwritten.
8567                 var c = this.constraintBox = this.constraints.call(this, mover);
8568                 c.r = c.l + c.w;
8569                 c.b = c.t + c.h;
8570                 if(this.within){
8571                         var mb = dojo._getMarginSize(mover.node);
8572                         c.r -= mb.w;
8573                         c.b -= mb.h;
8574                 }
8575         },
8576         onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8577                 // summary:
8578                 //              called during every move notification;
8579                 //              should actually move the node; can be overwritten.
8580                 var c = this.constraintBox, s = mover.node.style;
8581                 this.onMoving(mover, leftTop);
8582                 leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
8583                 leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
8584                 s.left = leftTop.l + "px";
8585                 s.top  = leftTop.t + "px";
8586                 this.onMoved(mover, leftTop);
8587         }
8588 });
8589
8590 /*=====
8591 dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
8592         // box: Object
8593         //              a constraint box
8594         box: {}
8595 });
8596 =====*/
8597
8598 dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
8599         // box:
8600         //              object attributes (for markup)
8601         box: {},
8602         
8603         // markup methods
8604         markupFactory: function(params, node){
8605                 return new dojo.dnd.move.boxConstrainedMoveable(node, params);
8606         },
8607
8608         constructor: function(node, params){
8609                 // summary:
8610                 //              an object, which makes a node moveable
8611                 // node: Node
8612                 //              a node (or node's id) to be moved
8613                 // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
8614                 //              an optional object with parameters
8615                 var box = params && params.box;
8616                 this.constraints = function(){ return box; };
8617         }
8618 });
8619
8620 /*=====
8621 dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
8622         // area: String
8623         //              A parent's area to restrict the move.
8624         //              Can be "margin", "border", "padding", or "content".
8625         area: ""
8626 });
8627 =====*/
8628
8629 dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
8630         // area:
8631         //              object attributes (for markup)
8632         area: "content",
8633
8634         // markup methods
8635         markupFactory: function(params, node){
8636                 return new dojo.dnd.move.parentConstrainedMoveable(node, params);
8637         },
8638
8639         constructor: function(node, params){
8640                 // summary:
8641                 //              an object, which makes a node moveable
8642                 // node: Node
8643                 //              a node (or node's id) to be moved
8644                 // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
8645                 //              an optional object with parameters
8646                 var area = params && params.area;
8647                 this.constraints = function(){
8648                         var n = this.node.parentNode,
8649                                 s = dojo.getComputedStyle(n),
8650                                 mb = dojo._getMarginBox(n, s);
8651                         if(area == "margin"){
8652                                 return mb;      // Object
8653                         }
8654                         var t = dojo._getMarginExtents(n, s);
8655                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8656                         if(area == "border"){
8657                                 return mb;      // Object
8658                         }
8659                         t = dojo._getBorderExtents(n, s);
8660                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8661                         if(area == "padding"){
8662                                 return mb;      // Object
8663                         }
8664                         t = dojo._getPadExtents(n, s);
8665                         mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
8666                         return mb;      // Object
8667                 };
8668         }
8669 });
8670
8671 // patching functions one level up for compatibility
8672
8673 dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
8674 dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
8675 dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
8676
8677 }
8678
8679 if(!dojo._hasResource["dojo.dnd.TimedMoveable"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8680 dojo._hasResource["dojo.dnd.TimedMoveable"] = true;
8681 dojo.provide("dojo.dnd.TimedMoveable");
8682
8683
8684
8685 /*=====
8686 dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
8687         // timeout: Number
8688         //              delay move by this number of ms,
8689         //              accumulating position changes during the timeout
8690         timeout: 0
8691 });
8692 =====*/
8693
8694 (function(){
8695         // precalculate long expressions
8696         var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
8697                 
8698         dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
8699                 // summary:
8700                 //              A specialized version of Moveable to support an FPS throttling.
8701                 //              This class puts an upper restriction on FPS, which may reduce
8702                 //              the CPU load. The additional parameter "timeout" regulates
8703                 //              the delay before actually moving the moveable object.
8704                 
8705                 // object attributes (for markup)
8706                 timeout: 40,    // in ms, 40ms corresponds to 25 fps
8707         
8708                 constructor: function(node, params){
8709                         // summary:
8710                         //              an object that makes a node moveable with a timer
8711                         // node: Node||String
8712                         //              a node (or node's id) to be moved
8713                         // params: dojo.dnd.__TimedMoveableArgs
8714                         //              object with additional parameters.
8715                         
8716                         // sanitize parameters
8717                         if(!params){ params = {}; }
8718                         if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
8719                                 this.timeout = params.timeout;
8720                         }
8721                 },
8722         
8723                 // markup methods
8724                 markupFactory: function(params, node){
8725                         return new dojo.dnd.TimedMoveable(node, params);
8726                 },
8727         
8728                 onMoveStop: function(/* dojo.dnd.Mover */ mover){
8729                         if(mover._timer){
8730                                 // stop timer
8731                                 clearTimeout(mover._timer)
8732                                 // reflect the last received position
8733                                 oldOnMove.call(this, mover, mover._leftTop)
8734                         }
8735                         dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
8736                 },
8737                 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
8738                         mover._leftTop = leftTop;
8739                         if(!mover._timer){
8740                                 var _t = this;  // to avoid using dojo.hitch()
8741                                 mover._timer = setTimeout(function(){
8742                                         // we don't have any pending requests
8743                                         mover._timer = null;
8744                                         // reflect the last received position
8745                                         oldOnMove.call(_t, mover, mover._leftTop);
8746                                 }, this.timeout);
8747                         }
8748                 }
8749         });
8750 })();
8751
8752 }
8753
8754 if(!dojo._hasResource["dijit.form._FormMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8755 dojo._hasResource["dijit.form._FormMixin"] = true;
8756 dojo.provide("dijit.form._FormMixin");
8757
8758
8759
8760 dojo.declare("dijit.form._FormMixin", null, {
8761         // summary:
8762         //              Mixin for containers of form widgets (i.e. widgets that represent a single value
8763         //              and can be children of a <form> node or dijit.form.Form widget)
8764         // description:
8765         //              Can extract all the form widgets
8766         //              values and combine them into a single javascript object, or alternately
8767         //              take such an object and set the values for all the contained
8768         //              form widgets
8769
8770 /*=====
8771         // value: Object
8772         //              Name/value hash for each child widget with a name and value.
8773         //              Child widgets without names are not part of the hash.
8774         //
8775         //              If there are multiple child widgets w/the same name, value is an array,
8776         //              unless they are radio buttons in which case value is a scalar (since only
8777         //              one radio button can be checked at a time).
8778         //
8779         //              If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
8780         //
8781         //              Example:
8782         //      |       { name: "John Smith", interests: ["sports", "movies"] }
8783 =====*/
8784
8785         // state: [readonly] String
8786         //              Will be "Error" if one or more of the child widgets has an invalid value,
8787         //              "Incomplete" if not all of the required child widgets are filled in.  Otherwise, "",
8788         //              which indicates that the form is ready to be submitted.
8789         state: "",
8790
8791         //      TODO:
8792         //      * Repeater
8793         //      * better handling for arrays.  Often form elements have names with [] like
8794         //      * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
8795         //
8796         //
8797
8798                 reset: function(){
8799                         dojo.forEach(this.getDescendants(), function(widget){
8800                                 if(widget.reset){
8801                                         widget.reset();
8802                                 }
8803                         });
8804                 },
8805
8806                 validate: function(){
8807                         // summary:
8808                         //              returns if the form is valid - same as isValid - but
8809                         //              provides a few additional (ui-specific) features.
8810                         //              1 - it will highlight any sub-widgets that are not
8811                         //                      valid
8812                         //              2 - it will call focus() on the first invalid
8813                         //                      sub-widget
8814                         var didFocus = false;
8815                         return dojo.every(dojo.map(this.getDescendants(), function(widget){
8816                                 // Need to set this so that "required" widgets get their
8817                                 // state set.
8818                                 widget._hasBeenBlurred = true;
8819                                 var valid = widget.disabled || !widget.validate || widget.validate();
8820                                 if(!valid && !didFocus){
8821                                         // Set focus of the first non-valid widget
8822                                         dojo.window.scrollIntoView(widget.containerNode || widget.domNode);
8823                                         widget.focus();
8824                                         didFocus = true;
8825                                 }
8826                                 return valid;
8827                         }), function(item){ return item; });
8828                 },
8829
8830                 setValues: function(val){
8831                         dojo.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
8832                         return this.set('value', val);
8833                 },
8834                 _setValueAttr: function(/*Object*/ obj){
8835                         // summary:
8836                         //              Fill in form values from according to an Object (in the format returned by get('value'))
8837
8838                         // generate map from name --> [list of widgets with that name]
8839                         var map = { };
8840                         dojo.forEach(this.getDescendants(), function(widget){
8841                                 if(!widget.name){ return; }
8842                                 var entry = map[widget.name] || (map[widget.name] = [] );
8843                                 entry.push(widget);
8844                         });
8845
8846                         for(var name in map){
8847                                 if(!map.hasOwnProperty(name)){
8848                                         continue;
8849                                 }
8850                                 var widgets = map[name],                                                // array of widgets w/this name
8851                                         values = dojo.getObject(name, false, obj);      // list of values for those widgets
8852
8853                                 if(values === undefined){
8854                                         continue;
8855                                 }
8856                                 if(!dojo.isArray(values)){
8857                                         values = [ values ];
8858                                 }
8859                                 if(typeof widgets[0].checked == 'boolean'){
8860                                         // for checkbox/radio, values is a list of which widgets should be checked
8861                                         dojo.forEach(widgets, function(w, i){
8862                                                 w.set('value', dojo.indexOf(values, w.value) != -1);
8863                                         });
8864                                 }else if(widgets[0].multiple){
8865                                         // it takes an array (e.g. multi-select)
8866                                         widgets[0].set('value', values);
8867                                 }else{
8868                                         // otherwise, values is a list of values to be assigned sequentially to each widget
8869                                         dojo.forEach(widgets, function(w, i){
8870                                                 w.set('value', values[i]);
8871                                         });
8872                                 }
8873                         }
8874
8875                         /***
8876                          *      TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
8877
8878                         dojo.forEach(this.containerNode.elements, function(element){
8879                                 if(element.name == ''){return}; // like "continue"
8880                                 var namePath = element.name.split(".");
8881                                 var myObj=obj;
8882                                 var name=namePath[namePath.length-1];
8883                                 for(var j=1,len2=namePath.length;j<len2;++j){
8884                                         var p=namePath[j - 1];
8885                                         // repeater support block
8886                                         var nameA=p.split("[");
8887                                         if(nameA.length > 1){
8888                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
8889                                                         myObj[nameA[0]]=[ ];
8890                                                 } // if
8891
8892                                                 nameIndex=parseInt(nameA[1]);
8893                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
8894                                                         myObj[nameA[0]][nameIndex] = { };
8895                                                 }
8896                                                 myObj=myObj[nameA[0]][nameIndex];
8897                                                 continue;
8898                                         } // repeater support ends
8899
8900                                         if(typeof(myObj[p]) == "undefined"){
8901                                                 myObj=undefined;
8902                                                 break;
8903                                         };
8904                                         myObj=myObj[p];
8905                                 }
8906
8907                                 if(typeof(myObj) == "undefined"){
8908                                         return;         // like "continue"
8909                                 }
8910                                 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
8911                                         return;         // like "continue"
8912                                 }
8913
8914                                 // TODO: widget values (just call set('value', ...) on the widget)
8915
8916                                 // TODO: maybe should call dojo.getNodeProp() instead
8917                                 switch(element.type){
8918                                         case "checkbox":
8919                                                 element.checked = (name in myObj) &&
8920                                                         dojo.some(myObj[name], function(val){ return val == element.value; });
8921                                                 break;
8922                                         case "radio":
8923                                                 element.checked = (name in myObj) && myObj[name] == element.value;
8924                                                 break;
8925                                         case "select-multiple":
8926                                                 element.selectedIndex=-1;
8927                                                 dojo.forEach(element.options, function(option){
8928                                                         option.selected = dojo.some(myObj[name], function(val){ return option.value == val; });
8929                                                 });
8930                                                 break;
8931                                         case "select-one":
8932                                                 element.selectedIndex="0";
8933                                                 dojo.forEach(element.options, function(option){
8934                                                         option.selected = option.value == myObj[name];
8935                                                 });
8936                                                 break;
8937                                         case "hidden":
8938                                         case "text":
8939                                         case "textarea":
8940                                         case "password":
8941                                                 element.value = myObj[name] || "";
8942                                                 break;
8943                                 }
8944                         });
8945                         */
8946                         
8947                         // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
8948                         // which I am monitoring.
8949                 },
8950
8951                 getValues: function(){
8952                         dojo.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
8953                         return this.get('value');
8954                 },
8955                 _getValueAttr: function(){
8956                         // summary:
8957                         //              Returns Object representing form values.   See description of `value` for details.
8958                         // description:
8959
8960                         // The value is updated into this.value every time a child has an onChange event,
8961                         // so in the common case this function could just return this.value.   However,
8962                         // that wouldn't work when:
8963                         //
8964                         // 1. User presses return key to submit a form.  That doesn't fire an onchange event,
8965                         // and even if it did it would come too late due to the setTimout(..., 0) in _handleOnChange()
8966                         //
8967                         // 2. app for some reason calls this.get("value") while the user is typing into a
8968                         // form field.   Not sure if that case needs to be supported or not.
8969
8970                         // get widget values
8971                         var obj = { };
8972                         dojo.forEach(this.getDescendants(), function(widget){
8973                                 var name = widget.name;
8974                                 if(!name || widget.disabled){ return; }
8975
8976                                 // Single value widget (checkbox, radio, or plain <input> type widget)
8977                                 var value = widget.get('value');
8978
8979                                 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
8980                                 if(typeof widget.checked == 'boolean'){
8981                                         if(/Radio/.test(widget.declaredClass)){
8982                                                 // radio button
8983                                                 if(value !== false){
8984                                                         dojo.setObject(name, value, obj);
8985                                                 }else{
8986                                                         // give radio widgets a default of null
8987                                                         value = dojo.getObject(name, false, obj);
8988                                                         if(value === undefined){
8989                                                                 dojo.setObject(name, null, obj);
8990                                                         }
8991                                                 }
8992                                         }else{
8993                                                 // checkbox/toggle button
8994                                                 var ary=dojo.getObject(name, false, obj);
8995                                                 if(!ary){
8996                                                         ary=[];
8997                                                         dojo.setObject(name, ary, obj);
8998                                                 }
8999                                                 if(value !== false){
9000                                                         ary.push(value);
9001                                                 }
9002                                         }
9003                                 }else{
9004                                         var prev=dojo.getObject(name, false, obj);
9005                                         if(typeof prev != "undefined"){
9006                                                 if(dojo.isArray(prev)){
9007                                                         prev.push(value);
9008                                                 }else{
9009                                                         dojo.setObject(name, [prev, value], obj);
9010                                                 }
9011                                         }else{
9012                                                 // unique name
9013                                                 dojo.setObject(name, value, obj);
9014                                         }
9015                                 }
9016                         });
9017
9018                         /***
9019                          * code for plain input boxes (see also dojo.formToObject, can we use that instead of this code?
9020                          * but it doesn't understand [] notation, presumably)
9021                         var obj = { };
9022                         dojo.forEach(this.containerNode.elements, function(elm){
9023                                 if(!elm.name)   {
9024                                         return;         // like "continue"
9025                                 }
9026                                 var namePath = elm.name.split(".");
9027                                 var myObj=obj;
9028                                 var name=namePath[namePath.length-1];
9029                                 for(var j=1,len2=namePath.length;j<len2;++j){
9030                                         var nameIndex = null;
9031                                         var p=namePath[j - 1];
9032                                         var nameA=p.split("[");
9033                                         if(nameA.length > 1){
9034                                                 if(typeof(myObj[nameA[0]]) == "undefined"){
9035                                                         myObj[nameA[0]]=[ ];
9036                                                 } // if
9037                                                 nameIndex=parseInt(nameA[1]);
9038                                                 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
9039                                                         myObj[nameA[0]][nameIndex] = { };
9040                                                 }
9041                                         } else if(typeof(myObj[nameA[0]]) == "undefined"){
9042                                                 myObj[nameA[0]] = { }
9043                                         } // if
9044
9045                                         if(nameA.length == 1){
9046                                                 myObj=myObj[nameA[0]];
9047                                         } else{
9048                                                 myObj=myObj[nameA[0]][nameIndex];
9049                                         } // if
9050                                 } // for
9051
9052                                 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
9053                                         if(name == name.split("[")[0]){
9054                                                 myObj[name]=elm.value;
9055                                         } else{
9056                                                 // can not set value when there is no name
9057                                         }
9058                                 } else if(elm.type == "checkbox" && elm.checked){
9059                                         if(typeof(myObj[name]) == 'undefined'){
9060                                                 myObj[name]=[ ];
9061                                         }
9062                                         myObj[name].push(elm.value);
9063                                 } else if(elm.type == "select-multiple"){
9064                                         if(typeof(myObj[name]) == 'undefined'){
9065                                                 myObj[name]=[ ];
9066                                         }
9067                                         for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
9068                                                 if(elm.options[jdx].selected){
9069                                                         myObj[name].push(elm.options[jdx].value);
9070                                                 }
9071                                         }
9072                                 } // if
9073                                 name=undefined;
9074                         }); // forEach
9075                         ***/
9076                         return obj;
9077                 },
9078
9079                 isValid: function(){
9080                         // summary:
9081                         //              Returns true if all of the widgets are valid.
9082                         //              Deprecated, will be removed in 2.0.  Use get("state") instead.
9083
9084                         return this.state == "";
9085                 },
9086
9087                 onValidStateChange: function(isValid){
9088                         // summary:
9089                         //              Stub function to connect to if you want to do something
9090                         //              (like disable/enable a submit button) when the valid
9091                         //              state changes on the form as a whole.
9092                         //
9093                         //              Deprecated.  Will be removed in 2.0.  Use watch("state", ...) instead.
9094                 },
9095
9096                 _getState: function(){
9097                         // summary:
9098                         //              Compute what this.state should be based on state of children
9099                         var states = dojo.map(this._descendants, function(w){
9100                                 return w.get("state") || "";
9101                         });
9102
9103                         return dojo.indexOf(states, "Error") >= 0 ? "Error" :
9104                                 dojo.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
9105                 },
9106
9107                 disconnectChildren: function(){
9108                         // summary:
9109                         //              Remove connections to monitor changes to children's value, error state, and disabled state,
9110                         //              in order to update Form.value and Form.state.
9111                         dojo.forEach(this._childConnections || [], dojo.hitch(this, "disconnect"));
9112                         dojo.forEach(this._childWatches || [], function(w){ w.unwatch(); });
9113                 },
9114
9115                 connectChildren: function(/*Boolean*/ inStartup){
9116                         // summary:
9117                         //              Setup connections to monitor changes to children's value, error state, and disabled state,
9118                         //              in order to update Form.value and Form.state.
9119                         //
9120                         //              You can call this function directly, ex. in the event that you
9121                         //              programmatically add a widget to the form *after* the form has been
9122                         //              initialized.
9123
9124                         var _this = this;
9125
9126                         // Remove old connections, if any
9127                         this.disconnectChildren();
9128
9129                         this._descendants = this.getDescendants();
9130
9131                         // (Re)set this.value and this.state.   Send watch() notifications but not on startup.
9132                         var set = inStartup ? function(name, val){ _this[name] = val; } : dojo.hitch(this, "_set");
9133                         set("value", this.get("value"));
9134                         set("state", this._getState());
9135
9136                         // Monitor changes to error state and disabled state in order to update
9137                         // Form.state
9138                         var conns = (this._childConnections = []),
9139                                 watches = (this._childWatches = []);
9140                         dojo.forEach(dojo.filter(this._descendants,
9141                                 function(item){ return item.validate; }
9142                         ),
9143                         function(widget){
9144                                 // We are interested in whenever the widget changes validity state - or
9145                                 // whenever the disabled attribute on that widget is changed.
9146                                 dojo.forEach(["state", "disabled"], function(attr){
9147                                         watches.push(widget.watch(attr, function(attr, oldVal, newVal){
9148                                                 _this.set("state", _this._getState());
9149                                         }));
9150                                 });
9151                         });
9152
9153                         // And monitor calls to child.onChange so we can update this.value
9154                         var onChange = function(){
9155                                 // summary:
9156                                 //              Called when child's value or disabled state changes
9157                                 
9158                                 // Use setTimeout() to collapse value changes in multiple children into a single
9159                                 // update to my value.   Multiple updates will occur on:
9160                                 //      1. Form.set()
9161                                 //      2. Form.reset()
9162                                 //      3. user selecting a radio button (which will de-select another radio button,
9163                                 //               causing two onChange events)
9164                                 if(_this._onChangeDelayTimer){
9165                                         clearTimeout(_this._onChangeDelayTimer);
9166                                 }
9167                                 _this._onChangeDelayTimer = setTimeout(function(){
9168                                         delete _this._onChangeDelayTimer;
9169                                         _this._set("value", _this.get("value"));
9170                                 }, 10);
9171                         };
9172                         dojo.forEach(
9173                                 dojo.filter(this._descendants, function(item){ return item.onChange; } ),
9174                                 function(widget){
9175                                         // When a child widget's value changes,
9176                                         // the efficient thing to do is to just update that one attribute in this.value,
9177                                         // but that gets a little complicated when a checkbox is checked/unchecked
9178                                         // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
9179                                         // Doing simple thing for now.
9180                                         conns.push(_this.connect(widget, "onChange", onChange));
9181
9182                                         // Disabling/enabling a child widget should remove it's value from this.value.
9183                                         // Again, this code could be more efficient, doing simple thing for now.
9184                                         watches.push(widget.watch("disabled", onChange));
9185                                 }
9186                         );
9187                 },
9188
9189                 startup: function(){
9190                         this.inherited(arguments);
9191
9192                         // Initialize value and valid/invalid state tracking.  Needs to be done in startup()
9193                         // so that children are initialized.
9194                         this.connectChildren(true);
9195
9196                         // Make state change call onValidStateChange(), will be removed in 2.0
9197                         this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
9198                 },
9199
9200                 destroy: function(){
9201                         this.disconnectChildren();
9202                         this.inherited(arguments);
9203                 }
9204
9205         });
9206
9207 }
9208
9209 if(!dojo._hasResource["dijit._DialogMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9210 dojo._hasResource["dijit._DialogMixin"] = true;
9211 dojo.provide("dijit._DialogMixin");
9212
9213
9214
9215 dojo.declare("dijit._DialogMixin", null,
9216         {
9217                 // summary:
9218                 //              This provides functions useful to Dialog and TooltipDialog
9219
9220                 attributeMap: dijit._Widget.prototype.attributeMap,
9221
9222                 execute: function(/*Object*/ formContents){
9223                         // summary:
9224                         //              Callback when the user hits the submit button.
9225                         //              Override this method to handle Dialog execution.
9226                         // description:
9227                         //              After the user has pressed the submit button, the Dialog
9228                         //              first calls onExecute() to notify the container to hide the
9229                         //              dialog and restore focus to wherever it used to be.
9230                         //
9231                         //              *Then* this method is called.
9232                         // type:
9233                         //              callback
9234                 },
9235
9236                 onCancel: function(){
9237                         // summary:
9238                         //          Called when user has pressed the Dialog's cancel button, to notify container.
9239                         // description:
9240                         //          Developer shouldn't override or connect to this method;
9241                         //              it's a private communication device between the TooltipDialog
9242                         //              and the thing that opened it (ex: `dijit.form.DropDownButton`)
9243                         // type:
9244                         //              protected
9245                 },
9246
9247                 onExecute: function(){
9248                         // summary:
9249                         //          Called when user has pressed the dialog's OK button, to notify container.
9250                         // description:
9251                         //          Developer shouldn't override or connect to this method;
9252                         //              it's a private communication device between the TooltipDialog
9253                         //              and the thing that opened it (ex: `dijit.form.DropDownButton`)
9254                         // type:
9255                         //              protected
9256                 },
9257
9258                 _onSubmit: function(){
9259                         // summary:
9260                         //              Callback when user hits submit button
9261                         // type:
9262                         //              protected
9263                         this.onExecute();       // notify container that we are about to execute
9264                         this.execute(this.get('value'));
9265                 },
9266
9267                 _getFocusItems: function(){
9268                         // summary:
9269                         //              Finds focusable items in dialog,
9270                         //              and sets this._firstFocusItem and this._lastFocusItem
9271                         // tags:
9272                         //              protected
9273
9274                         var elems = dijit._getTabNavigable(this.containerNode);
9275                         this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
9276                         this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
9277                 }
9278         }
9279 );
9280
9281 }
9282
9283 if(!dojo._hasResource["dijit.DialogUnderlay"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9284 dojo._hasResource["dijit.DialogUnderlay"] = true;
9285 dojo.provide("dijit.DialogUnderlay");
9286
9287
9288
9289
9290
9291 dojo.declare(
9292         "dijit.DialogUnderlay",
9293         [dijit._Widget, dijit._Templated],
9294         {
9295                 // summary:
9296                 //              The component that blocks the screen behind a `dijit.Dialog`
9297                 //
9298                 // description:
9299                 //              A component used to block input behind a `dijit.Dialog`. Only a single
9300                 //              instance of this widget is created by `dijit.Dialog`, and saved as
9301                 //              a reference to be shared between all Dialogs as `dijit._underlay`
9302                 //
9303                 //              The underlay itself can be styled based on and id:
9304                 //      |       #myDialog_underlay { background-color:red; }
9305                 //
9306                 //              In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
9307                 //              suffixed with _underlay.
9308
9309                 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
9310                 // Inner div has opacity specified in CSS file.
9311                 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' dojoAttachPoint='node'></div></div>",
9312
9313                 // Parameters on creation or updatable later
9314
9315                 // dialogId: String
9316                 //              Id of the dialog.... DialogUnderlay's id is based on this id
9317                 dialogId: "",
9318
9319                 // class: String
9320                 //              This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
9321                 "class": "",
9322
9323                 attributeMap: { id: "domNode" },
9324
9325                 _setDialogIdAttr: function(id){
9326                         dojo.attr(this.node, "id", id + "_underlay");
9327                         this._set("dialogId", id);
9328                 },
9329
9330                 _setClassAttr: function(clazz){
9331                         this.node.className = "dijitDialogUnderlay " + clazz;
9332                         this._set("class", clazz);
9333                 },
9334
9335                 postCreate: function(){
9336                         // summary:
9337                         //              Append the underlay to the body
9338                         dojo.body().appendChild(this.domNode);
9339                 },
9340
9341                 layout: function(){
9342                         // summary:
9343                         //              Sets the background to the size of the viewport
9344                         //
9345                         // description:
9346                         //              Sets the background to the size of the viewport (rather than the size
9347                         //              of the document) since we need to cover the whole browser window, even
9348                         //              if the document is only a few lines long.
9349                         // tags:
9350                         //              private
9351
9352                         var is = this.node.style,
9353                                 os = this.domNode.style;
9354
9355                         // hide the background temporarily, so that the background itself isn't
9356                         // causing scrollbars to appear (might happen when user shrinks browser
9357                         // window and then we are called to resize)
9358                         os.display = "none";
9359
9360                         // then resize and show
9361                         var viewport = dojo.window.getBox();
9362                         os.top = viewport.t + "px";
9363                         os.left = viewport.l + "px";
9364                         is.width = viewport.w + "px";
9365                         is.height = viewport.h + "px";
9366                         os.display = "block";
9367                 },
9368
9369                 show: function(){
9370                         // summary:
9371                         //              Show the dialog underlay
9372                         this.domNode.style.display = "block";
9373                         this.layout();
9374                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
9375                 },
9376
9377                 hide: function(){
9378                         // summary:
9379                         //              Hides the dialog underlay
9380                         this.bgIframe.destroy();
9381                         delete this.bgIframe;
9382                         this.domNode.style.display = "none";
9383                 }
9384         }
9385 );
9386
9387 }
9388
9389 if(!dojo._hasResource["dijit.layout._ContentPaneResizeMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9390 dojo._hasResource["dijit.layout._ContentPaneResizeMixin"] = true;
9391 dojo.provide("dijit.layout._ContentPaneResizeMixin");
9392
9393
9394
9395
9396 dojo.declare("dijit.layout._ContentPaneResizeMixin", null, {
9397         // summary:
9398         //              Resize() functionality of ContentPane.   If there's a single layout widget
9399         //              child then it will call resize() with the same dimensions as the ContentPane.
9400         //              Otherwise just calls resize on each child.
9401         //
9402         //              Also implements basic startup() functionality, where starting the parent
9403         //              will start the children
9404
9405         // doLayout: Boolean
9406         //              - false - don't adjust size of children
9407         //              - true - if there is a single visible child widget, set it's size to
9408         //                              however big the ContentPane is
9409         doLayout: true,
9410
9411         // isContainer: [protected] Boolean
9412         //              Indicates that this widget acts as a "parent" to the descendant widgets.
9413         //              When the parent is started it will call startup() on the child widgets.
9414         //              See also `isLayoutContainer`.
9415         isContainer: true,
9416
9417         // isLayoutContainer: [protected] Boolean
9418         //              Indicates that this widget will call resize() on it's child widgets
9419         //              when they become visible.
9420         isLayoutContainer: true,
9421
9422         _startChildren: function(){
9423                 // summary:
9424                 //              Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
9425
9426                 // This starts all the widgets
9427                 dojo.forEach(this.getChildren(), function(child){
9428                         child.startup();
9429                         child._started = true;
9430                 });
9431         },
9432
9433         startup: function(){
9434                 // summary:
9435                 //              See `dijit.layout._LayoutWidget.startup` for description.
9436                 //              Although ContentPane doesn't extend _LayoutWidget, it does implement
9437                 //              the same API.
9438
9439                 if(this._started){ return; }
9440
9441                 var parent = dijit._Contained.prototype.getParent.call(this);
9442                 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
9443
9444                 // I need to call resize() on my child/children (when I become visible), unless
9445                 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
9446                 this._needLayout = !this._childOfLayoutWidget;
9447
9448                 this.inherited(arguments);
9449
9450                 this._startChildren();
9451
9452                 if(this._isShown()){
9453                         this._onShow();
9454                 }
9455
9456                 if(!this._childOfLayoutWidget){
9457                         // If my parent isn't a layout container, since my style *may be* width=height=100%
9458                         // or something similar (either set directly or via a CSS class),
9459                         // monitor when my size changes so that I can re-layout.
9460                         // For browsers where I can't directly monitor when my size changes,
9461                         // monitor when the viewport changes size, which *may* indicate a size change for me.
9462                         this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){
9463                                 // Using function(){} closure to ensure no arguments to resize.
9464                                 this._needLayout = !this._childOfLayoutWidget;
9465                                 this.resize();
9466                         });
9467                 }
9468         },
9469
9470         _checkIfSingleChild: function(){
9471                 // summary:
9472                 //              Test if we have exactly one visible widget as a child,
9473                 //              and if so assume that we are a container for that widget,
9474                 //              and should propagate startup() and resize() calls to it.
9475                 //              Skips over things like data stores since they aren't visible.
9476
9477                 var childNodes = dojo.query("> *", this.containerNode).filter(function(node){
9478                                 return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
9479                         }),
9480                         childWidgetNodes = childNodes.filter(function(node){
9481                                 return dojo.hasAttr(node, "data-dojo-type") || dojo.hasAttr(node, "dojoType") || dojo.hasAttr(node, "widgetId");
9482                         }),
9483                         candidateWidgets = dojo.filter(childWidgetNodes.map(dijit.byNode), function(widget){
9484                                 return widget && widget.domNode && widget.resize;
9485                         });
9486
9487                 if(
9488                         // all child nodes are widgets
9489                         childNodes.length == childWidgetNodes.length &&
9490
9491                         // all but one are invisible (like dojo.data)
9492                         candidateWidgets.length == 1
9493                 ){
9494                         this._singleChild = candidateWidgets[0];
9495                 }else{
9496                         delete this._singleChild;
9497                 }
9498
9499                 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
9500                 dojo.toggleClass(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
9501         },
9502
9503         resize: function(changeSize, resultSize){
9504                 // summary:
9505                 //              See `dijit.layout._LayoutWidget.resize` for description.
9506                 //              Although ContentPane doesn't extend _LayoutWidget, it does implement
9507                 //              the same API.
9508
9509                 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
9510                 // never called, so resize() is our trigger to do the initial href download (see [20099]).
9511                 // However, don't load href for closed TitlePanes.
9512                 if(!this._wasShown && this.open !== false){
9513                         this._onShow();
9514                 }
9515
9516                 this._resizeCalled = true;
9517
9518                 this._scheduleLayout(changeSize, resultSize);
9519         },
9520
9521         _scheduleLayout: function(changeSize, resultSize){
9522                 // summary:
9523                 //              Resize myself, and call resize() on each of my child layout widgets, either now
9524                 //              (if I'm currently visible) or when I become visible
9525                 if(this._isShown()){
9526                         this._layout(changeSize, resultSize);
9527                 }else{
9528                         this._needLayout = true;
9529                         this._changeSize = changeSize;
9530                         this._resultSize = resultSize;
9531                 }
9532         },
9533
9534         _layout: function(changeSize, resultSize){
9535                 // summary:
9536                 //              Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
9537                 //              Also, since I am a Container widget, each of my children expects me to
9538                 //              call resize() or layout() on them.
9539                 //
9540                 //              Should be called on initialization and also whenever we get new content
9541                 //              (from an href, or from set('content', ...))... but deferred until
9542                 //              the ContentPane is visible
9543
9544                 // Set margin box size, unless it wasn't specified, in which case use current size.
9545                 if(changeSize){
9546                         dojo.marginBox(this.domNode, changeSize);
9547                 }
9548
9549                 // Compute content box size of containerNode in case we [later] need to size our single child.
9550                 var cn = this.containerNode;
9551                 if(cn === this.domNode){
9552                         // If changeSize or resultSize was passed to this method and this.containerNode ==
9553                         // this.domNode then we can compute the content-box size without querying the node,
9554                         // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
9555                         var mb = resultSize || {};
9556                         dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize
9557                         if(!("h" in mb) || !("w" in mb)){
9558                                 mb = dojo.mixin(dojo.marginBox(cn), mb); // just use dojo.marginBox() to fill in missing values
9559                         }
9560                         this._contentBox = dijit.layout.marginBox2contentBox(cn, mb);
9561                 }else{
9562                         this._contentBox = dojo.contentBox(cn);
9563                 }
9564
9565                 this._layoutChildren();
9566
9567                 delete this._needLayout;
9568         },
9569         
9570         _layoutChildren: function(){
9571                 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
9572                 // of the ContentPane (rather than changing it through the set("content", ...) API.
9573                 if(this.doLayout){
9574                         this._checkIfSingleChild();
9575                 }
9576
9577                 if(this._singleChild && this._singleChild.resize){
9578                         var cb = this._contentBox || dojo.contentBox(this.containerNode);
9579
9580                         // note: if widget has padding this._contentBox will have l and t set,
9581                         // but don't pass them to resize() or it will doubly-offset the child
9582                         this._singleChild.resize({w: cb.w, h: cb.h});
9583                 }else{
9584                         // All my child widgets are independently sized (rather than matching my size),
9585                         // but I still need to call resize() on each child to make it layout.
9586                         dojo.forEach(this.getChildren(), function(widget){
9587                                 if(widget.resize){
9588                                         widget.resize();
9589                                 }
9590                         });
9591                 }
9592         },
9593
9594         _isShown: function(){
9595                 // summary:
9596                 //              Returns true if the content is currently shown.
9597                 // description:
9598                 //              If I am a child of a layout widget then it actually returns true if I've ever been visible,
9599                 //              not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
9600                 //              tree every call, and at least solves the performance problem on page load by deferring loading
9601                 //              hidden ContentPanes until they are first shown
9602
9603                 if(this._childOfLayoutWidget){
9604                         // If we are TitlePane, etc - we return that only *IF* we've been resized
9605                         if(this._resizeCalled && "open" in this){
9606                                 return this.open;
9607                         }
9608                         return this._resizeCalled;
9609                 }else if("open" in this){
9610                         return this.open;               // for TitlePane, etc.
9611                 }else{
9612                         var node = this.domNode, parent = this.domNode.parentNode;
9613                         return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !dojo.hasClass(node, "dijitHidden") &&
9614                                         parent && parent.style && (parent.style.display != 'none');
9615                 }
9616         },
9617
9618         _onShow: function(){
9619                 // summary:
9620                 //              Called when the ContentPane is made visible
9621                 // description:
9622                 //              For a plain ContentPane, this is called on initialization, from startup().
9623                 //              If the ContentPane is a hidden pane of a TabContainer etc., then it's
9624                 //              called whenever the pane is made visible.
9625                 //
9626                 //              Does layout/resize of child widget(s)
9627
9628                 if(this._needLayout){
9629                         // If a layout has been scheduled for when we become visible, do it now
9630                         this._layout(this._changeSize, this._resultSize);
9631                 }
9632
9633                 this.inherited(arguments);
9634
9635                 // Need to keep track of whether ContentPane has been shown (which is different than
9636                 // whether or not it's currently visible).
9637                 this._wasShown = true;
9638         }
9639 });
9640
9641 }
9642
9643 if(!dojo._hasResource["dojo.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9644 dojo._hasResource["dojo.html"] = true;
9645 dojo.provide("dojo.html");
9646
9647
9648 dojo.getObject("html", true, dojo);
9649
9650 // the parser might be needed..
9651 (function(){ // private scope, sort of a namespace
9652
9653         // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
9654         var idCounter = 0,
9655                 d = dojo;
9656         
9657         dojo.html._secureForInnerHtml = function(/*String*/ cont){
9658                 // summary:
9659                 //              removes !DOCTYPE and title elements from the html string.
9660                 //
9661                 //              khtml is picky about dom faults, you can't attach a style or <title> node as child of body
9662                 //              must go into head, so we need to cut out those tags
9663                 //      cont:
9664                 //              An html string for insertion into the dom
9665                 //
9666                 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
9667         };
9668
9669 /*====
9670         dojo.html._emptyNode = function(node){
9671                 // summary:
9672                 //              removes all child nodes from the given node
9673                 //      node: DOMNode
9674                 //              the parent element
9675         };
9676 =====*/
9677         dojo.html._emptyNode = dojo.empty;
9678
9679         dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
9680                 // summary:
9681                 //              inserts the given content into the given node
9682                 //      node:
9683                 //              the parent element
9684                 //      content:
9685                 //              the content to be set on the parent element.
9686                 //              This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
9687                 
9688                 // always empty
9689                 d.empty(node);
9690
9691                 if(cont) {
9692                         if(typeof cont == "string") {
9693                                 cont = d._toDom(cont, node.ownerDocument);
9694                         }
9695                         if(!cont.nodeType && d.isArrayLike(cont)) {
9696                                 // handle as enumerable, but it may shrink as we enumerate it
9697                                 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
9698                                         d.place( cont[i], node, "last");
9699                                 }
9700                         } else {
9701                                 // pass nodes, documentFragments and unknowns through to dojo.place
9702                                 d.place(cont, node, "last");
9703                         }
9704                 }
9705
9706                 // return DomNode
9707                 return node;
9708         };
9709
9710         // we wrap up the content-setting operation in a object
9711         dojo.declare("dojo.html._ContentSetter", null,
9712                 {
9713                         // node: DomNode|String
9714                         //              An node which will be the parent element that we set content into
9715                         node: "",
9716
9717                         // content: String|DomNode|DomNode[]
9718                         //              The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
9719                         content: "",
9720                         
9721                         // id: String?
9722                         //              Usually only used internally, and auto-generated with each instance
9723                         id: "",
9724
9725                         // cleanContent: Boolean
9726                         //              Should the content be treated as a full html document,
9727                         //              and the real content stripped of <html>, <body> wrapper before injection
9728                         cleanContent: false,
9729                         
9730                         // extractContent: Boolean
9731                         //              Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
9732                         extractContent: false,
9733
9734                         // parseContent: Boolean
9735                         //              Should the node by passed to the parser after the new content is set
9736                         parseContent: false,
9737
9738                         // parserScope: String
9739                         //              Flag passed to parser.  Root for attribute names to search for.   If scopeName is dojo,
9740                         //              will search for data-dojo-type (or dojoType).  For backwards compatibility
9741                         //              reasons defaults to dojo._scopeName (which is "dojo" except when
9742                         //              multi-version support is used, when it will be something like dojo16, dojo20, etc.)
9743                         parserScope: dojo._scopeName,
9744
9745                         // startup: Boolean
9746                         //              Start the child widgets after parsing them.   Only obeyed if parseContent is true.
9747                         startup: true,
9748                         
9749                         // lifecyle methods
9750                         constructor: function(/* Object */params, /* String|DomNode */node){
9751                                 //      summary:
9752                                 //              Provides a configurable, extensible object to wrap the setting on content on a node
9753                                 //              call the set() method to actually set the content..
9754  
9755                                 // the original params are mixed directly into the instance "this"
9756                                 dojo.mixin(this, params || {});
9757
9758                                 // give precedence to params.node vs. the node argument
9759                                 // and ensure its a node, not an id string
9760                                 node = this.node = dojo.byId( this.node || node );
9761         
9762                                 if(!this.id){
9763                                         this.id = [
9764                                                 "Setter",
9765                                                 (node) ? node.id || node.tagName : "",
9766                                                 idCounter++
9767                                         ].join("_");
9768                                 }
9769                         },
9770                         set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
9771                                 // summary:
9772                                 //              front-end to the set-content sequence
9773                                 //      cont:
9774                                 //              An html string, node or enumerable list of nodes for insertion into the dom
9775                                 //              If not provided, the object's content property will be used
9776                                 if(undefined !== cont){
9777                                         this.content = cont;
9778                                 }
9779                                 // in the re-use scenario, set needs to be able to mixin new configuration
9780                                 if(params){
9781                                         this._mixin(params);
9782                                 }
9783
9784                                 this.onBegin();
9785                                 this.setContent();
9786                                 this.onEnd();
9787
9788                                 return this.node;
9789                         },
9790                         setContent: function(){
9791                                 // summary:
9792                                 //              sets the content on the node
9793
9794                                 var node = this.node;
9795                                 if(!node) {
9796                                     // can't proceed
9797                                         throw new Error(this.declaredClass + ": setContent given no node");
9798                                 }
9799                                 try{
9800                                         node = dojo.html._setNodeContent(node, this.content);
9801                                 }catch(e){
9802                                         // check if a domfault occurs when we are appending this.errorMessage
9803                                         // like for instance if domNode is a UL and we try append a DIV
9804         
9805                                         // FIXME: need to allow the user to provide a content error message string
9806                                         var errMess = this.onContentError(e);
9807                                         try{
9808                                                 node.innerHTML = errMess;
9809                                         }catch(e){
9810                                                 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
9811                                         }
9812                                 }
9813                                 // always put back the node for the next method
9814                                 this.node = node; // DomNode
9815                         },
9816                         
9817                         empty: function() {
9818                                 // summary
9819                                 //      cleanly empty out existing content
9820
9821                                 // destroy any widgets from a previous run
9822                                 // NOTE: if you dont want this you'll need to empty
9823                                 // the parseResults array property yourself to avoid bad things happenning
9824                                 if(this.parseResults && this.parseResults.length) {
9825                                         dojo.forEach(this.parseResults, function(w) {
9826                                                 if(w.destroy){
9827                                                         w.destroy();
9828                                                 }
9829                                         });
9830                                         delete this.parseResults;
9831                                 }
9832                                 // this is fast, but if you know its already empty or safe, you could
9833                                 // override empty to skip this step
9834                                 dojo.html._emptyNode(this.node);
9835                         },
9836         
9837                         onBegin: function(){
9838                                 // summary
9839                                 //              Called after instantiation, but before set();
9840                                 //              It allows modification of any of the object properties
9841                                 //              - including the node and content provided - before the set operation actually takes place
9842                                 //              This default implementation checks for cleanContent and extractContent flags to
9843                                 //              optionally pre-process html string content
9844                                 var cont = this.content;
9845         
9846                                 if(dojo.isString(cont)){
9847                                         if(this.cleanContent){
9848                                                 cont = dojo.html._secureForInnerHtml(cont);
9849                                         }
9850   
9851                                         if(this.extractContent){
9852                                                 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
9853                                                 if(match){ cont = match[1]; }
9854                                         }
9855                                 }
9856
9857                                 // clean out the node and any cruft associated with it - like widgets
9858                                 this.empty();
9859                                 
9860                                 this.content = cont;
9861                                 return this.node; /* DomNode */
9862                         },
9863         
9864                         onEnd: function(){
9865                                 // summary
9866                                 //              Called after set(), when the new content has been pushed into the node
9867                                 //              It provides an opportunity for post-processing before handing back the node to the caller
9868                                 //              This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
9869                                 if(this.parseContent){
9870                                         // populates this.parseResults if you need those..
9871                                         this._parse();
9872                                 }
9873                                 return this.node; /* DomNode */
9874                         },
9875         
9876                         tearDown: function(){
9877                                 // summary
9878                                 //              manually reset the Setter instance if its being re-used for example for another set()
9879                                 // description
9880                                 //              tearDown() is not called automatically.
9881                                 //              In normal use, the Setter instance properties are simply allowed to fall out of scope
9882                                 //              but the tearDown method can be called to explicitly reset this instance.
9883                                 delete this.parseResults;
9884                                 delete this.node;
9885                                 delete this.content;
9886                         },
9887   
9888                         onContentError: function(err){
9889                                 return "Error occured setting content: " + err;
9890                         },
9891                         
9892                         _mixin: function(params){
9893                                 // mix properties/methods into the instance
9894                                 // TODO: the intention with tearDown is to put the Setter's state
9895                                 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
9896                                 // so we could do something here to move the original properties aside for later restoration
9897                                 var empty = {}, key;
9898                                 for(key in params){
9899                                         if(key in empty){ continue; }
9900                                         // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
9901                                         // .. but history shows we'll almost always guess wrong
9902                                         this[key] = params[key];
9903                                 }
9904                         },
9905                         _parse: function(){
9906                                 // summary:
9907                                 //              runs the dojo parser over the node contents, storing any results in this.parseResults
9908                                 //              Any errors resulting from parsing are passed to _onError for handling
9909
9910                                 var rootNode = this.node;
9911                                 try{
9912                                         // store the results (widgets, whatever) for potential retrieval
9913                                         var inherited = {};
9914                                         dojo.forEach(["dir", "lang", "textDir"], function(name){
9915                                                 if(this[name]){
9916                                                         inherited[name] = this[name];
9917                                                 }
9918                                         }, this);
9919                                         this.parseResults = dojo.parser.parse({
9920                                                 rootNode: rootNode,
9921                                                 noStart: !this.startup,
9922                                                 inherited: inherited,
9923                                                 scope: this.parserScope
9924                                         });
9925                                 }catch(e){
9926                                         this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
9927                                 }
9928                         },
9929   
9930                         _onError: function(type, err, consoleText){
9931                                 // summary:
9932                                 //              shows user the string that is returned by on[type]Error
9933                                 //              overide/implement on[type]Error and return your own string to customize
9934                                 var errText = this['on' + type + 'Error'].call(this, err);
9935                                 if(consoleText){
9936                                         console.error(consoleText, err);
9937                                 }else if(errText){ // a empty string won't change current content
9938                                         dojo.html._setNodeContent(this.node, errText, true);
9939                                 }
9940                         }
9941         }); // end dojo.declare()
9942
9943         dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
9944                         // summary:
9945                         //              inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
9946                         //              may be a better choice for simple HTML insertion.
9947                         // description:
9948                         //              Unless you need to use the params capabilities of this method, you should use
9949                         //              dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
9950                         //              an HTML string into the DOM, but it only handles inserting an HTML string as DOM
9951                         //              elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
9952                         //              or the other capabilities as defined by the params object for this method.
9953                         //      node:
9954                         //              the parent element that will receive the content
9955                         //      cont:
9956                         //              the content to be set on the parent element.
9957                         //              This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
9958                         //      params:
9959                         //              Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
9960                         //      example:
9961                         //              A safe string/node/nodelist content replacement/injection with hooks for extension
9962                         //              Example Usage:
9963                         //              dojo.html.set(node, "some string");
9964                         //              dojo.html.set(node, contentNode, {options});
9965                         //              dojo.html.set(node, myNode.childNodes, {options});
9966                 if(undefined == cont){
9967                         console.warn("dojo.html.set: no cont argument provided, using empty string");
9968                         cont = "";
9969                 }
9970                 if(!params){
9971                         // simple and fast
9972                         return dojo.html._setNodeContent(node, cont, true);
9973                 }else{
9974                         // more options but slower
9975                         // note the arguments are reversed in order, to match the convention for instantiation via the parser
9976                         var op = new dojo.html._ContentSetter(dojo.mixin(
9977                                         params,
9978                                         { content: cont, node: node }
9979                         ));
9980                         return op.set();
9981                 }
9982         };
9983 })();
9984
9985 }
9986
9987 if(!dojo._hasResource["dijit.layout.ContentPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9988 dojo._hasResource["dijit.layout.ContentPane"] = true;
9989 dojo.provide("dijit.layout.ContentPane");
9990
9991
9992
9993
9994
9995
9996
9997 dojo.declare(
9998         "dijit.layout.ContentPane", [dijit._Widget, dijit.layout._ContentPaneResizeMixin],
9999 {
10000         // summary:
10001         //              A widget containing an HTML fragment, specified inline
10002         //              or by uri.  Fragment may include widgets.
10003         //
10004         // description:
10005         //              This widget embeds a document fragment in the page, specified
10006         //              either by uri, javascript generated markup or DOM reference.
10007         //              Any widgets within this content are instantiated and managed,
10008         //              but laid out according to the HTML structure.  Unlike IFRAME,
10009         //              ContentPane embeds a document fragment as would be found
10010         //              inside the BODY tag of a full HTML document.  It should not
10011         //              contain the HTML, HEAD, or BODY tags.
10012         //              For more advanced functionality with scripts and
10013         //              stylesheets, see dojox.layout.ContentPane.  This widget may be
10014         //              used stand alone or as a base class for other widgets.
10015         //              ContentPane is useful as a child of other layout containers
10016         //              such as BorderContainer or TabContainer, but note that those
10017         //              widgets can contain any widget as a child.
10018         //
10019         // example:
10020         //              Some quick samples:
10021         //              To change the innerHTML: cp.set('content', '<b>new content</b>')
10022         //
10023         //              Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection))
10024         //
10025         //              To do an ajax update: cp.set('href', url)
10026
10027         // href: String
10028         //              The href of the content that displays now.
10029         //              Set this at construction if you want to load data externally when the
10030         //              pane is shown.  (Set preload=true to load it immediately.)
10031         //              Changing href after creation doesn't have any effect; Use set('href', ...);
10032         href: "",
10033
10034 /*=====
10035         // content: String || DomNode || NodeList || dijit._Widget
10036         //              The innerHTML of the ContentPane.
10037         //              Note that the initialization parameter / argument to set("content", ...)
10038         //              can be a String, DomNode, Nodelist, or _Widget.
10039         content: "",
10040 =====*/
10041
10042         // extractContent: Boolean
10043         //              Extract visible content from inside of <body> .... </body>.
10044         //              I.e., strip <html> and <head> (and it's contents) from the href
10045         extractContent: false,
10046
10047         // parseOnLoad: Boolean
10048         //              Parse content and create the widgets, if any.
10049         parseOnLoad: true,
10050
10051         // parserScope: String
10052         //              Flag passed to parser.  Root for attribute names to search for.   If scopeName is dojo,
10053         //              will search for data-dojo-type (or dojoType).  For backwards compatibility
10054         //              reasons defaults to dojo._scopeName (which is "dojo" except when
10055         //              multi-version support is used, when it will be something like dojo16, dojo20, etc.)
10056         parserScope: dojo._scopeName,
10057
10058         // preventCache: Boolean
10059         //              Prevent caching of data from href's by appending a timestamp to the href.
10060         preventCache: false,
10061
10062         // preload: Boolean
10063         //              Force load of data on initialization even if pane is hidden.
10064         preload: false,
10065
10066         // refreshOnShow: Boolean
10067         //              Refresh (re-download) content when pane goes from hidden to shown
10068         refreshOnShow: false,
10069
10070         // loadingMessage: String
10071         //              Message that shows while downloading
10072         loadingMessage: "<span class='dijitContentPaneLoading'>${loadingState}</span>",
10073
10074         // errorMessage: String
10075         //              Message that shows if an error occurs
10076         errorMessage: "<span class='dijitContentPaneError'>${errorState}</span>",
10077
10078         // isLoaded: [readonly] Boolean
10079         //              True if the ContentPane has data in it, either specified
10080         //              during initialization (via href or inline content), or set
10081         //              via set('content', ...) / set('href', ...)
10082         //
10083         //              False if it doesn't have any content, or if ContentPane is
10084         //              still in the process of downloading href.
10085         isLoaded: false,
10086
10087         baseClass: "dijitContentPane",
10088
10089         // ioArgs: Object
10090         //              Parameters to pass to xhrGet() request, for example:
10091         // |    <div dojoType="dijit.layout.ContentPane" href="./bar" ioArgs="{timeout: 500}">
10092         ioArgs: {},
10093
10094         // onLoadDeferred: [readonly] dojo.Deferred
10095         //              This is the `dojo.Deferred` returned by set('href', ...) and refresh().
10096         //              Calling onLoadDeferred.addCallback() or addErrback() registers your
10097         //              callback to be called only once, when the prior set('href', ...) call or
10098         //              the initial href parameter to the constructor finishes loading.
10099         //
10100         //              This is different than an onLoad() handler which gets called any time any href
10101         //              or content is loaded.
10102         onLoadDeferred: null,
10103
10104         // Override _Widget's attributeMap because we don't want the title attribute (used to specify
10105         // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
10106         // entire pane.
10107         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
10108                 title: []
10109         }),
10110
10111         // Flag to parser that I'll parse my contents, so it shouldn't.
10112         stopParser: true,
10113
10114         // template: [private] Boolean
10115         //              Flag from the parser that this ContentPane is inside a template
10116         //              so the contents are pre-parsed.
10117         // (TODO: this declaration can be commented out in 2.0)
10118         template: false,
10119
10120         create: function(params, srcNodeRef){
10121                 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
10122                 // processed in the same way as contents set via set("content", ...), calling the parser etc.
10123                 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
10124                 if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
10125                         var df = dojo.doc.createDocumentFragment();
10126                         srcNodeRef = dojo.byId(srcNodeRef)
10127                         while(srcNodeRef.firstChild){
10128                                 df.appendChild(srcNodeRef.firstChild);
10129                         }
10130                         params = dojo.delegate(params, {content: df});
10131                 }
10132                 this.inherited(arguments, [params, srcNodeRef]);
10133         },
10134
10135         postMixInProperties: function(){
10136                 this.inherited(arguments);
10137                 var messages = dojo.i18n.getLocalization("dijit", "loading", this.lang);
10138                 this.loadingMessage = dojo.string.substitute(this.loadingMessage, messages);
10139                 this.errorMessage = dojo.string.substitute(this.errorMessage, messages);
10140         },
10141
10142         buildRendering: function(){
10143                 this.inherited(arguments);
10144
10145                 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
10146                 // For subclasses of ContentPane that do have a template, does nothing.
10147                 if(!this.containerNode){
10148                         this.containerNode = this.domNode;
10149                 }
10150
10151                 // remove the title attribute so it doesn't show up when hovering
10152                 // over a node  (TODO: remove in 2.0, no longer needed after #11490)
10153                 this.domNode.title = "";
10154
10155                 if(!dojo.attr(this.domNode,"role")){
10156                         dijit.setWaiRole(this.domNode, "group");
10157                 }
10158         },
10159
10160         _startChildren: function(){
10161                 // summary:
10162                 //              Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
10163
10164                 // This starts all the widgets
10165                 this.inherited(arguments);
10166
10167                 // And this catches stuff like dojo.dnd.Source
10168                 if(this._contentSetter){
10169                         dojo.forEach(this._contentSetter.parseResults, function(obj){
10170                                 if(!obj._started && !obj._destroyed && dojo.isFunction(obj.startup)){
10171                                         obj.startup();
10172                                         obj._started = true;
10173                                 }
10174                         }, this);
10175                 }
10176         },
10177
10178         setHref: function(/*String|Uri*/ href){
10179                 // summary:
10180                 //              Deprecated.   Use set('href', ...) instead.
10181                 dojo.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
10182                 return this.set("href", href);
10183         },
10184         _setHrefAttr: function(/*String|Uri*/ href){
10185                 // summary:
10186                 //              Hook so set("href", ...) works.
10187                 // description:
10188                 //              Reset the (external defined) content of this pane and replace with new url
10189                 //              Note: It delays the download until widget is shown if preload is false.
10190                 //      href:
10191                 //              url to the page you want to get, must be within the same domain as your mainpage
10192
10193                 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
10194                 this.cancel();
10195
10196                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
10197                 this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
10198
10199                 this._set("href", href);
10200
10201                 // _setHrefAttr() is called during creation and by the user, after creation.
10202                 // Assuming preload == false, only in the second case do we actually load the URL;
10203                 // otherwise it's done in startup(), and only if this widget is shown.
10204                 if(this.preload || (this._created && this._isShown())){
10205                         this._load();
10206                 }else{
10207                         // Set flag to indicate that href needs to be loaded the next time the
10208                         // ContentPane is made visible
10209                         this._hrefChanged = true;
10210                 }
10211
10212                 return this.onLoadDeferred;             // dojo.Deferred
10213         },
10214
10215         setContent: function(/*String|DomNode|Nodelist*/data){
10216                 // summary:
10217                 //              Deprecated.   Use set('content', ...) instead.
10218                 dojo.deprecated("dijit.layout.ContentPane.setContent() is deprecated.  Use set('content', ...) instead.", "", "2.0");
10219                 this.set("content", data);
10220         },
10221         _setContentAttr: function(/*String|DomNode|Nodelist*/data){
10222                 // summary:
10223                 //              Hook to make set("content", ...) work.
10224                 //              Replaces old content with data content, include style classes from old content
10225                 //      data:
10226                 //              the new Content may be String, DomNode or NodeList
10227                 //
10228                 //              if data is a NodeList (or an array of nodes) nodes are copied
10229                 //              so you can import nodes from another document implicitly
10230
10231                 // clear href so we can't run refresh and clear content
10232                 // refresh should only work if we downloaded the content
10233                 this._set("href", "");
10234
10235                 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
10236                 this.cancel();
10237
10238                 // Even though user is just setting content directly, still need to define an onLoadDeferred
10239                 // because the _onLoadHandler() handler is still getting called from setContent()
10240                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
10241                 if(this._created){
10242                         // For back-compat reasons, call onLoad() for set('content', ...)
10243                         // calls but not for content specified in srcNodeRef (ie: <div dojoType=ContentPane>...</div>)
10244                         // or as initialization parameter (ie: new ContentPane({content: ...})
10245                         this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
10246                 }
10247
10248                 this._setContent(data || "");
10249
10250                 this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
10251
10252                 return this.onLoadDeferred;     // dojo.Deferred
10253         },
10254         _getContentAttr: function(){
10255                 // summary:
10256                 //              Hook to make get("content") work
10257                 return this.containerNode.innerHTML;
10258         },
10259
10260         cancel: function(){
10261                 // summary:
10262                 //              Cancels an in-flight download of content
10263                 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
10264                         this._xhrDfd.cancel();
10265                 }
10266                 delete this._xhrDfd; // garbage collect
10267
10268                 this.onLoadDeferred = null;
10269         },
10270
10271         uninitialize: function(){
10272                 if(this._beingDestroyed){
10273                         this.cancel();
10274                 }
10275                 this.inherited(arguments);
10276         },
10277
10278         destroyRecursive: function(/*Boolean*/ preserveDom){
10279                 // summary:
10280                 //              Destroy the ContentPane and its contents
10281
10282                 // if we have multiple controllers destroying us, bail after the first
10283                 if(this._beingDestroyed){
10284                         return;
10285                 }
10286                 this.inherited(arguments);
10287         },
10288
10289         _onShow: function(){
10290                 // summary:
10291                 //              Called when the ContentPane is made visible
10292                 // description:
10293                 //              For a plain ContentPane, this is called on initialization, from startup().
10294                 //              If the ContentPane is a hidden pane of a TabContainer etc., then it's
10295                 //              called whenever the pane is made visible.
10296                 //
10297                 //              Does necessary processing, including href download and layout/resize of
10298                 //              child widget(s)
10299
10300                 this.inherited(arguments);
10301
10302                 if(this.href){
10303                         if(!this._xhrDfd && // if there's an href that isn't already being loaded
10304                                 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
10305                         ){
10306                                 return this.refresh();  // If child has an href, promise that fires when the load is complete
10307                         }
10308                 }
10309         },
10310
10311         refresh: function(){
10312                 // summary:
10313                 //              [Re]download contents of href and display
10314                 // description:
10315                 //              1. cancels any currently in-flight requests
10316                 //              2. posts "loading..." message
10317                 //              3. sends XHR to download new data
10318
10319                 // Cancel possible prior in-flight request
10320                 this.cancel();
10321
10322                 this.onLoadDeferred = new dojo.Deferred(dojo.hitch(this, "cancel"));
10323                 this.onLoadDeferred.addCallback(dojo.hitch(this, "onLoad"));
10324                 this._load();
10325                 return this.onLoadDeferred;             // If child has an href, promise that fires when refresh is complete
10326         },
10327
10328         _load: function(){
10329                 // summary:
10330                 //              Load/reload the href specified in this.href
10331
10332                 // display loading message
10333                 this._setContent(this.onDownloadStart(), true);
10334
10335                 var self = this;
10336                 var getArgs = {
10337                         preventCache: (this.preventCache || this.refreshOnShow),
10338                         url: this.href,
10339                         handleAs: "text"
10340                 };
10341                 if(dojo.isObject(this.ioArgs)){
10342                         dojo.mixin(getArgs, this.ioArgs);
10343                 }
10344
10345                 var hand = (this._xhrDfd = (this.ioMethod || dojo.xhrGet)(getArgs));
10346
10347                 hand.addCallback(function(html){
10348                         try{
10349                                 self._isDownloaded = true;
10350                                 self._setContent(html, false);
10351                                 self.onDownloadEnd();
10352                         }catch(err){
10353                                 self._onError('Content', err); // onContentError
10354                         }
10355                         delete self._xhrDfd;
10356                         return html;
10357                 });
10358
10359                 hand.addErrback(function(err){
10360                         if(!hand.canceled){
10361                                 // show error message in the pane
10362                                 self._onError('Download', err); // onDownloadError
10363                         }
10364                         delete self._xhrDfd;
10365                         return err;
10366                 });
10367
10368                 // Remove flag saying that a load is needed
10369                 delete this._hrefChanged;
10370         },
10371
10372         _onLoadHandler: function(data){
10373                 // summary:
10374                 //              This is called whenever new content is being loaded
10375                 this._set("isLoaded", true);
10376                 try{
10377                         this.onLoadDeferred.callback(data);
10378                 }catch(e){
10379                         console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
10380                 }
10381         },
10382
10383         _onUnloadHandler: function(){
10384                 // summary:
10385                 //              This is called whenever the content is being unloaded
10386                 this._set("isLoaded", false);
10387                 try{
10388                         this.onUnload();
10389                 }catch(e){
10390                         console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
10391                 }
10392         },
10393
10394         destroyDescendants: function(){
10395                 // summary:
10396                 //              Destroy all the widgets inside the ContentPane and empty containerNode
10397
10398                 // Make sure we call onUnload (but only when the ContentPane has real content)
10399                 if(this.isLoaded){
10400                         this._onUnloadHandler();
10401                 }
10402
10403                 // Even if this.isLoaded == false there might still be a "Loading..." message
10404                 // to erase, so continue...
10405
10406                 // For historical reasons we need to delete all widgets under this.containerNode,
10407                 // even ones that the user has created manually.
10408                 var setter = this._contentSetter;
10409                 dojo.forEach(this.getChildren(), function(widget){
10410                         if(widget.destroyRecursive){
10411                                 widget.destroyRecursive();
10412                         }
10413                 });
10414                 if(setter){
10415                         // Most of the widgets in setter.parseResults have already been destroyed, but
10416                         // things like Menu that have been moved to <body> haven't yet
10417                         dojo.forEach(setter.parseResults, function(widget){
10418                                 if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == dojo.body()){
10419                                         widget.destroyRecursive();
10420                                 }
10421                         });
10422                         delete setter.parseResults;
10423                 }
10424
10425                 // And then clear away all the DOM nodes
10426                 dojo.html._emptyNode(this.containerNode);
10427
10428                 // Delete any state information we have about current contents
10429                 delete this._singleChild;
10430         },
10431
10432         _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
10433                 // summary:
10434                 //              Insert the content into the container node
10435
10436                 // first get rid of child widgets
10437                 this.destroyDescendants();
10438
10439                 // dojo.html.set will take care of the rest of the details
10440                 // we provide an override for the error handling to ensure the widget gets the errors
10441                 // configure the setter instance with only the relevant widget instance properties
10442                 // NOTE: unless we hook into attr, or provide property setters for each property,
10443                 // we need to re-configure the ContentSetter with each use
10444                 var setter = this._contentSetter;
10445                 if(! (setter && setter instanceof dojo.html._ContentSetter)){
10446                         setter = this._contentSetter = new dojo.html._ContentSetter({
10447                                 node: this.containerNode,
10448                                 _onError: dojo.hitch(this, this._onError),
10449                                 onContentError: dojo.hitch(this, function(e){
10450                                         // fires if a domfault occurs when we are appending this.errorMessage
10451                                         // like for instance if domNode is a UL and we try append a DIV
10452                                         var errMess = this.onContentError(e);
10453                                         try{
10454                                                 this.containerNode.innerHTML = errMess;
10455                                         }catch(e){
10456                                                 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
10457                                         }
10458                                 })/*,
10459                                 _onError */
10460                         });
10461                 };
10462
10463                 var setterParams = dojo.mixin({
10464                         cleanContent: this.cleanContent,
10465                         extractContent: this.extractContent,
10466                         parseContent: this.parseOnLoad,
10467                         parserScope: this.parserScope,
10468                         startup: false,
10469                         dir: this.dir,
10470                         lang: this.lang
10471                 }, this._contentSetterParams || {});
10472
10473                 setter.set( (dojo.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
10474
10475                 // setter params must be pulled afresh from the ContentPane each time
10476                 delete this._contentSetterParams;
10477
10478                 if(this.doLayout){
10479                         this._checkIfSingleChild();
10480                 }
10481
10482                 if(!isFakeContent){
10483                         if(this._started){
10484                                 // Startup each top level child widget (and they will start their children, recursively)
10485                                 this._startChildren();
10486         
10487                                 // Call resize() on each of my child layout widgets,
10488                                 // or resize() on my single child layout widget...
10489                                 // either now (if I'm currently visible) or when I become visible
10490                                 this._scheduleLayout();
10491                         }
10492
10493                         this._onLoadHandler(cont);
10494                 }
10495         },
10496
10497         _onError: function(type, err, consoleText){
10498                 this.onLoadDeferred.errback(err);
10499
10500                 // shows user the string that is returned by on[type]Error
10501                 // override on[type]Error and return your own string to customize
10502                 var errText = this['on' + type + 'Error'].call(this, err);
10503                 if(consoleText){
10504                         console.error(consoleText, err);
10505                 }else if(errText){// a empty string won't change current content
10506                         this._setContent(errText, true);
10507                 }
10508         },
10509
10510         // EVENT's, should be overide-able
10511         onLoad: function(data){
10512                 // summary:
10513                 //              Event hook, is called after everything is loaded and widgetified
10514                 // tags:
10515                 //              callback
10516         },
10517
10518         onUnload: function(){
10519                 // summary:
10520                 //              Event hook, is called before old content is cleared
10521                 // tags:
10522                 //              callback
10523         },
10524
10525         onDownloadStart: function(){
10526                 // summary:
10527                 //              Called before download starts.
10528                 // description:
10529                 //              The string returned by this function will be the html
10530                 //              that tells the user we are loading something.
10531                 //              Override with your own function if you want to change text.
10532                 // tags:
10533                 //              extension
10534                 return this.loadingMessage;
10535         },
10536
10537         onContentError: function(/*Error*/ error){
10538                 // summary:
10539                 //              Called on DOM faults, require faults etc. in content.
10540                 //
10541                 //              In order to display an error message in the pane, return
10542                 //              the error message from this method, as an HTML string.
10543                 //
10544                 //              By default (if this method is not overriden), it returns
10545                 //              nothing, so the error message is just printed to the console.
10546                 // tags:
10547                 //              extension
10548         },
10549
10550         onDownloadError: function(/*Error*/ error){
10551                 // summary:
10552                 //              Called when download error occurs.
10553                 //
10554                 //              In order to display an error message in the pane, return
10555                 //              the error message from this method, as an HTML string.
10556                 //
10557                 //              Default behavior (if this method is not overriden) is to display
10558                 //              the error message inside the pane.
10559                 // tags:
10560                 //              extension
10561                 return this.errorMessage;
10562         },
10563
10564         onDownloadEnd: function(){
10565                 // summary:
10566                 //              Called when download is finished.
10567                 // tags:
10568                 //              callback
10569         }
10570 });
10571
10572 }
10573
10574 if(!dojo._hasResource["dijit.TooltipDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10575 dojo._hasResource["dijit.TooltipDialog"] = true;
10576 dojo.provide("dijit.TooltipDialog");
10577
10578
10579
10580
10581
10582
10583 dojo.declare(
10584                 "dijit.TooltipDialog",
10585                 [dijit.layout.ContentPane, dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin],
10586                 {
10587                         // summary:
10588                         //              Pops up a dialog that appears like a Tooltip
10589
10590                         // title: String
10591                         //              Description of tooltip dialog (required for a11y)
10592                         title: "",
10593
10594                         // doLayout: [protected] Boolean
10595                         //              Don't change this parameter from the default value.
10596                         //              This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
10597                         //              is never a child of a layout container, nor can you specify the size of
10598                         //              TooltipDialog in order to control the size of an inner widget.
10599                         doLayout: false,
10600
10601                         // autofocus: Boolean
10602                         //              A Toggle to modify the default focus behavior of a Dialog, which
10603                         //              is to focus on the first dialog element after opening the dialog.
10604                         //              False will disable autofocusing. Default: true
10605                         autofocus: true,
10606
10607                         // baseClass: [protected] String
10608                         //              The root className to use for the various states of this widget
10609                         baseClass: "dijitTooltipDialog",
10610
10611                         // _firstFocusItem: [private] [readonly] DomNode
10612                         //              The pointer to the first focusable node in the dialog.
10613                         //              Set by `dijit._DialogMixin._getFocusItems`.
10614                         _firstFocusItem: null,
10615
10616                         // _lastFocusItem: [private] [readonly] DomNode
10617                         //              The pointer to which node has focus prior to our dialog.
10618                         //              Set by `dijit._DialogMixin._getFocusItems`.
10619                         _lastFocusItem: null,
10620
10621                         templateString: dojo.cache("dijit", "templates/TooltipDialog.html", "<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" dojoAttachPoint=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\"></div>\n</div>\n"),
10622
10623                         _setTitleAttr: function(/*String*/ title){
10624                                 this.containerNode.title = title;
10625                                 this._set("title", title)
10626                         },
10627
10628                         postCreate: function(){
10629                                 this.inherited(arguments);
10630                                 this.connect(this.containerNode, "onkeypress", "_onKey");
10631                         },
10632
10633                         orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ corner){
10634                                 // summary:
10635                                 //              Configure widget to be displayed in given position relative to the button.
10636                                 //              This is called from the dijit.popup code, and should not be called
10637                                 //              directly.
10638                                 // tags:
10639                                 //              protected
10640                                 var newC = "dijitTooltipAB" + (corner.charAt(1) == 'L' ? "Left" : "Right")
10641                                                 + " dijitTooltip"
10642                                                 + (corner.charAt(0) == 'T' ? "Below" : "Above");
10643                                 
10644                                 dojo.replaceClass(this.domNode, newC, this._currentOrientClass || "");
10645                                 this._currentOrientClass = newC;
10646                         },
10647
10648                         focus: function(){
10649                                 // summary:
10650                                 //              Focus on first field
10651                                 this._getFocusItems(this.containerNode);
10652                                 dijit.focus(this._firstFocusItem);
10653                         },
10654
10655                         onOpen: function(/*Object*/ pos){
10656                                 // summary:
10657                                 //              Called when dialog is displayed.
10658                                 //              This is called from the dijit.popup code, and should not be called directly.
10659                                 // tags:
10660                                 //              protected
10661
10662                                 this.orient(this.domNode,pos.aroundCorner, pos.corner);
10663                                 this._onShow(); // lazy load trigger
10664                         },
10665
10666                         onClose: function(){
10667                                 // summary:
10668                                 //              Called when dialog is hidden.
10669                                 //              This is called from the dijit.popup code, and should not be called directly.
10670                                 // tags:
10671                                 //              protected
10672                                 this.onHide();
10673                         },
10674
10675                         _onKey: function(/*Event*/ evt){
10676                                 // summary:
10677                                 //              Handler for keyboard events
10678                                 // description:
10679                                 //              Keep keyboard focus in dialog; close dialog on escape key
10680                                 // tags:
10681                                 //              private
10682
10683                                 var node = evt.target;
10684                                 var dk = dojo.keys;
10685                                 if(evt.charOrCode === dk.TAB){
10686                                         this._getFocusItems(this.containerNode);
10687                                 }
10688                                 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
10689                                 if(evt.charOrCode == dk.ESCAPE){
10690                                         // Use setTimeout to avoid crash on IE, see #10396.
10691                                         setTimeout(dojo.hitch(this, "onCancel"), 0);
10692                                         dojo.stopEvent(evt);
10693                                 }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
10694                                         if(!singleFocusItem){
10695                                                 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
10696                                         }
10697                                         dojo.stopEvent(evt);
10698                                 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
10699                                         if(!singleFocusItem){
10700                                                 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
10701                                         }
10702                                         dojo.stopEvent(evt);
10703                                 }else if(evt.charOrCode === dk.TAB){
10704                                         // we want the browser's default tab handling to move focus
10705                                         // but we don't want the tab to propagate upwards
10706                                         evt.stopPropagation();
10707                                 }
10708                         }
10709                 }
10710         );
10711
10712 }
10713
10714 if(!dojo._hasResource["dijit.Dialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
10715 dojo._hasResource["dijit.Dialog"] = true;
10716 dojo.provide("dijit.Dialog");
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732 // dijit/TooltipDialog required for back-compat.  TODO: remove in 2.0
10733
10734 /*=====
10735 dijit._underlay = function(kwArgs){
10736         // summary:
10737         //              A shared instance of a `dijit.DialogUnderlay`
10738         //
10739         // description:
10740         //              A shared instance of a `dijit.DialogUnderlay` created and
10741         //              used by `dijit.Dialog`, though never created until some Dialog
10742         //              or subclass thereof is shown.
10743 };
10744 =====*/
10745 dojo.declare(
10746         "dijit._DialogBase",
10747         [dijit._Templated, dijit.form._FormMixin, dijit._DialogMixin, dijit._CssStateMixin],
10748         {
10749                 // summary:
10750                 //              A modal dialog Widget
10751                 //
10752                 // description:
10753                 //              Pops up a modal dialog window, blocking access to the screen
10754                 //              and also graying out the screen Dialog is extended from
10755                 //              ContentPane so it supports all the same parameters (href, etc.)
10756                 //
10757                 // example:
10758                 // |    <div dojoType="dijit.Dialog" href="test.html"></div>
10759                 //
10760                 // example:
10761                 // |    var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
10762                 // |    dojo.body().appendChild(foo.domNode);
10763                 // |    foo.startup();
10764
10765                 templateString: dojo.cache("dijit", "templates/Dialog.html", "<div class=\"dijitDialog\" role=\"dialog\" aria-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=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\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"),
10766                 
10767                 baseClass: "dijitDialog",
10768                 
10769                 cssStateNodes: {
10770                         closeButtonNode: "dijitDialogCloseIcon"
10771                 },
10772
10773                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
10774                         title: [
10775                                 { node: "titleNode", type: "innerHTML" },
10776                                 { node: "titleBar", type: "attribute" }
10777                         ],
10778                         "aria-describedby":""
10779                 }),
10780
10781                 // open: [readonly] Boolean
10782                 //              True if Dialog is currently displayed on screen.
10783                 open: false,
10784
10785                 // duration: Integer
10786                 //              The time in milliseconds it takes the dialog to fade in and out
10787                 duration: dijit.defaultDuration,
10788
10789                 // refocus: Boolean
10790                 //              A Toggle to modify the default focus behavior of a Dialog, which
10791                 //              is to re-focus the element which had focus before being opened.
10792                 //              False will disable refocusing. Default: true
10793                 refocus: true,
10794
10795                 // autofocus: Boolean
10796                 //              A Toggle to modify the default focus behavior of a Dialog, which
10797                 //              is to focus on the first dialog element after opening the dialog.
10798                 //              False will disable autofocusing. Default: true
10799                 autofocus: true,
10800
10801                 // _firstFocusItem: [private readonly] DomNode
10802                 //              The pointer to the first focusable node in the dialog.
10803                 //              Set by `dijit._DialogMixin._getFocusItems`.
10804                 _firstFocusItem: null,
10805
10806                 // _lastFocusItem: [private readonly] DomNode
10807                 //              The pointer to which node has focus prior to our dialog.
10808                 //              Set by `dijit._DialogMixin._getFocusItems`.
10809                 _lastFocusItem: null,
10810
10811                 // doLayout: [protected] Boolean
10812                 //              Don't change this parameter from the default value.
10813                 //              This ContentPane parameter doesn't make sense for Dialog, since Dialog
10814                 //              is never a child of a layout container, nor can you specify the size of
10815                 //              Dialog in order to control the size of an inner widget.
10816                 doLayout: false,
10817
10818                 // draggable: Boolean
10819                 //              Toggles the moveable aspect of the Dialog. If true, Dialog
10820                 //              can be dragged by it's title. If false it will remain centered
10821                 //              in the viewport.
10822                 draggable: true,
10823
10824                 //aria-describedby: String
10825                 //              Allows the user to add an aria-describedby attribute onto the dialog.   The value should
10826                 //              be the id of the container element of text that describes the dialog purpose (usually
10827                 //              the first text in the dialog).
10828                 //              <div dojoType="dijit.Dialog" aria-describedby="intro" .....>
10829                 //                      <div id="intro">Introductory text</div>
10830                 //                      <div>rest of dialog contents</div>
10831                 //              </div>
10832                 "aria-describedby":"",
10833
10834                 postMixInProperties: function(){
10835                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
10836                         dojo.mixin(this, _nlsResources);
10837                         this.inherited(arguments);
10838                 },
10839
10840                 postCreate: function(){
10841                         dojo.style(this.domNode, {
10842                                 display: "none",
10843                                 position:"absolute"
10844                         });
10845                         dojo.body().appendChild(this.domNode);
10846
10847                         this.inherited(arguments);
10848
10849                         this.connect(this, "onExecute", "hide");
10850                         this.connect(this, "onCancel", "hide");
10851                         this._modalconnects = [];
10852                 },
10853
10854                 onLoad: function(){
10855                         // summary:
10856                         //              Called when data has been loaded from an href.
10857                         //              Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
10858                         //              but should *not* be overridden.
10859                         // tags:
10860                         //              callback
10861
10862                         // when href is specified we need to reposition the dialog after the data is loaded
10863                         // and find the focusable elements
10864                         this._position();
10865                         if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
10866                                 this._getFocusItems(this.domNode);
10867                                 dijit.focus(this._firstFocusItem);
10868                         }
10869                         this.inherited(arguments);
10870                 },
10871
10872                 _endDrag: function(e){
10873                         // summary:
10874                         //              Called after dragging the Dialog. Saves the position of the dialog in the viewport.
10875                         // tags:
10876                         //              private
10877                         if(e && e.node && e.node === this.domNode){
10878                                 this._relativePosition = dojo.position(e.node);
10879                         }
10880                 },
10881
10882                 _setup: function(){
10883                         // summary:
10884                         //              Stuff we need to do before showing the Dialog for the first
10885                         //              time (but we defer it until right beforehand, for
10886                         //              performance reasons).
10887                         // tags:
10888                         //              private
10889
10890                         var node = this.domNode;
10891
10892                         if(this.titleBar && this.draggable){
10893                                 this._moveable = (dojo.isIE == 6) ?
10894                                         new dojo.dnd.TimedMoveable(node, { handle: this.titleBar }) :   // prevent overload, see #5285
10895                                         new dojo.dnd.Moveable(node, { handle: this.titleBar, timeout: 0 });
10896                                 this._dndListener = dojo.subscribe("/dnd/move/stop",this,"_endDrag");
10897                         }else{
10898                                 dojo.addClass(node,"dijitDialogFixed");
10899                         }
10900
10901                         this.underlayAttrs = {
10902                                 dialogId: this.id,
10903                                 "class": dojo.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
10904                         };
10905                 },
10906
10907                 _size: function(){
10908                         // summary:
10909                         //              If necessary, shrink dialog contents so dialog fits in viewport
10910                         // tags:
10911                         //              private
10912
10913                         this._checkIfSingleChild();
10914
10915                         // If we resized the dialog contents earlier, reset them back to original size, so
10916                         // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
10917                         // Need to do this before the dojo.marginBox(this.domNode) call below.
10918                         if(this._singleChild){
10919                                 if(this._singleChildOriginalStyle){
10920                                         this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
10921                                 }
10922                                 delete this._singleChildOriginalStyle;
10923                         }else{
10924                                 dojo.style(this.containerNode, {
10925                                         width:"auto",
10926                                         height:"auto"
10927                                 });
10928                         }
10929
10930                         var mb = dojo._getMarginSize(this.domNode);
10931                         var viewport = dojo.window.getBox();
10932                         if(mb.w >= viewport.w || mb.h >= viewport.h){
10933                                 // Reduce size of dialog contents so that dialog fits in viewport
10934
10935                                 var w = Math.min(mb.w, Math.floor(viewport.w * 0.75)),
10936                                         h = Math.min(mb.h, Math.floor(viewport.h * 0.75));
10937
10938                                 if(this._singleChild && this._singleChild.resize){
10939                                         this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
10940                                         this._singleChild.resize({w: w, h: h});
10941                                 }else{
10942                                         dojo.style(this.containerNode, {
10943                                                 width: w + "px",
10944                                                 height: h + "px",
10945                                                 overflow: "auto",
10946                                                 position: "relative"    // workaround IE bug moving scrollbar or dragging dialog
10947                                         });
10948                                 }
10949                         }else{
10950                                 if(this._singleChild && this._singleChild.resize){
10951                                         this._singleChild.resize();
10952                                 }
10953                         }
10954                 },
10955
10956                 _position: function(){
10957                         // summary:
10958                         //              Position modal dialog in the viewport. If no relative offset
10959                         //              in the viewport has been determined (by dragging, for instance),
10960                         //              center the node. Otherwise, use the Dialog's stored relative offset,
10961                         //              and position the node to top: left: values based on the viewport.
10962                         // tags:
10963                         //              private
10964                         if(!dojo.hasClass(dojo.body(),"dojoMove")){
10965                                 var node = this.domNode,
10966                                         viewport = dojo.window.getBox(),
10967                                         p = this._relativePosition,
10968                                         bb = p ? null : dojo._getBorderBox(node),
10969                                         l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
10970                                         t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
10971                                 ;
10972                                 dojo.style(node,{
10973                                         left: l + "px",
10974                                         top: t + "px"
10975                                 });
10976                         }
10977                 },
10978
10979                 _onKey: function(/*Event*/ evt){
10980                         // summary:
10981                         //              Handles the keyboard events for accessibility reasons
10982                         // tags:
10983                         //              private
10984
10985                         if(evt.charOrCode){
10986                                 var dk = dojo.keys;
10987                                 var node = evt.target;
10988                                 if(evt.charOrCode === dk.TAB){
10989                                         this._getFocusItems(this.domNode);
10990                                 }
10991                                 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
10992                                 // see if we are shift-tabbing from first focusable item on dialog
10993                                 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === dk.TAB){
10994                                         if(!singleFocusItem){
10995                                                 dijit.focus(this._lastFocusItem); // send focus to last item in dialog
10996                                         }
10997                                         dojo.stopEvent(evt);
10998                                 }else if(node == this._lastFocusItem && evt.charOrCode === dk.TAB && !evt.shiftKey){
10999                                         if(!singleFocusItem){
11000                                                 dijit.focus(this._firstFocusItem); // send focus to first item in dialog
11001                                         }
11002                                         dojo.stopEvent(evt);
11003                                 }else{
11004                                         // see if the key is for the dialog
11005                                         while(node){
11006                                                 if(node == this.domNode || dojo.hasClass(node, "dijitPopup")){
11007                                                         if(evt.charOrCode == dk.ESCAPE){
11008                                                                 this.onCancel();
11009                                                         }else{
11010                                                                 return; // just let it go
11011                                                         }
11012                                                 }
11013                                                 node = node.parentNode;
11014                                         }
11015                                         // this key is for the disabled document window
11016                                         if(evt.charOrCode !== dk.TAB){ // allow tabbing into the dialog for a11y
11017                                                 dojo.stopEvent(evt);
11018                                         // opera won't tab to a div
11019                                         }else if(!dojo.isOpera){
11020                                                 try{
11021                                                         this._firstFocusItem.focus();
11022                                                 }catch(e){ /*squelch*/ }
11023                                         }
11024                                 }
11025                         }
11026                 },
11027
11028                 show: function(){
11029                         // summary:
11030                         //              Display the dialog
11031                         // returns: dojo.Deferred
11032                         //              Deferred object that resolves when the display animation is complete
11033
11034                         if(this.open){ return; }
11035
11036                         if(!this._started){
11037                                 this.startup();
11038                         }
11039
11040                         // first time we show the dialog, there's some initialization stuff to do
11041                         if(!this._alreadyInitialized){
11042                                 this._setup();
11043                                 this._alreadyInitialized=true;
11044                         }
11045
11046                         if(this._fadeOutDeferred){
11047                                 this._fadeOutDeferred.cancel();
11048                         }
11049
11050                         this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
11051                         this._modalconnects.push(dojo.connect(window, "onresize", this, function(){
11052                                 // IE gives spurious resize events and can actually get stuck
11053                                 // in an infinite loop if we don't ignore them
11054                                 var viewport = dojo.window.getBox();
11055                                 if(!this._oldViewport ||
11056                                                 viewport.h != this._oldViewport.h ||
11057                                                 viewport.w != this._oldViewport.w){
11058                                         this.layout();
11059                                         this._oldViewport = viewport;
11060                                 }
11061                         }));
11062                         this._modalconnects.push(dojo.connect(this.domNode, "onkeypress", this, "_onKey"));
11063
11064                         dojo.style(this.domNode, {
11065                                 opacity:0,
11066                                 display:""
11067                         });
11068
11069                         this._set("open", true);
11070                         this._onShow(); // lazy load trigger
11071
11072                         this._size();
11073                         this._position();
11074
11075                         // fade-in Animation object, setup below
11076                         var fadeIn;
11077
11078                         this._fadeInDeferred = new dojo.Deferred(dojo.hitch(this, function(){
11079                                 fadeIn.stop();
11080                                 delete this._fadeInDeferred;
11081                         }));
11082
11083                         fadeIn = dojo.fadeIn({
11084                                 node: this.domNode,
11085                                 duration: this.duration,
11086                                 beforeBegin: dojo.hitch(this, function(){
11087                                         dijit._DialogLevelManager.show(this, this.underlayAttrs);
11088                                 }),
11089                                 onEnd: dojo.hitch(this, function(){
11090                                         if(this.autofocus && dijit._DialogLevelManager.isTop(this)){
11091                                                 // find focusable items each time dialog is shown since if dialog contains a widget the
11092                                                 // first focusable items can change
11093                                                 this._getFocusItems(this.domNode);
11094                                                 dijit.focus(this._firstFocusItem);
11095                                         }
11096                                         this._fadeInDeferred.callback(true);
11097                                         delete this._fadeInDeferred;
11098                                 })
11099                         }).play();
11100                         
11101                         return this._fadeInDeferred;
11102                 },
11103
11104                 hide: function(){
11105                         // summary:
11106                         //              Hide the dialog
11107                         // returns: dojo.Deferred
11108                         //              Deferred object that resolves when the hide animation is complete
11109
11110                         // if we haven't been initialized yet then we aren't showing and we can just return
11111                         if(!this._alreadyInitialized){
11112                                 return;
11113                         }
11114                         if(this._fadeInDeferred){
11115                                 this._fadeInDeferred.cancel();
11116                         }
11117
11118                         // fade-in Animation object, setup below
11119                         var fadeOut;
11120
11121                         this._fadeOutDeferred = new dojo.Deferred(dojo.hitch(this, function(){
11122                                 fadeOut.stop();
11123                                 delete this._fadeOutDeferred;
11124                         }));
11125
11126                         fadeOut = dojo.fadeOut({
11127                                 node: this.domNode,
11128                                 duration: this.duration,
11129                                 onEnd: dojo.hitch(this, function(){
11130                                         this.domNode.style.display = "none";
11131                                         dijit._DialogLevelManager.hide(this);
11132                                         this.onHide();
11133                                         this._fadeOutDeferred.callback(true);
11134                                         delete this._fadeOutDeferred;
11135                                 })
11136                          }).play();
11137
11138                         if(this._scrollConnected){
11139                                 this._scrollConnected = false;
11140                         }
11141                         dojo.forEach(this._modalconnects, dojo.disconnect);
11142                         this._modalconnects = [];
11143
11144                         if(this._relativePosition){
11145                                 delete this._relativePosition;
11146                         }
11147                         this._set("open", false);
11148
11149                         return this._fadeOutDeferred;
11150                 },
11151
11152                 layout: function(){
11153                         // summary:
11154                         //              Position the Dialog and the underlay
11155                         // tags:
11156                         //              private
11157                         if(this.domNode.style.display != "none"){
11158                                 if(dijit._underlay){    // avoid race condition during show()
11159                                         dijit._underlay.layout();
11160                                 }
11161                                 this._position();
11162                         }
11163                 },
11164
11165                 destroy: function(){
11166                         if(this._fadeInDeferred){
11167                                 this._fadeInDeferred.cancel();
11168                         }
11169                         if(this._fadeOutDeferred){
11170                                 this._fadeOutDeferred.cancel();
11171                         }
11172                         if(this._moveable){
11173                                 this._moveable.destroy();
11174                         }
11175                         if(this._dndListener){
11176                                 dojo.unsubscribe(this._dndListener);
11177                         }
11178                         dojo.forEach(this._modalconnects, dojo.disconnect);
11179
11180                         dijit._DialogLevelManager.hide(this);
11181
11182                         this.inherited(arguments);
11183                 }
11184         }
11185 );
11186
11187 dojo.declare(
11188         "dijit.Dialog",
11189         [dijit.layout.ContentPane, dijit._DialogBase],
11190         {}
11191 );
11192
11193 dijit._DialogLevelManager = {
11194         // summary:
11195         //              Controls the various active "levels" on the page, starting with the
11196         //              stuff initially visible on the page (at z-index 0), and then having an entry for
11197         //              each Dialog shown.
11198
11199         show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
11200                 // summary:
11201                 //              Call right before fade-in animation for new dialog.
11202                 //              Saves current focus, displays/adjusts underlay for new dialog,
11203                 //              and sets the z-index of the dialog itself.
11204                 //
11205                 //              New dialog will be displayed on top of all currently displayed dialogs.
11206                 //
11207                 //              Caller is responsible for setting focus in new dialog after the fade-in
11208                 //              animation completes.
11209
11210                 var ds = dijit._dialogStack;
11211
11212                 // Save current focus
11213                 ds[ds.length-1].focus = dijit.getFocus(dialog);
11214
11215                 // Display the underlay, or if already displayed then adjust for this new dialog
11216                 var underlay = dijit._underlay;
11217                 if(!underlay || underlay._destroyed){
11218                         underlay = dijit._underlay = new dijit.DialogUnderlay(underlayAttrs);
11219                 }else{
11220                         underlay.set(dialog.underlayAttrs);
11221                 }
11222
11223                 // Set z-index a bit above previous dialog
11224                 var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : 950;
11225                 if(ds.length == 1){     // first dialog
11226                         underlay.show();
11227                 }
11228                 dojo.style(dijit._underlay.domNode, 'zIndex', zIndex - 1);
11229
11230                 // Dialog
11231                 dojo.style(dialog.domNode, 'zIndex', zIndex);
11232
11233                 ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
11234         },
11235
11236         hide: function(/*dijit._Widget*/ dialog){
11237                 // summary:
11238                 //              Called when the specified dialog is hidden/destroyed, after the fade-out
11239                 //              animation ends, in order to reset page focus, fix the underlay, etc.
11240                 //              If the specified dialog isn't open then does nothing.
11241                 //
11242                 //              Caller is responsible for either setting display:none on the dialog domNode,
11243                 //              or calling dijit.popup.hide(), or removing it from the page DOM.
11244
11245                 var ds = dijit._dialogStack;
11246
11247                 if(ds[ds.length-1].dialog == dialog){
11248                         // Removing the top (or only) dialog in the stack, return focus
11249                         // to previous dialog
11250
11251                         ds.pop();
11252
11253                         var pd = ds[ds.length-1];       // the new active dialog (or the base page itself)
11254
11255                         // Adjust underlay
11256                         if(ds.length == 1){
11257                                 // Returning to original page.
11258                                 // Hide the underlay, unless the underlay widget has already been destroyed
11259                                 // because we are being called during page unload (when all widgets are destroyed)
11260                                 if(!dijit._underlay._destroyed){
11261                                         dijit._underlay.hide();
11262                                 }
11263                         }else{
11264                                 // Popping back to previous dialog, adjust underlay
11265                                 dojo.style(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
11266                                 dijit._underlay.set(pd.underlayAttrs);
11267                         }
11268
11269                         // Adjust focus
11270                         if(dialog.refocus){
11271                                 // If we are returning control to a previous dialog but for some reason
11272                                 // that dialog didn't have a focused field, set focus to first focusable item.
11273                                 // This situation could happen if two dialogs appeared at nearly the same time,
11274                                 // since a dialog doesn't set it's focus until the fade-in is finished.
11275                                 var focus = pd.focus;
11276                                 if(!focus || (pd.dialog && !dojo.isDescendant(focus.node, pd.dialog.domNode))){
11277                                         pd.dialog._getFocusItems(pd.dialog.domNode);
11278                                         focus = pd.dialog._firstFocusItem;
11279                                 }
11280         
11281                                 try{
11282                                         dijit.focus(focus);
11283                                 }catch(e){
11284                                         /* focus() will fail if user opened the dialog by clicking a non-focusable element */
11285                                 }
11286                         }
11287                 }else{
11288                         // Removing a dialog out of order (#9944, #10705).
11289                         // Don't need to mess with underlay or z-index or anything.
11290                         var idx = dojo.indexOf(dojo.map(ds, function(elem){return elem.dialog}), dialog);
11291                         if(idx != -1){
11292                                 ds.splice(idx, 1);
11293                         }
11294                 }
11295         },
11296
11297         isTop: function(/*dijit._Widget*/ dialog){
11298                 // summary:
11299                 //              Returns true if specified Dialog is the top in the task
11300                 var ds = dijit._dialogStack;
11301                 return ds[ds.length-1].dialog == dialog;
11302         }
11303 };
11304
11305 // Stack representing the various active "levels" on the page, starting with the
11306 // stuff initially visible on the page (at z-index 0), and then having an entry for
11307 // each Dialog shown.
11308 // Each element in stack has form {
11309 //              dialog: dialogWidget,
11310 //              focus: returnFromGetFocus(),
11311 //              underlayAttrs: attributes to set on underlay (when this widget is active)
11312 // }
11313 dijit._dialogStack = [
11314         {dialog: null, focus: null, underlayAttrs: null}        // entry for stuff at z-index: 0
11315 ];
11316
11317 }
11318
11319 if(!dojo._hasResource["dijit._HasDropDown"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11320 dojo._hasResource["dijit._HasDropDown"] = true;
11321 dojo.provide("dijit._HasDropDown");
11322
11323
11324
11325 dojo.declare("dijit._HasDropDown",
11326         null,
11327         {
11328                 // summary:
11329                 //              Mixin for widgets that need drop down ability.
11330
11331                 // _buttonNode: [protected] DomNode
11332                 //              The button/icon/node to click to display the drop down.
11333                 //              Can be set via a dojoAttachPoint assignment.
11334                 //              If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
11335                 _buttonNode: null,
11336
11337                 // _arrowWrapperNode: [protected] DomNode
11338                 //              Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
11339                 //              on where the drop down is set to be positioned.
11340                 //              Can be set via a dojoAttachPoint assignment.
11341                 //              If missing, then _buttonNode will be used.
11342                 _arrowWrapperNode: null,
11343
11344                 // _popupStateNode: [protected] DomNode
11345                 //              The node to set the popupActive class on.
11346                 //              Can be set via a dojoAttachPoint assignment.
11347                 //              If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
11348                 _popupStateNode: null,
11349
11350                 // _aroundNode: [protected] DomNode
11351                 //              The node to display the popup around.
11352                 //              Can be set via a dojoAttachPoint assignment.
11353                 //              If missing, then domNode will be used.
11354                 _aroundNode: null,
11355
11356                 // dropDown: [protected] Widget
11357                 //              The widget to display as a popup.  This widget *must* be
11358                 //              defined before the startup function is called.
11359                 dropDown: null,
11360
11361                 // autoWidth: [protected] Boolean
11362                 //              Set to true to make the drop down at least as wide as this
11363                 //              widget.  Set to false if the drop down should just be its
11364                 //              default width
11365                 autoWidth: true,
11366
11367                 // forceWidth: [protected] Boolean
11368                 //              Set to true to make the drop down exactly as wide as this
11369                 //              widget.  Overrides autoWidth.
11370                 forceWidth: false,
11371
11372                 // maxHeight: [protected] Integer
11373                 //              The max height for our dropdown.
11374                 //              Any dropdown taller than this will have scrollbars.
11375                 //              Set to 0 for no max height, or -1 to limit height to available space in viewport
11376                 maxHeight: 0,
11377
11378                 // dropDownPosition: [const] String[]
11379                 //              This variable controls the position of the drop down.
11380                 //              It's an array of strings with the following values:
11381                 //
11382                 //                      * before: places drop down to the left of the target node/widget, or to the right in
11383                 //                        the case of RTL scripts like Hebrew and Arabic
11384                 //                      * after: places drop down to the right of the target node/widget, or to the left in
11385                 //                        the case of RTL scripts like Hebrew and Arabic
11386                 //                      * above: drop down goes above target node
11387                 //                      * below: drop down goes below target node
11388                 //
11389                 //              The list is positions is tried, in order, until a position is found where the drop down fits
11390                 //              within the viewport.
11391                 //
11392                 dropDownPosition: ["below","above"],
11393
11394                 // _stopClickEvents: Boolean
11395                 //              When set to false, the click events will not be stopped, in
11396                 //              case you want to use them in your subwidget
11397                 _stopClickEvents: true,
11398
11399                 _onDropDownMouseDown: function(/*Event*/ e){
11400                         // summary:
11401                         //              Callback when the user mousedown's on the arrow icon
11402
11403                         if(this.disabled || this.readOnly){ return; }
11404
11405                         dojo.stopEvent(e);
11406
11407                         this._docHandler = this.connect(dojo.doc, "onmouseup", "_onDropDownMouseUp");
11408
11409                         this.toggleDropDown();
11410                 },
11411
11412                 _onDropDownMouseUp: function(/*Event?*/ e){
11413                         // summary:
11414                         //              Callback when the user lifts their mouse after mouse down on the arrow icon.
11415                         //              If the drop is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
11416                         //              dropDown node.  If the event is missing, then we are not
11417                         //              a mouseup event.
11418                         //
11419                         //              This is useful for the common mouse movement pattern
11420                         //              with native browser <select> nodes:
11421                         //                      1. mouse down on the select node (probably on the arrow)
11422                         //                      2. move mouse to a menu item while holding down the mouse button
11423                         //                      3. mouse up.  this selects the menu item as though the user had clicked it.
11424                         if(e && this._docHandler){
11425                                 this.disconnect(this._docHandler);
11426                         }
11427                         var dropDown = this.dropDown, overMenu = false;
11428
11429                         if(e && this._opened){
11430                                 // This code deals with the corner-case when the drop down covers the original widget,
11431                                 // because it's so large.  In that case mouse-up shouldn't select a value from the menu.
11432                                 // Find out if our target is somewhere in our dropdown widget,
11433                                 // but not over our _buttonNode (the clickable node)
11434                                 var c = dojo.position(this._buttonNode, true);
11435                                 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
11436                                         !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
11437                                         var t = e.target;
11438                                         while(t && !overMenu){
11439                                                 if(dojo.hasClass(t, "dijitPopup")){
11440                                                         overMenu = true;
11441                                                 }else{
11442                                                         t = t.parentNode;
11443                                                 }
11444                                         }
11445                                         if(overMenu){
11446                                                 t = e.target;
11447                                                 if(dropDown.onItemClick){
11448                                                         var menuItem;
11449                                                         while(t && !(menuItem = dijit.byNode(t))){
11450                                                                 t = t.parentNode;
11451                                                         }
11452                                                         if(menuItem && menuItem.onClick && menuItem.getParent){
11453                                                                 menuItem.getParent().onItemClick(menuItem, e);
11454                                                         }
11455                                                 }
11456                                                 return;
11457                                         }
11458                                 }
11459                         }
11460                         if(this._opened && dropDown.focus && dropDown.autoFocus !== false){
11461                                 // Focus the dropdown widget - do it on a delay so that we
11462                                 // don't steal our own focus.
11463                                 window.setTimeout(dojo.hitch(dropDown, "focus"), 1);
11464                         }
11465                 },
11466
11467                 _onDropDownClick: function(/*Event*/ e){
11468                         // the drop down was already opened on mousedown/keydown; just need to call stopEvent()
11469                         if(this._stopClickEvents){
11470                                 dojo.stopEvent(e);
11471                         }
11472                 },
11473
11474                 buildRendering: function(){
11475                         this.inherited(arguments);
11476
11477                         this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
11478                         this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
11479
11480                         // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
11481                         // based on where drop down will normally appear
11482                         var defaultPos = {
11483                                         "after" : this.isLeftToRight() ? "Right" : "Left",
11484                                         "before" : this.isLeftToRight() ? "Left" : "Right",
11485                                         "above" : "Up",
11486                                         "below" : "Down",
11487                                         "left" : "Left",
11488                                         "right" : "Right"
11489                         }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
11490                         dojo.addClass(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
11491                 },
11492
11493                 postCreate: function(){
11494                         // summary:
11495                         //              set up nodes and connect our mouse and keypress events
11496
11497                         this.inherited(arguments);
11498
11499                         this.connect(this._buttonNode, "onmousedown", "_onDropDownMouseDown");
11500                         this.connect(this._buttonNode, "onclick", "_onDropDownClick");
11501                         this.connect(this.focusNode, "onkeypress", "_onKey");
11502                         this.connect(this.focusNode, "onkeyup", "_onKeyUp");
11503                 },
11504
11505                 destroy: function(){
11506                         if(this.dropDown){
11507                                 // Destroy the drop down, unless it's already been destroyed.  This can happen because
11508                                 // the drop down is a direct child of <body> even though it's logically my child.
11509                                 if(!this.dropDown._destroyed){
11510                                         this.dropDown.destroyRecursive();
11511                                 }
11512                                 delete this.dropDown;
11513                         }
11514                         this.inherited(arguments);
11515                 },
11516
11517                 _onKey: function(/*Event*/ e){
11518                         // summary:
11519                         //              Callback when the user presses a key while focused on the button node
11520
11521                         if(this.disabled || this.readOnly){ return; }
11522
11523                         var d = this.dropDown, target = e.target;
11524                         if(d && this._opened && d.handleKey){
11525                                 if(d.handleKey(e) === false){
11526                                         /* false return code means that the drop down handled the key */
11527                                         dojo.stopEvent(e);
11528                                         return;
11529                                 }
11530                         }
11531                         if(d && this._opened && e.charOrCode == dojo.keys.ESCAPE){
11532                                 this.closeDropDown();
11533                                 dojo.stopEvent(e);
11534                         }else if(!this._opened &&
11535                                         (e.charOrCode == dojo.keys.DOWN_ARROW ||
11536                                                 ( (e.charOrCode == dojo.keys.ENTER || e.charOrCode == " ") &&
11537                                                   //ignore enter and space if the event is for a text input
11538                                                   ((target.tagName || "").toLowerCase() !== 'input' ||
11539                                                      (target.type && target.type.toLowerCase() !== 'text'))))){
11540                                 // Toggle the drop down, but wait until keyup so that the drop down doesn't
11541                                 // get a stray keyup event, or in the case of key-repeat (because user held
11542                                 // down key for too long), stray keydown events
11543                                 this._toggleOnKeyUp = true;
11544                                 dojo.stopEvent(e);
11545                         }
11546                 },
11547
11548                 _onKeyUp: function(){
11549                         if(this._toggleOnKeyUp){
11550                                 delete this._toggleOnKeyUp;
11551                                 this.toggleDropDown();
11552                                 var d = this.dropDown;  // drop down may not exist until toggleDropDown() call
11553                                 if(d && d.focus){
11554                                         setTimeout(dojo.hitch(d, "focus"), 1);
11555                                 }
11556                         }
11557                 },
11558
11559                 _onBlur: function(){
11560                         // summary:
11561                         //              Called magically when focus has shifted away from this widget and it's dropdown
11562
11563                         // Don't focus on button if the user has explicitly focused on something else (happens
11564                         // when user clicks another control causing the current popup to close)..
11565                         // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
11566                         // it when you display:none a node with focus.
11567                         var focusMe = dijit._curFocus && this.dropDown && dojo.isDescendant(dijit._curFocus, this.dropDown.domNode);
11568
11569                         this.closeDropDown(focusMe);
11570
11571                         this.inherited(arguments);
11572                 },
11573
11574                 isLoaded: function(){
11575                         // summary:
11576                         //              Returns whether or not the dropdown is loaded.  This can
11577                         //              be overridden in order to force a call to loadDropDown().
11578                         // tags:
11579                         //              protected
11580
11581                         return true;
11582                 },
11583
11584                 loadDropDown: function(/* Function */ loadCallback){
11585                         // summary:
11586                         //              Loads the data for the dropdown, and at some point, calls
11587                         //              the given callback.   This is basically a callback when the
11588                         //              user presses the down arrow button to open the drop down.
11589                         // tags:
11590                         //              protected
11591
11592                         loadCallback();
11593                 },
11594
11595                 toggleDropDown: function(){
11596                         // summary:
11597                         //              Callback when the user presses the down arrow button or presses
11598                         //              the down arrow key to open/close the drop down.
11599                         //              Toggle the drop-down widget; if it is up, close it, if not, open it
11600                         // tags:
11601                         //              protected
11602
11603                         if(this.disabled || this.readOnly){ return; }
11604                         if(!this._opened){
11605                                 // If we aren't loaded, load it first so there isn't a flicker
11606                                 if(!this.isLoaded()){
11607                                         this.loadDropDown(dojo.hitch(this, "openDropDown"));
11608                                         return;
11609                                 }else{
11610                                         this.openDropDown();
11611                                 }
11612                         }else{
11613                                 this.closeDropDown();
11614                         }
11615                 },
11616
11617                 openDropDown: function(){
11618                         // summary:
11619                         //              Opens the dropdown for this widget.   To be called only when this.dropDown
11620                         //              has been created and is ready to display (ie, it's data is loaded).
11621                         // returns:
11622                         //              return value of dijit.popup.open()
11623                         // tags:
11624                         //              protected
11625
11626                         var dropDown = this.dropDown,
11627                                 ddNode = dropDown.domNode,
11628                                 aroundNode = this._aroundNode || this.domNode,
11629                                 self = this;
11630
11631                         // Prepare our popup's height and honor maxHeight if it exists.
11632
11633                         // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
11634                         // ie, dependent on how much space is available (BK)
11635
11636                         if(!this._preparedNode){
11637                                 this._preparedNode = true;
11638                                 // Check if we have explicitly set width and height on the dropdown widget dom node
11639                                 if(ddNode.style.width){
11640                                         this._explicitDDWidth = true;
11641                                 }
11642                                 if(ddNode.style.height){
11643                                         this._explicitDDHeight = true;
11644                                 }
11645                         }
11646
11647                         // Code for resizing dropdown (height limitation, or increasing width to match my width)
11648                         if(this.maxHeight || this.forceWidth || this.autoWidth){
11649                                 var myStyle = {
11650                                         display: "",
11651                                         visibility: "hidden"
11652                                 };
11653                                 if(!this._explicitDDWidth){
11654                                         myStyle.width = "";
11655                                 }
11656                                 if(!this._explicitDDHeight){
11657                                         myStyle.height = "";
11658                                 }
11659                                 dojo.style(ddNode, myStyle);
11660                                 
11661                                 // Figure out maximum height allowed (if there is a height restriction)
11662                                 var maxHeight = this.maxHeight;
11663                                 if(maxHeight == -1){
11664                                         // limit height to space available in viewport either above or below my domNode
11665                                         // (whichever side has more room)
11666                                         var viewport = dojo.window.getBox(),
11667                                                 position = dojo.position(aroundNode, false);
11668                                         maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
11669                                 }
11670
11671                                 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
11672                                 // so we call startup() and also get the size
11673                                 if(dropDown.startup && !dropDown._started){
11674                                         dropDown.startup();
11675                                 }
11676
11677                                 dijit.popup.moveOffScreen(dropDown);
11678                                 // Get size of drop down, and determine if vertical scroll bar needed
11679                                 var mb = dojo._getMarginSize(ddNode);
11680                                 var overHeight = (maxHeight && mb.h > maxHeight);
11681                                 dojo.style(ddNode, {
11682                                         overflowX: "hidden",
11683                                         overflowY: overHeight ? "auto" : "hidden"
11684                                 });
11685                                 if(overHeight){
11686                                         mb.h = maxHeight;
11687                                         if("w" in mb){
11688                                                 mb.w += 16;     // room for vertical scrollbar
11689                                         }
11690                                 }else{
11691                                         delete mb.h;
11692                                 }
11693
11694                                 // Adjust dropdown width to match or be larger than my width
11695                                 if(this.forceWidth){
11696                                         mb.w = aroundNode.offsetWidth;
11697                                 }else if(this.autoWidth){
11698                                         mb.w = Math.max(mb.w, aroundNode.offsetWidth);
11699                                 }else{
11700                                         delete mb.w;
11701                                 }
11702                                 
11703                                 // And finally, resize the dropdown to calculated height and width
11704                                 if(dojo.isFunction(dropDown.resize)){
11705                                         dropDown.resize(mb);
11706                                 }else{
11707                                         dojo.marginBox(ddNode, mb);
11708                                 }
11709                         }
11710
11711                         var retVal = dijit.popup.open({
11712                                 parent: this,
11713                                 popup: dropDown,
11714                                 around: aroundNode,
11715                                 orient: dijit.getPopupAroundAlignment((this.dropDownPosition && this.dropDownPosition.length) ? this.dropDownPosition : ["below"],this.isLeftToRight()),
11716                                 onExecute: function(){
11717                                         self.closeDropDown(true);
11718                                 },
11719                                 onCancel: function(){
11720                                         self.closeDropDown(true);
11721                                 },
11722                                 onClose: function(){
11723                                         dojo.attr(self._popupStateNode, "popupActive", false);
11724                                         dojo.removeClass(self._popupStateNode, "dijitHasDropDownOpen");
11725                                         self._opened = false;
11726                                 }
11727                         });
11728                         dojo.attr(this._popupStateNode, "popupActive", "true");
11729                         dojo.addClass(self._popupStateNode, "dijitHasDropDownOpen");
11730                         this._opened=true;
11731
11732                         // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
11733                         return retVal;
11734                 },
11735
11736                 closeDropDown: function(/*Boolean*/ focus){
11737                         // summary:
11738                         //              Closes the drop down on this widget
11739                         // focus:
11740                         //              If true, refocuses the button widget
11741                         // tags:
11742                         //              protected
11743
11744                         if(this._opened){
11745                                 if(focus){ this.focus(); }
11746                                 dijit.popup.close(this.dropDown);
11747                                 this._opened = false;
11748                         }
11749                 }
11750
11751         }
11752 );
11753
11754 }
11755
11756 if(!dojo._hasResource["dijit.form.Button"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
11757 dojo._hasResource["dijit.form.Button"] = true;
11758 dojo.provide("dijit.form.Button");
11759
11760
11761
11762
11763
11764 dojo.declare("dijit.form.Button",
11765         dijit.form._FormWidget,
11766         {
11767         // summary:
11768         //              Basically the same thing as a normal HTML button, but with special styling.
11769         // description:
11770         //              Buttons can display a label, an icon, or both.
11771         //              A label should always be specified (through innerHTML) or the label
11772         //              attribute.  It can be hidden via showLabel=false.
11773         // example:
11774         // |    <button dojoType="dijit.form.Button" onClick="...">Hello world</button>
11775         //
11776         // example:
11777         // |    var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
11778         // |    dojo.body().appendChild(button1.domNode);
11779
11780         // label: HTML String
11781         //              Text to display in button.
11782         //              If the label is hidden (showLabel=false) then and no title has
11783         //              been specified, then label is also set as title attribute of icon.
11784         label: "",
11785
11786         // showLabel: Boolean
11787         //              Set this to true to hide the label text and display only the icon.
11788         //              (If showLabel=false then iconClass must be specified.)
11789         //              Especially useful for toolbars.
11790         //              If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
11791         //
11792         //              The exception case is for computers in high-contrast mode, where the label
11793         //              will still be displayed, since the icon doesn't appear.
11794         showLabel: true,
11795
11796         // iconClass: String
11797         //              Class to apply to DOMNode in button to make it display an icon
11798         iconClass: "",
11799
11800         // type: String
11801         //              Defines the type of button.  "button", "submit", or "reset".
11802         type: "button",
11803
11804         baseClass: "dijitButton",
11805
11806         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\trole=\"button\" aria-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\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
11807
11808         attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
11809                 value: "valueNode"
11810         }),
11811
11812         _onClick: function(/*Event*/ e){
11813                 // summary:
11814                 //              Internal function to handle click actions
11815                 if(this.disabled){
11816                         return false;
11817                 }
11818                 this._clicked(); // widget click actions
11819                 return this.onClick(e); // user click actions
11820         },
11821
11822         _onButtonClick: function(/*Event*/ e){
11823                 // summary:
11824                 //              Handler when the user activates the button portion.
11825                 if(this._onClick(e) === false){ // returning nothing is same as true
11826                         e.preventDefault(); // needed for checkbox
11827                 }else if(this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a nonform widget needs to be signalled
11828                         for(var node=this.domNode; node.parentNode/*#5935*/; node=node.parentNode){
11829                                 var widget=dijit.byNode(node);
11830                                 if(widget && typeof widget._onSubmit == "function"){
11831                                         widget._onSubmit(e);
11832                                         break;
11833                                 }
11834                         }
11835                 }else if(this.valueNode){
11836                         this.valueNode.click();
11837                         e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
11838                 }
11839         },
11840
11841         buildRendering: function(){
11842                 this.inherited(arguments);
11843                 dojo.setSelectable(this.focusNode, false);
11844         },
11845
11846         _fillContent: function(/*DomNode*/ source){
11847                 // Overrides _Templated._fillContent().
11848                 // If button label is specified as srcNodeRef.innerHTML rather than
11849                 // this.params.label, handle it here.
11850                 // TODO: remove the method in 2.0, parser will do it all for me
11851                 if(source && (!this.params || !("label" in this.params))){
11852                         this.set('label', source.innerHTML);
11853                 }
11854         },
11855
11856         _setShowLabelAttr: function(val){
11857                 if(this.containerNode){
11858                         dojo.toggleClass(this.containerNode, "dijitDisplayNone", !val);
11859                 }
11860                 this._set("showLabel", val);
11861         },
11862
11863         onClick: function(/*Event*/ e){
11864                 // summary:
11865                 //              Callback for when button is clicked.
11866                 //              If type="submit", return true to perform submit, or false to cancel it.
11867                 // type:
11868                 //              callback
11869                 return true;            // Boolean
11870         },
11871
11872         _clicked: function(/*Event*/ e){
11873                 // summary:
11874                 //              Internal overridable function for when the button is clicked
11875         },
11876
11877         setLabel: function(/*String*/ content){
11878                 // summary:
11879                 //              Deprecated.  Use set('label', ...) instead.
11880                 dojo.deprecated("dijit.form.Button.setLabel() is deprecated.  Use set('label', ...) instead.", "", "2.0");
11881                 this.set("label", content);
11882         },
11883
11884         _setLabelAttr: function(/*String*/ content){
11885                 // summary:
11886                 //              Hook for set('label', ...) to work.
11887                 // description:
11888                 //              Set the label (text) of the button; takes an HTML string.
11889                 this._set("label", content);
11890                 this.containerNode.innerHTML = content;
11891                 if(this.showLabel == false && !this.params.title){
11892                         this.titleNode.title = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
11893                 }
11894         },
11895
11896         _setIconClassAttr: function(/*String*/ val){
11897                 // Custom method so that icon node is hidden when not in use, to avoid excess padding/margin
11898                 // appearing around it (even if it's a 0x0 sized <img> node)
11899
11900                 var oldVal = this.iconClass || "dijitNoIcon",
11901                         newVal = val || "dijitNoIcon";
11902                 dojo.replaceClass(this.iconNode, newVal, oldVal);
11903                 this._set("iconClass", val);
11904         }
11905 });
11906
11907
11908 dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container, dijit._HasDropDown], {
11909         // summary:
11910         //              A button with a drop down
11911         //
11912         // example:
11913         // |    <button dojoType="dijit.form.DropDownButton" label="Hello world">
11914         // |            <div dojotype="dijit.Menu">...</div>
11915         // |    </button>
11916         //
11917         // example:
11918         // |    var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
11919         // |    dojo.body().appendChild(button1);
11920         //
11921
11922         baseClass : "dijitDropDownButton",
11923
11924         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\trole=\"button\" aria-haspopup=\"true\" aria-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\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/></span>\n"),
11925
11926         _fillContent: function(){
11927                 // Overrides Button._fillContent().
11928                 //
11929                 // My inner HTML contains both the button contents and a drop down widget, like
11930                 // <DropDownButton>  <span>push me</span>  <Menu> ... </Menu> </DropDownButton>
11931                 // The first node is assumed to be the button content. The widget is the popup.
11932
11933                 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
11934                         //FIXME: figure out how to filter out the widget and use all remaining nodes as button
11935                         //      content, not just nodes[0]
11936                         var nodes = dojo.query("*", this.srcNodeRef);
11937                         dijit.form.DropDownButton.superclass._fillContent.call(this, nodes[0]);
11938
11939                         // save pointer to srcNode so we can grab the drop down widget after it's instantiated
11940                         this.dropDownContainer = this.srcNodeRef;
11941                 }
11942         },
11943
11944         startup: function(){
11945                 if(this._started){ return; }
11946
11947                 // the child widget from srcNodeRef is the dropdown widget.  Insert it in the page DOM,
11948                 // make it invisible, and store a reference to pass to the popup code.
11949                 if(!this.dropDown && this.dropDownContainer){
11950                         var dropDownNode = dojo.query("[widgetId]", this.dropDownContainer)[0];
11951                         this.dropDown = dijit.byNode(dropDownNode);
11952                         delete this.dropDownContainer;
11953                 }
11954                 if(this.dropDown){
11955                         dijit.popup.hide(this.dropDown);
11956                 }
11957
11958                 this.inherited(arguments);
11959         },
11960
11961         isLoaded: function(){
11962                 // Returns whether or not we are loaded - if our dropdown has an href,
11963                 // then we want to check that.
11964                 var dropDown = this.dropDown;
11965                 return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
11966         },
11967
11968         loadDropDown: function(){
11969                 // Loads our dropdown
11970                 var dropDown = this.dropDown;
11971                 if(!dropDown){ return; }
11972                 if(!this.isLoaded()){
11973                         var handler = dojo.connect(dropDown, "onLoad", this, function(){
11974                                 dojo.disconnect(handler);
11975                                 this.openDropDown();
11976                         });
11977                         dropDown.refresh();
11978                 }else{
11979                         this.openDropDown();
11980                 }
11981         },
11982
11983         isFocusable: function(){
11984                 // Overridden so that focus is handled by the _HasDropDown mixin, not by
11985                 // the _FormWidget mixin.
11986                 return this.inherited(arguments) && !this._mouseDown;
11987         }
11988 });
11989
11990 dojo.declare("dijit.form.ComboButton", dijit.form.DropDownButton, {
11991         // summary:
11992         //              A combination button and drop-down button.
11993         //              Users can click one side to "press" the button, or click an arrow
11994         //              icon to display the drop down.
11995         //
11996         // example:
11997         // |    <button dojoType="dijit.form.ComboButton" onClick="...">
11998         // |            <span>Hello world</span>
11999         // |            <div dojoType="dijit.Menu">...</div>
12000         // |    </button>
12001         //
12002         // example:
12003         // |    var button1 = new dijit.form.ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
12004         // |    dojo.body().appendChild(button1.domNode);
12005         //
12006
12007         templateString: dojo.cache("dijit.form", "templates/ComboButton.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"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\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" dojoAttachPoint=\"containerNode\" role=\"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\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"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"),
12008
12009         attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
12010                 id: "",
12011                 tabIndex: ["focusNode", "titleNode"],
12012                 title: "titleNode"
12013         }),
12014
12015         // optionsTitle: String
12016         //              Text that describes the options menu (accessibility)
12017         optionsTitle: "",
12018
12019         baseClass: "dijitComboButton",
12020
12021         // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
12022         // mouse action over specified node
12023         cssStateNodes: {
12024                 "buttonNode": "dijitButtonNode",
12025                 "titleNode": "dijitButtonContents",
12026                 "_popupStateNode": "dijitDownArrowButton"
12027         },
12028
12029         _focusedNode: null,
12030
12031         _onButtonKeyPress: function(/*Event*/ evt){
12032                 // summary:
12033                 //              Handler for right arrow key when focus is on left part of button
12034                 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
12035                         dijit.focus(this._popupStateNode);
12036                         dojo.stopEvent(evt);
12037                 }
12038         },
12039
12040         _onArrowKeyPress: function(/*Event*/ evt){
12041                 // summary:
12042                 //              Handler for left arrow key when focus is on right part of button
12043                 if(evt.charOrCode == dojo.keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
12044                         dijit.focus(this.titleNode);
12045                         dojo.stopEvent(evt);
12046                 }
12047         },
12048         
12049         focus: function(/*String*/ position){
12050                 // summary:
12051                 //              Focuses this widget to according to position, if specified,
12052                 //              otherwise on arrow node
12053                 // position:
12054                 //              "start" or "end"
12055                 if(!this.disabled){
12056                         dijit.focus(position == "start" ? this.titleNode : this._popupStateNode);
12057                 }
12058         }
12059 });
12060
12061 dojo.declare("dijit.form.ToggleButton", dijit.form.Button, {
12062         // summary:
12063         //              A button that can be in two states (checked or not).
12064         //              Can be base class for things like tabs or checkbox or radio buttons
12065
12066         baseClass: "dijitToggleButton",
12067
12068         // checked: Boolean
12069         //              Corresponds to the native HTML <input> element's attribute.
12070         //              In markup, specified as "checked='checked'" or just "checked".
12071         //              True if the button is depressed, or the checkbox is checked,
12072         //              or the radio button is selected, etc.
12073         checked: false,
12074
12075         attributeMap: dojo.mixin(dojo.clone(dijit.form.Button.prototype.attributeMap), {
12076                 checked:"focusNode"
12077         }),
12078
12079         _clicked: function(/*Event*/ evt){
12080                 this.set('checked', !this.checked);
12081         },
12082
12083         _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
12084                 this._set("checked", value);
12085                 dojo.attr(this.focusNode || this.domNode, "checked", value);
12086                 dijit.setWaiState(this.focusNode || this.domNode, "pressed", value);
12087                 this._handleOnChange(value, priorityChange);
12088         },
12089
12090         setChecked: function(/*Boolean*/ checked){
12091                 // summary:
12092                 //              Deprecated.  Use set('checked', true/false) instead.
12093                 dojo.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
12094                 this.set('checked', checked);
12095         },
12096
12097         reset: function(){
12098                 // summary:
12099                 //              Reset the widget's value to what it was at initialization time
12100
12101                 this._hasBeenBlurred = false;
12102
12103                 // set checked state to original setting
12104                 this.set('checked', this.params.checked || false);
12105         }
12106 });
12107
12108 }
12109
12110 if(!dojo._hasResource["dijit.form.ToggleButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12111 dojo._hasResource["dijit.form.ToggleButton"] = true;
12112 dojo.provide("dijit.form.ToggleButton");
12113
12114
12115
12116
12117 }
12118
12119 if(!dojo._hasResource["dijit.form.CheckBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12120 dojo._hasResource["dijit.form.CheckBox"] = true;
12121 dojo.provide("dijit.form.CheckBox");
12122
12123
12124
12125 dojo.declare(
12126         "dijit.form.CheckBox",
12127         dijit.form.ToggleButton,
12128         {
12129                 // summary:
12130                 //              Same as an HTML checkbox, but with fancy styling.
12131                 //
12132                 // description:
12133                 //              User interacts with real html inputs.
12134                 //              On onclick (which occurs by mouse click, space-bar, or
12135                 //              using the arrow keys to switch the selected radio button),
12136                 //              we update the state of the checkbox/radio.
12137                 //
12138                 //              There are two modes:
12139                 //                      1. High contrast mode
12140                 //                      2. Normal mode
12141                 //
12142                 //              In case 1, the regular html inputs are shown and used by the user.
12143                 //              In case 2, the regular html inputs are invisible but still used by
12144                 //              the user. They are turned quasi-invisible and overlay the background-image.
12145
12146                 templateString: dojo.cache("dijit.form", "templates/CheckBox.html", "<div class=\"dijit dijitReset dijitInline\" role=\"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"),
12147
12148                 baseClass: "dijitCheckBox",
12149
12150                 // type: [private] String
12151                 //              type attribute on <input> node.
12152                 //              Overrides `dijit.form.Button.type`.  Users should not change this value.
12153                 type: "checkbox",
12154
12155                 // value: String
12156                 //              As an initialization parameter, equivalent to value field on normal checkbox
12157                 //              (if checked, the value is passed as the value when form is submitted).
12158                 //
12159                 //              However, get('value') will return either the string or false depending on
12160                 //              whether or not the checkbox is checked.
12161                 //
12162                 //              set('value', string) will check the checkbox and change the value to the
12163                 //              specified string
12164                 //
12165                 //              set('value', boolean) will change the checked state.
12166                 value: "on",
12167
12168                 // readOnly: Boolean
12169                 //              Should this widget respond to user input?
12170                 //              In markup, this is specified as "readOnly".
12171                 //              Similar to disabled except readOnly form values are submitted.
12172                 readOnly: false,
12173                 
12174                 // the attributeMap should inherit from dijit.form._FormWidget.prototype.attributeMap
12175                 // instead of ToggleButton as the icon mapping has no meaning for a CheckBox
12176                 attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, {
12177                         readOnly: "focusNode"
12178                 }),
12179
12180                 _setReadOnlyAttr: function(/*Boolean*/ value){
12181                         this._set("readOnly", value);
12182                         dojo.attr(this.focusNode, 'readOnly', value);
12183                         dijit.setWaiState(this.focusNode, "readonly", value);
12184                 },
12185
12186                 _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
12187                         // summary:
12188                         //              Handler for value= attribute to constructor, and also calls to
12189                         //              set('value', val).
12190                         // description:
12191                         //              During initialization, just saves as attribute to the <input type=checkbox>.
12192                         //
12193                         //              After initialization,
12194                         //              when passed a boolean, controls whether or not the CheckBox is checked.
12195                         //              If passed a string, changes the value attribute of the CheckBox (the one
12196                         //              specified as "value" when the CheckBox was constructed (ex: <input
12197                         //              dojoType="dijit.CheckBox" value="chicken">)
12198                         if(typeof newValue == "string"){
12199                                 this._set("value", newValue);
12200                                 dojo.attr(this.focusNode, 'value', newValue);
12201                                 newValue = true;
12202                         }
12203                         if(this._created){
12204                                 this.set('checked', newValue, priorityChange);
12205                         }
12206                 },
12207                 _getValueAttr: function(){
12208                         // summary:
12209                         //              Hook so get('value') works.
12210                         // description:
12211                         //              If the CheckBox is checked, returns the value attribute.
12212                         //              Otherwise returns false.
12213                         return (this.checked ? this.value : false);
12214                 },
12215
12216                 // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
12217                 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
12218                 _setLabelAttr: undefined,
12219
12220                 postMixInProperties: function(){
12221                         if(this.value == ""){
12222                                 this.value = "on";
12223                         }
12224
12225                         // Need to set initial checked state as part of template, so that form submit works.
12226                         // dojo.attr(node, "checked", bool) doesn't work on IEuntil node has been attached
12227                         // to <body>, see #8666
12228                         this.checkedAttrSetting = this.checked ? "checked" : "";
12229
12230                         this.inherited(arguments);
12231                 },
12232
12233                  _fillContent: function(/*DomNode*/ source){
12234                         // Override Button::_fillContent() since it doesn't make sense for CheckBox,
12235                         // since CheckBox doesn't even have a container
12236                 },
12237
12238                 reset: function(){
12239                         // Override ToggleButton.reset()
12240
12241                         this._hasBeenBlurred = false;
12242
12243                         this.set('checked', this.params.checked || false);
12244
12245                         // Handle unlikely event that the <input type=checkbox> value attribute has changed
12246                         this._set("value", this.params.value || "on");
12247                         dojo.attr(this.focusNode, 'value', this.value);
12248                 },
12249
12250                 _onFocus: function(){
12251                         if(this.id){
12252                                 dojo.query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
12253                         }
12254                         this.inherited(arguments);
12255                 },
12256
12257                 _onBlur: function(){
12258                         if(this.id){
12259                                 dojo.query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
12260                         }
12261                         this.inherited(arguments);
12262                 },
12263
12264                 _onClick: function(/*Event*/ e){
12265                         // summary:
12266                         //              Internal function to handle click actions - need to check
12267                         //              readOnly, since button no longer does that check.
12268                         if(this.readOnly){
12269                                 dojo.stopEvent(e);
12270                                 return false;
12271                         }
12272                         return this.inherited(arguments);
12273                 }
12274         }
12275 );
12276
12277 dojo.declare(
12278         "dijit.form.RadioButton",
12279         dijit.form.CheckBox,
12280         {
12281                 // summary:
12282                 //              Same as an HTML radio, but with fancy styling.
12283
12284                 type: "radio",
12285                 baseClass: "dijitRadio",
12286
12287                 _setCheckedAttr: function(/*Boolean*/ value){
12288                         // If I am being checked then have to deselect currently checked radio button
12289                         this.inherited(arguments);
12290                         if(!this._created){ return; }
12291                         if(value){
12292                                 var _this = this;
12293                                 // search for radio buttons with the same name that need to be unchecked
12294                                 dojo.query("INPUT[type=radio]", this.focusNode.form || dojo.doc).forEach( // can't use name= since dojo.query doesn't support [] in the name
12295                                         function(inputNode){
12296                                                 if(inputNode.name == _this.name && inputNode != _this.focusNode && inputNode.form == _this.focusNode.form){
12297                                                         var widget = dijit.getEnclosingWidget(inputNode);
12298                                                         if(widget && widget.checked){
12299                                                                 widget.set('checked', false);
12300                                                         }
12301                                                 }
12302                                         }
12303                                 );
12304                         }
12305                 },
12306
12307                 _clicked: function(/*Event*/ e){
12308                         if(!this.checked){
12309                                 this.set('checked', true);
12310                         }
12311                 }
12312         }
12313 );
12314
12315 }
12316
12317 if(!dojo._hasResource["dijit.form.DropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12318 dojo._hasResource["dijit.form.DropDownButton"] = true;
12319 dojo.provide("dijit.form.DropDownButton");
12320
12321
12322
12323
12324 }
12325
12326 if(!dojo._hasResource["dojo.regexp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12327 dojo._hasResource["dojo.regexp"] = true;
12328 dojo.provide("dojo.regexp");
12329
12330 dojo.getObject("regexp", true, dojo);
12331
12332 /*=====
12333 dojo.regexp = {
12334         // summary: Regular expressions and Builder resources
12335 };
12336 =====*/
12337
12338 dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
12339         //      summary:
12340         //              Adds escape sequences for special characters in regular expressions
12341         // except:
12342         //              a String with special characters to be left unescaped
12343
12344         return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
12345                 if(except && except.indexOf(ch) != -1){
12346                         return ch;
12347                 }
12348                 return "\\" + ch;
12349         }); // String
12350 };
12351
12352 dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
12353         //      summary:
12354         //              Builds a regular expression that groups subexpressions
12355         //      description:
12356         //              A utility function used by some of the RE generators. The
12357         //              subexpressions are constructed by the function, re, in the second
12358         //              parameter.  re builds one subexpression for each elem in the array
12359         //              a, in the first parameter. Returns a string for a regular
12360         //              expression that groups all the subexpressions.
12361         // arr:
12362         //              A single value or an array of values.
12363         // re:
12364         //              A function. Takes one parameter and converts it to a regular
12365         //              expression.
12366         // nonCapture:
12367         //              If true, uses non-capturing match, otherwise matches are retained
12368         //              by regular expression. Defaults to false
12369
12370         // case 1: a is a single value.
12371         if(!(arr instanceof Array)){
12372                 return re(arr); // String
12373         }
12374
12375         // case 2: a is an array
12376         var b = [];
12377         for(var i = 0; i < arr.length; i++){
12378                 // convert each elem to a RE
12379                 b.push(re(arr[i]));
12380         }
12381
12382          // join the REs as alternatives in a RE group.
12383         return dojo.regexp.group(b.join("|"), nonCapture); // String
12384 };
12385
12386 dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
12387         // summary:
12388         //              adds group match to expression
12389         // nonCapture:
12390         //              If true, uses non-capturing match, otherwise matches are retained
12391         //              by regular expression.
12392         return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
12393 };
12394
12395 }
12396
12397 if(!dojo._hasResource["dojo.data.util.sorter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12398 dojo._hasResource["dojo.data.util.sorter"] = true;
12399 dojo.provide("dojo.data.util.sorter");
12400
12401 dojo.getObject("data.util.sorter", true, dojo);
12402
12403 dojo.data.util.sorter.basicComparator = function(       /*anything*/ a,
12404                                                                                                         /*anything*/ b){
12405         //      summary:
12406         //              Basic comparision function that compares if an item is greater or less than another item
12407         //      description:
12408         //              returns 1 if a > b, -1 if a < b, 0 if equal.
12409         //              'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
12410         //              And compared to each other, null is equivalent to undefined.
12411         
12412         //null is a problematic compare, so if null, we set to undefined.
12413         //Makes the check logic simple, compact, and consistent
12414         //And (null == undefined) === true, so the check later against null
12415         //works for undefined and is less bytes.
12416         var r = -1;
12417         if(a === null){
12418                 a = undefined;
12419         }
12420         if(b === null){
12421                 b = undefined;
12422         }
12423         if(a == b){
12424                 r = 0;
12425         }else if(a > b || a == null){
12426                 r = 1;
12427         }
12428         return r; //int {-1,0,1}
12429 };
12430
12431 dojo.data.util.sorter.createSortFunction = function(    /* attributes array */sortSpec,
12432                                                                                                                 /*dojo.data.core.Read*/ store){
12433         //      summary:
12434         //              Helper function to generate the sorting function based off the list of sort attributes.
12435         //      description:
12436         //              The sort function creation will look for a property on the store called 'comparatorMap'.  If it exists
12437         //              it will look in the mapping for comparisons function for the attributes.  If one is found, it will
12438         //              use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
12439         //              Returns the sorting function for this particular list of attributes and sorting directions.
12440         //
12441         //      sortSpec: array
12442         //              A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
12443         //              The objects should be formatted as follows:
12444         //              {
12445         //                      attribute: "attributeName-string" || attribute,
12446         //                      descending: true|false;   // Default is false.
12447         //              }
12448         //      store: object
12449         //              The datastore object to look up item values from.
12450         //
12451         var sortFunctions=[];
12452
12453         function createSortFunction(attr, dir, comp, s){
12454                 //Passing in comp and s (comparator and store), makes this
12455                 //function much faster.
12456                 return function(itemA, itemB){
12457                         var a = s.getValue(itemA, attr);
12458                         var b = s.getValue(itemB, attr);
12459                         return dir * comp(a,b); //int
12460                 };
12461         }
12462         var sortAttribute;
12463         var map = store.comparatorMap;
12464         var bc = dojo.data.util.sorter.basicComparator;
12465         for(var i = 0; i < sortSpec.length; i++){
12466                 sortAttribute = sortSpec[i];
12467                 var attr = sortAttribute.attribute;
12468                 if(attr){
12469                         var dir = (sortAttribute.descending) ? -1 : 1;
12470                         var comp = bc;
12471                         if(map){
12472                                 if(typeof attr !== "string" && ("toString" in attr)){
12473                                          attr = attr.toString();
12474                                 }
12475                                 comp = map[attr] || bc;
12476                         }
12477                         sortFunctions.push(createSortFunction(attr,
12478                                 dir, comp, store));
12479                 }
12480         }
12481         return function(rowA, rowB){
12482                 var i=0;
12483                 while(i < sortFunctions.length){
12484                         var ret = sortFunctions[i++](rowA, rowB);
12485                         if(ret !== 0){
12486                                 return ret;//int
12487                         }
12488                 }
12489                 return 0; //int
12490         }; // Function
12491 };
12492
12493 }
12494
12495 if(!dojo._hasResource["dojo.data.util.simpleFetch"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12496 dojo._hasResource["dojo.data.util.simpleFetch"] = true;
12497 dojo.provide("dojo.data.util.simpleFetch");
12498
12499
12500 dojo.getObject("data.util.simpleFetch", true, dojo);
12501
12502 dojo.data.util.simpleFetch.fetch = function(/* Object? */ request){
12503         //      summary:
12504         //              The simpleFetch mixin is designed to serve as a set of function(s) that can
12505         //              be mixed into other datastore implementations to accelerate their development.
12506         //              The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
12507         //              call by returning an array of all the found items that matched the query.  The simpleFetch mixin
12508         //              is not designed to work for datastores that respond to a fetch() call by incrementally
12509         //              loading items, or sequentially loading partial batches of the result
12510         //              set.  For datastores that mixin simpleFetch, simpleFetch
12511         //              implements a fetch method that automatically handles eight of the fetch()
12512         //              arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
12513         //              The class mixing in simpleFetch should not implement fetch(),
12514         //              but should instead implement a _fetchItems() method.  The _fetchItems()
12515         //              method takes three arguments, the keywordArgs object that was passed
12516         //              to fetch(), a callback function to be called when the result array is
12517         //              available, and an error callback to be called if something goes wrong.
12518         //              The _fetchItems() method should ignore any keywordArgs parameters for
12519         //              start, count, onBegin, onItem, onComplete, onError, sort, and scope.
12520         //              The _fetchItems() method needs to correctly handle any other keywordArgs
12521         //              parameters, including the query parameter and any optional parameters
12522         //              (such as includeChildren).  The _fetchItems() method should create an array of
12523         //              result items and pass it to the fetchHandler along with the original request object
12524         //              -- or, the _fetchItems() method may, if it wants to, create an new request object
12525         //              with other specifics about the request that are specific to the datastore and pass
12526         //              that as the request object to the handler.
12527         //
12528         //              For more information on this specific function, see dojo.data.api.Read.fetch()
12529         request = request || {};
12530         if(!request.store){
12531                 request.store = this;
12532         }
12533         var self = this;
12534
12535         var _errorHandler = function(errorData, requestObject){
12536                 if(requestObject.onError){
12537                         var scope = requestObject.scope || dojo.global;
12538                         requestObject.onError.call(scope, errorData, requestObject);
12539                 }
12540         };
12541
12542         var _fetchHandler = function(items, requestObject){
12543                 var oldAbortFunction = requestObject.abort || null;
12544                 var aborted = false;
12545
12546                 var startIndex = requestObject.start?requestObject.start:0;
12547                 var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
12548
12549                 requestObject.abort = function(){
12550                         aborted = true;
12551                         if(oldAbortFunction){
12552                                 oldAbortFunction.call(requestObject);
12553                         }
12554                 };
12555
12556                 var scope = requestObject.scope || dojo.global;
12557                 if(!requestObject.store){
12558                         requestObject.store = self;
12559                 }
12560                 if(requestObject.onBegin){
12561                         requestObject.onBegin.call(scope, items.length, requestObject);
12562                 }
12563                 if(requestObject.sort){
12564                         items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
12565                 }
12566                 if(requestObject.onItem){
12567                         for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
12568                                 var item = items[i];
12569                                 if(!aborted){
12570                                         requestObject.onItem.call(scope, item, requestObject);
12571                                 }
12572                         }
12573                 }
12574                 if(requestObject.onComplete && !aborted){
12575                         var subset = null;
12576                         if(!requestObject.onItem){
12577                                 subset = items.slice(startIndex, endIndex);
12578                         }
12579                         requestObject.onComplete.call(scope, subset, requestObject);
12580                 }
12581         };
12582         this._fetchItems(request, _fetchHandler, _errorHandler);
12583         return request; // Object
12584 };
12585
12586 }
12587
12588 if(!dojo._hasResource["dojo.data.util.filter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12589 dojo._hasResource["dojo.data.util.filter"] = true;
12590 dojo.provide("dojo.data.util.filter");
12591
12592 dojo.getObject("data.util.filter", true, dojo);
12593
12594 dojo.data.util.filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
12595         //      summary:
12596         //              Helper function to convert a simple pattern to a regular expression for matching.
12597         //      description:
12598         //              Returns a regular expression object that conforms to the defined conversion rules.
12599         //              For example:
12600         //                      ca*   -> /^ca.*$/
12601         //                      *ca*  -> /^.*ca.*$/
12602         //                      *c\*a*  -> /^.*c\*a.*$/
12603         //                      *c\*a?*  -> /^.*c\*a..*$/
12604         //                      and so on.
12605         //
12606         //      pattern: string
12607         //              A simple matching pattern to convert that follows basic rules:
12608         //                      * Means match anything, so ca* means match anything starting with ca
12609         //                      ? Means match single character.  So, b?b will match to bob and bab, and so on.
12610         //              \ is an escape character.  So for example, \* means do not treat * as a match, but literal character *.
12611         //                              To use a \ as a character in the string, it must be escaped.  So in the pattern it should be
12612         //                              represented by \\ to be treated as an ordinary \ character instead of an escape.
12613         //
12614         //      ignoreCase:
12615         //              An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
12616         //              By default, it is assumed case sensitive.
12617
12618         var rxp = "^";
12619         var c = null;
12620         for(var i = 0; i < pattern.length; i++){
12621                 c = pattern.charAt(i);
12622                 switch(c){
12623                         case '\\':
12624                                 rxp += c;
12625                                 i++;
12626                                 rxp += pattern.charAt(i);
12627                                 break;
12628                         case '*':
12629                                 rxp += ".*"; break;
12630                         case '?':
12631                                 rxp += "."; break;
12632                         case '$':
12633                         case '^':
12634                         case '/':
12635                         case '+':
12636                         case '.':
12637                         case '|':
12638                         case '(':
12639                         case ')':
12640                         case '{':
12641                         case '}':
12642                         case '[':
12643                         case ']':
12644                                 rxp += "\\"; //fallthrough
12645                         default:
12646                                 rxp += c;
12647                 }
12648         }
12649         rxp += "$";
12650         if(ignoreCase){
12651                 return new RegExp(rxp,"mi"); //RegExp
12652         }else{
12653                 return new RegExp(rxp,"m"); //RegExp
12654         }
12655         
12656 };
12657
12658 }
12659
12660 if(!dojo._hasResource["dijit.form.TextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
12661 dojo._hasResource["dijit.form.TextBox"] = true;
12662 dojo.provide("dijit.form.TextBox");
12663
12664
12665
12666 dojo.declare(
12667         "dijit.form.TextBox",
12668         dijit.form._FormValueWidget,
12669         {
12670                 // summary:
12671                 //              A base class for textbox form inputs
12672
12673                 // trim: Boolean
12674                 //              Removes leading and trailing whitespace if true.  Default is false.
12675                 trim: false,
12676
12677                 // uppercase: Boolean
12678                 //              Converts all characters to uppercase if true.  Default is false.
12679                 uppercase: false,
12680
12681                 // lowercase: Boolean
12682                 //              Converts all characters to lowercase if true.  Default is false.
12683                 lowercase: false,
12684
12685                 // propercase: Boolean
12686                 //              Converts the first character of each word to uppercase if true.
12687                 propercase: false,
12688
12689                 // maxLength: String
12690                 //              HTML INPUT tag maxLength declaration.
12691                 maxLength: "",
12692
12693                 // selectOnClick: [const] Boolean
12694                 //              If true, all text will be selected when focused with mouse
12695                 selectOnClick: false,
12696
12697                 // placeHolder: String
12698                 //              Defines a hint to help users fill out the input field (as defined in HTML 5).
12699                 //              This should only contain plain text (no html markup).
12700                 placeHolder: "",
12701                 
12702                 templateString: dojo.cache("dijit.form", "templates/TextBox.html", "<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"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"),
12703                 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" dojoAttachPoint="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
12704
12705                 _buttonInputDisabled: dojo.isIE ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
12706
12707                 baseClass: "dijitTextBox",
12708
12709                 attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
12710                         maxLength: "focusNode"
12711                 }),
12712                 
12713                 postMixInProperties: function(){
12714                         var type = this.type.toLowerCase();
12715                         if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == dijit.form.TextBox.prototype.templateString)){
12716                                 this.templateString = this._singleNodeTemplate;
12717                         }
12718                         this.inherited(arguments);
12719                 },
12720
12721                 _setPlaceHolderAttr: function(v){
12722                         this._set("placeHolder", v);
12723                         if(!this._phspan){
12724                                 this._attachPoints.push('_phspan');
12725                                 /* dijitInputField class gives placeHolder same padding as the input field
12726                                  * parent node already has dijitInputField class but it doesn't affect this <span>
12727                                  * since it's position: absolute.
12728                                  */
12729                                 this._phspan = dojo.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
12730                         }
12731                         this._phspan.innerHTML="";
12732                         this._phspan.appendChild(document.createTextNode(v));
12733                         
12734                         this._updatePlaceHolder();
12735                 },
12736                 
12737                 _updatePlaceHolder: function(){
12738                         if(this._phspan){
12739                                 this._phspan.style.display=(this.placeHolder&&!this._focused&&!this.textbox.value)?"":"none";
12740                         }
12741                 },
12742
12743                 _getValueAttr: function(){
12744                         // summary:
12745                         //              Hook so get('value') works as we like.
12746                         // description:
12747                         //              For `dijit.form.TextBox` this basically returns the value of the <input>.
12748                         //
12749                         //              For `dijit.form.MappedTextBox` subclasses, which have both
12750                         //              a "displayed value" and a separate "submit value",
12751                         //              This treats the "displayed value" as the master value, computing the
12752                         //              submit value from it via this.parse().
12753                         return this.parse(this.get('displayedValue'), this.constraints);
12754                 },
12755
12756                 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
12757                         // summary:
12758                         //              Hook so set('value', ...) works.
12759                         //
12760                         // description:
12761                         //              Sets the value of the widget to "value" which can be of
12762                         //              any type as determined by the widget.
12763                         //
12764                         // value:
12765                         //              The visual element value is also set to a corresponding,
12766                         //              but not necessarily the same, value.
12767                         //
12768                         // formattedValue:
12769                         //              If specified, used to set the visual element value,
12770                         //              otherwise a computed visual value is used.
12771                         //
12772                         // priorityChange:
12773                         //              If true, an onChange event is fired immediately instead of
12774                         //              waiting for the next blur event.
12775
12776                         var filteredValue;
12777                         if(value !== undefined){
12778                                 // TODO: this is calling filter() on both the display value and the actual value.
12779                                 // I added a comment to the filter() definition about this, but it should be changed.
12780                                 filteredValue = this.filter(value);
12781                                 if(typeof formattedValue != "string"){
12782                                         if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
12783                                                 formattedValue = this.filter(this.format(filteredValue, this.constraints));
12784                                         }else{ formattedValue = ''; }
12785                                 }
12786                         }
12787                         if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
12788                                 this.textbox.value = formattedValue;
12789                                 this._set("displayedValue", this.get("displayedValue"));
12790                         }
12791
12792                         this._updatePlaceHolder();
12793
12794                         this.inherited(arguments, [filteredValue, priorityChange]);
12795                 },
12796
12797                 // displayedValue: String
12798                 //              For subclasses like ComboBox where the displayed value
12799                 //              (ex: Kentucky) and the serialized value (ex: KY) are different,
12800                 //              this represents the displayed value.
12801                 //
12802                 //              Setting 'displayedValue' through set('displayedValue', ...)
12803                 //              updates 'value', and vice-versa.  Otherwise 'value' is updated
12804                 //              from 'displayedValue' periodically, like onBlur etc.
12805                 //
12806                 //              TODO: move declaration to MappedTextBox?
12807                 //              Problem is that ComboBox references displayedValue,
12808                 //              for benefit of FilteringSelect.
12809                 displayedValue: "",
12810
12811                 getDisplayedValue: function(){
12812                         // summary:
12813                         //              Deprecated.  Use get('displayedValue') instead.
12814                         // tags:
12815                         //              deprecated
12816                         dojo.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
12817                         return this.get('displayedValue');
12818                 },
12819
12820                 _getDisplayedValueAttr: function(){
12821                         // summary:
12822                         //              Hook so get('displayedValue') works.
12823                         // description:
12824                         //              Returns the displayed value (what the user sees on the screen),
12825                         //              after filtering (ie, trimming spaces etc.).
12826                         //
12827                         //              For some subclasses of TextBox (like ComboBox), the displayed value
12828                         //              is different from the serialized value that's actually
12829                         //              sent to the server (see dijit.form.ValidationTextBox.serialize)
12830
12831                         // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
12832                         // this method
12833                         // TODO: this isn't really the displayed value when the user is typing
12834                         return this.filter(this.textbox.value);
12835                 },
12836
12837                 setDisplayedValue: function(/*String*/ value){
12838                         // summary:
12839                         //              Deprecated.  Use set('displayedValue', ...) instead.
12840                         // tags:
12841                         //              deprecated
12842                         dojo.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
12843                         this.set('displayedValue', value);
12844                 },
12845
12846                 _setDisplayedValueAttr: function(/*String*/ value){
12847                         // summary:
12848                         //              Hook so set('displayedValue', ...) works.
12849                         // description:
12850                         //              Sets the value of the visual element to the string "value".
12851                         //              The widget value is also set to a corresponding,
12852                         //              but not necessarily the same, value.
12853
12854                         if(value === null || value === undefined){ value = '' }
12855                         else if(typeof value != "string"){ value = String(value) }
12856
12857                         this.textbox.value = value;
12858
12859                         // sets the serialized value to something corresponding to specified displayedValue
12860                         // (if possible), and also updates the textbox.value, for example converting "123"
12861                         // to "123.00"
12862                         this._setValueAttr(this.get('value'), undefined);
12863
12864                         this._set("displayedValue", this.get('displayedValue'));
12865                 },
12866
12867                 format: function(/*String*/ value, /*Object*/ constraints){
12868                         // summary:
12869                         //              Replacable function to convert a value to a properly formatted string.
12870                         // tags:
12871                         //              protected extension
12872                         return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
12873                 },
12874
12875                 parse: function(/*String*/ value, /*Object*/ constraints){
12876                         // summary:
12877                         //              Replacable function to convert a formatted string to a value
12878                         // tags:
12879                         //              protected extension
12880
12881                         return value;   // String
12882                 },
12883
12884                 _refreshState: function(){
12885                         // summary:
12886                         //              After the user types some characters, etc., this method is
12887                         //              called to check the field for validity etc.  The base method
12888                         //              in `dijit.form.TextBox` does nothing, but subclasses override.
12889                         // tags:
12890                         //              protected
12891                 },
12892
12893                 _onInput: function(e){
12894                         if(e && e.type && /key/i.test(e.type) && e.keyCode){
12895                                 switch(e.keyCode){
12896                                         case dojo.keys.SHIFT:
12897                                         case dojo.keys.ALT:
12898                                         case dojo.keys.CTRL:
12899                                         case dojo.keys.TAB:
12900                                                 return;
12901                                 }
12902                         }
12903                         if(this.intermediateChanges){
12904                                 var _this = this;
12905                                 // the setTimeout allows the key to post to the widget input box
12906                                 setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
12907                         }
12908                         this._refreshState();
12909
12910                         // In case someone is watch()'ing for changes to displayedValue
12911                         this._set("displayedValue", this.get("displayedValue"));
12912                 },
12913
12914                 postCreate: function(){
12915                         if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
12916                                 // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
12917                                 setTimeout(dojo.hitch(this, function(){
12918                                 var s = dojo.getComputedStyle(this.domNode);
12919                                 if(s){
12920                                         var ff = s.fontFamily;
12921                                         if(ff){
12922                                                 var inputs = this.domNode.getElementsByTagName("INPUT");
12923                                                 if(inputs){
12924                                                         for(var i=0; i < inputs.length; i++){
12925                                                                 inputs[i].style.fontFamily = ff;
12926                                                         }
12927                                                 }
12928                                         }
12929                                 }
12930                                 }), 0);
12931                         }
12932
12933                         // setting the value here is needed since value="" in the template causes "undefined"
12934                         // and setting in the DOM (instead of the JS object) helps with form reset actions
12935                         this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
12936
12937                         this.inherited(arguments);
12938
12939                         if(dojo.isMoz || dojo.isOpera){
12940                                 this.connect(this.textbox, "oninput", "_onInput");
12941                         }else{
12942                                 this.connect(this.textbox, "onkeydown", "_onInput");
12943                                 this.connect(this.textbox, "onkeyup", "_onInput");
12944                                 this.connect(this.textbox, "onpaste", "_onInput");
12945                                 this.connect(this.textbox, "oncut", "_onInput");
12946                         }
12947                 },
12948
12949                 _blankValue: '', // if the textbox is blank, what value should be reported
12950                 filter: function(val){
12951                         // summary:
12952                         //              Auto-corrections (such as trimming) that are applied to textbox
12953                         //              value on blur or form submit.
12954                         // description:
12955                         //              For MappedTextBox subclasses, this is called twice
12956                         //                      - once with the display value
12957                         //                      - once the value as set/returned by set('value', ...)
12958                         //              and get('value'), ex: a Number for NumberTextBox.
12959                         //
12960                         //              In the latter case it does corrections like converting null to NaN.  In
12961                         //              the former case the NumberTextBox.filter() method calls this.inherited()
12962                         //              to execute standard trimming code in TextBox.filter().
12963                         //
12964                         //              TODO: break this into two methods in 2.0
12965                         //
12966                         // tags:
12967                         //              protected extension
12968                         if(val === null){ return this._blankValue; }
12969                         if(typeof val != "string"){ return val; }
12970                         if(this.trim){
12971                                 val = dojo.trim(val);
12972                         }
12973                         if(this.uppercase){
12974                                 val = val.toUpperCase();
12975                         }
12976                         if(this.lowercase){
12977                                 val = val.toLowerCase();
12978                         }
12979                         if(this.propercase){
12980                                 val = val.replace(/[^\s]+/g, function(word){
12981                                         return word.substring(0,1).toUpperCase() + word.substring(1);
12982                                 });
12983                         }
12984                         return val;
12985                 },
12986
12987                 _setBlurValue: function(){
12988                         this._setValueAttr(this.get('value'), true);
12989                 },
12990
12991                 _onBlur: function(e){
12992                         if(this.disabled){ return; }
12993                         this._setBlurValue();
12994                         this.inherited(arguments);
12995
12996                         if(this._selectOnClickHandle){
12997                                 this.disconnect(this._selectOnClickHandle);
12998                         }
12999                         if(this.selectOnClick && dojo.isMoz){
13000                                 this.textbox.selectionStart = this.textbox.selectionEnd = undefined; // clear selection so that the next mouse click doesn't reselect
13001                         }
13002                         
13003                         this._updatePlaceHolder();
13004                 },
13005
13006                 _onFocus: function(/*String*/ by){
13007                         if(this.disabled || this.readOnly){ return; }
13008
13009                         // Select all text on focus via click if nothing already selected.
13010                         // Since mouse-up will clear the selection need to defer selection until after mouse-up.
13011                         // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
13012                         if(this.selectOnClick && by == "mouse"){
13013                                 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
13014                                         // Only select all text on first click; otherwise users would have no way to clear
13015                                         // the selection.
13016                                         this.disconnect(this._selectOnClickHandle);
13017
13018                                         // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
13019                                         // and if not, then select all the text
13020                                         var textIsNotSelected;
13021                                         if(dojo.isIE){
13022                                                 var range = dojo.doc.selection.createRange();
13023                                                 var parent = range.parentElement();
13024                                                 textIsNotSelected = parent == this.textbox && range.text.length == 0;
13025                                         }else{
13026                                                 textIsNotSelected = this.textbox.selectionStart == this.textbox.selectionEnd;
13027                                         }
13028                                         if(textIsNotSelected){
13029                                                 dijit.selectInputText(this.textbox);
13030                                         }
13031                                 });
13032                         }
13033
13034                         this._updatePlaceHolder();
13035                         
13036                         // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
13037                         // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
13038                         this.inherited(arguments);
13039
13040                         this._refreshState();
13041                 },
13042
13043                 reset: function(){
13044                         // Overrides dijit._FormWidget.reset().
13045                         // Additionally resets the displayed textbox value to ''
13046                         this.textbox.value = '';
13047                         this.inherited(arguments);
13048                 }
13049         }
13050 );
13051
13052 dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
13053         // summary:
13054         //              Select text in the input element argument, from start (default 0), to stop (default end).
13055
13056         // TODO: use functions in _editor/selection.js?
13057         var _window = dojo.global;
13058         var _document = dojo.doc;
13059         element = dojo.byId(element);
13060         if(isNaN(start)){ start = 0; }
13061         if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
13062         dijit.focus(element);
13063         if(_document["selection"] && dojo.body()["createTextRange"]){ // IE
13064                 if(element.createTextRange){
13065                         var r = element.createTextRange();
13066                         r.collapse(true);
13067                         r.moveStart("character", -99999); // move to 0
13068                         r.moveStart("character", start); // delta from 0 is the correct position
13069                         r.moveEnd("character", stop-start);
13070                         r.select();
13071                 }
13072         }else if(_window["getSelection"]){
13073                 if(element.setSelectionRange){
13074                         element.setSelectionRange(start, stop);
13075                 }
13076         }
13077 };
13078
13079 }
13080
13081 if(!dojo._hasResource["dijit.Tooltip"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13082 dojo._hasResource["dijit.Tooltip"] = true;
13083 dojo.provide("dijit.Tooltip");
13084
13085
13086
13087
13088 dojo.declare(
13089         "dijit._MasterTooltip",
13090         [dijit._Widget, dijit._Templated],
13091         {
13092                 // summary:
13093                 //              Internal widget that holds the actual tooltip markup,
13094                 //              which occurs once per page.
13095                 //              Called by Tooltip widgets which are just containers to hold
13096                 //              the markup
13097                 // tags:
13098                 //              protected
13099
13100                 // duration: Integer
13101                 //              Milliseconds to fade in/fade out
13102                 duration: dijit.defaultDuration,
13103
13104                 templateString: dojo.cache("dijit", "templates/Tooltip.html", "<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" dojoAttachPoint=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" dojoAttachPoint=\"connectorNode\"></div\n></div>\n"),
13105
13106                 postCreate: function(){
13107                         dojo.body().appendChild(this.domNode);
13108
13109                         this.bgIframe = new dijit.BackgroundIframe(this.domNode);
13110
13111                         // Setup fade-in and fade-out functions.
13112                         this.fadeIn = dojo.fadeIn({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onShow") });
13113                         this.fadeOut = dojo.fadeOut({ node: this.domNode, duration: this.duration, onEnd: dojo.hitch(this, "_onHide") });
13114                 },
13115
13116                 show: function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
13117                         // summary:
13118                         //              Display tooltip w/specified contents to right of specified node
13119                         //              (To left if there's no space on the right, or if rtl == true)
13120
13121                         if(this.aroundNode && this.aroundNode === aroundNode){
13122                                 return;
13123                         }
13124
13125                         // reset width; it may have been set by orient() on a previous tooltip show()
13126                         this.domNode.width = "auto";
13127
13128                         if(this.fadeOut.status() == "playing"){
13129                                 // previous tooltip is being hidden; wait until the hide completes then show new one
13130                                 this._onDeck=arguments;
13131                                 return;
13132                         }
13133                         this.containerNode.innerHTML=innerHTML;
13134
13135                         var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, dijit.getPopupAroundAlignment((position && position.length) ? position : dijit.Tooltip.defaultPosition, !rtl), dojo.hitch(this, "orient"));
13136
13137                         // show it
13138                         dojo.style(this.domNode, "opacity", 0);
13139                         this.fadeIn.play();
13140                         this.isShowingNow = true;
13141                         this.aroundNode = aroundNode;
13142                 },
13143
13144                 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
13145                         // summary:
13146                         //              Private function to set CSS for tooltip node based on which position it's in.
13147                         //              This is called by the dijit popup code.   It will also reduce the tooltip's
13148                         //              width to whatever width is available
13149                         // tags:
13150                         //              protected
13151                         this.connectorNode.style.top = ""; //reset to default
13152                         
13153                         //Adjust the spaceAvailable width, without changing the spaceAvailable object
13154                         var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
13155
13156                         node.className = "dijitTooltip " +
13157                                 {
13158                                         "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
13159                                         "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
13160                                         "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
13161                                         "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
13162                                         "BR-BL": "dijitTooltipRight",
13163                                         "BL-BR": "dijitTooltipLeft"
13164                                 }[aroundCorner + "-" + tooltipCorner];
13165                                 
13166                         // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
13167                         this.domNode.style.width = "auto";
13168                         var size = dojo.contentBox(this.domNode);
13169                         
13170                         var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
13171                         var widthWasReduced = width < size.w;
13172                         
13173                         this.domNode.style.width = width+"px";
13174                                                 
13175                         //Adjust width for tooltips that have a really long word or a nowrap setting
13176                         if(widthWasReduced){
13177                                 this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
13178                                 var scrollWidth = this.containerNode.scrollWidth;
13179                                 this.containerNode.style.overflow = "visible"; //change it back
13180                                 if(scrollWidth > width){
13181                                         scrollWidth = scrollWidth + dojo.style(this.domNode,"paddingLeft") + dojo.style(this.domNode,"paddingRight");
13182                                         this.domNode.style.width = scrollWidth + "px";
13183                                 }
13184                         }
13185                         
13186                         // Reposition the tooltip connector.
13187                         if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
13188                                 var mb = dojo.marginBox(node);
13189                                 var tooltipConnectorHeight = this.connectorNode.offsetHeight;
13190                                 if(mb.h > spaceAvailable.h){
13191                                         // The tooltip starts at the top of the page and will extend past the aroundNode
13192                                         var aroundNodePlacement = spaceAvailable.h - (aroundNodeCoords.h / 2) - (tooltipConnectorHeight / 2);
13193                                         this.connectorNode.style.top = aroundNodePlacement + "px";
13194                                         this.connectorNode.style.bottom = "";
13195                                 }else{
13196                                         // Align center of connector with center of aroundNode, except don't let bottom
13197                                         // of connector extend below bottom of tooltip content, or top of connector
13198                                         // extend past top of tooltip content
13199                                         this.connectorNode.style.bottom = Math.min(
13200                                                 Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
13201                                                 mb.h - tooltipConnectorHeight) + "px";
13202                                         this.connectorNode.style.top = "";
13203                                 }
13204                         }else{
13205                                 // reset the tooltip back to the defaults
13206                                 this.connectorNode.style.top = "";
13207                                 this.connectorNode.style.bottom = "";
13208                         }
13209                         
13210                         return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
13211                 },
13212
13213                 _onShow: function(){
13214                         // summary:
13215                         //              Called at end of fade-in operation
13216                         // tags:
13217                         //              protected
13218                         if(dojo.isIE){
13219                                 // the arrow won't show up on a node w/an opacity filter
13220                                 this.domNode.style.filter="";
13221                         }
13222                 },
13223
13224                 hide: function(aroundNode){
13225                         // summary:
13226                         //              Hide the tooltip
13227
13228                         if(this._onDeck && this._onDeck[1] == aroundNode){
13229                                 // this hide request is for a show() that hasn't even started yet;
13230                                 // just cancel the pending show()
13231                                 this._onDeck=null;
13232                         }else if(this.aroundNode === aroundNode){
13233                                 // this hide request is for the currently displayed tooltip
13234                                 this.fadeIn.stop();
13235                                 this.isShowingNow = false;
13236                                 this.aroundNode = null;
13237                                 this.fadeOut.play();
13238                         }else{
13239                                 // just ignore the call, it's for a tooltip that has already been erased
13240                         }
13241                 },
13242
13243                 _onHide: function(){
13244                         // summary:
13245                         //              Called at end of fade-out operation
13246                         // tags:
13247                         //              protected
13248
13249                         this.domNode.style.cssText="";  // to position offscreen again
13250                         this.containerNode.innerHTML="";
13251                         if(this._onDeck){
13252                                 // a show request has been queued up; do it now
13253                                 this.show.apply(this, this._onDeck);
13254                                 this._onDeck=null;
13255                         }
13256                 }
13257
13258         }
13259 );
13260
13261 dijit.showTooltip = function(/*String*/ innerHTML, /*DomNode*/ aroundNode, /*String[]?*/ position, /*Boolean*/ rtl){
13262         // summary:
13263         //              Display tooltip w/specified contents in specified position.
13264         //              See description of dijit.Tooltip.defaultPosition for details on position parameter.
13265         //              If position is not specified then dijit.Tooltip.defaultPosition is used.
13266         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
13267         return dijit._masterTT.show(innerHTML, aroundNode, position, rtl);
13268 };
13269
13270 dijit.hideTooltip = function(aroundNode){
13271         // summary:
13272         //              Hide the tooltip
13273         if(!dijit._masterTT){ dijit._masterTT = new dijit._MasterTooltip(); }
13274         return dijit._masterTT.hide(aroundNode);
13275 };
13276
13277 dojo.declare(
13278         "dijit.Tooltip",
13279         dijit._Widget,
13280         {
13281                 // summary:
13282                 //              Pops up a tooltip (a help message) when you hover over a node.
13283
13284                 // label: String
13285                 //              Text to display in the tooltip.
13286                 //              Specified as innerHTML when creating the widget from markup.
13287                 label: "",
13288
13289                 // showDelay: Integer
13290                 //              Number of milliseconds to wait after hovering over/focusing on the object, before
13291                 //              the tooltip is displayed.
13292                 showDelay: 400,
13293
13294                 // connectId: String|String[]
13295                 //              Id of domNode(s) to attach the tooltip to.
13296                 //              When user hovers over specified dom node, the tooltip will appear.
13297                 connectId: [],
13298
13299                 // position: String[]
13300                 //              See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
13301                 position: [],
13302
13303                 _setConnectIdAttr: function(/*String*/ newId){
13304                         // summary:
13305                         //              Connect to node(s) (specified by id)
13306
13307                         // Remove connections to old nodes (if there are any)
13308                         dojo.forEach(this._connections || [], function(nested){
13309                                 dojo.forEach(nested, dojo.hitch(this, "disconnect"));
13310                         }, this);
13311
13312                         // Make connections to nodes in newIds.
13313                         var ary = dojo.isArrayLike(newId) ? newId : (newId ? [newId] : []);
13314                         this._connections = dojo.map(ary, function(id){
13315                                 var node = dojo.byId(id);
13316                                 return node ? [
13317                                         this.connect(node, "onmouseenter", "_onTargetMouseEnter"),
13318                                         this.connect(node, "onmouseleave", "_onTargetMouseLeave"),
13319                                         this.connect(node, "onfocus", "_onTargetFocus"),
13320                                         this.connect(node, "onblur", "_onTargetBlur")
13321                                 ] : [];
13322                         }, this);
13323         
13324                         this._set("connectId", newId);
13325
13326                         this._connectIds = ary; // save as array
13327                 },
13328
13329                 addTarget: function(/*DOMNODE || String*/ node){
13330                         // summary:
13331                         //              Attach tooltip to specified node if it's not already connected
13332
13333                         // TODO: remove in 2.0 and just use set("connectId", ...) interface
13334
13335                         var id = node.id || node;
13336                         if(dojo.indexOf(this._connectIds, id) == -1){
13337                                 this.set("connectId", this._connectIds.concat(id));
13338                         }
13339                 },
13340
13341                 removeTarget: function(/*DOMNODE || String*/ node){
13342                         // summary:
13343                         //              Detach tooltip from specified node
13344
13345                         // TODO: remove in 2.0 and just use set("connectId", ...) interface
13346                         
13347                         var id = node.id || node,       // map from DOMNode back to plain id string
13348                                 idx = dojo.indexOf(this._connectIds, id);
13349                         if(idx >= 0){
13350                                 // remove id (modifies original this._connectIds but that's OK in this case)
13351                                 this._connectIds.splice(idx, 1);
13352                                 this.set("connectId", this._connectIds);
13353                         }
13354                 },
13355
13356                 buildRendering: function(){
13357                         this.inherited(arguments);
13358                         dojo.addClass(this.domNode,"dijitTooltipData");
13359                 },
13360
13361                 startup: function(){
13362                         this.inherited(arguments);
13363
13364                         // If this tooltip was created in a template, or for some other reason the specified connectId[s]
13365                         // didn't exist during the widget's initialization, then connect now.
13366                         var ids = this.connectId;
13367                         dojo.forEach(dojo.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
13368                 },
13369
13370                 _onTargetMouseEnter: function(/*Event*/ e){
13371                         // summary:
13372                         //              Handler for mouseenter event on the target node
13373                         // tags:
13374                         //              private
13375                         this._onHover(e);
13376                 },
13377
13378                 _onTargetMouseLeave: function(/*Event*/ e){
13379                         // summary:
13380                         //              Handler for mouseleave event on the target node
13381                         // tags:
13382                         //              private
13383                         this._onUnHover(e);
13384                 },
13385
13386                 _onTargetFocus: function(/*Event*/ e){
13387                         // summary:
13388                         //              Handler for focus event on the target node
13389                         // tags:
13390                         //              private
13391
13392                         this._focus = true;
13393                         this._onHover(e);
13394                 },
13395
13396                 _onTargetBlur: function(/*Event*/ e){
13397                         // summary:
13398                         //              Handler for blur event on the target node
13399                         // tags:
13400                         //              private
13401
13402                         this._focus = false;
13403                         this._onUnHover(e);
13404                 },
13405
13406                 _onHover: function(/*Event*/ e){
13407                         // summary:
13408                         //              Despite the name of this method, it actually handles both hover and focus
13409                         //              events on the target node, setting a timer to show the tooltip.
13410                         // tags:
13411                         //              private
13412                         if(!this._showTimer){
13413                                 var target = e.target;
13414                                 this._showTimer = setTimeout(dojo.hitch(this, function(){this.open(target)}), this.showDelay);
13415                         }
13416                 },
13417
13418                 _onUnHover: function(/*Event*/ e){
13419                         // summary:
13420                         //              Despite the name of this method, it actually handles both mouseleave and blur
13421                         //              events on the target node, hiding the tooltip.
13422                         // tags:
13423                         //              private
13424
13425                         // keep a tooltip open if the associated element still has focus (even though the
13426                         // mouse moved away)
13427                         if(this._focus){ return; }
13428
13429                         if(this._showTimer){
13430                                 clearTimeout(this._showTimer);
13431                                 delete this._showTimer;
13432                         }
13433                         this.close();
13434                 },
13435
13436                 open: function(/*DomNode*/ target){
13437                         // summary:
13438                         //              Display the tooltip; usually not called directly.
13439                         // tags:
13440                         //              private
13441
13442                         if(this._showTimer){
13443                                 clearTimeout(this._showTimer);
13444                                 delete this._showTimer;
13445                         }
13446                         dijit.showTooltip(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight());
13447
13448                         this._connectNode = target;
13449                         this.onShow(target, this.position);
13450                 },
13451
13452                 close: function(){
13453                         // summary:
13454                         //              Hide the tooltip or cancel timer for show of tooltip
13455                         // tags:
13456                         //              private
13457
13458                         if(this._connectNode){
13459                                 // if tooltip is currently shown
13460                                 dijit.hideTooltip(this._connectNode);
13461                                 delete this._connectNode;
13462                                 this.onHide();
13463                         }
13464                         if(this._showTimer){
13465                                 // if tooltip is scheduled to be shown (after a brief delay)
13466                                 clearTimeout(this._showTimer);
13467                                 delete this._showTimer;
13468                         }
13469                 },
13470
13471                 onShow: function(target, position){
13472                         // summary:
13473                         //              Called when the tooltip is shown
13474                         // tags:
13475                         //              callback
13476                 },
13477
13478                 onHide: function(){
13479                         // summary:
13480                         //              Called when the tooltip is hidden
13481                         // tags:
13482                         //              callback
13483                 },
13484
13485                 uninitialize: function(){
13486                         this.close();
13487                         this.inherited(arguments);
13488                 }
13489         }
13490 );
13491
13492 // dijit.Tooltip.defaultPosition: String[]
13493 //              This variable controls the position of tooltips, if the position is not specified to
13494 //              the Tooltip widget or *TextBox widget itself.  It's an array of strings with the following values:
13495 //
13496 //                      * before: places tooltip to the left of the target node/widget, or to the right in
13497 //                        the case of RTL scripts like Hebrew and Arabic
13498 //                      * after: places tooltip to the right of the target node/widget, or to the left in
13499 //                        the case of RTL scripts like Hebrew and Arabic
13500 //                      * above: tooltip goes above target node
13501 //                      * below: tooltip goes below target node
13502 //
13503 //              The list is positions is tried, in order, until a position is found where the tooltip fits
13504 //              within the viewport.
13505 //
13506 //              Be careful setting this parameter.  A value of "above" may work fine until the user scrolls
13507 //              the screen so that there's no room above the target node.   Nodes with drop downs, like
13508 //              DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
13509 //              that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
13510 //              is only room below (or above) the target node, but not both.
13511 dijit.Tooltip.defaultPosition = ["after", "before"];
13512
13513 }
13514
13515 if(!dojo._hasResource["dijit.form.ValidationTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13516 dojo._hasResource["dijit.form.ValidationTextBox"] = true;
13517 dojo.provide("dijit.form.ValidationTextBox");
13518
13519
13520
13521
13522
13523
13524 /*=====
13525         dijit.form.ValidationTextBox.__Constraints = function(){
13526                 // locale: String
13527                 //              locale used for validation, picks up value from this widget's lang attribute
13528                 // _flags_: anything
13529                 //              various flags passed to regExpGen function
13530                 this.locale = "";
13531                 this._flags_ = "";
13532         }
13533 =====*/
13534
13535 dojo.declare(
13536         "dijit.form.ValidationTextBox",
13537         dijit.form.TextBox,
13538         {
13539                 // summary:
13540                 //              Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
13541                 // tags:
13542                 //              protected
13543
13544                 templateString: dojo.cache("dijit.form", "templates/ValidationTextBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"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"),
13545                 baseClass: "dijitTextBox dijitValidationTextBox",
13546
13547                 // required: Boolean
13548                 //              User is required to enter data into this field.
13549                 required: false,
13550
13551                 // promptMessage: String
13552                 //              If defined, display this hint string immediately on focus to the textbox, if empty.
13553                 //              Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
13554                 //              Think of this like a tooltip that tells the user what to do, not an error message
13555                 //              that tells the user what they've done wrong.
13556                 //
13557                 //              Message disappears when user starts typing.
13558                 promptMessage: "",
13559
13560                 // invalidMessage: String
13561                 //              The message to display if value is invalid.
13562                 //              The translated string value is read from the message file by default.
13563                 //              Set to "" to use the promptMessage instead.
13564                 invalidMessage: "$_unset_$",
13565
13566                 // missingMessage: String
13567                 //              The message to display if value is empty and the field is required.
13568                 //              The translated string value is read from the message file by default.
13569                 //              Set to "" to use the invalidMessage instead.
13570                 missingMessage: "$_unset_$",
13571
13572                 // message: String
13573                 //              Currently error/prompt message.
13574                 //              When using the default tooltip implementation, this will only be
13575                 //              displayed when the field is focused.
13576                 message: "",
13577
13578                 // constraints: dijit.form.ValidationTextBox.__Constraints
13579                 //              user-defined object needed to pass parameters to the validator functions
13580                 constraints: {},
13581
13582                 // regExp: [extension protected] String
13583                 //              regular expression string used to validate the input
13584                 //              Do not specify both regExp and regExpGen
13585                 regExp: ".*",
13586
13587                 regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ constraints){
13588                         // summary:
13589                         //              Overridable function used to generate regExp when dependent on constraints.
13590                         //              Do not specify both regExp and regExpGen.
13591                         // tags:
13592                         //              extension protected
13593                         return this.regExp; // String
13594                 },
13595
13596                 // state: [readonly] String
13597                 //              Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
13598                 state: "",
13599
13600                 // tooltipPosition: String[]
13601                 //              See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
13602                 tooltipPosition: [],
13603
13604                 _setValueAttr: function(){
13605                         // summary:
13606                         //              Hook so set('value', ...) works.
13607                         this.inherited(arguments);
13608                         this.validate(this._focused);
13609                 },
13610
13611                 validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
13612                         // summary:
13613                         //              Overridable function used to validate the text input against the regular expression.
13614                         // tags:
13615                         //              protected
13616                         return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
13617                                 (!this.required || !this._isEmpty(value)) &&
13618                                 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
13619                 },
13620
13621                 _isValidSubset: function(){
13622                         // summary:
13623                         //              Returns true if the value is either already valid or could be made valid by appending characters.
13624                         //              This is used for validation while the user [may be] still typing.
13625                         return this.textbox.value.search(this._partialre) == 0;
13626                 },
13627
13628                 isValid: function(/*Boolean*/ isFocused){
13629                         // summary:
13630                         //              Tests if value is valid.
13631                         //              Can override with your own routine in a subclass.
13632                         // tags:
13633                         //              protected
13634                         return this.validator(this.textbox.value, this.constraints);
13635                 },
13636
13637                 _isEmpty: function(value){
13638                         // summary:
13639                         //              Checks for whitespace
13640                         return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
13641                 },
13642
13643                 getErrorMessage: function(/*Boolean*/ isFocused){
13644                         // summary:
13645                         //              Return an error message to show if appropriate
13646                         // tags:
13647                         //              protected
13648                         return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
13649                 },
13650
13651                 getPromptMessage: function(/*Boolean*/ isFocused){
13652                         // summary:
13653                         //              Return a hint message to show when widget is first focused
13654                         // tags:
13655                         //              protected
13656                         return this.promptMessage; // String
13657                 },
13658
13659                 _maskValidSubsetError: true,
13660                 validate: function(/*Boolean*/ isFocused){
13661                         // summary:
13662                         //              Called by oninit, onblur, and onkeypress.
13663                         // description:
13664                         //              Show missing or invalid messages if appropriate, and highlight textbox field.
13665                         // tags:
13666                         //              protected
13667                         var message = "";
13668                         var isValid = this.disabled || this.isValid(isFocused);
13669                         if(isValid){ this._maskValidSubsetError = true; }
13670                         var isEmpty = this._isEmpty(this.textbox.value);
13671                         var isValidSubset = !isValid && isFocused && this._isValidSubset();
13672                         this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
13673                         dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
13674
13675                         if(this.state == "Error"){
13676                                 this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
13677                                 message = this.getErrorMessage(isFocused);
13678                         }else if(this.state == "Incomplete"){
13679                                 message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
13680                                 this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
13681                         }else if(isEmpty){
13682                                 message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
13683                         }
13684                         this.set("message", message);
13685
13686                         return isValid;
13687                 },
13688
13689                 displayMessage: function(/*String*/ message){
13690                         // summary:
13691                         //              Overridable method to display validation errors/hints.
13692                         //              By default uses a tooltip.
13693                         // tags:
13694                         //              extension
13695                         dijit.hideTooltip(this.domNode);
13696                         if(message && this._focused){
13697                                 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
13698                         }
13699                 },
13700
13701                 _refreshState: function(){
13702                         // Overrides TextBox._refreshState()
13703                         this.validate(this._focused);
13704                         this.inherited(arguments);
13705                 },
13706
13707                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
13708
13709                 constructor: function(){
13710                         this.constraints = {};
13711                 },
13712
13713                 _setConstraintsAttr: function(/*Object*/ constraints){
13714                         if(!constraints.locale && this.lang){
13715                                 constraints.locale = this.lang;
13716                         }
13717                         this._set("constraints", constraints);
13718                         this._computePartialRE();
13719                 },
13720
13721                 _computePartialRE: function(){
13722                         var p = this.regExpGen(this.constraints);
13723                         this.regExp = p;
13724                         var partialre = "";
13725                         // parse the regexp and produce a new regexp that matches valid subsets
13726                         // if the regexp is .* then there's no use in matching subsets since everything is valid
13727                         if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
13728                                 function (re){
13729                                         switch(re.charAt(0)){
13730                                                 case '{':
13731                                                 case '+':
13732                                                 case '?':
13733                                                 case '*':
13734                                                 case '^':
13735                                                 case '$':
13736                                                 case '|':
13737                                                 case '(':
13738                                                         partialre += re;
13739                                                         break;
13740                                                 case ")":
13741                                                         partialre += "|$)";
13742                                                         break;
13743                                                  default:
13744                                                         partialre += "(?:"+re+"|$)";
13745                                                         break;
13746                                         }
13747                                 }
13748                         );}
13749                         try{ // this is needed for now since the above regexp parsing needs more test verification
13750                                 "".search(partialre);
13751                         }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
13752                                 partialre = this.regExp;
13753                                 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
13754                         } // should never be here unless the original RE is bad or the parsing is bad
13755                         this._partialre = "^(?:" + partialre + ")$";
13756                 },
13757
13758                 postMixInProperties: function(){
13759                         this.inherited(arguments);
13760                         this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
13761                         if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
13762                         if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
13763                         if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
13764                         if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
13765                         this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
13766                 },
13767
13768                 _setDisabledAttr: function(/*Boolean*/ value){
13769                         this.inherited(arguments);      // call FormValueWidget._setDisabledAttr()
13770                         this._refreshState();
13771                 },
13772
13773                 _setRequiredAttr: function(/*Boolean*/ value){
13774                         this._set("required", value);
13775                         dijit.setWaiState(this.focusNode, "required", value);
13776                         this._refreshState();
13777                 },
13778
13779                 _setMessageAttr: function(/*String*/ message){
13780                         this._set("message", message);
13781                         this.displayMessage(message);
13782                 },
13783
13784                 reset:function(){
13785                         // Overrides dijit.form.TextBox.reset() by also
13786                         // hiding errors about partial matches
13787                         this._maskValidSubsetError = true;
13788                         this.inherited(arguments);
13789                 },
13790
13791                 _onBlur: function(){
13792                         // the message still exists but for back-compat, and to erase the tooltip
13793                         // (if the message is being displayed as a tooltip), call displayMessage('')
13794                         this.displayMessage('');
13795
13796                         this.inherited(arguments);
13797                 }
13798         }
13799 );
13800
13801 dojo.declare(
13802         "dijit.form.MappedTextBox",
13803         dijit.form.ValidationTextBox,
13804         {
13805                 // summary:
13806                 //              A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
13807                 //              a visible formatted display value, and a serializable
13808                 //              value in a hidden input field which is actually sent to the server.
13809                 // description:
13810                 //              The visible display may
13811                 //              be locale-dependent and interactive.  The value sent to the server is stored in a hidden
13812                 //              input field which uses the `name` attribute declared by the original widget.  That value sent
13813                 //              to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
13814                 //              locale-neutral.
13815                 // tags:
13816                 //              protected
13817
13818                 postMixInProperties: function(){
13819                         this.inherited(arguments);
13820
13821                         // we want the name attribute to go to the hidden <input>, not the displayed <input>,
13822                         // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
13823                         this.nameAttrSetting = "";
13824                 },
13825
13826                 serialize: function(/*anything*/ val, /*Object?*/ options){
13827                         // summary:
13828                         //              Overridable function used to convert the get('value') result to a canonical
13829                         //              (non-localized) string.  For example, will print dates in ISO format, and
13830                         //              numbers the same way as they are represented in javascript.
13831                         // tags:
13832                         //              protected extension
13833                         return val.toString ? val.toString() : ""; // String
13834                 },
13835
13836                 toString: function(){
13837                         // summary:
13838                         //              Returns widget as a printable string using the widget's value
13839                         // tags:
13840                         //              protected
13841                         var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
13842                         return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
13843                 },
13844
13845                 validate: function(){
13846                         // Overrides `dijit.form.TextBox.validate`
13847                         this.valueNode.value = this.toString();
13848                         return this.inherited(arguments);
13849                 },
13850
13851                 buildRendering: function(){
13852                         // Overrides `dijit._Templated.buildRendering`
13853
13854                         this.inherited(arguments);
13855
13856                         // Create a hidden <input> node with the serialized value used for submit
13857                         // (as opposed to the displayed value).
13858                         // Passing in name as markup rather than calling dojo.create() with an attrs argument
13859                         // to make dojo.query(input[name=...]) work on IE. (see #8660)
13860                         this.valueNode = dojo.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
13861                 },
13862
13863                 reset: function(){
13864                         // Overrides `dijit.form.ValidationTextBox.reset` to
13865                         // reset the hidden textbox value to ''
13866                         this.valueNode.value = '';
13867                         this.inherited(arguments);
13868                 }
13869         }
13870 );
13871
13872 /*=====
13873         dijit.form.RangeBoundTextBox.__Constraints = function(){
13874                 // min: Number
13875                 //              Minimum signed value.  Default is -Infinity
13876                 // max: Number
13877                 //              Maximum signed value.  Default is +Infinity
13878                 this.min = min;
13879                 this.max = max;
13880         }
13881 =====*/
13882
13883 dojo.declare(
13884         "dijit.form.RangeBoundTextBox",
13885         dijit.form.MappedTextBox,
13886         {
13887                 // summary:
13888                 //              Base class for textbox form widgets which defines a range of valid values.
13889
13890                 // rangeMessage: String
13891                 //              The message to display if value is out-of-range
13892                 rangeMessage: "",
13893
13894                 /*=====
13895                 // constraints: dijit.form.RangeBoundTextBox.__Constraints
13896                 constraints: {},
13897                 ======*/
13898
13899                 rangeCheck: function(/*Number*/ primitive, /*dijit.form.RangeBoundTextBox.__Constraints*/ constraints){
13900                         // summary:
13901                         //              Overridable function used to validate the range of the numeric input value.
13902                         // tags:
13903                         //              protected
13904                         return  ("min" in constraints? (this.compare(primitive,constraints.min) >= 0) : true) &&
13905                                 ("max" in constraints? (this.compare(primitive,constraints.max) <= 0) : true); // Boolean
13906                 },
13907
13908                 isInRange: function(/*Boolean*/ isFocused){
13909                         // summary:
13910                         //              Tests if the value is in the min/max range specified in constraints
13911                         // tags:
13912                         //              protected
13913                         return this.rangeCheck(this.get('value'), this.constraints);
13914                 },
13915
13916                 _isDefinitelyOutOfRange: function(){
13917                         // summary:
13918                         //              Returns true if the value is out of range and will remain
13919                         //              out of range even if the user types more characters
13920                         var val = this.get('value');
13921                         var isTooLittle = false;
13922                         var isTooMuch = false;
13923                         if("min" in this.constraints){
13924                                 var min = this.constraints.min;
13925                                 min = this.compare(val, ((typeof min == "number") && min >= 0 && val !=0) ? 0 : min);
13926                                 isTooLittle = (typeof min == "number") && min < 0;
13927                         }
13928                         if("max" in this.constraints){
13929                                 var max = this.constraints.max;
13930                                 max = this.compare(val, ((typeof max != "number") || max > 0) ? max : 0);
13931                                 isTooMuch = (typeof max == "number") && max > 0;
13932                         }
13933                         return isTooLittle || isTooMuch;
13934                 },
13935
13936                 _isValidSubset: function(){
13937                         // summary:
13938                         //              Overrides `dijit.form.ValidationTextBox._isValidSubset`.
13939                         //              Returns true if the input is syntactically valid, and either within
13940                         //              range or could be made in range by more typing.
13941                         return this.inherited(arguments) && !this._isDefinitelyOutOfRange();
13942                 },
13943
13944                 isValid: function(/*Boolean*/ isFocused){
13945                         // Overrides dijit.form.ValidationTextBox.isValid to check that the value is also in range.
13946                         return this.inherited(arguments) &&
13947                                 ((this._isEmpty(this.textbox.value) && !this.required) || this.isInRange(isFocused)); // Boolean
13948                 },
13949
13950                 getErrorMessage: function(/*Boolean*/ isFocused){
13951                         // Overrides dijit.form.ValidationTextBox.getErrorMessage to print "out of range" message if appropriate
13952                         var v = this.get('value');
13953                         if(v !== null && v !== '' && v !== undefined && (typeof v != "number" || !isNaN(v)) && !this.isInRange(isFocused)){ // don't check isInRange w/o a real value
13954                                 return this.rangeMessage; // String
13955                         }
13956                         return this.inherited(arguments);
13957                 },
13958
13959                 postMixInProperties: function(){
13960                         this.inherited(arguments);
13961                         if(!this.rangeMessage){
13962                                 this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
13963                                 this.rangeMessage = this.messages.rangeMessage;
13964                         }
13965                 },
13966
13967                 _setConstraintsAttr: function(/*Object*/ constraints){
13968                         this.inherited(arguments);
13969                         if(this.focusNode){ // not set when called from postMixInProperties
13970                                 if(this.constraints.min !== undefined){
13971                                         dijit.setWaiState(this.focusNode, "valuemin", this.constraints.min);
13972                                 }else{
13973                                         dijit.removeWaiState(this.focusNode, "valuemin");
13974                                 }
13975                                 if(this.constraints.max !== undefined){
13976                                         dijit.setWaiState(this.focusNode, "valuemax", this.constraints.max);
13977                                 }else{
13978                                         dijit.removeWaiState(this.focusNode, "valuemax");
13979                                 }
13980                         }
13981                 },
13982
13983                 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange){
13984                         // summary:
13985                         //              Hook so set('value', ...) works.
13986
13987                         dijit.setWaiState(this.focusNode, "valuenow", value);
13988                         this.inherited(arguments);
13989                 }
13990         }
13991 );
13992
13993 }
13994
13995 if(!dojo._hasResource["dijit.form.ComboBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
13996 dojo._hasResource["dijit.form.ComboBox"] = true;
13997 dojo.provide("dijit.form.ComboBox");
13998
13999
14000
14001
14002
14003
14004
14005
14006
14007
14008
14009 dojo.declare(
14010         "dijit.form.ComboBoxMixin",
14011         dijit._HasDropDown,
14012         {
14013                 // summary:
14014                 //              Implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
14015                 // description:
14016                 //              All widgets that mix in dijit.form.ComboBoxMixin must extend `dijit.form._FormValueWidget`.
14017                 // tags:
14018                 //              protected
14019
14020                 // item: Object
14021                 //              This is the item returned by the dojo.data.store implementation that
14022                 //              provides the data for this ComboBox, it's the currently selected item.
14023                 item: null,
14024
14025                 // pageSize: Integer
14026                 //              Argument to data provider.
14027                 //              Specifies number of search results per page (before hitting "next" button)
14028                 pageSize: Infinity,
14029
14030                 // store: [const] Object
14031                 //              Reference to data provider object used by this ComboBox
14032                 store: null,
14033
14034                 // fetchProperties: Object
14035                 //              Mixin to the dojo.data store's fetch.
14036                 //              For example, to set the sort order of the ComboBox menu, pass:
14037                 //      |       { sort: [{attribute:"name",descending: true}] }
14038                 //              To override the default queryOptions so that deep=false, do:
14039                 //      |       { queryOptions: {ignoreCase: true, deep: false} }
14040                 fetchProperties:{},
14041
14042                 // query: Object
14043                 //              A query that can be passed to 'store' to initially filter the items,
14044                 //              before doing further filtering based on `searchAttr` and the key.
14045                 //              Any reference to the `searchAttr` is ignored.
14046                 query: {},
14047
14048                 // autoComplete: Boolean
14049                 //              If user types in a partial string, and then tab out of the `<input>` box,
14050                 //              automatically copy the first entry displayed in the drop down list to
14051                 //              the `<input>` field
14052                 autoComplete: true,
14053
14054                 // highlightMatch: String
14055                 //              One of: "first", "all" or "none".
14056                 //
14057                 //              If the ComboBox/FilteringSelect opens with the search results and the searched
14058                 //              string can be found, it will be highlighted.  If set to "all"
14059                 //              then will probably want to change `queryExpr` parameter to '*${0}*'
14060                 //
14061                 //              Highlighting is only performed when `labelType` is "text", so as to not
14062                 //              interfere with any HTML markup an HTML label might contain.
14063                 highlightMatch: "first",
14064
14065                 // searchDelay: Integer
14066                 //              Delay in milliseconds between when user types something and we start
14067                 //              searching based on that value
14068                 searchDelay: 100,
14069
14070                 // searchAttr: String
14071                 //              Search for items in the data store where this attribute (in the item)
14072                 //              matches what the user typed
14073                 searchAttr: "name",
14074
14075                 // labelAttr: String?
14076                 //              The entries in the drop down list come from this attribute in the
14077                 //              dojo.data items.
14078                 //              If not specified, the searchAttr attribute is used instead.
14079                 labelAttr: "",
14080
14081                 // labelType: String
14082                 //              Specifies how to interpret the labelAttr in the data store items.
14083                 //              Can be "html" or "text".
14084                 labelType: "text",
14085
14086                 // queryExpr: String
14087                 //              This specifies what query ComboBox/FilteringSelect sends to the data store,
14088                 //              based on what the user has typed.  Changing this expression will modify
14089                 //              whether the drop down shows only exact matches, a "starting with" match,
14090                 //              etc.  Use it in conjunction with highlightMatch.
14091                 //              dojo.data query expression pattern.
14092                 //              `${0}` will be substituted for the user text.
14093                 //              `*` is used for wildcards.
14094                 //              `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
14095                 queryExpr: "${0}*",
14096
14097                 // ignoreCase: Boolean
14098                 //              Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
14099                 ignoreCase: true,
14100
14101                 // hasDownArrow: Boolean
14102                 //              Set this textbox to have a down arrow button, to display the drop down list.
14103                 //              Defaults to true.
14104                 hasDownArrow: true,
14105
14106                 templateString: dojo.cache("dijit.form", "templates/DropDownBox.html", "<div class=\"dijit dijitReset dijitInlineTable dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdojoAttachPoint=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"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=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"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\tdojoAttachPoint=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"),
14107
14108                 baseClass: "dijitTextBox dijitComboBox",
14109
14110                 // dropDownClass: [protected extension] String
14111                 //              Name of the dropdown widget class used to select a date/time.
14112                 //              Subclasses should specify this.
14113                 dropDownClass: "dijit.form._ComboBoxMenu",
14114
14115                 // Set classes like dijitDownArrowButtonHover depending on
14116                 // mouse action over button node
14117                 cssStateNodes: {
14118                         "_buttonNode": "dijitDownArrowButton"
14119                 },
14120
14121                 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
14122                 maxHeight: -1,
14123
14124                 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
14125                 _stopClickEvents: false,
14126
14127                 _getCaretPos: function(/*DomNode*/ element){
14128                         // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
14129                         var pos = 0;
14130                         if(typeof(element.selectionStart) == "number"){
14131                                 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
14132                                 pos = element.selectionStart;
14133                         }else if(dojo.isIE){
14134                                 // in the case of a mouse click in a popup being handled,
14135                                 // then the dojo.doc.selection is not the textarea, but the popup
14136                                 // var r = dojo.doc.selection.createRange();
14137                                 // hack to get IE 6 to play nice. What a POS browser.
14138                                 var tr = dojo.doc.selection.createRange().duplicate();
14139                                 var ntr = element.createTextRange();
14140                                 tr.move("character",0);
14141                                 ntr.move("character",0);
14142                                 try{
14143                                         // If control doesn't have focus, you get an exception.
14144                                         // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
14145                                         // There appears to be no workaround for this - googled for quite a while.
14146                                         ntr.setEndPoint("EndToEnd", tr);
14147                                         pos = String(ntr.text).replace(/\r/g,"").length;
14148                                 }catch(e){
14149                                         // If focus has shifted, 0 is fine for caret pos.
14150                                 }
14151                         }
14152                         return pos;
14153                 },
14154
14155                 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
14156                         location = parseInt(location);
14157                         dijit.selectInputText(element, location, location);
14158                 },
14159
14160                 _setDisabledAttr: function(/*Boolean*/ value){
14161                         // Additional code to set disabled state of ComboBox node.
14162                         // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
14163                         this.inherited(arguments);
14164                         dijit.setWaiState(this.domNode, "disabled", value);
14165                 },
14166
14167                 _abortQuery: function(){
14168                         // stop in-progress query
14169                         if(this.searchTimer){
14170                                 clearTimeout(this.searchTimer);
14171                                 this.searchTimer = null;
14172                         }
14173                         if(this._fetchHandle){
14174                                 if(this._fetchHandle.abort){ this._fetchHandle.abort(); }
14175                                 this._fetchHandle = null;
14176                         }
14177                 },
14178
14179                 _onInput: function(/*Event*/ evt){
14180                         // summary:
14181                         //              Handles paste events
14182                         if(!this.searchTimer && (evt.type == 'paste'/*IE|WebKit*/ || evt.type == 'input'/*Firefox*/) && this._lastInput != this.textbox.value){
14183                                 this.searchTimer = setTimeout(dojo.hitch(this, function(){
14184                                         this._onKey({charOrCode: 229}); // fake IME key to cause a search
14185                                 }), 100); // long delay that will probably be preempted by keyboard input
14186                         }
14187                         this.inherited(arguments);
14188                 },
14189
14190                 _onKey: function(/*Event*/ evt){
14191                         // summary:
14192                         //              Handles keyboard events
14193
14194                         var key = evt.charOrCode;
14195
14196                         // except for cutting/pasting case - ctrl + x/v
14197                         if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == dojo.keys.SHIFT){
14198                                 return; // throw out weird key combinations and spurious events
14199                         }
14200                         
14201                         var doSearch = false;
14202                         var pw = this.dropDown;
14203                         var dk = dojo.keys;
14204                         var highlighted = null;
14205                         this._prev_key_backspace = false;
14206                         this._abortQuery();
14207
14208                         // _HasDropDown will do some of the work:
14209                         //              1. when drop down is not yet shown:
14210                         //                      - if user presses the down arrow key, call loadDropDown()
14211                         //              2. when drop down is already displayed:
14212                         //                      - on ESC key, call closeDropDown()
14213                         //                      - otherwise, call dropDown.handleKey() to process the keystroke
14214                         this.inherited(arguments);
14215
14216                         if(this._opened){
14217                                 highlighted = pw.getHighlightedOption();
14218                         }
14219                         switch(key){
14220                                 case dk.PAGE_DOWN:
14221                                 case dk.DOWN_ARROW:
14222                                 case dk.PAGE_UP:
14223                                 case dk.UP_ARROW:
14224                                         // Keystroke caused ComboBox_menu to move to a different item.
14225                                         // Copy new item to <input> box.
14226                                         if(this._opened){
14227                                                 this._announceOption(highlighted);
14228                                         }
14229                                         dojo.stopEvent(evt);
14230                                         break;
14231
14232                                 case dk.ENTER:
14233                                         // prevent submitting form if user presses enter. Also
14234                                         // prevent accepting the value if either Next or Previous
14235                                         // are selected
14236                                         if(highlighted){
14237                                                 // only stop event on prev/next
14238                                                 if(highlighted == pw.nextButton){
14239                                                         this._nextSearch(1);
14240                                                         dojo.stopEvent(evt);
14241                                                         break;
14242                                                 }else if(highlighted == pw.previousButton){
14243                                                         this._nextSearch(-1);
14244                                                         dojo.stopEvent(evt);
14245                                                         break;
14246                                                 }
14247                                         }else{
14248                                                 // Update 'value' (ex: KY) according to currently displayed text
14249                                                 this._setBlurValue(); // set value if needed
14250                                                 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
14251                                         }
14252                                         // default case:
14253                                         // if enter pressed while drop down is open, or for FilteringSelect,
14254                                         // if we are in the middle of a query to convert a directly typed in value to an item,
14255                                         // prevent submit, but allow event to bubble
14256                                         if(this._opened || this._fetchHandle){
14257                                         evt.preventDefault();
14258                                         }
14259                                         // fall through
14260
14261                                 case dk.TAB:
14262                                         var newvalue = this.get('displayedValue');
14263                                         //      if the user had More Choices selected fall into the
14264                                         //      _onBlur handler
14265                                         if(pw && (
14266                                                 newvalue == pw._messages["previousMessage"] ||
14267                                                 newvalue == pw._messages["nextMessage"])
14268                                         ){
14269                                                 break;
14270                                         }
14271                                         if(highlighted){
14272                                                 this._selectOption();
14273                                         }
14274                                         if(this._opened){
14275                                                 this._lastQuery = null; // in case results come back later
14276                                                 this.closeDropDown();
14277                                         }
14278                                         break;
14279
14280                                 case ' ':
14281                                         if(highlighted){
14282                                                 // user is effectively clicking a choice in the drop down menu
14283                                                 dojo.stopEvent(evt);
14284                                                 this._selectOption();
14285                                                 this.closeDropDown();
14286                                         }else{
14287                                                 // user typed a space into the input box, treat as normal character
14288                                                 doSearch = true;
14289                                         }
14290                                         break;
14291
14292                                 case dk.DELETE:
14293                                 case dk.BACKSPACE:
14294                                         this._prev_key_backspace = true;
14295                                         doSearch = true;
14296                                         break;
14297
14298                                 default:
14299                                         // Non char keys (F1-F12 etc..)  shouldn't open list.
14300                                         // Ascii characters and IME input (Chinese, Japanese etc.) should.
14301                                         //IME input produces keycode == 229.
14302                                         doSearch = typeof key == 'string' || key == 229;
14303                         }
14304                         if(doSearch){
14305                                 // need to wait a tad before start search so that the event
14306                                 // bubbles through DOM and we have value visible
14307                                 this.item = undefined; // undefined means item needs to be set
14308                                 this.searchTimer = setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
14309                         }
14310                 },
14311
14312                 _autoCompleteText: function(/*String*/ text){
14313                         // summary:
14314                         //              Fill in the textbox with the first item from the drop down
14315                         //              list, and highlight the characters that were
14316                         //              auto-completed. For example, if user typed "CA" and the
14317                         //              drop down list appeared, the textbox would be changed to
14318                         //              "California" and "ifornia" would be highlighted.
14319
14320                         var fn = this.focusNode;
14321
14322                         // IE7: clear selection so next highlight works all the time
14323                         dijit.selectInputText(fn, fn.value.length);
14324                         // does text autoComplete the value in the textbox?
14325                         var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
14326                         if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
14327                                 var cpos = this._getCaretPos(fn);
14328                                 // only try to extend if we added the last character at the end of the input
14329                                 if((cpos+1) > fn.value.length){
14330                                         // only add to input node as we would overwrite Capitalisation of chars
14331                                         // actually, that is ok
14332                                         fn.value = text;//.substr(cpos);
14333                                         // visually highlight the autocompleted characters
14334                                         dijit.selectInputText(fn, cpos);
14335                                 }
14336                         }else{
14337                                 // text does not autoComplete; replace the whole value and highlight
14338                                 fn.value = text;
14339                                 dijit.selectInputText(fn);
14340                         }
14341                 },
14342
14343                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
14344                         // summary:
14345                         //              Callback when a search completes.
14346                         // description:
14347                         //              1. generates drop-down list and calls _showResultList() to display it
14348                         //              2. if this result list is from user pressing "more choices"/"previous choices"
14349                         //                      then tell screen reader to announce new option
14350                         this._fetchHandle = null;
14351                         if(     this.disabled ||
14352                                 this.readOnly ||
14353                                 (dataObject.query[this.searchAttr] != this._lastQuery)
14354                         ){
14355                                 return;
14356                         }
14357                         var wasSelected = this.dropDown._highlighted_option && dojo.hasClass(this.dropDown._highlighted_option, "dijitMenuItemSelected");
14358                         this.dropDown.clearResultList();
14359                         if(!results.length && !this._maxOptions){ // if no results and not just the previous choices button
14360                                 this.closeDropDown();
14361                                 return;
14362                         }
14363
14364                         // Fill in the textbox with the first item from the drop down list,
14365                         // and highlight the characters that were auto-completed. For
14366                         // example, if user typed "CA" and the drop down list appeared, the
14367                         // textbox would be changed to "California" and "ifornia" would be
14368                         // highlighted.
14369
14370                         dataObject._maxOptions = this._maxOptions;
14371                         var nodes = this.dropDown.createOptions(
14372                                 results,
14373                                 dataObject,
14374                                 dojo.hitch(this, "_getMenuLabelFromItem")
14375                         );
14376
14377                         // show our list (only if we have content, else nothing)
14378                         this._showResultList();
14379
14380                         // #4091:
14381                         //              tell the screen reader that the paging callback finished by
14382                         //              shouting the next choice
14383                         if(dataObject.direction){
14384                                 if(1 == dataObject.direction){
14385                                         this.dropDown.highlightFirstOption();
14386                                 }else if(-1 == dataObject.direction){
14387                                         this.dropDown.highlightLastOption();
14388                                 }
14389                                 if(wasSelected){
14390                                         this._announceOption(this.dropDown.getHighlightedOption());
14391                                 }
14392                         }else if(this.autoComplete && !this._prev_key_backspace
14393                                 // when the user clicks the arrow button to show the full list,
14394                                 // startSearch looks for "*".
14395                                 // it does not make sense to autocomplete
14396                                 // if they are just previewing the options available.
14397                                 && !/^[*]+$/.test(dataObject.query[this.searchAttr])){
14398                                         this._announceOption(nodes[1]); // 1st real item
14399                         }
14400                 },
14401
14402                 _showResultList: function(){
14403                         // summary:
14404                         //              Display the drop down if not already displayed, or if it is displayed, then
14405                         //              reposition it if necessary (reposition may be necessary if drop down's height changed).
14406
14407                         this.closeDropDown(true);
14408
14409                         // hide the tooltip
14410                         this.displayMessage("");
14411
14412                         this.openDropDown();
14413
14414                         dijit.setWaiState(this.domNode, "expanded", "true");
14415                 },
14416
14417                 loadDropDown: function(/*Function*/ callback){
14418                         // Overrides _HasDropDown.loadDropDown().
14419                         // This is called when user has pressed button icon or pressed the down arrow key
14420                         // to open the drop down.
14421                         
14422                         this._startSearchAll();
14423                 },
14424
14425                 isLoaded: function(){
14426                         // signal to _HasDropDown that it needs to call loadDropDown() to load the
14427                         // drop down asynchronously before displaying it
14428                         return false;
14429                 },
14430
14431                 closeDropDown: function(){
14432                         // Overrides _HasDropDown.closeDropDown().  Closes the drop down (assuming that it's open).
14433                         // This method is the callback when the user types ESC or clicking
14434                         // the button icon while the drop down is open.  It's also called by other code.
14435                         this._abortQuery();
14436                         if(this._opened){
14437                                 this.inherited(arguments);
14438                                 dijit.setWaiState(this.domNode, "expanded", "false");
14439                                 dijit.removeWaiState(this.focusNode,"activedescendant");
14440                         }
14441                 },
14442
14443                 _setBlurValue: function(){
14444                         // if the user clicks away from the textbox OR tabs away, set the
14445                         // value to the textbox value
14446                         // #4617:
14447                         //              if value is now more choices or previous choices, revert
14448                         //              the value
14449                         var newvalue = this.get('displayedValue');
14450                         var pw = this.dropDown;
14451                         if(pw && (
14452                                 newvalue == pw._messages["previousMessage"] ||
14453                                 newvalue == pw._messages["nextMessage"]
14454                                 )
14455                         ){
14456                                 this._setValueAttr(this._lastValueReported, true);
14457                         }else if(typeof this.item == "undefined"){
14458                                 // Update 'value' (ex: KY) according to currently displayed text
14459                                 this.item = null;
14460                                 this.set('displayedValue', newvalue);
14461                         }else{
14462                                 if(this.value != this._lastValueReported){
14463                                         dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true);
14464                                 }
14465                                 this._refreshState();
14466                         }
14467                 },
14468
14469                 _onBlur: function(){
14470                         // summary:
14471                         //              Called magically when focus has shifted away from this widget and it's drop down
14472                         this.closeDropDown();
14473                         this.inherited(arguments);
14474                 },
14475
14476                 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
14477                         // summary:
14478                         //              Set the displayed valued in the input box, and the hidden value
14479                         //              that gets submitted, based on a dojo.data store item.
14480                         // description:
14481                         //              Users shouldn't call this function; they should be calling
14482                         //              set('item', value)
14483                         // tags:
14484                         //              private
14485                         if(!displayedValue){
14486                                 displayedValue = this.store.getValue(item, this.searchAttr);
14487                         }
14488                         var value = this._getValueField() != this.searchAttr? this.store.getIdentity(item) : displayedValue;
14489                         this._set("item", item);
14490                         dijit.form.ComboBox.superclass._setValueAttr.call(this, value, priorityChange, displayedValue);
14491                 },
14492
14493                 _announceOption: function(/*Node*/ node){
14494                         // summary:
14495                         //              a11y code that puts the highlighted option in the textbox.
14496                         //              This way screen readers will know what is happening in the
14497                         //              menu.
14498
14499                         if(!node){
14500                                 return;
14501                         }
14502                         // pull the text value from the item attached to the DOM node
14503                         var newValue;
14504                         if(node == this.dropDown.nextButton ||
14505                                 node == this.dropDown.previousButton){
14506                                 newValue = node.innerHTML;
14507                                 this.item = undefined;
14508                                 this.value = '';
14509                         }else{
14510                                 newValue = this.store.getValue(node.item, this.searchAttr).toString();
14511                                 this.set('item', node.item, false, newValue);
14512                         }
14513                         // get the text that the user manually entered (cut off autocompleted text)
14514                         this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
14515                         // set up ARIA activedescendant
14516                         dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
14517                         // autocomplete the rest of the option to announce change
14518                         this._autoCompleteText(newValue);
14519                 },
14520
14521                 _selectOption: function(/*Event*/ evt){
14522                         // summary:
14523                         //              Menu callback function, called when an item in the menu is selected.
14524                         if(evt){
14525                                 this._announceOption(evt.target);
14526                         }
14527                         this.closeDropDown();
14528                         this._setCaretPos(this.focusNode, this.focusNode.value.length);
14529                         dijit.form._FormValueWidget.prototype._setValueAttr.call(this, this.value, true); // set this.value and fire onChange
14530                 },
14531
14532                 _startSearchAll: function(){
14533                         this._startSearch('');
14534                 },
14535
14536                 _startSearchFromInput: function(){
14537                         this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
14538                 },
14539
14540                 _getQueryString: function(/*String*/ text){
14541                         return dojo.string.substitute(this.queryExpr, [text]);
14542                 },
14543
14544                 _startSearch: function(/*String*/ key){
14545                         // summary:
14546                         //              Starts a search for elements matching key (key=="" means to return all items),
14547                         //              and calls _openResultList() when the search completes, to display the results.
14548                         if(!this.dropDown){
14549                                 var popupId = this.id + "_popup",
14550                                 dropDownConstructor = dojo.getObject(this.dropDownClass, false);
14551                                 this.dropDown = new dropDownConstructor({
14552                                         onChange: dojo.hitch(this, this._selectOption),
14553                                         id: popupId,
14554                                         dir: this.dir
14555                                 });
14556                                 dijit.removeWaiState(this.focusNode,"activedescendant");
14557                                 dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
14558                         }
14559                         // create a new query to prevent accidentally querying for a hidden
14560                         // value from FilteringSelect's keyField
14561                         var query = dojo.clone(this.query); // #5970
14562                         this._lastInput = key; // Store exactly what was entered by the user.
14563                         this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
14564                         // #5970: set _lastQuery, *then* start the timeout
14565                         // otherwise, if the user types and the last query returns before the timeout,
14566                         // _lastQuery won't be set and their input gets rewritten
14567                         this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
14568                                 this.searchTimer = null;
14569                                 var fetch = {
14570                                         queryOptions: {
14571                                                 ignoreCase: this.ignoreCase,
14572                                                 deep: true
14573                                         },
14574                                         query: query,
14575                                         onBegin: dojo.hitch(this, "_setMaxOptions"),
14576                                         onComplete: dojo.hitch(this, "_openResultList"),
14577                                         onError: function(errText){
14578                                                 _this._fetchHandle = null;
14579                                                 console.error('dijit.form.ComboBox: ' + errText);
14580                                                 _this.closeDropDown();
14581                                         },
14582                                         start: 0,
14583                                         count: this.pageSize
14584                                 };
14585                                 dojo.mixin(fetch, _this.fetchProperties);
14586                                 this._fetchHandle = _this.store.fetch(fetch);
14587
14588                                 var nextSearch = function(dataObject, direction){
14589                                         dataObject.start += dataObject.count*direction;
14590                                         // #4091:
14591                                         //              tell callback the direction of the paging so the screen
14592                                         //              reader knows which menu option to shout
14593                                         dataObject.direction = direction;
14594                                         this._fetchHandle = this.store.fetch(dataObject);
14595                                         this.focus();
14596                                 };
14597                                 this._nextSearch = this.dropDown.onPage = dojo.hitch(this, nextSearch, this._fetchHandle);
14598                         }, query, this), this.searchDelay);
14599                 },
14600
14601                 _setMaxOptions: function(size, request){
14602                          this._maxOptions = size;
14603                 },
14604
14605                 _getValueField: function(){
14606                         // summary:
14607                         //              Helper for postMixInProperties() to set this.value based on data inlined into the markup.
14608                         //              Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
14609                         return this.searchAttr;
14610                 },
14611
14612                 //////////// INITIALIZATION METHODS ///////////////////////////////////////
14613
14614                 constructor: function(){
14615                         this.query={};
14616                         this.fetchProperties={};
14617                 },
14618
14619                 postMixInProperties: function(){
14620                         if(!this.store){
14621                                 var srcNodeRef = this.srcNodeRef;
14622
14623                                 // if user didn't specify store, then assume there are option tags
14624                                 this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
14625
14626                                 // if there is no value set and there is an option list, set
14627                                 // the value to the first value to be consistent with native
14628                                 // Select
14629
14630                                 // Firefox and Safari set value
14631                                 // IE6 and Opera set selectedIndex, which is automatically set
14632                                 // by the selected attribute of an option tag
14633                                 // IE6 does not set value, Opera sets value = selectedIndex
14634                                 if(!("value" in this.params)){
14635                                         var item = (this.item = this.store.fetchSelectedItem());
14636                                         if(item){
14637                                                 var valueField = this._getValueField();
14638                                                 this.value = this.store.getValue(item, valueField);
14639                                         }
14640                                 }
14641                         }
14642
14643                         this.inherited(arguments);
14644                 },
14645
14646                 postCreate: function(){
14647                         // summary:
14648                         //              Subclasses must call this method from their postCreate() methods
14649                         // tags:
14650                         //              protected
14651
14652                         // find any associated label element and add to ComboBox node.
14653                         var label=dojo.query('label[for="'+this.id+'"]');
14654                         if(label.length){
14655                                 label[0].id = (this.id+"_label");
14656                                 dijit.setWaiState(this.domNode, "labelledby", label[0].id);
14657
14658                         }
14659                         this.inherited(arguments);
14660                 },
14661
14662                 _setHasDownArrowAttr: function(val){
14663                         this.hasDownArrow = val;
14664                         this._buttonNode.style.display = val ? "" : "none";
14665                 },
14666
14667                 _getMenuLabelFromItem: function(/*Item*/ item){
14668                         var label = this.labelFunc(item, this.store),
14669                                 labelType = this.labelType;
14670                         // If labelType is not "text" we don't want to screw any markup ot whatever.
14671                         if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
14672                                 label = this.doHighlight(label, this._escapeHtml(this._lastInput));
14673                                 labelType = "html";
14674                         }
14675                         return {html: labelType == "html", label: label};
14676                 },
14677
14678                 doHighlight: function(/*String*/ label, /*String*/ find){
14679                         // summary:
14680                         //              Highlights the string entered by the user in the menu.  By default this
14681                         //              highlights the first occurrence found. Override this method
14682                         //              to implement your custom highlighting.
14683                         // tags:
14684                         //              protected
14685
14686                         var
14687                                 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
14688                                 modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
14689                                 i = this.queryExpr.indexOf("${0}");
14690                         find = dojo.regexp.escapeString(find); // escape regexp special chars
14691                         return this._escapeHtml(label).replace(
14692                                 // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
14693                                 new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
14694                                 '<span class="dijitComboBoxHighlightMatch">$1</span>'
14695                         ); // returns String, (almost) valid HTML (entities encoded)
14696                 },
14697
14698                 _escapeHtml: function(/*String*/ str){
14699                         // TODO Should become dojo.html.entities(), when exists use instead
14700                         // summary:
14701                         //              Adds escape sequences for special characters in XML: &<>"'
14702                         str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
14703                                 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
14704                         return str; // string
14705                 },
14706
14707                 reset: function(){
14708                         // Overrides the _FormWidget.reset().
14709                         // Additionally reset the .item (to clean up).
14710                         this.item = null;
14711                         this.inherited(arguments);
14712                 },
14713
14714                 labelFunc: function(/*item*/ item, /*dojo.data.store*/ store){
14715                         // summary:
14716                         //              Computes the label to display based on the dojo.data store item.
14717                         // returns:
14718                         //              The label that the ComboBox should display
14719                         // tags:
14720                         //              private
14721
14722                         // Use toString() because XMLStore returns an XMLItem whereas this
14723                         // method is expected to return a String (#9354)
14724                         return store.getValue(item, this.labelAttr || this.searchAttr).toString(); // String
14725                 }
14726         }
14727 );
14728
14729 dojo.declare(
14730         "dijit.form._ComboBoxMenu",
14731         [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
14732         {
14733                 // summary:
14734                 //              Focus-less menu for internal use in `dijit.form.ComboBox`
14735                 // tags:
14736                 //              private
14737
14738                 templateString: "<ul class='dijitReset dijitMenu' dojoAttachEvent='onmousedown:_onMouseDown,onmouseup:_onMouseUp,onmouseover:_onMouseOver,onmouseout:_onMouseOut' style='overflow: \"auto\"; overflow-x: \"hidden\";'>"
14739                                 +"<li class='dijitMenuItem dijitMenuPreviousButton' dojoAttachPoint='previousButton' role='option'></li>"
14740                                 +"<li class='dijitMenuItem dijitMenuNextButton' dojoAttachPoint='nextButton' role='option'></li>"
14741                         +"</ul>",
14742
14743                 // _messages: Object
14744                 //              Holds "next" and "previous" text for paging buttons on drop down
14745                 _messages: null,
14746                 
14747                 baseClass: "dijitComboBoxMenu",
14748
14749                 postMixInProperties: function(){
14750                         this.inherited(arguments);
14751                         this._messages = dojo.i18n.getLocalization("dijit.form", "ComboBox", this.lang);
14752                 },
14753
14754                 buildRendering: function(){
14755                         this.inherited(arguments);
14756
14757                         // fill in template with i18n messages
14758                         this.previousButton.innerHTML = this._messages["previousMessage"];
14759                         this.nextButton.innerHTML = this._messages["nextMessage"];
14760                 },
14761
14762                 _setValueAttr: function(/*Object*/ value){
14763                         this.value = value;
14764                         this.onChange(value);
14765                 },
14766
14767                 // stubs
14768                 onChange: function(/*Object*/ value){
14769                         // summary:
14770                         //              Notifies ComboBox/FilteringSelect that user clicked an option in the drop down menu.
14771                         //              Probably should be called onSelect.
14772                         // tags:
14773                         //              callback
14774                 },
14775                 onPage: function(/*Number*/ direction){
14776                         // summary:
14777                         //              Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
14778                         // tags:
14779                         //              callback
14780                 },
14781
14782                 onClose: function(){
14783                         // summary:
14784                         //              Callback from dijit.popup code to this widget, notifying it that it closed
14785                         // tags:
14786                         //              private
14787                         this._blurOptionNode();
14788                 },
14789
14790                 _createOption: function(/*Object*/ item, labelFunc){
14791                         // summary:
14792                         //              Creates an option to appear on the popup menu subclassed by
14793                         //              `dijit.form.FilteringSelect`.
14794
14795                         var menuitem = dojo.create("li", {
14796                                 "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
14797                                 role: "option"
14798                         });
14799                         var labelObject = labelFunc(item);
14800                         if(labelObject.html){
14801                                 menuitem.innerHTML = labelObject.label;
14802                         }else{
14803                                 menuitem.appendChild(
14804                                         dojo.doc.createTextNode(labelObject.label)
14805                                 );
14806                         }
14807                         // #3250: in blank options, assign a normal height
14808                         if(menuitem.innerHTML == ""){
14809                                 menuitem.innerHTML = "&nbsp;";
14810                         }
14811                         menuitem.item=item;
14812                         return menuitem;
14813                 },
14814
14815                 createOptions: function(results, dataObject, labelFunc){
14816                         // summary:
14817                         //              Fills in the items in the drop down list
14818                         // results:
14819                         //              Array of dojo.data items
14820                         // dataObject:
14821                         //              dojo.data store
14822                         // labelFunc:
14823                         //              Function to produce a label in the drop down list from a dojo.data item
14824
14825                         //this._dataObject=dataObject;
14826                         //this._dataObject.onComplete=dojo.hitch(comboBox, comboBox._openResultList);
14827                         // display "Previous . . ." button
14828                         this.previousButton.style.display = (dataObject.start == 0) ? "none" : "";
14829                         dojo.attr(this.previousButton, "id", this.id + "_prev");
14830                         // create options using _createOption function defined by parent
14831                         // ComboBox (or FilteringSelect) class
14832                         // #2309:
14833                         //              iterate over cache nondestructively
14834                         dojo.forEach(results, function(item, i){
14835                                 var menuitem = this._createOption(item, labelFunc);
14836                                 dojo.attr(menuitem, "id", this.id + i);
14837                                 this.domNode.insertBefore(menuitem, this.nextButton);
14838                         }, this);
14839                         // display "Next . . ." button
14840                         var displayMore = false;
14841                         //Try to determine if we should show 'more'...
14842                         if(dataObject._maxOptions && dataObject._maxOptions != -1){
14843                                 if((dataObject.start + dataObject.count) < dataObject._maxOptions){
14844                                         displayMore = true;
14845                                 }else if((dataObject.start + dataObject.count) > dataObject._maxOptions && dataObject.count == results.length){
14846                                         //Weird return from a datastore, where a start + count > maxOptions
14847                                         // implies maxOptions isn't really valid and we have to go into faking it.
14848                                         //And more or less assume more if count == results.length
14849                                         displayMore = true;
14850                                 }
14851                         }else if(dataObject.count == results.length){
14852                                 //Don't know the size, so we do the best we can based off count alone.
14853                                 //So, if we have an exact match to count, assume more.
14854                                 displayMore = true;
14855                         }
14856
14857                         this.nextButton.style.display = displayMore ? "" : "none";
14858                         dojo.attr(this.nextButton,"id", this.id + "_next");
14859                         return this.domNode.childNodes;
14860                 },
14861
14862                 clearResultList: function(){
14863                         // summary:
14864                         //              Clears the entries in the drop down list, but of course keeps the previous and next buttons.
14865                         while(this.domNode.childNodes.length>2){
14866                                 this.domNode.removeChild(this.domNode.childNodes[this.domNode.childNodes.length-2]);
14867                         }
14868                         this._blurOptionNode();
14869                 },
14870
14871                 _onMouseDown: function(/*Event*/ evt){
14872                         dojo.stopEvent(evt);
14873                 },
14874
14875                 _onMouseUp: function(/*Event*/ evt){
14876                         if(evt.target === this.domNode || !this._highlighted_option){
14877                                 // !this._highlighted_option check to prevent immediate selection when menu appears on top
14878                                 // of <input>, see #9898.  Note that _HasDropDown also has code to prevent this.
14879                                 return;
14880                         }else if(evt.target == this.previousButton){
14881                                 this._blurOptionNode();
14882                                 this.onPage(-1);
14883                         }else if(evt.target == this.nextButton){
14884                                 this._blurOptionNode();
14885                                 this.onPage(1);
14886                         }else{
14887                                 var tgt = evt.target;
14888                                 // while the clicked node is inside the div
14889                                 while(!tgt.item){
14890                                         // recurse to the top
14891                                         tgt = tgt.parentNode;
14892                                 }
14893                                 this._setValueAttr({ target: tgt }, true);
14894                         }
14895                 },
14896
14897                 _onMouseOver: function(/*Event*/ evt){
14898                         if(evt.target === this.domNode){ return; }
14899                         var tgt = evt.target;
14900                         if(!(tgt == this.previousButton || tgt == this.nextButton)){
14901                                 // while the clicked node is inside the div
14902                                 while(!tgt.item){
14903                                         // recurse to the top
14904                                         tgt = tgt.parentNode;
14905                                 }
14906                         }
14907                         this._focusOptionNode(tgt);
14908                 },
14909
14910                 _onMouseOut: function(/*Event*/ evt){
14911                         if(evt.target === this.domNode){ return; }
14912                         this._blurOptionNode();
14913                 },
14914
14915                 _focusOptionNode: function(/*DomNode*/ node){
14916                         // summary:
14917                         //              Does the actual highlight.
14918                         if(this._highlighted_option != node){
14919                                 this._blurOptionNode();
14920                                 this._highlighted_option = node;
14921                                 dojo.addClass(this._highlighted_option, "dijitMenuItemSelected");
14922                         }
14923                 },
14924
14925                 _blurOptionNode: function(){
14926                         // summary:
14927                         //              Removes highlight on highlighted option.
14928                         if(this._highlighted_option){
14929                                 dojo.removeClass(this._highlighted_option, "dijitMenuItemSelected");
14930                                 this._highlighted_option = null;
14931                         }
14932                 },
14933
14934                 _highlightNextOption: function(){
14935                         // summary:
14936                         //              Highlight the item just below the current selection.
14937                         //              If nothing selected, highlight first option.
14938
14939                         // because each press of a button clears the menu,
14940                         // the highlighted option sometimes becomes detached from the menu!
14941                         // test to see if the option has a parent to see if this is the case.
14942                         if(!this.getHighlightedOption()){
14943                                 var fc = this.domNode.firstChild;
14944                                 this._focusOptionNode(fc.style.display == "none" ? fc.nextSibling : fc);
14945                         }else{
14946                                 var ns = this._highlighted_option.nextSibling;
14947                                 if(ns && ns.style.display != "none"){
14948                                         this._focusOptionNode(ns);
14949                                 }else{
14950                                         this.highlightFirstOption();
14951                                 }
14952                         }
14953                         // scrollIntoView is called outside of _focusOptionNode because in IE putting it inside causes the menu to scroll up on mouseover
14954                         dojo.window.scrollIntoView(this._highlighted_option);
14955                 },
14956
14957                 highlightFirstOption: function(){
14958                         // summary:
14959                         //              Highlight the first real item in the list (not Previous Choices).
14960                         var first = this.domNode.firstChild;
14961                         var second = first.nextSibling;
14962                         this._focusOptionNode(second.style.display == "none" ? first : second); // remotely possible that Previous Choices is the only thing in the list
14963                         dojo.window.scrollIntoView(this._highlighted_option);
14964                 },
14965
14966                 highlightLastOption: function(){
14967                         // summary:
14968                         //              Highlight the last real item in the list (not More Choices).
14969                         this._focusOptionNode(this.domNode.lastChild.previousSibling);
14970                         dojo.window.scrollIntoView(this._highlighted_option);
14971                 },
14972
14973                 _highlightPrevOption: function(){
14974                         // summary:
14975                         //              Highlight the item just above the current selection.
14976                         //              If nothing selected, highlight last option (if
14977                         //              you select Previous and try to keep scrolling up the list).
14978                         if(!this.getHighlightedOption()){
14979                                 var lc = this.domNode.lastChild;
14980                                 this._focusOptionNode(lc.style.display == "none" ? lc.previousSibling : lc);
14981                         }else{
14982                                 var ps = this._highlighted_option.previousSibling;
14983                                 if(ps && ps.style.display != "none"){
14984                                         this._focusOptionNode(ps);
14985                                 }else{
14986                                         this.highlightLastOption();
14987                                 }
14988                         }
14989                         dojo.window.scrollIntoView(this._highlighted_option);
14990                 },
14991
14992                 _page: function(/*Boolean*/ up){
14993                         // summary:
14994                         //              Handles page-up and page-down keypresses
14995
14996                         var scrollamount = 0;
14997                         var oldscroll = this.domNode.scrollTop;
14998                         var height = dojo.style(this.domNode, "height");
14999                         // if no item is highlighted, highlight the first option
15000                         if(!this.getHighlightedOption()){
15001                                 this._highlightNextOption();
15002                         }
15003                         while(scrollamount<height){
15004                                 if(up){
15005                                         // stop at option 1
15006                                         if(!this.getHighlightedOption().previousSibling ||
15007                                                 this._highlighted_option.previousSibling.style.display == "none"){
15008                                                 break;
15009                                         }
15010                                         this._highlightPrevOption();
15011                                 }else{
15012                                         // stop at last option
15013                                         if(!this.getHighlightedOption().nextSibling ||
15014                                                 this._highlighted_option.nextSibling.style.display == "none"){
15015                                                 break;
15016                                         }
15017                                         this._highlightNextOption();
15018                                 }
15019                                 // going backwards
15020                                 var newscroll=this.domNode.scrollTop;
15021                                 scrollamount+=(newscroll-oldscroll)*(up ? -1:1);
15022                                 oldscroll=newscroll;
15023                         }
15024                 },
15025
15026                 pageUp: function(){
15027                         // summary:
15028                         //              Handles pageup keypress.
15029                         //              TODO: just call _page directly from handleKey().
15030                         // tags:
15031                         //              private
15032                         this._page(true);
15033                 },
15034
15035                 pageDown: function(){
15036                         // summary:
15037                         //              Handles pagedown keypress.
15038                         //              TODO: just call _page directly from handleKey().
15039                         // tags:
15040                         //              private
15041                         this._page(false);
15042                 },
15043
15044                 getHighlightedOption: function(){
15045                         // summary:
15046                         //              Returns the highlighted option.
15047                         var ho = this._highlighted_option;
15048                         return (ho && ho.parentNode) ? ho : null;
15049                 },
15050
15051                 handleKey: function(evt){
15052                         // summary:
15053                         //              Handle keystroke event forwarded from ComboBox, returning false if it's
15054                         //              a keystroke I recognize and process, true otherwise.
15055                         switch(evt.charOrCode){
15056                                 case dojo.keys.DOWN_ARROW:
15057                                         this._highlightNextOption();
15058                                         return false;
15059                                 case dojo.keys.PAGE_DOWN:
15060                                         this.pageDown();
15061                                         return false;
15062                                 case dojo.keys.UP_ARROW:
15063                                         this._highlightPrevOption();
15064                                         return false;
15065                                 case dojo.keys.PAGE_UP:
15066                                         this.pageUp();
15067                                         return false;
15068                                 default:
15069                                         return true;
15070                         }
15071                 }
15072         }
15073 );
15074
15075 dojo.declare(
15076         "dijit.form.ComboBox",
15077         [dijit.form.ValidationTextBox, dijit.form.ComboBoxMixin],
15078         {
15079                 // summary:
15080                 //              Auto-completing text box, and base class for dijit.form.FilteringSelect.
15081                 //
15082                 // description:
15083                 //              The drop down box's values are populated from an class called
15084                 //              a data provider, which returns a list of values based on the characters
15085                 //              that the user has typed into the input box.
15086                 //              If OPTION tags are used as the data provider via markup,
15087                 //              then the OPTION tag's child text node is used as the widget value
15088                 //              when selected.  The OPTION tag's value attribute is ignored.
15089                 //              To set the default value when using OPTION tags, specify the selected
15090                 //              attribute on 1 of the child OPTION tags.
15091                 //
15092                 //              Some of the options to the ComboBox are actually arguments to the data
15093                 //              provider.
15094
15095                 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
15096                         // summary:
15097                         //              Hook so set('value', value) works.
15098                         // description:
15099                         //              Sets the value of the select.
15100                         this._set("item", null); // value not looked up in store
15101                         if(!value){ value = ''; } // null translates to blank
15102                         dijit.form.ValidationTextBox.prototype._setValueAttr.call(this, value, priorityChange, displayedValue);
15103                 }
15104         }
15105 );
15106
15107 dojo.declare("dijit.form._ComboBoxDataStore", null, {
15108         // summary:
15109         //              Inefficient but small data store specialized for inlined `dijit.form.ComboBox` data
15110         //
15111         // description:
15112         //              Provides a store for inlined data like:
15113         //
15114         //      |       <select>
15115         //      |               <option value="AL">Alabama</option>
15116         //      |               ...
15117         //
15118         //              Actually. just implements the subset of dojo.data.Read/Notification
15119         //              needed for ComboBox and FilteringSelect to work.
15120         //
15121         //              Note that an item is just a pointer to the <option> DomNode.
15122
15123         constructor: function( /*DomNode*/ root){
15124                 this.root = root;
15125                 if(root.tagName != "SELECT" && root.firstChild){
15126                         root = dojo.query("select", root);
15127                         if(root.length > 0){ // SELECT is a child of srcNodeRef
15128                                 root = root[0];
15129                         }else{ // no select, so create 1 to parent the option tags to define selectedIndex
15130                                 this.root.innerHTML = "<SELECT>"+this.root.innerHTML+"</SELECT>";
15131                                 root = this.root.firstChild;
15132                         }
15133                         this.root = root;
15134                 }
15135                 dojo.query("> option", root).forEach(function(node){
15136                         //      TODO: this was added in #3858 but unclear why/if it's needed;  doesn't seem to be.
15137                         //      If it is needed then can we just hide the select itself instead?
15138                         //node.style.display="none";
15139                         node.innerHTML = dojo.trim(node.innerHTML);
15140                 });
15141
15142         },
15143
15144         getValue: function(     /*item*/ item,
15145                                                 /*attribute-name-string*/ attribute,
15146                                                 /*value?*/ defaultValue){
15147                 return (attribute == "value") ? item.value : (item.innerText || item.textContent || '');
15148         },
15149
15150         isItemLoaded: function(/*anything*/ something){
15151                 return true;
15152         },
15153
15154         getFeatures: function(){
15155                 return {"dojo.data.api.Read": true, "dojo.data.api.Identity": true};
15156         },
15157
15158         _fetchItems: function(  /*Object*/ args,
15159                                                         /*Function*/ findCallback,
15160                                                         /*Function*/ errorCallback){
15161                 // summary:
15162                 //              See dojo.data.util.simpleFetch.fetch()
15163                 if(!args.query){ args.query = {}; }
15164                 if(!args.query.name){ args.query.name = ""; }
15165                 if(!args.queryOptions){ args.queryOptions = {}; }
15166                 var matcher = dojo.data.util.filter.patternToRegExp(args.query.name, args.queryOptions.ignoreCase),
15167                         items = dojo.query("> option", this.root).filter(function(option){
15168                                 return (option.innerText || option.textContent || '').match(matcher);
15169                         } );
15170                 if(args.sort){
15171                         items.sort(dojo.data.util.sorter.createSortFunction(args.sort, this));
15172                 }
15173                 findCallback(items, args);
15174         },
15175
15176         close: function(/*dojo.data.api.Request || args || null*/ request){
15177                 return;
15178         },
15179
15180         getLabel: function(/*item*/ item){
15181                 return item.innerHTML;
15182         },
15183
15184         getIdentity: function(/*item*/ item){
15185                 return dojo.attr(item, "value");
15186         },
15187
15188         fetchItemByIdentity: function(/*Object*/ args){
15189                 // summary:
15190                 //              Given the identity of an item, this method returns the item that has
15191                 //              that identity through the onItem callback.
15192                 //              Refer to dojo.data.api.Identity.fetchItemByIdentity() for more details.
15193                 //
15194                 // description:
15195                 //              Given arguments like:
15196                 //
15197                 //      |               {identity: "CA", onItem: function(item){...}
15198                 //
15199                 //              Call `onItem()` with the DOM node `<option value="CA">California</option>`
15200                 var item = dojo.query("> option[value='" + args.identity + "']", this.root)[0];
15201                 args.onItem(item);
15202         },
15203
15204         fetchSelectedItem: function(){
15205                 // summary:
15206                 //              Get the option marked as selected, like `<option selected>`.
15207                 //              Not part of dojo.data API.
15208                 var root = this.root,
15209                         si = root.selectedIndex;
15210                 return typeof si == "number"
15211                         ? dojo.query("> option:nth-child(" + (si != -1 ? si+1 : 1) + ")", root)[0]
15212                         : null; // dojo.data.Item
15213         }
15214 });
15215 //Mix in the simple fetch implementation to this class.
15216 dojo.extend(dijit.form._ComboBoxDataStore,dojo.data.util.simpleFetch);
15217
15218 }
15219
15220 if(!dojo._hasResource["dijit.form.FilteringSelect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15221 dojo._hasResource["dijit.form.FilteringSelect"] = true;
15222 dojo.provide("dijit.form.FilteringSelect");
15223
15224
15225
15226 dojo.declare(
15227         "dijit.form.FilteringSelect",
15228         [dijit.form.MappedTextBox, dijit.form.ComboBoxMixin],
15229         {
15230                 // summary:
15231                 //              An enhanced version of the HTML SELECT tag, populated dynamically
15232                 //
15233                 // description:
15234                 //              An enhanced version of the HTML SELECT tag, populated dynamically. It works
15235                 //              very nicely with very large data sets because it can load and page data as needed.
15236                 //              It also resembles ComboBox, but does not allow values outside of the provided ones.
15237                 //              If OPTION tags are used as the data provider via markup, then the
15238                 //              OPTION tag's child text node is used as the displayed value when selected
15239                 //              while the OPTION tag's value attribute is used as the widget value on form submit.
15240                 //              To set the default value when using OPTION tags, specify the selected
15241                 //              attribute on 1 of the child OPTION tags.
15242                 //
15243                 //              Similar features:
15244                 //                      - There is a drop down list of possible values.
15245                 //                      - You can only enter a value from the drop down list.  (You can't
15246                 //                              enter an arbitrary value.)
15247                 //                      - The value submitted with the form is the hidden value (ex: CA),
15248                 //                              not the displayed value a.k.a. label (ex: California)
15249                 //
15250                 //              Enhancements over plain HTML version:
15251                 //                      - If you type in some text then it will filter down the list of
15252                 //                              possible values in the drop down list.
15253                 //                      - List can be specified either as a static list or via a javascript
15254                 //                              function (that can get the list from a server)
15255
15256                 // required: Boolean
15257                 //              True (default) if user is required to enter a value into this field.
15258                 required: true,
15259
15260                 _lastDisplayedValue: "",
15261
15262                 _isValidSubset: function(){
15263                         return this._opened;
15264                 },
15265
15266                 isValid: function(){
15267                         // Overrides ValidationTextBox.isValid()
15268                         return this.item || (!this.required && this.get('displayedValue') == ""); // #5974
15269                 },
15270
15271                 _refreshState: function(){
15272                         if(!this.searchTimer){ // state will be refreshed after results are returned
15273                                 this.inherited(arguments);
15274                         }
15275                 },
15276
15277                 _callbackSetLabel: function(
15278                                                 /*Array*/ result,
15279                                                 /*Object*/ dataObject,
15280                                                 /*Boolean?*/ priorityChange){
15281                         // summary:
15282                         //              Callback from dojo.data after lookup of user entered value finishes
15283
15284                         // setValue does a synchronous lookup,
15285                         // so it calls _callbackSetLabel directly,
15286                         // and so does not pass dataObject
15287                         // still need to test against _lastQuery in case it came too late
15288                         if((dataObject && dataObject.query[this.searchAttr] != this._lastQuery) || (!dataObject && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
15289                                 return;
15290                         }
15291                         if(!result.length){
15292                                 //#3268: don't modify display value on bad input
15293                                 //#3285: change CSS to indicate error
15294                                 this.valueNode.value = "";
15295                                 dijit.form.TextBox.superclass._setValueAttr.call(this, "", priorityChange || (priorityChange === undefined && !this._focused));
15296                                 this._set("item", null);
15297                                 this.validate(this._focused);
15298                         }else{
15299                                 this.set('item', result[0], priorityChange);
15300                         }
15301                 },
15302
15303                 _openResultList: function(/*Object*/ results, /*Object*/ dataObject){
15304                         // Callback when a data store query completes.
15305                         // Overrides ComboBox._openResultList()
15306
15307                         // #3285: tap into search callback to see if user's query resembles a match
15308                         if(dataObject.query[this.searchAttr] != this._lastQuery){
15309                                 return;
15310                         }
15311                         dijit.form.ComboBoxMixin.prototype._openResultList.apply(this, arguments);
15312
15313                         if(this.item === undefined){ // item == undefined for keyboard search
15314                                 // If the search returned no items that means that the user typed
15315                                 // in something invalid (and they can't make it valid by typing more characters),
15316                                 // so flag the FilteringSelect as being in an invalid state
15317                                 this.validate(true);
15318                         }
15319                 },
15320
15321                 _getValueAttr: function(){
15322                         // summary:
15323                         //              Hook for get('value') to work.
15324
15325                         // don't get the textbox value but rather the previously set hidden value.
15326                         // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
15327                         return this.valueNode.value;
15328                 },
15329
15330                 _getValueField: function(){
15331                         // Overrides ComboBox._getValueField()
15332                         return "value";
15333                 },
15334
15335                 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange){
15336                         // summary:
15337                         //              Hook so set('value', value) works.
15338                         // description:
15339                         //              Sets the value of the select.
15340                         //              Also sets the label to the corresponding value by reverse lookup.
15341                         if(!this._onChangeActive){ priorityChange = null; }
15342                         this._lastQuery = value;
15343
15344                         if(value === null || value === ''){
15345                                 this._setDisplayedValueAttr('', priorityChange);
15346                                 return;
15347                         }
15348
15349                         //#3347: fetchItemByIdentity if no keyAttr specified
15350                         var self = this;
15351                         this.store.fetchItemByIdentity({
15352                                 identity: value,
15353                                 onItem: function(item){
15354                                         self._callbackSetLabel(item? [item] : [], undefined, priorityChange);
15355                                 }
15356                         });
15357                 },
15358
15359                 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
15360                         // summary:
15361                         //              Set the displayed valued in the input box, and the hidden value
15362                         //              that gets submitted, based on a dojo.data store item.
15363                         // description:
15364                         //              Users shouldn't call this function; they should be calling
15365                         //              set('item', value)
15366                         // tags:
15367                         //              private
15368                         this.inherited(arguments);
15369                         this.valueNode.value = this.value;
15370                         this._lastDisplayedValue = this.textbox.value;
15371                 },
15372
15373                 _getDisplayQueryString: function(/*String*/ text){
15374                         return text.replace(/([\\\*\?])/g, "\\$1");
15375                 },
15376
15377                 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
15378                         // summary:
15379                         //              Hook so set('displayedValue', label) works.
15380                         // description:
15381                         //              Sets textbox to display label. Also performs reverse lookup
15382                         //              to set the hidden value.  label should corresponding to item.searchAttr.
15383
15384                         if(label == null){ label = ''; }
15385
15386                         // This is called at initialization along with every custom setter.
15387                         // Usually (or always?) the call can be ignored.   If it needs to be
15388                         // processed then at least make sure that the XHR request doesn't trigger an onChange()
15389                         // event, even if it returns after creation has finished
15390                         if(!this._created){
15391                                 if(!("displayedValue" in this.params)){
15392                                         return;
15393                                 }
15394                                 priorityChange = false;
15395                         }
15396
15397                         // Do a reverse lookup to map the specified displayedValue to the hidden value.
15398                         // Note that if there's a custom labelFunc() this code
15399                         if(this.store){
15400                                 this.closeDropDown();
15401                                 var query = dojo.clone(this.query); // #6196: populate query with user-specifics
15402                                 // escape meta characters of dojo.data.util.filter.patternToRegExp().
15403                                 this._lastQuery = query[this.searchAttr] = this._getDisplayQueryString(label);
15404                                 // If the label is not valid, the callback will never set it,
15405                                 // so the last valid value will get the warning textbox.   Set the
15406                                 // textbox value now so that the impending warning will make
15407                                 // sense to the user
15408                                 this.textbox.value = label;
15409                                 this._lastDisplayedValue = label;
15410                                 this._set("displayedValue", label);     // for watch("displayedValue") notification
15411                                 var _this = this;
15412                                 var fetch = {
15413                                         query: query,
15414                                         queryOptions: {
15415                                                 ignoreCase: this.ignoreCase,
15416                                                 deep: true
15417                                         },
15418                                         onComplete: function(result, dataObject){
15419                                                 _this._fetchHandle = null;
15420                                                 dojo.hitch(_this, "_callbackSetLabel")(result, dataObject, priorityChange);
15421                                         },
15422                                         onError: function(errText){
15423                                                 _this._fetchHandle = null;
15424                                                 console.error('dijit.form.FilteringSelect: ' + errText);
15425                                                 dojo.hitch(_this, "_callbackSetLabel")([], undefined, false);
15426                                         }
15427                                 };
15428                                 dojo.mixin(fetch, this.fetchProperties);
15429                                 this._fetchHandle = this.store.fetch(fetch);
15430                         }
15431                 },
15432
15433                 undo: function(){
15434                         this.set('displayedValue', this._lastDisplayedValue);
15435                 }
15436         }
15437 );
15438
15439 }
15440
15441 if(!dojo._hasResource["dijit.form.Form"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15442 dojo._hasResource["dijit.form.Form"] = true;
15443 dojo.provide("dijit.form.Form");
15444
15445
15446
15447
15448
15449
15450 dojo.declare(
15451         "dijit.form.Form",
15452         [dijit._Widget, dijit._Templated, dijit.form._FormMixin, dijit.layout._ContentPaneResizeMixin],
15453         {
15454                 // summary:
15455                 //              Widget corresponding to HTML form tag, for validation and serialization
15456                 //
15457                 // example:
15458                 //      |       <form dojoType="dijit.form.Form" id="myForm">
15459                 //      |               Name: <input type="text" name="name" />
15460                 //      |       </form>
15461                 //      |       myObj = {name: "John Doe"};
15462                 //      |       dijit.byId('myForm').set('value', myObj);
15463                 //      |
15464                 //      |       myObj=dijit.byId('myForm').get('value');
15465
15466                 // HTML <FORM> attributes
15467
15468                 // name: String?
15469                 //              Name of form for scripting.
15470                 name: "",
15471
15472                 // action: String?
15473                 //              Server-side form handler.
15474                 action: "",
15475
15476                 // method: String?
15477                 //              HTTP method used to submit the form, either "GET" or "POST".
15478                 method: "",
15479
15480                 // encType: String?
15481                 //              Encoding type for the form, ex: application/x-www-form-urlencoded.
15482                 encType: "",
15483
15484                 // accept-charset: String?
15485                 //              List of supported charsets.
15486                 "accept-charset": "",
15487
15488                 // accept: String?
15489                 //              List of MIME types for file upload.
15490                 accept: "",
15491
15492                 // target: String?
15493                 //              Target frame for the document to be opened in.
15494                 target: "",
15495
15496                 templateString: "<form dojoAttachPoint='containerNode' dojoAttachEvent='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
15497
15498                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
15499                         action: "",
15500                         method: "",
15501                         encType: "",
15502                         "accept-charset": "",
15503                         accept: "",
15504                         target: ""
15505                 }),
15506
15507                 postMixInProperties: function(){
15508                         // Setup name=foo string to be referenced from the template (but only if a name has been specified)
15509                         // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660
15510                         this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
15511                         this.inherited(arguments);
15512                 },
15513
15514                 execute: function(/*Object*/ formContents){
15515                         // summary:
15516                         //              Deprecated: use submit()
15517                         // tags:
15518                         //              deprecated
15519                 },
15520
15521                 onExecute: function(){
15522                         // summary:
15523                         //              Deprecated: use onSubmit()
15524                         // tags:
15525                         //              deprecated
15526                 },
15527
15528                 _setEncTypeAttr: function(/*String*/ value){
15529                         this.encType = value;
15530                         dojo.attr(this.domNode, "encType", value);
15531                         if(dojo.isIE){ this.domNode.encoding = value; }
15532                 },
15533
15534                 postCreate: function(){
15535                         // IE tries to hide encType
15536                         // TODO: remove in 2.0, no longer necessary with data-dojo-params
15537                         if(dojo.isIE && this.srcNodeRef && this.srcNodeRef.attributes){
15538                                 var item = this.srcNodeRef.attributes.getNamedItem('encType');
15539                                 if(item && !item.specified && (typeof item.value == "string")){
15540                                         this.set('encType', item.value);
15541                                 }
15542                         }
15543                         this.inherited(arguments);
15544                 },
15545
15546                 reset: function(/*Event?*/ e){
15547                         // summary:
15548                         //              restores all widget values back to their init values,
15549                         //              calls onReset() which can cancel the reset by returning false
15550
15551                         // create fake event so we can know if preventDefault() is called
15552                         var faux = {
15553                                 returnValue: true, // the IE way
15554                                 preventDefault: function(){ // not IE
15555                                                         this.returnValue = false;
15556                                                 },
15557                                 stopPropagation: function(){},
15558                                 currentTarget: e ? e.target : this.domNode,
15559                                 target: e ? e.target : this.domNode
15560                         };
15561                         // if return value is not exactly false, and haven't called preventDefault(), then reset
15562                         if(!(this.onReset(faux) === false) && faux.returnValue){
15563                                 this.inherited(arguments, []);
15564                         }
15565                 },
15566
15567                 onReset: function(/*Event?*/ e){
15568                         // summary:
15569                         //              Callback when user resets the form. This method is intended
15570                         //              to be over-ridden. When the `reset` method is called
15571                         //              programmatically, the return value from `onReset` is used
15572                         //              to compute whether or not resetting should proceed
15573                         // tags:
15574                         //              callback
15575                         return true; // Boolean
15576                 },
15577
15578                 _onReset: function(e){
15579                         this.reset(e);
15580                         dojo.stopEvent(e);
15581                         return false;
15582                 },
15583
15584                 _onSubmit: function(e){
15585                         var fp = dijit.form.Form.prototype;
15586                         // TODO: remove this if statement beginning with 2.0
15587                         if(this.execute != fp.execute || this.onExecute != fp.onExecute){
15588                                 dojo.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
15589                                 this.onExecute();
15590                                 this.execute(this.getValues());
15591                         }
15592                         if(this.onSubmit(e) === false){ // only exactly false stops submit
15593                                 dojo.stopEvent(e);
15594                         }
15595                 },
15596
15597                 onSubmit: function(/*Event?*/ e){
15598                         // summary:
15599                         //              Callback when user submits the form.
15600                         // description:
15601                         //              This method is intended to be over-ridden, but by default it checks and
15602                         //              returns the validity of form elements. When the `submit`
15603                         //              method is called programmatically, the return value from
15604                         //              `onSubmit` is used to compute whether or not submission
15605                         //              should proceed
15606                         // tags:
15607                         //              extension
15608
15609                         return this.isValid(); // Boolean
15610                 },
15611
15612                 submit: function(){
15613                         // summary:
15614                         //              programmatically submit form if and only if the `onSubmit` returns true
15615                         if(!(this.onSubmit() === false)){
15616                                 this.containerNode.submit();
15617                         }
15618                 }
15619         }
15620 );
15621
15622 }
15623
15624 if(!dojo._hasResource["dijit.form.RadioButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15625 dojo._hasResource["dijit.form.RadioButton"] = true;
15626 dojo.provide("dijit.form.RadioButton");
15627
15628
15629
15630 // TODO: for 2.0, move the RadioButton code into this file
15631
15632 }
15633
15634 if(!dojo._hasResource["dijit.form._FormSelectWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
15635 dojo._hasResource["dijit.form._FormSelectWidget"] = true;
15636 dojo.provide("dijit.form._FormSelectWidget");
15637
15638
15639
15640
15641 /*=====
15642 dijit.form.__SelectOption = function(){
15643         // value: String
15644         //              The value of the option.  Setting to empty (or missing) will
15645         //              place a separator at that location
15646         // label: String
15647         //              The label for our option.  It can contain html tags.
15648         // selected: Boolean
15649         //              Whether or not we are a selected option
15650         // disabled: Boolean
15651         //              Whether or not this specific option is disabled
15652         this.value = value;
15653         this.label = label;
15654         this.selected = selected;
15655         this.disabled = disabled;
15656 }
15657 =====*/
15658
15659 dojo.declare("dijit.form._FormSelectWidget", dijit.form._FormValueWidget, {
15660         // summary:
15661         //              Extends _FormValueWidget in order to provide "select-specific"
15662         //              values - i.e., those values that are unique to <select> elements.
15663         //              This also provides the mechanism for reading the elements from
15664         //              a store, if desired.
15665
15666         // multiple: [const] Boolean
15667         //              Whether or not we are multi-valued
15668         multiple: false,
15669
15670         // options: dijit.form.__SelectOption[]
15671         //              The set of options for our select item.  Roughly corresponds to
15672         //              the html <option> tag.
15673         options: null,
15674
15675         // store: dojo.data.api.Identity
15676         //              A store which, at the very least impelements dojo.data.api.Identity
15677         //              to use for getting our list of options - rather than reading them
15678         //              from the <option> html tags.
15679         store: null,
15680
15681         // query: object
15682         //              A query to use when fetching items from our store
15683         query: null,
15684
15685         // queryOptions: object
15686         //              Query options to use when fetching from the store
15687         queryOptions: null,
15688
15689         // onFetch: Function
15690         //              A callback to do with an onFetch - but before any items are actually
15691         //              iterated over (i.e. to filter even futher what you want to add)
15692         onFetch: null,
15693
15694         // sortByLabel: Boolean
15695         //              Flag to sort the options returned from a store by the label of
15696         //              the store.
15697         sortByLabel: true,
15698
15699
15700         // loadChildrenOnOpen: Boolean
15701         //              By default loadChildren is called when the items are fetched from the
15702         //              store.  This property allows delaying loadChildren (and the creation
15703         //              of the options/menuitems) until the user clicks the button to open the
15704         //              dropdown.
15705         loadChildrenOnOpen: false,
15706
15707         getOptions: function(/*anything*/ valueOrIdx){
15708                 // summary:
15709                 //              Returns a given option (or options).
15710                 // valueOrIdx:
15711                 //              If passed in as a string, that string is used to look up the option
15712                 //              in the array of options - based on the value property.
15713                 //              (See dijit.form.__SelectOption).
15714                 //
15715                 //              If passed in a number, then the option with the given index (0-based)
15716                 //              within this select will be returned.
15717                 //
15718                 //              If passed in a dijit.form.__SelectOption, the same option will be
15719                 //              returned if and only if it exists within this select.
15720                 //
15721                 //              If passed an array, then an array will be returned with each element
15722                 //              in the array being looked up.
15723                 //
15724                 //              If not passed a value, then all options will be returned
15725                 //
15726                 // returns:
15727                 //              The option corresponding with the given value or index.  null
15728                 //              is returned if any of the following are true:
15729                 //                      - A string value is passed in which doesn't exist
15730                 //                      - An index is passed in which is outside the bounds of the array of options
15731                 //                      - A dijit.form.__SelectOption is passed in which is not a part of the select
15732
15733                 // NOTE: the compare for passing in a dijit.form.__SelectOption checks
15734                 //              if the value property matches - NOT if the exact option exists
15735                 // NOTE: if passing in an array, null elements will be placed in the returned
15736                 //              array when a value is not found.
15737                 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
15738
15739                 if(lookupValue === undefined){
15740                         return opts; // dijit.form.__SelectOption[]
15741                 }
15742                 if(dojo.isArray(lookupValue)){
15743                         return dojo.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
15744                 }
15745                 if(dojo.isObject(valueOrIdx)){
15746                         // We were passed an option - so see if it's in our array (directly),
15747                         // and if it's not, try and find it by value.
15748                         if(!dojo.some(this.options, function(o, idx){
15749                                 if(o === lookupValue ||
15750                                         (o.value && o.value === lookupValue.value)){
15751                                         lookupValue = idx;
15752                                         return true;
15753                                 }
15754                                 return false;
15755                         })){
15756                                 lookupValue = -1;
15757                         }
15758                 }
15759                 if(typeof lookupValue == "string"){
15760                         for(var i=0; i<l; i++){
15761                                 if(opts[i].value === lookupValue){
15762                                         lookupValue = i;
15763                                         break;
15764                                 }
15765                         }
15766                 }
15767                 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
15768                         return this.options[lookupValue] // dijit.form.__SelectOption
15769                 }
15770                 return null; // null
15771         },
15772
15773         addOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ option){
15774                 // summary:
15775                 //              Adds an option or options to the end of the select.  If value
15776                 //              of the option is empty or missing, a separator is created instead.
15777                 //              Passing in an array of options will yield slightly better performance
15778                 //              since the children are only loaded once.
15779                 if(!dojo.isArray(option)){ option = [option]; }
15780                 dojo.forEach(option, function(i){
15781                         if(i && dojo.isObject(i)){
15782                                 this.options.push(i);
15783                         }
15784                 }, this);
15785                 this._loadChildren();
15786         },
15787
15788         removeOption: function(/*String|dijit.form.__SelectOption|Number|Array*/ valueOrIdx){
15789                 // summary:
15790                 //              Removes the given option or options.  You can remove by string
15791                 //              (in which case the value is removed), number (in which case the
15792                 //              index in the options array is removed), or select option (in
15793                 //              which case, the select option with a matching value is removed).
15794                 //              You can also pass in an array of those values for a slightly
15795                 //              better performance since the children are only loaded once.
15796                 if(!dojo.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
15797                 var oldOpts = this.getOptions(valueOrIdx);
15798                 dojo.forEach(oldOpts, function(i){
15799                         // We can get null back in our array - if our option was not found.  In
15800                         // that case, we don't want to blow up...
15801                         if(i){
15802                                 this.options = dojo.filter(this.options, function(node, idx){
15803                                         return (node.value !== i.value || node.label !== i.label);
15804                                 });
15805                                 this._removeOptionItem(i);
15806                         }
15807                 }, this);
15808                 this._loadChildren();
15809         },
15810
15811         updateOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ newOption){
15812                 // summary:
15813                 //              Updates the values of the given option.  The option to update
15814                 //              is matched based on the value of the entered option.  Passing
15815                 //              in an array of new options will yeild better performance since
15816                 //              the children will only be loaded once.
15817                 if(!dojo.isArray(newOption)){ newOption = [newOption]; }
15818                 dojo.forEach(newOption, function(i){
15819                         var oldOpt = this.getOptions(i), k;
15820                         if(oldOpt){
15821                                 for(k in i){ oldOpt[k] = i[k]; }
15822                         }
15823                 }, this);
15824                 this._loadChildren();
15825         },
15826
15827         setStore: function(/*dojo.data.api.Identity*/ store,
15828                                                 /*anything?*/ selectedValue,
15829                                                 /*Object?*/ fetchArgs){
15830                 // summary:
15831                 //              Sets the store you would like to use with this select widget.
15832                 //              The selected value is the value of the new store to set.  This
15833                 //              function returns the original store, in case you want to reuse
15834                 //              it or something.
15835                 // store: dojo.data.api.Identity
15836                 //              The store you would like to use - it MUST implement Identity,
15837                 //              and MAY implement Notification.
15838                 // selectedValue: anything?
15839                 //              The value that this widget should set itself to *after* the store
15840                 //              has been loaded
15841                 // fetchArgs: Object?
15842                 //              The arguments that will be passed to the store's fetch() function
15843                 var oStore = this.store;
15844                 fetchArgs = fetchArgs || {};
15845                 if(oStore !== store){
15846                         // Our store has changed, so update our notifications
15847                         dojo.forEach(this._notifyConnections || [], dojo.disconnect);
15848                         delete this._notifyConnections;
15849                         if(store && store.getFeatures()["dojo.data.api.Notification"]){
15850                                 this._notifyConnections = [
15851                                         dojo.connect(store, "onNew", this, "_onNewItem"),
15852                                         dojo.connect(store, "onDelete", this, "_onDeleteItem"),
15853                                         dojo.connect(store, "onSet", this, "_onSetItem")
15854                                 ];
15855                         }
15856                         this._set("store", store);
15857                 }
15858
15859                 // Turn off change notifications while we make all these changes
15860                 this._onChangeActive = false;
15861
15862                 // Remove existing options (if there are any)
15863                 if(this.options && this.options.length){
15864                         this.removeOption(this.options);
15865                 }
15866
15867                 // Add our new options
15868                 if(store){
15869                         this._loadingStore = true;
15870                         store.fetch(dojo.delegate(fetchArgs, {
15871                                 onComplete: function(items, opts){
15872                                         if(this.sortByLabel && !fetchArgs.sort && items.length){
15873                                                 items.sort(dojo.data.util.sorter.createSortFunction([{
15874                                                         attribute: store.getLabelAttributes(items[0])[0]
15875                                                 }], store));
15876                                         }
15877         
15878                                         if(fetchArgs.onFetch){
15879                                                         items = fetchArgs.onFetch.call(this, items, opts);
15880                                         }
15881                                         // TODO: Add these guys as a batch, instead of separately
15882                                         dojo.forEach(items, function(i){
15883                                                 this._addOptionForItem(i);
15884                                         }, this);
15885         
15886                                         // Set our value (which might be undefined), and then tweak
15887                                         // it to send a change event with the real value
15888                                         this._loadingStore = false;
15889                                                 this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
15890                                         delete this._pendingValue;
15891         
15892                                         if(!this.loadChildrenOnOpen){
15893                                                 this._loadChildren();
15894                                         }else{
15895                                                 this._pseudoLoadChildren(items);
15896                                         }
15897                                         this._fetchedWith = opts;
15898                                         this._lastValueReported = this.multiple ? [] : null;
15899                                         this._onChangeActive = true;
15900                                         this.onSetStore();
15901                                         this._handleOnChange(this.value);
15902                                 },
15903                                 scope: this
15904                         }));
15905                 }else{
15906                         delete this._fetchedWith;
15907                 }
15908                 return oStore;  // dojo.data.api.Identity
15909         },
15910
15911         // TODO: implement set() and watch() for store and query, although not sure how to handle
15912         // setting them individually rather than together (as in setStore() above)
15913
15914         _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
15915                 // summary:
15916                 //              set the value of the widget.
15917                 //              If a string is passed, then we set our value from looking it up.
15918                 if(this._loadingStore){
15919                         // Our store is loading - so save our value, and we'll set it when
15920                         // we're done
15921                         this._pendingValue = newValue;
15922                         return;
15923                 }
15924                 var opts = this.getOptions() || [];
15925                 if(!dojo.isArray(newValue)){
15926                         newValue = [newValue];
15927                 }
15928                 dojo.forEach(newValue, function(i, idx){
15929                         if(!dojo.isObject(i)){
15930                                 i = i + "";
15931                         }
15932                         if(typeof i === "string"){
15933                                 newValue[idx] = dojo.filter(opts, function(node){
15934                                         return node.value === i;
15935                                 })[0] || {value: "", label: ""};
15936                         }
15937                 }, this);
15938
15939                 // Make sure some sane default is set
15940                 newValue = dojo.filter(newValue, function(i){ return i && i.value; });
15941                 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
15942                         newValue[0] = opts[0];
15943                 }
15944                 dojo.forEach(opts, function(i){
15945                         i.selected = dojo.some(newValue, function(v){ return v.value === i.value; });
15946                 });
15947                 var val = dojo.map(newValue, function(i){ return i.value; }),
15948                         disp = dojo.map(newValue, function(i){ return i.label; });
15949
15950                 this._set("value", this.multiple ? val : val[0]);
15951                 this._setDisplay(this.multiple ? disp : disp[0]);
15952                 this._updateSelection();
15953                 this._handleOnChange(this.value, priorityChange);
15954         },
15955
15956         _getDisplayedValueAttr: function(){
15957                 // summary:
15958                 //              returns the displayed value of the widget
15959                 var val = this.get("value");
15960                 if(!dojo.isArray(val)){
15961                         val = [val];
15962                 }
15963                 var ret = dojo.map(this.getOptions(val), function(v){
15964                         if(v && "label" in v){
15965                                 return v.label;
15966                         }else if(v){
15967                                 return v.value;
15968                         }
15969                         return null;
15970                 }, this);
15971                 return this.multiple ? ret : ret[0];
15972         },
15973
15974         _loadChildren: function(){
15975                 // summary:
15976                 //              Loads the children represented by this widget's options.
15977                 //              reset the menu to make it populatable on the next click
15978                 if(this._loadingStore){ return; }
15979                 dojo.forEach(this._getChildren(), function(child){
15980                         child.destroyRecursive();
15981                 });
15982                 // Add each menu item
15983                 dojo.forEach(this.options, this._addOptionItem, this);
15984
15985                 // Update states
15986                 this._updateSelection();
15987         },
15988
15989         _updateSelection: function(){
15990                 // summary:
15991                 //              Sets the "selected" class on the item for styling purposes
15992                 this._set("value", this._getValueFromOpts());
15993                 var val = this.value;
15994                 if(!dojo.isArray(val)){
15995                         val = [val];
15996                 }
15997                 if(val && val[0]){
15998                         dojo.forEach(this._getChildren(), function(child){
15999                                 var isSelected = dojo.some(val, function(v){
16000                                         return child.option && (v === child.option.value);
16001                                 });
16002                                 dojo.toggleClass(child.domNode, this.baseClass + "SelectedOption", isSelected);
16003                                 dijit.setWaiState(child.domNode, "selected", isSelected);
16004                         }, this);
16005                 }
16006         },
16007
16008         _getValueFromOpts: function(){
16009                 // summary:
16010                 //              Returns the value of the widget by reading the options for
16011                 //              the selected flag
16012                 var opts = this.getOptions() || [];
16013                 if(!this.multiple && opts.length){
16014                         // Mirror what a select does - choose the first one
16015                         var opt = dojo.filter(opts, function(i){
16016                                 return i.selected;
16017                         })[0];
16018                         if(opt && opt.value){
16019                                 return opt.value
16020                         }else{
16021                                 opts[0].selected = true;
16022                                 return opts[0].value;
16023                         }
16024                 }else if(this.multiple){
16025                         // Set value to be the sum of all selected
16026                         return dojo.map(dojo.filter(opts, function(i){
16027                                 return i.selected;
16028                         }), function(i){
16029                                 return i.value;
16030                         }) || [];
16031                 }
16032                 return "";
16033         },
16034
16035         // Internal functions to call when we have store notifications come in
16036         _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
16037                 if(!parentInfo || !parentInfo.parent){
16038                         // Only add it if we are top-level
16039                         this._addOptionForItem(item);
16040                 }
16041         },
16042         _onDeleteItem: function(/*item*/ item){
16043                 var store = this.store;
16044                 this.removeOption(store.getIdentity(item));
16045         },
16046         _onSetItem: function(/*item*/ item){
16047                 this.updateOption(this._getOptionObjForItem(item));
16048         },
16049
16050         _getOptionObjForItem: function(item){
16051                 // summary:
16052                 //              Returns an option object based off the given item.  The "value"
16053                 //              of the option item will be the identity of the item, the "label"
16054                 //              of the option will be the label of the item.  If the item contains
16055                 //              children, the children value of the item will be set
16056                 var store = this.store, label = store.getLabel(item),
16057                         value = (label ? store.getIdentity(item) : null);
16058                 return {value: value, label: label, item:item}; // dijit.form.__SelectOption
16059         },
16060
16061         _addOptionForItem: function(/*item*/ item){
16062                 // summary:
16063                 //              Creates (and adds) the option for the given item
16064                 var store = this.store;
16065                 if(!store.isItemLoaded(item)){
16066                         // We are not loaded - so let's load it and add later
16067                         store.loadItem({item: item, onComplete: function(i){
16068                                 this._addOptionForItem(item);
16069                         },
16070                         scope: this});
16071                         return;
16072                 }
16073                 var newOpt = this._getOptionObjForItem(item);
16074                 this.addOption(newOpt);
16075         },
16076
16077         constructor: function(/*Object*/ keywordArgs){
16078                 // summary:
16079                 //              Saves off our value, if we have an initial one set so we
16080                 //              can use it if we have a store as well (see startup())
16081                 this._oValue = (keywordArgs || {}).value || null;
16082         },
16083
16084         buildRendering: function(){
16085                 this.inherited(arguments);
16086                 dojo.setSelectable(this.focusNode, false);
16087         },
16088
16089         _fillContent: function(){
16090                 // summary:
16091                 //              Loads our options and sets up our dropdown correctly.  We
16092                 //              don't want any content, so we don't call any inherit chain
16093                 //              function.
16094                 var opts = this.options;
16095                 if(!opts){
16096                         opts = this.options = this.srcNodeRef ? dojo.query(">",
16097                                                 this.srcNodeRef).map(function(node){
16098                                                         if(node.getAttribute("type") === "separator"){
16099                                                                 return { value: "", label: "", selected: false, disabled: false };
16100                                                         }
16101                                                         return {
16102                                                                 value: (node.getAttribute("data-" + dojo._scopeName + "-value") || node.getAttribute("value")),
16103                                                                                 label: String(node.innerHTML),
16104                                                                 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
16105                                                                 // looking for data-dojo-value above.  perhaps we should data-dojo-props="" this whole thing?)
16106                                                                 // decide before 1.6
16107                                                                                 selected: node.getAttribute("selected") || false,
16108                                                                 disabled: node.getAttribute("disabled") || false
16109                                                         };
16110                                                 }, this) : [];
16111                 }
16112                 if(!this.value){
16113                         this._set("value", this._getValueFromOpts());
16114                 }else if(this.multiple && typeof this.value == "string"){
16115                         this_set("value", this.value.split(","));
16116                 }
16117         },
16118
16119         postCreate: function(){
16120                 // summary:
16121                 //              sets up our event handling that we need for functioning
16122                 //              as a select
16123                 this.inherited(arguments);
16124
16125                 // Make our event connections for updating state
16126                 this.connect(this, "onChange", "_updateSelection");
16127                 this.connect(this, "startup", "_loadChildren");
16128
16129                 this._setValueAttr(this.value, null);
16130         },
16131
16132         startup: function(){
16133                 // summary:
16134                 //              Connects in our store, if we have one defined
16135                 this.inherited(arguments);
16136                 var store = this.store, fetchArgs = {};
16137                 dojo.forEach(["query", "queryOptions", "onFetch"], function(i){
16138                         if(this[i]){
16139                                 fetchArgs[i] = this[i];
16140                         }
16141                         delete this[i];
16142                 }, this);
16143                 if(store && store.getFeatures()["dojo.data.api.Identity"]){
16144                         // Temporarily set our store to null so that it will get set
16145                         // and connected appropriately
16146                         this.store = null;
16147                         this.setStore(store, this._oValue, fetchArgs);
16148                 }
16149         },
16150
16151         destroy: function(){
16152                 // summary:
16153                 //              Clean up our connections
16154                 dojo.forEach(this._notifyConnections || [], dojo.disconnect);
16155                 this.inherited(arguments);
16156         },
16157
16158         _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
16159                 // summary:
16160                 //              User-overridable function which, for the given option, adds an
16161                 //              item to the select.  If the option doesn't have a value, then a
16162                 //              separator is added in that place.  Make sure to store the option
16163                 //              in the created option widget.
16164         },
16165
16166         _removeOptionItem: function(/*dijit.form.__SelectOption*/ option){
16167                 // summary:
16168                 //              User-overridable function which, for the given option, removes
16169                 //              its item from the select.
16170         },
16171
16172         _setDisplay: function(/*String or String[]*/ newDisplay){
16173                 // summary:
16174                 //              Overridable function which will set the display for the
16175                 //              widget.  newDisplay is either a string (in the case of
16176                 //              single selects) or array of strings (in the case of multi-selects)
16177         },
16178
16179         _getChildren: function(){
16180                 // summary:
16181                 //              Overridable function to return the children that this widget contains.
16182                 return [];
16183         },
16184
16185         _getSelectedOptionsAttr: function(){
16186                 // summary:
16187                 //              hooks into this.attr to provide a mechanism for getting the
16188                 //              option items for the current value of the widget.
16189                 return this.getOptions(this.get("value"));
16190         },
16191
16192         _pseudoLoadChildren: function(/*item[]*/ items){
16193                 // summary:
16194                 //              a function that will "fake" loading children, if needed, and
16195                 //              if we have set to not load children until the widget opens.
16196                 // items:
16197                 //              An array of items that will be loaded, when needed
16198         },
16199
16200         onSetStore: function(){
16201                 // summary:
16202                 //              a function that can be connected to in order to receive a
16203                 //              notification that the store has finished loading and all options
16204                 //              from that store are available
16205         }
16206 });
16207
16208 }
16209
16210 if(!dojo._hasResource["dijit._KeyNavContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16211 dojo._hasResource["dijit._KeyNavContainer"] = true;
16212 dojo.provide("dijit._KeyNavContainer");
16213
16214
16215
16216 dojo.declare("dijit._KeyNavContainer",
16217         dijit._Container,
16218         {
16219
16220                 // summary:
16221                 //              A _Container with keyboard navigation of its children.
16222                 // description:
16223                 //              To use this mixin, call connectKeyNavHandlers() in
16224                 //              postCreate() and call startupKeyNavChildren() in startup().
16225                 //              It provides normalized keyboard and focusing code for Container
16226                 //              widgets.
16227 /*=====
16228                 // focusedChild: [protected] Widget
16229                 //              The currently focused child widget, or null if there isn't one
16230                 focusedChild: null,
16231 =====*/
16232
16233                 // tabIndex: Integer
16234                 //              Tab index of the container; same as HTML tabIndex attribute.
16235                 //              Note then when user tabs into the container, focus is immediately
16236                 //              moved to the first item in the container.
16237                 tabIndex: "0",
16238
16239                 _keyNavCodes: {},
16240
16241                 connectKeyNavHandlers: function(/*dojo.keys[]*/ prevKeyCodes, /*dojo.keys[]*/ nextKeyCodes){
16242                         // summary:
16243                         //              Call in postCreate() to attach the keyboard handlers
16244                         //              to the container.
16245                         // preKeyCodes: dojo.keys[]
16246                         //              Key codes for navigating to the previous child.
16247                         // nextKeyCodes: dojo.keys[]
16248                         //              Key codes for navigating to the next child.
16249                         // tags:
16250                         //              protected
16251
16252                         var keyCodes = (this._keyNavCodes = {});
16253                         var prev = dojo.hitch(this, this.focusPrev);
16254                         var next = dojo.hitch(this, this.focusNext);
16255                         dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
16256                         dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
16257                         keyCodes[dojo.keys.HOME] = dojo.hitch(this, "focusFirstChild");
16258                         keyCodes[dojo.keys.END] = dojo.hitch(this, "focusLastChild");
16259                         this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
16260                         this.connect(this.domNode, "onfocus", "_onContainerFocus");
16261                 },
16262
16263                 startupKeyNavChildren: function(){
16264                         // summary:
16265                         //              Call in startup() to set child tabindexes to -1
16266                         // tags:
16267                         //              protected
16268                         dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
16269                 },
16270
16271                 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
16272                         // summary:
16273                         //              Add a child to our _Container
16274                         dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
16275                         this._startupChild(widget);
16276                 },
16277
16278                 focus: function(){
16279                         // summary:
16280                         //              Default focus() implementation: focus the first child.
16281                         this.focusFirstChild();
16282                 },
16283
16284                 focusFirstChild: function(){
16285                         // summary:
16286                         //              Focus the first focusable child in the container.
16287                         // tags:
16288                         //              protected
16289                         var child = this._getFirstFocusableChild();
16290                         if(child){ // edge case: Menu could be empty or hidden
16291                                 this.focusChild(child);
16292                         }
16293                 },
16294
16295                 focusLastChild: function(){
16296                         // summary:
16297                         //              Focus the last focusable child in the container.
16298                         // tags:
16299                         //              protected
16300                         var child = this._getLastFocusableChild();
16301                         if(child){ // edge case: Menu could be empty or hidden
16302                                 this.focusChild(child);
16303                         }
16304                 },
16305
16306                 focusNext: function(){
16307                         // summary:
16308                         //              Focus the next widget
16309                         // tags:
16310                         //              protected
16311                         var child = this._getNextFocusableChild(this.focusedChild, 1);
16312                         this.focusChild(child);
16313                 },
16314
16315                 focusPrev: function(){
16316                         // summary:
16317                         //              Focus the last focusable node in the previous widget
16318                         //              (ex: go to the ComboButton icon section rather than button section)
16319                         // tags:
16320                         //              protected
16321                         var child = this._getNextFocusableChild(this.focusedChild, -1);
16322                         this.focusChild(child, true);
16323                 },
16324
16325                 focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
16326                         // summary:
16327                         //              Focus widget.
16328                         // widget:
16329                         //              Reference to container's child widget
16330                         // last:
16331                         //              If true and if widget has multiple focusable nodes, focus the
16332                         //              last one instead of the first one
16333                         // tags:
16334                         //              protected
16335                         
16336                         if(this.focusedChild && widget !== this.focusedChild){
16337                                 this._onChildBlur(this.focusedChild);
16338                         }
16339                         widget.set("tabIndex", this.tabIndex);  // for IE focus outline to appear, must set tabIndex before focs
16340                         widget.focus(last ? "end" : "start");
16341                         this._set("focusedChild", widget);
16342                 },
16343
16344                 _startupChild: function(/*dijit._Widget*/ widget){
16345                         // summary:
16346                         //              Setup for each child widget
16347                         // description:
16348                         //              Sets tabIndex=-1 on each child, so that the tab key will
16349                         //              leave the container rather than visiting each child.
16350                         // tags:
16351                         //              private
16352                         
16353                         widget.set("tabIndex", "-1");
16354                         
16355                         this.connect(widget, "_onFocus", function(){
16356                                 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
16357                                 widget.set("tabIndex", this.tabIndex);
16358                         });
16359                         this.connect(widget, "_onBlur", function(){
16360                                 widget.set("tabIndex", "-1");
16361                         });
16362                 },
16363
16364                 _onContainerFocus: function(evt){
16365                         // summary:
16366                         //              Handler for when the container gets focus
16367                         // description:
16368                         //              Initially the container itself has a tabIndex, but when it gets
16369                         //              focus, switch focus to first child...
16370                         // tags:
16371                         //              private
16372
16373                         // Note that we can't use _onFocus() because switching focus from the
16374                         // _onFocus() handler confuses the focus.js code
16375                         // (because it causes _onFocusNode() to be called recursively)
16376
16377                         // focus bubbles on Firefox,
16378                         // so just make sure that focus has really gone to the container
16379                         if(evt.target !== this.domNode){ return; }
16380
16381                         this.focusFirstChild();
16382
16383                         // and then set the container's tabIndex to -1,
16384                         // (don't remove as that breaks Safari 4)
16385                         // so that tab or shift-tab will go to the fields after/before
16386                         // the container, rather than the container itself
16387                         dojo.attr(this.domNode, "tabIndex", "-1");
16388                 },
16389
16390                 _onBlur: function(evt){
16391                         // When focus is moved away the container, and its descendant (popup) widgets,
16392                         // then restore the container's tabIndex so that user can tab to it again.
16393                         // Note that using _onBlur() so that this doesn't happen when focus is shifted
16394                         // to one of my child widgets (typically a popup)
16395                         if(this.tabIndex){
16396                                 dojo.attr(this.domNode, "tabIndex", this.tabIndex);
16397                         }
16398                         this.inherited(arguments);
16399                 },
16400
16401                 _onContainerKeypress: function(evt){
16402                         // summary:
16403                         //              When a key is pressed, if it's an arrow key etc. then
16404                         //              it's handled here.
16405                         // tags:
16406                         //              private
16407                         if(evt.ctrlKey || evt.altKey){ return; }
16408                         var func = this._keyNavCodes[evt.charOrCode];
16409                         if(func){
16410                                 func();
16411                                 dojo.stopEvent(evt);
16412                         }
16413                 },
16414
16415                 _onChildBlur: function(/*dijit._Widget*/ widget){
16416                         // summary:
16417                         //              Called when focus leaves a child widget to go
16418                         //              to a sibling widget.
16419                         // tags:
16420                         //              protected
16421                 },
16422
16423                 _getFirstFocusableChild: function(){
16424                         // summary:
16425                         //              Returns first child that can be focused
16426                         return this._getNextFocusableChild(null, 1);    // dijit._Widget
16427                 },
16428
16429                 _getLastFocusableChild: function(){
16430                         // summary:
16431                         //              Returns last child that can be focused
16432                         return this._getNextFocusableChild(null, -1);   // dijit._Widget
16433                 },
16434
16435                 _getNextFocusableChild: function(child, dir){
16436                         // summary:
16437                         //              Returns the next or previous focusable child, compared
16438                         //              to "child"
16439                         // child: Widget
16440                         //              The current widget
16441                         // dir: Integer
16442                         //              * 1 = after
16443                         //              * -1 = before
16444                         if(child){
16445                                 child = this._getSiblingOfChild(child, dir);
16446                         }
16447                         var children = this.getChildren();
16448                         for(var i=0; i < children.length; i++){
16449                                 if(!child){
16450                                         child = children[(dir>0) ? 0 : (children.length-1)];
16451                                 }
16452                                 if(child.isFocusable()){
16453                                         return child;   // dijit._Widget
16454                                 }
16455                                 child = this._getSiblingOfChild(child, dir);
16456                         }
16457                         // no focusable child found
16458                         return null;    // dijit._Widget
16459                 }
16460         }
16461 );
16462
16463 }
16464
16465 if(!dojo._hasResource["dijit.MenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16466 dojo._hasResource["dijit.MenuItem"] = true;
16467 dojo.provide("dijit.MenuItem");
16468
16469
16470
16471
16472
16473
16474 dojo.declare("dijit.MenuItem",
16475                 [dijit._Widget, dijit._Templated, dijit._Contained, dijit._CssStateMixin],
16476                 {
16477                 // summary:
16478                 //              A line item in a Menu Widget
16479
16480                 // Make 3 columns
16481                 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
16482                 templateString: dojo.cache("dijit", "templates/MenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"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\" role=\"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"),
16483
16484                 attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
16485                         label: { node: "containerNode", type: "innerHTML" },
16486                         iconClass: { node: "iconNode", type: "class" }
16487                 }),
16488
16489                 baseClass: "dijitMenuItem",
16490
16491                 // label: String
16492                 //              Menu text
16493                 label: '',
16494
16495                 // iconClass: String
16496                 //              Class to apply to DOMNode to make it display an icon.
16497                 iconClass: "",
16498
16499                 // accelKey: String
16500                 //              Text for the accelerator (shortcut) key combination.
16501                 //              Note that although Menu can display accelerator keys there
16502                 //              is no infrastructure to actually catch and execute these
16503                 //              accelerators.
16504                 accelKey: "",
16505
16506                 // disabled: Boolean
16507                 //              If true, the menu item is disabled.
16508                 //              If false, the menu item is enabled.
16509                 disabled: false,
16510
16511                 _fillContent: function(/*DomNode*/ source){
16512                         // If button label is specified as srcNodeRef.innerHTML rather than
16513                         // this.params.label, handle it here.
16514                         if(source && !("label" in this.params)){
16515                                 this.set('label', source.innerHTML);
16516                         }
16517                 },
16518
16519                 buildRendering: function(){
16520                         this.inherited(arguments);
16521                         var label = this.id+"_text";
16522                         dojo.attr(this.containerNode, "id", label);
16523                         if(this.accelKeyNode){
16524                                 dojo.attr(this.accelKeyNode, "id", this.id + "_accel");
16525                                 label += " " + this.id + "_accel";
16526                         }
16527                         dijit.setWaiState(this.domNode, "labelledby", label);
16528                         dojo.setSelectable(this.domNode, false);
16529                 },
16530
16531                 _onHover: function(){
16532                         // summary:
16533                         //              Handler when mouse is moved onto menu item
16534                         // tags:
16535                         //              protected
16536                         this.getParent().onItemHover(this);
16537                 },
16538
16539                 _onUnhover: function(){
16540                         // summary:
16541                         //              Handler when mouse is moved off of menu item,
16542                         //              possibly to a child menu, or maybe to a sibling
16543                         //              menuitem or somewhere else entirely.
16544                         // tags:
16545                         //              protected
16546
16547                         // if we are unhovering the currently selected item
16548                         // then unselect it
16549                         this.getParent().onItemUnhover(this);
16550
16551                         // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
16552                         // FF and IE don't generate an onmouseout event for the MenuItem.
16553                         // So, help out _CssStateMixin in this case.
16554                         this._set("hovering", false);
16555                 },
16556
16557                 _onClick: function(evt){
16558                         // summary:
16559                         //              Internal handler for click events on MenuItem.
16560                         // tags:
16561                         //              private
16562                         this.getParent().onItemClick(this, evt);
16563                         dojo.stopEvent(evt);
16564                 },
16565
16566                 onClick: function(/*Event*/ evt){
16567                         // summary:
16568                         //              User defined function to handle clicks
16569                         // tags:
16570                         //              callback
16571                 },
16572
16573                 focus: function(){
16574                         // summary:
16575                         //              Focus on this MenuItem
16576                         try{
16577                                 if(dojo.isIE == 8){
16578                                         // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
16579                                         this.containerNode.focus();
16580                                 }
16581                                 dijit.focus(this.focusNode);
16582                         }catch(e){
16583                                 // this throws on IE (at least) in some scenarios
16584                         }
16585                 },
16586
16587                 _onFocus: function(){
16588                         // summary:
16589                         //              This is called by the focus manager when focus
16590                         //              goes to this MenuItem or a child menu.
16591                         // tags:
16592                         //              protected
16593                         this._setSelected(true);
16594                         this.getParent()._onItemFocus(this);
16595
16596                         this.inherited(arguments);
16597                 },
16598
16599                 _setSelected: function(selected){
16600                         // summary:
16601                         //              Indicate that this node is the currently selected one
16602                         // tags:
16603                         //              private
16604
16605                         /***
16606                          * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
16607                          * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
16608                          * That's not supposed to happen, but the problem is:
16609                          * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
16610                          * points to the parent Menu, bypassing the parent MenuItem... thus the
16611                          * MenuItem is not in the chain of active widgets and gets a premature call to
16612                          * _onBlur()
16613                          */
16614
16615                         dojo.toggleClass(this.domNode, "dijitMenuItemSelected", selected);
16616                 },
16617
16618                 setLabel: function(/*String*/ content){
16619                         // summary:
16620                         //              Deprecated.   Use set('label', ...) instead.
16621                         // tags:
16622                         //              deprecated
16623                         dojo.deprecated("dijit.MenuItem.setLabel() is deprecated.  Use set('label', ...) instead.", "", "2.0");
16624                         this.set("label", content);
16625                 },
16626
16627                 setDisabled: function(/*Boolean*/ disabled){
16628                         // summary:
16629                         //              Deprecated.   Use set('disabled', bool) instead.
16630                         // tags:
16631                         //              deprecated
16632                         dojo.deprecated("dijit.Menu.setDisabled() is deprecated.  Use set('disabled', bool) instead.", "", "2.0");
16633                         this.set('disabled', disabled);
16634                 },
16635                 _setDisabledAttr: function(/*Boolean*/ value){
16636                         // summary:
16637                         //              Hook for attr('disabled', ...) to work.
16638                         //              Enable or disable this menu item.
16639
16640                         dijit.setWaiState(this.focusNode, 'disabled', value ? 'true' : 'false');
16641                         this._set("disabled", value);
16642                 },
16643                 _setAccelKeyAttr: function(/*String*/ value){
16644                         // summary:
16645                         //              Hook for attr('accelKey', ...) to work.
16646                         //              Set accelKey on this menu item.
16647
16648                         this.accelKeyNode.style.display=value?"":"none";
16649                         this.accelKeyNode.innerHTML=value;
16650                         //have to use colSpan to make it work in IE
16651                         dojo.attr(this.containerNode,'colSpan',value?"1":"2");
16652                         
16653                         this._set("accelKey", value);
16654                 }
16655         });
16656
16657 }
16658
16659 if(!dojo._hasResource["dijit.PopupMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16660 dojo._hasResource["dijit.PopupMenuItem"] = true;
16661 dojo.provide("dijit.PopupMenuItem");
16662
16663
16664
16665 dojo.declare("dijit.PopupMenuItem",
16666                 dijit.MenuItem,
16667                 {
16668                 _fillContent: function(){
16669                         // summary:
16670                         //              When Menu is declared in markup, this code gets the menu label and
16671                         //              the popup widget from the srcNodeRef.
16672                         // description:
16673                         //              srcNodeRefinnerHTML contains both the menu item text and a popup widget
16674                         //              The first part holds the menu item text and the second part is the popup
16675                         // example:
16676                         // |    <div dojoType="dijit.PopupMenuItem">
16677                         // |            <span>pick me</span>
16678                         // |            <popup> ... </popup>
16679                         // |    </div>
16680                         // tags:
16681                         //              protected
16682
16683                         if(this.srcNodeRef){
16684                                 var nodes = dojo.query("*", this.srcNodeRef);
16685                                 dijit.PopupMenuItem.superclass._fillContent.call(this, nodes[0]);
16686
16687                                 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
16688                                 this.dropDownContainer = this.srcNodeRef;
16689                         }
16690                 },
16691
16692                 startup: function(){
16693                         if(this._started){ return; }
16694                         this.inherited(arguments);
16695
16696                         // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
16697                         // land now.  move it to dojo.doc.body.
16698                         if(!this.popup){
16699                                 var node = dojo.query("[widgetId]", this.dropDownContainer)[0];
16700                                 this.popup = dijit.byNode(node);
16701                         }
16702                         dojo.body().appendChild(this.popup.domNode);
16703                         this.popup.startup();
16704
16705                         this.popup.domNode.style.display="none";
16706                         if(this.arrowWrapper){
16707                                 dojo.style(this.arrowWrapper, "visibility", "");
16708                         }
16709                         dijit.setWaiState(this.focusNode, "haspopup", "true");
16710                 },
16711
16712                 destroyDescendants: function(){
16713                         if(this.popup){
16714                                 // Destroy the popup, unless it's already been destroyed.  This can happen because
16715                                 // the popup is a direct child of <body> even though it's logically my child.
16716                                 if(!this.popup._destroyed){
16717                                         this.popup.destroyRecursive();
16718                                 }
16719                                 delete this.popup;
16720                         }
16721                         this.inherited(arguments);
16722                 }
16723         });
16724
16725 }
16726
16727 if(!dojo._hasResource["dijit.CheckedMenuItem"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16728 dojo._hasResource["dijit.CheckedMenuItem"] = true;
16729 dojo.provide("dijit.CheckedMenuItem");
16730
16731
16732
16733 dojo.declare("dijit.CheckedMenuItem",
16734                 dijit.MenuItem,
16735                 {
16736                 // summary:
16737                 //              A checkbox-like menu item for toggling on and off
16738
16739                 templateString: dojo.cache("dijit", "templates/CheckedMenuItem.html", "<tr class=\"dijitReset dijitMenuItem\" dojoAttachPoint=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\"\n\t\tdojoAttachEvent=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"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\" role=\"presentation\">&nbsp;</td>\n</tr>\n"),
16740
16741                 // checked: Boolean
16742                 //              Our checked state
16743                 checked: false,
16744                 _setCheckedAttr: function(/*Boolean*/ checked){
16745                         // summary:
16746                         //              Hook so attr('checked', bool) works.
16747                         //              Sets the class and state for the check box.
16748                         dojo.toggleClass(this.domNode, "dijitCheckedMenuItemChecked", checked);
16749                         dijit.setWaiState(this.domNode, "checked", checked);
16750                         this._set("checked", checked);
16751                 },
16752
16753                 onChange: function(/*Boolean*/ checked){
16754                         // summary:
16755                         //              User defined function to handle check/uncheck events
16756                         // tags:
16757                         //              callback
16758                 },
16759
16760                 _onClick: function(/*Event*/ e){
16761                         // summary:
16762                         //              Clicking this item just toggles its state
16763                         // tags:
16764                         //              private
16765                         if(!this.disabled){
16766                                 this.set("checked", !this.checked);
16767                                 this.onChange(this.checked);
16768                         }
16769                         this.inherited(arguments);
16770                 }
16771         });
16772
16773 }
16774
16775 if(!dojo._hasResource["dijit.MenuSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16776 dojo._hasResource["dijit.MenuSeparator"] = true;
16777 dojo.provide("dijit.MenuSeparator");
16778
16779
16780
16781
16782
16783 dojo.declare("dijit.MenuSeparator",
16784                 [dijit._Widget, dijit._Templated, dijit._Contained],
16785                 {
16786                 // summary:
16787                 //              A line between two menu items
16788
16789                 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"),
16790
16791                 buildRendering: function(){
16792                         this.inherited(arguments);
16793                         dojo.setSelectable(this.domNode, false);
16794                 },
16795
16796                 isFocusable: function(){
16797                         // summary:
16798                         //              Override to always return false
16799                         // tags:
16800                         //              protected
16801
16802                         return false; // Boolean
16803                 }
16804         });
16805
16806 }
16807
16808 if(!dojo._hasResource["dijit.Menu"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
16809 dojo._hasResource["dijit.Menu"] = true;
16810 dojo.provide("dijit.Menu");
16811
16812
16813
16814
16815
16816
16817
16818
16819
16820
16821 // "dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator" for Back-compat (TODO: remove in 2.0)
16822
16823 dojo.declare("dijit._MenuBase",
16824         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
16825 {
16826         // summary:
16827         //              Base class for Menu and MenuBar
16828
16829         // parentMenu: [readonly] Widget
16830         //              pointer to menu that displayed me
16831         parentMenu: null,
16832
16833         // popupDelay: Integer
16834         //              number of milliseconds before hovering (without clicking) causes the popup to automatically open.
16835         popupDelay: 500,
16836
16837         startup: function(){
16838                 if(this._started){ return; }
16839
16840                 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
16841                 this.startupKeyNavChildren();
16842
16843                 this.inherited(arguments);
16844         },
16845
16846         onExecute: function(){
16847                 // summary:
16848                 //              Attach point for notification about when a menu item has been executed.
16849                 //              This is an internal mechanism used for Menus to signal to their parent to
16850                 //              close them, because they are about to execute the onClick handler.   In
16851                 //              general developers should not attach to or override this method.
16852                 // tags:
16853                 //              protected
16854         },
16855
16856         onCancel: function(/*Boolean*/ closeAll){
16857                 // summary:
16858                 //              Attach point for notification about when the user cancels the current menu
16859                 //              This is an internal mechanism used for Menus to signal to their parent to
16860                 //              close them.  In general developers should not attach to or override this method.
16861                 // tags:
16862                 //              protected
16863         },
16864
16865         _moveToPopup: function(/*Event*/ evt){
16866                 // summary:
16867                 //              This handles the right arrow key (left arrow key on RTL systems),
16868                 //              which will either open a submenu, or move to the next item in the
16869                 //              ancestor MenuBar
16870                 // tags:
16871                 //              private
16872
16873                 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
16874                         this.focusedChild._onClick(evt);
16875                 }else{
16876                         var topMenu = this._getTopMenu();
16877                         if(topMenu && topMenu._isMenuBar){
16878                                 topMenu.focusNext();
16879                         }
16880                 }
16881         },
16882
16883         _onPopupHover: function(/*Event*/ evt){
16884                 // summary:
16885                 //              This handler is called when the mouse moves over the popup.
16886                 // tags:
16887                 //              private
16888
16889                 // if the mouse hovers over a menu popup that is in pending-close state,
16890                 // then stop the close operation.
16891                 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
16892                 if(this.currentPopup && this.currentPopup._pendingClose_timer){
16893                         var parentMenu = this.currentPopup.parentMenu;
16894                         // highlight the parent menu item pointing to this popup
16895                         if(parentMenu.focusedChild){
16896                                 parentMenu.focusedChild._setSelected(false);
16897                         }
16898                         parentMenu.focusedChild = this.currentPopup.from_item;
16899                         parentMenu.focusedChild._setSelected(true);
16900                         // cancel the pending close
16901                         this._stopPendingCloseTimer(this.currentPopup);
16902                 }
16903         },
16904
16905         onItemHover: function(/*MenuItem*/ item){
16906                 // summary:
16907                 //              Called when cursor is over a MenuItem.
16908                 // tags:
16909                 //              protected
16910
16911                 // Don't do anything unless user has "activated" the menu by:
16912                 //              1) clicking it
16913                 //              2) opening it from a parent menu (which automatically focuses it)
16914                 if(this.isActive){
16915                         this.focusChild(item);
16916                         if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
16917                                 this.hover_timer = setTimeout(dojo.hitch(this, "_openPopup"), this.popupDelay);
16918                         }
16919                 }
16920                 // if the user is mixing mouse and keyboard navigation,
16921                 // then the menu may not be active but a menu item has focus,
16922                 // but it's not the item that the mouse just hovered over.
16923                 // To avoid both keyboard and mouse selections, use the latest.
16924                 if(this.focusedChild){
16925                         this.focusChild(item);
16926                 }
16927                 this._hoveredChild = item;
16928         },
16929
16930         _onChildBlur: function(item){
16931                 // summary:
16932                 //              Called when a child MenuItem becomes inactive because focus
16933                 //              has been removed from the MenuItem *and* it's descendant menus.
16934                 // tags:
16935                 //              private
16936                 this._stopPopupTimer();
16937                 item._setSelected(false);
16938                 // Close all popups that are open and descendants of this menu
16939                 var itemPopup = item.popup;
16940                 if(itemPopup){
16941                         this._stopPendingCloseTimer(itemPopup);
16942                         itemPopup._pendingClose_timer = setTimeout(function(){
16943                                 itemPopup._pendingClose_timer = null;
16944                                 if(itemPopup.parentMenu){
16945                                         itemPopup.parentMenu.currentPopup = null;
16946                                 }
16947                                 dijit.popup.close(itemPopup); // this calls onClose
16948                         }, this.popupDelay);
16949                 }
16950         },
16951
16952         onItemUnhover: function(/*MenuItem*/ item){
16953                 // summary:
16954                 //              Callback fires when mouse exits a MenuItem
16955                 // tags:
16956                 //              protected
16957
16958                 if(this.isActive){
16959                         this._stopPopupTimer();
16960                 }
16961                 if(this._hoveredChild == item){ this._hoveredChild = null; }
16962         },
16963
16964         _stopPopupTimer: function(){
16965                 // summary:
16966                 //              Cancels the popup timer because the user has stop hovering
16967                 //              on the MenuItem, etc.
16968                 // tags:
16969                 //              private
16970                 if(this.hover_timer){
16971                         clearTimeout(this.hover_timer);
16972                         this.hover_timer = null;
16973                 }
16974         },
16975
16976         _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
16977                 // summary:
16978                 //              Cancels the pending-close timer because the close has been preempted
16979                 // tags:
16980                 //              private
16981                 if(popup._pendingClose_timer){
16982                         clearTimeout(popup._pendingClose_timer);
16983                         popup._pendingClose_timer = null;
16984                 }
16985         },
16986
16987         _stopFocusTimer: function(){
16988                 // summary:
16989                 //              Cancels the pending-focus timer because the menu was closed before focus occured
16990                 // tags:
16991                 //              private
16992                 if(this._focus_timer){
16993                         clearTimeout(this._focus_timer);
16994                         this._focus_timer = null;
16995                 }
16996         },
16997
16998         _getTopMenu: function(){
16999                 // summary:
17000                 //              Returns the top menu in this chain of Menus
17001                 // tags:
17002                 //              private
17003                 for(var top=this; top.parentMenu; top=top.parentMenu);
17004                 return top;
17005         },
17006
17007         onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
17008                 // summary:
17009                 //              Handle clicks on an item.
17010                 // tags:
17011                 //              private
17012
17013                 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
17014                 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
17015                         this._markActive();
17016                 }
17017
17018                 this.focusChild(item);
17019
17020                 if(item.disabled){ return false; }
17021
17022                 if(item.popup){
17023                         this._openPopup();
17024                 }else{
17025                         // before calling user defined handler, close hierarchy of menus
17026                         // and restore focus to place it was when menu was opened
17027                         this.onExecute();
17028
17029                         // user defined handler for click
17030                         item.onClick(evt);
17031                 }
17032         },
17033
17034         _openPopup: function(){
17035                 // summary:
17036                 //              Open the popup to the side of/underneath the current menu item
17037                 // tags:
17038                 //              protected
17039
17040                 this._stopPopupTimer();
17041                 var from_item = this.focusedChild;
17042                 if(!from_item){ return; } // the focused child lost focus since the timer was started
17043                 var popup = from_item.popup;
17044                 if(popup.isShowingNow){ return; }
17045                 if(this.currentPopup){
17046                         this._stopPendingCloseTimer(this.currentPopup);
17047                         dijit.popup.close(this.currentPopup);
17048                 }
17049                 popup.parentMenu = this;
17050                 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
17051                 var self = this;
17052                 dijit.popup.open({
17053                         parent: this,
17054                         popup: popup,
17055                         around: from_item.domNode,
17056                         orient: this._orient || (this.isLeftToRight() ?
17057                                                                         {'TR': 'TL', 'TL': 'TR', 'BR': 'BL', 'BL': 'BR'} :
17058                                                                         {'TL': 'TR', 'TR': 'TL', 'BL': 'BR', 'BR': 'BL'}),
17059                         onCancel: function(){ // called when the child menu is canceled
17060                                 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
17061                                 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
17062                                 self.focusChild(from_item);     // put focus back on my node
17063                                 self._cleanUp();                        // close the submenu (be sure this is done _after_ focus is moved)
17064                                 from_item._setSelected(true); // oops, _cleanUp() deselected the item
17065                                 self.focusedChild = from_item;  // and unset focusedChild
17066                         },
17067                         onExecute: dojo.hitch(this, "_cleanUp")
17068                 });
17069
17070                 this.currentPopup = popup;
17071                 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
17072                 popup.connect(popup.domNode, "onmouseenter", dojo.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
17073
17074                 if(popup.focus){
17075                         // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
17076                         // if the cursor happens to collide with the popup, it will generate an onmouseover event
17077                         // even though the mouse wasn't moved.   Use a setTimeout() to call popup.focus so that
17078                         // our focus() call overrides the onmouseover event, rather than vice-versa.  (#8742)
17079                         popup._focus_timer = setTimeout(dojo.hitch(popup, function(){
17080                                 this._focus_timer = null;
17081                                 this.focus();
17082                         }), 0);
17083                 }
17084         },
17085
17086         _markActive: function(){
17087                 // summary:
17088                 //              Mark this menu's state as active.
17089                 //              Called when this Menu gets focus from:
17090                 //                      1) clicking it (mouse or via space/arrow key)
17091                 //                      2) being opened by a parent menu.
17092                 //              This is not called just from mouse hover.
17093                 //              Focusing a menu via TAB does NOT automatically set isActive
17094                 //              since TAB is a navigation operation and not a selection one.
17095                 //              For Windows apps, pressing the ALT key focuses the menubar
17096                 //              menus (similar to TAB navigation) but the menu is not active
17097                 //              (ie no dropdown) until an item is clicked.
17098                 this.isActive = true;
17099                 dojo.replaceClass(this.domNode, "dijitMenuActive", "dijitMenuPassive");
17100         },
17101
17102         onOpen: function(/*Event*/ e){
17103                 // summary:
17104                 //              Callback when this menu is opened.
17105                 //              This is called by the popup manager as notification that the menu
17106                 //              was opened.
17107                 // tags:
17108                 //              private
17109
17110                 this.isShowingNow = true;
17111                 this._markActive();
17112         },
17113
17114         _markInactive: function(){
17115                 // summary:
17116                 //              Mark this menu's state as inactive.
17117                 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
17118                 dojo.replaceClass(this.domNode, "dijitMenuPassive", "dijitMenuActive");
17119         },
17120
17121         onClose: function(){
17122                 // summary:
17123                 //              Callback when this menu is closed.
17124                 //              This is called by the popup manager as notification that the menu
17125                 //              was closed.
17126                 // tags:
17127                 //              private
17128
17129                 this._stopFocusTimer();
17130                 this._markInactive();
17131                 this.isShowingNow = false;
17132                 this.parentMenu = null;
17133         },
17134
17135         _closeChild: function(){
17136                 // summary:
17137                 //              Called when submenu is clicked or focus is lost.  Close hierarchy of menus.
17138                 // tags:
17139                 //              private
17140                 this._stopPopupTimer();
17141
17142                 var fromItem = this.focusedChild && this.focusedChild.from_item;
17143
17144                 if(this.currentPopup){
17145                         // If focus is on my child menu then move focus to me,
17146                         // because IE doesn't like it when you display:none a node with focus
17147                         if(dijit._curFocus && dojo.isDescendant(dijit._curFocus, this.currentPopup.domNode)){
17148                                 this.focusedChild.focusNode.focus();
17149                         }
17150                         // Close all popups that are open and descendants of this menu
17151                         dijit.popup.close(this.currentPopup);
17152                         this.currentPopup = null;
17153                 }
17154
17155                 if(this.focusedChild){ // unhighlight the focused item
17156                         this.focusedChild._setSelected(false);
17157                         this.focusedChild._onUnhover();
17158                         this.focusedChild = null;
17159                 }
17160         },
17161
17162         _onItemFocus: function(/*MenuItem*/ item){
17163                 // summary:
17164                 //              Called when child of this Menu gets focus from:
17165                 //                      1) clicking it
17166                 //                      2) tabbing into it
17167                 //                      3) being opened by a parent menu.
17168                 //              This is not called just from mouse hover.
17169                 if(this._hoveredChild && this._hoveredChild != item){
17170                         this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
17171                 }
17172         },
17173
17174         _onBlur: function(){
17175                 // summary:
17176                 //              Called when focus is moved away from this Menu and it's submenus.
17177                 // tags:
17178                 //              protected
17179                 this._cleanUp();
17180                 this.inherited(arguments);
17181         },
17182
17183         _cleanUp: function(){
17184                 // summary:
17185                 //              Called when the user is done with this menu.  Closes hierarchy of menus.
17186                 // tags:
17187                 //              private
17188
17189                 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
17190                 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
17191                         this._markInactive();
17192                 }
17193         }
17194 });
17195
17196 dojo.declare("dijit.Menu",
17197         dijit._MenuBase,
17198         {
17199         // summary
17200         //              A context menu you can assign to multiple elements
17201
17202         // TODO: most of the code in here is just for context menu (right-click menu)
17203         // support.  In retrospect that should have been a separate class (dijit.ContextMenu).
17204         // Split them for 2.0
17205
17206         constructor: function(){
17207                 this._bindings = [];
17208         },
17209
17210         templateString: dojo.cache("dijit", "templates/Menu.html", "<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" dojoAttachEvent=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" dojoAttachPoint=\"containerNode\"></tbody>\n</table>\n"),
17211
17212         baseClass: "dijitMenu",
17213
17214         // targetNodeIds: [const] String[]
17215         //              Array of dom node ids of nodes to attach to.
17216         //              Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
17217         targetNodeIds: [],
17218
17219         // contextMenuForWindow: [const] Boolean
17220         //              If true, right clicking anywhere on the window will cause this context menu to open.
17221         //              If false, must specify targetNodeIds.
17222         contextMenuForWindow: false,
17223
17224         // leftClickToOpen: [const] Boolean
17225         //              If true, menu will open on left click instead of right click, similiar to a file menu.
17226         leftClickToOpen: false,
17227
17228         // refocus: Boolean
17229         //              When this menu closes, re-focus the element which had focus before it was opened.
17230         refocus: true,
17231
17232         postCreate: function(){
17233                 if(this.contextMenuForWindow){
17234                         this.bindDomNode(dojo.body());
17235                 }else{
17236                         // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
17237                         // later set('targetNodeIds', ...) call.   There's also a problem that targetNodeIds[]
17238                         // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
17239                         dojo.forEach(this.targetNodeIds, this.bindDomNode, this);
17240                 }
17241                 var k = dojo.keys, l = this.isLeftToRight();
17242                 this._openSubMenuKey = l ? k.RIGHT_ARROW : k.LEFT_ARROW;
17243                 this._closeSubMenuKey = l ? k.LEFT_ARROW : k.RIGHT_ARROW;
17244                 this.connectKeyNavHandlers([k.UP_ARROW], [k.DOWN_ARROW]);
17245         },
17246
17247         _onKeyPress: function(/*Event*/ evt){
17248                 // summary:
17249                 //              Handle keyboard based menu navigation.
17250                 // tags:
17251                 //              protected
17252
17253                 if(evt.ctrlKey || evt.altKey){ return; }
17254
17255                 switch(evt.charOrCode){
17256                         case this._openSubMenuKey:
17257                                 this._moveToPopup(evt);
17258                                 dojo.stopEvent(evt);
17259                                 break;
17260                         case this._closeSubMenuKey:
17261                                 if(this.parentMenu){
17262                                         if(this.parentMenu._isMenuBar){
17263                                                 this.parentMenu.focusPrev();
17264                                         }else{
17265                                                 this.onCancel(false);
17266                                         }
17267                                 }else{
17268                                         dojo.stopEvent(evt);
17269                                 }
17270                                 break;
17271                 }
17272         },
17273
17274         // thanks burstlib!
17275         _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
17276                 // summary:
17277                 //              Returns the window reference of the passed iframe
17278                 // tags:
17279                 //              private
17280                 var win = dojo.window.get(this._iframeContentDocument(iframe_el)) ||
17281                         // Moz. TODO: is this available when defaultView isn't?
17282                         this._iframeContentDocument(iframe_el)['__parent__'] ||
17283                         (iframe_el.name && dojo.doc.frames[iframe_el.name]) || null;
17284                 return win;     //      Window
17285         },
17286
17287         _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
17288                 // summary:
17289                 //              Returns a reference to the document object inside iframe_el
17290                 // tags:
17291                 //              protected
17292                 var doc = iframe_el.contentDocument // W3
17293                         || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
17294                         || (iframe_el.name && dojo.doc.frames[iframe_el.name] && dojo.doc.frames[iframe_el.name].document)
17295                         || null;
17296                 return doc;     //      HTMLDocument
17297         },
17298
17299         bindDomNode: function(/*String|DomNode*/ node){
17300                 // summary:
17301                 //              Attach menu to given node
17302                 node = dojo.byId(node);
17303
17304                 var cn; // Connect node
17305
17306                 // Support context menus on iframes.   Rather than binding to the iframe itself we need
17307                 // to bind to the <body> node inside the iframe.
17308                 if(node.tagName.toLowerCase() == "iframe"){
17309                         var iframe = node,
17310                                 win = this._iframeContentWindow(iframe);
17311                         cn = dojo.withGlobal(win, dojo.body);
17312                 }else{
17313                         
17314                         // To capture these events at the top level, attach to <html>, not <body>.
17315                         // Otherwise right-click context menu just doesn't work.
17316                         cn = (node == dojo.body() ? dojo.doc.documentElement : node);
17317                 }
17318
17319
17320                 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
17321                 var binding = {
17322                         node: node,
17323                         iframe: iframe
17324                 };
17325
17326                 // Save info about binding in _bindings[], and make node itself record index(+1) into
17327                 // _bindings[] array.   Prefix w/_dijitMenu to avoid setting an attribute that may
17328                 // start with a number, which fails on FF/safari.
17329                 dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
17330
17331                 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
17332                 // loading yet, in which case we need to wait for the onload event first, and then connect
17333                 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
17334                 // we need to monitor keyboard events in addition to the oncontextmenu event.
17335                 var doConnects = dojo.hitch(this, function(cn){
17336                         return [
17337                                 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
17338                                 // rather than shift-F10?
17339                                 dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
17340                                         // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
17341                                         dojo.stopEvent(evt);
17342                                         this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
17343                                 }),
17344                                 dojo.connect(cn, "onkeydown", this, function(evt){
17345                                         if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
17346                                                 dojo.stopEvent(evt);
17347                                                 this._scheduleOpen(evt.target, iframe); // no coords - open near target node
17348                                         }
17349                                 })
17350                         ];
17351                 });
17352                 binding.connects = cn ? doConnects(cn) : [];
17353
17354                 if(iframe){
17355                         // Setup handler to [re]bind to the iframe when the contents are initially loaded,
17356                         // and every time the contents change.
17357                         // Need to do this b/c we are actually binding to the iframe's <body> node.
17358                         // Note: can't use dojo.connect(), see #9609.
17359
17360                         binding.onloadHandler = dojo.hitch(this, function(){
17361                                 // want to remove old connections, but IE throws exceptions when trying to
17362                                 // access the <body> node because it's already gone, or at least in a state of limbo
17363
17364                                 var win = this._iframeContentWindow(iframe);
17365                                         cn = dojo.withGlobal(win, dojo.body);
17366                                 binding.connects = doConnects(cn);
17367                         });
17368                         if(iframe.addEventListener){
17369                                 iframe.addEventListener("load", binding.onloadHandler, false);
17370                         }else{
17371                                 iframe.attachEvent("onload", binding.onloadHandler);
17372                         }
17373                 }
17374         },
17375
17376         unBindDomNode: function(/*String|DomNode*/ nodeName){
17377                 // summary:
17378                 //              Detach menu from given node
17379
17380                 var node;
17381                 try{
17382                         node = dojo.byId(nodeName);
17383                 }catch(e){
17384                         // On IE the dojo.byId() call will get an exception if the attach point was
17385                         // the <body> node of an <iframe> that has since been reloaded (and thus the
17386                         // <body> node is in a limbo state of destruction.
17387                         return;
17388                 }
17389
17390                 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
17391                 var attrName = "_dijitMenu" + this.id;
17392                 if(node && dojo.hasAttr(node, attrName)){
17393                         var bid = dojo.attr(node, attrName)-1, b = this._bindings[bid];
17394                         dojo.forEach(b.connects, dojo.disconnect);
17395
17396                         // Remove listener for iframe onload events
17397                         var iframe = b.iframe;
17398                         if(iframe){
17399                                 if(iframe.removeEventListener){
17400                                         iframe.removeEventListener("load", b.onloadHandler, false);
17401                                 }else{
17402                                         iframe.detachEvent("onload", b.onloadHandler);
17403                                 }
17404                         }
17405
17406                         dojo.removeAttr(node, attrName);
17407                         delete this._bindings[bid];
17408                 }
17409         },
17410
17411         _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
17412                 // summary:
17413                 //              Set timer to display myself.  Using a timer rather than displaying immediately solves
17414                 //              two problems:
17415                 //
17416                 //              1. IE: without the delay, focus work in "open" causes the system
17417                 //              context menu to appear in spite of stopEvent.
17418                 //
17419                 //              2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
17420                 //              even after a dojo.stopEvent(e).  (Shift-F10 on windows doesn't generate the
17421                 //              oncontextmenu event.)
17422
17423                 if(!this._openTimer){
17424                         this._openTimer = setTimeout(dojo.hitch(this, function(){
17425                                 delete this._openTimer;
17426                                 this._openMyself({
17427                                         target: target,
17428                                         iframe: iframe,
17429                                         coords: coords
17430                                 });
17431                         }), 1);
17432                 }
17433         },
17434
17435         _openMyself: function(args){
17436                 // summary:
17437                 //              Internal function for opening myself when the user does a right-click or something similar.
17438                 // args:
17439                 //              This is an Object containing:
17440                 //              * target:
17441                 //                      The node that is being clicked
17442                 //              * iframe:
17443                 //                      If an <iframe> is being clicked, iframe points to that iframe
17444                 //              * coords:
17445                 //                      Put menu at specified x/y position in viewport, or if iframe is
17446                 //                      specified, then relative to iframe.
17447                 //
17448                 //              _openMyself() formerly took the event object, and since various code references
17449                 //              evt.target (after connecting to _openMyself()), using an Object for parameters
17450                 //              (so that old code still works).
17451
17452                 var target = args.target,
17453                         iframe = args.iframe,
17454                         coords = args.coords;
17455
17456                 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
17457                 // then near the node the menu is assigned to.
17458                 if(coords){
17459                         if(iframe){
17460                                 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
17461                                 var od = target.ownerDocument,
17462                                         ifc = dojo.position(iframe, true),
17463                                         win = this._iframeContentWindow(iframe),
17464                                         scroll = dojo.withGlobal(win, "_docScroll", dojo);
17465         
17466                                 var cs = dojo.getComputedStyle(iframe),
17467                                         tp = dojo._toPixelValue,
17468                                         left = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingLeft)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderLeftWidth) : 0),
17469                                         top = (dojo.isIE && dojo.isQuirks ? 0 : tp(iframe, cs.paddingTop)) + (dojo.isIE && dojo.isQuirks ? tp(iframe, cs.borderTopWidth) : 0);
17470
17471                                 coords.x += ifc.x + left - scroll.x;
17472                                 coords.y += ifc.y + top - scroll.y;
17473                         }
17474                 }else{
17475                         coords = dojo.position(target, true);
17476                         coords.x += 10;
17477                         coords.y += 10;
17478                 }
17479
17480                 var self=this;
17481                 var savedFocus = dijit.getFocus(this);
17482                 function closeAndRestoreFocus(){
17483                         // user has clicked on a menu or popup
17484                         if(self.refocus){
17485                                 dijit.focus(savedFocus);
17486                         }
17487                         dijit.popup.close(self);
17488                 }
17489                 dijit.popup.open({
17490                         popup: this,
17491                         x: coords.x,
17492                         y: coords.y,
17493                         onExecute: closeAndRestoreFocus,
17494                         onCancel: closeAndRestoreFocus,
17495                         orient: this.isLeftToRight() ? 'L' : 'R'
17496                 });
17497                 this.focus();
17498
17499                 this._onBlur = function(){
17500                         this.inherited('_onBlur', arguments);
17501                         // Usually the parent closes the child widget but if this is a context
17502                         // menu then there is no parent
17503                         dijit.popup.close(this);
17504                         // don't try to restore focus; user has clicked another part of the screen
17505                         // and set focus there
17506                 };
17507         },
17508
17509         uninitialize: function(){
17510                 dojo.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
17511                 this.inherited(arguments);
17512         }
17513 }
17514 );
17515
17516 }
17517
17518 if(!dojo._hasResource["dijit.form.Select"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17519 dojo._hasResource["dijit.form.Select"] = true;
17520 dojo.provide("dijit.form.Select");
17521
17522
17523
17524
17525
17526
17527
17528 dojo.declare("dijit.form._SelectMenu", dijit.Menu, {
17529         // summary:
17530         //              An internally-used menu for dropdown that allows us a vertical scrollbar
17531         buildRendering: function(){
17532                 // summary:
17533                 //              Stub in our own changes, so that our domNode is not a table
17534                 //              otherwise, we won't respond correctly to heights/overflows
17535                 this.inherited(arguments);
17536                 var o = (this.menuTableNode = this.domNode);
17537                 var n = (this.domNode = dojo.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
17538                 if(o.parentNode){
17539                         o.parentNode.replaceChild(n, o);
17540                 }
17541                 dojo.removeClass(o, "dijitMenuTable");
17542                 n.className = o.className + " dijitSelectMenu";
17543                 o.className = "dijitReset dijitMenuTable";
17544                 dijit.setWaiRole(o,"listbox");
17545                 dijit.setWaiRole(n,"presentation");
17546                 n.appendChild(o);
17547         },
17548
17549         postCreate: function(){
17550                 // summary:
17551                 //              stop mousemove from selecting text on IE to be consistent with other browsers
17552
17553                 this.inherited(arguments);
17554
17555                 this.connect(this.domNode, "onmousemove", dojo.stopEvent);
17556         },
17557
17558         resize: function(/*Object*/ mb){
17559                 // summary:
17560                 //              Overridden so that we are able to handle resizing our
17561                 //              internal widget.  Note that this is not a "full" resize
17562                 //              implementation - it only works correctly if you pass it a
17563                 //              marginBox.
17564                 //
17565                 // mb: Object
17566                 //              The margin box to set this dropdown to.
17567                 if(mb){
17568                         dojo.marginBox(this.domNode, mb);
17569                         if("w" in mb){
17570                                 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
17571                                 // 100% is safer than a pixel value because there may be a scroll bar with
17572                                 // browser/OS specific width.
17573                                 this.menuTableNode.style.width = "100%";
17574                         }
17575                 }
17576         }
17577 });
17578
17579 dojo.declare("dijit.form.Select", [dijit.form._FormSelectWidget, dijit._HasDropDown], {
17580         // summary:
17581         //              This is a "styleable" select box - it is basically a DropDownButton which
17582         //              can take a <select> as its input.
17583
17584         baseClass: "dijitSelect",
17585
17586         templateString: dojo.cache("dijit.form", "templates/Select.html", "<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdojoAttachPoint=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"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}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdojoAttachPoint=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"),
17587
17588         // attributeMap: Object
17589         //              Add in our style to be applied to the focus node
17590         attributeMap: dojo.mixin(dojo.clone(dijit.form._FormSelectWidget.prototype.attributeMap),{style:"tableNode"}),
17591
17592         // required: Boolean
17593         //              Can be true or false, default is false.
17594         required: false,
17595
17596         // state: String
17597         //              Shows current state (ie, validation result) of input (Normal, Warning, or Error)
17598         state: "",
17599
17600         // message: String
17601         //              Currently displayed error/prompt message
17602         message: "",
17603
17604         //      tooltipPosition: String[]
17605         //              See description of dijit.Tooltip.defaultPosition for details on this parameter.
17606         tooltipPosition: [],
17607
17608         // emptyLabel: string
17609         //              What to display in an "empty" dropdown
17610         emptyLabel: "&nbsp;",
17611
17612         // _isLoaded: Boolean
17613         //              Whether or not we have been loaded
17614         _isLoaded: false,
17615
17616         // _childrenLoaded: Boolean
17617         //              Whether or not our children have been loaded
17618         _childrenLoaded: false,
17619
17620         _fillContent: function(){
17621                 // summary:
17622                 //              Set the value to be the first, or the selected index
17623                 this.inherited(arguments);
17624                 // set value from selected option
17625                 if(this.options.length && !this.value && this.srcNodeRef){
17626                         var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
17627                         this.value = this.options[si >= 0 ? si : 0].value;
17628                 }
17629                 // Create the dropDown widget
17630                 this.dropDown = new dijit.form._SelectMenu({id: this.id + "_menu"});
17631                 dojo.addClass(this.dropDown.domNode, this.baseClass + "Menu");
17632         },
17633
17634         _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
17635                 // summary:
17636                 //              For the given option, return the menu item that should be
17637                 //              used to display it.  This can be overridden as needed
17638                 if(!option.value && !option.label){
17639                         // We are a separator (no label set for it)
17640                         return new dijit.MenuSeparator();
17641                 }else{
17642                         // Just a regular menu option
17643                         var click = dojo.hitch(this, "_setValueAttr", option);
17644                         var item = new dijit.MenuItem({
17645                                 option: option,
17646                                 label: option.label || this.emptyLabel,
17647                                 onClick: click,
17648                                 disabled: option.disabled || false
17649                         });
17650                         dijit.setWaiRole(item.focusNode, "listitem");
17651                         return item;
17652                 }
17653         },
17654
17655         _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
17656                 // summary:
17657                 //              For the given option, add an option to our dropdown.
17658                 //              If the option doesn't have a value, then a separator is added
17659                 //              in that place.
17660                 if(this.dropDown){
17661                         this.dropDown.addChild(this._getMenuItemForOption(option));
17662                 }
17663         },
17664
17665         _getChildren: function(){
17666                 if(!this.dropDown){
17667                         return [];
17668                 }
17669                 return this.dropDown.getChildren();
17670         },
17671
17672         _loadChildren: function(/*Boolean*/ loadMenuItems){
17673                 // summary:
17674                 //              Resets the menu and the length attribute of the button - and
17675                 //              ensures that the label is appropriately set.
17676                 //      loadMenuItems: Boolean
17677                 //              actually loads the child menu items - we only do this when we are
17678                 //              populating for showing the dropdown.
17679
17680                 if(loadMenuItems === true){
17681                         // this.inherited destroys this.dropDown's child widgets (MenuItems).
17682                         // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
17683                         // issues later in _setSelected). (see #10296)
17684                         if(this.dropDown){
17685                                 delete this.dropDown.focusedChild;
17686                         }
17687                         if(this.options.length){
17688                                 this.inherited(arguments);
17689                         }else{
17690                                 // Drop down menu is blank but add one blank entry just so something appears on the screen
17691                                 // to let users know that they are no choices (mimicing native select behavior)
17692                                 dojo.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
17693                                 var item = new dijit.MenuItem({label: "&nbsp;"});
17694                                 this.dropDown.addChild(item);
17695                         }
17696                 }else{
17697                         this._updateSelection();
17698                 }
17699
17700                 this._isLoaded = false;
17701                 this._childrenLoaded = true;
17702
17703                 if(!this._loadingStore){
17704                         // Don't call this if we are loading - since we will handle it later
17705                         this._setValueAttr(this.value);
17706                 }
17707         },
17708
17709         _setValueAttr: function(value){
17710                 this.inherited(arguments);
17711                 dojo.attr(this.valueNode, "value", this.get("value"));
17712         },
17713
17714         _setDisplay: function(/*String*/ newDisplay){
17715                 // summary:
17716                 //              sets the display for the given value (or values)
17717                 var lbl = newDisplay || this.emptyLabel;
17718                 this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
17719                 dijit.setWaiState(this.focusNode, "valuetext", lbl);
17720         },
17721
17722         validate: function(/*Boolean*/ isFocused){
17723                 // summary:
17724                 //              Called by oninit, onblur, and onkeypress.
17725                 // description:
17726                 //              Show missing or invalid messages if appropriate, and highlight textbox field.
17727                 //              Used when a select is initially set to no value and the user is required to
17728                 //              set the value.
17729                 
17730                 var isValid = this.isValid(isFocused);
17731                 this._set("state", isValid ? "" : "Error");
17732                 dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
17733                 var message = isValid ? "" : this._missingMsg;
17734                 if(this.message !== message){
17735                         this._set("message", message);
17736                         dijit.hideTooltip(this.domNode);
17737                         if(message){
17738                                 dijit.showTooltip(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
17739                         }
17740                 }
17741                 return isValid;
17742         },
17743
17744         isValid: function(/*Boolean*/ isFocused){
17745                 // summary:
17746                 //              Whether or not this is a valid value.  The only way a Select
17747                 //              can be invalid is when it's required but nothing is selected.
17748                 return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
17749         },
17750
17751         reset: function(){
17752                 // summary:
17753                 //              Overridden so that the state will be cleared.
17754                 this.inherited(arguments);
17755                 dijit.hideTooltip(this.domNode);
17756                 this._set("state", "");
17757                 this._set("message", "")
17758         },
17759
17760         postMixInProperties: function(){
17761                 // summary:
17762                 //              set the missing message
17763                 this.inherited(arguments);
17764                 this._missingMsg = dojo.i18n.getLocalization("dijit.form", "validate",
17765                                                                         this.lang).missingMessage;
17766         },
17767
17768         postCreate: function(){
17769                 // summary:
17770                 //              stop mousemove from selecting text on IE to be consistent with other browsers
17771
17772                 this.inherited(arguments);
17773
17774                 this.connect(this.domNode, "onmousemove", dojo.stopEvent);
17775         },
17776
17777         _setStyleAttr: function(/*String||Object*/ value){
17778                 this.inherited(arguments);
17779                 dojo.toggleClass(this.domNode, this.baseClass + "FixedWidth", !!this.tableNode.style.width);
17780         },
17781
17782         isLoaded: function(){
17783                 return this._isLoaded;
17784         },
17785
17786         loadDropDown: function(/*Function*/ loadCallback){
17787                 // summary:
17788                 //              populates the menu
17789                 this._loadChildren(true);
17790                 this._isLoaded = true;
17791                 loadCallback();
17792         },
17793
17794         closeDropDown: function(){
17795                 // overriding _HasDropDown.closeDropDown()
17796                 this.inherited(arguments);
17797
17798                 if(this.dropDown && this.dropDown.menuTableNode){
17799                         // Erase possible width: 100% setting from _SelectMenu.resize().
17800                         // Leaving it would interfere with the next openDropDown() call, which
17801                         // queries the natural size of the drop down.
17802                         this.dropDown.menuTableNode.style.width = "";
17803                 }
17804         },
17805
17806         uninitialize: function(preserveDom){
17807                 if(this.dropDown && !this.dropDown._destroyed){
17808                         this.dropDown.destroyRecursive(preserveDom);
17809                         delete this.dropDown;
17810                 }
17811                 this.inherited(arguments);
17812         }
17813 });
17814
17815 }
17816
17817 if(!dojo._hasResource["dijit.form.SimpleTextarea"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17818 dojo._hasResource["dijit.form.SimpleTextarea"] = true;
17819 dojo.provide("dijit.form.SimpleTextarea");
17820
17821
17822
17823 dojo.declare("dijit.form.SimpleTextarea",
17824         dijit.form.TextBox,
17825         {
17826         // summary:
17827         //              A simple textarea that degrades, and responds to
17828         //              minimal LayoutContainer usage, and works with dijit.form.Form.
17829         //              Doesn't automatically size according to input, like Textarea.
17830         //
17831         // example:
17832         //      |       <textarea dojoType="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
17833         //
17834         // example:
17835         //      |       new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
17836
17837         baseClass: "dijitTextBox dijitTextArea",
17838
17839         attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
17840                 rows:"textbox", cols: "textbox"
17841         }),
17842
17843         // rows: Number
17844         //              The number of rows of text.
17845         rows: "3",
17846
17847         // rows: Number
17848         //              The number of characters per line.
17849         cols: "20",
17850
17851         templateString: "<textarea ${!nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
17852
17853         postMixInProperties: function(){
17854                 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
17855                 // TODO: parser will handle this in 2.0
17856                 if(!this.value && this.srcNodeRef){
17857                         this.value = this.srcNodeRef.value;
17858                 }
17859                 this.inherited(arguments);
17860         },
17861
17862         buildRendering: function(){
17863                 this.inherited(arguments);
17864                 if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
17865                         dojo.addClass(this.textbox, "dijitTextAreaCols");
17866                 }
17867         },
17868
17869         filter: function(/*String*/ value){
17870                 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
17871                 // as \r\n instead of just \n
17872                 if(value){
17873                         value = value.replace(/\r/g,"");
17874                 }
17875                 return this.inherited(arguments);
17876         },
17877
17878         _previousValue: "",
17879         _onInput: function(/*Event?*/ e){
17880                 // Override TextBox._onInput() to enforce maxLength restriction
17881                 if(this.maxLength){
17882                         var maxLength = parseInt(this.maxLength);
17883                         var value = this.textbox.value.replace(/\r/g,'');
17884                         var overflow = value.length - maxLength;
17885                         if(overflow > 0){
17886                                 if(e){ dojo.stopEvent(e); }
17887                                 var textarea = this.textbox;
17888                                 if(textarea.selectionStart){
17889                                         var pos = textarea.selectionStart;
17890                                         var cr = 0;
17891                                         if(dojo.isOpera){
17892                                                 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
17893                                         }
17894                                         this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
17895                                         textarea.setSelectionRange(pos-overflow, pos-overflow);
17896                                 }else if(dojo.doc.selection){ //IE
17897                                         textarea.focus();
17898                                         var range = dojo.doc.selection.createRange();
17899                                         // delete overflow characters
17900                                         range.moveStart("character", -overflow);
17901                                         range.text = '';
17902                                         // show cursor
17903                                         range.select();
17904                                 }
17905                         }
17906                         this._previousValue = this.textbox.value;
17907                 }
17908                 this.inherited(arguments);
17909         }
17910 });
17911
17912 }
17913
17914 if(!dojo._hasResource["dijit.InlineEditBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
17915 dojo._hasResource["dijit.InlineEditBox"] = true;
17916 dojo.provide("dijit.InlineEditBox");
17917
17918
17919
17920
17921
17922
17923
17924
17925 dojo.declare("dijit.InlineEditBox",
17926         dijit._Widget,
17927         {
17928         // summary:
17929         //              An element with in-line edit capabilites
17930         //
17931         // description:
17932         //              Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
17933         //              when you click it, an editor shows up in place of the original
17934         //              text.  Optionally, Save and Cancel button are displayed below the edit widget.
17935         //              When Save is clicked, the text is pulled from the edit
17936         //              widget and redisplayed and the edit widget is again hidden.
17937         //              By default a plain Textarea widget is used as the editor (or for
17938         //              inline values a TextBox), but you can specify an editor such as
17939         //              dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
17940         //              An edit widget must support the following API to be used:
17941         //                      - displayedValue or value as initialization parameter,
17942         //                      and available through set('displayedValue') / set('value')
17943         //                      - void focus()
17944         //                      - DOM-node focusNode = node containing editable text
17945
17946         // editing: [readonly] Boolean
17947         //              Is the node currently in edit mode?
17948         editing: false,
17949
17950         // autoSave: Boolean
17951         //              Changing the value automatically saves it; don't have to push save button
17952         //              (and save button isn't even displayed)
17953         autoSave: true,
17954
17955         // buttonSave: String
17956         //              Save button label
17957         buttonSave: "",
17958
17959         // buttonCancel: String
17960         //              Cancel button label
17961         buttonCancel: "",
17962
17963         // renderAsHtml: Boolean
17964         //              Set this to true if the specified Editor's value should be interpreted as HTML
17965         //              rather than plain text (ex: `dijit.Editor`)
17966         renderAsHtml: false,
17967
17968         // editor: String|Function
17969         //              Class name (or reference to the Class) for Editor widget
17970         editor: "dijit.form.TextBox",
17971
17972         // editorWrapper: String|Function
17973         //              Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
17974         //              buttons.
17975         editorWrapper: "dijit._InlineEditor",
17976
17977         // editorParams: Object
17978         //              Set of parameters for editor, like {required: true}
17979         editorParams: {},
17980
17981         // disabled: Boolean
17982         //              If true, clicking the InlineEditBox to edit it will have no effect.
17983         disabled: false,
17984
17985         onChange: function(value){
17986                 // summary:
17987                 //              Set this handler to be notified of changes to value.
17988                 // tags:
17989                 //              callback
17990         },
17991
17992         onCancel: function(){
17993                 // summary:
17994                 //              Set this handler to be notified when editing is cancelled.
17995                 // tags:
17996                 //              callback
17997         },
17998
17999         // width: String
18000         //              Width of editor.  By default it's width=100% (ie, block mode).
18001         width: "100%",
18002
18003         // value: String
18004         //              The display value of the widget in read-only mode
18005         value: "",
18006
18007         // noValueIndicator: [const] String
18008         //              The text that gets displayed when there is no value (so that the user has a place to click to edit)
18009         noValueIndicator: dojo.isIE <= 6 ?      // font-family needed on IE6 but it messes up IE8
18010                 "<span style='font-family: wingdings; text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>" :
18011                 "<span style='text-decoration: underline;'>&nbsp;&nbsp;&nbsp;&nbsp;&#x270d;&nbsp;&nbsp;&nbsp;&nbsp;</span>",
18012
18013         constructor: function(){
18014                 // summary:
18015                 //              Sets up private arrays etc.
18016                 // tags:
18017                 //              private
18018                 this.editorParams = {};
18019         },
18020
18021         postMixInProperties: function(){
18022                 this.inherited(arguments);
18023
18024                 // save pointer to original source node, since Widget nulls-out srcNodeRef
18025                 this.displayNode = this.srcNodeRef;
18026
18027                 // connect handlers to the display node
18028                 var events = {
18029                         ondijitclick: "_onClick",
18030                         onmouseover: "_onMouseOver",
18031                         onmouseout: "_onMouseOut",
18032                         onfocus: "_onMouseOver",
18033                         onblur: "_onMouseOut"
18034                 };
18035                 for(var name in events){
18036                         this.connect(this.displayNode, name, events[name]);
18037                 }
18038                 dijit.setWaiRole(this.displayNode, "button");
18039                 if(!this.displayNode.getAttribute("tabIndex")){
18040                         this.displayNode.setAttribute("tabIndex", 0);
18041                 }
18042
18043                 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
18044                    this.value = dojo.trim(this.renderAsHtml ? this.displayNode.innerHTML :
18045                       (this.displayNode.innerText||this.displayNode.textContent||""));
18046                 }
18047                 if(!this.value){
18048                     this.displayNode.innerHTML = this.noValueIndicator;
18049                 }
18050
18051                 dojo.addClass(this.displayNode, 'dijitInlineEditBoxDisplayMode');
18052         },
18053
18054         setDisabled: function(/*Boolean*/ disabled){
18055                 // summary:
18056                 //              Deprecated.   Use set('disabled', ...) instead.
18057                 // tags:
18058                 //              deprecated
18059                 dojo.deprecated("dijit.InlineEditBox.setDisabled() is deprecated.  Use set('disabled', bool) instead.", "", "2.0");
18060                 this.set('disabled', disabled);
18061         },
18062
18063         _setDisabledAttr: function(/*Boolean*/ disabled){
18064                 // summary:
18065                 //              Hook to make set("disabled", ...) work.
18066                 //              Set disabled state of widget.
18067                 dijit.setWaiState(this.domNode, "disabled", disabled);
18068                 if(disabled){
18069                         this.displayNode.removeAttribute("tabIndex");
18070                 }else{
18071                         this.displayNode.setAttribute("tabIndex", 0);
18072                 }
18073                 dojo.toggleClass(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
18074                 this._set("disabled", disabled);
18075         },
18076
18077         _onMouseOver: function(){
18078                 // summary:
18079                 //              Handler for onmouseover and onfocus event.
18080                 // tags:
18081                 //              private
18082                 if(!this.disabled){
18083                         dojo.addClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
18084                 }
18085         },
18086
18087         _onMouseOut: function(){
18088                 // summary:
18089                 //              Handler for onmouseout and onblur event.
18090                 // tags:
18091                 //              private
18092                 dojo.removeClass(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
18093         },
18094
18095         _onClick: function(/*Event*/ e){
18096                 // summary:
18097                 //              Handler for onclick event.
18098                 // tags:
18099                 //              private
18100                 if(this.disabled){ return; }
18101                 if(e){ dojo.stopEvent(e); }
18102                 this._onMouseOut();
18103
18104                 // Since FF gets upset if you move a node while in an event handler for that node...
18105                 setTimeout(dojo.hitch(this, "edit"), 0);
18106         },
18107
18108         edit: function(){
18109                 // summary:
18110                 //              Display the editor widget in place of the original (read only) markup.
18111                 // tags:
18112                 //              private
18113
18114                 if(this.disabled || this.editing){ return; }
18115                 this.editing = true;
18116
18117                 // save some display node values that can be restored later
18118                 this._savedPosition = dojo.style(this.displayNode, "position") || "static";
18119                 this._savedOpacity = dojo.style(this.displayNode, "opacity") || "1";
18120                 this._savedTabIndex = dojo.attr(this.displayNode, "tabIndex") || "0";
18121
18122                 if(this.wrapperWidget){
18123                         var ew = this.wrapperWidget.editWidget;
18124                         ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
18125                 }else{
18126                         // Placeholder for edit widget
18127                         // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
18128                         // when Calendar dropdown appears, which happens automatically on focus.
18129                         var placeholder = dojo.create("span", null, this.domNode, "before");
18130
18131                         // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
18132                         var ewc = typeof this.editorWrapper == "string" ? dojo.getObject(this.editorWrapper) : this.editorWrapper;
18133                         this.wrapperWidget = new ewc({
18134                                 value: this.value,
18135                                 buttonSave: this.buttonSave,
18136                                 buttonCancel: this.buttonCancel,
18137                                 dir: this.dir,
18138                                 lang: this.lang,
18139                                 tabIndex: this._savedTabIndex,
18140                                 editor: this.editor,
18141                                 inlineEditBox: this,
18142                                 sourceStyle: dojo.getComputedStyle(this.displayNode),
18143                                 save: dojo.hitch(this, "save"),
18144                                 cancel: dojo.hitch(this, "cancel")
18145                         }, placeholder);
18146                         if(!this._started){
18147                                 this.startup();
18148                         }
18149                 }
18150                 var ww = this.wrapperWidget;
18151
18152                 if(dojo.isIE){
18153                         dijit.focus(dijit.getFocus()); // IE (at least 8) needs help with tab order changes
18154                 }
18155                 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
18156                 // and then when it's finished rendering, we switch from display mode to editor
18157                 // position:absolute releases screen space allocated to the display node
18158                 // opacity:0 is the same as visibility:hidden but is still focusable
18159                 // visiblity:hidden removes focus outline
18160
18161                 dojo.style(this.displayNode, { position: "absolute", opacity: "0", display: "none" }); // makes display node invisible, display style used for focus-ability
18162                 dojo.style(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
18163                 dojo.attr(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
18164
18165                 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
18166                 // focus can be shifted without incident.  (browser may needs some time to render the editor.)
18167                 setTimeout(dojo.hitch(this, function(){
18168                         ww.focus(); // both nodes are showing, so we can switch focus safely
18169                         ww._resetValue = ww.getValue();
18170                 }), 0);
18171         },
18172
18173         _onBlur: function(){
18174                 // summary:
18175                 //              Called when focus moves outside the InlineEditBox.
18176                 //              Performs garbage collection.
18177                 // tags:
18178                 //              private
18179
18180                 this.inherited(arguments);
18181                 if(!this.editing){
18182                         /* causes IE focus problems, see TooltipDialog_a11y.html...
18183                         setTimeout(dojo.hitch(this, function(){
18184                                 if(this.wrapperWidget){
18185                                         this.wrapperWidget.destroy();
18186                                         delete this.wrapperWidget;
18187                                 }
18188                         }), 0);
18189                         */
18190                 }
18191         },
18192
18193         destroy: function(){
18194                 if(this.wrapperWidget && !this.wrapperWidget._destroyed){
18195                         this.wrapperWidget.destroy();
18196                         delete this.wrapperWidget;
18197                 }
18198                 this.inherited(arguments);
18199         },
18200
18201         _showText: function(/*Boolean*/ focus){
18202                 // summary:
18203                 //              Revert to display mode, and optionally focus on display node
18204                 // tags:
18205                 //              private
18206
18207                 var ww = this.wrapperWidget;
18208                 dojo.style(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
18209                 dojo.style(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity, display: "" }); // make the original text visible
18210                 dojo.attr(this.displayNode, "tabIndex", this._savedTabIndex);
18211                 if(focus){
18212                         dijit.focus(this.displayNode);
18213                 }
18214         },
18215
18216         save: function(/*Boolean*/ focus){
18217                 // summary:
18218                 //              Save the contents of the editor and revert to display mode.
18219                 // focus: Boolean
18220                 //              Focus on the display mode text
18221                 // tags:
18222                 //              private
18223
18224                 if(this.disabled || !this.editing){ return; }
18225                 this.editing = false;
18226
18227                 var ww = this.wrapperWidget;
18228                 var value = ww.getValue();
18229                 this.set('value', value); // display changed, formatted value
18230
18231                 this._showText(focus); // set focus as needed
18232         },
18233
18234         setValue: function(/*String*/ val){
18235                 // summary:
18236                 //              Deprecated.   Use set('value', ...) instead.
18237                 // tags:
18238                 //              deprecated
18239                 dojo.deprecated("dijit.InlineEditBox.setValue() is deprecated.  Use set('value', ...) instead.", "", "2.0");
18240                 return this.set("value", val);
18241         },
18242
18243         _setValueAttr: function(/*String*/ val){
18244                 // summary:
18245                 //              Hook to make set("value", ...) work.
18246                 //              Inserts specified HTML value into this node, or an "input needed" character if node is blank.
18247
18248                 val = dojo.trim(val);
18249                 var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
18250                 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
18251                 this._set("value", val);
18252
18253                 if(this._started){
18254                         // tell the world that we have changed
18255                         setTimeout(dojo.hitch(this, "onChange", val), 0); // setTimeout prevents browser freeze for long-running event handlers
18256                 }
18257         },
18258
18259         getValue: function(){
18260                 // summary:
18261                 //              Deprecated.   Use get('value') instead.
18262                 // tags:
18263                 //              deprecated
18264                 dojo.deprecated("dijit.InlineEditBox.getValue() is deprecated.  Use get('value') instead.", "", "2.0");
18265                 return this.get("value");
18266         },
18267
18268         cancel: function(/*Boolean*/ focus){
18269                 // summary:
18270                 //              Revert to display mode, discarding any changes made in the editor
18271                 // tags:
18272                 //              private
18273
18274                 if(this.disabled || !this.editing){ return; }
18275                 this.editing = false;
18276
18277                 // tell the world that we have no changes
18278                 setTimeout(dojo.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
18279
18280                 this._showText(focus);
18281         }
18282 });
18283
18284 dojo.declare(
18285         "dijit._InlineEditor",
18286          [dijit._Widget, dijit._Templated],
18287 {
18288         // summary:
18289         //              Internal widget used by InlineEditBox, displayed when in editing mode
18290         //              to display the editor and maybe save/cancel buttons.  Calling code should
18291         //              connect to save/cancel methods to detect when editing is finished
18292         //
18293         //              Has mainly the same parameters as InlineEditBox, plus these values:
18294         //
18295         // style: Object
18296         //              Set of CSS attributes of display node, to replicate in editor
18297         //
18298         // value: String
18299         //              Value as an HTML string or plain text string, depending on renderAsHTML flag
18300
18301         templateString: dojo.cache("dijit", "templates/InlineEditBox.html", "<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit.form.Button\"  data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"),
18302         widgetsInTemplate: true,
18303
18304         postMixInProperties: function(){
18305                 this.inherited(arguments);
18306                 this.messages = dojo.i18n.getLocalization("dijit", "common", this.lang);
18307                 dojo.forEach(["buttonSave", "buttonCancel"], function(prop){
18308                         if(!this[prop]){ this[prop] = this.messages[prop]; }
18309                 }, this);
18310         },
18311
18312         buildRendering: function(){
18313                 this.inherited(arguments);
18314
18315                 // Create edit widget in place in the template
18316                 var cls = typeof this.editor == "string" ? dojo.getObject(this.editor) : this.editor;
18317
18318                 // Copy the style from the source
18319                 // Don't copy ALL properties though, just the necessary/applicable ones.
18320                 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
18321                 // is a relative value like 200%, rather than an absolute value like 24px, and
18322                 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
18323                 var srcStyle = this.sourceStyle,
18324                         editStyle = "line-height:" + srcStyle.lineHeight + ";",
18325                         destStyle = dojo.getComputedStyle(this.domNode);
18326                 dojo.forEach(["Weight","Family","Size","Style"], function(prop){
18327                         var textStyle = srcStyle["font"+prop],
18328                                 wrapperStyle = destStyle["font"+prop];
18329                         if(wrapperStyle != textStyle){
18330                                 editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
18331                         }
18332                 }, this);
18333                 dojo.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
18334                         this.domNode.style[prop] = srcStyle[prop];
18335                 }, this);
18336                 var width = this.inlineEditBox.width;
18337                 if(width == "100%"){
18338                         // block mode
18339                         editStyle += "width:100%;";
18340                         this.domNode.style.display = "block";
18341                 }else{
18342                         // inline-block mode
18343                         editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
18344                 }
18345                 var editorParams = dojo.delegate(this.inlineEditBox.editorParams, {
18346                         style: editStyle,
18347                         dir: this.dir,
18348                         lang: this.lang
18349                 });
18350                 editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
18351                 this.editWidget = new cls(editorParams, this.editorPlaceholder);
18352
18353                 if(this.inlineEditBox.autoSave){
18354                         // Remove the save/cancel buttons since saving is done by simply tabbing away or
18355                         // selecting a value from the drop down list
18356                         dojo.destroy(this.buttonContainer);
18357                 }
18358         },
18359
18360         postCreate: function(){
18361                 this.inherited(arguments);
18362
18363                 var ew = this.editWidget;
18364
18365                 if(this.inlineEditBox.autoSave){
18366                         // Selecting a value from a drop down list causes an onChange event and then we save
18367                         this.connect(ew, "onChange", "_onChange");
18368
18369                         // ESC and TAB should cancel and save.  Note that edit widgets do a stopEvent() on ESC key (to
18370                         // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
18371                         // so this is the only way we can see the key press event.
18372                         this.connect(ew, "onKeyPress", "_onKeyPress");
18373                 }else{
18374                         // If possible, enable/disable save button based on whether the user has changed the value
18375                         if("intermediateChanges" in ew){
18376                                 ew.set("intermediateChanges", true);
18377                                 this.connect(ew, "onChange", "_onIntermediateChange");
18378                                 this.saveButton.set("disabled", true);
18379                         }
18380                 }
18381         },
18382
18383         _onIntermediateChange: function(val){
18384                 // summary:
18385                 //              Called for editor widgets that support the intermediateChanges=true flag as a way
18386                 //              to detect when to enable/disabled the save button
18387                 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
18388         },
18389
18390         destroy: function(){
18391                 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
18392                 this.inherited(arguments);
18393         },
18394
18395         getValue: function(){
18396                 // summary:
18397                 //              Return the [display] value of the edit widget
18398                 var ew = this.editWidget;
18399                 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
18400         },
18401
18402         _onKeyPress: function(e){
18403                 // summary:
18404                 //              Handler for keypress in the edit box in autoSave mode.
18405                 // description:
18406                 //              For autoSave widgets, if Esc/Enter, call cancel/save.
18407                 // tags:
18408                 //              private
18409
18410                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
18411                         if(e.altKey || e.ctrlKey){ return; }
18412                         // If Enter/Esc pressed, treat as save/cancel.
18413                         if(e.charOrCode == dojo.keys.ESCAPE){
18414                                 dojo.stopEvent(e);
18415                                 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
18416                         }else if(e.charOrCode == dojo.keys.ENTER && e.target.tagName == "INPUT"){
18417                                 dojo.stopEvent(e);
18418                                 this._onChange(); // fire _onBlur and then save
18419                         }
18420
18421                         // _onBlur will handle TAB automatically by allowing
18422                         // the TAB to change focus before we mess with the DOM: #6227
18423                         // Expounding by request:
18424                         //      The current focus is on the edit widget input field.
18425                         //      save() will hide and destroy this widget.
18426                         //      We want the focus to jump from the currently hidden
18427                         //      displayNode, but since it's hidden, it's impossible to
18428                         //      unhide it, focus it, and then have the browser focus
18429                         //      away from it to the next focusable element since each
18430                         //      of these events is asynchronous and the focus-to-next-element
18431                         //      is already queued.
18432                         //      So we allow the browser time to unqueue the move-focus event
18433                         //      before we do all the hide/show stuff.
18434                 }
18435         },
18436
18437         _onBlur: function(){
18438                 // summary:
18439                 //              Called when focus moves outside the editor
18440                 // tags:
18441                 //              private
18442
18443                 this.inherited(arguments);
18444                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
18445                         if(this.getValue() == this._resetValue){
18446                                 this.cancel(false);
18447                         }else if(this.enableSave()){
18448                                 this.save(false);
18449                         }
18450                 }
18451         },
18452
18453         _onChange: function(){
18454                 // summary:
18455                 //              Called when the underlying widget fires an onChange event,
18456                 //              such as when the user selects a value from the drop down list of a ComboBox,
18457                 //              which means that the user has finished entering the value and we should save.
18458                 // tags:
18459                 //              private
18460
18461                 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
18462                         dojo.style(this.inlineEditBox.displayNode, { display: "" });
18463                         dijit.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
18464                 }
18465         },
18466
18467         enableSave: function(){
18468                 // summary:
18469                 //              User overridable function returning a Boolean to indicate
18470                 //              if the Save button should be enabled or not - usually due to invalid conditions
18471                 // tags:
18472                 //              extension
18473                 return (
18474                         this.editWidget.isValid
18475                         ? this.editWidget.isValid()
18476                         : true
18477                 );
18478         },
18479
18480         focus: function(){
18481                 // summary:
18482                 //              Focus the edit widget.
18483                 // tags:
18484                 //              protected
18485
18486                 this.editWidget.focus();
18487                 setTimeout(dojo.hitch(this, function(){
18488                         if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
18489                                 dijit.selectInputText(this.editWidget.focusNode);
18490                         }
18491                 }), 0);
18492         }
18493 });
18494
18495 }
18496
18497 if(!dojo._hasResource["dojo.cookie"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18498 dojo._hasResource["dojo.cookie"] = true;
18499 dojo.provide("dojo.cookie");
18500
18501
18502
18503 /*=====
18504 dojo.__cookieProps = function(){
18505         //      expires: Date|String|Number?
18506         //              If a number, the number of days from today at which the cookie
18507         //              will expire. If a date, the date past which the cookie will expire.
18508         //              If expires is in the past, the cookie will be deleted.
18509         //              If expires is omitted or is 0, the cookie will expire when the browser closes. << FIXME: 0 seems to disappear right away? FF3.
18510         //      path: String?
18511         //              The path to use for the cookie.
18512         //      domain: String?
18513         //              The domain to use for the cookie.
18514         //      secure: Boolean?
18515         //              Whether to only send the cookie on secure connections
18516         this.expires = expires;
18517         this.path = path;
18518         this.domain = domain;
18519         this.secure = secure;
18520 }
18521 =====*/
18522
18523
18524 dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
18525         //      summary:
18526         //              Get or set a cookie.
18527         //      description:
18528         //              If one argument is passed, returns the value of the cookie
18529         //              For two or more arguments, acts as a setter.
18530         //      name:
18531         //              Name of the cookie
18532         //      value:
18533         //              Value for the cookie
18534         //      props:
18535         //              Properties for the cookie
18536         //      example:
18537         //              set a cookie with the JSON-serialized contents of an object which
18538         //              will expire 5 days from now:
18539         //      |       dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
18540         //
18541         //      example:
18542         //              de-serialize a cookie back into a JavaScript object:
18543         //      |       var config = dojo.fromJson(dojo.cookie("configObj"));
18544         //
18545         //      example:
18546         //              delete a cookie:
18547         //      |       dojo.cookie("configObj", null, {expires: -1});
18548         var c = document.cookie;
18549         if(arguments.length == 1){
18550                 var matches = c.match(new RegExp("(?:^|; )" + dojo.regexp.escapeString(name) + "=([^;]*)"));
18551                 return matches ? decodeURIComponent(matches[1]) : undefined; // String or undefined
18552         }else{
18553                 props = props || {};
18554 // FIXME: expires=0 seems to disappear right away, not on close? (FF3)  Change docs?
18555                 var exp = props.expires;
18556                 if(typeof exp == "number"){
18557                         var d = new Date();
18558                         d.setTime(d.getTime() + exp*24*60*60*1000);
18559                         exp = props.expires = d;
18560                 }
18561                 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
18562
18563                 value = encodeURIComponent(value);
18564                 var updatedCookie = name + "=" + value, propName;
18565                 for(propName in props){
18566                         updatedCookie += "; " + propName;
18567                         var propValue = props[propName];
18568                         if(propValue !== true){ updatedCookie += "=" + propValue; }
18569                 }
18570                 document.cookie = updatedCookie;
18571         }
18572 };
18573
18574 dojo.cookie.isSupported = function(){
18575         //      summary:
18576         //              Use to determine if the current browser supports cookies or not.
18577         //
18578         //              Returns true if user allows cookies.
18579         //              Returns false if user doesn't allow cookies.
18580
18581         if(!("cookieEnabled" in navigator)){
18582                 this("__djCookieTest__", "CookiesAllowed");
18583                 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
18584                 if(navigator.cookieEnabled){
18585                         this("__djCookieTest__", "", {expires: -1});
18586                 }
18587         }
18588         return navigator.cookieEnabled;
18589 };
18590
18591 }
18592
18593 if(!dojo._hasResource["dijit.layout.StackController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18594 dojo._hasResource["dijit.layout.StackController"] = true;
18595 dojo.provide("dijit.layout.StackController");
18596
18597
18598
18599
18600
18601
18602
18603 dojo.declare(
18604                 "dijit.layout.StackController",
18605                 [dijit._Widget, dijit._Templated, dijit._Container],
18606                 {
18607                         // summary:
18608                         //              Set of buttons to select a page in a page list.
18609                         // description:
18610                         //              Monitors the specified StackContainer, and whenever a page is
18611                         //              added, deleted, or selected, updates itself accordingly.
18612
18613                         templateString: "<span role='tablist' dojoAttachEvent='onkeypress' class='dijitStackController'></span>",
18614
18615                         // containerId: [const] String
18616                         //              The id of the page container that I point to
18617                         containerId: "",
18618
18619                         // buttonWidget: [const] String
18620                         //              The name of the button widget to create to correspond to each page
18621                         buttonWidget: "dijit.layout._StackButton",
18622
18623                         constructor: function(){
18624                                 this.pane2button = {};          // mapping from pane id to buttons
18625                                 this.pane2connects = {};        // mapping from pane id to this.connect() handles
18626                                 this.pane2watches = {};         // mapping from pane id to watch() handles
18627                         },
18628
18629                         buildRendering: function(){
18630                                 this.inherited(arguments);
18631                                 dijit.setWaiRole(this.domNode, "tablist");      // TODO: unneeded?   it's in template above.
18632                         },
18633
18634                         postCreate: function(){
18635                                 this.inherited(arguments);
18636
18637                                 // Listen to notifications from StackContainer
18638                                 this.subscribe(this.containerId+"-startup", "onStartup");
18639                                 this.subscribe(this.containerId+"-addChild", "onAddChild");
18640                                 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
18641                                 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
18642                                 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
18643                         },
18644
18645                         onStartup: function(/*Object*/ info){
18646                                 // summary:
18647                                 //              Called after StackContainer has finished initializing
18648                                 // tags:
18649                                 //              private
18650                                 dojo.forEach(info.children, this.onAddChild, this);
18651                                 if(info.selected){
18652                                         // Show button corresponding to selected pane (unless selected
18653                                         // is null because there are no panes)
18654                                         this.onSelectChild(info.selected);
18655                                 }
18656                         },
18657
18658                         destroy: function(){
18659                                 for(var pane in this.pane2button){
18660                                         this.onRemoveChild(dijit.byId(pane));
18661                                 }
18662                                 this.inherited(arguments);
18663                         },
18664
18665                         onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
18666                                 // summary:
18667                                 //              Called whenever a page is added to the container.
18668                                 //              Create button corresponding to the page.
18669                                 // tags:
18670                                 //              private
18671
18672                                 // create an instance of the button widget
18673                                 var cls = dojo.getObject(this.buttonWidget);
18674                                 var button = new cls({
18675                                         id: this.id + "_" + page.id,
18676                                         label: page.title,
18677                                         dir: page.dir,
18678                                         lang: page.lang,
18679                                         showLabel: page.showTitle,
18680                                         iconClass: page.iconClass,
18681                                         closeButton: page.closable,
18682                                         title: page.tooltip
18683                                 });
18684                                 dijit.setWaiState(button.focusNode,"selected", "false");
18685
18686
18687                                 // map from page attribute to corresponding tab button attribute
18688                                 var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
18689                                         buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
18690
18691                                 // watch() so events like page title changes are reflected in tab button
18692                                 this.pane2watches[page.id] = dojo.map(pageAttrList, function(pageAttr, idx){
18693                                         return page.watch(pageAttr, function(name, oldVal, newVal){
18694                                                 button.set(buttonAttrList[idx], newVal);
18695                                         });
18696                                 });
18697                                         
18698                                 // connections so that clicking a tab button selects the corresponding page
18699                                 this.pane2connects[page.id] = [
18700                                         this.connect(button, 'onClick', dojo.hitch(this,"onButtonClick", page)),
18701                                         this.connect(button, 'onClickCloseButton', dojo.hitch(this,"onCloseButtonClick", page))
18702                                 ];
18703
18704                                 this.addChild(button, insertIndex);
18705                                 this.pane2button[page.id] = button;
18706                                 page.controlButton = button;    // this value might be overwritten if two tabs point to same container
18707                                 if(!this._currentChild){ // put the first child into the tab order
18708                                         button.focusNode.setAttribute("tabIndex", "0");
18709                                         dijit.setWaiState(button.focusNode, "selected", "true");
18710                                         this._currentChild = page;
18711                                 }
18712                                 // make sure all tabs have the same length
18713                                 if(!this.isLeftToRight() && dojo.isIE && this._rectifyRtlTabList){
18714                                         this._rectifyRtlTabList();
18715                                 }
18716                         },
18717
18718                         onRemoveChild: function(/*dijit._Widget*/ page){
18719                                 // summary:
18720                                 //              Called whenever a page is removed from the container.
18721                                 //              Remove the button corresponding to the page.
18722                                 // tags:
18723                                 //              private
18724
18725                                 if(this._currentChild === page){ this._currentChild = null; }
18726
18727                                 // disconnect/unwatch connections/watches related to page being removed
18728                                 dojo.forEach(this.pane2connects[page.id], dojo.hitch(this, "disconnect"));
18729                                 delete this.pane2connects[page.id];
18730                                 dojo.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
18731                                 delete this.pane2watches[page.id];
18732
18733                                 var button = this.pane2button[page.id];
18734                                 if(button){
18735                                         this.removeChild(button);
18736                                         delete this.pane2button[page.id];
18737                                         button.destroy();
18738                                 }
18739                                 delete page.controlButton;
18740                         },
18741
18742                         onSelectChild: function(/*dijit._Widget*/ page){
18743                                 // summary:
18744                                 //              Called when a page has been selected in the StackContainer, either by me or by another StackController
18745                                 // tags:
18746                                 //              private
18747
18748                                 if(!page){ return; }
18749
18750                                 if(this._currentChild){
18751                                         var oldButton=this.pane2button[this._currentChild.id];
18752                                         oldButton.set('checked', false);
18753                                         dijit.setWaiState(oldButton.focusNode, "selected", "false");
18754                                         oldButton.focusNode.setAttribute("tabIndex", "-1");
18755                                 }
18756
18757                                 var newButton=this.pane2button[page.id];
18758                                 newButton.set('checked', true);
18759                                 dijit.setWaiState(newButton.focusNode, "selected", "true");
18760                                 this._currentChild = page;
18761                                 newButton.focusNode.setAttribute("tabIndex", "0");
18762                                 var container = dijit.byId(this.containerId);
18763                                 dijit.setWaiState(container.containerNode, "labelledby", newButton.id);
18764                         },
18765
18766                         onButtonClick: function(/*dijit._Widget*/ page){
18767                                 // summary:
18768                                 //              Called whenever one of my child buttons is pressed in an attempt to select a page
18769                                 // tags:
18770                                 //              private
18771
18772                                 var container = dijit.byId(this.containerId);
18773                                 container.selectChild(page);
18774                         },
18775
18776                         onCloseButtonClick: function(/*dijit._Widget*/ page){
18777                                 // summary:
18778                                 //              Called whenever one of my child buttons [X] is pressed in an attempt to close a page
18779                                 // tags:
18780                                 //              private
18781
18782                                 var container = dijit.byId(this.containerId);
18783                                 container.closeChild(page);
18784                                 if(this._currentChild){
18785                                         var b = this.pane2button[this._currentChild.id];
18786                                         if(b){
18787                                                 dijit.focus(b.focusNode || b.domNode);
18788                                         }
18789                                 }
18790                         },
18791
18792                         // TODO: this is a bit redundant with forward, back api in StackContainer
18793                         adjacent: function(/*Boolean*/ forward){
18794                                 // summary:
18795                                 //              Helper for onkeypress to find next/previous button
18796                                 // tags:
18797                                 //              private
18798
18799                                 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
18800                                 // find currently focused button in children array
18801                                 var children = this.getChildren();
18802                                 var current = dojo.indexOf(children, this.pane2button[this._currentChild.id]);
18803                                 // pick next button to focus on
18804                                 var offset = forward ? 1 : children.length - 1;
18805                                 return children[ (current + offset) % children.length ]; // dijit._Widget
18806                         },
18807
18808                         onkeypress: function(/*Event*/ e){
18809                                 // summary:
18810                                 //              Handle keystrokes on the page list, for advancing to next/previous button
18811                                 //              and closing the current page if the page is closable.
18812                                 // tags:
18813                                 //              private
18814
18815                                 if(this.disabled || e.altKey ){ return; }
18816                                 var forward = null;
18817                                 if(e.ctrlKey || !e._djpage){
18818                                         var k = dojo.keys;
18819                                         switch(e.charOrCode){
18820                                                 case k.LEFT_ARROW:
18821                                                 case k.UP_ARROW:
18822                                                         if(!e._djpage){ forward = false; }
18823                                                         break;
18824                                                 case k.PAGE_UP:
18825                                                         if(e.ctrlKey){ forward = false; }
18826                                                         break;
18827                                                 case k.RIGHT_ARROW:
18828                                                 case k.DOWN_ARROW:
18829                                                         if(!e._djpage){ forward = true; }
18830                                                         break;
18831                                                 case k.PAGE_DOWN:
18832                                                         if(e.ctrlKey){ forward = true; }
18833                                                         break;
18834                                                 case k.HOME:
18835                                                 case k.END:
18836                                                         var children = this.getChildren();
18837                                                         if(children && children.length){
18838                                                                 children[e.charOrCode == k.HOME ? 0 : children.length-1].onClick();
18839                                                         }
18840                                                         dojo.stopEvent(e);
18841                                                         break;
18842                                                 case k.DELETE:
18843                                                         if(this._currentChild.closable){
18844                                                                 this.onCloseButtonClick(this._currentChild);
18845                                                         }
18846                                                         dojo.stopEvent(e);
18847                                                         break;
18848                                                 default:
18849                                                         if(e.ctrlKey){
18850                                                                 if(e.charOrCode === k.TAB){
18851                                                                         this.adjacent(!e.shiftKey).onClick();
18852                                                                         dojo.stopEvent(e);
18853                                                                 }else if(e.charOrCode == "w"){
18854                                                                         if(this._currentChild.closable){
18855                                                                                 this.onCloseButtonClick(this._currentChild);
18856                                                                         }
18857                                                                         dojo.stopEvent(e); // avoid browser tab closing.
18858                                                                 }
18859                                                         }
18860                                         }
18861                                         // handle next/previous page navigation (left/right arrow, etc.)
18862                                         if(forward !== null){
18863                                                 this.adjacent(forward).onClick();
18864                                                 dojo.stopEvent(e);
18865                                         }
18866                                 }
18867                         },
18868
18869                         onContainerKeyPress: function(/*Object*/ info){
18870                                 // summary:
18871                                 //              Called when there was a keypress on the container
18872                                 // tags:
18873                                 //              private
18874                                 info.e._djpage = info.page;
18875                                 this.onkeypress(info.e);
18876                         }
18877         });
18878
18879
18880 dojo.declare("dijit.layout._StackButton",
18881                 dijit.form.ToggleButton,
18882                 {
18883                 // summary:
18884                 //              Internal widget used by StackContainer.
18885                 // description:
18886                 //              The button-like or tab-like object you click to select or delete a page
18887                 // tags:
18888                 //              private
18889
18890                 // Override _FormWidget.tabIndex.
18891                 // StackContainer buttons are not in the tab order by default.
18892                 // Probably we should be calling this.startupKeyNavChildren() instead.
18893                 tabIndex: "-1",
18894
18895                 buildRendering: function(/*Event*/ evt){
18896                         this.inherited(arguments);
18897                         dijit.setWaiRole((this.focusNode || this.domNode), "tab");
18898                 },
18899
18900                 onClick: function(/*Event*/ evt){
18901                         // summary:
18902                         //              This is for TabContainer where the tabs are <span> rather than button,
18903                         //              so need to set focus explicitly (on some browsers)
18904                         //              Note that you shouldn't override this method, but you can connect to it.
18905                         dijit.focus(this.focusNode);
18906
18907                         // ... now let StackController catch the event and tell me what to do
18908                 },
18909
18910                 onClickCloseButton: function(/*Event*/ evt){
18911                         // summary:
18912                         //              StackContainer connects to this function; if your widget contains a close button
18913                         //              then clicking it should call this function.
18914                         //              Note that you shouldn't override this method, but you can connect to it.
18915                         evt.stopPropagation();
18916                 }
18917         });
18918
18919 }
18920
18921 if(!dojo._hasResource["dijit.layout.StackContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
18922 dojo._hasResource["dijit.layout.StackContainer"] = true;
18923 dojo.provide("dijit.layout.StackContainer");
18924
18925
18926
18927
18928
18929
18930
18931 dojo.declare(
18932         "dijit.layout.StackContainer",
18933         dijit.layout._LayoutWidget,
18934         {
18935         // summary:
18936         //              A container that has multiple children, but shows only
18937         //              one child at a time
18938         //
18939         // description:
18940         //              A container for widgets (ContentPanes, for example) That displays
18941         //              only one Widget at a time.
18942         //
18943         //              Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
18944         //
18945         //              Can be base class for container, Wizard, Show, etc.
18946
18947         // doLayout: Boolean
18948         //              If true, change the size of my currently displayed child to match my size
18949         doLayout: true,
18950
18951         // persist: Boolean
18952         //              Remembers the selected child across sessions
18953         persist: false,
18954
18955         baseClass: "dijitStackContainer",
18956
18957 /*=====
18958         // selectedChildWidget: [readonly] dijit._Widget
18959         //              References the currently selected child widget, if any.
18960         //              Adjust selected child with selectChild() method.
18961         selectedChildWidget: null,
18962 =====*/
18963
18964         buildRendering: function(){
18965                 this.inherited(arguments);
18966                 dojo.addClass(this.domNode, "dijitLayoutContainer");
18967                 dijit.setWaiRole(this.containerNode, "tabpanel");
18968         },
18969
18970         postCreate: function(){
18971                 this.inherited(arguments);
18972                 this.connect(this.domNode, "onkeypress", this._onKeyPress);
18973         },
18974
18975         startup: function(){
18976                 if(this._started){ return; }
18977
18978                 var children = this.getChildren();
18979
18980                 // Setup each page panel to be initially hidden
18981                 dojo.forEach(children, this._setupChild, this);
18982
18983                 // Figure out which child to initially display, defaulting to first one
18984                 if(this.persist){
18985                         this.selectedChildWidget = dijit.byId(dojo.cookie(this.id + "_selectedChild"));
18986                 }else{
18987                         dojo.some(children, function(child){
18988                                 if(child.selected){
18989                                         this.selectedChildWidget = child;
18990                                 }
18991                                 return child.selected;
18992                         }, this);
18993                 }
18994                 var selected = this.selectedChildWidget;
18995                 if(!selected && children[0]){
18996                         selected = this.selectedChildWidget = children[0];
18997                         selected.selected = true;
18998                 }
18999
19000                 // Publish information about myself so any StackControllers can initialize.
19001                 // This needs to happen before this.inherited(arguments) so that for
19002                 // TabContainer, this._contentBox doesn't include the space for the tab labels.
19003                 dojo.publish(this.id+"-startup", [{children: children, selected: selected}]);
19004
19005                 // Startup each child widget, and do initial layout like setting this._contentBox,
19006                 // then calls this.resize() which does the initial sizing on the selected child.
19007                 this.inherited(arguments);
19008         },
19009
19010         resize: function(){
19011                 // Resize is called when we are first made visible (it's called from startup()
19012                 // if we are initially visible).   If this is the first time we've been made
19013                 // visible then show our first child.
19014                 var selected = this.selectedChildWidget;
19015                 if(selected && !this._hasBeenShown){
19016                         this._hasBeenShown = true;
19017                         this._showChild(selected);
19018                 }
19019                 this.inherited(arguments);
19020         },
19021
19022         _setupChild: function(/*dijit._Widget*/ child){
19023                 // Overrides _LayoutWidget._setupChild()
19024
19025                 this.inherited(arguments);
19026
19027                 dojo.replaceClass(child.domNode, "dijitHidden", "dijitVisible");
19028
19029                 // remove the title attribute so it doesn't show up when i hover
19030                 // over a node
19031                 child.domNode.title = "";
19032         },
19033
19034         addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
19035                 // Overrides _Container.addChild() to do layout and publish events
19036
19037                 this.inherited(arguments);
19038
19039                 if(this._started){
19040                         dojo.publish(this.id+"-addChild", [child, insertIndex]);
19041
19042                         // in case the tab titles have overflowed from one line to two lines
19043                         // (or, if this if first child, from zero lines to one line)
19044                         // TODO: w/ScrollingTabController this is no longer necessary, although
19045                         // ScrollTabController.resize() does need to get called to show/hide
19046                         // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild()
19047                         this.layout();
19048
19049                         // if this is the first child, then select it
19050                         if(!this.selectedChildWidget){
19051                                 this.selectChild(child);
19052                         }
19053                 }
19054         },
19055
19056         removeChild: function(/*dijit._Widget*/ page){
19057                 // Overrides _Container.removeChild() to do layout and publish events
19058
19059                 this.inherited(arguments);
19060
19061                 if(this._started){
19062                         // this will notify any tablists to remove a button; do this first because it may affect sizing
19063                         dojo.publish(this.id + "-removeChild", [page]);
19064                 }
19065
19066                 // If we are being destroyed than don't run the code below (to select another page), because we are deleting
19067                 // every page one by one
19068                 if(this._beingDestroyed){ return; }
19069
19070                 // Select new page to display, also updating TabController to show the respective tab.
19071                 // Do this before layout call because it can affect the height of the TabController.
19072                 if(this.selectedChildWidget === page){
19073                         this.selectedChildWidget = undefined;
19074                         if(this._started){
19075                                 var children = this.getChildren();
19076                                 if(children.length){
19077                                         this.selectChild(children[0]);
19078                                 }
19079                         }
19080                 }
19081
19082                 if(this._started){
19083                         // In case the tab titles now take up one line instead of two lines
19084                         // (note though that ScrollingTabController never overflows to multiple lines),
19085                         // or the height has changed slightly because of addition/removal of tab which close icon
19086                         this.layout();
19087                 }
19088         },
19089
19090         selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
19091                 // summary:
19092                 //              Show the given widget (which must be one of my children)
19093                 // page:
19094                 //              Reference to child widget or id of child widget
19095
19096                 page = dijit.byId(page);
19097
19098                 if(this.selectedChildWidget != page){
19099                         // Deselect old page and select new one
19100                         var d = this._transition(page, this.selectedChildWidget, animate);
19101                         this._set("selectedChildWidget", page);
19102                         dojo.publish(this.id+"-selectChild", [page]);
19103
19104                         if(this.persist){
19105                                 dojo.cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
19106                         }
19107                 }
19108
19109                 return d;               // If child has an href, promise that fires when the child's href finishes loading
19110         },
19111
19112         _transition: function(/*dijit._Widget*/ newWidget, /*dijit._Widget*/ oldWidget, /*Boolean*/ animate){
19113                 // summary:
19114                 //              Hide the old widget and display the new widget.
19115                 //              Subclasses should override this.
19116                 // tags:
19117                 //              protected extension
19118                 if(oldWidget){
19119                         this._hideChild(oldWidget);
19120                 }
19121                 var d = this._showChild(newWidget);
19122
19123                 // Size the new widget, in case this is the first time it's being shown,
19124                 // or I have been resized since the last time it was shown.
19125                 // Note that page must be visible for resizing to work.
19126                 if(newWidget.resize){
19127                         if(this.doLayout){
19128                                 newWidget.resize(this._containerContentBox || this._contentBox);
19129                         }else{
19130                                 // the child should pick it's own size but we still need to call resize()
19131                                 // (with no arguments) to let the widget lay itself out
19132                                 newWidget.resize();
19133                         }
19134                 }
19135
19136                 return d;       // If child has an href, promise that fires when the child's href finishes loading
19137         },
19138
19139         _adjacent: function(/*Boolean*/ forward){
19140                 // summary:
19141                 //              Gets the next/previous child widget in this container from the current selection.
19142                 var children = this.getChildren();
19143                 var index = dojo.indexOf(children, this.selectedChildWidget);
19144                 index += forward ? 1 : children.length - 1;
19145                 return children[ index % children.length ]; // dijit._Widget
19146         },
19147
19148         forward: function(){
19149                 // summary:
19150                 //              Advance to next page.
19151                 return this.selectChild(this._adjacent(true), true);
19152         },
19153
19154         back: function(){
19155                 // summary:
19156                 //              Go back to previous page.
19157                 return this.selectChild(this._adjacent(false), true);
19158         },
19159
19160         _onKeyPress: function(e){
19161                 dojo.publish(this.id+"-containerKeyPress", [{ e: e, page: this}]);
19162         },
19163
19164         layout: function(){
19165                 // Implement _LayoutWidget.layout() virtual method.
19166                 if(this.doLayout && this.selectedChildWidget && this.selectedChildWidget.resize){
19167                         this.selectedChildWidget.resize(this._containerContentBox || this._contentBox);
19168                 }
19169         },
19170
19171         _showChild: function(/*dijit._Widget*/ page){
19172                 // summary:
19173                 //              Show the specified child by changing it's CSS, and call _onShow()/onShow() so
19174                 //              it can do any updates it needs regarding loading href's etc.
19175                 // returns:
19176                 //              Promise that fires when page has finished showing, or true if there's no href
19177                 var children = this.getChildren();
19178                 page.isFirstChild = (page == children[0]);
19179                 page.isLastChild = (page == children[children.length-1]);
19180                 page._set("selected", true);
19181
19182                 dojo.replaceClass(page.domNode, "dijitVisible", "dijitHidden");
19183
19184                 return page._onShow() || true;
19185         },
19186
19187         _hideChild: function(/*dijit._Widget*/ page){
19188                 // summary:
19189                 //              Hide the specified child by changing it's CSS, and call _onHide() so
19190                 //              it's notified.
19191                 page._set("selected", false);
19192                 dojo.replaceClass(page.domNode, "dijitHidden", "dijitVisible");
19193
19194                 page.onHide();
19195         },
19196
19197         closeChild: function(/*dijit._Widget*/ page){
19198                 // summary:
19199                 //              Callback when user clicks the [X] to remove a page.
19200                 //              If onClose() returns true then remove and destroy the child.
19201                 // tags:
19202                 //              private
19203                 var remove = page.onClose(this, page);
19204                 if(remove){
19205                         this.removeChild(page);
19206                         // makes sure we can clean up executeScripts in ContentPane onUnLoad
19207                         page.destroyRecursive();
19208                 }
19209         },
19210
19211         destroyDescendants: function(/*Boolean*/ preserveDom){
19212                 dojo.forEach(this.getChildren(), function(child){
19213                         this.removeChild(child);
19214                         child.destroyRecursive(preserveDom);
19215                 }, this);
19216         }
19217 });
19218
19219 // For back-compat, remove for 2.0
19220
19221
19222 // These arguments can be specified for the children of a StackContainer.
19223 // Since any widget can be specified as a StackContainer child, mix them
19224 // into the base widget class.  (This is a hack, but it's effective.)
19225 dojo.extend(dijit._Widget, {
19226         // selected: Boolean
19227         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
19228         //              Specifies that this widget should be the initially displayed pane.
19229         //              Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
19230         selected: false,
19231
19232         // closable: Boolean
19233         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
19234         //              True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
19235         closable: false,
19236
19237         // iconClass: String
19238         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
19239         //              CSS Class specifying icon to use in label associated with this pane.
19240         iconClass: "",
19241
19242         // showTitle: Boolean
19243         //              Parameter for children of `dijit.layout.StackContainer` or subclasses.
19244         //              When true, display title of this widget as tab label etc., rather than just using
19245         //              icon specified in iconClass
19246         showTitle: true
19247 });
19248
19249 }
19250
19251 if(!dojo._hasResource["dijit.layout.AccordionPane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19252 dojo._hasResource["dijit.layout.AccordionPane"] = true;
19253 dojo.provide("dijit.layout.AccordionPane");
19254
19255
19256
19257 dojo.declare("dijit.layout.AccordionPane", dijit.layout.ContentPane, {
19258         // summary:
19259         //              Deprecated widget.   Use `dijit.layout.ContentPane` instead.
19260         // tags:
19261         //              deprecated
19262
19263         constructor: function(){
19264                 dojo.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
19265         },
19266
19267         onSelected: function(){
19268                 // summary:
19269                 //              called when this pane is selected
19270         }
19271 });
19272
19273 }
19274
19275 if(!dojo._hasResource["dijit.layout.AccordionContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19276 dojo._hasResource["dijit.layout.AccordionContainer"] = true;
19277 dojo.provide("dijit.layout.AccordionContainer");
19278
19279
19280
19281
19282
19283
19284
19285
19286 //dojo.require("dijit.layout.AccordionPane ");  // for back compat, remove for 2.0
19287
19288 // Design notes:
19289 //
19290 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
19291 // is wrapped in a _AccordionInnerContainer.   This is hidden from the caller.
19292 //
19293 // The resulting markup will look like:
19294 //
19295 //      <div class=dijitAccordionContainer>
19296 //              <div class=dijitAccordionInnerContainer>        (one pane)
19297 //                              <div class=dijitAccordionTitle>         (title bar) ... </div>
19298 //                              <div class=dijtAccordionChildWrapper>   (content pane) </div>
19299 //              </div>
19300 //      </div>
19301 //
19302 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
19303 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
19304 // which on claro has a 1px border plus a 2px bottom margin.
19305 //
19306 // During animation there are two dijtAccordionChildWrapper's shown, so we need
19307 // to compensate for that.
19308
19309 dojo.declare(
19310         "dijit.layout.AccordionContainer",
19311         dijit.layout.StackContainer,
19312         {
19313                 // summary:
19314                 //              Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
19315                 //              and switching between panes is visualized by sliding the other panes up/down.
19316                 // example:
19317                 //      |       <div dojoType="dijit.layout.AccordionContainer">
19318                 //      |               <div dojoType="dijit.layout.ContentPane" title="pane 1">
19319                 //      |               </div>
19320                 //      |               <div dojoType="dijit.layout.ContentPane" title="pane 2">
19321                 //      |                       <p>This is some text</p>
19322                 //      |               </div>
19323                 //      |       </div>
19324
19325                 // duration: Integer
19326                 //              Amount of time (in ms) it takes to slide panes
19327                 duration: dijit.defaultDuration,
19328
19329                 // buttonWidget: [const] String
19330                 //              The name of the widget used to display the title of each pane
19331                 buttonWidget: "dijit.layout._AccordionButton",
19332
19333 /*=====
19334                 // _verticalSpace: Number
19335                 //              Pixels of space available for the open pane
19336                 //              (my content box size minus the cumulative size of all the title bars)
19337                 _verticalSpace: 0,
19338 =====*/
19339                 baseClass: "dijitAccordionContainer",
19340
19341                 buildRendering: function(){
19342                         this.inherited(arguments);
19343                         this.domNode.style.overflow = "hidden";         // TODO: put this in dijit.css
19344                         dijit.setWaiRole(this.domNode, "tablist");      // TODO: put this in template
19345                 },
19346
19347                 startup: function(){
19348                         if(this._started){ return; }
19349                         this.inherited(arguments);
19350                         if(this.selectedChildWidget){
19351                                 var style = this.selectedChildWidget.containerNode.style;
19352                                 style.display = "";
19353                                 style.overflow = "auto";
19354                                 this.selectedChildWidget._wrapperWidget.set("selected", true);
19355                         }
19356                 },
19357
19358                 layout: function(){
19359                         // Implement _LayoutWidget.layout() virtual method.
19360                         // Set the height of the open pane based on what room remains.
19361
19362                         var openPane = this.selectedChildWidget;
19363                         
19364                         if(!openPane){ return;}
19365
19366                         // space taken up by title, plus wrapper div (with border/margin) for open pane
19367                         var wrapperDomNode = openPane._wrapperWidget.domNode,
19368                                 wrapperDomNodeMargin = dojo._getMarginExtents(wrapperDomNode),
19369                                 wrapperDomNodePadBorder = dojo._getPadBorderExtents(wrapperDomNode),
19370                                 wrapperContainerNode = openPane._wrapperWidget.containerNode,
19371                                 wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode),
19372                                 wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
19373                                 mySize = this._contentBox;
19374
19375                         // get cumulative height of all the unselected title bars
19376                         var totalCollapsedHeight = 0;
19377                         dojo.forEach(this.getChildren(), function(child){
19378                     if(child != openPane){
19379                                         totalCollapsedHeight += dojo._getMarginSize(child._wrapperWidget.domNode).h;
19380                                 }
19381                         });
19382                         this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
19383                                 - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
19384                                 - openPane._buttonWidget.getTitleHeight();
19385
19386                         // Memo size to make displayed child
19387                         this._containerContentBox = {
19388                                 h: this._verticalSpace,
19389                                 w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
19390                                         - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
19391                         };
19392
19393                         if(openPane){
19394                                 openPane.resize(this._containerContentBox);
19395                         }
19396                 },
19397
19398                 _setupChild: function(child){
19399                         // Overrides _LayoutWidget._setupChild().
19400                         // Put wrapper widget around the child widget, showing title
19401
19402                         child._wrapperWidget = new dijit.layout._AccordionInnerContainer({
19403                                 contentWidget: child,
19404                                 buttonWidget: this.buttonWidget,
19405                                 id: child.id + "_wrapper",
19406                                 dir: child.dir,
19407                                 lang: child.lang,
19408                                 parent: this
19409                         });
19410
19411                         this.inherited(arguments);
19412                 },
19413
19414                 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
19415                         if(this._started){
19416                                 // Adding a child to a started Accordion is complicated because children have
19417                                 // wrapper widgets.  Default code path (calling this.inherited()) would add
19418                                 // the new child inside another child's wrapper.
19419
19420                                 // First add in child as a direct child of this AccordionContainer
19421                                 dojo.place(child.domNode, this.containerNode, insertIndex);
19422
19423                                 if(!child._started){
19424                                         child.startup();
19425                                 }
19426                                 
19427                                 // Then stick the wrapper widget around the child widget
19428                                 this._setupChild(child);
19429
19430                                 // Code below copied from StackContainer
19431                                 dojo.publish(this.id+"-addChild", [child, insertIndex]);
19432                                 this.layout();
19433                                 if(!this.selectedChildWidget){
19434                                         this.selectChild(child);
19435                                 }
19436                         }else{
19437                                 // We haven't been started yet so just add in the child widget directly,
19438                                 // and the wrapper will be created on startup()
19439                                 this.inherited(arguments);
19440                         }
19441                 },
19442
19443                 removeChild: function(child){
19444                         // Overrides _LayoutWidget.removeChild().
19445
19446                         // Destroy wrapper widget first, before StackContainer.getChildren() call.
19447                         // Replace wrapper widget with true child widget (ContentPane etc.).
19448                         // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
19449                         if(child._wrapperWidget){
19450                                 dojo.place(child.domNode, child._wrapperWidget.domNode, "after");
19451                                 child._wrapperWidget.destroy();
19452                                 delete child._wrapperWidget;
19453                         }
19454
19455                         dojo.removeClass(child.domNode, "dijitHidden");
19456
19457                         this.inherited(arguments);
19458                 },
19459
19460                 getChildren: function(){
19461                         // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
19462                         return dojo.map(this.inherited(arguments), function(child){
19463                                 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
19464                         }, this);
19465                 },
19466
19467                 destroy: function(){
19468                         if(this._animation){
19469                                 this._animation.stop();
19470                         }
19471                         dojo.forEach(this.getChildren(), function(child){
19472                                 // If AccordionContainer has been started, then each child has a wrapper widget which
19473                                 // also needs to be destroyed.
19474                                 if(child._wrapperWidget){
19475                                         child._wrapperWidget.destroy();
19476                                 }else{
19477                                         child.destroyRecursive();
19478                                 }
19479                         });
19480                         this.inherited(arguments);
19481                 },
19482
19483                 _showChild: function(child){
19484                         // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
19485                         child._wrapperWidget.containerNode.style.display="block";
19486                         return this.inherited(arguments);
19487                 },
19488
19489                 _hideChild: function(child){
19490                         // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
19491                         child._wrapperWidget.containerNode.style.display="none";
19492                         this.inherited(arguments);
19493                 },
19494
19495                 _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){
19496                         // Overrides StackContainer._transition() to provide sliding of title bars etc.
19497
19498                         if(dojo.isIE < 8){
19499                                 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
19500                                 animate = false;
19501                         }
19502
19503                         if(this._animation){
19504                                 // there's an in-progress animation.  speedily end it so we can do the newly requested one
19505                                 this._animation.stop(true);
19506                                 delete this._animation;
19507                         }
19508
19509                         var self = this;
19510
19511                         if(newWidget){
19512                                 newWidget._wrapperWidget.set("selected", true);
19513
19514                                 var d = this._showChild(newWidget);     // prepare widget to be slid in
19515
19516                                 // Size the new widget, in case this is the first time it's being shown,
19517                                 // or I have been resized since the last time it was shown.
19518                                 // Note that page must be visible for resizing to work.
19519                                 if(this.doLayout && newWidget.resize){
19520                                         newWidget.resize(this._containerContentBox);
19521                                 }
19522                         }
19523
19524                         if(oldWidget){
19525                                 oldWidget._wrapperWidget.set("selected", false);
19526                                 if(!animate){
19527                                         this._hideChild(oldWidget);
19528                                 }
19529                         }
19530
19531                         if(animate){
19532                                 var newContents = newWidget._wrapperWidget.containerNode,
19533                                         oldContents = oldWidget._wrapperWidget.containerNode;
19534
19535                                 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
19536                                 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
19537                                 // Have to compensate for that by immediately shrinking the pane being closed.
19538                                 var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
19539                                         wrapperContainerNodeMargin = dojo._getMarginExtents(wrapperContainerNode),
19540                                         wrapperContainerNodePadBorder = dojo._getPadBorderExtents(wrapperContainerNode),
19541                                         animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
19542
19543                                 oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
19544
19545                                 this._animation = new dojo.Animation({
19546                                         node: newContents,
19547                                         duration: this.duration,
19548                                         curve: [1, this._verticalSpace - animationHeightOverhead - 1],
19549                                         onAnimate: function(value){
19550                                                 value = Math.floor(value);      // avoid fractional values
19551                                                 newContents.style.height = value + "px";
19552                                                 oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
19553                                         },
19554                                         onEnd: function(){
19555                                                 delete self._animation;
19556                                                 newContents.style.height = "auto";
19557                                                 oldWidget._wrapperWidget.containerNode.style.display = "none";
19558                                                 oldContents.style.height = "auto";
19559                                                 self._hideChild(oldWidget);
19560                                         }
19561                                 });
19562                                 this._animation.onStop = this._animation.onEnd;
19563                                 this._animation.play();
19564                         }
19565
19566                         return d;       // If child has an href, promise that fires when the widget has finished loading
19567                 },
19568
19569                 // note: we are treating the container as controller here
19570                 _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
19571                         // summary:
19572                         //              Handle keypress events
19573                         // description:
19574                         //              This is called from a handler on AccordionContainer.domNode
19575                         //              (setup in StackContainer), and is also called directly from
19576                         //              the click handler for accordion labels
19577                         if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
19578                                 return;
19579                         }
19580                         var k = dojo.keys,
19581                                 c = e.charOrCode;
19582                         if((fromTitle && (c == k.LEFT_ARROW || c == k.UP_ARROW)) ||
19583                                         (e.ctrlKey && c == k.PAGE_UP)){
19584                                 this._adjacent(false)._buttonWidget._onTitleClick();
19585                                 dojo.stopEvent(e);
19586                         }else if((fromTitle && (c == k.RIGHT_ARROW || c == k.DOWN_ARROW)) ||
19587                                         (e.ctrlKey && (c == k.PAGE_DOWN || c == k.TAB))){
19588                                 this._adjacent(true)._buttonWidget._onTitleClick();
19589                                 dojo.stopEvent(e);
19590                         }
19591                 }
19592         }
19593 );
19594
19595 dojo.declare("dijit.layout._AccordionInnerContainer",
19596         [dijit._Widget, dijit._CssStateMixin], {
19597                 // summary:
19598                 //              Internal widget placed as direct child of AccordionContainer.containerNode.
19599                 //              When other widgets are added as children to an AccordionContainer they are wrapped in
19600                 //              this widget.
19601                 
19602 /*=====
19603                 // buttonWidget: String
19604                 //              Name of class to use to instantiate title
19605                 //              (Wish we didn't have a separate widget for just the title but maintaining it
19606                 //              for backwards compatibility, is it worth it?)
19607                  buttonWidget: null,
19608 =====*/
19609
19610 /*=====
19611                 // contentWidget: dijit._Widget
19612                 //              Pointer to the real child widget
19613                 contentWidget: null,
19614 =====*/
19615
19616                 baseClass: "dijitAccordionInnerContainer",
19617
19618                 // tell nested layout widget that we will take care of sizing
19619                 isContainer: true,
19620                 isLayoutContainer: true,
19621
19622                 buildRendering: function(){
19623                         // Builds a template like:
19624                         //      <div class=dijitAccordionInnerContainer>
19625                         //              Button
19626                         //              <div class=dijitAccordionChildWrapper>
19627                         //                      ContentPane
19628                         //              </div>
19629                         //      </div>
19630
19631                         // Create wrapper div, placed where the child is now
19632                         this.domNode = dojo.place("<div class='" + this.baseClass + "'>", this.contentWidget.domNode, "after");
19633                         
19634                         // wrapper div's first child is the button widget (ie, the title bar)
19635                         var child = this.contentWidget,
19636                                 cls = dojo.getObject(this.buttonWidget);
19637                         this.button = child._buttonWidget = (new cls({
19638                                 contentWidget: child,
19639                                 label: child.title,
19640                                 title: child.tooltip,
19641                                 dir: child.dir,
19642                                 lang: child.lang,
19643                                 iconClass: child.iconClass,
19644                                 id: child.id + "_button",
19645                                 parent: this.parent
19646                         })).placeAt(this.domNode);
19647                         
19648                         // and then the actual content widget (changing it from prior-sibling to last-child),
19649                         // wrapped by a <div class=dijitAccordionChildWrapper>
19650                         this.containerNode = dojo.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
19651                         dojo.place(this.contentWidget.domNode, this.containerNode);
19652                 },
19653
19654                 postCreate: function(){
19655                         this.inherited(arguments);
19656
19657                         // Map changes in content widget's title etc. to changes in the button
19658                         var button = this.button;
19659                         this._contentWidgetWatches = [
19660                                 this.contentWidget.watch('title', dojo.hitch(this, function(name, oldValue, newValue){
19661                                         button.set("label", newValue);
19662                                 })),
19663                                 this.contentWidget.watch('tooltip', dojo.hitch(this, function(name, oldValue, newValue){
19664                                         button.set("title", newValue);
19665                                 })),
19666                                 this.contentWidget.watch('iconClass', dojo.hitch(this, function(name, oldValue, newValue){
19667                                         button.set("iconClass", newValue);
19668                                 }))
19669                         ];
19670                 },
19671
19672                 _setSelectedAttr: function(/*Boolean*/ isSelected){
19673                         this._set("selected", isSelected);
19674                         this.button.set("selected", isSelected);
19675                         if(isSelected){
19676                                 var cw = this.contentWidget;
19677                                 if(cw.onSelected){ cw.onSelected(); }
19678                         }
19679                 },
19680
19681                 startup: function(){
19682                         // Called by _Container.addChild()
19683                         this.contentWidget.startup();
19684                 },
19685
19686                 destroy: function(){
19687                         this.button.destroyRecursive();
19688
19689                         dojo.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
19690
19691                         delete this.contentWidget._buttonWidget;
19692                         delete this.contentWidget._wrapperWidget;
19693
19694                         this.inherited(arguments);
19695                 },
19696                 
19697                 destroyDescendants: function(){
19698                         // since getChildren isn't working for me, have to code this manually
19699                         this.contentWidget.destroyRecursive();
19700                 }
19701 });
19702
19703 dojo.declare("dijit.layout._AccordionButton",
19704         [dijit._Widget, dijit._Templated, dijit._CssStateMixin],
19705         {
19706         // summary:
19707         //              The title bar to click to open up an accordion pane.
19708         //              Internal widget used by AccordionContainer.
19709         // tags:
19710         //              private
19711
19712         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' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" dojoAttachPoint='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" dojoAttachPoint='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"),
19713         attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
19714                 label: {node: "titleTextNode", type: "innerHTML" },
19715                 title: {node: "titleTextNode", type: "attribute", attribute: "title"},
19716                 iconClass: { node: "iconNode", type: "class" }
19717         }),
19718
19719         baseClass: "dijitAccordionTitle",
19720
19721         getParent: function(){
19722                 // summary:
19723                 //              Returns the AccordionContainer parent.
19724                 // tags:
19725                 //              private
19726                 return this.parent;
19727         },
19728
19729         buildRendering: function(){
19730                 this.inherited(arguments);
19731                 var titleTextNodeId = this.id.replace(' ','_');
19732                 dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
19733                 dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
19734                 dojo.setSelectable(this.domNode, false);
19735         },
19736
19737         getTitleHeight: function(){
19738                 // summary:
19739                 //              Returns the height of the title dom node.
19740                 return dojo._getMarginSize(this.domNode).h;     // Integer
19741         },
19742
19743         // TODO: maybe the parent should set these methods directly rather than forcing the code
19744         // into the button widget?
19745         _onTitleClick: function(){
19746                 // summary:
19747                 //              Callback when someone clicks my title.
19748                 var parent = this.getParent();
19749                         parent.selectChild(this.contentWidget, true);
19750                         dijit.focus(this.focusNode);
19751         },
19752
19753         _onTitleKeyPress: function(/*Event*/ evt){
19754                 return this.getParent()._onKeyPress(evt, this.contentWidget);
19755         },
19756
19757         _setSelectedAttr: function(/*Boolean*/ isSelected){
19758                 this._set("selected", isSelected);
19759                 dijit.setWaiState(this.focusNode, "expanded", isSelected);
19760                 dijit.setWaiState(this.focusNode, "selected", isSelected);
19761                 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
19762         }
19763 });
19764
19765 }
19766
19767 if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
19768 dojo._hasResource["dijit.layout.BorderContainer"] = true;
19769 dojo.provide("dijit.layout.BorderContainer");
19770
19771
19772
19773
19774
19775 dojo.declare(
19776         "dijit.layout.BorderContainer",
19777         dijit.layout._LayoutWidget,
19778 {
19779         // summary:
19780         //              Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
19781         //
19782         // description:
19783         //              A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
19784         //              that contains a child widget marked region="center" and optionally children widgets marked
19785         //              region equal to "top", "bottom", "leading", "trailing", "left" or "right".
19786         //              Children along the edges will be laid out according to width or height dimensions and may
19787         //              include optional splitters (splitter="true") to make them resizable by the user.  The remaining
19788         //              space is designated for the center region.
19789         //
19790         //              The outer size must be specified on the BorderContainer node.  Width must be specified for the sides
19791         //              and height for the top and bottom, respectively.  No dimensions should be specified on the center;
19792         //              it will fill the remaining space.  Regions named "leading" and "trailing" may be used just like
19793         //              "left" and "right" except that they will be reversed in right-to-left environments.
19794         //
19795         //              For complex layouts, multiple children can be specified for a single region.   In this case, the
19796         //              layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
19797         //              and which child is closer to the center (high layoutPriority).   layoutPriority can also be used
19798         //              instead of the design attribute to conrol layout precedence of horizontal vs. vertical panes.
19799         // example:
19800         // |    <div dojoType="dijit.layout.BorderContainer" design="sidebar" gutters="false"
19801         // |            style="width: 400px; height: 300px;">
19802         // |            <div dojoType="dijit.layout.ContentPane" region="top">header text</div>
19803         // |            <div dojoType="dijit.layout.ContentPane" region="right" splitter="true" style="width: 200px;">table of contents</div>
19804         // |            <div dojoType="dijit.layout.ContentPane" region="center">client area</div>
19805         // |    </div>
19806
19807         // design: String
19808         //              Which design is used for the layout:
19809         //                      - "headline" (default) where the top and bottom extend
19810         //                              the full width of the container
19811         //                      - "sidebar" where the left and right sides extend from top to bottom.
19812         design: "headline",
19813
19814         // gutters: [const] Boolean
19815         //              Give each pane a border and margin.
19816         //              Margin determined by domNode.paddingLeft.
19817         //              When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
19818         gutters: true,
19819
19820         // liveSplitters: [const] Boolean
19821         //              Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
19822         liveSplitters: true,
19823
19824         // persist: Boolean
19825         //              Save splitter positions in a cookie.
19826         persist: false,
19827
19828         baseClass: "dijitBorderContainer",
19829
19830         // _splitterClass: String
19831         //              Optional hook to override the default Splitter widget used by BorderContainer
19832         _splitterClass: "dijit.layout._Splitter",
19833
19834         postMixInProperties: function(){
19835                 // change class name to indicate that BorderContainer is being used purely for
19836                 // layout (like LayoutContainer) rather than for pretty formatting.
19837                 if(!this.gutters){
19838                         this.baseClass += "NoGutter";
19839                 }
19840                 this.inherited(arguments);
19841         },
19842
19843         startup: function(){
19844                 if(this._started){ return; }
19845                 dojo.forEach(this.getChildren(), this._setupChild, this);
19846                 this.inherited(arguments);
19847         },
19848
19849         _setupChild: function(/*dijit._Widget*/ child){
19850                 // Override _LayoutWidget._setupChild().
19851
19852                 var region = child.region;
19853                 if(region){
19854                         this.inherited(arguments);
19855
19856                         dojo.addClass(child.domNode, this.baseClass+"Pane");
19857
19858                         var ltr = this.isLeftToRight();
19859                         if(region == "leading"){ region = ltr ? "left" : "right"; }
19860                         if(region == "trailing"){ region = ltr ? "right" : "left"; }
19861
19862                         // Create draggable splitter for resizing pane,
19863                         // or alternately if splitter=false but BorderContainer.gutters=true then
19864                         // insert dummy div just for spacing
19865                         if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
19866                                 var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
19867                                 var splitter = new _Splitter({
19868                                         id: child.id + "_splitter",
19869                                         container: this,
19870                                         child: child,
19871                                         region: region,
19872                                         live: this.liveSplitters
19873                                 });
19874                                 splitter.isSplitter = true;
19875                                 child._splitterWidget = splitter;
19876
19877                                 dojo.place(splitter.domNode, child.domNode, "after");
19878
19879                                 // Splitters aren't added as Contained children, so we need to call startup explicitly
19880                                 splitter.startup();
19881                         }
19882                         child.region = region;  // TODO: technically wrong since it overwrites "trailing" with "left" etc.
19883                 }
19884         },
19885
19886         layout: function(){
19887                 // Implement _LayoutWidget.layout() virtual method.
19888                 this._layoutChildren();
19889         },
19890
19891         addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
19892                 // Override _LayoutWidget.addChild().
19893                 this.inherited(arguments);
19894                 if(this._started){
19895                         this.layout(); //OPT
19896                 }
19897         },
19898
19899         removeChild: function(/*dijit._Widget*/ child){
19900                 // Override _LayoutWidget.removeChild().
19901
19902                 var region = child.region;
19903                 var splitter = child._splitterWidget
19904                 if(splitter){
19905                         splitter.destroy();
19906                         delete child._splitterWidget;
19907                 }
19908                 this.inherited(arguments);
19909                 
19910                 if(this._started){
19911                         this._layoutChildren();
19912                 }
19913                 // Clean up whatever style changes we made to the child pane.
19914                 // Unclear how height and width should be handled.
19915                 dojo.removeClass(child.domNode, this.baseClass+"Pane");
19916                 dojo.style(child.domNode, {
19917                         top: "auto",
19918                         bottom: "auto",
19919                         left: "auto",
19920                         right: "auto",
19921                         position: "static"
19922                 });
19923                 dojo.style(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
19924         },
19925
19926         getChildren: function(){
19927                 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
19928                 return dojo.filter(this.inherited(arguments), function(widget){
19929                         return !widget.isSplitter;
19930                 });
19931         },
19932
19933         // TODO: remove in 2.0
19934         getSplitter: function(/*String*/region){
19935                 // summary:
19936                 //              Returns the widget responsible for rendering the splitter associated with region
19937                 // tags:
19938                 //              deprecated
19939                 return dojo.filter(this.getChildren(), function(child){
19940                         return child.region == region;
19941                 })[0]._splitterWidget;
19942         },
19943
19944         resize: function(newSize, currentSize){
19945                 // Overrides _LayoutWidget.resize().
19946
19947                 // resetting potential padding to 0px to provide support for 100% width/height + padding
19948                 // TODO: this hack doesn't respect the box model and is a temporary fix
19949                 if(!this.cs || !this.pe){
19950                         var node = this.domNode;
19951                         this.cs = dojo.getComputedStyle(node);
19952                         this.pe = dojo._getPadExtents(node, this.cs);
19953                         this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
19954                         this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
19955
19956                         dojo.style(node, "padding", "0px");
19957                 }
19958
19959                 this.inherited(arguments);
19960         },
19961
19962         _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
19963                 // summary:
19964                 //              This is the main routine for setting size/position of each child.
19965                 // description:
19966                 //              With no arguments, measures the height of top/bottom panes, the width
19967                 //              of left/right panes, and then sizes all panes accordingly.
19968                 //
19969                 //              With changedRegion specified (as "left", "top", "bottom", or "right"),
19970                 //              it changes that region's width/height to changedRegionSize and
19971                 //              then resizes other regions that were affected.
19972                 // changedChildId:
19973                 //              Id of the child which should be resized because splitter was dragged.
19974                 // changedChildSize:
19975                 //              The new width/height (in pixels) to make specified child
19976
19977                 if(!this._borderBox || !this._borderBox.h){
19978                         // We are currently hidden, or we haven't been sized by our parent yet.
19979                         // Abort.   Someone will resize us later.
19980                         return;
19981                 }
19982
19983                 // Generate list of wrappers of my children in the order that I want layoutChildren()
19984                 // to process them (i.e. from the outside to the inside)
19985                 var wrappers = dojo.map(this.getChildren(), function(child, idx){
19986                         return {
19987                                 pane: child,
19988                                 weight: [
19989                                         child.region == "center" ? Infinity : 0,
19990                                         child.layoutPriority,
19991                                         (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
19992                                         idx
19993                                 ]
19994                         };
19995                 }, this);
19996                 wrappers.sort(function(a, b){
19997                         var aw = a.weight, bw = b.weight;
19998                         for(var i=0; i<aw.length; i++){
19999                                 if(aw[i] != bw[i]){
20000                                         return aw[i] - bw[i];
20001                                 }
20002                         }
20003                         return 0;
20004                 });
20005
20006                 // Make new list, combining the externally specified children with splitters and gutters
20007                 var childrenAndSplitters = [];
20008                 dojo.forEach(wrappers, function(wrapper){
20009                         var pane = wrapper.pane;
20010                         childrenAndSplitters.push(pane);
20011                         if(pane._splitterWidget){
20012                                 childrenAndSplitters.push(pane._splitterWidget);
20013                         }
20014                 });
20015
20016                 // Compute the box in which to lay out my children
20017                 var dim = {
20018                         l: this.pe.l,
20019                         t: this.pe.t,
20020                         w: this._borderBox.w - this.pe.w,
20021                         h: this._borderBox.h - this.pe.h
20022                 };
20023
20024                 // Layout the children, possibly changing size due to a splitter drag
20025                 dijit.layout.layoutChildren(this.domNode, dim, childrenAndSplitters,
20026                         changedChildId, changedChildSize);
20027         },
20028
20029         destroyRecursive: function(){
20030                 // Destroy splitters first, while getChildren() still works
20031                 dojo.forEach(this.getChildren(), function(child){
20032                         var splitter = child._splitterWidget;
20033                         if(splitter){
20034                                 splitter.destroy();
20035                         }
20036                         delete child._splitterWidget;
20037                 });
20038
20039                 // Then destroy the real children, and myself
20040                 this.inherited(arguments);
20041         }
20042 });
20043
20044 // This argument can be specified for the children of a BorderContainer.
20045 // Since any widget can be specified as a LayoutContainer child, mix it
20046 // into the base widget class.  (This is a hack, but it's effective.)
20047 dojo.extend(dijit._Widget, {
20048         // region: [const] String
20049         //              Parameter for children of `dijit.layout.BorderContainer`.
20050         //              Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
20051         //              See the `dijit.layout.BorderContainer` description for details.
20052         region: '',
20053
20054         // layoutPriority: [const] Number
20055         //              Parameter for children of `dijit.layout.BorderContainer`.
20056         //              Children with a higher layoutPriority will be placed closer to the BorderContainer center,
20057         //              between children with a lower layoutPriority.
20058         layoutPriority: 0,
20059
20060         // splitter: [const] Boolean
20061         //              Parameter for child of `dijit.layout.BorderContainer` where region != "center".
20062         //              If true, enables user to resize the widget by putting a draggable splitter between
20063         //              this widget and the region=center widget.
20064         splitter: false,
20065
20066         // minSize: [const] Number
20067         //              Parameter for children of `dijit.layout.BorderContainer`.
20068         //              Specifies a minimum size (in pixels) for this widget when resized by a splitter.
20069         minSize: 0,
20070
20071         // maxSize: [const] Number
20072         //              Parameter for children of `dijit.layout.BorderContainer`.
20073         //              Specifies a maximum size (in pixels) for this widget when resized by a splitter.
20074         maxSize: Infinity
20075 });
20076
20077 dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
20078 {
20079         // summary:
20080         //              A draggable spacer between two items in a `dijit.layout.BorderContainer`.
20081         // description:
20082         //              This is instantiated by `dijit.layout.BorderContainer`.  Users should not
20083         //              create it directly.
20084         // tags:
20085         //              private
20086
20087 /*=====
20088         // container: [const] dijit.layout.BorderContainer
20089         //              Pointer to the parent BorderContainer
20090         container: null,
20091
20092         // child: [const] dijit.layout._LayoutWidget
20093         //              Pointer to the pane associated with this splitter
20094         child: null,
20095
20096         // region: [const] String
20097         //              Region of pane associated with this splitter.
20098         //              "top", "bottom", "left", "right".
20099         region: null,
20100 =====*/
20101
20102         // live: [const] Boolean
20103         //              If true, the child's size changes and the child widget is redrawn as you drag the splitter;
20104         //              otherwise, the size doesn't change until you drop the splitter (by mouse-up)
20105         live: true,
20106
20107         templateString: '<div class="dijitSplitter" dojoAttachEvent="onkeypress:_onKeyPress,onmousedown:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
20108
20109         postMixInProperties: function(){
20110                 this.inherited(arguments);
20111
20112                 this.horizontal = /top|bottom/.test(this.region);
20113                 this._factor = /top|left/.test(this.region) ? 1 : -1;
20114                 this._cookieName = this.container.id + "_" + this.region;
20115         },
20116
20117         buildRendering: function(){
20118                 this.inherited(arguments);
20119
20120                 dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
20121
20122                 if(this.container.persist){
20123                         // restore old size
20124                         var persistSize = dojo.cookie(this._cookieName);
20125                         if(persistSize){
20126                                 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
20127                         }
20128                 }
20129         },
20130
20131         _computeMaxSize: function(){
20132                 // summary:
20133                 //              Return the maximum size that my corresponding pane can be set to
20134
20135                 var dim = this.horizontal ? 'h' : 'w',
20136                         childSize = dojo.marginBox(this.child.domNode)[dim],
20137                         center = dojo.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
20138                         spaceAvailable = dojo.marginBox(center.domNode)[dim];   // can expand until center is crushed to 0
20139
20140                 return Math.min(this.child.maxSize, childSize + spaceAvailable);
20141         },
20142
20143         _startDrag: function(e){
20144                 if(!this.cover){
20145                         this.cover = dojo.doc.createElement('div');
20146                         dojo.addClass(this.cover, "dijitSplitterCover");
20147                         dojo.place(this.cover, this.child.domNode, "after");
20148                 }
20149                 dojo.addClass(this.cover, "dijitSplitterCoverActive");
20150
20151                 // Safeguard in case the stop event was missed.  Shouldn't be necessary if we always get the mouse up.
20152                 if(this.fake){ dojo.destroy(this.fake); }
20153                 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
20154                         // create fake splitter to display at old position while we drag
20155                         (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
20156                         dojo.addClass(this.domNode, "dijitSplitterShadow");
20157                         dojo.place(this.fake, this.domNode, "after");
20158                 }
20159                 dojo.addClass(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
20160                 if(this.fake){
20161                         dojo.removeClass(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
20162                 }
20163
20164                 //Performance: load data info local vars for onmousevent function closure
20165                 var factor = this._factor,
20166                         isHorizontal = this.horizontal,
20167                         axis = isHorizontal ? "pageY" : "pageX",
20168                         pageStart = e[axis],
20169                         splitterStyle = this.domNode.style,
20170                         dim = isHorizontal ? 'h' : 'w',
20171                         childStart = dojo.marginBox(this.child.domNode)[dim],
20172                         max = this._computeMaxSize(),
20173                         min = this.child.minSize || 20,
20174                         region = this.region,
20175                         splitterAttr = region == "top" || region == "bottom" ? "top" : "left",  // style attribute of splitter to adjust
20176                         splitterStart = parseInt(splitterStyle[splitterAttr], 10),
20177                         resize = this._resize,
20178                         layoutFunc = dojo.hitch(this.container, "_layoutChildren", this.child.id),
20179                         de = dojo.doc;
20180
20181                 this._handlers = (this._handlers || []).concat([
20182                         dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
20183                                 var delta = e[axis] - pageStart,
20184                                         childSize = factor * delta + childStart,
20185                                         boundChildSize = Math.max(Math.min(childSize, max), min);
20186
20187                                 if(resize || forceResize){
20188                                         layoutFunc(boundChildSize);
20189                                 }
20190                                 // TODO: setting style directly (usually) sets content box size, need to set margin box size
20191                                 splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
20192                         }),
20193                         dojo.connect(de, "ondragstart", dojo.stopEvent),
20194                         dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
20195                         dojo.connect(de, "onmouseup", this, "_stopDrag")
20196                 ]);
20197                 dojo.stopEvent(e);
20198         },
20199
20200         _onMouse: function(e){
20201                 var o = (e.type == "mouseover" || e.type == "mouseenter");
20202                 dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
20203                 dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
20204         },
20205
20206         _stopDrag: function(e){
20207                 try{
20208                         if(this.cover){
20209                                 dojo.removeClass(this.cover, "dijitSplitterCoverActive");
20210                         }
20211                         if(this.fake){ dojo.destroy(this.fake); }
20212                         dojo.removeClass(this.domNode, "dijitSplitterActive dijitSplitter"
20213                                 + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
20214                         this._drag(e); //TODO: redundant with onmousemove?
20215                         this._drag(e, true);
20216                 }finally{
20217                         this._cleanupHandlers();
20218                         delete this._drag;
20219                 }
20220
20221                 if(this.container.persist){
20222                         dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
20223                 }
20224         },
20225
20226         _cleanupHandlers: function(){
20227                 dojo.forEach(this._handlers, dojo.disconnect);
20228                 delete this._handlers;
20229         },
20230
20231         _onKeyPress: function(/*Event*/ e){
20232                 // should we apply typematic to this?
20233                 this._resize = true;
20234                 var horizontal = this.horizontal;
20235                 var tick = 1;
20236                 var dk = dojo.keys;
20237                 switch(e.charOrCode){
20238                         case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
20239                                 tick *= -1;
20240 //                              break;
20241                         case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
20242                                 break;
20243                         default:
20244 //                              this.inherited(arguments);
20245                                 return;
20246                 }
20247                 var childSize = dojo._getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
20248                 this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
20249                 dojo.stopEvent(e);
20250         },
20251
20252         destroy: function(){
20253                 this._cleanupHandlers();
20254                 delete this.child;
20255                 delete this.container;
20256                 delete this.cover;
20257                 delete this.fake;
20258                 this.inherited(arguments);
20259         }
20260 });
20261
20262 dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated],
20263 {
20264         // summary:
20265         //              Just a spacer div to separate side pane from center pane.
20266         //              Basically a trick to lookup the gutter/splitter width from the theme.
20267         // description:
20268         //              Instantiated by `dijit.layout.BorderContainer`.  Users should not
20269         //              create directly.
20270         // tags:
20271         //              private
20272
20273         templateString: '<div class="dijitGutter" role="presentation"></div>',
20274
20275         postMixInProperties: function(){
20276                 this.inherited(arguments);
20277                 this.horizontal = /top|bottom/.test(this.region);
20278         },
20279
20280         buildRendering: function(){
20281                 this.inherited(arguments);
20282                 dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
20283         }
20284 });
20285
20286 }
20287
20288 if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20289 dojo._hasResource["dijit.layout._TabContainerBase"] = true;
20290 dojo.provide("dijit.layout._TabContainerBase");
20291
20292
20293
20294
20295 dojo.declare("dijit.layout._TabContainerBase",
20296         [dijit.layout.StackContainer, dijit._Templated],
20297         {
20298         // summary:
20299         //              Abstract base class for TabContainer.   Must define _makeController() to instantiate
20300         //              and return the widget that displays the tab labels
20301         // description:
20302         //              A TabContainer is a container that has multiple panes, but shows only
20303         //              one pane at a time.  There are a set of tabs corresponding to each pane,
20304         //              where each tab has the name (aka title) of the pane, and optionally a close button.
20305
20306         // tabPosition: String
20307         //              Defines where tabs go relative to tab content.
20308         //              "top", "bottom", "left-h", "right-h"
20309         tabPosition: "top",
20310
20311         baseClass: "dijitTabContainer",
20312
20313         // tabStrip: [const] Boolean
20314         //              Defines whether the tablist gets an extra class for layouting, putting a border/shading
20315         //              around the set of tabs.   Not supported by claro theme.
20316         tabStrip: false,
20317
20318         // nested: [const] Boolean
20319         //              If true, use styling for a TabContainer nested inside another TabContainer.
20320         //              For tundra etc., makes tabs look like links, and hides the outer
20321         //              border since the outer TabContainer already has a border.
20322         nested: false,
20323
20324         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"),
20325
20326         postMixInProperties: function(){
20327                 // set class name according to tab position, ex: dijitTabContainerTop
20328                 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
20329
20330                 this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
20331
20332                 this.inherited(arguments);
20333         },
20334
20335         buildRendering: function(){
20336                 this.inherited(arguments);
20337
20338                 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
20339                 this.tablist = this._makeController(this.tablistNode);
20340
20341                 if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
20342
20343                 if(this.nested){
20344                         /* workaround IE's lack of support for "a > b" selectors by
20345                          * tagging each node in the template.
20346                          */
20347                         dojo.addClass(this.domNode, "dijitTabContainerNested");
20348                         dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
20349                         dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
20350                         dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
20351                 }else{
20352                         dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
20353                 }
20354         },
20355
20356         _setupChild: function(/*dijit._Widget*/ tab){
20357                 // Overrides StackContainer._setupChild().
20358                 dojo.addClass(tab.domNode, "dijitTabPane");
20359                 this.inherited(arguments);
20360         },
20361
20362         startup: function(){
20363                 if(this._started){ return; }
20364
20365                 // wire up the tablist and its tabs
20366                 this.tablist.startup();
20367
20368                 this.inherited(arguments);
20369         },
20370
20371         layout: function(){
20372                 // Overrides StackContainer.layout().
20373                 // Configure the content pane to take up all the space except for where the tabs are
20374
20375                 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
20376
20377                 var sc = this.selectedChildWidget;
20378
20379                 if(this.doLayout){
20380                         // position and size the titles and the container node
20381                         var titleAlign = this.tabPosition.replace(/-h/, "");
20382                         this.tablist.layoutAlign = titleAlign;
20383                         var children = [this.tablist, {
20384                                 domNode: this.tablistSpacer,
20385                                 layoutAlign: titleAlign
20386                         }, {
20387                                 domNode: this.containerNode,
20388                                 layoutAlign: "client"
20389                         }];
20390                         dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
20391
20392                         // Compute size to make each of my children.
20393                         // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
20394                         this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
20395
20396                         if(sc && sc.resize){
20397                                 sc.resize(this._containerContentBox);
20398                         }
20399                 }else{
20400                         // just layout the tab controller, so it can position left/right buttons etc.
20401                         if(this.tablist.resize){
20402                                 //make the tabs zero width so that they don't interfere with width calc, then reset
20403                                 var s = this.tablist.domNode.style;
20404                                 s.width="0";
20405                                 var width = dojo.contentBox(this.domNode).w;
20406                                 s.width="";
20407                                 this.tablist.resize({w: width});
20408                         }
20409
20410                         // and call resize() on the selected pane just to tell it that it's been made visible
20411                         if(sc && sc.resize){
20412                                 sc.resize();
20413                         }
20414                 }
20415         },
20416
20417         destroy: function(){
20418                 if(this.tablist){
20419                         this.tablist.destroy();
20420                 }
20421                 this.inherited(arguments);
20422         }
20423 });
20424
20425 }
20426
20427 if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20428 dojo._hasResource["dijit.layout.TabController"] = true;
20429 dojo.provide("dijit.layout.TabController");
20430
20431
20432
20433
20434
20435
20436 // Menu is used for an accessible close button, would be nice to have a lighter-weight solution
20437
20438
20439 dojo.declare("dijit.layout.TabController",
20440         dijit.layout.StackController,
20441 {
20442         // summary:
20443         //              Set of tabs (the things with titles and a close button, that you click to show a tab panel).
20444         //              Used internally by `dijit.layout.TabContainer`.
20445         // description:
20446         //              Lets the user select the currently shown pane in a TabContainer or StackContainer.
20447         //              TabController also monitors the TabContainer, and whenever a pane is
20448         //              added or deleted updates itself accordingly.
20449         // tags:
20450         //              private
20451
20452         templateString: "<div role='tablist' dojoAttachEvent='onkeypress:onkeypress'></div>",
20453
20454         // tabPosition: String
20455         //              Defines where tabs go relative to the content.
20456         //              "top", "bottom", "left-h", "right-h"
20457         tabPosition: "top",
20458
20459         // buttonWidget: String
20460         //              The name of the tab widget to create to correspond to each page
20461         buttonWidget: "dijit.layout._TabButton",
20462
20463         _rectifyRtlTabList: function(){
20464                 // summary:
20465                 //              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
20466
20467                 if(0 >= this.tabPosition.indexOf('-h')){ return; }
20468                 if(!this.pane2button){ return; }
20469
20470                 var maxWidth = 0;
20471                 for(var pane in this.pane2button){
20472                         var ow = this.pane2button[pane].innerDiv.scrollWidth;
20473                         maxWidth = Math.max(maxWidth, ow);
20474                 }
20475                 //unify the length of all the tabs
20476                 for(pane in this.pane2button){
20477                         this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
20478                 }
20479         }
20480 });
20481
20482 dojo.declare("dijit.layout._TabButton",
20483         dijit.layout._StackButton,
20484         {
20485         // summary:
20486         //              A tab (the thing you click to select a pane).
20487         // description:
20488         //              Contains the title of the pane, and optionally a close-button to destroy the pane.
20489         //              This is an internal widget and should not be instantiated directly.
20490         // tags:
20491         //              private
20492
20493         // baseClass: String
20494         //              The CSS class applied to the domNode.
20495         baseClass: "dijitTab",
20496
20497         // Apply dijitTabCloseButtonHover when close button is hovered
20498         cssStateNodes: {
20499                 closeNode: "dijitTabCloseButton"
20500         },
20501
20502         templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "<div role=\"presentation\" dojoAttachPoint=\"titleNode\" dojoAttachEvent='onclick:onClick'>\n    <div role=\"presentation\" class='dijitTabInnerDiv' dojoAttachPoint='innerDiv'>\n        <div role=\"presentation\" class='dijitTabContent' dojoAttachPoint='tabContent'>\n        \t<div role=\"presentation\" dojoAttachPoint='focusNode'>\n\t\t        <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" 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' role=\"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"),
20503
20504         // Override _FormWidget.scrollOnFocus.
20505         // Don't scroll the whole tab container into view when the button is focused.
20506         scrollOnFocus: false,
20507
20508         buildRendering: function(){
20509                 this.inherited(arguments);
20510
20511                 dojo.setSelectable(this.containerNode, false);
20512         },
20513
20514         startup: function(){
20515                 this.inherited(arguments);
20516                 var n = this.domNode;
20517
20518                 // Required to give IE6 a kick, as it initially hides the
20519                 // tabs until they are focused on.
20520                 setTimeout(function(){
20521                         n.className = n.className;
20522                 }, 1);
20523         },
20524
20525         _setCloseButtonAttr: function(/*Boolean*/ disp){
20526                 // summary:
20527                 //              Hide/show close button
20528                 this._set("closeButton", disp);
20529                 dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
20530                 this.closeNode.style.display = disp ? "" : "none";
20531                 if(disp){
20532                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
20533                         if(this.closeNode){
20534                                 dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
20535                         }
20536                         // add context menu onto title button
20537                         var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
20538                         this._closeMenu = new dijit.Menu({
20539                                 id: this.id+"_Menu",
20540                                 dir: this.dir,
20541                                 lang: this.lang,
20542                                 targetNodeIds: [this.domNode]
20543                         });
20544
20545                         this._closeMenu.addChild(new dijit.MenuItem({
20546                                 label: _nlsResources.itemClose,
20547                                 dir: this.dir,
20548                                 lang: this.lang,
20549                                 onClick: dojo.hitch(this, "onClickCloseButton")
20550                         }));
20551                 }else{
20552                         if(this._closeMenu){
20553                                 this._closeMenu.destroyRecursive();
20554                                 delete this._closeMenu;
20555                         }
20556                 }
20557         },
20558         _setLabelAttr: function(/*String*/ content){
20559                 // summary:
20560                 //              Hook for set('label', ...) to work.
20561                 // description:
20562                 //              takes an HTML string.
20563                 //              Inherited ToggleButton implementation will Set the label (text) of the button;
20564                 //              Need to set the alt attribute of icon on tab buttons if no label displayed
20565                 this.inherited(arguments);
20566                 if(this.showLabel == false && !this.params.title){
20567                         this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
20568                 }
20569         },
20570
20571         destroy: function(){
20572                 if(this._closeMenu){
20573                         this._closeMenu.destroyRecursive();
20574                         delete this._closeMenu;
20575                 }
20576                 this.inherited(arguments);
20577         }
20578 });
20579
20580 }
20581
20582 if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
20583 dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
20584 dojo.provide("dijit.layout.ScrollingTabController");
20585
20586
20587
20588
20589
20590
20591 dojo.declare("dijit.layout.ScrollingTabController",
20592         dijit.layout.TabController,
20593         {
20594         // summary:
20595         //              Set of tabs with left/right arrow keys and a menu to switch between tabs not
20596         //              all fitting on a single row.
20597         //              Works only for horizontal tabs (either above or below the content, not to the left
20598         //              or right).
20599         // tags:
20600         //              private
20601
20602         templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div dojoType=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\" containerId=\"${containerId}\" iconClass=\"dijitTabStripMenuIcon\"\n\t\t\tdropDownPosition=\"below-alt, above-alt\"\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 role='tablist' dojoAttachEvent='onkeypress:onkeypress'\n\t\t\t\tdojoAttachPoint='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>\n"),
20603
20604         // useMenu: [const] Boolean
20605         //              True if a menu should be used to select tabs when they are too
20606         //              wide to fit the TabContainer, false otherwise.
20607         useMenu: true,
20608
20609         // useSlider: [const] Boolean
20610         //              True if a slider should be used to select tabs when they are too
20611         //              wide to fit the TabContainer, false otherwise.
20612         useSlider: true,
20613
20614         // tabStripClass: [const] String
20615         //              The css class to apply to the tab strip, if it is visible.
20616         tabStripClass: "",
20617
20618         widgetsInTemplate: true,
20619
20620         // _minScroll: Number
20621         //              The distance in pixels from the edge of the tab strip which,
20622         //              if a scroll animation is less than, forces the scroll to
20623         //              go all the way to the left/right.
20624         _minScroll: 5,
20625
20626         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
20627                 "class": "containerNode"
20628         }),
20629
20630         buildRendering: function(){
20631                 this.inherited(arguments);
20632                 var n = this.domNode;
20633
20634                 this.scrollNode = this.tablistWrapper;
20635                 this._initButtons();
20636
20637                 if(!this.tabStripClass){
20638                         this.tabStripClass = "dijitTabContainer" +
20639                                 this.tabPosition.charAt(0).toUpperCase() +
20640                                 this.tabPosition.substr(1).replace(/-.*/, "") +
20641                                 "None";
20642                         dojo.addClass(n, "tabStrip-disabled")
20643                 }
20644
20645                 dojo.addClass(this.tablistWrapper, this.tabStripClass);
20646         },
20647
20648         onStartup: function(){
20649                 this.inherited(arguments);
20650
20651                 // Do not show the TabController until the related
20652                 // StackController has added it's children.  This gives
20653                 // a less visually jumpy instantiation.
20654                 dojo.style(this.domNode, "visibility", "visible");
20655                 this._postStartup = true;
20656         },
20657
20658         onAddChild: function(page, insertIndex){
20659                 this.inherited(arguments);
20660
20661                 // changes to the tab button label or iconClass will have changed the width of the
20662                 // buttons, so do a resize
20663                 dojo.forEach(["label", "iconClass"], function(attr){
20664                         this.pane2watches[page.id].push(
20665                                 this.pane2button[page.id].watch(attr, dojo.hitch(this, function(name, oldValue, newValue){
20666                                         if(this._postStartup && this._dim){
20667                                                 this.resize(this._dim);
20668                                         }
20669                                 }))
20670                         );
20671                 }, this);
20672
20673                 // Increment the width of the wrapper when a tab is added
20674                 // This makes sure that the buttons never wrap.
20675                 // The value 200 is chosen as it should be bigger than most
20676                 // Tab button widths.
20677                 dojo.style(this.containerNode, "width",
20678                         (dojo.style(this.containerNode, "width") + 200) + "px");
20679         },
20680
20681         onRemoveChild: function(page, insertIndex){
20682                 // null out _selectedTab because we are about to delete that dom node
20683                 var button = this.pane2button[page.id];
20684                 if(this._selectedTab === button.domNode){
20685                         this._selectedTab = null;
20686                 }
20687
20688                 this.inherited(arguments);
20689         },
20690
20691         _initButtons: function(){
20692                 // summary:
20693                 //              Creates the buttons used to scroll to view tabs that
20694                 //              may not be visible if the TabContainer is too narrow.
20695
20696                 // Make a list of the buttons to display when the tab labels become
20697                 // wider than the TabContainer, and hide the other buttons.
20698                 // Also gets the total width of the displayed buttons.
20699                 this._btnWidth = 0;
20700                 this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
20701                         if((this.useMenu && btn == this._menuBtn.domNode) ||
20702                                 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
20703                                 this._btnWidth += dojo._getMarginSize(btn).w;
20704                                 return true;
20705                         }else{
20706                                 dojo.style(btn, "display", "none");
20707                                 return false;
20708                         }
20709                 }, this);
20710         },
20711
20712         _getTabsWidth: function(){
20713                 var children = this.getChildren();
20714                 if(children.length){
20715                         var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
20716                                 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
20717                         return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
20718                 }else{
20719                         return 0;
20720                 }
20721         },
20722
20723         _enableBtn: function(width){
20724                 // summary:
20725                 //              Determines if the tabs are wider than the width of the TabContainer, and
20726                 //              thus that we need to display left/right/menu navigation buttons.
20727                 var tabsWidth = this._getTabsWidth();
20728                 width = width || dojo.style(this.scrollNode, "width");
20729                 return tabsWidth > 0 && width < tabsWidth;
20730         },
20731
20732         resize: function(dim){
20733                 // summary:
20734                 //              Hides or displays the buttons used to scroll the tab list and launch the menu
20735                 //              that selects tabs.
20736
20737                 if(this.domNode.offsetWidth == 0){
20738                         return;
20739                 }
20740
20741                 // Save the dimensions to be used when a child is renamed.
20742                 this._dim = dim;
20743
20744                 // Set my height to be my natural height (tall enough for one row of tab labels),
20745                 // and my content-box width based on margin-box width specified in dim parameter.
20746                 // But first reset scrollNode.height in case it was set by layoutChildren() call
20747                 // in a previous run of this method.
20748                 this.scrollNode.style.height = "auto";
20749                 this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
20750                 this._contentBox.h = this.scrollNode.offsetHeight;
20751                 dojo.contentBox(this.domNode, this._contentBox);
20752
20753                 // Show/hide the left/right/menu navigation buttons depending on whether or not they
20754                 // are needed.
20755                 var enable = this._enableBtn(this._contentBox.w);
20756                 this._buttons.style("display", enable ? "" : "none");
20757
20758                 // Position and size the navigation buttons and the tablist
20759                 this._leftBtn.layoutAlign = "left";
20760                 this._rightBtn.layoutAlign = "right";
20761                 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
20762                 dijit.layout.layoutChildren(this.domNode, this._contentBox,
20763                         [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
20764
20765                 // set proper scroll so that selected tab is visible
20766                 if(this._selectedTab){
20767                         if(this._anim && this._anim.status() == "playing"){
20768                                 this._anim.stop();
20769                         }
20770                         var w = this.scrollNode,
20771                                 sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
20772                         w.scrollLeft = sl;
20773                 }
20774
20775                 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
20776                 this._setButtonClass(this._getScroll());
20777                 
20778                 this._postResize = true;
20779
20780                 // Return my size so layoutChildren() can use it.
20781                 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
20782                 return {h: this._contentBox.h, w: dim.w};
20783         },
20784
20785         _getScroll: function(){
20786                 // summary:
20787                 //              Returns the current scroll of the tabs where 0 means
20788                 //              "scrolled all the way to the left" and some positive number, based on #
20789                 //              of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
20790                 var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
20791                                 dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
20792                                          + (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
20793                 return sl;
20794         },
20795
20796         _convertToScrollLeft: function(val){
20797                 // summary:
20798                 //              Given a scroll value where 0 means "scrolled all the way to the left"
20799                 //              and some positive number, based on # of pixels of possible scroll (ex: 1000)
20800                 //              means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
20801                 //              to achieve that scroll.
20802                 //
20803                 //              This method is to adjust for RTL funniness in various browsers and versions.
20804                 if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
20805                         return val;
20806                 }else{
20807                         var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
20808                         return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
20809                 }
20810         },
20811
20812         onSelectChild: function(/*dijit._Widget*/ page){
20813                 // summary:
20814                 //              Smoothly scrolls to a tab when it is selected.
20815
20816                 var tab = this.pane2button[page.id];
20817                 if(!tab || !page){return;}
20818
20819                 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
20820                 var node = tab.domNode;
20821                 if(this._postResize && node != this._selectedTab){
20822                         this._selectedTab = node;
20823
20824                         var sl = this._getScroll();
20825
20826                         if(sl > node.offsetLeft ||
20827                                         sl + dojo.style(this.scrollNode, "width") <
20828                                         node.offsetLeft + dojo.style(node, "width")){
20829                                 this.createSmoothScroll().play();
20830                         }
20831                 }
20832
20833                 this.inherited(arguments);
20834         },
20835
20836         _getScrollBounds: function(){
20837                 // summary:
20838                 //              Returns the minimum and maximum scroll setting to show the leftmost and rightmost
20839                 //              tabs (respectively)
20840                 var children = this.getChildren(),
20841                         scrollNodeWidth = dojo.style(this.scrollNode, "width"),         // about 500px
20842                         containerWidth = dojo.style(this.containerNode, "width"),       // 50,000px
20843                         maxPossibleScroll = containerWidth - scrollNodeWidth,   // scrolling until right edge of containerNode visible
20844                         tabsWidth = this._getTabsWidth();
20845
20846                 if(children.length && tabsWidth > scrollNodeWidth){
20847                         // Scrolling should happen
20848                         return {
20849                                 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
20850                                 max: this.isLeftToRight() ?
20851                                         (children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
20852                                         maxPossibleScroll
20853                         };
20854                 }else{
20855                         // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
20856                         var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
20857                         return {
20858                                 min: onlyScrollPosition,
20859                                 max: onlyScrollPosition
20860                         };
20861                 }
20862         },
20863
20864         _getScrollForSelectedTab: function(){
20865                 // summary:
20866                 //              Returns the scroll value setting so that the selected tab
20867                 //              will appear in the center
20868                 var w = this.scrollNode,
20869                         n = this._selectedTab,
20870                         scrollNodeWidth = dojo.style(this.scrollNode, "width"),
20871                         scrollBounds = this._getScrollBounds();
20872
20873                 // TODO: scroll minimal amount (to either right or left) so that
20874                 // selected tab is fully visible, and just return if it's already visible?
20875                 var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
20876                 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
20877
20878                 // TODO:
20879                 // If scrolling close to the left side or right side, scroll
20880                 // all the way to the left or right.  See this._minScroll.
20881                 // (But need to make sure that doesn't scroll the tab out of view...)
20882                 return pos;
20883         },
20884
20885         createSmoothScroll: function(x){
20886                 // summary:
20887                 //              Creates a dojo._Animation object that smoothly scrolls the tab list
20888                 //              either to a fixed horizontal pixel value, or to the selected tab.
20889                 // description:
20890                 //              If an number argument is passed to the function, that horizontal
20891                 //              pixel position is scrolled to.  Otherwise the currently selected
20892                 //              tab is scrolled to.
20893                 // x: Integer?
20894                 //              An optional pixel value to scroll to, indicating distance from left.
20895
20896                 // Calculate position to scroll to
20897                 if(arguments.length > 0){
20898                         // position specified by caller, just make sure it's within bounds
20899                         var scrollBounds = this._getScrollBounds();
20900                         x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
20901                 }else{
20902                         // scroll to center the current tab
20903                         x = this._getScrollForSelectedTab();
20904                 }
20905
20906                 if(this._anim && this._anim.status() == "playing"){
20907                         this._anim.stop();
20908                 }
20909
20910                 var self = this,
20911                         w = this.scrollNode,
20912                         anim = new dojo._Animation({
20913                                 beforeBegin: function(){
20914                                         if(this.curve){ delete this.curve; }
20915                                         var oldS = w.scrollLeft,
20916                                                 newS = self._convertToScrollLeft(x);
20917                                         anim.curve = new dojo._Line(oldS, newS);
20918                                 },
20919                                 onAnimate: function(val){
20920                                         w.scrollLeft = val;
20921                                 }
20922                         });
20923                 this._anim = anim;
20924
20925                 // Disable/enable left/right buttons according to new scroll position
20926                 this._setButtonClass(x);
20927
20928                 return anim; // dojo._Animation
20929         },
20930
20931         _getBtnNode: function(/*Event*/ e){
20932                 // summary:
20933                 //              Gets a button DOM node from a mouse click event.
20934                 // e:
20935                 //              The mouse click event.
20936                 var n = e.target;
20937                 while(n && !dojo.hasClass(n, "tabStripButton")){
20938                         n = n.parentNode;
20939                 }
20940                 return n;
20941         },
20942
20943         doSlideRight: function(/*Event*/ e){
20944                 // summary:
20945                 //              Scrolls the menu to the right.
20946                 // e:
20947                 //              The mouse click event.
20948                 this.doSlide(1, this._getBtnNode(e));
20949         },
20950
20951         doSlideLeft: function(/*Event*/ e){
20952                 // summary:
20953                 //              Scrolls the menu to the left.
20954                 // e:
20955                 //              The mouse click event.
20956                 this.doSlide(-1,this._getBtnNode(e));
20957         },
20958
20959         doSlide: function(/*Number*/ direction, /*DomNode*/ node){
20960                 // summary:
20961                 //              Scrolls the tab list to the left or right by 75% of the widget width.
20962                 // direction:
20963                 //              If the direction is 1, the widget scrolls to the right, if it is
20964                 //              -1, it scrolls to the left.
20965
20966                 if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
20967
20968                 var sWidth = dojo.style(this.scrollNode, "width");
20969                 var d = (sWidth * 0.75) * direction;
20970
20971                 var to = this._getScroll() + d;
20972
20973                 this._setButtonClass(to);
20974
20975                 this.createSmoothScroll(to).play();
20976         },
20977
20978         _setButtonClass: function(/*Number*/ scroll){
20979                 // summary:
20980                 //              Disables the left scroll button if the tabs are scrolled all the way to the left,
20981                 //              or the right scroll button in the opposite case.
20982                 // scroll: Integer
20983                 //              amount of horizontal scroll
20984
20985                 var scrollBounds = this._getScrollBounds();
20986                 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
20987                 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
20988         }
20989 });
20990
20991
20992 dojo.declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
20993         baseClass: "dijitTab tabStripButton",
20994
20995         templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "<div dojoAttachEvent=\"onclick:_onButtonClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" dojoattachpoint=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" dojoattachpoint=\"tabContent\">\n\t\t\t<img role=\"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"),
20996
20997                 // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
20998                 // able to tab to the left/right/menu buttons
20999         tabIndex: "",
21000
21001         // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
21002         // either (this override avoids focus() call in FormWidget.js)
21003         isFocusable: function(){ return false; }
21004 });
21005
21006 dojo.declare("dijit.layout._ScrollingTabControllerButton",
21007         [dijit.form.Button, dijit.layout._ScrollingTabControllerButtonMixin]);
21008
21009 dojo.declare(
21010         "dijit.layout._ScrollingTabControllerMenuButton",
21011         [dijit.form.Button, dijit._HasDropDown, dijit.layout._ScrollingTabControllerButtonMixin],
21012 {
21013         // id of the TabContainer itself
21014         containerId: "",
21015
21016         // -1 so user can't tab into the button, but so that button can still be focused programatically.
21017         // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
21018         tabIndex: "-1",
21019
21020         isLoaded: function(){
21021                 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
21022                 return false;
21023         },
21024
21025         loadDropDown: function(callback){
21026                 this.dropDown = new dijit.Menu({
21027                         id: this.containerId + "_menu",
21028                         dir: this.dir,
21029                         lang: this.lang
21030                 });
21031                 var container = dijit.byId(this.containerId);
21032                 dojo.forEach(container.getChildren(), function(page){
21033                         var menuItem = new dijit.MenuItem({
21034                                 id: page.id + "_stcMi",
21035                                 label: page.title,
21036                                 iconClass: page.iconClass,
21037                                 dir: page.dir,
21038                                 lang: page.lang,
21039                                 onClick: function(){
21040                                         container.selectChild(page);
21041                                 }
21042                         });
21043                         this.dropDown.addChild(menuItem);
21044                 }, this);
21045                 callback();
21046         },
21047
21048         closeDropDown: function(/*Boolean*/ focus){
21049                 this.inherited(arguments);
21050                 if(this.dropDown){
21051                         this.dropDown.destroyRecursive();
21052                         delete this.dropDown;
21053                 }
21054         }
21055 });
21056
21057 }
21058
21059 if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21060 dojo._hasResource["dijit.layout.TabContainer"] = true;
21061 dojo.provide("dijit.layout.TabContainer");
21062
21063
21064
21065
21066
21067 dojo.declare("dijit.layout.TabContainer",
21068         dijit.layout._TabContainerBase,
21069         {
21070                 // summary:
21071                 //              A Container with tabs to select each child (only one of which is displayed at a time).
21072                 // description:
21073                 //              A TabContainer is a container that has multiple panes, but shows only
21074                 //              one pane at a time.  There are a set of tabs corresponding to each pane,
21075                 //              where each tab has the name (aka title) of the pane, and optionally a close button.
21076
21077                 // useMenu: [const] Boolean
21078                 //              True if a menu should be used to select tabs when they are too
21079                 //              wide to fit the TabContainer, false otherwise.
21080                 useMenu: true,
21081
21082                 // useSlider: [const] Boolean
21083                 //              True if a slider should be used to select tabs when they are too
21084                 //              wide to fit the TabContainer, false otherwise.
21085                 useSlider: true,
21086
21087                 // controllerWidget: String
21088                 //              An optional parameter to override the widget used to display the tab labels
21089                 controllerWidget: "",
21090
21091                 _makeController: function(/*DomNode*/ srcNode){
21092                         // summary:
21093                         //              Instantiate tablist controller widget and return reference to it.
21094                         //              Callback from _TabContainerBase.postCreate().
21095                         // tags:
21096                         //              protected extension
21097
21098                         var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
21099                                 TabController = dojo.getObject(this.controllerWidget);
21100
21101                         return new TabController({
21102                                 id: this.id + "_tablist",
21103                                 dir: this.dir,
21104                                 lang: this.lang,
21105                                 tabPosition: this.tabPosition,
21106                                 doLayout: this.doLayout,
21107                                 containerId: this.id,
21108                                 "class": cls,
21109                                 nested: this.nested,
21110                                 useMenu: this.useMenu,
21111                                 useSlider: this.useSlider,
21112                                 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
21113                         }, srcNode);
21114                 },
21115
21116                 postMixInProperties: function(){
21117                         this.inherited(arguments);
21118
21119                         // Scrolling controller only works for horizontal non-nested tabs
21120                         if(!this.controllerWidget){
21121                                 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
21122                                                         "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
21123                         }
21124                 }
21125 });
21126
21127 }
21128
21129 if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21130 dojo._hasResource["dojo.number"] = true;
21131 dojo.provide("dojo.number");
21132
21133
21134
21135
21136
21137 dojo.getObject("number", true, dojo);
21138
21139 /*=====
21140 dojo.number = {
21141         // summary: localized formatting and parsing routines for Number
21142 }
21143
21144 dojo.number.__FormatOptions = function(){
21145         //      pattern: String?
21146         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21147         //              with this string.  Default value is based on locale.  Overriding this property will defeat
21148         //              localization.  Literal characters in patterns are not supported.
21149         //      type: String?
21150         //              choose a format type based on the locale from the following:
21151         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
21152         //      places: Number?
21153         //              fixed number of decimal places to show.  This overrides any
21154         //              information in the provided pattern.
21155         //      round: Number?
21156         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
21157         //              means do not round.
21158         //      locale: String?
21159         //              override the locale used to determine formatting rules
21160         //      fractional: Boolean?
21161         //              If false, show no decimal places, overriding places and pattern settings.
21162         this.pattern = pattern;
21163         this.type = type;
21164         this.places = places;
21165         this.round = round;
21166         this.locale = locale;
21167         this.fractional = fractional;
21168 }
21169 =====*/
21170
21171 dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
21172         // summary:
21173         //              Format a Number as a String, using locale-specific settings
21174         // description:
21175         //              Create a string from a Number using a known localized pattern.
21176         //              Formatting patterns appropriate to the locale are chosen from the
21177         //              [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
21178         //              delimiters.
21179         //              If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
21180         // value:
21181         //              the number to be formatted
21182
21183         options = dojo.mixin({}, options || {});
21184         var locale = dojo.i18n.normalizeLocale(options.locale),
21185                 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
21186         options.customs = bundle;
21187         var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
21188         if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
21189         return dojo.number._applyPattern(value, pattern, options); // String
21190 };
21191
21192 //dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
21193 dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
21194
21195 dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
21196         // summary:
21197         //              Apply pattern to format value as a string using options. Gives no
21198         //              consideration to local customs.
21199         // value:
21200         //              the number to be formatted.
21201         // pattern:
21202         //              a pattern string as described by
21203         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21204         // options: dojo.number.__FormatOptions?
21205         //              _applyPattern is usually called via `dojo.number.format()` which
21206         //              populates an extra property in the options parameter, "customs".
21207         //              The customs object specifies group and decimal parameters if set.
21208
21209         //TODO: support escapes
21210         options = options || {};
21211         var group = options.customs.group,
21212                 decimal = options.customs.decimal,
21213                 patternList = pattern.split(';'),
21214                 positivePattern = patternList[0];
21215         pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
21216
21217         //TODO: only test against unescaped
21218         if(pattern.indexOf('%') != -1){
21219                 value *= 100;
21220         }else if(pattern.indexOf('\u2030') != -1){
21221                 value *= 1000; // per mille
21222         }else if(pattern.indexOf('\u00a4') != -1){
21223                 group = options.customs.currencyGroup || group;//mixins instead?
21224                 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
21225                 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
21226                         var prop = ["symbol", "currency", "displayName"][match.length-1];
21227                         return options[prop] || options.currency || "";
21228                 });
21229         }else if(pattern.indexOf('E') != -1){
21230                 throw new Error("exponential notation not supported");
21231         }
21232         
21233         //TODO: support @ sig figs?
21234         var numberPatternRE = dojo.number._numberPatternRE;
21235         var numberPattern = positivePattern.match(numberPatternRE);
21236         if(!numberPattern){
21237                 throw new Error("unable to find a number expression in pattern: "+pattern);
21238         }
21239         if(options.fractional === false){ options.places = 0; }
21240         return pattern.replace(numberPatternRE,
21241                 dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
21242 };
21243
21244 dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
21245         //      summary:
21246         //              Rounds to the nearest value with the given number of decimal places, away from zero
21247         //      description:
21248         //              Rounds to the nearest value with the given number of decimal places, away from zero if equal.
21249         //              Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
21250         //              fractional increments also, such as the nearest quarter.
21251         //              NOTE: Subject to floating point errors.  See dojox.math.round for experimental workaround.
21252         //      value:
21253         //              The number to round
21254         //      places:
21255         //              The number of decimal places where rounding takes place.  Defaults to 0 for whole rounding.
21256         //              Must be non-negative.
21257         //      increment:
21258         //              Rounds next place to nearest value of increment/10.  10 by default.
21259         //      example:
21260         //              >>> dojo.number.round(-0.5)
21261         //              -1
21262         //              >>> dojo.number.round(162.295, 2)
21263         //              162.29  // note floating point error.  Should be 162.3
21264         //              >>> dojo.number.round(10.71, 0, 2.5)
21265         //              10.75
21266         var factor = 10 / (increment || 10);
21267         return (factor * +value).toFixed(places) / factor; // Number
21268 };
21269
21270 if((0.9).toFixed() == 0){
21271         // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
21272         // is just after the rounding place and is >=5
21273         (function(){
21274                 var round = dojo.number.round;
21275                 dojo.number.round = function(v, p, m){
21276                         var d = Math.pow(10, -p || 0), a = Math.abs(v);
21277                         if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
21278                                 d = 0;
21279                         }
21280                         return round(v, p, m) + (v > 0 ? d : -d);
21281                 };
21282         })();
21283 }
21284
21285 /*=====
21286 dojo.number.__FormatAbsoluteOptions = function(){
21287         //      decimal: String?
21288         //              the decimal separator
21289         //      group: String?
21290         //              the group separator
21291         //      places: Number?|String?
21292         //              number of decimal places.  the range "n,m" will format to m places.
21293         //      round: Number?
21294         //              5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
21295         //              means don't round.
21296         this.decimal = decimal;
21297         this.group = group;
21298         this.places = places;
21299         this.round = round;
21300 }
21301 =====*/
21302
21303 dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
21304         // summary:
21305         //              Apply numeric pattern to absolute value using options. Gives no
21306         //              consideration to local customs.
21307         // value:
21308         //              the number to be formatted, ignores sign
21309         // pattern:
21310         //              the number portion of a pattern (e.g. `#,##0.00`)
21311         options = options || {};
21312         if(options.places === true){options.places=0;}
21313         if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
21314
21315         var patternParts = pattern.split("."),
21316                 comma = typeof options.places == "string" && options.places.indexOf(","),
21317                 maxPlaces = options.places;
21318         if(comma){
21319                 maxPlaces = options.places.substring(comma + 1);
21320         }else if(!(maxPlaces >= 0)){
21321                 maxPlaces = (patternParts[1] || []).length;
21322         }
21323         if(!(options.round < 0)){
21324                 value = dojo.number.round(value, maxPlaces, options.round);
21325         }
21326
21327         var valueParts = String(Math.abs(value)).split("."),
21328                 fractional = valueParts[1] || "";
21329         if(patternParts[1] || options.places){
21330                 if(comma){
21331                         options.places = options.places.substring(0, comma);
21332                 }
21333                 // Pad fractional with trailing zeros
21334                 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
21335                 if(pad > fractional.length){
21336                         valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
21337                 }
21338
21339                 // Truncate fractional
21340                 if(maxPlaces < fractional.length){
21341                         valueParts[1] = fractional.substr(0, maxPlaces);
21342                 }
21343         }else{
21344                 if(valueParts[1]){ valueParts.pop(); }
21345         }
21346
21347         // Pad whole with leading zeros
21348         var patternDigits = patternParts[0].replace(',', '');
21349         pad = patternDigits.indexOf("0");
21350         if(pad != -1){
21351                 pad = patternDigits.length - pad;
21352                 if(pad > valueParts[0].length){
21353                         valueParts[0] = dojo.string.pad(valueParts[0], pad);
21354                 }
21355
21356                 // Truncate whole
21357                 if(patternDigits.indexOf("#") == -1){
21358                         valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
21359                 }
21360         }
21361
21362         // Add group separators
21363         var index = patternParts[0].lastIndexOf(','),
21364                 groupSize, groupSize2;
21365         if(index != -1){
21366                 groupSize = patternParts[0].length - index - 1;
21367                 var remainder = patternParts[0].substr(0, index);
21368                 index = remainder.lastIndexOf(',');
21369                 if(index != -1){
21370                         groupSize2 = remainder.length - index - 1;
21371                 }
21372         }
21373         var pieces = [];
21374         for(var whole = valueParts[0]; whole;){
21375                 var off = whole.length - groupSize;
21376                 pieces.push((off > 0) ? whole.substr(off) : whole);
21377                 whole = (off > 0) ? whole.slice(0, off) : "";
21378                 if(groupSize2){
21379                         groupSize = groupSize2;
21380                         delete groupSize2;
21381                 }
21382         }
21383         valueParts[0] = pieces.reverse().join(options.group || ",");
21384
21385         return valueParts.join(options.decimal || ".");
21386 };
21387
21388 /*=====
21389 dojo.number.__RegexpOptions = function(){
21390         //      pattern: String?
21391         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21392         //              with this string.  Default value is based on locale.  Overriding this property will defeat
21393         //              localization.
21394         //      type: String?
21395         //              choose a format type based on the locale from the following:
21396         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
21397         //      locale: String?
21398         //              override the locale used to determine formatting rules
21399         //      strict: Boolean?
21400         //              strict parsing, false by default.  Strict parsing requires input as produced by the format() method.
21401         //              Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
21402         //      places: Number|String?
21403         //              number of decimal places to accept: Infinity, a positive number, or
21404         //              a range "n,m".  Defined by pattern or Infinity if pattern not provided.
21405         this.pattern = pattern;
21406         this.type = type;
21407         this.locale = locale;
21408         this.strict = strict;
21409         this.places = places;
21410 }
21411 =====*/
21412 dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
21413         //      summary:
21414         //              Builds the regular needed to parse a number
21415         //      description:
21416         //              Returns regular expression with positive and negative match, group
21417         //              and decimal separators
21418         return dojo.number._parseInfo(options).regexp; // String
21419 };
21420
21421 dojo.number._parseInfo = function(/*Object?*/options){
21422         options = options || {};
21423         var locale = dojo.i18n.normalizeLocale(options.locale),
21424                 bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
21425                 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
21426 //TODO: memoize?
21427                 group = bundle.group,
21428                 decimal = bundle.decimal,
21429                 factor = 1;
21430
21431         if(pattern.indexOf('%') != -1){
21432                 factor /= 100;
21433         }else if(pattern.indexOf('\u2030') != -1){
21434                 factor /= 1000; // per mille
21435         }else{
21436                 var isCurrency = pattern.indexOf('\u00a4') != -1;
21437                 if(isCurrency){
21438                         group = bundle.currencyGroup || group;
21439                         decimal = bundle.currencyDecimal || decimal;
21440                 }
21441         }
21442
21443         //TODO: handle quoted escapes
21444         var patternList = pattern.split(';');
21445         if(patternList.length == 1){
21446                 patternList.push("-" + patternList[0]);
21447         }
21448
21449         var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
21450                 pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
21451                 return pattern.replace(dojo.number._numberPatternRE, function(format){
21452                         var flags = {
21453                                 signed: false,
21454                                 separator: options.strict ? group : [group,""],
21455                                 fractional: options.fractional,
21456                                 decimal: decimal,
21457                                 exponent: false
21458                                 },
21459
21460                                 parts = format.split('.'),
21461                                 places = options.places;
21462
21463                         // special condition for percent (factor != 1)
21464                         // allow decimal places even if not specified in pattern
21465                         if(parts.length == 1 && factor != 1){
21466                             parts[1] = "###";
21467                         }
21468                         if(parts.length == 1 || places === 0){
21469                                 flags.fractional = false;
21470                         }else{
21471                                 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
21472                                 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
21473                                 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
21474                                 flags.places = places;
21475                         }
21476                         var groups = parts[0].split(',');
21477                         if(groups.length > 1){
21478                                 flags.groupSize = groups.pop().length;
21479                                 if(groups.length > 1){
21480                                         flags.groupSize2 = groups.pop().length;
21481                                 }
21482                         }
21483                         return "("+dojo.number._realNumberRegexp(flags)+")";
21484                 });
21485         }, true);
21486
21487         if(isCurrency){
21488                 // substitute the currency symbol for the placeholder in the pattern
21489                 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
21490                         var prop = ["symbol", "currency", "displayName"][target.length-1],
21491                                 symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
21492                         before = before ? "[\\s\\xa0]" : "";
21493                         after = after ? "[\\s\\xa0]" : "";
21494                         if(!options.strict){
21495                                 if(before){before += "*";}
21496                                 if(after){after += "*";}
21497                                 return "(?:"+before+symbol+after+")?";
21498                         }
21499                         return before+symbol+after;
21500                 });
21501         }
21502
21503 //TODO: substitute localized sign/percent/permille/etc.?
21504
21505         // normalize whitespace and return
21506         return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
21507 };
21508
21509 /*=====
21510 dojo.number.__ParseOptions = function(){
21511         //      pattern: String?
21512         //              override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21513         //              with this string.  Default value is based on locale.  Overriding this property will defeat
21514         //              localization.  Literal characters in patterns are not supported.
21515         //      type: String?
21516         //              choose a format type based on the locale from the following:
21517         //              decimal, scientific (not yet supported), percent, currency. decimal by default.
21518         //      locale: String?
21519         //              override the locale used to determine formatting rules
21520         //      strict: Boolean?
21521         //              strict parsing, false by default.  Strict parsing requires input as produced by the format() method.
21522         //              Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
21523         //      fractional: Boolean?|Array?
21524         //              Whether to include the fractional portion, where the number of decimal places are implied by pattern
21525         //              or explicit 'places' parameter.  The value [true,false] makes the fractional portion optional.
21526         this.pattern = pattern;
21527         this.type = type;
21528         this.locale = locale;
21529         this.strict = strict;
21530         this.fractional = fractional;
21531 }
21532 =====*/
21533 dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
21534         // summary:
21535         //              Convert a properly formatted string to a primitive Number, using
21536         //              locale-specific settings.
21537         // description:
21538         //              Create a Number from a string using a known localized pattern.
21539         //              Formatting patterns are chosen appropriate to the locale
21540         //              and follow the syntax described by
21541         //              [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
21542         //              Note that literal characters in patterns are not supported.
21543         // expression:
21544         //              A string representation of a Number
21545         var info = dojo.number._parseInfo(options),
21546                 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
21547         if(!results){
21548                 return NaN; //NaN
21549         }
21550         var absoluteMatch = results[1]; // match for the positive expression
21551         if(!results[1]){
21552                 if(!results[2]){
21553                         return NaN; //NaN
21554                 }
21555                 // matched the negative pattern
21556                 absoluteMatch =results[2];
21557                 info.factor *= -1;
21558         }
21559
21560         // Transform it to something Javascript can parse as a number.  Normalize
21561         // decimal point and strip out group separators or alternate forms of whitespace
21562         absoluteMatch = absoluteMatch.
21563                 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
21564                 replace(info.decimal, ".");
21565         // Adjust for negative sign, percent, etc. as necessary
21566         return absoluteMatch * info.factor; //Number
21567 };
21568
21569 /*=====
21570 dojo.number.__RealNumberRegexpFlags = function(){
21571         //      places: Number?
21572         //              The integer number of decimal places or a range given as "n,m".  If
21573         //              not given, the decimal part is optional and the number of places is
21574         //              unlimited.
21575         //      decimal: String?
21576         //              A string for the character used as the decimal point.  Default
21577         //              is ".".
21578         //      fractional: Boolean?|Array?
21579         //              Whether decimal places are used.  Can be true, false, or [true,
21580         //              false].  Default is [true, false] which means optional.
21581         //      exponent: Boolean?|Array?
21582         //              Express in exponential notation.  Can be true, false, or [true,
21583         //              false]. Default is [true, false], (i.e. will match if the
21584         //              exponential part is present are not).
21585         //      eSigned: Boolean?|Array?
21586         //              The leading plus-or-minus sign on the exponent.  Can be true,
21587         //              false, or [true, false].  Default is [true, false], (i.e. will
21588         //              match if it is signed or unsigned).  flags in regexp.integer can be
21589         //              applied.
21590         this.places = places;
21591         this.decimal = decimal;
21592         this.fractional = fractional;
21593         this.exponent = exponent;
21594         this.eSigned = eSigned;
21595 }
21596 =====*/
21597
21598 dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
21599         // summary:
21600         //              Builds a regular expression to match a real number in exponential
21601         //              notation
21602
21603         // assign default values to missing parameters
21604         flags = flags || {};
21605         //TODO: use mixin instead?
21606         if(!("places" in flags)){ flags.places = Infinity; }
21607         if(typeof flags.decimal != "string"){ flags.decimal = "."; }
21608         if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
21609         if(!("exponent" in flags)){ flags.exponent = [true, false]; }
21610         if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
21611
21612         var integerRE = dojo.number._integerRegexp(flags),
21613                 decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
21614                 function(q){
21615                         var re = "";
21616                         if(q && (flags.places!==0)){
21617                                 re = "\\" + flags.decimal;
21618                                 if(flags.places == Infinity){
21619                                         re = "(?:" + re + "\\d+)?";
21620                                 }else{
21621                                         re += "\\d{" + flags.places + "}";
21622                                 }
21623                         }
21624                         return re;
21625                 },
21626                 true
21627         );
21628
21629         var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
21630                 function(q){
21631                         if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
21632                         return "";
21633                 }
21634         );
21635
21636         var realRE = integerRE + decimalRE;
21637         // allow for decimals without integers, e.g. .25
21638         if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
21639         return realRE + exponentRE; // String
21640 };
21641
21642 /*=====
21643 dojo.number.__IntegerRegexpFlags = function(){
21644         //      signed: Boolean?
21645         //              The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
21646         //              Default is `[true, false]`, (i.e. will match if it is signed
21647         //              or unsigned).
21648         //      separator: String?
21649         //              The character used as the thousands separator. Default is no
21650         //              separator. For more than one symbol use an array, e.g. `[",", ""]`,
21651         //              makes ',' optional.
21652         //      groupSize: Number?
21653         //              group size between separators
21654         //      groupSize2: Number?
21655         //              second grouping, where separators 2..n have a different interval than the first separator (for India)
21656         this.signed = signed;
21657         this.separator = separator;
21658         this.groupSize = groupSize;
21659         this.groupSize2 = groupSize2;
21660 }
21661 =====*/
21662
21663 dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
21664         // summary:
21665         //              Builds a regular expression that matches an integer
21666
21667         // assign default values to missing parameters
21668         flags = flags || {};
21669         if(!("signed" in flags)){ flags.signed = [true, false]; }
21670         if(!("separator" in flags)){
21671                 flags.separator = "";
21672         }else if(!("groupSize" in flags)){
21673                 flags.groupSize = 3;
21674         }
21675
21676         var signRE = dojo.regexp.buildGroupRE(flags.signed,
21677                 function(q){ return q ? "[-+]" : ""; },
21678                 true
21679         );
21680
21681         var numberRE = dojo.regexp.buildGroupRE(flags.separator,
21682                 function(sep){
21683                         if(!sep){
21684                                 return "(?:\\d+)";
21685                         }
21686
21687                         sep = dojo.regexp.escapeString(sep);
21688                         if(sep == " "){ sep = "\\s"; }
21689                         else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
21690
21691                         var grp = flags.groupSize, grp2 = flags.groupSize2;
21692                         //TODO: should we continue to enforce that numbers with separators begin with 1-9?  See #6933
21693                         if(grp2){
21694                                 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
21695                                 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
21696                         }
21697                         return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
21698                 },
21699                 true
21700         );
21701
21702         return signRE + numberRE; // String
21703 };
21704
21705 }
21706
21707 if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21708 dojo._hasResource["dijit.ProgressBar"] = true;
21709 dojo.provide("dijit.ProgressBar");
21710
21711
21712
21713
21714
21715
21716 dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
21717         // summary:
21718         //              A progress indication widget, showing the amount completed
21719         //              (often the percentage completed) of a task.
21720         //
21721         // example:
21722         // |    <div dojoType="ProgressBar"
21723         // |             places="0"
21724         // |             value="..." maximum="...">
21725         // |    </div>
21726
21727         // progress: [const] String (Percentage or Number)
21728         //              Number or percentage indicating amount of task completed.
21729         //              Deprecated.   Use "value" instead.
21730         progress: "0",
21731
21732         // value: String (Percentage or Number)
21733         //              Number or percentage indicating amount of task completed.
21734         //              With "%": percentage value, 0% <= progress <= 100%, or
21735         //              without "%": absolute value, 0 <= progress <= maximum.
21736         //              Infinity means that the progress bar is indeterminate.
21737         value: "",
21738
21739         // maximum: [const] Float
21740         //              Max sample number
21741         maximum: 100,
21742
21743         // places: [const] Number
21744         //              Number of places to show in values; 0 by default
21745         places: 0,
21746
21747         // indeterminate: [const] Boolean
21748         //              If false: show progress value (number or percentage).
21749         //              If true: show that a process is underway but that the amount completed is unknown.
21750         //              Deprecated.   Use "value" instead.
21751         indeterminate: false,
21752
21753         // label: String?
21754         //              Label on progress bar.   Defaults to percentage for determinate progress bar and
21755         //              blank for indeterminate progress bar.
21756         label:"",
21757
21758         // name: String
21759         //              this is the field name (for a form) if set. This needs to be set if you want to use
21760         //              this widget in a dijit.form.Form widget (such as dijit.Dialog)
21761         name: '',
21762
21763         templateString: dojo.cache("dijit", "templates/ProgressBar.html", "<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div  dojoAttachPoint=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&nbsp;</span\n\t></div\n\t><div dojoAttachPoint=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img dojoAttachPoint=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"),
21764
21765         // _indeterminateHighContrastImagePath: [private] dojo._URL
21766         //              URL to image to use for indeterminate progress bar when display is in high contrast mode
21767         _indeterminateHighContrastImagePath:
21768                 dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
21769
21770         postMixInProperties: function(){
21771                 this.inherited(arguments);
21772                 if(!("value" in this.params)){
21773                         this.value = this.indeterminate ? Infinity : this.progress;
21774                 }
21775         },
21776
21777         buildRendering: function(){
21778                 this.inherited(arguments);
21779                 this.indeterminateHighContrastImage.setAttribute("src",
21780                         this._indeterminateHighContrastImagePath.toString());
21781                 this.update();
21782         },
21783
21784         update: function(/*Object?*/attributes){
21785                 // summary:
21786                 //              Internal method to change attributes of ProgressBar, similar to set(hash).  Users should call
21787                 //              set("value", ...) rather than calling this method directly.
21788                 // attributes:
21789                 //              May provide progress and/or maximum properties on this parameter;
21790                 //              see attribute specs for details.
21791                 // example:
21792                 //      |       myProgressBar.update({'indeterminate': true});
21793                 //      |       myProgressBar.update({'progress': 80});
21794                 //      |       myProgressBar.update({'indeterminate': true, label:"Loading ..." })
21795                 // tags:
21796                 //              private
21797
21798                 // TODO: deprecate this method and use set() instead
21799
21800                 dojo.mixin(this, attributes || {});
21801                 var tip = this.internalProgress, ap = this.domNode;
21802                 var percent = 1;
21803                 if(this.indeterminate){
21804                         dijit.removeWaiState(ap, "valuenow");
21805                         dijit.removeWaiState(ap, "valuemin");
21806                         dijit.removeWaiState(ap, "valuemax");
21807                 }else{
21808                         if(String(this.progress).indexOf("%") != -1){
21809                                 percent = Math.min(parseFloat(this.progress)/100, 1);
21810                                 this.progress = percent * this.maximum;
21811                         }else{
21812                                 this.progress = Math.min(this.progress, this.maximum);
21813                                 percent = this.progress / this.maximum;
21814                         }
21815
21816                         dijit.setWaiState(ap, "describedby", this.labelNode.id);
21817                         dijit.setWaiState(ap, "valuenow", this.progress);
21818                         dijit.setWaiState(ap, "valuemin", 0);
21819                         dijit.setWaiState(ap, "valuemax", this.maximum);
21820                 }
21821                 this.labelNode.innerHTML = this.report(percent);
21822
21823                 dojo.toggleClass(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
21824                 tip.style.width = (percent * 100) + "%";
21825                 this.onChange();
21826         },
21827
21828         _setValueAttr: function(v){
21829                 this._set("value", v);
21830                 if(v == Infinity){
21831                         this.update({indeterminate:true});
21832                 }else{
21833                         this.update({indeterminate:false, progress:v});
21834                 }
21835         },
21836
21837         _setLabelAttr: function(label){
21838                 this._set("label", label);
21839                 this.update();
21840         },
21841
21842         _setIndeterminateAttr: function(indeterminate){
21843                 // Deprecated, use set("value", ...) instead
21844                 this.indeterminate = indeterminate;
21845                 this.update();
21846         },
21847
21848         report: function(/*float*/percent){
21849                 // summary:
21850                 //              Generates message to show inside progress bar (normally indicating amount of task completed).
21851                 //              May be overridden.
21852                 // tags:
21853                 //              extension
21854
21855                 return this.label ? this.label :
21856                                 (this.indeterminate ? "&nbsp;" : dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
21857         },
21858
21859         onChange: function(){
21860                 // summary:
21861                 //              Callback fired when progress updates.
21862                 // tags:
21863                 //              extension
21864         }
21865 });
21866
21867 }
21868
21869 if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21870 dojo._hasResource["dijit.ToolbarSeparator"] = true;
21871 dojo.provide("dijit.ToolbarSeparator");
21872
21873
21874
21875
21876 dojo.declare("dijit.ToolbarSeparator",
21877                 [ dijit._Widget, dijit._Templated ],
21878                 {
21879                 // summary:
21880                 //              A spacer between two `dijit.Toolbar` items
21881                 templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
21882                 buildRendering: function(){
21883                         this.inherited(arguments);
21884                         dojo.setSelectable(this.domNode, false);
21885                 },
21886                 isFocusable: function(){
21887                         // summary:
21888                         //              This widget isn't focusable, so pass along that fact.
21889                         // tags:
21890                         //              protected
21891                         return false;
21892                 }
21893
21894         });
21895
21896 }
21897
21898 if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21899 dojo._hasResource["dijit.Toolbar"] = true;
21900 dojo.provide("dijit.Toolbar");
21901
21902
21903
21904
21905
21906
21907 // Note: require of ToolbarSeparator is for back-compat, remove for 2.0
21908
21909 dojo.declare("dijit.Toolbar",
21910         [dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
21911         {
21912         // summary:
21913         //              A Toolbar widget, used to hold things like `dijit.Editor` buttons
21914
21915         templateString:
21916                 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" dojoAttachPoint="containerNode">' +
21917                 //      '<table style="table-layout: fixed" class="dijitReset dijitToolbarTable">' + // factor out style
21918                 //              '<tr class="dijitReset" dojoAttachPoint="containerNode"></tr>'+
21919                 //      '</table>' +
21920                 '</div>',
21921
21922         baseClass: "dijitToolbar",
21923
21924         postCreate: function(){
21925                 this.inherited(arguments);
21926
21927                 this.connectKeyNavHandlers(
21928                         this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
21929                         this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
21930                 );
21931         },
21932
21933         startup: function(){
21934                 if(this._started){ return; }
21935
21936                 this.startupKeyNavChildren();
21937
21938                 this.inherited(arguments);
21939         }
21940 }
21941 );
21942
21943 }
21944
21945 if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21946 dojo._hasResource["dojo.DeferredList"] = true;
21947 dojo.provide("dojo.DeferredList");
21948
21949
21950 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
21951         // summary:
21952         //              Provides event handling for a group of Deferred objects.
21953         // description:
21954         //              DeferredList takes an array of existing deferreds and returns a new deferred of its own
21955         //              this new deferred will typically have its callback fired when all of the deferreds in
21956         //              the given list have fired their own deferreds.  The parameters `fireOnOneCallback` and
21957         //              fireOnOneErrback, will fire before all the deferreds as appropriate
21958         //
21959         //      list:
21960         //              The list of deferreds to be synchronizied with this DeferredList
21961         //      fireOnOneCallback:
21962         //              Will cause the DeferredLists callback to be fired as soon as any
21963         //              of the deferreds in its list have been fired instead of waiting until
21964         //              the entire list has finished
21965         //      fireonOneErrback:
21966         //              Will cause the errback to fire upon any of the deferreds errback
21967         //      canceller:
21968         //              A deferred canceller function, see dojo.Deferred
21969         var resultList = [];
21970         dojo.Deferred.call(this);
21971         var self = this;
21972         if(list.length === 0 && !fireOnOneCallback){
21973                 this.resolve([0, []]);
21974         }
21975         var finished = 0;
21976         dojo.forEach(list, function(item, i){
21977                 item.then(function(result){
21978                         if(fireOnOneCallback){
21979                                 self.resolve([i, result]);
21980                         }else{
21981                                 addResult(true, result);
21982                         }
21983                 },function(error){
21984                         if(fireOnOneErrback){
21985                                 self.reject(error);
21986                         }else{
21987                                 addResult(false, error);
21988                         }
21989                         if(consumeErrors){
21990                                 return null;
21991                         }
21992                         throw error;
21993                 });
21994                 function addResult(succeeded, result){
21995                         resultList[i] = [succeeded, result];
21996                         finished++;
21997                         if(finished === list.length){
21998                                 self.resolve(resultList);
21999                         }
22000                         
22001                 }
22002         });
22003 };
22004 dojo.DeferredList.prototype = new dojo.Deferred();
22005
22006 dojo.DeferredList.prototype.gatherResults= function(deferredList){
22007         // summary:
22008         //      Gathers the results of the deferreds for packaging
22009         //      as the parameters to the Deferred Lists' callback
22010
22011         var d = new dojo.DeferredList(deferredList, false, true, false);
22012         d.addCallback(function(results){
22013                 var ret = [];
22014                 dojo.forEach(results, function(result){
22015                         ret.push(result[1]);
22016                 });
22017                 return ret;
22018         });
22019         return d;
22020 };
22021
22022 }
22023
22024 if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22025 dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
22026 dojo.provide("dijit.tree.TreeStoreModel");
22027
22028
22029 dojo.declare(
22030                 "dijit.tree.TreeStoreModel",
22031                 null,
22032         {
22033                 // summary:
22034                 //              Implements dijit.Tree.model connecting to a store with a single
22035                 //              root item.  Any methods passed into the constructor will override
22036                 //              the ones defined here.
22037
22038                 // store: dojo.data.Store
22039                 //              Underlying store
22040                 store: null,
22041
22042                 // childrenAttrs: String[]
22043                 //              One or more attribute names (attributes in the dojo.data item) that specify that item's children
22044                 childrenAttrs: ["children"],
22045
22046                 // newItemIdAttr: String
22047                 //              Name of attribute in the Object passed to newItem() that specifies the id.
22048                 //
22049                 //              If newItemIdAttr is set then it's used when newItem() is called to see if an
22050                 //              item with the same id already exists, and if so just links to the old item
22051                 //              (so that the old item ends up with two parents).
22052                 //
22053                 //              Setting this to null or "" will make every drop create a new item.
22054                 newItemIdAttr: "id",
22055
22056                 // labelAttr: String
22057                 //              If specified, get label for tree node from this attribute, rather
22058                 //              than by calling store.getLabel()
22059                 labelAttr: "",
22060
22061                 // root: [readonly] dojo.data.Item
22062                 //              Pointer to the root item (read only, not a parameter)
22063                 root: null,
22064
22065                 // query: anything
22066                 //              Specifies datastore query to return the root item for the tree.
22067                 //              Must only return a single item.   Alternately can just pass in pointer
22068                 //              to root item.
22069                 // example:
22070                 //      |       {id:'ROOT'}
22071                 query: null,
22072
22073                 // deferItemLoadingUntilExpand: Boolean
22074                 //              Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
22075                 //              until they are expanded. This allows for lazying loading where only one
22076                 //              loadItem (and generally one network call, consequently) per expansion
22077                 //              (rather than one for each child).
22078                 //              This relies on partial loading of the children items; each children item of a
22079                 //              fully loaded item should contain the label and info about having children.
22080                 deferItemLoadingUntilExpand: false,
22081
22082                 constructor: function(/* Object */ args){
22083                         // summary:
22084                         //              Passed the arguments listed above (store, etc)
22085                         // tags:
22086                         //              private
22087
22088                         dojo.mixin(this, args);
22089
22090                         this.connects = [];
22091
22092                         var store = this.store;
22093                         if(!store.getFeatures()['dojo.data.api.Identity']){
22094                                 throw new Error("dijit.Tree: store must support dojo.data.Identity");
22095                         }
22096
22097                         // if the store supports Notification, subscribe to the notification events
22098                         if(store.getFeatures()['dojo.data.api.Notification']){
22099                                 this.connects = this.connects.concat([
22100                                         dojo.connect(store, "onNew", this, "onNewItem"),
22101                                         dojo.connect(store, "onDelete", this, "onDeleteItem"),
22102                                         dojo.connect(store, "onSet", this, "onSetItem")
22103                                 ]);
22104                         }
22105                 },
22106
22107                 destroy: function(){
22108                         dojo.forEach(this.connects, dojo.disconnect);
22109                         // TODO: should cancel any in-progress processing of getRoot(), getChildren()
22110                 },
22111
22112                 // =======================================================================
22113                 // Methods for traversing hierarchy
22114
22115                 getRoot: function(onItem, onError){
22116                         // summary:
22117                         //              Calls onItem with the root item for the tree, possibly a fabricated item.
22118                         //              Calls onError on error.
22119                         if(this.root){
22120                                 onItem(this.root);
22121                         }else{
22122                                 this.store.fetch({
22123                                         query: this.query,
22124                                         onComplete: dojo.hitch(this, function(items){
22125                                                 if(items.length != 1){
22126                                                         throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
22127                                                                 " items, but must return exactly one item");
22128                                                 }
22129                                                 this.root = items[0];
22130                                                 onItem(this.root);
22131                                         }),
22132                                         onError: onError
22133                                 });
22134                         }
22135                 },
22136
22137                 mayHaveChildren: function(/*dojo.data.Item*/ item){
22138                         // summary:
22139                         //              Tells if an item has or may have children.  Implementing logic here
22140                         //              avoids showing +/- expando icon for nodes that we know don't have children.
22141                         //              (For efficiency reasons we may not want to check if an element actually
22142                         //              has children until user clicks the expando node)
22143                         return dojo.some(this.childrenAttrs, function(attr){
22144                                 return this.store.hasAttribute(item, attr);
22145                         }, this);
22146                 },
22147
22148                 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
22149                         // summary:
22150                         //              Calls onComplete() with array of child items of given parent item, all loaded.
22151
22152                         var store = this.store;
22153                         if(!store.isItemLoaded(parentItem)){
22154                                 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
22155                                 // mode, so we will load it and just return the children (without loading each
22156                                 // child item)
22157                                 var getChildren = dojo.hitch(this, arguments.callee);
22158                                 store.loadItem({
22159                                         item: parentItem,
22160                                         onItem: function(parentItem){
22161                                                 getChildren(parentItem, onComplete, onError);
22162                                         },
22163                                         onError: onError
22164                                 });
22165                                 return;
22166                         }
22167                         // get children of specified item
22168                         var childItems = [];
22169                         for(var i=0; i<this.childrenAttrs.length; i++){
22170                                 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
22171                                 childItems = childItems.concat(vals);
22172                         }
22173
22174                         // count how many items need to be loaded
22175                         var _waitCount = 0;
22176                         if(!this.deferItemLoadingUntilExpand){
22177                                 dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
22178                         }
22179
22180                         if(_waitCount == 0){
22181                                 // all items are already loaded (or we aren't loading them).  proceed...
22182                                 onComplete(childItems);
22183                         }else{
22184                                 // still waiting for some or all of the items to load
22185                                 dojo.forEach(childItems, function(item, idx){
22186                                         if(!store.isItemLoaded(item)){
22187                                                 store.loadItem({
22188                                                         item: item,
22189                                                         onItem: function(item){
22190                                                                 childItems[idx] = item;
22191                                                                 if(--_waitCount == 0){
22192                                                                         // all nodes have been loaded, send them to the tree
22193                                                                         onComplete(childItems);
22194                                                                 }
22195                                                         },
22196                                                         onError: onError
22197                                                 });
22198                                         }
22199                                 });
22200                         }
22201                 },
22202
22203                 // =======================================================================
22204                 // Inspecting items
22205
22206                 isItem: function(/* anything */ something){
22207                         return this.store.isItem(something);    // Boolean
22208                 },
22209
22210                 fetchItemByIdentity: function(/* object */ keywordArgs){
22211                         this.store.fetchItemByIdentity(keywordArgs);
22212                 },
22213
22214                 getIdentity: function(/* item */ item){
22215                         return this.store.getIdentity(item);    // Object
22216                 },
22217
22218                 getLabel: function(/*dojo.data.Item*/ item){
22219                         // summary:
22220                         //              Get the label for an item
22221                         if(this.labelAttr){
22222                                 return this.store.getValue(item,this.labelAttr);        // String
22223                         }else{
22224                                 return this.store.getLabel(item);       // String
22225                         }
22226                 },
22227
22228                 // =======================================================================
22229                 // Write interface
22230
22231                 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
22232                         // summary:
22233                         //              Creates a new item.   See `dojo.data.api.Write` for details on args.
22234                         //              Used in drag & drop when item from external source dropped onto tree.
22235                         // description:
22236                         //              Developers will need to override this method if new items get added
22237                         //              to parents with multiple children attributes, in order to define which
22238                         //              children attribute points to the new item.
22239
22240                         var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
22241
22242                         if(this.newItemIdAttr && args[this.newItemIdAttr]){
22243                                 // Maybe there's already a corresponding item in the store; if so, reuse it.
22244                                 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
22245                                         if(item){
22246                                                 // There's already a matching item in store, use it
22247                                                 this.pasteItem(item, null, parent, true, insertIndex);
22248                                         }else{
22249                                                 // Create new item in the tree, based on the drag source.
22250                                                 LnewItem=this.store.newItem(args, pInfo);
22251                                                 if (LnewItem && (insertIndex!=undefined)){
22252                                                         // Move new item to desired position
22253                                                         this.pasteItem(LnewItem, parent, parent, false, insertIndex);
22254                                                 }
22255                                         }
22256                                 }});
22257                         }else{
22258                                 // [as far as we know] there is no id so we must assume this is a new item
22259                                 LnewItem=this.store.newItem(args, pInfo);
22260                                 if (LnewItem && (insertIndex!=undefined)){
22261                                         // Move new item to desired position
22262                                         this.pasteItem(LnewItem, parent, parent, false, insertIndex);
22263                                 }
22264                         }
22265                 },
22266
22267                 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
22268                         // summary:
22269                         //              Move or copy an item from one parent item to another.
22270                         //              Used in drag & drop
22271                         var store = this.store,
22272                                 parentAttr = this.childrenAttrs[0];     // name of "children" attr in parent item
22273
22274                         // remove child from source item, and record the attribute that child occurred in
22275                         if(oldParentItem){
22276                                 dojo.forEach(this.childrenAttrs, function(attr){
22277                                         if(store.containsValue(oldParentItem, attr, childItem)){
22278                                                 if(!bCopy){
22279                                                         var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){
22280                                                                 return x != childItem;
22281                                                         });
22282                                                         store.setValues(oldParentItem, attr, values);
22283                                                 }
22284                                                 parentAttr = attr;
22285                                         }
22286                                 });
22287                         }
22288
22289                         // modify target item's children attribute to include this item
22290                         if(newParentItem){
22291                                 if(typeof insertIndex == "number"){
22292                                         // call slice() to avoid modifying the original array, confusing the data store
22293                                         var childItems = store.getValues(newParentItem, parentAttr).slice();
22294                                         childItems.splice(insertIndex, 0, childItem);
22295                                         store.setValues(newParentItem, parentAttr, childItems);
22296                                 }else{
22297                                         store.setValues(newParentItem, parentAttr,
22298                                                 store.getValues(newParentItem, parentAttr).concat(childItem));
22299                                 }
22300                         }
22301                 },
22302
22303                 // =======================================================================
22304                 // Callbacks
22305
22306                 onChange: function(/*dojo.data.Item*/ item){
22307                         // summary:
22308                         //              Callback whenever an item has changed, so that Tree
22309                         //              can update the label, icon, etc.   Note that changes
22310                         //              to an item's children or parent(s) will trigger an
22311                         //              onChildrenChange() so you can ignore those changes here.
22312                         // tags:
22313                         //              callback
22314                 },
22315
22316                 onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
22317                         // summary:
22318                         //              Callback to do notifications about new, updated, or deleted items.
22319                         // tags:
22320                         //              callback
22321                 },
22322
22323                 onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
22324                         // summary:
22325                         //              Callback when an item has been deleted.
22326                         // description:
22327                         //              Note that there will also be an onChildrenChange() callback for the parent
22328                         //              of this item.
22329                         // tags:
22330                         //              callback
22331                 },
22332
22333                 // =======================================================================
22334                 // Events from data store
22335
22336                 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
22337                         // summary:
22338                         //              Handler for when new items appear in the store, either from a drop operation
22339                         //              or some other way.   Updates the tree view (if necessary).
22340                         // description:
22341                         //              If the new item is a child of an existing item,
22342                         //              calls onChildrenChange() with the new list of children
22343                         //              for that existing item.
22344                         //
22345                         // tags:
22346                         //              extension
22347
22348                         // We only care about the new item if it has a parent that corresponds to a TreeNode
22349                         // we are currently displaying
22350                         if(!parentInfo){
22351                                 return;
22352                         }
22353
22354                         // Call onChildrenChange() on parent (ie, existing) item with new list of children
22355                         // In the common case, the new list of children is simply parentInfo.newValue or
22356                         // [ parentInfo.newValue ], although if items in the store has multiple
22357                         // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
22358                         // so call getChildren() to be sure to get right answer.
22359                         this.getChildren(parentInfo.item, dojo.hitch(this, function(children){
22360                                 this.onChildrenChange(parentInfo.item, children);
22361                         }));
22362                 },
22363
22364                 onDeleteItem: function(/*Object*/ item){
22365                         // summary:
22366                         //              Handler for delete notifications from underlying store
22367                         this.onDelete(item);
22368                 },
22369
22370                 onSetItem: function(/* item */ item,
22371                                                 /* attribute-name-string */ attribute,
22372                                                 /* object | array */ oldValue,
22373                                                 /* object | array */ newValue){
22374                         // summary:
22375                         //              Updates the tree view according to changes in the data store.
22376                         // description:
22377                         //              Handles updates to an item's children by calling onChildrenChange(), and
22378                         //              other updates to an item by calling onChange().
22379                         //
22380                         //              See `onNewItem` for more details on handling updates to an item's children.
22381                         // tags:
22382                         //              extension
22383
22384                         if(dojo.indexOf(this.childrenAttrs, attribute) != -1){
22385                                 // item's children list changed
22386                                 this.getChildren(item, dojo.hitch(this, function(children){
22387                                         // See comments in onNewItem() about calling getChildren()
22388                                         this.onChildrenChange(item, children);
22389                                 }));
22390                         }else{
22391                                 // item's label/icon/etc. changed.
22392                                 this.onChange(item);
22393                         }
22394                 }
22395         });
22396
22397 }
22398
22399 if(!dojo._hasResource["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22400 dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
22401 dojo.provide("dijit.tree.ForestStoreModel");
22402
22403
22404
22405 dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
22406         // summary:
22407         //              Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
22408         //              a.k.a. a store that has multiple "top level" items.
22409         //
22410         // description
22411         //              Use this class to wrap a dojo.data store, making all the items matching the specified query
22412         //              appear as children of a fabricated "root item".  If no query is specified then all the
22413         //              items returned by fetch() on the underlying store become children of the root item.
22414         //              This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
22415         //
22416         //              When using this class the developer must override a number of methods according to their app and
22417         //              data, including:
22418         //                      - onNewRootItem
22419         //                      - onAddToRoot
22420         //                      - onLeaveRoot
22421         //                      - onNewItem
22422         //                      - onSetItem
22423
22424         // Parameters to constructor
22425
22426         // rootId: String
22427         //              ID of fabricated root item
22428         rootId: "$root$",
22429
22430         // rootLabel: String
22431         //              Label of fabricated root item
22432         rootLabel: "ROOT",
22433
22434         // query: String
22435         //              Specifies the set of children of the root item.
22436         // example:
22437         //      |       {type:'continent'}
22438         query: null,
22439
22440         // End of parameters to constructor
22441
22442         constructor: function(params){
22443                 // summary:
22444                 //              Sets up variables, etc.
22445                 // tags:
22446                 //              private
22447
22448                 // Make dummy root item
22449                 this.root = {
22450                         store: this,
22451                         root: true,
22452                         id: params.rootId,
22453                         label: params.rootLabel,
22454                         children: params.rootChildren   // optional param
22455                 };
22456         },
22457
22458         // =======================================================================
22459         // Methods for traversing hierarchy
22460
22461         mayHaveChildren: function(/*dojo.data.Item*/ item){
22462                 // summary:
22463                 //              Tells if an item has or may have children.  Implementing logic here
22464                 //              avoids showing +/- expando icon for nodes that we know don't have children.
22465                 //              (For efficiency reasons we may not want to check if an element actually
22466                 //              has children until user clicks the expando node)
22467                 // tags:
22468                 //              extension
22469                 return item === this.root || this.inherited(arguments);
22470         },
22471
22472         getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
22473                 // summary:
22474                 //              Calls onComplete() with array of child items of given parent item, all loaded.
22475                 if(parentItem === this.root){
22476                         if(this.root.children){
22477                                 // already loaded, just return
22478                                 callback(this.root.children);
22479                         }else{
22480                                 this.store.fetch({
22481                                         query: this.query,
22482                                         onComplete: dojo.hitch(this, function(items){
22483                                                 this.root.children = items;
22484                                                 callback(items);
22485                                         }),
22486                                         onError: onError
22487                                 });
22488                         }
22489                 }else{
22490                         this.inherited(arguments);
22491                 }
22492         },
22493
22494         // =======================================================================
22495         // Inspecting items
22496
22497         isItem: function(/* anything */ something){
22498                 return (something === this.root) ? true : this.inherited(arguments);
22499         },
22500
22501         fetchItemByIdentity: function(/* object */ keywordArgs){
22502                 if(keywordArgs.identity == this.root.id){
22503                         var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
22504                         if(keywordArgs.onItem){
22505                                 keywordArgs.onItem.call(scope, this.root);
22506                         }
22507                 }else{
22508                         this.inherited(arguments);
22509                 }
22510         },
22511
22512         getIdentity: function(/* item */ item){
22513                 return (item === this.root) ? this.root.id : this.inherited(arguments);
22514         },
22515
22516         getLabel: function(/* item */ item){
22517                 return  (item === this.root) ? this.root.label : this.inherited(arguments);
22518         },
22519
22520         // =======================================================================
22521         // Write interface
22522
22523         newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
22524                 // summary:
22525                 //              Creates a new item.   See dojo.data.api.Write for details on args.
22526                 //              Used in drag & drop when item from external source dropped onto tree.
22527                 if(parent === this.root){
22528                         this.onNewRootItem(args);
22529                         return this.store.newItem(args);
22530                 }else{
22531                         return this.inherited(arguments);
22532                 }
22533         },
22534
22535         onNewRootItem: function(args){
22536                 // summary:
22537                 //              User can override this method to modify a new element that's being
22538                 //              added to the root of the tree, for example to add a flag like root=true
22539         },
22540
22541         pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
22542                 // summary:
22543                 //              Move or copy an item from one parent item to another.
22544                 //              Used in drag & drop
22545                 if(oldParentItem === this.root){
22546                         if(!bCopy){
22547                                 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
22548                                 // this.query... thus triggering an onChildrenChange() event to notify the Tree
22549                                 // that this element is no longer a child of the root node
22550                                 this.onLeaveRoot(childItem);
22551                         }
22552                 }
22553                 dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
22554                         oldParentItem === this.root ? null : oldParentItem,
22555                         newParentItem === this.root ? null : newParentItem,
22556                         bCopy,
22557                         insertIndex
22558                 );
22559                 if(newParentItem === this.root){
22560                         // It's onAddToRoot()'s responsibility to modify the item so it matches
22561                         // this.query... thus triggering an onChildrenChange() event to notify the Tree
22562                         // that this element is now a child of the root node
22563                         this.onAddToRoot(childItem);
22564                 }
22565         },
22566
22567         // =======================================================================
22568         // Handling for top level children
22569
22570         onAddToRoot: function(/* item */ item){
22571                 // summary:
22572                 //              Called when item added to root of tree; user must override this method
22573                 //              to modify the item so that it matches the query for top level items
22574                 // example:
22575                 //      |       store.setValue(item, "root", true);
22576                 // tags:
22577                 //              extension
22578                 console.log(this, ": item ", item, " added to root");
22579         },
22580
22581         onLeaveRoot: function(/* item */ item){
22582                 // summary:
22583                 //              Called when item removed from root of tree; user must override this method
22584                 //              to modify the item so it doesn't match the query for top level items
22585                 // example:
22586                 //      |       store.unsetAttribute(item, "root");
22587                 // tags:
22588                 //              extension
22589                 console.log(this, ": item ", item, " removed from root");
22590         },
22591
22592         // =======================================================================
22593         // Events from data store
22594
22595         _requeryTop: function(){
22596                 // reruns the query for the children of the root node,
22597                 // sending out an onSet notification if those children have changed
22598                 var oldChildren = this.root.children || [];
22599                 this.store.fetch({
22600                         query: this.query,
22601                         onComplete: dojo.hitch(this, function(newChildren){
22602                                 this.root.children = newChildren;
22603
22604                                 // If the list of children or the order of children has changed...
22605                                 if(oldChildren.length != newChildren.length ||
22606                                         dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
22607                                         this.onChildrenChange(this.root, newChildren);
22608                                 }
22609                         })
22610                 });
22611         },
22612
22613         onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
22614                 // summary:
22615                 //              Handler for when new items appear in the store.  Developers should override this
22616                 //              method to be more efficient based on their app/data.
22617                 // description:
22618                 //              Note that the default implementation requeries the top level items every time
22619                 //              a new item is created, since any new item could be a top level item (even in
22620                 //              addition to being a child of another item, since items can have multiple parents).
22621                 //
22622                 //              If developers can detect which items are possible top level items (based on the item and the
22623                 //              parentInfo parameters), they should override this method to only call _requeryTop() for top
22624                 //              level items.  Often all top level items have parentInfo==null, but
22625                 //              that will depend on which store you use and what your data is like.
22626                 // tags:
22627                 //              extension
22628                 this._requeryTop();
22629
22630                 this.inherited(arguments);
22631         },
22632
22633         onDeleteItem: function(/*Object*/ item){
22634                 // summary:
22635                 //              Handler for delete notifications from underlying store
22636
22637                 // check if this was a child of root, and if so send notification that root's children
22638                 // have changed
22639                 if(dojo.indexOf(this.root.children, item) != -1){
22640                         this._requeryTop();
22641                 }
22642
22643                 this.inherited(arguments);
22644         },
22645
22646         onSetItem: function(/* item */ item,
22647                                         /* attribute-name-string */ attribute,
22648                                         /* object | array */ oldValue,
22649                                         /* object | array */ newValue){
22650                 // summary:
22651                 //              Updates the tree view according to changes to an item in the data store.
22652                 //              Developers should override this method to be more efficient based on their app/data.
22653                 // description:
22654                 //              Handles updates to an item's children by calling onChildrenChange(), and
22655                 //              other updates to an item by calling onChange().
22656                 //
22657                 //              Also, any change to any item re-executes the query for the tree's top-level items,
22658                 //              since this modified item may have started/stopped matching the query for top level items.
22659                 //
22660                 //              If possible, developers should override this function to only call _requeryTop() when
22661                 //              the change to the item has caused it to stop/start being a top level item in the tree.
22662                 // tags:
22663                 //              extension
22664
22665                 this._requeryTop();
22666                 this.inherited(arguments);
22667         }
22668
22669 });
22670
22671 }
22672
22673 if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
22674 dojo._hasResource["dojo.dnd.Container"] = true;
22675 dojo.provide("dojo.dnd.Container");
22676
22677
22678
22679
22680 /*
22681         Container states:
22682                 ""              - normal state
22683                 "Over"  - mouse over a container
22684         Container item states:
22685                 ""              - normal state
22686                 "Over"  - mouse over a container item
22687 */
22688
22689 /*=====
22690 dojo.declare("dojo.dnd.__ContainerArgs", [], {
22691         creator: function(){
22692                 // summary:
22693                 //              a creator function, which takes a data item, and returns an object like that:
22694                 //              {node: newNode, data: usedData, type: arrayOfStrings}
22695         },
22696
22697         // skipForm: Boolean
22698         //              don't start the drag operation, if clicked on form elements
22699         skipForm: false,
22700
22701         // dropParent: Node||String
22702         //              node or node's id to use as the parent node for dropped items
22703         //              (must be underneath the 'node' parameter in the DOM)
22704         dropParent: null,
22705
22706         // _skipStartup: Boolean
22707         //              skip startup(), which collects children, for deferred initialization
22708         //              (this is used in the markup mode)
22709         _skipStartup: false
22710 });
22711
22712 dojo.dnd.Item = function(){
22713         // summary:
22714         //              Represents (one of) the source node(s) being dragged.
22715         //              Contains (at least) the "type" and "data" attributes.
22716         // type: String[]
22717         //              Type(s) of this item, by default this is ["text"]
22718         // data: Object
22719         //              Logical representation of the object being dragged.
22720         //              If the drag object's type is "text" then data is a String,
22721         //              if it's another type then data could be a different Object,
22722         //              perhaps a name/value hash.
22723         
22724         this.type = type;
22725         this.data = data;
22726 }
22727 =====*/
22728
22729 dojo.declare("dojo.dnd.Container", null, {
22730         // summary:
22731         //              a Container object, which knows when mouse hovers over it,
22732         //              and over which element it hovers
22733         
22734         // object attributes (for markup)
22735         skipForm: false,
22736         
22737         /*=====
22738         // current: DomNode
22739         //              The DOM node the mouse is currently hovered over
22740         current: null,
22741         
22742         // map: Hash<String, dojo.dnd.Item>
22743         //              Map from an item's id (which is also the DOMNode's id) to
22744         //              the dojo.dnd.Item itself.
22745         map: {},
22746         =====*/
22747         
22748         constructor: function(node, params){
22749                 // summary:
22750                 //              a constructor of the Container
22751                 // node: Node
22752                 //              node or node's id to build the container on
22753                 // params: dojo.dnd.__ContainerArgs
22754                 //              a dictionary of parameters
22755                 this.node = dojo.byId(node);
22756                 if(!params){ params = {}; }
22757                 this.creator = params.creator || null;
22758                 this.skipForm = params.skipForm;
22759                 this.parent = params.dropParent && dojo.byId(params.dropParent);
22760                 
22761                 // class-specific variables
22762                 this.map = {};
22763                 this.current = null;
22764
22765                 // states
22766                 this.containerState = "";
22767                 dojo.addClass(this.node, "dojoDndContainer");
22768                 
22769                 // mark up children
22770                 if(!(params && params._skipStartup)){
22771                         this.startup();
22772                 }
22773
22774                 // set up events
22775                 this.events = [
22776                         dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
22777                         dojo.connect(this.node, "onmouseout",  this, "onMouseOut"),
22778                         // cancel text selection and text dragging
22779                         dojo.connect(this.node, "ondragstart",   this, "onSelectStart"),
22780                         dojo.connect(this.node, "onselectstart", this, "onSelectStart")
22781                 ];
22782         },
22783         
22784         // object attributes (for markup)
22785         creator: function(){
22786                 // summary:
22787                 //              creator function, dummy at the moment
22788         },
22789         
22790         // abstract access to the map
22791         getItem: function(/*String*/ key){
22792                 // summary:
22793                 //              returns a data item by its key (id)
22794                 return this.map[key];   // dojo.dnd.Item
22795         },
22796         setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
22797                 // summary:
22798                 //              associates a data item with its key (id)
22799                 this.map[key] = data;
22800         },
22801         delItem: function(/*String*/ key){
22802                 // summary:
22803                 //              removes a data item from the map by its key (id)
22804                 delete this.map[key];
22805         },
22806         forInItems: function(/*Function*/ f, /*Object?*/ o){
22807                 // summary:
22808                 //              iterates over a data map skipping members that
22809                 //              are present in the empty object (IE and/or 3rd-party libraries).
22810                 o = o || dojo.global;
22811                 var m = this.map, e = dojo.dnd._empty;
22812                 for(var i in m){
22813                         if(i in e){ continue; }
22814                         f.call(o, m[i], i, this);
22815                 }
22816                 return o;       // Object
22817         },
22818         clearItems: function(){
22819                 // summary:
22820                 //              removes all data items from the map
22821                 this.map = {};
22822         },
22823         
22824         // methods
22825         getAllNodes: function(){
22826                 // summary:
22827                 //              returns a list (an array) of all valid child nodes
22828                 return dojo.query("> .dojoDndItem", this.parent);       // NodeList
22829         },
22830         sync: function(){
22831                 // summary:
22832                 //              sync up the node list with the data map
22833                 var map = {};
22834                 this.getAllNodes().forEach(function(node){
22835                         if(node.id){
22836                                 var item = this.getItem(node.id);
22837                                 if(item){
22838                                         map[node.id] = item;
22839                                         return;
22840                                 }
22841                         }else{
22842                                 node.id = dojo.dnd.getUniqueId();
22843                         }
22844                         var type = node.getAttribute("dndType"),
22845                                 data = node.getAttribute("dndData");
22846                         map[node.id] = {
22847                                 data: data || node.innerHTML,
22848                                 type: type ? type.split(/\s*,\s*/) : ["text"]
22849                         };
22850                 }, this);
22851                 this.map = map;
22852                 return this;    // self
22853         },
22854         insertNodes: function(data, before, anchor){
22855                 // summary:
22856                 //              inserts an array of new nodes before/after an anchor node
22857                 // data: Array
22858                 //              a list of data items, which should be processed by the creator function
22859                 // before: Boolean
22860                 //              insert before the anchor, if true, and after the anchor otherwise
22861                 // anchor: Node
22862                 //              the anchor node to be used as a point of insertion
22863                 if(!this.parent.firstChild){
22864                         anchor = null;
22865                 }else if(before){
22866                         if(!anchor){
22867                                 anchor = this.parent.firstChild;
22868                         }
22869                 }else{
22870                         if(anchor){
22871                                 anchor = anchor.nextSibling;
22872                         }
22873                 }
22874                 if(anchor){
22875                         for(var i = 0; i < data.length; ++i){
22876                                 var t = this._normalizedCreator(data[i]);
22877                                 this.setItem(t.node.id, {data: t.data, type: t.type});
22878                                 this.parent.insertBefore(t.node, anchor);
22879                         }
22880                 }else{
22881                         for(var i = 0; i < data.length; ++i){
22882                                 var t = this._normalizedCreator(data[i]);
22883                                 this.setItem(t.node.id, {data: t.data, type: t.type});
22884                                 this.parent.appendChild(t.node);
22885                         }
22886                 }
22887                 return this;    // self
22888         },
22889         destroy: function(){
22890                 // summary:
22891                 //              prepares this object to be garbage-collected
22892                 dojo.forEach(this.events, dojo.disconnect);
22893                 this.clearItems();
22894                 this.node = this.parent = this.current = null;
22895         },
22896
22897         // markup methods
22898         markupFactory: function(params, node){
22899                 params._skipStartup = true;
22900                 return new dojo.dnd.Container(node, params);
22901         },
22902         startup: function(){
22903                 // summary:
22904                 //              collects valid child items and populate the map
22905                 
22906                 // set up the real parent node
22907                 if(!this.parent){
22908                         // use the standard algorithm, if not assigned
22909                         this.parent = this.node;
22910                         if(this.parent.tagName.toLowerCase() == "table"){
22911                                 var c = this.parent.getElementsByTagName("tbody");
22912                                 if(c && c.length){ this.parent = c[0]; }
22913                         }
22914                 }
22915                 this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
22916
22917                 // process specially marked children
22918                 this.sync();
22919         },
22920
22921         // mouse events
22922         onMouseOver: function(e){
22923                 // summary:
22924                 //              event processor for onmouseover
22925                 // e: Event
22926                 //              mouse event
22927                 var n = e.relatedTarget;
22928                 while(n){
22929                         if(n == this.node){ break; }
22930                         try{
22931                                 n = n.parentNode;
22932                         }catch(x){
22933                                 n = null;
22934                         }
22935                 }
22936                 if(!n){
22937                         this._changeState("Container", "Over");
22938                         this.onOverEvent();
22939                 }
22940                 n = this._getChildByEvent(e);
22941                 if(this.current == n){ return; }
22942                 if(this.current){ this._removeItemClass(this.current, "Over"); }
22943                 if(n){ this._addItemClass(n, "Over"); }
22944                 this.current = n;
22945         },
22946         onMouseOut: function(e){
22947                 // summary:
22948                 //              event processor for onmouseout
22949                 // e: Event
22950                 //              mouse event
22951                 for(var n = e.relatedTarget; n;){
22952                         if(n == this.node){ return; }
22953                         try{
22954                                 n = n.parentNode;
22955                         }catch(x){
22956                                 n = null;
22957                         }
22958                 }
22959                 if(this.current){
22960                         this._removeItemClass(this.current, "Over");
22961                         this.current = null;
22962                 }
22963                 this._changeState("Container", "");
22964                 this.onOutEvent();
22965         },
22966         onSelectStart: function(e){
22967                 // summary:
22968                 //              event processor for onselectevent and ondragevent
22969                 // e: Event
22970                 //              mouse event
22971                 if(!this.skipForm || !dojo.dnd.isFormElement(e)){
22972                         dojo.stopEvent(e);
22973                 }
22974         },
22975         
22976         // utilities
22977         onOverEvent: function(){
22978                 // summary:
22979                 //              this function is called once, when mouse is over our container
22980         },
22981         onOutEvent: function(){
22982                 // summary:
22983                 //              this function is called once, when mouse is out of our container
22984         },
22985         _changeState: function(type, newState){
22986                 // summary:
22987                 //              changes a named state to new state value
22988                 // type: String
22989                 //              a name of the state to change
22990                 // newState: String
22991                 //              new state
22992                 var prefix = "dojoDnd" + type;
22993                 var state  = type.toLowerCase() + "State";
22994                 //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
22995                 dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
22996                 this[state] = newState;
22997         },
22998         _addItemClass: function(node, type){
22999                 // summary:
23000                 //              adds a class with prefix "dojoDndItem"
23001                 // node: Node
23002                 //              a node
23003                 // type: String
23004                 //              a variable suffix for a class name
23005                 dojo.addClass(node, "dojoDndItem" + type);
23006         },
23007         _removeItemClass: function(node, type){
23008                 // summary:
23009                 //              removes a class with prefix "dojoDndItem"
23010                 // node: Node
23011                 //              a node
23012                 // type: String
23013                 //              a variable suffix for a class name
23014                 dojo.removeClass(node, "dojoDndItem" + type);
23015         },
23016         _getChildByEvent: function(e){
23017                 // summary:
23018                 //              gets a child, which is under the mouse at the moment, or null
23019                 // e: Event
23020                 //              a mouse event
23021                 var node = e.target;
23022                 if(node){
23023                         for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
23024                                 if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
23025                         }
23026                 }
23027                 return null;
23028         },
23029         _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
23030                 // summary:
23031                 //              adds all necessary data to the output of the user-supplied creator function
23032                 var t = (this.creator || this.defaultCreator).call(this, item, hint);
23033                 if(!dojo.isArray(t.type)){ t.type = ["text"]; }
23034                 if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
23035                 dojo.addClass(t.node, "dojoDndItem");
23036                 return t;
23037         }
23038 });
23039
23040 dojo.dnd._createNode = function(tag){
23041         // summary:
23042         //              returns a function, which creates an element of given tag
23043         //              (SPAN by default) and sets its innerHTML to given text
23044         // tag: String
23045         //              a tag name or empty for SPAN
23046         if(!tag){ return dojo.dnd._createSpan; }
23047         return function(text){  // Function
23048                 return dojo.create(tag, {innerHTML: text});     // Node
23049         };
23050 };
23051
23052 dojo.dnd._createTrTd = function(text){
23053         // summary:
23054         //              creates a TR/TD structure with given text as an innerHTML of TD
23055         // text: String
23056         //              a text for TD
23057         var tr = dojo.create("tr");
23058         dojo.create("td", {innerHTML: text}, tr);
23059         return tr;      // Node
23060 };
23061
23062 dojo.dnd._createSpan = function(text){
23063         // summary:
23064         //              creates a SPAN element with given text as its innerHTML
23065         // text: String
23066         //              a text for SPAN
23067         return dojo.create("span", {innerHTML: text});  // Node
23068 };
23069
23070 // dojo.dnd._defaultCreatorNodes: Object
23071 //              a dictionary that maps container tag names to child tag names
23072 dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
23073
23074 dojo.dnd._defaultCreator = function(node){
23075         // summary:
23076         //              takes a parent node, and returns an appropriate creator function
23077         // node: Node
23078         //              a container node
23079         var tag = node.tagName.toLowerCase();
23080         var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
23081                         dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
23082         return function(item, hint){    // Function
23083                 var isObj = item && dojo.isObject(item), data, type, n;
23084                 if(isObj && item.tagName && item.nodeType && item.getAttribute){
23085                         // process a DOM node
23086                         data = item.getAttribute("dndData") || item.innerHTML;
23087                         type = item.getAttribute("dndType");
23088                         type = type ? type.split(/\s*,\s*/) : ["text"];
23089                         n = item;       // this node is going to be moved rather than copied
23090                 }else{
23091                         // process a DnD item object or a string
23092                         data = (isObj && item.data) ? item.data : item;
23093                         type = (isObj && item.type) ? item.type : ["text"];
23094                         n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
23095                 }
23096                 if(!n.id){
23097                         n.id = dojo.dnd.getUniqueId();
23098                 }
23099                 return {node: n, data: data, type: type};
23100         };
23101 };
23102
23103 }
23104
23105 if(!dojo._hasResource["dijit.tree._dndContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23106 dojo._hasResource["dijit.tree._dndContainer"] = true;
23107 dojo.provide("dijit.tree._dndContainer");
23108
23109
23110
23111
23112 dojo.getObject("tree", true, dojo);
23113
23114 dijit.tree._compareNodes = function(n1, n2){
23115         if(n1 === n2){
23116                 return 0;
23117         }
23118         
23119         if('sourceIndex' in document.documentElement){ //IE
23120                 //TODO: does not yet work if n1 and/or n2 is a text node
23121                 return n1.sourceIndex - n2.sourceIndex;
23122         }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
23123                 return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
23124         }else if(document.createRange){ //Webkit
23125                 var r1 = doc.createRange();
23126                 r1.setStartBefore(n1);
23127
23128                 var r2 = doc.createRange();
23129                 r2.setStartBefore(n2);
23130
23131                 return r1.compareBoundaryPoints(r1.END_TO_END, r2);
23132         }else{
23133                 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
23134         }
23135 };
23136
23137 dojo.declare("dijit.tree._dndContainer",
23138         null,
23139         {
23140
23141                 // summary:
23142                 //              This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
23143                 //              It's modeled after `dojo.dnd.Container`.
23144                 // tags:
23145                 //              protected
23146
23147                 /*=====
23148                 // current: DomNode
23149                 //              The currently hovered TreeNode.rowNode (which is the DOM node
23150                 //              associated w/a given node in the tree, excluding it's descendants)
23151                 current: null,
23152                 =====*/
23153
23154                 constructor: function(tree, params){
23155                         // summary:
23156                         //              A constructor of the Container
23157                         // tree: Node
23158                         //              Node or node's id to build the container on
23159                         // params: dijit.tree.__SourceArgs
23160                         //              A dict of parameters, which gets mixed into the object
23161                         // tags:
23162                         //              private
23163                         this.tree = tree;
23164                         this.node = tree.domNode;       // TODO: rename; it's not a TreeNode but the whole Tree
23165                         dojo.mixin(this, params);
23166
23167                         // class-specific variables
23168                         this.map = {};
23169                         this.current = null;    // current TreeNode's DOM node
23170
23171                         // states
23172                         this.containerState = "";
23173                         dojo.addClass(this.node, "dojoDndContainer");
23174
23175                         // set up events
23176                         this.events = [
23177                                 // container level events
23178                                 dojo.connect(this.node, "onmouseenter", this, "onOverEvent"),
23179                                 dojo.connect(this.node, "onmouseleave", this, "onOutEvent"),
23180
23181                                 // switching between TreeNodes
23182                                 dojo.connect(this.tree, "_onNodeMouseEnter", this, "onMouseOver"),
23183                                 dojo.connect(this.tree, "_onNodeMouseLeave", this, "onMouseOut"),
23184
23185                                 // cancel text selection and text dragging
23186                                 dojo.connect(this.node, "ondragstart", dojo, "stopEvent"),
23187                                 dojo.connect(this.node, "onselectstart", dojo, "stopEvent")
23188                         ];
23189                 },
23190
23191                 getItem: function(/*String*/ key){
23192                         // summary:
23193                         //              Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
23194                         //              Called by dojo.dnd.Source.checkAcceptance().
23195                         // tags:
23196                         //              protected
23197
23198                         var widget = this.selection[key],
23199                                 ret = {
23200                                         data: widget,
23201                                         type: ["treeNode"]
23202                                 };
23203
23204                         return ret;     // dojo.dnd.Item
23205                 },
23206
23207                 destroy: function(){
23208                         // summary:
23209                         //              Prepares this object to be garbage-collected
23210
23211                         dojo.forEach(this.events, dojo.disconnect);
23212                         // this.clearItems();
23213                         this.node = this.parent = null;
23214                 },
23215
23216                 // mouse events
23217                 onMouseOver: function(/*TreeNode*/ widget, /*Event*/ evt){
23218                         // summary:
23219                         //              Called when mouse is moved over a TreeNode
23220                         // tags:
23221                         //              protected
23222                         this.current = widget;
23223                 },
23224
23225                 onMouseOut: function(/*TreeNode*/ widget, /*Event*/ evt){
23226                         // summary:
23227                         //              Called when mouse is moved away from a TreeNode
23228                         // tags:
23229                         //              protected
23230                         this.current = null;
23231                 },
23232
23233                 _changeState: function(type, newState){
23234                         // summary:
23235                         //              Changes a named state to new state value
23236                         // type: String
23237                         //              A name of the state to change
23238                         // newState: String
23239                         //              new state
23240                         var prefix = "dojoDnd" + type;
23241                         var state = type.toLowerCase() + "State";
23242                         //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
23243                         dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
23244                         this[state] = newState;
23245                 },
23246
23247                 _addItemClass: function(node, type){
23248                         // summary:
23249                         //              Adds a class with prefix "dojoDndItem"
23250                         // node: Node
23251                         //              A node
23252                         // type: String
23253                         //              A variable suffix for a class name
23254                         dojo.addClass(node, "dojoDndItem" + type);
23255                 },
23256
23257                 _removeItemClass: function(node, type){
23258                         // summary:
23259                         //              Removes a class with prefix "dojoDndItem"
23260                         // node: Node
23261                         //              A node
23262                         // type: String
23263                         //              A variable suffix for a class name
23264                         dojo.removeClass(node, "dojoDndItem" + type);
23265                 },
23266
23267                 onOverEvent: function(){
23268                         // summary:
23269                         //              This function is called once, when mouse is over our container
23270                         // tags:
23271                         //              protected
23272                         this._changeState("Container", "Over");
23273                 },
23274
23275                 onOutEvent: function(){
23276                         // summary:
23277                         //              This function is called once, when mouse is out of our container
23278                         // tags:
23279                         //              protected
23280                         this._changeState("Container", "");
23281                 }
23282 });
23283
23284 }
23285
23286 if(!dojo._hasResource["dijit.tree._dndSelector"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23287 dojo._hasResource["dijit.tree._dndSelector"] = true;
23288 dojo.provide("dijit.tree._dndSelector");
23289
23290
23291
23292
23293 dojo.declare("dijit.tree._dndSelector",
23294         dijit.tree._dndContainer,
23295         {
23296                 // summary:
23297                 //              This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
23298                 //              It's based on `dojo.dnd.Selector`.
23299                 // tags:
23300                 //              protected
23301
23302                 /*=====
23303                 // selection: Hash<String, DomNode>
23304                 //              (id, DomNode) map for every TreeNode that's currently selected.
23305                 //              The DOMNode is the TreeNode.rowNode.
23306                 selection: {},
23307                 =====*/
23308
23309                 constructor: function(tree, params){
23310                         // summary:
23311                         //              Initialization
23312                         // tags:
23313                         //              private
23314
23315                         this.selection={};
23316                         this.anchor = null;
23317
23318                         dijit.setWaiState(this.tree.domNode, "multiselect", !this.singular);
23319
23320                         this.events.push(
23321                                 dojo.connect(this.tree.domNode, "onmousedown", this,"onMouseDown"),
23322                                 dojo.connect(this.tree.domNode, "onmouseup", this,"onMouseUp"),
23323                                 dojo.connect(this.tree.domNode, "onmousemove", this,"onMouseMove")
23324                         );
23325                 },
23326
23327                 //      singular: Boolean
23328                 //              Allows selection of only one element, if true.
23329                 //              Tree hasn't been tested in singular=true mode, unclear if it works.
23330                 singular: false,
23331
23332                 // methods
23333                 getSelectedTreeNodes: function(){
23334                         // summary:
23335                         //              Returns a list of selected node(s).
23336                         //              Used by dndSource on the start of a drag.
23337                         // tags:
23338                         //              protected
23339                         var nodes=[], sel = this.selection;
23340                         for(var i in sel){
23341                                 nodes.push(sel[i]);
23342                         }
23343                         return nodes;
23344                 },
23345
23346                 selectNone: function(){
23347                         // summary:
23348                         //              Unselects all items
23349                         // tags:
23350                         //              private
23351
23352                         this.setSelection([]);
23353                         return this;    // self
23354                 },
23355
23356                 destroy: function(){
23357                         // summary:
23358                         //              Prepares the object to be garbage-collected
23359                         this.inherited(arguments);
23360                         this.selection = this.anchor = null;
23361                 },
23362                 addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
23363                         // summary
23364                         //              add node to current selection
23365                         // node: Node
23366                         //              node to add
23367                         // isAnchor: Boolean
23368                         //              Whether the node should become anchor.
23369
23370                         this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
23371                         if(isAnchor){ this.anchor = node; }
23372                         return node;
23373                 },
23374                 removeTreeNode: function(/*dijit._TreeNode*/node){
23375                         // summary
23376                         //              remove node from current selection
23377                         // node: Node
23378                         //              node to remove
23379                         this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]))
23380                         return node;
23381                 },
23382                 isTreeNodeSelected: function(/*dijit._TreeNode*/node){
23383                         // summary
23384                         //              return true if node is currently selected
23385                         // node: Node
23386                         //              the node to check whether it's in the current selection
23387
23388                         return node.id && !!this.selection[node.id];
23389                 },
23390                 setSelection: function(/*dijit._treeNode[]*/ newSelection){
23391                         // summary
23392                         //      set the list of selected nodes to be exactly newSelection. All changes to the
23393                         //      selection should be passed through this function, which ensures that derived
23394                         //      attributes are kept up to date. Anchor will be deleted if it has been removed
23395                         //      from the selection, but no new anchor will be added by this function.
23396                         // newSelection: Node[]
23397                         //      list of tree nodes to make selected
23398                         var oldSelection = this.getSelectedTreeNodes();
23399                         dojo.forEach(this._setDifference(oldSelection, newSelection), dojo.hitch(this, function(node){
23400                                 node.setSelected(false);
23401                                 if(this.anchor == node){
23402                                         delete this.anchor;
23403                                 }
23404                                 delete this.selection[node.id];
23405                         }));
23406                         dojo.forEach(this._setDifference(newSelection, oldSelection), dojo.hitch(this, function(node){
23407                                 node.setSelected(true);
23408                                 this.selection[node.id] = node;
23409                         }));
23410                         this._updateSelectionProperties();
23411                 },
23412                 _setDifference: function(xs,ys){
23413                         // summary
23414                         //      Returns a copy of xs which lacks any objects
23415                         //      occurring in ys. Checks for membership by
23416                         //      modifying and then reading the object, so it will
23417                         //      not properly handle sets of numbers or strings.
23418                         
23419                         dojo.forEach(ys, function(y){ y.__exclude__ = true; });
23420                         var ret = dojo.filter(xs, function(x){ return !x.__exclude__; });
23421
23422                         // clean up after ourselves.
23423                         dojo.forEach(ys, function(y){ delete y['__exclude__'] });
23424                         return ret;
23425                 },
23426                 _updateSelectionProperties: function() {
23427                         // summary
23428                         //      Update the following tree properties from the current selection:
23429                         //      path[s], selectedItem[s], selectedNode[s]
23430                         
23431                         var selected = this.getSelectedTreeNodes();
23432                         var paths = [], nodes = [];
23433                         dojo.forEach(selected, function(node) {
23434                                 nodes.push(node);
23435                                 paths.push(node.getTreePath());
23436                         });
23437                         var items = dojo.map(nodes,function(node) { return node.item; });
23438                         this.tree._set("paths", paths);
23439                         this.tree._set("path", paths[0] || []);
23440                         this.tree._set("selectedNodes", nodes);
23441                         this.tree._set("selectedNode", nodes[0] || null);
23442                         this.tree._set("selectedItems", items);
23443                         this.tree._set("selectedItem", items[0] || null);
23444                 },
23445                 // mouse events
23446                 onMouseDown: function(e){
23447                         // summary:
23448                         //              Event processor for onmousedown
23449                         // e: Event
23450                         //              mouse event
23451                         // tags:
23452                         //              protected
23453
23454                         // ignore click on expando node
23455                         if(!this.current || this.tree.isExpandoNode( e.target, this.current)){ return; }
23456
23457                         if(e.button == dojo.mouseButtons.RIGHT){ return; }      // ignore right-click
23458
23459                         dojo.stopEvent(e);
23460
23461                         var treeNode = this.current,
23462                           copy = dojo.isCopyKey(e), id = treeNode.id;
23463
23464                         // if shift key is not pressed, and the node is already in the selection,
23465                         // delay deselection until onmouseup so in the case of DND, deselection
23466                         // will be canceled by onmousemove.
23467                         if(!this.singular && !e.shiftKey && this.selection[id]){
23468                                 this._doDeselect = true;
23469                                 return;
23470                         }else{
23471                                 this._doDeselect = false;
23472                         }
23473                         this.userSelect(treeNode, copy, e.shiftKey);
23474                 },
23475
23476                 onMouseUp: function(e){
23477                         // summary:
23478                         //              Event processor for onmouseup
23479                         // e: Event
23480                         //              mouse event
23481                         // tags:
23482                         //              protected
23483
23484                         // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
23485                         // a already selected item (to deselect the item), or click on a not-yet selected item
23486                         // (which should remove all current selection, and add the clicked item). This can not
23487                         // be done in onMouseDown, because the user may start a drag after mousedown. By moving
23488                         // the deselection logic here, the user can drags an already selected item.
23489                         if(!this._doDeselect){ return; }
23490                         this._doDeselect = false;
23491                         this.userSelect(this.current, dojo.isCopyKey( e ), e.shiftKey);
23492                 },
23493                 onMouseMove: function(e){
23494                         // summary
23495                         //              event processor for onmousemove
23496                         // e: Event
23497                         //              mouse event
23498                         this._doDeselect = false;
23499                 },
23500
23501                 userSelect: function(node, multi, range){
23502                         // summary:
23503                         //              Add or remove the given node from selection, responding
23504                         //      to a user action such as a click or keypress.
23505                         // multi: Boolean
23506                         //              Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
23507                         // range: Boolean
23508                         //              Indicates whether this is meant to be a ranged action (e.g. shift-click)
23509                         // tags:
23510                         //              protected
23511
23512                         if(this.singular){
23513                                 if(this.anchor == node && multi){
23514                                         this.selectNone();
23515                                 }else{
23516                                         this.setSelection([node]);
23517                                         this.anchor = node;
23518                                 }
23519                         }else{
23520                                 if(range && this.anchor){
23521                                         var cr = dijit.tree._compareNodes(this.anchor.rowNode, node.rowNode),
23522                                         begin, end, anchor = this.anchor;
23523                                         
23524                                         if(cr < 0){ //current is after anchor
23525                                                 begin = anchor;
23526                                                 end = node;
23527                                         }else{ //current is before anchor
23528                                                 begin = node;
23529                                                 end = anchor;
23530                                         }
23531                                         nodes = [];
23532                                         //add everything betweeen begin and end inclusively
23533                                         while(begin != end) {
23534                                                 nodes.push(begin)
23535                                                 begin = this.tree._getNextNode(begin);
23536                                         }
23537                                         nodes.push(end)
23538
23539                                         this.setSelection(nodes);
23540                                 }else{
23541                                     if( this.selection[ node.id ] && multi ) {
23542                                                 this.removeTreeNode( node );
23543                                     } else if(multi) {
23544                                                 this.addTreeNode(node, true);
23545                                         } else {
23546                                                 this.setSelection([node]);
23547                                                 this.anchor = node;
23548                                     }
23549                                 }
23550                         }
23551                 },
23552
23553                 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
23554                         // summary:
23555                         //              Iterates over selected items;
23556                         //              see `dojo.dnd.Container.forInItems()` for details
23557                         o = o || dojo.global;
23558                         for(var id in this.selection){
23559                                 // console.log("selected item id: " + id);
23560                                 f.call(o, this.getItem(id), id, this);
23561                         }
23562                 }
23563 });
23564
23565 }
23566
23567 if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
23568 dojo._hasResource["dijit.Tree"] = true;
23569 dojo.provide("dijit.Tree");
23570
23571
23572
23573
23574
23575
23576
23577
23578
23579
23580
23581
23582
23583 dojo.declare(
23584         "dijit._TreeNode",
23585         [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained, dijit._CssStateMixin],
23586 {
23587         // summary:
23588         //              Single node within a tree.   This class is used internally
23589         //              by Tree and should not be accessed directly.
23590         // tags:
23591         //              private
23592
23593         // item: [const] dojo.data.Item
23594         //              the dojo.data entry this tree represents
23595         item: null,
23596
23597         // isTreeNode: [protected] Boolean
23598         //              Indicates that this is a TreeNode.   Used by `dijit.Tree` only,
23599         //              should not be accessed directly.
23600         isTreeNode: true,
23601
23602         // label: String
23603         //              Text of this tree node
23604         label: "",
23605
23606         // isExpandable: [private] Boolean
23607         //              This node has children, so show the expando node (+ sign)
23608         isExpandable: null,
23609
23610         // isExpanded: [readonly] Boolean
23611         //              This node is currently expanded (ie, opened)
23612         isExpanded: false,
23613
23614         // state: [private] String
23615         //              Dynamic loading-related stuff.
23616         //              When an empty folder node appears, it is "UNCHECKED" first,
23617         //              then after dojo.data query it becomes "LOADING" and, finally "LOADED"
23618         state: "UNCHECKED",
23619
23620         templateString: dojo.cache("dijit", "templates/TreeNode.html", "<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div dojoAttachPoint=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" dojoAttachEvent=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\n\t\t><img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span dojoAttachPoint=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span dojoAttachPoint=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" dojoAttachPoint=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span dojoAttachPoint=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\" dojoAttachEvent=\"onfocus:_onLabelFocus\"></span>\n\t\t</span\n\t></div>\n\t<div dojoAttachPoint=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n"),
23621
23622         baseClass: "dijitTreeNode",
23623
23624         // For hover effect for tree node, and focus effect for label
23625         cssStateNodes: {
23626                 rowNode: "dijitTreeRow",
23627                 labelNode: "dijitTreeLabel"
23628         },
23629
23630         attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
23631                 label: {node: "labelNode", type: "innerText"},
23632                 tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
23633         }),
23634
23635         buildRendering: function(){
23636                 this.inherited(arguments);
23637
23638                 // set expand icon for leaf
23639                 this._setExpando();
23640
23641                 // set icon and label class based on item
23642                 this._updateItemClasses(this.item);
23643
23644                 if(this.isExpandable){
23645                         dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
23646                 }
23647
23648                 //aria-selected should be false on all selectable elements.
23649                 this.setSelected(false);
23650         },
23651
23652         _setIndentAttr: function(indent){
23653                 // summary:
23654                 //              Tell this node how many levels it should be indented
23655                 // description:
23656                 //              0 for top level nodes, 1 for their children, 2 for their
23657                 //              grandchildren, etc.
23658
23659                 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
23660                 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
23661
23662                 dojo.style(this.domNode, "backgroundPosition",  pixels + " 0px");
23663                 dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
23664
23665                 dojo.forEach(this.getChildren(), function(child){
23666                         child.set("indent", indent+1);
23667                 });
23668                 
23669                 this._set("indent", indent);
23670         },
23671
23672         markProcessing: function(){
23673                 // summary:
23674                 //              Visually denote that tree is loading data, etc.
23675                 // tags:
23676                 //              private
23677                 this.state = "LOADING";
23678                 this._setExpando(true);
23679         },
23680
23681         unmarkProcessing: function(){
23682                 // summary:
23683                 //              Clear markup from markProcessing() call
23684                 // tags:
23685                 //              private
23686                 this._setExpando(false);
23687         },
23688
23689         _updateItemClasses: function(item){
23690                 // summary:
23691                 //              Set appropriate CSS classes for icon and label dom node
23692                 //              (used to allow for item updates to change respective CSS)
23693                 // tags:
23694                 //              private
23695                 var tree = this.tree, model = tree.model;
23696                 if(tree._v10Compat && item === model.root){
23697                         // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
23698                         item = null;
23699                 }
23700                 this._applyClassAndStyle(item, "icon", "Icon");
23701                 this._applyClassAndStyle(item, "label", "Label");
23702                 this._applyClassAndStyle(item, "row", "Row");
23703         },
23704
23705         _applyClassAndStyle: function(item, lower, upper){
23706                 // summary:
23707                 //              Set the appropriate CSS classes and styles for labels, icons and rows.
23708                 //
23709                 // item:
23710                 //              The data item.
23711                 //
23712                 // lower:
23713                 //              The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
23714                 //
23715                 // upper:
23716                 //              The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
23717                 //
23718                 // tags:
23719                 //              private
23720
23721                 var clsName = "_" + lower + "Class";
23722                 var nodeName = lower + "Node";
23723                 var oldCls = this[clsName];
23724
23725                 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
23726                 dojo.replaceClass(this[nodeName], this[clsName] || "", oldCls || "");
23727  
23728                 dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
23729         },
23730
23731         _updateLayout: function(){
23732                 // summary:
23733                 //              Set appropriate CSS classes for this.domNode
23734                 // tags:
23735                 //              private
23736                 var parent = this.getParent();
23737                 if(!parent || parent.rowNode.style.display == "none"){
23738                         /* if we are hiding the root node then make every first level child look like a root node */
23739                         dojo.addClass(this.domNode, "dijitTreeIsRoot");
23740                 }else{
23741                         dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
23742                 }
23743         },
23744
23745         _setExpando: function(/*Boolean*/ processing){
23746                 // summary:
23747                 //              Set the right image for the expando node
23748                 // tags:
23749                 //              private
23750
23751                 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
23752                                                 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
23753                         _a11yStates = ["*","-","+","*"],
23754                         idx = processing ? 0 : (this.isExpandable ?     (this.isExpanded ? 1 : 2) : 3);
23755
23756                 // apply the appropriate class to the expando node
23757                 dojo.replaceClass(this.expandoNode, styles[idx], styles);
23758
23759                 // provide a non-image based indicator for images-off mode
23760                 this.expandoNodeText.innerHTML = _a11yStates[idx];
23761
23762         },
23763
23764         expand: function(){
23765                 // summary:
23766                 //              Show my children
23767                 // returns:
23768                 //              Deferred that fires when expansion is complete
23769
23770                 // If there's already an expand in progress or we are already expanded, just return
23771                 if(this._expandDeferred){
23772                         return this._expandDeferred;            // dojo.Deferred
23773                 }
23774
23775                 // cancel in progress collapse operation
23776                 this._wipeOut && this._wipeOut.stop();
23777
23778                 // All the state information for when a node is expanded, maybe this should be
23779                 // set when the animation completes instead
23780                 this.isExpanded = true;
23781                 dijit.setWaiState(this.labelNode, "expanded", "true");
23782                 if(this.tree.showRoot || this !== this.tree.rootNode){
23783                         dijit.setWaiRole(this.containerNode, "group");
23784                 }
23785                 dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
23786                 this._setExpando();
23787                 this._updateItemClasses(this.item);
23788                 if(this == this.tree.rootNode){
23789                         dijit.setWaiState(this.tree.domNode, "expanded", "true");
23790                 }
23791
23792                 var def,
23793                         wipeIn = dojo.fx.wipeIn({
23794                                 node: this.containerNode, duration: dijit.defaultDuration,
23795                                 onEnd: function(){
23796                                         def.callback(true);
23797                                 }
23798                         });
23799
23800                 // Deferred that fires when expand is complete
23801                 def = (this._expandDeferred = new dojo.Deferred(function(){
23802                         // Canceller
23803                         wipeIn.stop();
23804                 }));
23805
23806                 wipeIn.play();
23807
23808                 return def;             // dojo.Deferred
23809         },
23810
23811         collapse: function(){
23812                 // summary:
23813                 //              Collapse this node (if it's expanded)
23814
23815                 if(!this.isExpanded){ return; }
23816
23817                 // cancel in progress expand operation
23818                 if(this._expandDeferred){
23819                         this._expandDeferred.cancel();
23820                         delete this._expandDeferred;
23821                 }
23822
23823                 this.isExpanded = false;
23824                 dijit.setWaiState(this.labelNode, "expanded", "false");
23825                 if(this == this.tree.rootNode){
23826                         dijit.setWaiState(this.tree.domNode, "expanded", "false");
23827                 }
23828                 dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
23829                 this._setExpando();
23830                 this._updateItemClasses(this.item);
23831
23832                 if(!this._wipeOut){
23833                         this._wipeOut = dojo.fx.wipeOut({
23834                                 node: this.containerNode, duration: dijit.defaultDuration
23835                         });
23836                 }
23837                 this._wipeOut.play();
23838         },
23839
23840         // indent: Integer
23841         //              Levels from this node to the root node
23842         indent: 0,
23843
23844         setChildItems: function(/* Object[] */ items){
23845                 // summary:
23846                 //              Sets the child items of this node, removing/adding nodes
23847                 //              from current children to match specified items[] array.
23848                 //              Also, if this.persist == true, expands any children that were previously
23849                 //              opened.
23850                 // returns:
23851                 //              Deferred object that fires after all previously opened children
23852                 //              have been expanded again (or fires instantly if there are no such children).
23853
23854                 var tree = this.tree,
23855                         model = tree.model,
23856                         defs = [];      // list of deferreds that need to fire before I am complete
23857
23858
23859                 // Orphan all my existing children.
23860                 // If items contains some of the same items as before then we will reattach them.
23861                 // Don't call this.removeChild() because that will collapse the tree etc.
23862                 dojo.forEach(this.getChildren(), function(child){
23863                         dijit._Container.prototype.removeChild.call(this, child);
23864                 }, this);
23865
23866                 this.state = "LOADED";
23867
23868                 if(items && items.length > 0){
23869                         this.isExpandable = true;
23870
23871                         // Create _TreeNode widget for each specified tree node, unless one already
23872                         // exists and isn't being used (presumably it's from a DnD move and was recently
23873                         // released
23874                         dojo.forEach(items, function(item){
23875                                 var id = model.getIdentity(item),
23876                                         existingNodes = tree._itemNodesMap[id],
23877                                         node;
23878                                 if(existingNodes){
23879                                         for(var i=0;i<existingNodes.length;i++){
23880                                                 if(existingNodes[i] && !existingNodes[i].getParent()){
23881                                                         node = existingNodes[i];
23882                                                         node.set('indent', this.indent+1);
23883                                                         break;
23884                                                 }
23885                                         }
23886                                 }
23887                                 if(!node){
23888                                         node = this.tree._createTreeNode({
23889                                                         item: item,
23890                                                         tree: tree,
23891                                                         isExpandable: model.mayHaveChildren(item),
23892                                                         label: tree.getLabel(item),
23893                                                         tooltip: tree.getTooltip(item),
23894                                                         dir: tree.dir,
23895                                                         lang: tree.lang,
23896                                                         indent: this.indent + 1
23897                                                 });
23898                                         if(existingNodes){
23899                                                 existingNodes.push(node);
23900                                         }else{
23901                                                 tree._itemNodesMap[id] = [node];
23902                                         }
23903                                 }
23904                                 this.addChild(node);
23905
23906                                 // If node was previously opened then open it again now (this may trigger
23907                                 // more data store accesses, recursively)
23908                                 if(this.tree.autoExpand || this.tree._state(item)){
23909                                         defs.push(tree._expandNode(node));
23910                                 }
23911                         }, this);
23912
23913                         // note that updateLayout() needs to be called on each child after
23914                         // _all_ the children exist
23915                         dojo.forEach(this.getChildren(), function(child, idx){
23916                                 child._updateLayout();
23917                         });
23918                 }else{
23919                         this.isExpandable=false;
23920                 }
23921
23922                 if(this._setExpando){
23923                         // change expando to/from dot or + icon, as appropriate
23924                         this._setExpando(false);
23925                 }
23926
23927                 // Set leaf icon or folder icon, as appropriate
23928                 this._updateItemClasses(this.item);
23929
23930                 // On initial tree show, make the selected TreeNode as either the root node of the tree,
23931                 // or the first child, if the root node is hidden
23932                 if(this == tree.rootNode){
23933                         var fc = this.tree.showRoot ? this : this.getChildren()[0];
23934                         if(fc){
23935                                 fc.setFocusable(true);
23936                                 tree.lastFocused = fc;
23937                         }else{
23938                                 // fallback: no nodes in tree so focus on Tree <div> itself
23939                                 tree.domNode.setAttribute("tabIndex", "0");
23940                         }
23941                 }
23942
23943                 return new dojo.DeferredList(defs);     // dojo.Deferred
23944         },
23945
23946         getTreePath: function(){
23947                 var node = this;
23948                 var path = [];
23949                 while(node && node !== this.tree.rootNode){
23950                                 path.unshift(node.item);
23951                                 node = node.getParent();
23952                 }
23953                 path.unshift(this.tree.rootNode.item);
23954
23955                 return path;
23956         },
23957
23958         getIdentity: function() {
23959                 return this.tree.model.getIdentity(this.item);
23960         },
23961
23962         removeChild: function(/* treeNode */ node){
23963                 this.inherited(arguments);
23964
23965                 var children = this.getChildren();
23966                 if(children.length == 0){
23967                         this.isExpandable = false;
23968                         this.collapse();
23969                 }
23970
23971                 dojo.forEach(children, function(child){
23972                                 child._updateLayout();
23973                 });
23974         },
23975
23976         makeExpandable: function(){
23977                 // summary:
23978                 //              if this node wasn't already showing the expando node,
23979                 //              turn it into one and call _setExpando()
23980
23981                 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
23982
23983                 this.isExpandable = true;
23984                 this._setExpando(false);
23985         },
23986
23987         _onLabelFocus: function(evt){
23988                 // summary:
23989                 //              Called when this row is focused (possibly programatically)
23990                 //              Note that we aren't using _onFocus() builtin to dijit
23991                 //              because it's called when focus is moved to a descendant TreeNode.
23992                 // tags:
23993                 //              private
23994                 this.tree._onNodeFocus(this);
23995         },
23996
23997         setSelected: function(/*Boolean*/ selected){
23998                 // summary:
23999                 //              A Tree has a (single) currently selected node.
24000                 //              Mark that this node is/isn't that currently selected node.
24001                 // description:
24002                 //              In particular, setting a node as selected involves setting tabIndex
24003                 //              so that when user tabs to the tree, focus will go to that node (only).
24004                 dijit.setWaiState(this.labelNode, "selected", selected);
24005                 dojo.toggleClass(this.rowNode, "dijitTreeRowSelected", selected);
24006         },
24007
24008         setFocusable: function(/*Boolean*/ selected){
24009                 // summary:
24010                 //              A Tree has a (single) node that's focusable.
24011                 //              Mark that this node is/isn't that currently focsuable node.
24012                 // description:
24013                 //              In particular, setting a node as selected involves setting tabIndex
24014                 //              so that when user tabs to the tree, focus will go to that node (only).
24015
24016                 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
24017         },
24018
24019         _onClick: function(evt){
24020                 // summary:
24021                 //              Handler for onclick event on a node
24022                 // tags:
24023                 //              private
24024                 this.tree._onClick(this, evt);
24025         },
24026         _onDblClick: function(evt){
24027                 // summary:
24028                 //              Handler for ondblclick event on a node
24029                 // tags:
24030                 //              private
24031                 this.tree._onDblClick(this, evt);
24032         },
24033
24034         _onMouseEnter: function(evt){
24035                 // summary:
24036                 //              Handler for onmouseenter event on a node
24037                 // tags:
24038                 //              private
24039                 this.tree._onNodeMouseEnter(this, evt);
24040         },
24041
24042         _onMouseLeave: function(evt){
24043                 // summary:
24044                 //              Handler for onmouseenter event on a node
24045                 // tags:
24046                 //              private
24047                 this.tree._onNodeMouseLeave(this, evt);
24048         }
24049 });
24050
24051 dojo.declare(
24052         "dijit.Tree",
24053         [dijit._Widget, dijit._Templated],
24054 {
24055         // summary:
24056         //              This widget displays hierarchical data from a store.
24057
24058         // store: [deprecated] String||dojo.data.Store
24059         //              Deprecated.  Use "model" parameter instead.
24060         //              The store to get data to display in the tree.
24061         store: null,
24062
24063         // model: dijit.Tree.model
24064         //              Interface to read tree data, get notifications of changes to tree data,
24065         //              and for handling drop operations (i.e drag and drop onto the tree)
24066         model: null,
24067
24068         // query: [deprecated] anything
24069         //              Deprecated.  User should specify query to the model directly instead.
24070         //              Specifies datastore query to return the root item or top items for the tree.
24071         query: null,
24072
24073         // label: [deprecated] String
24074         //              Deprecated.  Use dijit.tree.ForestStoreModel directly instead.
24075         //              Used in conjunction with query parameter.
24076         //              If a query is specified (rather than a root node id), and a label is also specified,
24077         //              then a fake root node is created and displayed, with this label.
24078         label: "",
24079
24080         // showRoot: [const] Boolean
24081         //              Should the root node be displayed, or hidden?
24082         showRoot: true,
24083
24084         // childrenAttr: [deprecated] String[]
24085         //              Deprecated.   This information should be specified in the model.
24086         //              One ore more attributes that holds children of a tree node
24087         childrenAttr: ["children"],
24088
24089         // paths: String[][] or Item[][]
24090         //              Full paths from rootNode to selected nodes expressed as array of items or array of ids.
24091         //              Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
24092         //              returns a Deferred to indicate when the set is complete.
24093         paths: [],
24094         
24095         // path: String[] or Item[]
24096         //      Backward compatible singular variant of paths.
24097         path: [],
24098
24099         // selectedItems: [readonly] Item[]
24100         //              The currently selected items in this tree.
24101         //              This property can only be set (via set('selectedItems', ...)) when that item is already
24102         //              visible in the tree.   (I.e. the tree has already been expanded to show that node.)
24103         //              Should generally use `paths` attribute to set the selected items instead.
24104         selectedItems: null,
24105
24106         // selectedItem: [readonly] Item
24107         //      Backward compatible singular variant of selectedItems.
24108         selectedItem: null,
24109
24110         // openOnClick: Boolean
24111         //              If true, clicking a folder node's label will open it, rather than calling onClick()
24112         openOnClick: false,
24113
24114         // openOnDblClick: Boolean
24115         //              If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
24116         openOnDblClick: false,
24117
24118         templateString: dojo.cache("dijit", "templates/Tree.html", "<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdojoAttachEvent=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" dojoAttachPoint=\"indentDetector\"></div>\n</div>\n"),
24119
24120         // persist: Boolean
24121         //              Enables/disables use of cookies for state saving.
24122         persist: true,
24123
24124         // autoExpand: Boolean
24125         //              Fully expand the tree on load.   Overrides `persist`.
24126         autoExpand: false,
24127
24128         // dndController: [protected] String
24129         //              Class name to use as as the dnd controller.  Specifying this class enables DnD.
24130         //              Generally you should specify this as "dijit.tree.dndSource".
24131         //      Default of "dijit.tree._dndSelector" handles selection only (no actual DnD).
24132         dndController: "dijit.tree._dndSelector",
24133
24134         // parameters to pull off of the tree and pass on to the dndController as its params
24135         dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
24136
24137         //declare the above items so they can be pulled from the tree's markup
24138
24139         // onDndDrop: [protected] Function
24140         //              Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
24141         //              Generally this doesn't need to be set.
24142         onDndDrop: null,
24143
24144         /*=====
24145         itemCreator: function(nodes, target, source){
24146                 // summary:
24147                 //              Returns objects passed to `Tree.model.newItem()` based on DnD nodes
24148                 //              dropped onto the tree.   Developer must override this method to enable
24149                 //              dropping from external sources onto this Tree, unless the Tree.model's items
24150                 //              happen to look like {id: 123, name: "Apple" } with no other attributes.
24151                 // description:
24152                 //              For each node in nodes[], which came from source, create a hash of name/value
24153                 //              pairs to be passed to Tree.model.newItem().  Returns array of those hashes.
24154                 // nodes: DomNode[]
24155                 //              The DOMNodes dragged from the source container
24156                 // target: DomNode
24157                 //              The target TreeNode.rowNode
24158                 // source: dojo.dnd.Source
24159                 //              The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
24160                 // returns: Object[]
24161                 //              Array of name/value hashes for each new item to be added to the Tree, like:
24162                 // |    [
24163                 // |            { id: 123, label: "apple", foo: "bar" },
24164                 // |            { id: 456, label: "pear", zaz: "bam" }
24165                 // |    ]
24166                 // tags:
24167                 //              extension
24168                 return [{}];
24169         },
24170         =====*/
24171         itemCreator: null,
24172
24173         // onDndCancel: [protected] Function
24174         //              Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
24175         //              Generally this doesn't need to be set.
24176         onDndCancel: null,
24177
24178 /*=====
24179         checkAcceptance: function(source, nodes){
24180                 // summary:
24181                 //              Checks if the Tree itself can accept nodes from this source
24182                 // source: dijit.tree._dndSource
24183                 //              The source which provides items
24184                 // nodes: DOMNode[]
24185                 //              Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
24186                 //              source is a dijit.Tree.
24187                 // tags:
24188                 //              extension
24189                 return true;    // Boolean
24190         },
24191 =====*/
24192         checkAcceptance: null,
24193
24194 /*=====
24195         checkItemAcceptance: function(target, source, position){
24196                 // summary:
24197                 //              Stub function to be overridden if one wants to check for the ability to drop at the node/item level
24198                 // description:
24199                 //              In the base case, this is called to check if target can become a child of source.
24200                 //              When betweenThreshold is set, position="before" or "after" means that we
24201                 //              are asking if the source node can be dropped before/after the target node.
24202                 // target: DOMNode
24203                 //              The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
24204                 //              Use dijit.getEnclosingWidget(target) to get the TreeNode.
24205                 // source: dijit.tree.dndSource
24206                 //              The (set of) nodes we are dropping
24207                 // position: String
24208                 //              "over", "before", or "after"
24209                 // tags:
24210                 //              extension
24211                 return true;    // Boolean
24212         },
24213 =====*/
24214         checkItemAcceptance: null,
24215
24216         // dragThreshold: Integer
24217         //              Number of pixels mouse moves before it's considered the start of a drag operation
24218         dragThreshold: 5,
24219
24220         // betweenThreshold: Integer
24221         //              Set to a positive value to allow drag and drop "between" nodes.
24222         //
24223         //              If during DnD mouse is over a (target) node but less than betweenThreshold
24224         //              pixels from the bottom edge, dropping the the dragged node will make it
24225         //              the next sibling of the target node, rather than the child.
24226         //
24227         //              Similarly, if mouse is over a target node but less that betweenThreshold
24228         //              pixels from the top edge, dropping the dragged node will make it
24229         //              the target node's previous sibling rather than the target node's child.
24230         betweenThreshold: 0,
24231
24232         // _nodePixelIndent: Integer
24233         //              Number of pixels to indent tree nodes (relative to parent node).
24234         //              Default is 19 but can be overridden by setting CSS class dijitTreeIndent
24235         //              and calling resize() or startup() on tree after it's in the DOM.
24236         _nodePixelIndent: 19,
24237
24238         _publish: function(/*String*/ topicName, /*Object*/ message){
24239                 // summary:
24240                 //              Publish a message for this widget/topic
24241                 dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message || {})]);
24242         },
24243
24244         postMixInProperties: function(){
24245                 this.tree = this;
24246
24247                 if(this.autoExpand){
24248                         // There's little point in saving opened/closed state of nodes for a Tree
24249                         // that initially opens all it's nodes.
24250                         this.persist = false;
24251                 }
24252
24253                 this._itemNodesMap={};
24254
24255                 if(!this.cookieName){
24256                         this.cookieName = this.id + "SaveStateCookie";
24257                 }
24258
24259                 this._loadDeferred = new dojo.Deferred();
24260
24261                 this.inherited(arguments);
24262         },
24263
24264         postCreate: function(){
24265                 this._initState();
24266
24267                 // Create glue between store and Tree, if not specified directly by user
24268                 if(!this.model){
24269                         this._store2model();
24270                 }
24271
24272                 // monitor changes to items
24273                 this.connect(this.model, "onChange", "_onItemChange");
24274                 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
24275                 this.connect(this.model, "onDelete", "_onItemDelete");
24276
24277                 this._load();
24278
24279                 this.inherited(arguments);
24280
24281                 if(this.dndController){
24282                         if(dojo.isString(this.dndController)){
24283                                 this.dndController = dojo.getObject(this.dndController);
24284                         }
24285                         var params={};
24286                         for(var i=0; i<this.dndParams.length;i++){
24287                                 if(this[this.dndParams[i]]){
24288                                         params[this.dndParams[i]] = this[this.dndParams[i]];
24289                                 }
24290                         }
24291                         this.dndController = new this.dndController(this, params);
24292                 }
24293         },
24294
24295         _store2model: function(){
24296                 // summary:
24297                 //              User specified a store&query rather than model, so create model from store/query
24298                 this._v10Compat = true;
24299                 dojo.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
24300
24301                 var modelParams = {
24302                         id: this.id + "_ForestStoreModel",
24303                         store: this.store,
24304                         query: this.query,
24305                         childrenAttrs: this.childrenAttr
24306                 };
24307
24308                 // Only override the model's mayHaveChildren() method if the user has specified an override
24309                 if(this.params.mayHaveChildren){
24310                         modelParams.mayHaveChildren = dojo.hitch(this, "mayHaveChildren");
24311                 }
24312
24313                 if(this.params.getItemChildren){
24314                         modelParams.getChildren = dojo.hitch(this, function(item, onComplete, onError){
24315                                 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
24316                         });
24317                 }
24318                 this.model = new dijit.tree.ForestStoreModel(modelParams);
24319
24320                 // For backwards compatibility, the visibility of the root node is controlled by
24321                 // whether or not the user has specified a label
24322                 this.showRoot = Boolean(this.label);
24323         },
24324
24325         onLoad: function(){
24326                 // summary:
24327                 //              Called when tree finishes loading and expanding.
24328                 // description:
24329                 //              If persist == true the loading may encompass many levels of fetches
24330                 //              from the data store, each asynchronous.   Waits for all to finish.
24331                 // tags:
24332                 //              callback
24333         },
24334
24335         _load: function(){
24336                 // summary:
24337                 //              Initial load of the tree.
24338                 //              Load root node (possibly hidden) and it's children.
24339                 this.model.getRoot(
24340                         dojo.hitch(this, function(item){
24341                                 var rn = (this.rootNode = this.tree._createTreeNode({
24342                                         item: item,
24343                                         tree: this,
24344                                         isExpandable: true,
24345                                         label: this.label || this.getLabel(item),
24346                                         indent: this.showRoot ? 0 : -1
24347                                 }));
24348                                 if(!this.showRoot){
24349                                         rn.rowNode.style.display="none";
24350                                         // if root is not visible, move tree role to the invisible
24351                                         // root node's containerNode, see #12135
24352                                         dijit.setWaiRole(this.domNode, 'presentation');
24353                                         
24354                                         dijit.setWaiRole(rn.labelNode, 'presentation');
24355                                         dijit.setWaiRole(rn.containerNode, 'tree');
24356                                 }
24357                                 this.domNode.appendChild(rn.domNode);
24358                                 var identity = this.model.getIdentity(item);
24359                                 if(this._itemNodesMap[identity]){
24360                                         this._itemNodesMap[identity].push(rn);
24361                                 }else{
24362                                         this._itemNodesMap[identity] = [rn];
24363                                 }
24364
24365                                 rn._updateLayout();             // sets "dijitTreeIsRoot" CSS classname
24366
24367                                 // load top level children and then fire onLoad() event
24368                                 this._expandNode(rn).addCallback(dojo.hitch(this, function(){
24369                                         this._loadDeferred.callback(true);
24370                                         this.onLoad();
24371                                 }));
24372                         }),
24373                         function(err){
24374                                 console.error(this, ": error loading root: ", err);
24375                         }
24376                 );
24377         },
24378
24379         getNodesByItem: function(/*dojo.data.Item or id*/ item){
24380                 // summary:
24381                 //              Returns all tree nodes that refer to an item
24382                 // returns:
24383                 //              Array of tree nodes that refer to passed item
24384
24385                 if(!item){ return []; }
24386                 var identity = dojo.isString(item) ? item : this.model.getIdentity(item);
24387                 // return a copy so widget don't get messed up by changes to returned array
24388                 return [].concat(this._itemNodesMap[identity]);
24389         },
24390
24391         _setSelectedItemAttr: function(/*dojo.data.Item or id*/ item){
24392                 this.set('selectedItems', [item]);
24393         },
24394
24395         _setSelectedItemsAttr: function(/*dojo.data.Items or ids*/ items){
24396                 // summary:
24397                 //              Select tree nodes related to passed items.
24398                 //              WARNING: if model use multi-parented items or desired tree node isn't already loaded
24399                 //              behavior is undefined. Use set('paths', ...) instead.
24400                 var tree = this;
24401                 this._loadDeferred.addCallback( dojo.hitch(this, function(){
24402                         var identities = dojo.map(items, function(item){
24403                                 return (!item || dojo.isString(item)) ? item : tree.model.getIdentity(item);
24404                         });
24405                         var nodes = [];
24406                         dojo.forEach(identities, function(id){
24407                                 nodes = nodes.concat(tree._itemNodesMap[id] || []);
24408                         });
24409                         this.set('selectedNodes', nodes);
24410                 }));
24411         },
24412
24413         _setPathAttr: function(/*Item[] || String[]*/ path){
24414                 // summary:
24415                 //      Singular variant of _setPathsAttr
24416                 if(path.length) {
24417                         return this.set("paths", [path]);
24418                 } else {
24419                         //Empty list is interpreted as "select nothing"
24420                         return this.set("paths", []);
24421                 }
24422         },
24423         
24424         _setPathsAttr: function(/*Item[][] || String[][]*/ paths){
24425                 // summary:
24426                 //              Select the tree nodes identified by passed paths.
24427                 // paths:
24428                 //              Array of arrays of items or item id's
24429                 // returns:
24430                 //              Deferred to indicate when the set is complete
24431                 var tree = this;
24432
24433                 // We may need to wait for some nodes to expand, so setting
24434                 // each path will involve a Deferred. We bring those deferreds
24435                 // together witha DeferredList.
24436                 return new dojo.DeferredList(dojo.map(paths, function(path){
24437                         var d = new dojo.Deferred();
24438                         
24439                         // normalize path to use identity
24440                         path = dojo.map(path, function(item){
24441                                 return dojo.isString(item) ? item : tree.model.getIdentity(item);
24442                         });
24443
24444                         if(path.length){
24445                                 // Wait for the tree to load, if it hasn't already.
24446                                 tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
24447                         }else{
24448                                 d.errback("Empty path");
24449                         }
24450                         return d;
24451                 })).addCallback(setNodes);
24452
24453                 function selectPath(path, nodes, def){
24454                         // Traverse path; the next path component should be among "nodes".
24455                         var nextPath = path.shift();
24456                         var nextNode = dojo.filter(nodes, function(node){
24457                                 return node.getIdentity() == nextPath;
24458                         })[0];
24459                         if(!!nextNode){
24460                                 if(path.length){
24461                                         tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
24462                                 }else{
24463                                         //Successfully reached the end of this path
24464                                         def.callback(nextNode);
24465                                 }
24466                         } else {
24467                                 def.errback("Could not expand path at " + nextPath);
24468                         }
24469                 }
24470                 
24471                 function setNodes(newNodes){
24472                         //After all expansion is finished, set the selection to
24473                         //the set of nodes successfully found.
24474                         tree.set("selectedNodes", dojo.map(
24475                                 dojo.filter(newNodes,function(x){return x[0];}),
24476                                 function(x){return x[1];}));
24477                 }
24478         },
24479
24480         _setSelectedNodeAttr: function(node){
24481                 this.set('selectedNodes', [node]);
24482         },
24483         _setSelectedNodesAttr: function(nodes){
24484                 this._loadDeferred.addCallback( dojo.hitch(this, function(){
24485                         this.dndController.setSelection(nodes);
24486                 }));
24487         },
24488
24489
24490         ////////////// Data store related functions //////////////////////
24491         // These just get passed to the model; they are here for back-compat
24492
24493         mayHaveChildren: function(/*dojo.data.Item*/ item){
24494                 // summary:
24495                 //              Deprecated.   This should be specified on the model itself.
24496                 //
24497                 //              Overridable function to tell if an item has or may have children.
24498                 //              Controls whether or not +/- expando icon is shown.
24499                 //              (For efficiency reasons we may not want to check if an element actually
24500                 //              has children until user clicks the expando node)
24501                 // tags:
24502                 //              deprecated
24503         },
24504
24505         getItemChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete){
24506                 // summary:
24507                 //              Deprecated.   This should be specified on the model itself.
24508                 //
24509                 //              Overridable function that return array of child items of given parent item,
24510                 //              or if parentItem==null then return top items in tree
24511                 // tags:
24512                 //              deprecated
24513         },
24514
24515         ///////////////////////////////////////////////////////
24516         // Functions for converting an item to a TreeNode
24517         getLabel: function(/*dojo.data.Item*/ item){
24518                 // summary:
24519                 //              Overridable function to get the label for a tree node (given the item)
24520                 // tags:
24521                 //              extension
24522                 return this.model.getLabel(item);       // String
24523         },
24524
24525         getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24526                 // summary:
24527                 //              Overridable function to return CSS class name to display icon
24528                 // tags:
24529                 //              extension
24530                 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
24531         },
24532
24533         getLabelClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24534                 // summary:
24535                 //              Overridable function to return CSS class name to display label
24536                 // tags:
24537                 //              extension
24538         },
24539
24540         getRowClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24541                 // summary:
24542                 //              Overridable function to return CSS class name to display row
24543                 // tags:
24544                 //              extension
24545         },
24546
24547         getIconStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24548                 // summary:
24549                 //              Overridable function to return CSS styles to display icon
24550                 // returns:
24551                 //              Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
24552                 // tags:
24553                 //              extension
24554         },
24555
24556         getLabelStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24557                 // summary:
24558                 //              Overridable function to return CSS styles to display label
24559                 // returns:
24560                 //              Object suitable for input to dojo.style() like {color: "red", background: "green"}
24561                 // tags:
24562                 //              extension
24563         },
24564
24565         getRowStyle: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
24566                 // summary:
24567                 //              Overridable function to return CSS styles to display row
24568                 // returns:
24569                 //              Object suitable for input to dojo.style() like {background-color: "#bbb"}
24570                 // tags:
24571                 //              extension
24572         },
24573
24574         getTooltip: function(/*dojo.data.Item*/ item){
24575                 // summary:
24576                 //              Overridable function to get the tooltip for a tree node (given the item)
24577                 // tags:
24578                 //              extension
24579                 return "";      // String
24580         },
24581
24582         /////////// Keyboard and Mouse handlers ////////////////////
24583
24584         _onKeyPress: function(/*Event*/ e){
24585                 // summary:
24586                 //              Translates keypress events into commands for the controller
24587                 if(e.altKey){ return; }
24588                 var dk = dojo.keys;
24589                 var treeNode = dijit.getEnclosingWidget(e.target);
24590                 if(!treeNode){ return; }
24591
24592                 var key = e.charOrCode;
24593                 if(typeof key == "string" && key != " "){       // handle printables (letter navigation)
24594                         // Check for key navigation.
24595                         if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
24596                                 this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
24597                                 dojo.stopEvent(e);
24598                         }
24599                 }else{  // handle non-printables (arrow keys)
24600                         // clear record of recent printables (being saved for multi-char letter navigation),
24601                         // because "a", down-arrow, "b" shouldn't search for "ab"
24602                         if(this._curSearch){
24603                                 clearTimeout(this._curSearch.timer);
24604                                 delete this._curSearch;
24605                         }
24606
24607                         var map = this._keyHandlerMap;
24608                         if(!map){
24609                                 // setup table mapping keys to events
24610                                 map = {};
24611                                 map[dk.ENTER]="_onEnterKey";
24612                                 //On WebKit based browsers, the combination ctrl-enter
24613                                 //does not get passed through. To allow accessible
24614                                 //multi-select on those browsers, the space key is
24615                                 //also used for selection.
24616                                 map[dk.SPACE]= map[" "] = "_onEnterKey";
24617                                 map[this.isLeftToRight() ? dk.LEFT_ARROW : dk.RIGHT_ARROW]="_onLeftArrow";
24618                                 map[this.isLeftToRight() ? dk.RIGHT_ARROW : dk.LEFT_ARROW]="_onRightArrow";
24619                                 map[dk.UP_ARROW]="_onUpArrow";
24620                                 map[dk.DOWN_ARROW]="_onDownArrow";
24621                                 map[dk.HOME]="_onHomeKey";
24622                                 map[dk.END]="_onEndKey";
24623                                 this._keyHandlerMap = map;
24624                         }
24625                         if(this._keyHandlerMap[key]){
24626                                 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
24627                                 dojo.stopEvent(e);
24628                         }
24629                 }
24630         },
24631
24632         _onEnterKey: function(/*Object*/ message){
24633                 this._publish("execute", { item: message.item, node: message.node } );
24634                 this.dndController.userSelect(message.node, dojo.isCopyKey( message.evt ), message.evt.shiftKey);
24635                 this.onClick(message.item, message.node, message.evt);
24636         },
24637
24638         _onDownArrow: function(/*Object*/ message){
24639                 // summary:
24640                 //              down arrow pressed; get next visible node, set focus there
24641                 var node = this._getNextNode(message.node);
24642                 if(node && node.isTreeNode){
24643                         this.focusNode(node);
24644                 }
24645         },
24646
24647         _onUpArrow: function(/*Object*/ message){
24648                 // summary:
24649                 //              Up arrow pressed; move to previous visible node
24650
24651                 var node = message.node;
24652
24653                 // if younger siblings
24654                 var previousSibling = node.getPreviousSibling();
24655                 if(previousSibling){
24656                         node = previousSibling;
24657                         // if the previous node is expanded, dive in deep
24658                         while(node.isExpandable && node.isExpanded && node.hasChildren()){
24659                                 // move to the last child
24660                                 var children = node.getChildren();
24661                                 node = children[children.length-1];
24662                         }
24663                 }else{
24664                         // if this is the first child, return the parent
24665                         // unless the parent is the root of a tree with a hidden root
24666                         var parent = node.getParent();
24667                         if(!(!this.showRoot && parent === this.rootNode)){
24668                                 node = parent;
24669                         }
24670                 }
24671
24672                 if(node && node.isTreeNode){
24673                         this.focusNode(node);
24674                 }
24675         },
24676
24677         _onRightArrow: function(/*Object*/ message){
24678                 // summary:
24679                 //              Right arrow pressed; go to child node
24680                 var node = message.node;
24681
24682                 // if not expanded, expand, else move to 1st child
24683                 if(node.isExpandable && !node.isExpanded){
24684                         this._expandNode(node);
24685                 }else if(node.hasChildren()){
24686                         node = node.getChildren()[0];
24687                         if(node && node.isTreeNode){
24688                                 this.focusNode(node);
24689                         }
24690                 }
24691         },
24692
24693         _onLeftArrow: function(/*Object*/ message){
24694                 // summary:
24695                 //              Left arrow pressed.
24696                 //              If not collapsed, collapse, else move to parent.
24697
24698                 var node = message.node;
24699
24700                 if(node.isExpandable && node.isExpanded){
24701                         this._collapseNode(node);
24702                 }else{
24703                         var parent = node.getParent();
24704                         if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
24705                                 this.focusNode(parent);
24706                         }
24707                 }
24708         },
24709
24710         _onHomeKey: function(){
24711                 // summary:
24712                 //              Home key pressed; get first visible node, and set focus there
24713                 var node = this._getRootOrFirstNode();
24714                 if(node){
24715                         this.focusNode(node);
24716                 }
24717         },
24718
24719         _onEndKey: function(/*Object*/ message){
24720                 // summary:
24721                 //              End key pressed; go to last visible node.
24722
24723                 var node = this.rootNode;
24724                 while(node.isExpanded){
24725                         var c = node.getChildren();
24726                         node = c[c.length - 1];
24727                 }
24728
24729                 if(node && node.isTreeNode){
24730                         this.focusNode(node);
24731                 }
24732         },
24733
24734         // multiCharSearchDuration: Number
24735         //              If multiple characters are typed where each keystroke happens within
24736         //              multiCharSearchDuration of the previous keystroke,
24737         //              search for nodes matching all the keystrokes.
24738         //
24739         //              For example, typing "ab" will search for entries starting with
24740         //              "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
24741         multiCharSearchDuration: 250,
24742
24743         _onLetterKeyNav: function(message){
24744                 // summary:
24745                 //              Called when user presses a prinatable key; search for node starting with recently typed letters.
24746                 // message: Object
24747                 //              Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
24748
24749                 // Branch depending on whether this key starts a new search, or modifies an existing search
24750                 var cs = this._curSearch;
24751                 if(cs){
24752                         // We are continuing a search.  Ex: user has pressed 'a', and now has pressed
24753                         // 'b', so we want to search for nodes starting w/"ab".
24754                         cs.pattern = cs.pattern + message.key;
24755                         clearTimeout(cs.timer);
24756                 }else{
24757                         // We are starting a new search
24758                         cs = this._curSearch = {
24759                                         pattern: message.key,
24760                                         startNode: message.node
24761                         };
24762                 }
24763
24764                 // set/reset timer to forget recent keystrokes
24765                 var self = this;
24766                 cs.timer = setTimeout(function(){
24767                         delete self._curSearch;
24768                 }, this.multiCharSearchDuration);
24769
24770                 // Navigate to TreeNode matching keystrokes [entered so far].
24771                 var node = cs.startNode;
24772                 do{
24773                         node = this._getNextNode(node);
24774                         //check for last node, jump to first node if necessary
24775                         if(!node){
24776                                 node = this._getRootOrFirstNode();
24777                         }
24778                 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
24779                 if(node && node.isTreeNode){
24780                         // no need to set focus if back where we started
24781                         if(node !== cs.startNode){
24782                                 this.focusNode(node);
24783                         }
24784                 }
24785         },
24786
24787         isExpandoNode: function(node, widget){
24788                 // summary:
24789                 //              check whether a dom node is the expandoNode for a particular TreeNode widget
24790                 return dojo.isDescendant(node, widget.expandoNode);
24791         },
24792         _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
24793                 // summary:
24794                 //              Translates click events into commands for the controller to process
24795
24796                 var domElement = e.target,
24797                         isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
24798
24799                 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
24800                         // expando node was clicked, or label of a folder node was clicked; open it
24801                         if(nodeWidget.isExpandable){
24802                                 this._onExpandoClick({node:nodeWidget});
24803                         }
24804                 }else{
24805                         this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
24806                         this.onClick(nodeWidget.item, nodeWidget, e);
24807                         this.focusNode(nodeWidget);
24808                 }
24809                 dojo.stopEvent(e);
24810         },
24811         _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
24812                 // summary:
24813                 //              Translates double-click events into commands for the controller to process
24814
24815                 var domElement = e.target,
24816                         isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
24817
24818                 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
24819                         // expando node was clicked, or label of a folder node was clicked; open it
24820                         if(nodeWidget.isExpandable){
24821                                 this._onExpandoClick({node:nodeWidget});
24822                         }
24823                 }else{
24824                         this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
24825                         this.onDblClick(nodeWidget.item, nodeWidget, e);
24826                         this.focusNode(nodeWidget);
24827                 }
24828                 dojo.stopEvent(e);
24829         },
24830
24831         _onExpandoClick: function(/*Object*/ message){
24832                 // summary:
24833                 //              User clicked the +/- icon; expand or collapse my children.
24834                 var node = message.node;
24835
24836                 // If we are collapsing, we might be hiding the currently focused node.
24837                 // Also, clicking the expando node might have erased focus from the current node.
24838                 // For simplicity's sake just focus on the node with the expando.
24839                 this.focusNode(node);
24840
24841                 if(node.isExpanded){
24842                         this._collapseNode(node);
24843                 }else{
24844                         this._expandNode(node);
24845                 }
24846         },
24847
24848         onClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
24849                 // summary:
24850                 //              Callback when a tree node is clicked
24851                 // tags:
24852                 //              callback
24853         },
24854         onDblClick: function(/* dojo.data */ item, /*TreeNode*/ node, /*Event*/ evt){
24855                 // summary:
24856                 //              Callback when a tree node is double-clicked
24857                 // tags:
24858                 //              callback
24859         },
24860         onOpen: function(/* dojo.data */ item, /*TreeNode*/ node){
24861                 // summary:
24862                 //              Callback when a node is opened
24863                 // tags:
24864                 //              callback
24865         },
24866         onClose: function(/* dojo.data */ item, /*TreeNode*/ node){
24867                 // summary:
24868                 //              Callback when a node is closed
24869                 // tags:
24870                 //              callback
24871         },
24872
24873         _getNextNode: function(node){
24874                 // summary:
24875                 //              Get next visible node
24876
24877                 if(node.isExpandable && node.isExpanded && node.hasChildren()){
24878                         // if this is an expanded node, get the first child
24879                         return node.getChildren()[0];           // _TreeNode
24880                 }else{
24881                         // find a parent node with a sibling
24882                         while(node && node.isTreeNode){
24883                                 var returnNode = node.getNextSibling();
24884                                 if(returnNode){
24885                                         return returnNode;              // _TreeNode
24886                                 }
24887                                 node = node.getParent();
24888                         }
24889                         return null;
24890                 }
24891         },
24892
24893         _getRootOrFirstNode: function(){
24894                 // summary:
24895                 //              Get first visible node
24896                 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
24897         },
24898
24899         _collapseNode: function(/*_TreeNode*/ node){
24900                 // summary:
24901                 //              Called when the user has requested to collapse the node
24902
24903                 if(node._expandNodeDeferred){
24904                         delete node._expandNodeDeferred;
24905                 }
24906
24907                 if(node.isExpandable){
24908                         if(node.state == "LOADING"){
24909                                 // ignore clicks while we are in the process of loading data
24910                                 return;
24911                         }
24912
24913                         node.collapse();
24914                         this.onClose(node.item, node);
24915
24916                         if(node.item){
24917                                 this._state(node.item,false);
24918                                 this._saveState();
24919                         }
24920                 }
24921         },
24922
24923         _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
24924                 // summary:
24925                 //              Called when the user has requested to expand the node
24926                 // recursive:
24927                 //              Internal flag used when _expandNode() calls itself, don't set.
24928                 // returns:
24929                 //              Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
24930                 //              that were previously opened too
24931
24932                 if(node._expandNodeDeferred && !recursive){
24933                         // there's already an expand in progress (or completed), so just return
24934                         return node._expandNodeDeferred;        // dojo.Deferred
24935                 }
24936
24937                 var model = this.model,
24938                         item = node.item,
24939                         _this = this;
24940
24941                 switch(node.state){
24942                         case "UNCHECKED":
24943                                 // need to load all the children, and then expand
24944                                 node.markProcessing();
24945
24946                                 // Setup deferred to signal when the load and expand are finished.
24947                                 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
24948                                 var def = (node._expandNodeDeferred = new dojo.Deferred());
24949
24950                                 // Get the children
24951                                 model.getChildren(
24952                                         item,
24953                                         function(items){
24954                                                 node.unmarkProcessing();
24955
24956                                                 // Display the children and also start expanding any children that were previously expanded
24957                                                 // (if this.persist == true).   The returned Deferred will fire when those expansions finish.
24958                                                 var scid = node.setChildItems(items);
24959
24960                                                 // Call _expandNode() again but this time it will just to do the animation (default branch).
24961                                                 // The returned Deferred will fire when the animation completes.
24962                                                 // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
24963                                                 var ed = _this._expandNode(node, true);
24964
24965                                                 // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
24966                                                 // signal that I am done.
24967                                                 scid.addCallback(function(){
24968                                                         ed.addCallback(function(){
24969                                                                 def.callback();
24970                                                         })
24971                                                 });
24972                                         },
24973                                         function(err){
24974                                                 console.error(_this, ": error loading root children: ", err);
24975                                         }
24976                                 );
24977                                 break;
24978
24979                         default:        // "LOADED"
24980                                 // data is already loaded; just expand node
24981                                 def = (node._expandNodeDeferred = node.expand());
24982
24983                                 this.onOpen(node.item, node);
24984
24985                                 if(item){
24986                                         this._state(item, true);
24987                                         this._saveState();
24988                                 }
24989                 }
24990
24991                 return def;     // dojo.Deferred
24992         },
24993
24994         ////////////////// Miscellaneous functions ////////////////
24995
24996         focusNode: function(/* _tree.Node */ node){
24997                 // summary:
24998                 //              Focus on the specified node (which must be visible)
24999                 // tags:
25000                 //              protected
25001
25002                 // set focus so that the label will be voiced using screen readers
25003                 dijit.focus(node.labelNode);
25004         },
25005
25006         _onNodeFocus: function(/*dijit._Widget*/ node){
25007                 // summary:
25008                 //              Called when a TreeNode gets focus, either by user clicking
25009                 //              it, or programatically by arrow key handling code.
25010                 // description:
25011                 //              It marks that the current node is the selected one, and the previously
25012                 //              selected node no longer is.
25013
25014                 if(node && node != this.lastFocused){
25015                         if(this.lastFocused && !this.lastFocused._destroyed){
25016                                 // mark that the previously focsable node is no longer focusable
25017                                 this.lastFocused.setFocusable(false);
25018                         }
25019
25020                         // mark that the new node is the currently selected one
25021                         node.setFocusable(true);
25022                         this.lastFocused = node;
25023                 }
25024         },
25025
25026         _onNodeMouseEnter: function(/*dijit._Widget*/ node){
25027                 // summary:
25028                 //              Called when mouse is over a node (onmouseenter event),
25029                 //              this is monitored by the DND code
25030         },
25031
25032         _onNodeMouseLeave: function(/*dijit._Widget*/ node){
25033                 // summary:
25034                 //              Called when mouse leaves a node (onmouseleave event),
25035                 //              this is monitored by the DND code
25036         },
25037
25038         //////////////// Events from the model //////////////////////////
25039
25040         _onItemChange: function(/*Item*/ item){
25041                 // summary:
25042                 //              Processes notification of a change to an item's scalar values like label
25043                 var model = this.model,
25044                         identity = model.getIdentity(item),
25045                         nodes = this._itemNodesMap[identity];
25046
25047                 if(nodes){
25048                         var label = this.getLabel(item),
25049                                 tooltip = this.getTooltip(item);
25050                         dojo.forEach(nodes, function(node){
25051                                 node.set({
25052                                         item: item,             // theoretically could be new JS Object representing same item
25053                                         label: label,
25054                                         tooltip: tooltip
25055                                 });
25056                                 node._updateItemClasses(item);
25057                         });
25058                 }
25059         },
25060
25061         _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
25062                 // summary:
25063                 //              Processes notification of a change to an item's children
25064                 var model = this.model,
25065                         identity = model.getIdentity(parent),
25066                         parentNodes = this._itemNodesMap[identity];
25067
25068                 if(parentNodes){
25069                         dojo.forEach(parentNodes,function(parentNode){
25070                                 parentNode.setChildItems(newChildrenList);
25071                         });
25072                 }
25073         },
25074
25075         _onItemDelete: function(/*Item*/ item){
25076                 // summary:
25077                 //              Processes notification of a deletion of an item
25078                 var model = this.model,
25079                         identity = model.getIdentity(item),
25080                         nodes = this._itemNodesMap[identity];
25081
25082                 if(nodes){
25083                         dojo.forEach(nodes,function(node){
25084                                 // Remove node from set of selected nodes (if it's selected)
25085                                 this.dndController.removeTreeNode(node);
25086
25087                                 var parent = node.getParent();
25088                                 if(parent){
25089                                         // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
25090                                         parent.removeChild(node);
25091                                 }
25092                                 node.destroyRecursive();
25093                         }, this);
25094                         delete this._itemNodesMap[identity];
25095                 }
25096         },
25097
25098         /////////////// Miscellaneous funcs
25099
25100         _initState: function(){
25101                 // summary:
25102                 //              Load in which nodes should be opened automatically
25103                 if(this.persist){
25104                         var cookie = dojo.cookie(this.cookieName);
25105                         this._openedItemIds = {};
25106                         if(cookie){
25107                                 dojo.forEach(cookie.split(','), function(item){
25108                                         this._openedItemIds[item] = true;
25109                                 }, this);
25110                         }
25111                 }
25112         },
25113         _state: function(item,expanded){
25114                 // summary:
25115                 //              Query or set expanded state for an item,
25116                 if(!this.persist){
25117                         return false;
25118                 }
25119                 var id=this.model.getIdentity(item);
25120                 if(arguments.length === 1){
25121                         return this._openedItemIds[id];
25122                 }
25123                 if(expanded){
25124                         this._openedItemIds[id] = true;
25125                 }else{
25126                         delete this._openedItemIds[id];
25127                 }
25128         },
25129         _saveState: function(){
25130                 // summary:
25131                 //              Create and save a cookie with the currently expanded nodes identifiers
25132                 if(!this.persist){
25133                         return;
25134                 }
25135                 var ary = [];
25136                 for(var id in this._openedItemIds){
25137                         ary.push(id);
25138                 }
25139                 dojo.cookie(this.cookieName, ary.join(","), {expires:365});
25140         },
25141
25142         destroy: function(){
25143                 if(this._curSearch){
25144                         clearTimeout(this._curSearch.timer);
25145                         delete this._curSearch;
25146                 }
25147                 if(this.rootNode){
25148                         this.rootNode.destroyRecursive();
25149                 }
25150                 if(this.dndController && !dojo.isString(this.dndController)){
25151                         this.dndController.destroy();
25152                 }
25153                 this.rootNode = null;
25154                 this.inherited(arguments);
25155         },
25156
25157         destroyRecursive: function(){
25158                 // A tree is treated as a leaf, not as a node with children (like a grid),
25159                 // but defining destroyRecursive for back-compat.
25160                 this.destroy();
25161         },
25162
25163         resize: function(changeSize){
25164                 if(changeSize){
25165                         dojo.marginBox(this.domNode, changeSize);
25166                 }
25167
25168                 // The only JS sizing involved w/tree is the indentation, which is specified
25169                 // in CSS and read in through this dummy indentDetector node (tree must be
25170                 // visible and attached to the DOM to read this)
25171                 this._nodePixelIndent = dojo._getMarginSize(this.tree.indentDetector).w;
25172
25173                 if(this.tree.rootNode){
25174                         // If tree has already loaded, then reset indent for all the nodes
25175                         this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
25176                 }
25177         },
25178
25179         _createTreeNode: function(/*Object*/ args){
25180                 // summary:
25181                 //              creates a TreeNode
25182                 // description:
25183                 //              Developers can override this method to define their own TreeNode class;
25184                 //              However it will probably be removed in a future release in favor of a way
25185                 //              of just specifying a widget for the label, rather than one that contains
25186                 //              the children too.
25187                 return new dijit._TreeNode(args);
25188         }
25189 });
25190
25191 // For back-compat.  TODO: remove in 2.0
25192
25193 }
25194
25195 if(!dojo._hasResource["dojo.dnd.Avatar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25196 dojo._hasResource["dojo.dnd.Avatar"] = true;
25197 dojo.provide("dojo.dnd.Avatar");
25198
25199
25200
25201 dojo.declare("dojo.dnd.Avatar", null, {
25202         // summary:
25203         //              Object that represents transferred DnD items visually
25204         // manager: Object
25205         //              a DnD manager object
25206
25207         constructor: function(manager){
25208                 this.manager = manager;
25209                 this.construct();
25210         },
25211
25212         // methods
25213         construct: function(){
25214                 // summary:
25215                 //              constructor function;
25216                 //              it is separate so it can be (dynamically) overwritten in case of need
25217                 this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
25218                 var a = dojo.create("table", {
25219                                 "class": "dojoDndAvatar",
25220                                 style: {
25221                                         position: "absolute",
25222                                         zIndex:   "1999",
25223                                         margin:   "0px"
25224                                 }
25225                         }),
25226                         source = this.manager.source, node,
25227                         b = dojo.create("tbody", null, a),
25228                         tr = dojo.create("tr", null, b),
25229                         td = dojo.create("td", null, tr),
25230                         icon = this.isA11y ? dojo.create("span", {
25231                                                 id : "a11yIcon",
25232                                                 innerHTML : this.manager.copy ? '+' : "<"
25233                                         }, td) : null,
25234                         span = dojo.create("span", {
25235                                 innerHTML: source.generateText ? this._generateText() : ""
25236                         }, td),
25237                         k = Math.min(5, this.manager.nodes.length), i = 0;
25238                 // we have to set the opacity on IE only after the node is live
25239                 dojo.attr(tr, {
25240                         "class": "dojoDndAvatarHeader",
25241                         style: {opacity: 0.9}
25242                 });
25243                 for(; i < k; ++i){
25244                         if(source.creator){
25245                                 // create an avatar representation of the node
25246                                 node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
25247                         }else{
25248                                 // or just clone the node and hope it works
25249                                 node = this.manager.nodes[i].cloneNode(true);
25250                                 if(node.tagName.toLowerCase() == "tr"){
25251                                         // insert extra table nodes
25252                                         var table = dojo.create("table"),
25253                                                 tbody = dojo.create("tbody", null, table);
25254                                         tbody.appendChild(node);
25255                                         node = table;
25256                                 }
25257                         }
25258                         node.id = "";
25259                         tr = dojo.create("tr", null, b);
25260                         td = dojo.create("td", null, tr);
25261                         td.appendChild(node);
25262                         dojo.attr(tr, {
25263                                 "class": "dojoDndAvatarItem",
25264                                 style: {opacity: (9 - i) / 10}
25265                         });
25266                 }
25267                 this.node = a;
25268         },
25269         destroy: function(){
25270                 // summary:
25271                 //              destructor for the avatar; called to remove all references so it can be garbage-collected
25272                 dojo.destroy(this.node);
25273                 this.node = false;
25274         },
25275         update: function(){
25276                 // summary:
25277                 //              updates the avatar to reflect the current DnD state
25278                 dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
25279                 if (this.isA11y){
25280                         var icon = dojo.byId("a11yIcon");
25281                         var text = '+';   // assume canDrop && copy
25282                         if (this.manager.canDropFlag && !this.manager.copy) {
25283                                 text = '< '; // canDrop && move
25284                         }else if (!this.manager.canDropFlag && !this.manager.copy) {
25285                                 text = "o"; //!canDrop && move
25286                         }else if(!this.manager.canDropFlag){
25287                                 text = 'x';  // !canDrop && copy
25288                         }
25289                         icon.innerHTML=text;
25290                 }
25291                 // replace text
25292                 dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
25293                         function(node){
25294                                 node.innerHTML = this._generateText();
25295                         }, this);
25296         },
25297         _generateText: function(){
25298                 // summary: generates a proper text to reflect copying or moving of items
25299                 return this.manager.nodes.length.toString();
25300         }
25301 });
25302
25303 }
25304
25305 if(!dojo._hasResource["dojo.dnd.Manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25306 dojo._hasResource["dojo.dnd.Manager"] = true;
25307 dojo.provide("dojo.dnd.Manager");
25308
25309
25310
25311
25312
25313 dojo.declare("dojo.dnd.Manager", null, {
25314         // summary:
25315         //              the manager of DnD operations (usually a singleton)
25316         constructor: function(){
25317                 this.avatar  = null;
25318                 this.source = null;
25319                 this.nodes = [];
25320                 this.copy  = true;
25321                 this.target = null;
25322                 this.canDropFlag = false;
25323                 this.events = [];
25324         },
25325
25326         // avatar's offset from the mouse
25327         OFFSET_X: 16,
25328         OFFSET_Y: 16,
25329         
25330         // methods
25331         overSource: function(source){
25332                 // summary:
25333                 //              called when a source detected a mouse-over condition
25334                 // source: Object
25335                 //              the reporter
25336                 if(this.avatar){
25337                         this.target = (source && source.targetState != "Disabled") ? source : null;
25338                         this.canDropFlag = Boolean(this.target);
25339                         this.avatar.update();
25340                 }
25341                 dojo.publish("/dnd/source/over", [source]);
25342         },
25343         outSource: function(source){
25344                 // summary:
25345                 //              called when a source detected a mouse-out condition
25346                 // source: Object
25347                 //              the reporter
25348                 if(this.avatar){
25349                         if(this.target == source){
25350                                 this.target = null;
25351                                 this.canDropFlag = false;
25352                                 this.avatar.update();
25353                                 dojo.publish("/dnd/source/over", [null]);
25354                         }
25355                 }else{
25356                         dojo.publish("/dnd/source/over", [null]);
25357                 }
25358         },
25359         startDrag: function(source, nodes, copy){
25360                 // summary:
25361                 //              called to initiate the DnD operation
25362                 // source: Object
25363                 //              the source which provides items
25364                 // nodes: Array
25365                 //              the list of transferred items
25366                 // copy: Boolean
25367                 //              copy items, if true, move items otherwise
25368                 this.source = source;
25369                 this.nodes  = nodes;
25370                 this.copy   = Boolean(copy); // normalizing to true boolean
25371                 this.avatar = this.makeAvatar();
25372                 dojo.body().appendChild(this.avatar.node);
25373                 dojo.publish("/dnd/start", [source, nodes, this.copy]);
25374                 this.events = [
25375                         dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
25376                         dojo.connect(dojo.doc, "onmouseup",   this, "onMouseUp"),
25377                         dojo.connect(dojo.doc, "onkeydown",   this, "onKeyDown"),
25378                         dojo.connect(dojo.doc, "onkeyup",     this, "onKeyUp"),
25379                         // cancel text selection and text dragging
25380                         dojo.connect(dojo.doc, "ondragstart",   dojo.stopEvent),
25381                         dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
25382                 ];
25383                 var c = "dojoDnd" + (copy ? "Copy" : "Move");
25384                 dojo.addClass(dojo.body(), c);
25385         },
25386         canDrop: function(flag){
25387                 // summary:
25388                 //              called to notify if the current target can accept items
25389                 var canDropFlag = Boolean(this.target && flag);
25390                 if(this.canDropFlag != canDropFlag){
25391                         this.canDropFlag = canDropFlag;
25392                         this.avatar.update();
25393                 }
25394         },
25395         stopDrag: function(){
25396                 // summary:
25397                 //              stop the DnD in progress
25398                 dojo.removeClass(dojo.body(), ["dojoDndCopy", "dojoDndMove"]);
25399                 dojo.forEach(this.events, dojo.disconnect);
25400                 this.events = [];
25401                 this.avatar.destroy();
25402                 this.avatar = null;
25403                 this.source = this.target = null;
25404                 this.nodes = [];
25405         },
25406         makeAvatar: function(){
25407                 // summary:
25408                 //              makes the avatar; it is separate to be overwritten dynamically, if needed
25409                 return new dojo.dnd.Avatar(this);
25410         },
25411         updateAvatar: function(){
25412                 // summary:
25413                 //              updates the avatar; it is separate to be overwritten dynamically, if needed
25414                 this.avatar.update();
25415         },
25416         
25417         // mouse event processors
25418         onMouseMove: function(e){
25419                 // summary:
25420                 //              event processor for onmousemove
25421                 // e: Event
25422                 //              mouse event
25423                 var a = this.avatar;
25424                 if(a){
25425                         dojo.dnd.autoScrollNodes(e);
25426                         //dojo.dnd.autoScroll(e);
25427                         var s = a.node.style;
25428                         s.left = (e.pageX + this.OFFSET_X) + "px";
25429                         s.top  = (e.pageY + this.OFFSET_Y) + "px";
25430                         var copy = Boolean(this.source.copyState(dojo.isCopyKey(e)));
25431                         if(this.copy != copy){
25432                                 this._setCopyStatus(copy);
25433                         }
25434                 }
25435         },
25436         onMouseUp: function(e){
25437                 // summary:
25438                 //              event processor for onmouseup
25439                 // e: Event
25440                 //              mouse event
25441                 if(this.avatar){
25442                         if(this.target && this.canDropFlag){
25443                                 var copy = Boolean(this.source.copyState(dojo.isCopyKey(e))),
25444                                 params = [this.source, this.nodes, copy, this.target, e];
25445                                 dojo.publish("/dnd/drop/before", params);
25446                                 dojo.publish("/dnd/drop", params);
25447                         }else{
25448                                 dojo.publish("/dnd/cancel");
25449                         }
25450                         this.stopDrag();
25451                 }
25452         },
25453         
25454         // keyboard event processors
25455         onKeyDown: function(e){
25456                 // summary:
25457                 //              event processor for onkeydown:
25458                 //              watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
25459                 // e: Event
25460                 //              keyboard event
25461                 if(this.avatar){
25462                         switch(e.keyCode){
25463                                 case dojo.keys.CTRL:
25464                                         var copy = Boolean(this.source.copyState(true));
25465                                         if(this.copy != copy){
25466                                                 this._setCopyStatus(copy);
25467                                         }
25468                                         break;
25469                                 case dojo.keys.ESCAPE:
25470                                         dojo.publish("/dnd/cancel");
25471                                         this.stopDrag();
25472                                         break;
25473                         }
25474                 }
25475         },
25476         onKeyUp: function(e){
25477                 // summary:
25478                 //              event processor for onkeyup, watching for CTRL for copy/move status
25479                 // e: Event
25480                 //              keyboard event
25481                 if(this.avatar && e.keyCode == dojo.keys.CTRL){
25482                         var copy = Boolean(this.source.copyState(false));
25483                         if(this.copy != copy){
25484                                 this._setCopyStatus(copy);
25485                         }
25486                 }
25487         },
25488         
25489         // utilities
25490         _setCopyStatus: function(copy){
25491                 // summary:
25492                 //              changes the copy status
25493                 // copy: Boolean
25494                 //              the copy status
25495                 this.copy = copy;
25496                 this.source._markDndStatus(this.copy);
25497                 this.updateAvatar();
25498                 dojo.replaceClass(dojo.body(),
25499                         "dojoDnd" + (this.copy ? "Copy" : "Move"),
25500                         "dojoDnd" + (this.copy ? "Move" : "Copy"));
25501         }
25502 });
25503
25504 // dojo.dnd._manager:
25505 //              The manager singleton variable. Can be overwritten if needed.
25506 dojo.dnd._manager = null;
25507
25508 dojo.dnd.manager = function(){
25509         // summary:
25510         //              Returns the current DnD manager.  Creates one if it is not created yet.
25511         if(!dojo.dnd._manager){
25512                 dojo.dnd._manager = new dojo.dnd.Manager();
25513         }
25514         return dojo.dnd._manager;       // Object
25515 };
25516
25517 }
25518
25519 if(!dojo._hasResource["dijit.tree.dndSource"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
25520 dojo._hasResource["dijit.tree.dndSource"] = true;
25521 dojo.provide("dijit.tree.dndSource");
25522
25523
25524
25525
25526 /*=====
25527 dijit.tree.__SourceArgs = function(){
25528         // summary:
25529         //              A dict of parameters for Tree source configuration.
25530         // isSource: Boolean?
25531         //              Can be used as a DnD source. Defaults to true.
25532         // accept: String[]
25533         //              List of accepted types (text strings) for a target; defaults to
25534         //              ["text", "treeNode"]
25535         // copyOnly: Boolean?
25536         //              Copy items, if true, use a state of Ctrl key otherwise,
25537         // dragThreshold: Number
25538         //              The move delay in pixels before detecting a drag; 0 by default
25539         // betweenThreshold: Integer
25540         //              Distance from upper/lower edge of node to allow drop to reorder nodes
25541         this.isSource = isSource;
25542         this.accept = accept;
25543         this.autoSync = autoSync;
25544         this.copyOnly = copyOnly;
25545         this.dragThreshold = dragThreshold;
25546         this.betweenThreshold = betweenThreshold;
25547 }
25548 =====*/
25549
25550 dojo.declare("dijit.tree.dndSource", dijit.tree._dndSelector, {
25551         // summary:
25552         //              Handles drag and drop operations (as a source or a target) for `dijit.Tree`
25553
25554         // isSource: [private] Boolean
25555         //              Can be used as a DnD source.
25556         isSource: true,
25557
25558         // accept: String[]
25559         //              List of accepted types (text strings) for the Tree; defaults to
25560         //              ["text"]
25561         accept: ["text", "treeNode"],
25562
25563         // copyOnly: [private] Boolean
25564         //              Copy items, if true, use a state of Ctrl key otherwise
25565         copyOnly: false,
25566
25567         // dragThreshold: Number
25568         //              The move delay in pixels before detecting a drag; 5 by default
25569         dragThreshold: 5,
25570
25571         // betweenThreshold: Integer
25572         //              Distance from upper/lower edge of node to allow drop to reorder nodes
25573         betweenThreshold: 0,
25574
25575         constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
25576                 // summary:
25577                 //              a constructor of the Tree DnD Source
25578                 // tags:
25579                 //              private
25580                 if(!params){ params = {}; }
25581                 dojo.mixin(this, params);
25582                 this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
25583                 var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
25584                 this.accept = null;
25585                 if(type.length){
25586                         this.accept = {};
25587                         for(var i = 0; i < type.length; ++i){
25588                                 this.accept[type[i]] = 1;
25589                         }
25590                 }
25591
25592                 // class-specific variables
25593                 this.isDragging = false;
25594                 this.mouseDown = false;
25595                 this.targetAnchor = null;       // DOMNode corresponding to the currently moused over TreeNode
25596                 this.targetBox = null;  // coordinates of this.targetAnchor
25597                 this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
25598                 this._lastX = 0;
25599                 this._lastY = 0;
25600
25601                 // states
25602                 this.sourceState = "";
25603                 if(this.isSource){
25604                         dojo.addClass(this.node, "dojoDndSource");
25605                 }
25606                 this.targetState = "";
25607                 if(this.accept){
25608                         dojo.addClass(this.node, "dojoDndTarget");
25609                 }
25610
25611                 // set up events
25612                 this.topics = [
25613                         dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
25614                         dojo.subscribe("/dnd/start", this, "onDndStart"),
25615                         dojo.subscribe("/dnd/drop", this, "onDndDrop"),
25616                         dojo.subscribe("/dnd/cancel", this, "onDndCancel")
25617                 ];
25618         },
25619
25620         // methods
25621         checkAcceptance: function(source, nodes){
25622                 // summary:
25623                 //              Checks if the target can accept nodes from this source
25624                 // source: dijit.tree.dndSource
25625                 //              The source which provides items
25626                 // nodes: DOMNode[]
25627                 //              Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
25628                 //              source is a dijit.Tree.
25629                 // tags:
25630                 //              extension
25631                 return true;    // Boolean
25632         },
25633
25634         copyState: function(keyPressed){
25635                 // summary:
25636                 //              Returns true, if we need to copy items, false to move.
25637                 //              It is separated to be overwritten dynamically, if needed.
25638                 // keyPressed: Boolean
25639                 //              The "copy" control key was pressed
25640                 // tags:
25641                 //              protected
25642                 return this.copyOnly || keyPressed;     // Boolean
25643         },
25644         destroy: function(){
25645                 // summary:
25646                 //              Prepares the object to be garbage-collected.
25647                 this.inherited("destroy",arguments);
25648                 dojo.forEach(this.topics, dojo.unsubscribe);
25649                 this.targetAnchor = null;
25650         },
25651
25652         _onDragMouse: function(e){
25653                 // summary:
25654                 //              Helper method for processing onmousemove/onmouseover events while drag is in progress.
25655                 //              Keeps track of current drop target.
25656
25657                 var m = dojo.dnd.manager(),
25658                         oldTarget = this.targetAnchor,                  // the TreeNode corresponding to TreeNode mouse was previously over
25659                         newTarget = this.current,                               // TreeNode corresponding to TreeNode mouse is currently over
25660                         oldDropPosition = this.dropPosition;    // the previous drop position (over/before/after)
25661
25662                 // calculate if user is indicating to drop the dragged node before, after, or over
25663                 // (i.e., to become a child of) the target node
25664                 var newDropPosition = "Over";
25665                 if(newTarget && this.betweenThreshold > 0){
25666                         // If mouse is over a new TreeNode, then get new TreeNode's position and size
25667                         if(!this.targetBox || oldTarget != newTarget){
25668                                 this.targetBox = dojo.position(newTarget.rowNode, true);
25669                         }
25670                         if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
25671                                 newDropPosition = "Before";
25672                         }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
25673                                 newDropPosition = "After";
25674                         }
25675                 }
25676
25677                 if(newTarget != oldTarget || newDropPosition != oldDropPosition){
25678                         if(oldTarget){
25679                                 this._removeItemClass(oldTarget.rowNode, oldDropPosition);
25680                         }
25681                         if(newTarget){
25682                                 this._addItemClass(newTarget.rowNode, newDropPosition);
25683                         }
25684
25685                         // Check if it's ok to drop the dragged node on/before/after the target node.
25686                         if(!newTarget){
25687                                 m.canDrop(false);
25688                         }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
25689                                 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
25690                                 m.canDrop(false);
25691                         }else if(m.source == this && (newTarget.id in this.selection)){
25692                                 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
25693                                 m.canDrop(false);
25694                         }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
25695                                         && !this._isParentChildDrop(m.source, newTarget.rowNode)){
25696                                 m.canDrop(true);
25697                         }else{
25698                                 m.canDrop(false);
25699                         }
25700
25701                         this.targetAnchor = newTarget;
25702                         this.dropPosition = newDropPosition;
25703                 }
25704         },
25705
25706         onMouseMove: function(e){
25707                 // summary:
25708                 //              Called for any onmousemove events over the Tree
25709                 // e: Event
25710                 //              onmousemouse event
25711                 // tags:
25712                 //              private
25713                 if(this.isDragging && this.targetState == "Disabled"){ return; }
25714                 this.inherited(arguments);
25715                 var m = dojo.dnd.manager();
25716                 if(this.isDragging){
25717                         this._onDragMouse(e);
25718                 }else{
25719                         if(this.mouseDown && this.isSource &&
25720                                  (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
25721                                 var nodes = this.getSelectedTreeNodes();
25722                                 if(nodes.length){
25723                                         if(nodes.length > 1){
25724                                                 //filter out all selected items which has one of their ancestor selected as well
25725                                                 var seen = this.selection, i = 0, r = [], n, p;
25726                                                 nextitem: while((n = nodes[i++])){
25727                                                         for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
25728                                                                 if(seen[p.id]){ //parent is already selected, skip this node
25729                                                                         continue nextitem;
25730                                                                 }
25731                                                         }
25732                                                         //this node does not have any ancestors selected, add it
25733                                                         r.push(n);
25734                                                 }
25735                                                 nodes = r;
25736                                         }
25737                                         nodes = dojo.map(nodes, function(n){return n.domNode});
25738                                         m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
25739                                 }
25740                         }
25741                 }
25742         },
25743
25744         onMouseDown: function(e){
25745                 // summary:
25746                 //              Event processor for onmousedown
25747                 // e: Event
25748                 //              onmousedown event
25749                 // tags:
25750                 //              private
25751                 this.mouseDown = true;
25752                 this.mouseButton = e.button;
25753                 this._lastX = e.pageX;
25754                 this._lastY = e.pageY;
25755                 this.inherited(arguments);
25756         },
25757
25758         onMouseUp: function(e){
25759                 // summary:
25760                 //              Event processor for onmouseup
25761                 // e: Event
25762                 //              onmouseup event
25763                 // tags:
25764                 //              private
25765                 if(this.mouseDown){
25766                         this.mouseDown = false;
25767                         this.inherited(arguments);
25768                 }
25769         },
25770
25771         onMouseOut: function(){
25772                 // summary:
25773                 //              Event processor for when mouse is moved away from a TreeNode
25774                 // tags:
25775                 //              private
25776                 this.inherited(arguments);
25777                 this._unmarkTargetAnchor();
25778         },
25779
25780         checkItemAcceptance: function(target, source, position){
25781                 // summary:
25782                 //              Stub function to be overridden if one wants to check for the ability to drop at the node/item level
25783                 // description:
25784                 //              In the base case, this is called to check if target can become a child of source.
25785                 //              When betweenThreshold is set, position="before" or "after" means that we
25786                 //              are asking if the source node can be dropped before/after the target node.
25787                 // target: DOMNode
25788                 //              The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
25789                 //              Use dijit.getEnclosingWidget(target) to get the TreeNode.
25790                 // source: dijit.tree.dndSource
25791                 //              The (set of) nodes we are dropping
25792                 // position: String
25793                 //              "over", "before", or "after"
25794                 // tags:
25795                 //              extension
25796                 return true;
25797         },
25798
25799         // topic event processors
25800         onDndSourceOver: function(source){
25801                 // summary:
25802                 //              Topic event processor for /dnd/source/over, called when detected a current source.
25803                 // source: Object
25804                 //              The dijit.tree.dndSource / dojo.dnd.Source which has the mouse over it
25805                 // tags:
25806                 //              private
25807                 if(this != source){
25808                         this.mouseDown = false;
25809                         this._unmarkTargetAnchor();
25810                 }else if(this.isDragging){
25811                         var m = dojo.dnd.manager();
25812                         m.canDrop(false);
25813                 }
25814         },
25815         onDndStart: function(source, nodes, copy){
25816                 // summary:
25817                 //              Topic event processor for /dnd/start, called to initiate the DnD operation
25818                 // source: Object
25819                 //              The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
25820                 // nodes: DomNode[]
25821                 //              The list of transferred items, dndTreeNode nodes if dragging from a Tree
25822                 // copy: Boolean
25823                 //              Copy items, if true, move items otherwise
25824                 // tags:
25825                 //              private
25826
25827                 if(this.isSource){
25828                         this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
25829                 }
25830                 var accepted = this.checkAcceptance(source, nodes);
25831
25832                 this._changeState("Target", accepted ? "" : "Disabled");
25833
25834                 if(this == source){
25835                         dojo.dnd.manager().overSource(this);
25836                 }
25837
25838                 this.isDragging = true;
25839         },
25840
25841         itemCreator: function(/*DomNode[]*/ nodes, target, /*dojo.dnd.Source*/ source){
25842                 // summary:
25843                 //              Returns objects passed to `Tree.model.newItem()` based on DnD nodes
25844                 //              dropped onto the tree.   Developer must override this method to enable
25845                 //              dropping from external sources onto this Tree, unless the Tree.model's items
25846                 //              happen to look like {id: 123, name: "Apple" } with no other attributes.
25847                 // description:
25848                 //              For each node in nodes[], which came from source, create a hash of name/value
25849                 //              pairs to be passed to Tree.model.newItem().  Returns array of those hashes.
25850                 // returns: Object[]
25851                 //              Array of name/value hashes for each new item to be added to the Tree, like:
25852                 // |    [
25853                 // |            { id: 123, label: "apple", foo: "bar" },
25854                 // |            { id: 456, label: "pear", zaz: "bam" }
25855                 // |    ]
25856                 // tags:
25857                 //              extension
25858
25859                 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
25860                 // make signature itemCreator(sourceItem, node, target) (or similar).
25861
25862                 return dojo.map(nodes, function(node){
25863                         return {
25864                                 "id": node.id,
25865                                 "name": node.textContent || node.innerText || ""
25866                         };
25867                 }); // Object[]
25868         },
25869
25870         onDndDrop: function(source, nodes, copy){
25871                 // summary:
25872                 //              Topic event processor for /dnd/drop, called to finish the DnD operation.
25873                 // description:
25874                 //              Updates data store items according to where node was dragged from and dropped
25875                 //              to.   The tree will then respond to those data store updates and redraw itself.
25876                 // source: Object
25877                 //              The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
25878                 // nodes: DomNode[]
25879                 //              The list of transferred items, dndTreeNode nodes if dragging from a Tree
25880                 // copy: Boolean
25881                 //              Copy items, if true, move items otherwise
25882                 // tags:
25883                 //              protected
25884                 if(this.containerState == "Over"){
25885                         var tree = this.tree,
25886                                 model = tree.model,
25887                                 target = this.targetAnchor,
25888                                 requeryRoot = false;    // set to true iff top level items change
25889
25890                         this.isDragging = false;
25891
25892                         // Compute the new parent item
25893                         var targetWidget = target;
25894                         var newParentItem;
25895                         var insertIndex;
25896                         newParentItem = (targetWidget && targetWidget.item) || tree.item;
25897                         if(this.dropPosition == "Before" || this.dropPosition == "After"){
25898                                 // TODO: if there is no parent item then disallow the drop.
25899                                 // Actually this should be checked during onMouseMove too, to make the drag icon red.
25900                                 newParentItem = (targetWidget.getParent() && targetWidget.getParent().item) || tree.item;
25901                                 // Compute the insert index for reordering
25902                                 insertIndex = targetWidget.getIndexInParent();
25903                                 if(this.dropPosition == "After"){
25904                                         insertIndex = targetWidget.getIndexInParent() + 1;
25905                                 }
25906                         }else{
25907                                 newParentItem = (targetWidget && targetWidget.item) || tree.item;
25908                         }
25909
25910                         // If necessary, use this variable to hold array of hashes to pass to model.newItem()
25911                         // (one entry in the array for each dragged node).
25912                         var newItemsParams;
25913
25914                         dojo.forEach(nodes, function(node, idx){
25915                                 // dojo.dnd.Item representing the thing being dropped.
25916                                 // Don't confuse the use of item here (meaning a DnD item) with the
25917                                 // uses below where item means dojo.data item.
25918                                 var sourceItem = source.getItem(node.id);
25919
25920                                 // Information that's available if the source is another Tree
25921                                 // (possibly but not necessarily this tree, possibly but not
25922                                 // necessarily the same model as this Tree)
25923                                 if(dojo.indexOf(sourceItem.type, "treeNode") != -1){
25924                                         var childTreeNode = sourceItem.data,
25925                                                 childItem = childTreeNode.item,
25926                                                 oldParentItem = childTreeNode.getParent().item;
25927                                 }
25928
25929                                 if(source == this){
25930                                         // This is a node from my own tree, and we are moving it, not copying.
25931                                         // Remove item from old parent's children attribute.
25932                                         // TODO: dijit.tree.dndSelector should implement deleteSelectedNodes()
25933                                         // and this code should go there.
25934
25935                                         if(typeof insertIndex == "number"){
25936                                                 if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
25937                                                         insertIndex -= 1;
25938                                                 }
25939                                         }
25940                                         model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
25941                                 }else if(model.isItem(childItem)){
25942                                         // Item from same model
25943                                         // (maybe we should only do this branch if the source is a tree?)
25944                                         model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
25945                                 }else{
25946                                         // Get the hash to pass to model.newItem().  A single call to
25947                                         // itemCreator() returns an array of hashes, one for each drag source node.
25948                                         if(!newItemsParams){
25949                                                 newItemsParams = this.itemCreator(nodes, target.rowNode, source);
25950                                         }
25951
25952                                         // Create new item in the tree, based on the drag source.
25953                                         model.newItem(newItemsParams[idx], newParentItem, insertIndex);
25954                                 }
25955                         }, this);
25956
25957                         // Expand the target node (if it's currently collapsed) so the user can see
25958                         // where their node was dropped.   In particular since that node is still selected.
25959                         this.tree._expandNode(targetWidget);
25960                 }
25961                 this.onDndCancel();
25962         },
25963
25964         onDndCancel: function(){
25965                 // summary:
25966                 //              Topic event processor for /dnd/cancel, called to cancel the DnD operation
25967                 // tags:
25968                 //              private
25969                 this._unmarkTargetAnchor();
25970                 this.isDragging = false;
25971                 this.mouseDown = false;
25972                 delete this.mouseButton;
25973                 this._changeState("Source", "");
25974                 this._changeState("Target", "");
25975         },
25976
25977         // When focus moves in/out of the entire Tree
25978         onOverEvent: function(){
25979                 // summary:
25980                 //              This method is called when mouse is moved over our container (like onmouseenter)
25981                 // tags:
25982                 //              private
25983                 this.inherited(arguments);
25984                 dojo.dnd.manager().overSource(this);
25985         },
25986         onOutEvent: function(){
25987                 // summary:
25988                 //              This method is called when mouse is moved out of our container (like onmouseleave)
25989                 // tags:
25990                 //              private
25991                 this._unmarkTargetAnchor();
25992                 var m = dojo.dnd.manager();
25993                 if(this.isDragging){
25994                         m.canDrop(false);
25995                 }
25996                 m.outSource(this);
25997
25998                 this.inherited(arguments);
25999         },
26000
26001         _isParentChildDrop: function(source, targetRow){
26002                 // summary:
26003                 //              Checks whether the dragged items are parent rows in the tree which are being
26004                 //              dragged into their own children.
26005                 //
26006                 // source:
26007                 //              The DragSource object.
26008                 //
26009                 // targetRow:
26010                 //              The tree row onto which the dragged nodes are being dropped.
26011                 //
26012                 // tags:
26013                 //              private
26014
26015                 // If the dragged object is not coming from the tree this widget belongs to,
26016                 // it cannot be invalid.
26017                 if(!source.tree || source.tree != this.tree){
26018                         return false;
26019                 }
26020
26021
26022                 var root = source.tree.domNode;
26023                 var ids = source.selection;
26024
26025                 var node = targetRow.parentNode;
26026
26027                 // Iterate up the DOM hierarchy from the target drop row,
26028                 // checking of any of the dragged nodes have the same ID.
26029                 while(node != root && !ids[node.id]){
26030                         node = node.parentNode;
26031                 }
26032
26033                 return node.id && ids[node.id];
26034         },
26035
26036         _unmarkTargetAnchor: function(){
26037                 // summary:
26038                 //              Removes hover class of the current target anchor
26039                 // tags:
26040                 //              private
26041                 if(!this.targetAnchor){ return; }
26042                 this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
26043                 this.targetAnchor = null;
26044                 this.targetBox = null;
26045                 this.dropPosition = null;
26046         },
26047
26048         _markDndStatus: function(copy){
26049                 // summary:
26050                 //              Changes source's state based on "copy" status
26051                 this._changeState("Source", copy ? "Copied" : "Moved");
26052         }
26053 });
26054
26055 }
26056
26057 if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
26058 dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
26059 dojo.provide("dojo.data.ItemFileReadStore");
26060
26061
26062
26063
26064
26065 dojo.declare("dojo.data.ItemFileReadStore", null,{
26066         //      summary:
26067         //              The ItemFileReadStore implements the dojo.data.api.Read API and reads
26068         //              data from JSON files that have contents in this format --
26069         //              { items: [
26070         //                      { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
26071         //                      { name:'Fozzie Bear', wears:['hat', 'tie']},
26072         //                      { name:'Miss Piggy', pets:'Foo-Foo'}
26073         //              ]}
26074         //              Note that it can also contain an 'identifer' property that specified which attribute on the items
26075         //              in the array of items that acts as the unique identifier for that item.
26076         //
26077         constructor: function(/* Object */ keywordParameters){
26078                 //      summary: constructor
26079                 //      keywordParameters: {url: String}
26080                 //      keywordParameters: {data: jsonObject}
26081                 //      keywordParameters: {typeMap: object)
26082                 //              The structure of the typeMap object is as follows:
26083                 //              {
26084                 //                      type0: function || object,
26085                 //                      type1: function || object,
26086                 //                      ...
26087                 //                      typeN: function || object
26088                 //              }
26089                 //              Where if it is a function, it is assumed to be an object constructor that takes the
26090                 //              value of _value as the initialization parameters.  If it is an object, then it is assumed
26091                 //              to be an object of general form:
26092                 //              {
26093                 //                      type: function, //constructor.
26094                 //                      deserialize:    function(value) //The function that parses the value and constructs the object defined by type appropriately.
26095                 //              }
26096         
26097                 this._arrayOfAllItems = [];
26098                 this._arrayOfTopLevelItems = [];
26099                 this._loadFinished = false;
26100                 this._jsonFileUrl = keywordParameters.url;
26101                 this._ccUrl = keywordParameters.url;
26102                 this.url = keywordParameters.url;
26103                 this._jsonData = keywordParameters.data;
26104                 this.data = null;
26105                 this._datatypeMap = keywordParameters.typeMap || {};
26106                 if(!this._datatypeMap['Date']){
26107                         //If no default mapping for dates, then set this as default.
26108                         //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
26109                         //of generically representing dates.
26110                         this._datatypeMap['Date'] = {
26111                                                                                         type: Date,
26112                                                                                         deserialize: function(value){
26113                                                                                                 return dojo.date.stamp.fromISOString(value);
26114                                                                                         }
26115                                                                                 };
26116                 }
26117                 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
26118                 this._itemsByIdentity = null;
26119                 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
26120                 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
26121                 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
26122                 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
26123                 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
26124                 this._queuedFetches = [];
26125                 if(keywordParameters.urlPreventCache !== undefined){
26126                         this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
26127                 }
26128                 if(keywordParameters.hierarchical !== undefined){
26129                         this.hierarchical = keywordParameters.hierarchical?true:false;
26130                 }
26131                 if(keywordParameters.clearOnClose){
26132                         this.clearOnClose = true;
26133                 }
26134                 if("failOk" in keywordParameters){
26135                         this.failOk = keywordParameters.failOk?true:false;
26136                 }
26137         },
26138         
26139         url: "",        // use "" rather than undefined for the benefit of the parser (#3539)
26140
26141         //Internal var, crossCheckUrl.  Used so that setting either url or _jsonFileUrl, can still trigger a reload
26142         //when clearOnClose and close is used.
26143         _ccUrl: "",
26144
26145         data: null,     // define this so that the parser can populate it
26146
26147         typeMap: null, //Define so parser can populate.
26148         
26149         //Parameter to allow users to specify if a close call should force a reload or not.
26150         //By default, it retains the old behavior of not clearing if close is called.  But
26151         //if set true, the store will be reset to default state.  Note that by doing this,
26152         //all item handles will become invalid and a new fetch must be issued.
26153         clearOnClose: false,
26154
26155         //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
26156         //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
26157         //Added for tracker: #6072
26158         urlPreventCache: false,
26159         
26160         //Parameter for specifying that it is OK for the xhrGet call to fail silently.
26161         failOk: false,
26162
26163         //Parameter to indicate to process data from the url as hierarchical
26164         //(data items can contain other data items in js form).  Default is true
26165         //for backwards compatibility.  False means only root items are processed
26166         //as items, all child objects outside of type-mapped objects and those in
26167         //specific reference format, are left straight JS data objects.
26168         hierarchical: true,
26169
26170         _assertIsItem: function(/* item */ item){
26171                 //      summary:
26172                 //              This function tests whether the item passed in is indeed an item in the store.
26173                 //      item:
26174                 //              The item to test for being contained by the store.
26175                 if(!this.isItem(item)){
26176                         throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
26177                 }
26178         },
26179
26180         _assertIsAttribute: function(/* attribute-name-string */ attribute){
26181                 //      summary:
26182                 //              This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
26183                 //      attribute:
26184                 //              The attribute to test for being contained by the store.
26185                 if(typeof attribute !== "string"){
26186                         throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
26187                 }
26188         },
26189
26190         getValue: function(     /* item */ item,
26191                                                 /* attribute-name-string */ attribute,
26192                                                 /* value? */ defaultValue){
26193                 //      summary:
26194                 //              See dojo.data.api.Read.getValue()
26195                 var values = this.getValues(item, attribute);
26196                 return (values.length > 0)?values[0]:defaultValue; // mixed
26197         },
26198
26199         getValues: function(/* item */ item,
26200                                                 /* attribute-name-string */ attribute){
26201                 //      summary:
26202                 //              See dojo.data.api.Read.getValues()
26203
26204                 this._assertIsItem(item);
26205                 this._assertIsAttribute(attribute);
26206                 // Clone it before returning.  refs: #10474
26207                 return (item[attribute] || []).slice(0); // Array
26208         },
26209
26210         getAttributes: function(/* item */ item){
26211                 //      summary:
26212                 //              See dojo.data.api.Read.getAttributes()
26213                 this._assertIsItem(item);
26214                 var attributes = [];
26215                 for(var key in item){
26216                         // Save off only the real item attributes, not the special id marks for O(1) isItem.
26217                         if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
26218                                 attributes.push(key);
26219                         }
26220                 }
26221                 return attributes; // Array
26222         },
26223
26224         hasAttribute: function( /* item */ item,
26225                                                         /* attribute-name-string */ attribute){
26226                 //      summary:
26227                 //              See dojo.data.api.Read.hasAttribute()
26228                 this._assertIsItem(item);
26229                 this._assertIsAttribute(attribute);
26230                 return (attribute in item);
26231         },
26232
26233         containsValue: function(/* item */ item,
26234                                                         /* attribute-name-string */ attribute,
26235                                                         /* anything */ value){
26236                 //      summary:
26237                 //              See dojo.data.api.Read.containsValue()
26238                 var regexp = undefined;
26239                 if(typeof value === "string"){
26240                         regexp = dojo.data.util.filter.patternToRegExp(value, false);
26241                 }
26242                 return this._containsValue(item, attribute, value, regexp); //boolean.
26243         },
26244
26245         _containsValue: function(       /* item */ item,
26246                                                                 /* attribute-name-string */ attribute,
26247                                                                 /* anything */ value,
26248                                                                 /* RegExp?*/ regexp){
26249                 //      summary:
26250                 //              Internal function for looking at the values contained by the item.
26251                 //      description:
26252                 //              Internal function for looking at the values contained by the item.  This
26253                 //              function allows for denoting if the comparison should be case sensitive for
26254                 //              strings or not (for handling filtering cases where string case should not matter)
26255                 //
26256                 //      item:
26257                 //              The data item to examine for attribute values.
26258                 //      attribute:
26259                 //              The attribute to inspect.
26260                 //      value:
26261                 //              The value to match.
26262                 //      regexp:
26263                 //              Optional regular expression generated off value if value was of string type to handle wildcarding.
26264                 //              If present and attribute values are string, then it can be used for comparison instead of 'value'
26265                 return dojo.some(this.getValues(item, attribute), function(possibleValue){
26266                         if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
26267                                 if(possibleValue.toString().match(regexp)){
26268                                         return true; // Boolean
26269                                 }
26270                         }else if(value === possibleValue){
26271                                 return true; // Boolean
26272                         }
26273                 });
26274         },
26275
26276         isItem: function(/* anything */ something){
26277                 //      summary:
26278                 //              See dojo.data.api.Read.isItem()
26279                 if(something && something[this._storeRefPropName] === this){
26280                         if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
26281                                 return true;
26282                         }
26283                 }
26284                 return false; // Boolean
26285         },
26286
26287         isItemLoaded: function(/* anything */ something){
26288                 //      summary:
26289                 //              See dojo.data.api.Read.isItemLoaded()
26290                 return this.isItem(something); //boolean
26291         },
26292
26293         loadItem: function(/* object */ keywordArgs){
26294                 //      summary:
26295                 //              See dojo.data.api.Read.loadItem()
26296                 this._assertIsItem(keywordArgs.item);
26297         },
26298
26299         getFeatures: function(){
26300                 //      summary:
26301                 //              See dojo.data.api.Read.getFeatures()
26302                 return this._features; //Object
26303         },
26304
26305         getLabel: function(/* item */ item){
26306                 //      summary:
26307                 //              See dojo.data.api.Read.getLabel()
26308                 if(this._labelAttr && this.isItem(item)){
26309                         return this.getValue(item,this._labelAttr); //String
26310                 }
26311                 return undefined; //undefined
26312         },
26313
26314         getLabelAttributes: function(/* item */ item){
26315                 //      summary:
26316                 //              See dojo.data.api.Read.getLabelAttributes()
26317                 if(this._labelAttr){
26318                         return [this._labelAttr]; //array
26319                 }
26320                 return null; //null
26321         },
26322
26323         _fetchItems: function(  /* Object */ keywordArgs,
26324                                                         /* Function */ findCallback,
26325                                                         /* Function */ errorCallback){
26326                 //      summary:
26327                 //              See dojo.data.util.simpleFetch.fetch()
26328                 var self = this,
26329                     filter = function(requestArgs, arrayOfItems){
26330                         var items = [],
26331                             i, key;
26332                         if(requestArgs.query){
26333                                 var value,
26334                                     ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
26335
26336                                 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
26337                                 //same value for each item examined.  Much more efficient.
26338                                 var regexpList = {};
26339                                 for(key in requestArgs.query){
26340                                         value = requestArgs.query[key];
26341                                         if(typeof value === "string"){
26342                                                 regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
26343                                         }else if(value instanceof RegExp){
26344                                                 regexpList[key] = value;
26345                                         }
26346                                 }
26347                                 for(i = 0; i < arrayOfItems.length; ++i){
26348                                         var match = true;
26349                                         var candidateItem = arrayOfItems[i];
26350                                         if(candidateItem === null){
26351                                                 match = false;
26352                                         }else{
26353                                                 for(key in requestArgs.query){
26354                                                         value = requestArgs.query[key];
26355                                                         if(!self._containsValue(candidateItem, key, value, regexpList[key])){
26356                                                                 match = false;
26357                                                         }
26358                                                 }
26359                                         }
26360                                         if(match){
26361                                                 items.push(candidateItem);
26362                                         }
26363                                 }
26364                                 findCallback(items, requestArgs);
26365                         }else{
26366                                 // We want a copy to pass back in case the parent wishes to sort the array.
26367                                 // We shouldn't allow resort of the internal list, so that multiple callers
26368                                 // can get lists and sort without affecting each other.  We also need to
26369                                 // filter out any null values that have been left as a result of deleteItem()
26370                                 // calls in ItemFileWriteStore.
26371                                 for(i = 0; i < arrayOfItems.length; ++i){
26372                                         var item = arrayOfItems[i];
26373                                         if(item !== null){
26374                                                 items.push(item);
26375                                         }
26376                                 }
26377                                 findCallback(items, requestArgs);
26378                         }
26379                 };
26380
26381                 if(this._loadFinished){
26382                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
26383                 }else{
26384                         //Do a check on the JsonFileUrl and crosscheck it.
26385                         //If it doesn't match the cross-check, it needs to be updated
26386                         //This allows for either url or _jsonFileUrl to he changed to
26387                         //reset the store load location.  Done this way for backwards
26388                         //compatibility.  People use _jsonFileUrl (even though officially
26389                         //private.
26390                         if(this._jsonFileUrl !== this._ccUrl){
26391                                 dojo.deprecated("dojo.data.ItemFileReadStore: ",
26392                                         "To change the url, set the url property of the store," +
26393                                         " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
26394                                 this._ccUrl = this._jsonFileUrl;
26395                                 this.url = this._jsonFileUrl;
26396                         }else if(this.url !== this._ccUrl){
26397                                 this._jsonFileUrl = this.url;
26398                                 this._ccUrl = this.url;
26399                         }
26400
26401                         //See if there was any forced reset of data.
26402                         if(this.data != null){
26403                                 this._jsonData = this.data;
26404                                 this.data = null;
26405                         }
26406
26407                         if(this._jsonFileUrl){
26408                                 //If fetches come in before the loading has finished, but while
26409                                 //a load is in progress, we have to defer the fetching to be
26410                                 //invoked in the callback.
26411                                 if(this._loadInProgress){
26412                                         this._queuedFetches.push({args: keywordArgs, filter: filter});
26413                                 }else{
26414                                         this._loadInProgress = true;
26415                                         var getArgs = {
26416                                                         url: self._jsonFileUrl,
26417                                                         handleAs: "json-comment-optional",
26418                                                         preventCache: this.urlPreventCache,
26419                                                         failOk: this.failOk
26420                                                 };
26421                                         var getHandler = dojo.xhrGet(getArgs);
26422                                         getHandler.addCallback(function(data){
26423                                                 try{
26424                                                         self._getItemsFromLoadedData(data);
26425                                                         self._loadFinished = true;
26426                                                         self._loadInProgress = false;
26427                                                         
26428                                                         filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
26429                                                         self._handleQueuedFetches();
26430                                                 }catch(e){
26431                                                         self._loadFinished = true;
26432                                                         self._loadInProgress = false;
26433                                                         errorCallback(e, keywordArgs);
26434                                                 }
26435                                         });
26436                                         getHandler.addErrback(function(error){
26437                                                 self._loadInProgress = false;
26438                                                 errorCallback(error, keywordArgs);
26439                                         });
26440
26441                                         //Wire up the cancel to abort of the request
26442                                         //This call cancel on the deferred if it hasn't been called
26443                                         //yet and then will chain to the simple abort of the
26444                                         //simpleFetch keywordArgs
26445                                         var oldAbort = null;
26446                                         if(keywordArgs.abort){
26447                                                 oldAbort = keywordArgs.abort;
26448                                         }
26449                                         keywordArgs.abort = function(){
26450                                                 var df = getHandler;
26451                                                 if(df && df.fired === -1){
26452                                                         df.cancel();
26453                                                         df = null;
26454                                                 }
26455                                                 if(oldAbort){
26456                                                         oldAbort.call(keywordArgs);
26457                                                 }
26458                                         };
26459                                 }
26460                         }else if(this._jsonData){
26461                                 try{
26462                                         this._loadFinished = true;
26463                                         this._getItemsFromLoadedData(this._jsonData);
26464                                         this._jsonData = null;
26465                                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
26466                                 }catch(e){
26467                                         errorCallback(e, keywordArgs);
26468                                 }
26469                         }else{
26470                                 errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
26471                         }
26472                 }
26473         },
26474
26475         _handleQueuedFetches: function(){
26476                 //      summary:
26477                 //              Internal function to execute delayed request in the store.
26478                 //Execute any deferred fetches now.
26479                 if(this._queuedFetches.length > 0){
26480                         for(var i = 0; i < this._queuedFetches.length; i++){
26481                                 var fData = this._queuedFetches[i],
26482                                     delayedQuery = fData.args,
26483                                     delayedFilter = fData.filter;
26484                                 if(delayedFilter){
26485                                         delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
26486                                 }else{
26487                                         this.fetchItemByIdentity(delayedQuery);
26488                                 }
26489                         }
26490                         this._queuedFetches = [];
26491                 }
26492         },
26493
26494         _getItemsArray: function(/*object?*/queryOptions){
26495                 //      summary:
26496                 //              Internal function to determine which list of items to search over.
26497                 //      queryOptions: The query options parameter, if any.
26498                 if(queryOptions && queryOptions.deep){
26499                         return this._arrayOfAllItems;
26500                 }
26501                 return this._arrayOfTopLevelItems;
26502         },
26503
26504         close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
26505                  //     summary:
26506                  //             See dojo.data.api.Read.close()
26507                  if(this.clearOnClose &&
26508                         this._loadFinished &&
26509                         !this._loadInProgress){
26510                          //Reset all internalsback to default state.  This will force a reload
26511                          //on next fetch.  This also checks that the data or url param was set
26512                          //so that the store knows it can get data.  Without one of those being set,
26513                          //the next fetch will trigger an error.
26514
26515                          if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
26516                                  (this.url == "" || this.url == null)
26517                                 ) && this.data == null){
26518                                  console.debug("dojo.data.ItemFileReadStore: WARNING!  Data reload " +
26519                                         " information has not been provided." +
26520                                         "  Please set 'url' or 'data' to the appropriate value before" +
26521                                         " the next fetch");
26522                          }
26523                          this._arrayOfAllItems = [];
26524                          this._arrayOfTopLevelItems = [];
26525                          this._loadFinished = false;
26526                          this._itemsByIdentity = null;
26527                          this._loadInProgress = false;
26528                          this._queuedFetches = [];
26529                  }
26530         },
26531
26532         _getItemsFromLoadedData: function(/* Object */ dataObject){
26533                 //      summary:
26534                 //              Function to parse the loaded data into item format and build the internal items array.
26535                 //      description:
26536                 //              Function to parse the loaded data into item format and build the internal items array.
26537                 //
26538                 //      dataObject:
26539                 //              The JS data object containing the raw data to convery into item format.
26540                 //
26541                 //      returns: array
26542                 //              Array of items in store item format.
26543                 
26544                 // First, we define a couple little utility functions...
26545                 var addingArrays = false,
26546                     self = this;
26547                 
26548                 function valueIsAnItem(/* anything */ aValue){
26549                         // summary:
26550                         //              Given any sort of value that could be in the raw json data,
26551                         //              return true if we should interpret the value as being an
26552                         //              item itself, rather than a literal value or a reference.
26553                         // example:
26554                         //      |       false == valueIsAnItem("Kermit");
26555                         //      |       false == valueIsAnItem(42);
26556                         //      |       false == valueIsAnItem(new Date());
26557                         //      |       false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
26558                         //      |       false == valueIsAnItem({_reference:'Kermit'});
26559                         //      |       true == valueIsAnItem({name:'Kermit', color:'green'});
26560                         //      |       true == valueIsAnItem({iggy:'pop'});
26561                         //      |       true == valueIsAnItem({foo:42});
26562                         var isItem = (
26563                                 (aValue !== null) &&
26564                                 (typeof aValue === "object") &&
26565                                 (!dojo.isArray(aValue) || addingArrays) &&
26566                                 (!dojo.isFunction(aValue)) &&
26567                                 (aValue.constructor == Object || dojo.isArray(aValue)) &&
26568                                 (typeof aValue._reference === "undefined") &&
26569                                 (typeof aValue._type === "undefined") &&
26570                                 (typeof aValue._value === "undefined") &&
26571                                 self.hierarchical
26572                         );
26573                         return isItem;
26574                 }
26575                 
26576                 function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
26577                         self._arrayOfAllItems.push(anItem);
26578                         for(var attribute in anItem){
26579                                 var valueForAttribute = anItem[attribute];
26580                                 if(valueForAttribute){
26581                                         if(dojo.isArray(valueForAttribute)){
26582                                                 var valueArray = valueForAttribute;
26583                                                 for(var k = 0; k < valueArray.length; ++k){
26584                                                         var singleValue = valueArray[k];
26585                                                         if(valueIsAnItem(singleValue)){
26586                                                                 addItemAndSubItemsToArrayOfAllItems(singleValue);
26587                                                         }
26588                                                 }
26589                                         }else{
26590                                                 if(valueIsAnItem(valueForAttribute)){
26591                                                         addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
26592                                                 }
26593                                         }
26594                                 }
26595                         }
26596                 }
26597
26598                 this._labelAttr = dataObject.label;
26599
26600                 // We need to do some transformations to convert the data structure
26601                 // that we read from the file into a format that will be convenient
26602                 // to work with in memory.
26603
26604                 // Step 1: Walk through the object hierarchy and build a list of all items
26605                 var i,
26606                     item;
26607                 this._arrayOfAllItems = [];
26608                 this._arrayOfTopLevelItems = dataObject.items;
26609
26610                 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
26611                         item = this._arrayOfTopLevelItems[i];
26612                         if(dojo.isArray(item)){
26613                                 addingArrays = true;
26614                         }
26615                         addItemAndSubItemsToArrayOfAllItems(item);
26616                         item[this._rootItemPropName]=true;
26617                 }
26618
26619                 // Step 2: Walk through all the attribute values of all the items,
26620                 // and replace single values with arrays.  For example, we change this:
26621                 //              { name:'Miss Piggy', pets:'Foo-Foo'}
26622                 // into this:
26623                 //              { name:['Miss Piggy'], pets:['Foo-Foo']}
26624                 //
26625                 // We also store the attribute names so we can validate our store
26626                 // reference and item id special properties for the O(1) isItem
26627                 var allAttributeNames = {},
26628                     key;
26629
26630                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
26631                         item = this._arrayOfAllItems[i];
26632                         for(key in item){
26633                                 if(key !== this._rootItemPropName){
26634                                         var value = item[key];
26635                                         if(value !== null){
26636                                                 if(!dojo.isArray(value)){
26637                                                         item[key] = [value];
26638                                                 }
26639                                         }else{
26640                                                 item[key] = [null];
26641                                         }
26642                                 }
26643                                 allAttributeNames[key]=key;
26644                         }
26645                 }
26646
26647                 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
26648                 // This should go really fast, it will generally never even run the loop.
26649                 while(allAttributeNames[this._storeRefPropName]){
26650                         this._storeRefPropName += "_";
26651                 }
26652                 while(allAttributeNames[this._itemNumPropName]){
26653                         this._itemNumPropName += "_";
26654                 }
26655                 while(allAttributeNames[this._reverseRefMap]){
26656                         this._reverseRefMap += "_";
26657                 }
26658
26659                 // Step 4: Some data files specify an optional 'identifier', which is
26660                 // the name of an attribute that holds the identity of each item.
26661                 // If this data file specified an identifier attribute, then build a
26662                 // hash table of items keyed by the identity of the items.
26663                 var arrayOfValues;
26664
26665                 var identifier = dataObject.identifier;
26666                 if(identifier){
26667                         this._itemsByIdentity = {};
26668                         this._features['dojo.data.api.Identity'] = identifier;
26669                         for(i = 0; i < this._arrayOfAllItems.length; ++i){
26670                                 item = this._arrayOfAllItems[i];
26671                                 arrayOfValues = item[identifier];
26672                                 var identity = arrayOfValues[0];
26673                                 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
26674                                         this._itemsByIdentity[identity] = item;
26675                                 }else{
26676                                         if(this._jsonFileUrl){
26677                                                 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 + "]");
26678                                         }else if(this._jsonData){
26679                                                 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 + "]");
26680                                         }
26681                                 }
26682                         }
26683                 }else{
26684                         this._features['dojo.data.api.Identity'] = Number;
26685                 }
26686
26687                 // Step 5: Walk through all the items, and set each item's properties
26688                 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
26689                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
26690                         item = this._arrayOfAllItems[i];
26691                         item[this._storeRefPropName] = this;
26692                         item[this._itemNumPropName] = i;
26693                 }
26694
26695                 // Step 6: We walk through all the attribute values of all the items,
26696                 // looking for type/value literals and item-references.
26697                 //
26698                 // We replace item-references with pointers to items.  For example, we change:
26699                 //              { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
26700                 // into this:
26701                 //              { name:['Kermit'], friends:[miss_piggy] }
26702                 // (where miss_piggy is the object representing the 'Miss Piggy' item).
26703                 //
26704                 // We replace type/value pairs with typed-literals.  For example, we change:
26705                 //              { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
26706                 // into this:
26707                 //              { name:['Kermit'], born:(new Date(1918, 6, 18)) }
26708                 //
26709                 // We also generate the associate map for all items for the O(1) isItem function.
26710                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
26711                         item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
26712                         for(key in item){
26713                                 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
26714                                 for(var j = 0; j < arrayOfValues.length; ++j){
26715                                         value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
26716                                         if(value !== null && typeof value == "object"){
26717                                                 if(("_type" in value) && ("_value" in value)){
26718                                                         var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
26719                                                         var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
26720                                                         if(!mappingObj){
26721                                                                 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
26722                                                         }else if(dojo.isFunction(mappingObj)){
26723                                                                 arrayOfValues[j] = new mappingObj(value._value);
26724                                                         }else if(dojo.isFunction(mappingObj.deserialize)){
26725                                                                 arrayOfValues[j] = mappingObj.deserialize(value._value);
26726                                                         }else{
26727                                                                 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
26728                                                         }
26729                                                 }
26730                                                 if(value._reference){
26731                                                         var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
26732                                                         if(!dojo.isObject(referenceDescription)){
26733                                                                 // example: 'Miss Piggy'
26734                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
26735                                                                 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
26736                                                         }else{
26737                                                                 // example: {name:'Miss Piggy'}
26738                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
26739                                                                 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
26740                                                                         var candidateItem = this._arrayOfAllItems[k],
26741                                                                             found = true;
26742                                                                         for(var refKey in referenceDescription){
26743                                                                                 if(candidateItem[refKey] != referenceDescription[refKey]){
26744                                                                                         found = false;
26745                                                                                 }
26746                                                                         }
26747                                                                         if(found){
26748                                                                                 arrayOfValues[j] = candidateItem;
26749                                                                         }
26750                                                                 }
26751                                                         }
26752                                                         if(this.referenceIntegrity){
26753                                                                 var refItem = arrayOfValues[j];
26754                                                                 if(this.isItem(refItem)){
26755                                                                         this._addReferenceToMap(refItem, item, key);
26756                                                                 }
26757                                                         }
26758                                                 }else if(this.isItem(value)){
26759                                                         //It's a child item (not one referenced through _reference).
26760                                                         //We need to treat this as a referenced item, so it can be cleaned up
26761                                                         //in a write store easily.
26762                                                         if(this.referenceIntegrity){
26763                                                                 this._addReferenceToMap(value, item, key);
26764                                                         }
26765                                                 }
26766                                         }
26767                                 }
26768                         }
26769                 }
26770         },
26771
26772         _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
26773                  //     summary:
26774                  //             Method to add an reference map entry for an item and attribute.
26775                  //     description:
26776                  //             Method to add an reference map entry for an item and attribute.                  //
26777                  //     refItem:
26778                  //             The item that is referenced.
26779                  //     parentItem:
26780                  //             The item that holds the new reference to refItem.
26781                  //     attribute:
26782                  //             The attribute on parentItem that contains the new reference.
26783                  
26784                  //Stub function, does nothing.  Real processing is in ItemFileWriteStore.
26785         },
26786
26787         getIdentity: function(/* item */ item){
26788                 //      summary:
26789                 //              See dojo.data.api.Identity.getIdentity()
26790                 var identifier = this._features['dojo.data.api.Identity'];
26791                 if(identifier === Number){
26792                         return item[this._itemNumPropName]; // Number
26793                 }else{
26794                         var arrayOfValues = item[identifier];
26795                         if(arrayOfValues){
26796                                 return arrayOfValues[0]; // Object || String
26797                         }
26798                 }
26799                 return null; // null
26800         },
26801
26802         fetchItemByIdentity: function(/* Object */ keywordArgs){
26803                 //      summary:
26804                 //              See dojo.data.api.Identity.fetchItemByIdentity()
26805
26806                 // Hasn't loaded yet, we have to trigger the load.
26807                 var item,
26808                     scope;
26809                 if(!this._loadFinished){
26810                         var self = this;
26811                         //Do a check on the JsonFileUrl and crosscheck it.
26812                         //If it doesn't match the cross-check, it needs to be updated
26813                         //This allows for either url or _jsonFileUrl to he changed to
26814                         //reset the store load location.  Done this way for backwards
26815                         //compatibility.  People use _jsonFileUrl (even though officially
26816                         //private.
26817                         if(this._jsonFileUrl !== this._ccUrl){
26818                                 dojo.deprecated("dojo.data.ItemFileReadStore: ",
26819                                         "To change the url, set the url property of the store," +
26820                                         " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
26821                                 this._ccUrl = this._jsonFileUrl;
26822                                 this.url = this._jsonFileUrl;
26823                         }else if(this.url !== this._ccUrl){
26824                                 this._jsonFileUrl = this.url;
26825                                 this._ccUrl = this.url;
26826                         }
26827                         
26828                         //See if there was any forced reset of data.
26829                         if(this.data != null && this._jsonData == null){
26830                                 this._jsonData = this.data;
26831                                 this.data = null;
26832                         }
26833
26834                         if(this._jsonFileUrl){
26835
26836                                 if(this._loadInProgress){
26837                                         this._queuedFetches.push({args: keywordArgs});
26838                                 }else{
26839                                         this._loadInProgress = true;
26840                                         var getArgs = {
26841                                                         url: self._jsonFileUrl,
26842                                                         handleAs: "json-comment-optional",
26843                                                         preventCache: this.urlPreventCache,
26844                                                         failOk: this.failOk
26845                                         };
26846                                         var getHandler = dojo.xhrGet(getArgs);
26847                                         getHandler.addCallback(function(data){
26848                                                 var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
26849                                                 try{
26850                                                         self._getItemsFromLoadedData(data);
26851                                                         self._loadFinished = true;
26852                                                         self._loadInProgress = false;
26853                                                         item = self._getItemByIdentity(keywordArgs.identity);
26854                                                         if(keywordArgs.onItem){
26855                                                                 keywordArgs.onItem.call(scope, item);
26856                                                         }
26857                                                         self._handleQueuedFetches();
26858                                                 }catch(error){
26859                                                         self._loadInProgress = false;
26860                                                         if(keywordArgs.onError){
26861                                                                 keywordArgs.onError.call(scope, error);
26862                                                         }
26863                                                 }
26864                                         });
26865                                         getHandler.addErrback(function(error){
26866                                                 self._loadInProgress = false;
26867                                                 if(keywordArgs.onError){
26868                                                         var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
26869                                                         keywordArgs.onError.call(scope, error);
26870                                                 }
26871                                         });
26872                                 }
26873
26874                         }else if(this._jsonData){
26875                                 // Passed in data, no need to xhr.
26876                                 self._getItemsFromLoadedData(self._jsonData);
26877                                 self._jsonData = null;
26878                                 self._loadFinished = true;
26879                                 item = self._getItemByIdentity(keywordArgs.identity);
26880                                 if(keywordArgs.onItem){
26881                                         scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
26882                                         keywordArgs.onItem.call(scope, item);
26883                                 }
26884                         }
26885                 }else{
26886                         // Already loaded.  We can just look it up and call back.
26887                         item = this._getItemByIdentity(keywordArgs.identity);
26888                         if(keywordArgs.onItem){
26889                                 scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
26890                                 keywordArgs.onItem.call(scope, item);
26891                         }
26892                 }
26893         },
26894
26895         _getItemByIdentity: function(/* Object */ identity){
26896                 //      summary:
26897                 //              Internal function to look an item up by its identity map.
26898                 var item = null;
26899                 if(this._itemsByIdentity &&
26900                    Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
26901                         item = this._itemsByIdentity[identity];
26902                 }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
26903                         item = this._arrayOfAllItems[identity];
26904                 }
26905                 if(item === undefined){
26906                         item = null;
26907                 }
26908                 return item; // Object
26909         },
26910
26911         getIdentityAttributes: function(/* item */ item){
26912                 //      summary:
26913                 //              See dojo.data.api.Identity.getIdentityAttributes()
26914                  
26915                 var identifier = this._features['dojo.data.api.Identity'];
26916                 if(identifier === Number){
26917                         // If (identifier === Number) it means getIdentity() just returns
26918                         // an integer item-number for each item.  The dojo.data.api.Identity
26919                         // spec says we need to return null if the identity is not composed
26920                         // of attributes
26921                         return null; // null
26922                 }else{
26923                         return [identifier]; // Array
26924                 }
26925         },
26926         
26927         _forceLoad: function(){
26928                 //      summary:
26929                 //              Internal function to force a load of the store if it hasn't occurred yet.  This is required
26930                 //              for specific functions to work properly.
26931                 var self = this;
26932                 //Do a check on the JsonFileUrl and crosscheck it.
26933                 //If it doesn't match the cross-check, it needs to be updated
26934                 //This allows for either url or _jsonFileUrl to he changed to
26935                 //reset the store load location.  Done this way for backwards
26936                 //compatibility.  People use _jsonFileUrl (even though officially
26937                 //private.
26938                 if(this._jsonFileUrl !== this._ccUrl){
26939                         dojo.deprecated("dojo.data.ItemFileReadStore: ",
26940                                 "To change the url, set the url property of the store," +
26941                                 " not _jsonFileUrl.  _jsonFileUrl support will be removed in 2.0");
26942                         this._ccUrl = this._jsonFileUrl;
26943                         this.url = this._jsonFileUrl;
26944                 }else if(this.url !== this._ccUrl){
26945                         this._jsonFileUrl = this.url;
26946                         this._ccUrl = this.url;
26947                 }
26948
26949                 //See if there was any forced reset of data.
26950                 if(this.data != null){
26951                         this._jsonData = this.data;
26952                         this.data = null;
26953                 }
26954
26955                 if(this._jsonFileUrl){
26956                                 var getArgs = {
26957                                         url: this._jsonFileUrl,
26958                                         handleAs: "json-comment-optional",
26959                                         preventCache: this.urlPreventCache,
26960                                         failOk: this.failOk,
26961                                         sync: true
26962                                 };
26963                         var getHandler = dojo.xhrGet(getArgs);
26964                         getHandler.addCallback(function(data){
26965                                 try{
26966                                         //Check to be sure there wasn't another load going on concurrently
26967                                         //So we don't clobber data that comes in on it.  If there is a load going on
26968                                         //then do not save this data.  It will potentially clobber current data.
26969                                         //We mainly wanted to sync/wait here.
26970                                         //TODO:  Revisit the loading scheme of this store to improve multi-initial
26971                                         //request handling.
26972                                         if(self._loadInProgress !== true && !self._loadFinished){
26973                                                 self._getItemsFromLoadedData(data);
26974                                                 self._loadFinished = true;
26975                                         }else if(self._loadInProgress){
26976                                                 //Okay, we hit an error state we can't recover from.  A forced load occurred
26977                                                 //while an async load was occurring.  Since we cannot block at this point, the best
26978                                                 //that can be managed is to throw an error.
26979                                                 throw new Error("dojo.data.ItemFileReadStore:  Unable to perform a synchronous load, an async load is in progress.");
26980                                         }
26981                                 }catch(e){
26982                                         console.log(e);
26983                                         throw e;
26984                                 }
26985                         });
26986                         getHandler.addErrback(function(error){
26987                                 throw error;
26988                         });
26989                 }else if(this._jsonData){
26990                         self._getItemsFromLoadedData(self._jsonData);
26991                         self._jsonData = null;
26992                         self._loadFinished = true;
26993                 }
26994         }
26995 });
26996 //Mix in the simple fetch implementation to this class.
26997 dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
26998
26999 }
27000
27001 if(!dojo._hasResource["dojo.data.ItemFileWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
27002 dojo._hasResource["dojo.data.ItemFileWriteStore"] = true;
27003 dojo.provide("dojo.data.ItemFileWriteStore");
27004
27005
27006
27007 dojo.declare("dojo.data.ItemFileWriteStore", dojo.data.ItemFileReadStore, {
27008         constructor: function(/* object */ keywordParameters){
27009                 //      keywordParameters: {typeMap: object)
27010                 //              The structure of the typeMap object is as follows:
27011                 //              {
27012                 //                      type0: function || object,
27013                 //                      type1: function || object,
27014                 //                      ...
27015                 //                      typeN: function || object
27016                 //              }
27017                 //              Where if it is a function, it is assumed to be an object constructor that takes the
27018                 //              value of _value as the initialization parameters.  It is serialized assuming object.toString()
27019                 //              serialization.  If it is an object, then it is assumed
27020                 //              to be an object of general form:
27021                 //              {
27022                 //                      type: function, //constructor.
27023                 //                      deserialize:    function(value) //The function that parses the value and constructs the object defined by type appropriately.
27024                 //                      serialize:      function(object) //The function that converts the object back into the proper file format form.
27025                 //              }
27026
27027                 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
27028                 this._features['dojo.data.api.Write'] = true;
27029                 this._features['dojo.data.api.Notification'] = true;
27030                 
27031                 // For keeping track of changes so that we can implement isDirty and revert
27032                 this._pending = {
27033                         _newItems:{},
27034                         _modifiedItems:{},
27035                         _deletedItems:{}
27036                 };
27037
27038                 if(!this._datatypeMap['Date'].serialize){
27039                         this._datatypeMap['Date'].serialize = function(obj){
27040                                 return dojo.date.stamp.toISOString(obj, {zulu:true});
27041                         };
27042                 }
27043                 //Disable only if explicitly set to false.
27044                 if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
27045                         this.referenceIntegrity = false;
27046                 }
27047
27048                 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
27049                 this._saveInProgress = false;
27050         },
27051
27052         referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking.  This way it can also be disabled pogrammatially or declaratively.
27053
27054         _assert: function(/* boolean */ condition){
27055                 if(!condition){
27056                         throw new Error("assertion failed in ItemFileWriteStore");
27057                 }
27058         },
27059
27060         _getIdentifierAttribute: function(){
27061                 var identifierAttribute = this.getFeatures()['dojo.data.api.Identity'];
27062                 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
27063                 return identifierAttribute;
27064         },
27065         
27066         
27067 /* dojo.data.api.Write */
27068
27069         newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
27070                 // summary: See dojo.data.api.Write.newItem()
27071
27072                 this._assert(!this._saveInProgress);
27073
27074                 if(!this._loadFinished){
27075                         // We need to do this here so that we'll be able to find out what
27076                         // identifierAttribute was specified in the data file.
27077                         this._forceLoad();
27078                 }
27079
27080                 if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
27081                         throw new Error("newItem() was passed something other than an object");
27082                 }
27083                 var newIdentity = null;
27084                 var identifierAttribute = this._getIdentifierAttribute();
27085                 if(identifierAttribute === Number){
27086                         newIdentity = this._arrayOfAllItems.length;
27087                 }else{
27088                         newIdentity = keywordArgs[identifierAttribute];
27089                         if(typeof newIdentity === "undefined"){
27090                                 throw new Error("newItem() was not passed an identity for the new item");
27091                         }
27092                         if(dojo.isArray(newIdentity)){
27093                                 throw new Error("newItem() was not passed an single-valued identity");
27094                         }
27095                 }
27096                 
27097                 // make sure this identity is not already in use by another item, if identifiers were
27098                 // defined in the file.  Otherwise it would be the item count,
27099                 // which should always be unique in this case.
27100                 if(this._itemsByIdentity){
27101                         this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
27102                 }
27103                 this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
27104                 this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
27105                 
27106                 var newItem = {};
27107                 newItem[this._storeRefPropName] = this;
27108                 newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
27109                 if(this._itemsByIdentity){
27110                         this._itemsByIdentity[newIdentity] = newItem;
27111                         //We have to set the identifier now, otherwise we can't look it
27112                         //up at calls to setValueorValues in parentInfo handling.
27113                         newItem[identifierAttribute] = [newIdentity];
27114                 }
27115                 this._arrayOfAllItems.push(newItem);
27116
27117                 //We need to construct some data for the onNew call too...
27118                 var pInfo = null;
27119                 
27120                 // Now we need to check to see where we want to assign this thingm if any.
27121                 if(parentInfo && parentInfo.parent && parentInfo.attribute){
27122                         pInfo = {
27123                                 item: parentInfo.parent,
27124                                 attribute: parentInfo.attribute,
27125                                 oldValue: undefined
27126                         };
27127
27128                         //See if it is multi-valued or not and handle appropriately
27129                         //Generally, all attributes are multi-valued for this store
27130                         //So, we only need to append if there are already values present.
27131                         var values = this.getValues(parentInfo.parent, parentInfo.attribute);
27132                         if(values && values.length > 0){
27133                                 var tempValues = values.slice(0, values.length);
27134                                 if(values.length === 1){
27135                                         pInfo.oldValue = values[0];
27136                                 }else{
27137                                         pInfo.oldValue = values.slice(0, values.length);
27138                                 }
27139                                 tempValues.push(newItem);
27140                                 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
27141                                 pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
27142                         }else{
27143                                 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
27144                                 pInfo.newValue = newItem;
27145                         }
27146                 }else{
27147                         //Toplevel item, add to both top list as well as all list.
27148                         newItem[this._rootItemPropName]=true;
27149                         this._arrayOfTopLevelItems.push(newItem);
27150                 }
27151                 
27152                 this._pending._newItems[newIdentity] = newItem;
27153                 
27154                 //Clone over the properties to the new item
27155                 for(var key in keywordArgs){
27156                         if(key === this._storeRefPropName || key === this._itemNumPropName){
27157                                 // Bummer, the user is trying to do something like
27158                                 // newItem({_S:"foo"}).  Unfortunately, our superclass,
27159                                 // ItemFileReadStore, is already using _S in each of our items
27160                                 // to hold private info.  To avoid a naming collision, we
27161                                 // need to move all our private info to some other property
27162                                 // of all the items/objects.  So, we need to iterate over all
27163                                 // the items and do something like:
27164                                 //    item.__S = item._S;
27165                                 //    item._S = undefined;
27166                                 // But first we have to make sure the new "__S" variable is
27167                                 // not in use, which means we have to iterate over all the
27168                                 // items checking for that.
27169                                 throw new Error("encountered bug in ItemFileWriteStore.newItem");
27170                         }
27171                         var value = keywordArgs[key];
27172                         if(!dojo.isArray(value)){
27173                                 value = [value];
27174                         }
27175                         newItem[key] = value;
27176                         if(this.referenceIntegrity){
27177                                 for(var i = 0; i < value.length; i++){
27178                                         var val = value[i];
27179                                         if(this.isItem(val)){
27180                                                 this._addReferenceToMap(val, newItem, key);
27181                                         }
27182                                 }
27183                         }
27184                 }
27185                 this.onNew(newItem, pInfo); // dojo.data.api.Notification call
27186                 return newItem; // item
27187         },
27188         
27189         _removeArrayElement: function(/* Array */ array, /* anything */ element){
27190                 var index = dojo.indexOf(array, element);
27191                 if(index != -1){
27192                         array.splice(index, 1);
27193                         return true;
27194                 }
27195                 return false;
27196         },
27197         
27198         deleteItem: function(/* item */ item){
27199                 // summary: See dojo.data.api.Write.deleteItem()
27200                 this._assert(!this._saveInProgress);
27201                 this._assertIsItem(item);
27202
27203                 // Remove this item from the _arrayOfAllItems, but leave a null value in place
27204                 // of the item, so as not to change the length of the array, so that in newItem()
27205                 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
27206                 var indexInArrayOfAllItems = item[this._itemNumPropName];
27207                 var identity = this.getIdentity(item);
27208
27209                 //If we have reference integrity on, we need to do reference cleanup for the deleted item
27210                 if(this.referenceIntegrity){
27211                         //First scan all the attributes of this items for references and clean them up in the map
27212                         //As this item is going away, no need to track its references anymore.
27213
27214                         //Get the attributes list before we generate the backup so it
27215                         //doesn't pollute the attributes list.
27216                         var attributes = this.getAttributes(item);
27217
27218                         //Backup the map, we'll have to restore it potentially, in a revert.
27219                         if(item[this._reverseRefMap]){
27220                                 item["backup_" + this._reverseRefMap] = dojo.clone(item[this._reverseRefMap]);
27221                         }
27222                         
27223                         //TODO:  This causes a reversion problem.  This list won't be restored on revert since it is
27224                         //attached to the 'value'. item, not ours.  Need to back tese up somehow too.
27225                         //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
27226                         //later.  Or just record them and call _addReferenceToMap on them in revert.
27227                         dojo.forEach(attributes, function(attribute){
27228                                 dojo.forEach(this.getValues(item, attribute), function(value){
27229                                         if(this.isItem(value)){
27230                                                 //We have to back up all the references we had to others so they can be restored on a revert.
27231                                                 if(!item["backupRefs_" + this._reverseRefMap]){
27232                                                         item["backupRefs_" + this._reverseRefMap] = [];
27233                                                 }
27234                                                 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
27235                                                 this._removeReferenceFromMap(value, item, attribute);
27236                                         }
27237                                 }, this);
27238                         }, this);
27239
27240                         //Next, see if we have references to this item, if we do, we have to clean them up too.
27241                         var references = item[this._reverseRefMap];
27242                         if(references){
27243                                 //Look through all the items noted as references to clean them up.
27244                                 for(var itemId in references){
27245                                         var containingItem = null;
27246                                         if(this._itemsByIdentity){
27247                                                 containingItem = this._itemsByIdentity[itemId];
27248                                         }else{
27249                                                 containingItem = this._arrayOfAllItems[itemId];
27250                                         }
27251                                         //We have a reference to a containing item, now we have to process the
27252                                         //attributes and clear all references to the item being deleted.
27253                                         if(containingItem){
27254                                                 for(var attribute in references[itemId]){
27255                                                         var oldValues = this.getValues(containingItem, attribute) || [];
27256                                                         var newValues = dojo.filter(oldValues, function(possibleItem){
27257                                                                 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
27258                                                         }, this);
27259                                                         //Remove the note of the reference to the item and set the values on the modified attribute.
27260                                                         this._removeReferenceFromMap(item, containingItem, attribute);
27261                                                         if(newValues.length < oldValues.length){
27262                                                                 this._setValueOrValues(containingItem, attribute, newValues, true);
27263                                                         }
27264                                                 }
27265                                         }
27266                                 }
27267                         }
27268                 }
27269
27270                 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
27271
27272                 item[this._storeRefPropName] = null;
27273                 if(this._itemsByIdentity){
27274                         delete this._itemsByIdentity[identity];
27275                 }
27276                 this._pending._deletedItems[identity] = item;
27277                 
27278                 //Remove from the toplevel items, if necessary...
27279                 if(item[this._rootItemPropName]){
27280                         this._removeArrayElement(this._arrayOfTopLevelItems, item);
27281                 }
27282                 this.onDelete(item); // dojo.data.api.Notification call
27283                 return true;
27284         },
27285
27286         setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
27287                 // summary: See dojo.data.api.Write.set()
27288                 return this._setValueOrValues(item, attribute, value, true); // boolean
27289         },
27290         
27291         setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
27292                 // summary: See dojo.data.api.Write.setValues()
27293                 return this._setValueOrValues(item, attribute, values, true); // boolean
27294         },
27295         
27296         unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
27297                 // summary: See dojo.data.api.Write.unsetAttribute()
27298                 return this._setValueOrValues(item, attribute, [], true);
27299         },
27300         
27301         _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
27302                 this._assert(!this._saveInProgress);
27303                 
27304                 // Check for valid arguments
27305                 this._assertIsItem(item);
27306                 this._assert(dojo.isString(attribute));
27307                 this._assert(typeof newValueOrValues !== "undefined");
27308
27309                 // Make sure the user isn't trying to change the item's identity
27310                 var identifierAttribute = this._getIdentifierAttribute();
27311                 if(attribute == identifierAttribute){
27312                         throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
27313                 }
27314
27315                 // To implement the Notification API, we need to make a note of what
27316                 // the old attribute value was, so that we can pass that info when
27317                 // we call the onSet method.
27318                 var oldValueOrValues = this._getValueOrValues(item, attribute);
27319
27320                 var identity = this.getIdentity(item);
27321                 if(!this._pending._modifiedItems[identity]){
27322                         // Before we actually change the item, we make a copy of it to
27323                         // record the original state, so that we'll be able to revert if
27324                         // the revert method gets called.  If the item has already been
27325                         // modified then there's no need to do this now, since we already
27326                         // have a record of the original state.
27327                         var copyOfItemState = {};
27328                         for(var key in item){
27329                                 if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
27330                                         copyOfItemState[key] = item[key];
27331                                 }else if(key === this._reverseRefMap){
27332                                         copyOfItemState[key] = dojo.clone(item[key]);
27333                                 }else{
27334                                         copyOfItemState[key] = item[key].slice(0, item[key].length);
27335                                 }
27336                         }
27337                         // Now mark the item as dirty, and save the copy of the original state
27338                         this._pending._modifiedItems[identity] = copyOfItemState;
27339                 }
27340                 
27341                 // Okay, now we can actually change this attribute on the item
27342                 var success = false;
27343                 
27344                 if(dojo.isArray(newValueOrValues) && newValueOrValues.length === 0){
27345                         
27346                         // If we were passed an empty array as the value, that counts
27347                         // as "unsetting" the attribute, so we need to remove this
27348                         // attribute from the item.
27349                         success = delete item[attribute];
27350                         newValueOrValues = undefined; // used in the onSet Notification call below
27351
27352                         if(this.referenceIntegrity && oldValueOrValues){
27353                                 var oldValues = oldValueOrValues;
27354                                 if(!dojo.isArray(oldValues)){
27355                                         oldValues = [oldValues];
27356                                 }
27357                                 for(var i = 0; i < oldValues.length; i++){
27358                                         var value = oldValues[i];
27359                                         if(this.isItem(value)){
27360                                                 this._removeReferenceFromMap(value, item, attribute);
27361                                         }
27362                                 }
27363                         }
27364                 }else{
27365                         var newValueArray;
27366                         if(dojo.isArray(newValueOrValues)){
27367                                 var newValues = newValueOrValues;
27368                                 // Unfortunately, it's not safe to just do this:
27369                                 //    newValueArray = newValues;
27370                                 // Instead, we need to copy the array, which slice() does very nicely.
27371                                 // This is so that our internal data structure won't
27372                                 // get corrupted if the user mucks with the values array *after*
27373                                 // calling setValues().
27374                                 newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
27375                         }else{
27376                                 newValueArray = [newValueOrValues];
27377                         }
27378
27379                         //We need to handle reference integrity if this is on.
27380                         //In the case of set, we need to see if references were added or removed
27381                         //and update the reference tracking map accordingly.
27382                         if(this.referenceIntegrity){
27383                                 if(oldValueOrValues){
27384                                         var oldValues = oldValueOrValues;
27385                                         if(!dojo.isArray(oldValues)){
27386                                                 oldValues = [oldValues];
27387                                         }
27388                                         //Use an associative map to determine what was added/removed from the list.
27389                                         //Should be O(n) performant.  First look at all the old values and make a list of them
27390                                         //Then for any item not in the old list, we add it.  If it was already present, we remove it.
27391                                         //Then we pass over the map and any references left it it need to be removed (IE, no match in
27392                                         //the new values list).
27393                                         var map = {};
27394                                         dojo.forEach(oldValues, function(possibleItem){
27395                                                 if(this.isItem(possibleItem)){
27396                                                         var id = this.getIdentity(possibleItem);
27397                                                         map[id.toString()] = true;
27398                                                 }
27399                                         }, this);
27400                                         dojo.forEach(newValueArray, function(possibleItem){
27401                                                 if(this.isItem(possibleItem)){
27402                                                         var id = this.getIdentity(possibleItem);
27403                                                         if(map[id.toString()]){
27404                                                                 delete map[id.toString()];
27405                                                         }else{
27406                                                                 this._addReferenceToMap(possibleItem, item, attribute);
27407                                                         }
27408                                                 }
27409                                         }, this);
27410                                         for(var rId in map){
27411                                                 var removedItem;
27412                                                 if(this._itemsByIdentity){
27413                                                         removedItem = this._itemsByIdentity[rId];
27414                                                 }else{
27415                                                         removedItem = this._arrayOfAllItems[rId];
27416                                                 }
27417                                                 this._removeReferenceFromMap(removedItem, item, attribute);
27418                                         }
27419                                 }else{
27420                                         //Everything is new (no old values) so we have to just
27421                                         //insert all the references, if any.
27422                                         for(var i = 0; i < newValueArray.length; i++){
27423                                                 var value = newValueArray[i];
27424                                                 if(this.isItem(value)){
27425                                                         this._addReferenceToMap(value, item, attribute);
27426                                                 }
27427                                         }
27428                                 }
27429                         }
27430                         item[attribute] = newValueArray;
27431                         success = true;
27432                 }
27433
27434                 // Now we make the dojo.data.api.Notification call
27435                 if(callOnSet){
27436                         this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
27437                 }
27438                 return success; // boolean
27439         },
27440
27441         _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
27442                 //      summary:
27443                 //              Method to add an reference map entry for an item and attribute.
27444                 //      description:
27445                 //              Method to add an reference map entry for an item and attribute.                  //
27446                 //      refItem:
27447                 //              The item that is referenced.
27448                 //      parentItem:
27449                 //              The item that holds the new reference to refItem.
27450                 //      attribute:
27451                 //              The attribute on parentItem that contains the new reference.
27452                  
27453                 var parentId = this.getIdentity(parentItem);
27454                 var references = refItem[this._reverseRefMap];
27455
27456                 if(!references){
27457                         references = refItem[this._reverseRefMap] = {};
27458                 }
27459                 var itemRef = references[parentId];
27460                 if(!itemRef){
27461                         itemRef = references[parentId] = {};
27462                 }
27463                 itemRef[attribute] = true;
27464         },
27465
27466         _removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /*strin*/ attribute){
27467                 //      summary:
27468                 //              Method to remove an reference map entry for an item and attribute.
27469                 //      description:
27470                 //              Method to remove an reference map entry for an item and attribute.  This will
27471                 //              also perform cleanup on the map such that if there are no more references at all to
27472                 //              the item, its reference object and entry are removed.
27473                 //
27474                 //      refItem:
27475                 //              The item that is referenced.
27476                 //      parentItem:
27477                 //              The item holding a reference to refItem.
27478                 //      attribute:
27479                 //              The attribute on parentItem that contains the reference.
27480                 var identity = this.getIdentity(parentItem);
27481                 var references = refItem[this._reverseRefMap];
27482                 var itemId;
27483                 if(references){
27484                         for(itemId in references){
27485                                 if(itemId == identity){
27486                                         delete references[itemId][attribute];
27487                                         if(this._isEmpty(references[itemId])){
27488                                                 delete references[itemId];
27489                                         }
27490                                 }
27491                         }
27492                         if(this._isEmpty(references)){
27493                                 delete refItem[this._reverseRefMap];
27494                         }
27495                 }
27496         },
27497
27498         _dumpReferenceMap: function(){
27499                 //      summary:
27500                 //              Function to dump the reverse reference map of all items in the store for debug purposes.
27501                 //      description:
27502                 //              Function to dump the reverse reference map of all items in the store for debug purposes.
27503                 var i;
27504                 for(i = 0; i < this._arrayOfAllItems.length; i++){
27505                         var item = this._arrayOfAllItems[i];
27506                         if(item && item[this._reverseRefMap]){
27507                                 console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + dojo.toJson(item[this._reverseRefMap]));
27508                         }
27509                 }
27510         },
27511         
27512         _getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
27513                 var valueOrValues = undefined;
27514                 if(this.hasAttribute(item, attribute)){
27515                         var valueArray = this.getValues(item, attribute);
27516                         if(valueArray.length == 1){
27517                                 valueOrValues = valueArray[0];
27518                         }else{
27519                                 valueOrValues = valueArray;
27520                         }
27521                 }
27522                 return valueOrValues;
27523         },
27524         
27525         _flatten: function(/* anything */ value){
27526                 if(this.isItem(value)){
27527                         var item = value;
27528                         // Given an item, return an serializable object that provides a
27529                         // reference to the item.
27530                         // For example, given kermit:
27531                         //    var kermit = store.newItem({id:2, name:"Kermit"});
27532                         // we want to return
27533                         //    {_reference:2}
27534                         var identity = this.getIdentity(item);
27535                         var referenceObject = {_reference: identity};
27536                         return referenceObject;
27537                 }else{
27538                         if(typeof value === "object"){
27539                                 for(var type in this._datatypeMap){
27540                                         var typeMap = this._datatypeMap[type];
27541                                         if(dojo.isObject(typeMap) && !dojo.isFunction(typeMap)){
27542                                                 if(value instanceof typeMap.type){
27543                                                         if(!typeMap.serialize){
27544                                                                 throw new Error("ItemFileWriteStore:  No serializer defined for type mapping: [" + type + "]");
27545                                                         }
27546                                                         return {_type: type, _value: typeMap.serialize(value)};
27547                                                 }
27548                                         } else if(value instanceof typeMap){
27549                                                 //SImple mapping, therefore, return as a toString serialization.
27550                                                 return {_type: type, _value: value.toString()};
27551                                         }
27552                                 }
27553                         }
27554                         return value;
27555                 }
27556         },
27557         
27558         _getNewFileContentString: function(){
27559                 // summary:
27560                 //              Generate a string that can be saved to a file.
27561                 //              The result should look similar to:
27562                 //              http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
27563                 var serializableStructure = {};
27564                 
27565                 var identifierAttribute = this._getIdentifierAttribute();
27566                 if(identifierAttribute !== Number){
27567                         serializableStructure.identifier = identifierAttribute;
27568                 }
27569                 if(this._labelAttr){
27570                         serializableStructure.label = this._labelAttr;
27571                 }
27572                 serializableStructure.items = [];
27573                 for(var i = 0; i < this._arrayOfAllItems.length; ++i){
27574                         var item = this._arrayOfAllItems[i];
27575                         if(item !== null){
27576                                 var serializableItem = {};
27577                                 for(var key in item){
27578                                         if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
27579                                                 var attribute = key;
27580                                                 var valueArray = this.getValues(item, attribute);
27581                                                 if(valueArray.length == 1){
27582                                                         serializableItem[attribute] = this._flatten(valueArray[0]);
27583                                                 }else{
27584                                                         var serializableArray = [];
27585                                                         for(var j = 0; j < valueArray.length; ++j){
27586                                                                 serializableArray.push(this._flatten(valueArray[j]));
27587                                                                 serializableItem[attribute] = serializableArray;
27588                                                         }
27589                                                 }
27590                                         }
27591                                 }
27592                                 serializableStructure.items.push(serializableItem);
27593                         }
27594                 }
27595                 var prettyPrint = true;
27596                 return dojo.toJson(serializableStructure, prettyPrint);
27597         },
27598
27599         _isEmpty: function(something){
27600                 //      summary:
27601                 //              Function to determine if an array or object has no properties or values.
27602                 //      something:
27603                 //              The array or object to examine.
27604                 var empty = true;
27605                 if(dojo.isObject(something)){
27606                         var i;
27607                         for(i in something){
27608                                 empty = false;
27609                                 break;
27610                         }
27611                 }else if(dojo.isArray(something)){
27612                         if(something.length > 0){
27613                                 empty = false;
27614                         }
27615                 }
27616                 return empty; //boolean
27617         },
27618         
27619         save: function(/* object */ keywordArgs){
27620                 // summary: See dojo.data.api.Write.save()
27621                 this._assert(!this._saveInProgress);
27622                 
27623                 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
27624                 this._saveInProgress = true;
27625                 
27626                 var self = this;
27627                 var saveCompleteCallback = function(){
27628                         self._pending = {
27629                                 _newItems:{},
27630                                 _modifiedItems:{},
27631                                 _deletedItems:{}
27632                         };
27633
27634                         self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
27635                         if(keywordArgs && keywordArgs.onComplete){
27636                                 var scope = keywordArgs.scope || dojo.global;
27637                                 keywordArgs.onComplete.call(scope);
27638                         }
27639                 };
27640                 var saveFailedCallback = function(err){
27641                         self._saveInProgress = false;
27642                         if(keywordArgs && keywordArgs.onError){
27643                                 var scope = keywordArgs.scope || dojo.global;
27644                                 keywordArgs.onError.call(scope, err);
27645                         }
27646                 };
27647                 
27648                 if(this._saveEverything){
27649                         var newFileContentString = this._getNewFileContentString();
27650                         this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
27651                 }
27652                 if(this._saveCustom){
27653                         this._saveCustom(saveCompleteCallback, saveFailedCallback);
27654                 }
27655                 if(!this._saveEverything && !this._saveCustom){
27656                         // Looks like there is no user-defined save-handler function.
27657                         // That's fine, it just means the datastore is acting as a "mock-write"
27658                         // store -- changes get saved in memory but don't get saved to disk.
27659                         saveCompleteCallback();
27660                 }
27661         },
27662         
27663         revert: function(){
27664                 // summary: See dojo.data.api.Write.revert()
27665                 this._assert(!this._saveInProgress);
27666
27667                 var identity;
27668                 for(identity in this._pending._modifiedItems){
27669                         // find the original item and the modified item that replaced it
27670                         var copyOfItemState = this._pending._modifiedItems[identity];
27671                         var modifiedItem = null;
27672                         if(this._itemsByIdentity){
27673                                 modifiedItem = this._itemsByIdentity[identity];
27674                         }else{
27675                                 modifiedItem = this._arrayOfAllItems[identity];
27676                         }
27677         
27678                         // Restore the original item into a full-fledged item again, we want to try to
27679                         // keep the same object instance as if we don't it, causes bugs like #9022.
27680                         copyOfItemState[this._storeRefPropName] = this;
27681                         for(key in modifiedItem){
27682                                 delete modifiedItem[key];
27683                         }
27684                         dojo.mixin(modifiedItem, copyOfItemState);
27685                 }
27686                 var deletedItem;
27687                 for(identity in this._pending._deletedItems){
27688                         deletedItem = this._pending._deletedItems[identity];
27689                         deletedItem[this._storeRefPropName] = this;
27690                         var index = deletedItem[this._itemNumPropName];
27691
27692                         //Restore the reverse refererence map, if any.
27693                         if(deletedItem["backup_" + this._reverseRefMap]){
27694                                 deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
27695                                 delete deletedItem["backup_" + this._reverseRefMap];
27696                         }
27697                         this._arrayOfAllItems[index] = deletedItem;
27698                         if(this._itemsByIdentity){
27699                                 this._itemsByIdentity[identity] = deletedItem;
27700                         }
27701                         if(deletedItem[this._rootItemPropName]){
27702                                 this._arrayOfTopLevelItems.push(deletedItem);
27703                         }
27704                 }
27705                 //We have to pass through it again and restore the reference maps after all the
27706                 //undeletes have occurred.
27707                 for(identity in this._pending._deletedItems){
27708                         deletedItem = this._pending._deletedItems[identity];
27709                         if(deletedItem["backupRefs_" + this._reverseRefMap]){
27710                                 dojo.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
27711                                         var refItem;
27712                                         if(this._itemsByIdentity){
27713                                                 refItem = this._itemsByIdentity[reference.id];
27714                                         }else{
27715                                                 refItem = this._arrayOfAllItems[reference.id];
27716                                         }
27717                                         this._addReferenceToMap(refItem, deletedItem, reference.attr);
27718                                 }, this);
27719                                 delete deletedItem["backupRefs_" + this._reverseRefMap];
27720                         }
27721                 }
27722
27723                 for(identity in this._pending._newItems){
27724                         var newItem = this._pending._newItems[identity];
27725                         newItem[this._storeRefPropName] = null;
27726                         // null out the new item, but don't change the array index so
27727                         // so we can keep using _arrayOfAllItems.length.
27728                         this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
27729                         if(newItem[this._rootItemPropName]){
27730                                 this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
27731                         }
27732                         if(this._itemsByIdentity){
27733                                 delete this._itemsByIdentity[identity];
27734                         }
27735                 }
27736
27737                 this._pending = {
27738                         _newItems:{},
27739                         _modifiedItems:{},
27740                         _deletedItems:{}
27741                 };
27742                 return true; // boolean
27743         },
27744         
27745         isDirty: function(/* item? */ item){
27746                 // summary: See dojo.data.api.Write.isDirty()
27747                 if(item){
27748                         // return true if the item is dirty
27749                         var identity = this.getIdentity(item);
27750                         return new Boolean(this._pending._newItems[identity] ||
27751                                 this._pending._modifiedItems[identity] ||
27752                                 this._pending._deletedItems[identity]).valueOf(); // boolean
27753                 }else{
27754                         // return true if the store is dirty -- which means return true
27755                         // if there are any new items, dirty items, or modified items
27756                         if(!this._isEmpty(this._pending._newItems) ||
27757                                 !this._isEmpty(this._pending._modifiedItems) ||
27758                                 !this._isEmpty(this._pending._deletedItems)){
27759                                 return true;
27760                         }
27761                         return false; // boolean
27762                 }
27763         },
27764
27765 /* dojo.data.api.Notification */
27766
27767         onSet: function(/* item */ item,
27768                                         /*attribute-name-string*/ attribute,
27769                                         /*object | array*/ oldValue,
27770                                         /*object | array*/ newValue){
27771                 // summary: See dojo.data.api.Notification.onSet()
27772                 
27773                 // No need to do anything. This method is here just so that the
27774                 // client code can connect observers to it.
27775         },
27776
27777         onNew: function(/* item */ newItem, /*object?*/ parentInfo){
27778                 // summary: See dojo.data.api.Notification.onNew()
27779                 
27780                 // No need to do anything. This method is here just so that the
27781                 // client code can connect observers to it.
27782         },
27783
27784         onDelete: function(/* item */ deletedItem){
27785                 // summary: See dojo.data.api.Notification.onDelete()
27786                 
27787                 // No need to do anything. This method is here just so that the
27788                 // client code can connect observers to it.
27789         },
27790
27791         close: function(/* object? */ request){
27792                  // summary:
27793                  //             Over-ride of base close function of ItemFileReadStore to add in check for store state.
27794                  // description:
27795                  //             Over-ride of base close function of ItemFileReadStore to add in check for store state.
27796                  //             If the store is still dirty (unsaved changes), then an error will be thrown instead of
27797                  //             clearing the internal state for reload from the url.
27798
27799                  //Clear if not dirty ... or throw an error
27800                  if(this.clearOnClose){
27801                          if(!this.isDirty()){
27802                                  this.inherited(arguments);
27803                          }else{
27804                                  //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
27805                                  throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store.  Please save or revert the changes before invoking close.");
27806                          }
27807                  }
27808         }
27809 });
27810
27811 }
27812
27813
27814 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"]);