]> git.wh0rd.org - tt-rss.git/blob - lib/dojo/tt-rss-layer.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dojo / tt-rss-layer.js.uncompressed.js
1 require({cache:{
2 'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n",
3 'dijit/form/TextBox':function(){
4 require({cache:{
5 'url: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\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
6 define("dijit/form/TextBox", [
7 "dojo/_base/declare", // declare
8 "dojo/dom-construct", // domConstruct.create
9 "dojo/dom-style", // domStyle.getComputedStyle
10 "dojo/_base/kernel", // kernel.deprecated
11 "dojo/_base/lang", // lang.hitch
12 "dojo/sniff", // has("ie") has("mozilla")
13 "./_FormValueWidget",
14 "./_TextBoxMixin",
15 "dojo/text!./templates/TextBox.html",
16 "../main" // to export dijit._setSelectionRange, remove in 2.0
17 ], function(declare, domConstruct, domStyle, kernel, lang, has,
18 _FormValueWidget, _TextBoxMixin, template, dijit){
19
20 // module:
21 // dijit/form/TextBox
22
23 var TextBox = declare("dijit.form.TextBox", [_FormValueWidget, _TextBoxMixin], {
24 // summary:
25 // A base class for textbox form inputs
26
27 templateString: template,
28 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
29
30 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
31
32 baseClass: "dijitTextBox",
33
34 postMixInProperties: function(){
35 var type = this.type.toLowerCase();
36 if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
37 this.templateString = this._singleNodeTemplate;
38 }
39 this.inherited(arguments);
40 },
41
42 postCreate: function(){
43 this.inherited(arguments);
44
45 if(has("ie") < 9){
46 // IE INPUT tag fontFamily has to be set directly using STYLE
47 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
48 this.defer(function(){
49 try{
50 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
51 if(s){
52 var ff = s.fontFamily;
53 if(ff){
54 var inputs = this.domNode.getElementsByTagName("INPUT");
55 if(inputs){
56 for(var i=0; i < inputs.length; i++){
57 inputs[i].style.fontFamily = ff;
58 }
59 }
60 }
61 }
62 }catch(e){/*when used in a Dialog, and this is called before the dialog is
63 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
64 });
65 }
66 },
67
68 _onInput: function(e){
69 this.inherited(arguments);
70 if(this.intermediateChanges){ // _TextBoxMixin uses onInput
71 // allow the key to post to the widget input box
72 this.defer(function(){ this._handleOnChange(this.get('value'), false); });
73 }
74 },
75
76 _setPlaceHolderAttr: function(v){
77 this._set("placeHolder", v);
78 if(!this._phspan){
79 this._attachPoints.push('_phspan');
80 // dijitInputField class gives placeHolder same padding as the input field
81 // parent node already has dijitInputField class but it doesn't affect this <span>
82 // since it's position: absolute.
83 this._phspan = domConstruct.create('span',{ onmousedown:function(e){ e.preventDefault(); }, className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
84 }
85 this._phspan.innerHTML="";
86 this._phspan.appendChild(this._phspan.ownerDocument.createTextNode(v));
87 this._updatePlaceHolder();
88 },
89
90 _updatePlaceHolder: function(){
91 if(this._phspan){
92 this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
93 }
94 },
95
96 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
97 this.inherited(arguments);
98 this._updatePlaceHolder();
99 },
100
101 getDisplayedValue: function(){
102 // summary:
103 // Deprecated. Use get('displayedValue') instead.
104 // tags:
105 // deprecated
106 kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
107 return this.get('displayedValue');
108 },
109
110 setDisplayedValue: function(/*String*/ value){
111 // summary:
112 // Deprecated. Use set('displayedValue', ...) instead.
113 // tags:
114 // deprecated
115 kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
116 this.set('displayedValue', value);
117 },
118
119 _onBlur: function(e){
120 if(this.disabled){ return; }
121 this.inherited(arguments);
122 this._updatePlaceHolder();
123
124 if(has("mozilla")){
125 if(this.selectOnClick){
126 // clear selection so that the next mouse click doesn't reselect
127 this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
128 }
129 }
130 },
131
132 _onFocus: function(/*String*/ by){
133 if(this.disabled || this.readOnly){ return; }
134 this.inherited(arguments);
135 this._updatePlaceHolder();
136 }
137 });
138
139 if(has("ie")){
140 TextBox.prototype._isTextSelected = function(){
141 var range = this.ownerDocument.selection.createRange();
142 var parent = range.parentElement();
143 return parent == this.textbox && range.text.length > 0;
144 };
145
146 // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
147 dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
148 if(element.createTextRange){
149 var r = element.createTextRange();
150 r.collapse(true);
151 r.moveStart("character", -99999); // move to 0
152 r.moveStart("character", start); // delta from 0 is the correct position
153 r.moveEnd("character", stop-start);
154 r.select();
155 }
156 }
157 }
158
159 return TextBox;
160 });
161
162 },
163 'dijit/_base/scroll':function(){
164 define("dijit/_base/scroll", [
165 "dojo/window", // windowUtils.scrollIntoView
166 "../main" // export symbol to dijit
167 ], function(windowUtils, dijit){
168 // module:
169 // dijit/_base/scroll
170
171 /*=====
172 return {
173 // summary:
174 // Back compatibility module, new code should use windowUtils directly instead of using this module.
175 };
176 =====*/
177
178 dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
179 // summary:
180 // Scroll the passed node into view, if it is not already.
181 // Deprecated, use `windowUtils.scrollIntoView` instead.
182
183 windowUtils.scrollIntoView(node, pos);
184 };
185 });
186
187 },
188 'dijit/_TemplatedMixin':function(){
189 define("dijit/_TemplatedMixin", [
190 "dojo/_base/lang", // lang.getObject
191 "dojo/touch",
192 "./_WidgetBase",
193 "dojo/string", // string.substitute string.trim
194 "dojo/cache", // dojo.cache
195 "dojo/_base/array", // array.forEach
196 "dojo/_base/declare", // declare
197 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
198 "dojo/sniff", // has("ie")
199 "dojo/_base/unload" // unload.addOnWindowUnload
200 ], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload) {
201
202 // module:
203 // dijit/_TemplatedMixin
204
205 var _TemplatedMixin = declare("dijit._TemplatedMixin", null, {
206 // summary:
207 // Mixin for widgets that are instantiated from a template
208
209 // templateString: [protected] String
210 // A string that represents the widget template.
211 // Use in conjunction with dojo.cache() to load from a file.
212 templateString: null,
213
214 // templatePath: [protected deprecated] String
215 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
216 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
217 templatePath: null,
218
219 // skipNodeCache: [protected] Boolean
220 // If using a cached widget template nodes poses issues for a
221 // particular widget class, it can set this property to ensure
222 // that its template is always re-built from a string
223 _skipNodeCache: false,
224
225 // _earlyTemplatedStartup: Boolean
226 // A fallback to preserve the 1.0 - 1.3 behavior of children in
227 // templates having their startup called before the parent widget
228 // fires postCreate. Defaults to 'false', causing child widgets to
229 // have their .startup() called immediately before a parent widget
230 // .startup(), but always after the parent .postCreate(). Set to
231 // 'true' to re-enable to previous, arguably broken, behavior.
232 _earlyTemplatedStartup: false,
233
234 /*=====
235 // _attachPoints: [private] String[]
236 // List of widget attribute names associated with data-dojo-attach-point=... in the
237 // template, ex: ["containerNode", "labelNode"]
238 _attachPoints: [],
239
240 // _attachEvents: [private] Handle[]
241 // List of connections associated with data-dojo-attach-event=... in the
242 // template
243 _attachEvents: [],
244 =====*/
245
246 constructor: function(/*===== params, srcNodeRef =====*/){
247 // summary:
248 // Create the widget.
249 // params: Object|null
250 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
251 // and functions, typically callbacks like onClick.
252 // The hash can contain any of the widget's properties, excluding read-only properties.
253 // srcNodeRef: DOMNode|String?
254 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
255
256 this._attachPoints = [];
257 this._attachEvents = [];
258 },
259
260 _stringRepl: function(tmpl){
261 // summary:
262 // Does substitution of ${foo} type properties in template string
263 // tags:
264 // private
265 var className = this.declaredClass, _this = this;
266 // Cache contains a string because we need to do property replacement
267 // do the property replacement
268 return string.substitute(tmpl, this, function(value, key){
269 if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); }
270 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
271 if(value == null){ return ""; }
272
273 // Substitution keys beginning with ! will skip the transform step,
274 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
275 return key.charAt(0) == "!" ? value :
276 // Safer substitution, see heading "Attribute values" in
277 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
278 value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
279 }, this);
280 },
281
282 buildRendering: function(){
283 // summary:
284 // Construct the UI for this widget from a template, setting this.domNode.
285 // tags:
286 // protected
287
288 if(!this.templateString){
289 this.templateString = cache(this.templatePath, {sanitize: true});
290 }
291
292 // Lookup cached version of template, and download to cache if it
293 // isn't there already. Returns either a DomNode or a string, depending on
294 // whether or not the template contains ${foo} replacement parameters.
295 var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache, this.ownerDocument);
296
297 var node;
298 if(lang.isString(cached)){
299 node = domConstruct.toDom(this._stringRepl(cached), this.ownerDocument);
300 if(node.nodeType != 1){
301 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
302 throw new Error("Invalid template: " + cached);
303 }
304 }else{
305 // if it's a node, all we have to do is clone it
306 node = cached.cloneNode(true);
307 }
308
309 this.domNode = node;
310
311 // Call down to _Widget.buildRendering() to get base classes assigned
312 // TODO: change the baseClass assignment to _setBaseClassAttr
313 this.inherited(arguments);
314
315 // recurse through the node, looking for, and attaching to, our
316 // attachment points and events, which should be defined on the template node.
317 this._attachTemplateNodes(node, function(n,p){ return n.getAttribute(p); });
318
319 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
320
321 this._fillContent(this.srcNodeRef);
322 },
323
324 _beforeFillContent: function(){
325 },
326
327 _fillContent: function(/*DomNode*/ source){
328 // summary:
329 // Relocate source contents to templated container node.
330 // this.containerNode must be able to receive children, or exceptions will be thrown.
331 // tags:
332 // protected
333 var dest = this.containerNode;
334 if(source && dest){
335 while(source.hasChildNodes()){
336 dest.appendChild(source.firstChild);
337 }
338 }
339 },
340
341 _attachTemplateNodes: function(rootNode, getAttrFunc){
342 // summary:
343 // Iterate through the template and attach functions and nodes accordingly.
344 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
345 // etc. for those widgets.
346 // description:
347 // Map widget properties and functions to the handlers specified in
348 // the dom node and it's descendants. This function iterates over all
349 // nodes and looks for these properties:
350 //
351 // - dojoAttachPoint/data-dojo-attach-point
352 // - dojoAttachEvent/data-dojo-attach-event
353 // rootNode: DomNode|Widget[]
354 // the node to search for properties. All children will be searched.
355 // getAttrFunc: Function
356 // a function which will be used to obtain property for a given
357 // DomNode/Widget
358 // tags:
359 // private
360
361 var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
362 var x = lang.isArray(rootNode) ? 0 : -1;
363 for(; x < 0 || nodes[x]; x++){ // don't access nodes.length on IE, see #14346
364 var baseNode = (x == -1) ? rootNode : nodes[x];
365 if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
366 continue;
367 }
368 // Process data-dojo-attach-point
369 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
370 if(attachPoint){
371 var point, points = attachPoint.split(/\s*,\s*/);
372 while((point = points.shift())){
373 if(lang.isArray(this[point])){
374 this[point].push(baseNode);
375 }else{
376 this[point]=baseNode;
377 }
378 this._attachPoints.push(point);
379 }
380 }
381
382 // Process data-dojo-attach-event
383 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
384 if(attachEvent){
385 // NOTE: we want to support attributes that have the form
386 // "domEvent: nativeEvent; ..."
387 var event, events = attachEvent.split(/\s*,\s*/);
388 var trim = lang.trim;
389 while((event = events.shift())){
390 if(event){
391 var thisFunc = null;
392 if(event.indexOf(":") != -1){
393 // oh, if only JS had tuple assignment
394 var funcNameArr = event.split(":");
395 event = trim(funcNameArr[0]);
396 thisFunc = trim(funcNameArr[1]);
397 }else{
398 event = trim(event);
399 }
400 if(!thisFunc){
401 thisFunc = event;
402 }
403 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
404 this._attachEvents.push(this.connect(baseNode, touch[event] || event, thisFunc));
405 }
406 }
407 }
408 }
409 },
410
411 destroyRendering: function(){
412 // Delete all attach points to prevent IE6 memory leaks.
413 array.forEach(this._attachPoints, function(point){
414 delete this[point];
415 }, this);
416 this._attachPoints = [];
417
418 // And same for event handlers
419 array.forEach(this._attachEvents, this.disconnect, this);
420 this._attachEvents = [];
421
422 this.inherited(arguments);
423 }
424 });
425
426 // key is templateString; object is either string or DOM tree
427 _TemplatedMixin._templateCache = {};
428
429 _TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString, doc){
430 // summary:
431 // Static method to get a template based on the templatePath or
432 // templateString key
433 // templateString: String
434 // The template
435 // alwaysUseString: Boolean
436 // Don't cache the DOM tree for this template, even if it doesn't have any variables
437 // doc: Document?
438 // The target document. Defaults to document global if unspecified.
439 // returns: Mixed
440 // Either string (if there are ${} variables that need to be replaced) or just
441 // a DOM tree (if the node can be cloned directly)
442
443 // is it already cached?
444 var tmplts = _TemplatedMixin._templateCache;
445 var key = templateString;
446 var cached = tmplts[key];
447 if(cached){
448 try{
449 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
450 // current document, then use the current cached value
451 if(!cached.ownerDocument || cached.ownerDocument == (doc || document)){
452 // string or node of the same document
453 return cached;
454 }
455 }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
456 domConstruct.destroy(cached);
457 }
458
459 templateString = string.trim(templateString);
460
461 if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
462 // there are variables in the template so all we can do is cache the string
463 return (tmplts[key] = templateString); //String
464 }else{
465 // there are no variables in the template so we can cache the DOM tree
466 var node = domConstruct.toDom(templateString, doc);
467 if(node.nodeType != 1){
468 throw new Error("Invalid template: " + templateString);
469 }
470 return (tmplts[key] = node); //Node
471 }
472 };
473
474 if(has("ie")){
475 unload.addOnWindowUnload(function(){
476 var cache = _TemplatedMixin._templateCache;
477 for(var key in cache){
478 var value = cache[key];
479 if(typeof value == "object"){ // value is either a string or a DOM node template
480 domConstruct.destroy(value);
481 }
482 delete cache[key];
483 }
484 });
485 }
486
487 // These arguments can be specified for widgets which are used in templates.
488 // Since any widget can be specified as sub widgets in template, mix it
489 // into the base widget class. (This is a hack, but it's effective.).
490 // Remove for 2.0. Also, hide from API doc parser.
491 lang.extend(_WidgetBase, /*===== {} || =====*/ {
492 dojoAttachEvent: "",
493 dojoAttachPoint: ""
494 });
495
496 return _TemplatedMixin;
497 });
498
499 },
500 'dijit/_CssStateMixin':function(){
501 define("dijit/_CssStateMixin", [
502 "dojo/_base/array", // array.forEach array.map
503 "dojo/_base/declare", // declare
504 "dojo/dom", // dom.isDescendant()
505 "dojo/dom-class", // domClass.toggle
506 "dojo/has",
507 "dojo/_base/lang", // lang.hitch
508 "dojo/on",
509 "dojo/ready",
510 "dojo/_base/window", // win.body
511 "./registry"
512 ], function(array, declare, dom, domClass, has, lang, on, ready, win, registry){
513
514 // module:
515 // dijit/_CssStateMixin
516
517 var CssStateMixin = declare("dijit._CssStateMixin", [], {
518 // summary:
519 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
520 // state changes, and also higher-level state changes such becoming disabled or selected.
521 //
522 // description:
523 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
524 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
525 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
526 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
527 //
528 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
529 //
530 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
531 // within the widget).
532
533 // cssStateNodes: [protected] Object
534 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
535 //
536 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
537 // (like "dijitUpArrowButton"). Example:
538 // | {
539 // | "upArrowButton": "dijitUpArrowButton",
540 // | "downArrowButton": "dijitDownArrowButton"
541 // | }
542 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
543 // is hovered, etc.
544 cssStateNodes: {},
545
546 // hovering: [readonly] Boolean
547 // True if cursor is over this widget
548 hovering: false,
549
550 // active: [readonly] Boolean
551 // True if mouse was pressed while over this widget, and hasn't been released yet
552 active: false,
553
554 _applyAttributes: function(){
555 // This code would typically be in postCreate(), but putting in _applyAttributes() for
556 // performance: so the class changes happen before DOM is inserted into the document.
557 // Change back to postCreate() in 2.0. See #11635.
558
559 this.inherited(arguments);
560
561 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
562 array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr){
563 this.watch(attr, lang.hitch(this, "_setStateClass"));
564 }, this);
565
566 // Track hover and active mouse events on widget root node, plus possibly on subnodes
567 for(var ap in this.cssStateNodes){
568 this._trackMouseState(this[ap], this.cssStateNodes[ap]);
569 }
570 this._trackMouseState(this.domNode, this.baseClass);
571
572 // Set state initially; there's probably no hover/active/focus state but widget might be
573 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
574 this._setStateClass();
575 },
576
577 _cssMouseEvent: function(/*Event*/ event){
578 // summary:
579 // Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
580 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
581
582 if(!this.disabled){
583 switch(event.type){
584 case "mouseover":
585 this._set("hovering", true);
586 this._set("active", this._mouseDown);
587 break;
588 case "mouseout":
589 this._set("hovering", false);
590 this._set("active", false);
591 break;
592 case "mousedown":
593 case "touchstart":
594 this._set("active", true);
595 break;
596 case "mouseup":
597 case "touchend":
598 this._set("active", false);
599 break;
600 }
601 }
602 },
603
604 _setStateClass: function(){
605 // summary:
606 // Update the visual state of the widget by setting the css classes on this.domNode
607 // (or this.stateNode if defined) by combining this.baseClass with
608 // various suffixes that represent the current widget state(s).
609 //
610 // description:
611 // In the case where a widget has multiple
612 // states, it sets the class based on all possible
613 // combinations. For example, an invalid form widget that is being hovered
614 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
615 //
616 // The widget may have one or more of the following states, determined
617 // by this.state, this.checked, this.valid, and this.selected:
618 //
619 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
620 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
621 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
622 // - Selected - ex: currently selected tab will have this.selected==true
623 //
624 // In addition, it may have one or more of the following states,
625 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
626 //
627 // - Disabled - if the widget is disabled
628 // - Active - if the mouse (or space/enter key?) is being pressed down
629 // - Focused - if the widget has focus
630 // - Hover - if the mouse is over the widget
631
632 // Compute new set of classes
633 var newStateClasses = this.baseClass.split(" ");
634
635 function multiply(modifier){
636 newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
637 }
638
639 if(!this.isLeftToRight()){
640 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
641 multiply("Rtl");
642 }
643
644 var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
645 if(this.checked){
646 multiply(checkedState);
647 }
648 if(this.state){
649 multiply(this.state);
650 }
651 if(this.selected){
652 multiply("Selected");
653 }
654 if(this._opened){
655 multiply("Opened");
656 }
657
658 if(this.disabled){
659 multiply("Disabled");
660 }else if(this.readOnly){
661 multiply("ReadOnly");
662 }else{
663 if(this.active){
664 multiply("Active");
665 }else if(this.hovering){
666 multiply("Hover");
667 }
668 }
669
670 if(this.focused){
671 multiply("Focused");
672 }
673
674 // Remove old state classes and add new ones.
675 // For performance concerns we only write into domNode.className once.
676 var tn = this.stateNode || this.domNode,
677 classHash = {}; // set of all classes (state and otherwise) for node
678
679 array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
680
681 if("_stateClasses" in this){
682 array.forEach(this._stateClasses, function(c){ delete classHash[c]; });
683 }
684
685 array.forEach(newStateClasses, function(c){ classHash[c] = true; });
686
687 var newClasses = [];
688 for(var c in classHash){
689 newClasses.push(c);
690 }
691 tn.className = newClasses.join(" ");
692
693 this._stateClasses = newStateClasses;
694 },
695
696 _subnodeCssMouseEvent: function(node, clazz, evt){
697 // summary:
698 // Handler for hover/active mouse event on widget's subnode
699 if(this.disabled || this.readOnly){
700 return;
701 }
702 function hover(isHovering){
703 domClass.toggle(node, clazz+"Hover", isHovering);
704 }
705 function active(isActive){
706 domClass.toggle(node, clazz+"Active", isActive);
707 }
708 function focused(isFocused){
709 domClass.toggle(node, clazz+"Focused", isFocused);
710 }
711 switch(evt.type){
712 case "mouseover":
713 hover(true);
714 break;
715 case "mouseout":
716 hover(false);
717 active(false);
718 break;
719 case "mousedown":
720 case "touchstart":
721 active(true);
722 break;
723 case "mouseup":
724 case "touchend":
725 active(false);
726 break;
727 case "focus":
728 case "focusin":
729 focused(true);
730 break;
731 case "blur":
732 case "focusout":
733 focused(false);
734 break;
735 }
736 },
737
738 _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
739 // summary:
740 // Track mouse/focus events on specified node and set CSS class on that node to indicate
741 // current state. Usually not called directly, but via cssStateNodes attribute.
742 // description:
743 // Given class=foo, will set the following CSS class on the node
744 //
745 // - fooActive: if the user is currently pressing down the mouse button while over the node
746 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
747 // - fooFocus: if the node is focused
748 //
749 // Note that it won't set any classes if the widget is disabled.
750 // node: DomNode
751 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
752 // is handled specially and automatically just by mixing in this class.
753 // clazz: String
754 // CSS class name (ex: dijitSliderUpArrow)
755
756 // Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
757 // when node is hovered/active
758 node._cssState = clazz;
759 }
760 });
761
762 ready(function(){
763 // Document level listener to catch hover etc. events on widget root nodes and subnodes.
764 // Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
765 // have been hovered or unhovered (try test_Accordion.html)
766 function handler(evt){
767 // Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
768 // to avoid processing mouseout events moving from a widget's domNode to a descendant node;
769 // such events shouldn't be interpreted as a mouseleave on the widget.
770 if(!dom.isDescendant(evt.relatedTarget, evt.target)){
771 for(var node = evt.target; node && node != evt.relatedTarget; node = node.parentNode){
772 // Process any nodes with _cssState property. They are generally widget root nodes,
773 // but could also be sub-nodes within a widget
774 if(node._cssState){
775 var widget = registry.getEnclosingWidget(node);
776 if(widget){
777 if(node == widget.domNode){
778 // event on the widget's root node
779 widget._cssMouseEvent(evt);
780 }else{
781 // event on widget's sub-node
782 widget._subnodeCssMouseEvent(node, node._cssState, evt);
783 }
784 }
785 }
786 }
787 }
788 }
789 function ieHandler(evt){
790 evt.target = evt.srcElement;
791 handler(evt);
792 }
793
794 // Use addEventListener() (and attachEvent() on IE) to catch the relevant events even if other handlers
795 // (on individual nodes) call evt.stopPropagation() or event.stopEvent().
796 // Currently typematic.js is doing that, not sure why.
797 // Don't monitor mouseover/mouseout on mobile because iOS generates "phantom" mouseover/mouseout events when
798 // drag-scrolling, at the point in the viewport where the drag originated. Test the Tree in api viewer.
799 var body = win.body(),
800 types = (has("touch") ? [] : ["mouseover", "mouseout"]).concat(["mousedown", "touchstart", "mouseup", "touchend"]);
801 array.forEach(types, function(type){
802 if(body.addEventListener){
803 body.addEventListener(type, handler, true); // W3C
804 }else{
805 body.attachEvent("on"+type, ieHandler); // IE
806 }
807 });
808
809 // Track focus events on widget sub-nodes that have been registered via _trackMouseState().
810 // However, don't track focus events on the widget root nodes, because focus is tracked via the
811 // focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
812 // nodes or a subwidget's node or a popup node, etc.)
813 // Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
814 on(body, "focusin, focusout", function(evt){
815 var node = evt.target;
816 if(node._cssState && !node.getAttribute("widgetId")){
817 var widget = registry.getEnclosingWidget(node);
818 widget._subnodeCssMouseEvent(node, node._cssState, evt);
819 }
820 });
821 });
822
823 return CssStateMixin;
824 });
825
826 },
827 'dijit/layout/ScrollingTabController':function(){
828 require({cache:{
829 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
830 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>"}});
831 define("dijit/layout/ScrollingTabController", [
832 "dojo/_base/array", // array.forEach
833 "dojo/_base/declare", // declare
834 "dojo/dom-class", // domClass.add domClass.contains
835 "dojo/dom-geometry", // domGeometry.contentBox
836 "dojo/dom-style", // domStyle.style
837 "dojo/_base/fx", // Animation
838 "dojo/_base/lang", // lang.hitch
839 "dojo/on",
840 "dojo/query", // query
841 "dojo/sniff", // has("ie"), has("webkit"), has("quirks")
842 "../registry", // registry.byId()
843 "dojo/text!./templates/ScrollingTabController.html",
844 "dojo/text!./templates/_ScrollingTabControllerButton.html",
845 "./TabController",
846 "./utils", // marginBox2contextBox, layoutChildren
847 "../_WidgetsInTemplateMixin",
848 "../Menu",
849 "../MenuItem",
850 "../form/Button",
851 "../_HasDropDown",
852 "dojo/NodeList-dom" // NodeList.style
853 ], function(array, declare, domClass, domGeometry, domStyle, fx, lang, on, query, has,
854 registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
855 Menu, MenuItem, Button, _HasDropDown){
856
857 // module:
858 // dijit/layout/ScrollingTabController
859
860
861 var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
862 // summary:
863 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
864 // all fitting on a single row.
865 // Works only for horizontal tabs (either above or below the content, not to the left
866 // or right).
867 // tags:
868 // private
869
870 baseClass: "dijitTabController dijitScrollingTabController",
871
872 templateString: tabControllerTemplate,
873
874 // useMenu: [const] Boolean
875 // True if a menu should be used to select tabs when they are too
876 // wide to fit the TabContainer, false otherwise.
877 useMenu: true,
878
879 // useSlider: [const] Boolean
880 // True if a slider should be used to select tabs when they are too
881 // wide to fit the TabContainer, false otherwise.
882 useSlider: true,
883
884 // tabStripClass: [const] String
885 // The css class to apply to the tab strip, if it is visible.
886 tabStripClass: "",
887
888 widgetsInTemplate: true,
889
890 // _minScroll: Number
891 // The distance in pixels from the edge of the tab strip which,
892 // if a scroll animation is less than, forces the scroll to
893 // go all the way to the left/right.
894 _minScroll: 5,
895
896 // Override default behavior mapping class to DOMNode
897 _setClassAttr: { node: "containerNode", type: "class" },
898
899 buildRendering: function(){
900 this.inherited(arguments);
901 var n = this.domNode;
902
903 this.scrollNode = this.tablistWrapper;
904 this._initButtons();
905
906 if(!this.tabStripClass){
907 this.tabStripClass = "dijitTabContainer" +
908 this.tabPosition.charAt(0).toUpperCase() +
909 this.tabPosition.substr(1).replace(/-.*/, "") +
910 "None";
911 domClass.add(n, "tabStrip-disabled")
912 }
913
914 domClass.add(this.tablistWrapper, this.tabStripClass);
915 },
916
917 onStartup: function(){
918 this.inherited(arguments);
919
920 // TabController is hidden until it finishes drawing, to give
921 // a less visually jumpy instantiation. When it's finished, set visibility to ""
922 // to that the tabs are hidden/shown depending on the container's visibility setting.
923 domStyle.set(this.domNode, "visibility", "");
924 this._postStartup = true;
925
926 // changes to the tab button label or iconClass will have changed the width of the
927 // buttons, so do a resize
928 this.own(on(this.containerNode, "attrmodified-label, attrmodified-iconclass", lang.hitch(this, function(evt){
929 if(this._dim){
930 this.resize(this._dim);
931 }
932 })));
933 },
934
935 onAddChild: function(page, insertIndex){
936 this.inherited(arguments);
937
938 // Increment the width of the wrapper when a tab is added
939 // This makes sure that the buttons never wrap.
940 // The value 200 is chosen as it should be bigger than most
941 // Tab button widths.
942 domStyle.set(this.containerNode, "width",
943 (domStyle.get(this.containerNode, "width") + 200) + "px");
944 },
945
946 onRemoveChild: function(page, insertIndex){
947 // null out _selectedTab because we are about to delete that dom node
948 var button = this.pane2button[page.id];
949 if(this._selectedTab === button.domNode){
950 this._selectedTab = null;
951 }
952
953 this.inherited(arguments);
954 },
955
956 _initButtons: function(){
957 // summary:
958 // Creates the buttons used to scroll to view tabs that
959 // may not be visible if the TabContainer is too narrow.
960
961 // Make a list of the buttons to display when the tab labels become
962 // wider than the TabContainer, and hide the other buttons.
963 // Also gets the total width of the displayed buttons.
964 this._btnWidth = 0;
965 this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){
966 if((this.useMenu && btn == this._menuBtn.domNode) ||
967 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
968 this._btnWidth += domGeometry.getMarginSize(btn).w;
969 return true;
970 }else{
971 domStyle.set(btn, "display", "none");
972 return false;
973 }
974 }, this);
975 },
976
977 _getTabsWidth: function(){
978 var children = this.getChildren();
979 if(children.length){
980 var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
981 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
982 return rightTab.offsetLeft + rightTab.offsetWidth - leftTab.offsetLeft;
983 }else{
984 return 0;
985 }
986 },
987
988 _enableBtn: function(width){
989 // summary:
990 // Determines if the tabs are wider than the width of the TabContainer, and
991 // thus that we need to display left/right/menu navigation buttons.
992 var tabsWidth = this._getTabsWidth();
993 width = width || domStyle.get(this.scrollNode, "width");
994 return tabsWidth > 0 && width < tabsWidth;
995 },
996
997 resize: function(dim){
998 // summary:
999 // Hides or displays the buttons used to scroll the tab list and launch the menu
1000 // that selects tabs.
1001
1002 // Save the dimensions to be used when a child is renamed.
1003 this._dim = dim;
1004
1005 // Set my height to be my natural height (tall enough for one row of tab labels),
1006 // and my content-box width based on margin-box width specified in dim parameter.
1007 // But first reset scrollNode.height in case it was set by layoutChildren() call
1008 // in a previous run of this method.
1009 this.scrollNode.style.height = "auto";
1010 var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
1011 cb.h = this.scrollNode.offsetHeight;
1012 domGeometry.setContentSize(this.domNode, cb);
1013
1014 // Show/hide the left/right/menu navigation buttons depending on whether or not they
1015 // are needed.
1016 var enable = this._enableBtn(this._contentBox.w);
1017 this._buttons.style("display", enable ? "" : "none");
1018
1019 // Position and size the navigation buttons and the tablist
1020 this._leftBtn.layoutAlign = "left";
1021 this._rightBtn.layoutAlign = "right";
1022 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
1023 layoutUtils.layoutChildren(this.domNode, this._contentBox,
1024 [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
1025
1026 // set proper scroll so that selected tab is visible
1027 if(this._selectedTab){
1028 if(this._anim && this._anim.status() == "playing"){
1029 this._anim.stop();
1030 }
1031 this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab());
1032 }
1033
1034 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
1035 this._setButtonClass(this._getScroll());
1036
1037 this._postResize = true;
1038
1039 // Return my size so layoutChildren() can use it.
1040 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
1041 return {h: this._contentBox.h, w: dim.w};
1042 },
1043
1044 _getScroll: function(){
1045 // summary:
1046 // Returns the current scroll of the tabs where 0 means
1047 // "scrolled all the way to the left" and some positive number, based on #
1048 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
1049 return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft :
1050 domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width")
1051 + (has("ie") >= 8 ? -1 : 1) * this.scrollNode.scrollLeft;
1052 },
1053
1054 _convertToScrollLeft: function(val){
1055 // summary:
1056 // Given a scroll value where 0 means "scrolled all the way to the left"
1057 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
1058 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
1059 // to achieve that scroll.
1060 //
1061 // This method is to adjust for RTL funniness in various browsers and versions.
1062 if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
1063 return val;
1064 }else{
1065 var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width");
1066 return (has("ie") >= 8 ? -1 : 1) * (val - maxScroll);
1067 }
1068 },
1069
1070 onSelectChild: function(/*dijit/_WidgetBase*/ page){
1071 // summary:
1072 // Smoothly scrolls to a tab when it is selected.
1073
1074 var tab = this.pane2button[page.id];
1075 if(!tab || !page){return;}
1076
1077 var node = tab.domNode;
1078
1079 // Save the selection
1080 if(node != this._selectedTab){
1081 this._selectedTab = node;
1082
1083 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
1084 if(this._postResize){
1085 var sl = this._getScroll();
1086
1087 if(sl > node.offsetLeft ||
1088 sl + domStyle.get(this.scrollNode, "width") <
1089 node.offsetLeft + domStyle.get(node, "width")){
1090 this.createSmoothScroll().play();
1091 }
1092 }
1093 }
1094
1095 this.inherited(arguments);
1096 },
1097
1098 _getScrollBounds: function(){
1099 // summary:
1100 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
1101 // tabs (respectively)
1102 var children = this.getChildren(),
1103 scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
1104 containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
1105 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
1106 tabsWidth = this._getTabsWidth();
1107
1108 if(children.length && tabsWidth > scrollNodeWidth){
1109 // Scrolling should happen
1110 return {
1111 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
1112 max: this.isLeftToRight() ?
1113 (children[children.length-1].domNode.offsetLeft + children[children.length-1].domNode.offsetWidth) - scrollNodeWidth :
1114 maxPossibleScroll
1115 };
1116 }else{
1117 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
1118 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
1119 return {
1120 min: onlyScrollPosition,
1121 max: onlyScrollPosition
1122 };
1123 }
1124 },
1125
1126 _getScrollForSelectedTab: function(){
1127 // summary:
1128 // Returns the scroll value setting so that the selected tab
1129 // will appear in the center
1130 var w = this.scrollNode,
1131 n = this._selectedTab,
1132 scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
1133 scrollBounds = this._getScrollBounds();
1134
1135 // TODO: scroll minimal amount (to either right or left) so that
1136 // selected tab is fully visible, and just return if it's already visible?
1137 var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
1138 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
1139
1140 // TODO:
1141 // If scrolling close to the left side or right side, scroll
1142 // all the way to the left or right. See this._minScroll.
1143 // (But need to make sure that doesn't scroll the tab out of view...)
1144 return pos;
1145 },
1146
1147 createSmoothScroll: function(x){
1148 // summary:
1149 // Creates a dojo._Animation object that smoothly scrolls the tab list
1150 // either to a fixed horizontal pixel value, or to the selected tab.
1151 // description:
1152 // If an number argument is passed to the function, that horizontal
1153 // pixel position is scrolled to. Otherwise the currently selected
1154 // tab is scrolled to.
1155 // x: Integer?
1156 // An optional pixel value to scroll to, indicating distance from left.
1157
1158 // Calculate position to scroll to
1159 if(arguments.length > 0){
1160 // position specified by caller, just make sure it's within bounds
1161 var scrollBounds = this._getScrollBounds();
1162 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
1163 }else{
1164 // scroll to center the current tab
1165 x = this._getScrollForSelectedTab();
1166 }
1167
1168 if(this._anim && this._anim.status() == "playing"){
1169 this._anim.stop();
1170 }
1171
1172 var self = this,
1173 w = this.scrollNode,
1174 anim = new fx.Animation({
1175 beforeBegin: function(){
1176 if(this.curve){ delete this.curve; }
1177 var oldS = w.scrollLeft,
1178 newS = self._convertToScrollLeft(x);
1179 anim.curve = new fx._Line(oldS, newS);
1180 },
1181 onAnimate: function(val){
1182 w.scrollLeft = val;
1183 }
1184 });
1185 this._anim = anim;
1186
1187 // Disable/enable left/right buttons according to new scroll position
1188 this._setButtonClass(x);
1189
1190 return anim; // dojo/_base/fx/Animation
1191 },
1192
1193 _getBtnNode: function(/*Event*/ e){
1194 // summary:
1195 // Gets a button DOM node from a mouse click event.
1196 // e:
1197 // The mouse click event.
1198 var n = e.target;
1199 while(n && !domClass.contains(n, "tabStripButton")){
1200 n = n.parentNode;
1201 }
1202 return n;
1203 },
1204
1205 doSlideRight: function(/*Event*/ e){
1206 // summary:
1207 // Scrolls the menu to the right.
1208 // e:
1209 // The mouse click event.
1210 this.doSlide(1, this._getBtnNode(e));
1211 },
1212
1213 doSlideLeft: function(/*Event*/ e){
1214 // summary:
1215 // Scrolls the menu to the left.
1216 // e:
1217 // The mouse click event.
1218 this.doSlide(-1,this._getBtnNode(e));
1219 },
1220
1221 doSlide: function(/*Number*/ direction, /*DomNode*/ node){
1222 // summary:
1223 // Scrolls the tab list to the left or right by 75% of the widget width.
1224 // direction:
1225 // If the direction is 1, the widget scrolls to the right, if it is -1,
1226 // it scrolls to the left.
1227
1228 if(node && domClass.contains(node, "dijitTabDisabled")){return;}
1229
1230 var sWidth = domStyle.get(this.scrollNode, "width");
1231 var d = (sWidth * 0.75) * direction;
1232
1233 var to = this._getScroll() + d;
1234
1235 this._setButtonClass(to);
1236
1237 this.createSmoothScroll(to).play();
1238 },
1239
1240 _setButtonClass: function(/*Number*/ scroll){
1241 // summary:
1242 // Disables the left scroll button if the tabs are scrolled all the way to the left,
1243 // or the right scroll button in the opposite case.
1244 // scroll: Integer
1245 // amount of horizontal scroll
1246
1247 var scrollBounds = this._getScrollBounds();
1248 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
1249 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
1250 }
1251 });
1252
1253
1254 var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
1255 baseClass: "dijitTab tabStripButton",
1256
1257 templateString: buttonTemplate,
1258
1259 // Override inherited tabIndex: 0 from dijit/form/Button, because user shouldn't be
1260 // able to tab to the left/right/menu buttons
1261 tabIndex: "",
1262
1263 // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
1264 // either (this override avoids focus() call in FormWidget.js)
1265 isFocusable: function(){ return false; }
1266 });
1267
1268 // Class used in template
1269 declare("dijit.layout._ScrollingTabControllerButton",
1270 [Button, ScrollingTabControllerButtonMixin]);
1271
1272 // Class used in template
1273 declare(
1274 "dijit.layout._ScrollingTabControllerMenuButton",
1275 [Button, _HasDropDown, ScrollingTabControllerButtonMixin],
1276 {
1277 // id of the TabContainer itself
1278 containerId: "",
1279
1280 // -1 so user can't tab into the button, but so that button can still be focused programatically.
1281 // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
1282 tabIndex: "-1",
1283
1284 isLoaded: function(){
1285 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
1286 return false;
1287 },
1288
1289 loadDropDown: function(callback){
1290 this.dropDown = new Menu({
1291 id: this.containerId + "_menu",
1292 ownerDocument: this.ownerDocument,
1293 dir: this.dir,
1294 lang: this.lang,
1295 textDir: this.textDir
1296 });
1297 var container = registry.byId(this.containerId);
1298 array.forEach(container.getChildren(), function(page){
1299 var menuItem = new MenuItem({
1300 id: page.id + "_stcMi",
1301 label: page.title,
1302 iconClass: page.iconClass,
1303 disabled: page.disabled,
1304 ownerDocument: this.ownerDocument,
1305 dir: page.dir,
1306 lang: page.lang,
1307 textDir: page.textDir,
1308 onClick: function(){
1309 container.selectChild(page);
1310 }
1311 });
1312 this.dropDown.addChild(menuItem);
1313 }, this);
1314 callback();
1315 },
1316
1317 closeDropDown: function(/*Boolean*/ focus){
1318 this.inherited(arguments);
1319 if(this.dropDown){
1320 this.dropDown.destroyRecursive();
1321 delete this.dropDown;
1322 }
1323 }
1324 });
1325
1326 return ScrollingTabController;
1327 });
1328
1329 },
1330 'url: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\" data-dojo-attach-point=\"buttonNode\" data-dojo-attach-event=\"ondijitclick:_onClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" data-dojo-attach-point=\"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\tdata-dojo-attach-point=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdata-dojo-attach-event=\"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}\" data-dojo-attach-point=\"valueNode\" role=\"presentation\"\n\t\t/></td></tr></tbody\n></table>\n",
1331 'dijit/DialogUnderlay':function(){
1332 define("dijit/DialogUnderlay", [
1333 "dojo/_base/declare", // declare
1334 "dojo/dom-attr", // domAttr.set
1335 "dojo/window", // winUtils.getBox
1336 "./_Widget",
1337 "./_TemplatedMixin",
1338 "./BackgroundIframe"
1339 ], function(declare, domAttr, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
1340
1341 // module:
1342 // dijit/DialogUnderlay
1343
1344 return declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
1345 // summary:
1346 // The component that blocks the screen behind a `dijit.Dialog`
1347 //
1348 // description:
1349 // A component used to block input behind a `dijit.Dialog`. Only a single
1350 // instance of this widget is created by `dijit.Dialog`, and saved as
1351 // a reference to be shared between all Dialogs as `dijit._underlay`
1352 //
1353 // The underlay itself can be styled based on and id:
1354 // | #myDialog_underlay { background-color:red; }
1355 //
1356 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
1357 // suffixed with _underlay.
1358
1359 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
1360 // Inner div has opacity specified in CSS file.
1361 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
1362
1363 // Parameters on creation or updatable later
1364
1365 // dialogId: String
1366 // Id of the dialog.... DialogUnderlay's id is based on this id
1367 dialogId: "",
1368
1369 // class: String
1370 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
1371 "class": "",
1372
1373 _setDialogIdAttr: function(id){
1374 domAttr.set(this.node, "id", id + "_underlay");
1375 this._set("dialogId", id);
1376 },
1377
1378 _setClassAttr: function(clazz){
1379 this.node.className = "dijitDialogUnderlay " + clazz;
1380 this._set("class", clazz);
1381 },
1382
1383 postCreate: function(){
1384 // summary:
1385 // Append the underlay to the body
1386 this.ownerDocumentBody.appendChild(this.domNode);
1387 },
1388
1389 layout: function(){
1390 // summary:
1391 // Sets the background to the size of the viewport
1392 //
1393 // description:
1394 // Sets the background to the size of the viewport (rather than the size
1395 // of the document) since we need to cover the whole browser window, even
1396 // if the document is only a few lines long.
1397 // tags:
1398 // private
1399
1400 var is = this.node.style,
1401 os = this.domNode.style;
1402
1403 // hide the background temporarily, so that the background itself isn't
1404 // causing scrollbars to appear (might happen when user shrinks browser
1405 // window and then we are called to resize)
1406 os.display = "none";
1407
1408 // then resize and show
1409 var viewport = winUtils.getBox(this.ownerDocument);
1410 os.top = viewport.t + "px";
1411 os.left = viewport.l + "px";
1412 is.width = viewport.w + "px";
1413 is.height = viewport.h + "px";
1414 os.display = "block";
1415 },
1416
1417 show: function(){
1418 // summary:
1419 // Show the dialog underlay
1420 this.domNode.style.display = "block";
1421 this.layout();
1422 this.bgIframe = new BackgroundIframe(this.domNode);
1423 },
1424
1425 hide: function(){
1426 // summary:
1427 // Hides the dialog underlay
1428 this.bgIframe.destroy();
1429 delete this.bgIframe;
1430 this.domNode.style.display = "none";
1431 }
1432 });
1433 });
1434
1435 },
1436 'dijit/place':function(){
1437 define("dijit/place", [
1438 "dojo/_base/array", // array.forEach array.map array.some
1439 "dojo/dom-geometry", // domGeometry.position
1440 "dojo/dom-style", // domStyle.getComputedStyle
1441 "dojo/_base/kernel", // kernel.deprecated
1442 "dojo/_base/window", // win.body
1443 "dojo/window", // winUtils.getBox
1444 "./main" // dijit (defining dijit.place to match API doc)
1445 ], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
1446
1447 // module:
1448 // dijit/place
1449
1450
1451 function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){
1452 // summary:
1453 // Given a list of spots to put node, put it at the first spot where it fits,
1454 // of if it doesn't fit anywhere then the place with the least overflow
1455 // choices: Array
1456 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1457 // Above example says to put the top-left corner of the node at (10,20)
1458 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
1459 // for things like tooltip, they are displayed differently (and have different dimensions)
1460 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1461 // It also passes in the available size for the popup, which is useful for tooltips to
1462 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
1463 // how much the popup had to be modified to fit into the available space. This is used to determine
1464 // what the best placement is.
1465 // aroundNodeCoords: Object
1466 // Size of aroundNode, ex: {w: 200, h: 50}
1467
1468 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1469 // viewport over document
1470 var view = winUtils.getBox(node.ownerDocument);
1471
1472 // This won't work if the node is inside a <div style="position: relative">,
1473 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
1474 // and also it might get cutoff)
1475 if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
1476 win.body(node.ownerDocument).appendChild(node);
1477 }
1478
1479 var best = null;
1480 array.some(choices, function(choice){
1481 var corner = choice.corner;
1482 var pos = choice.pos;
1483 var overflow = 0;
1484
1485 // calculate amount of space available given specified position of node
1486 var spaceAvailable = {
1487 w: {
1488 'L': view.l + view.w - pos.x,
1489 'R': pos.x - view.l,
1490 'M': view.w
1491 }[corner.charAt(1)],
1492 h: {
1493 'T': view.t + view.h - pos.y,
1494 'B': pos.y - view.t,
1495 'M': view.h
1496 }[corner.charAt(0)]
1497 };
1498
1499 // Clear left/right position settings set earlier so they don't interfere with calculations,
1500 // specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
1501 var s = node.style;
1502 s.left = s.right = "auto";
1503
1504 // configure node to be displayed in given position relative to button
1505 // (need to do this in order to get an accurate size for the node, because
1506 // a tooltip's size changes based on position, due to triangle)
1507 if(layoutNode){
1508 var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
1509 overflow = typeof res == "undefined" ? 0 : res;
1510 }
1511
1512 // get node's size
1513 var style = node.style;
1514 var oldDisplay = style.display;
1515 var oldVis = style.visibility;
1516 if(style.display == "none"){
1517 style.visibility = "hidden";
1518 style.display = "";
1519 }
1520 var bb = domGeometry.position(node);
1521 style.display = oldDisplay;
1522 style.visibility = oldVis;
1523
1524 // coordinates and size of node with specified corner placed at pos,
1525 // and clipped by viewport
1526 var
1527 startXpos = {
1528 'L': pos.x,
1529 'R': pos.x - bb.w,
1530 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (bb.w >> 1)) - bb.w) // M orientation is more flexible
1531 }[corner.charAt(1)],
1532 startYpos = {
1533 'T': pos.y,
1534 'B': pos.y - bb.h,
1535 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (bb.h >> 1)) - bb.h)
1536 }[corner.charAt(0)],
1537 startX = Math.max(view.l, startXpos),
1538 startY = Math.max(view.t, startYpos),
1539 endX = Math.min(view.l + view.w, startXpos + bb.w),
1540 endY = Math.min(view.t + view.h, startYpos + bb.h),
1541 width = endX - startX,
1542 height = endY - startY;
1543
1544 overflow += (bb.w - width) + (bb.h - height);
1545
1546 if(best == null || overflow < best.overflow){
1547 best = {
1548 corner: corner,
1549 aroundCorner: choice.aroundCorner,
1550 x: startX,
1551 y: startY,
1552 w: width,
1553 h: height,
1554 overflow: overflow,
1555 spaceAvailable: spaceAvailable
1556 };
1557 }
1558
1559 return !overflow;
1560 });
1561
1562 // In case the best position is not the last one we checked, need to call
1563 // layoutNode() again.
1564 if(best.overflow && layoutNode){
1565 layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
1566 }
1567
1568 // And then position the node. Do this last, after the layoutNode() above
1569 // has sized the node, due to browser quirks when the viewport is scrolled
1570 // (specifically that a Tooltip will shrink to fit as though the window was
1571 // scrolled to the left).
1572 //
1573 // In RTL mode, set style.right rather than style.left so in the common case,
1574 // window resizes move the popup along with the aroundNode.
1575 var l = domGeometry.isBodyLtr(node.ownerDocument),
1576 s = node.style;
1577 s.top = best.y + "px";
1578 s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
1579 s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
1580
1581 return best;
1582 }
1583
1584 var place = {
1585 // summary:
1586 // Code to place a DOMNode relative to another DOMNode.
1587 // Load using require(["dijit/place"], function(place){ ... }).
1588
1589 at: function(node, pos, corners, padding){
1590 // summary:
1591 // Positions one of the node's corners at specified position
1592 // such that node is fully visible in viewport.
1593 // description:
1594 // NOTE: node is assumed to be absolutely or relatively positioned.
1595 // node: DOMNode
1596 // The node to position
1597 // pos: dijit/place.__Position
1598 // Object like {x: 10, y: 20}
1599 // corners: String[]
1600 // Array of Strings representing order to try corners in, like ["TR", "BL"].
1601 // Possible values are:
1602 //
1603 // - "BL" - bottom left
1604 // - "BR" - bottom right
1605 // - "TL" - top left
1606 // - "TR" - top right
1607 // padding: dijit/place.__Position?
1608 // optional param to set padding, to put some buffer around the element you want to position.
1609 // example:
1610 // Try to place node's top right corner at (10,20).
1611 // If that makes node go (partially) off screen, then try placing
1612 // bottom left corner at (10,20).
1613 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
1614 var choices = array.map(corners, function(corner){
1615 var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
1616 if(padding){
1617 c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
1618 c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
1619 }
1620 return c;
1621 });
1622
1623 return _place(node, choices);
1624 },
1625
1626 around: function(
1627 /*DomNode*/ node,
1628 /*DomNode|dijit/place.__Rectangle*/ anchor,
1629 /*String[]*/ positions,
1630 /*Boolean*/ leftToRight,
1631 /*Function?*/ layoutNode){
1632
1633 // summary:
1634 // Position node adjacent or kitty-corner to anchor
1635 // such that it's fully visible in viewport.
1636 // description:
1637 // Place node such that corner of node touches a corner of
1638 // aroundNode, and that node is fully visible.
1639 // anchor:
1640 // Either a DOMNode or a rectangle (object with x, y, width, height).
1641 // positions:
1642 // Ordered list of positions to try matching up.
1643 //
1644 // - before: places drop down to the left of the anchor node/widget, or to the right in the case
1645 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1646 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1647 // - after: places drop down to the right of the anchor node/widget, or to the left in the case
1648 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1649 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1650 // - before-centered: centers drop down to the left of the anchor node/widget, or to the right
1651 // in the case of RTL scripts like Hebrew and Arabic
1652 // - after-centered: centers drop down to the right of the anchor node/widget, or to the left
1653 // in the case of RTL scripts like Hebrew and Arabic
1654 // - above-centered: drop down is centered above anchor node
1655 // - above: drop down goes above anchor node, left sides aligned
1656 // - above-alt: drop down goes above anchor node, right sides aligned
1657 // - below-centered: drop down is centered above anchor node
1658 // - below: drop down goes below anchor node
1659 // - below-alt: drop down goes below anchor node, right sides aligned
1660 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1661 // For things like tooltip, they are displayed differently (and have different dimensions)
1662 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1663 // leftToRight:
1664 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
1665 // positions slightly.
1666 // example:
1667 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
1668 // This will try to position node such that node's top-left corner is at the same position
1669 // as the bottom left corner of the aroundNode (ie, put node below
1670 // aroundNode, with left edges aligned). If that fails it will try to put
1671 // the bottom-right corner of node where the top right corner of aroundNode is
1672 // (ie, put node above aroundNode, with right edges aligned)
1673 //
1674
1675 // if around is a DOMNode (or DOMNode id), convert to coordinates
1676 var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor)
1677 ? domGeometry.position(anchor, true)
1678 : anchor;
1679
1680 // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
1681 if(anchor.parentNode){
1682 // ignore nodes between position:relative and position:absolute
1683 var sawPosAbsolute = domStyle.getComputedStyle(anchor).position == "absolute";
1684 var parent = anchor.parentNode;
1685 while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
1686 var parentPos = domGeometry.position(parent, true),
1687 pcs = domStyle.getComputedStyle(parent);
1688 if(/relative|absolute/.test(pcs.position)){
1689 sawPosAbsolute = false;
1690 }
1691 if(!sawPosAbsolute && /hidden|auto|scroll/.test(pcs.overflow)){
1692 var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h);
1693 var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w);
1694 aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x);
1695 aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y);
1696 aroundNodePos.h = bottomYCoord - aroundNodePos.y;
1697 aroundNodePos.w = rightXCoord - aroundNodePos.x;
1698 }
1699 if(pcs.position == "absolute"){
1700 sawPosAbsolute = true;
1701 }
1702 parent = parent.parentNode;
1703 }
1704 }
1705
1706 var x = aroundNodePos.x,
1707 y = aroundNodePos.y,
1708 width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width),
1709 height = "h" in aroundNodePos ? aroundNodePos.h : (kernel.deprecated("place.around: dijit/place.__Rectangle: { x:"+x+", y:"+y+", height:"+aroundNodePos.height+", width:"+width+" } has been deprecated. Please use { x:"+x+", y:"+y+", h:"+aroundNodePos.height+", w:"+width+" }", "", "2.0"), aroundNodePos.h = aroundNodePos.height);
1710
1711 // Convert positions arguments into choices argument for _place()
1712 var choices = [];
1713 function push(aroundCorner, corner){
1714 choices.push({
1715 aroundCorner: aroundCorner,
1716 corner: corner,
1717 pos: {
1718 x: {
1719 'L': x,
1720 'R': x + width,
1721 'M': x + (width >> 1)
1722 }[aroundCorner.charAt(1)],
1723 y: {
1724 'T': y,
1725 'B': y + height,
1726 'M': y + (height >> 1)
1727 }[aroundCorner.charAt(0)]
1728 }
1729 })
1730 }
1731 array.forEach(positions, function(pos){
1732 var ltr = leftToRight;
1733 switch(pos){
1734 case "above-centered":
1735 push("TM", "BM");
1736 break;
1737 case "below-centered":
1738 push("BM", "TM");
1739 break;
1740 case "after-centered":
1741 ltr = !ltr;
1742 // fall through
1743 case "before-centered":
1744 push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
1745 break;
1746 case "after":
1747 ltr = !ltr;
1748 // fall through
1749 case "before":
1750 push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
1751 push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
1752 break;
1753 case "below-alt":
1754 ltr = !ltr;
1755 // fall through
1756 case "below":
1757 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1758 push(ltr ? "BL" : "BR", ltr ? "TL" : "TR");
1759 push(ltr ? "BR" : "BL", ltr ? "TR" : "TL");
1760 break;
1761 case "above-alt":
1762 ltr = !ltr;
1763 // fall through
1764 case "above":
1765 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1766 push(ltr ? "TL" : "TR", ltr ? "BL" : "BR");
1767 push(ltr ? "TR" : "TL", ltr ? "BR" : "BL");
1768 break;
1769 default:
1770 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
1771 // Not meant to be used directly.
1772 push(pos.aroundCorner, pos.corner);
1773 }
1774 });
1775
1776 var position = _place(node, choices, layoutNode, {w: width, h: height});
1777 position.aroundNodePos = aroundNodePos;
1778
1779 return position;
1780 }
1781 };
1782
1783 /*=====
1784 place.__Position = {
1785 // x: Integer
1786 // horizontal coordinate in pixels, relative to document body
1787 // y: Integer
1788 // vertical coordinate in pixels, relative to document body
1789 };
1790 place.__Rectangle = {
1791 // x: Integer
1792 // horizontal offset in pixels, relative to document body
1793 // y: Integer
1794 // vertical offset in pixels, relative to document body
1795 // w: Integer
1796 // width in pixels. Can also be specified as "width" for backwards-compatibility.
1797 // h: Integer
1798 // height in pixels. Can also be specified as "height" for backwards-compatibility.
1799 };
1800 =====*/
1801
1802 return dijit.place = place; // setting dijit.place for back-compat, remove for 2.0
1803 });
1804
1805 },
1806 'dijit/_HasDropDown':function(){
1807 define("dijit/_HasDropDown", [
1808 "dojo/_base/declare", // declare
1809 "dojo/_base/Deferred",
1810 "dojo/_base/event", // event.stop
1811 "dojo/dom", // dom.isDescendant
1812 "dojo/dom-attr", // domAttr.set
1813 "dojo/dom-class", // domClass.add domClass.contains domClass.remove
1814 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
1815 "dojo/dom-style", // domStyle.set
1816 "dojo/has", // has("touch")
1817 "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
1818 "dojo/_base/lang", // lang.hitch lang.isFunction
1819 "dojo/on",
1820 "dojo/window", // winUtils.getBox
1821 "./registry", // registry.byNode()
1822 "./focus",
1823 "./popup",
1824 "./_FocusMixin"
1825 ], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, on,
1826 winUtils, registry, focus, popup, _FocusMixin){
1827
1828
1829 // module:
1830 // dijit/_HasDropDown
1831
1832 return declare("dijit._HasDropDown", _FocusMixin, {
1833 // summary:
1834 // Mixin for widgets that need drop down ability.
1835
1836 // _buttonNode: [protected] DomNode
1837 // The button/icon/node to click to display the drop down.
1838 // Can be set via a data-dojo-attach-point assignment.
1839 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
1840 _buttonNode: null,
1841
1842 // _arrowWrapperNode: [protected] DomNode
1843 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
1844 // on where the drop down is set to be positioned.
1845 // Can be set via a data-dojo-attach-point assignment.
1846 // If missing, then _buttonNode will be used.
1847 _arrowWrapperNode: null,
1848
1849 // _popupStateNode: [protected] DomNode
1850 // The node to set the popupActive class on.
1851 // Can be set via a data-dojo-attach-point assignment.
1852 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
1853 _popupStateNode: null,
1854
1855 // _aroundNode: [protected] DomNode
1856 // The node to display the popup around.
1857 // Can be set via a data-dojo-attach-point assignment.
1858 // If missing, then domNode will be used.
1859 _aroundNode: null,
1860
1861 // dropDown: [protected] Widget
1862 // The widget to display as a popup. This widget *must* be
1863 // defined before the startup function is called.
1864 dropDown: null,
1865
1866 // autoWidth: [protected] Boolean
1867 // Set to true to make the drop down at least as wide as this
1868 // widget. Set to false if the drop down should just be its
1869 // default width
1870 autoWidth: true,
1871
1872 // forceWidth: [protected] Boolean
1873 // Set to true to make the drop down exactly as wide as this
1874 // widget. Overrides autoWidth.
1875 forceWidth: false,
1876
1877 // maxHeight: [protected] Integer
1878 // The max height for our dropdown.
1879 // Any dropdown taller than this will have scrollbars.
1880 // Set to 0 for no max height, or -1 to limit height to available space in viewport
1881 maxHeight: 0,
1882
1883 // dropDownPosition: [const] String[]
1884 // This variable controls the position of the drop down.
1885 // It's an array of strings with the following values:
1886 //
1887 // - before: places drop down to the left of the target node/widget, or to the right in
1888 // the case of RTL scripts like Hebrew and Arabic
1889 // - after: places drop down to the right of the target node/widget, or to the left in
1890 // the case of RTL scripts like Hebrew and Arabic
1891 // - above: drop down goes above target node
1892 // - below: drop down goes below target node
1893 //
1894 // The list is positions is tried, in order, until a position is found where the drop down fits
1895 // within the viewport.
1896 //
1897 dropDownPosition: ["below","above"],
1898
1899 // _stopClickEvents: Boolean
1900 // When set to false, the click events will not be stopped, in
1901 // case you want to use them in your subclass
1902 _stopClickEvents: true,
1903
1904 _onDropDownMouseDown: function(/*Event*/ e){
1905 // summary:
1906 // Callback when the user mousedown's on the arrow icon
1907 if(this.disabled || this.readOnly){ return; }
1908
1909 // Prevent default to stop things like text selection, but don't stop propagation, so that:
1910 // 1. TimeTextBox etc. can focus the <input> on mousedown
1911 // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
1912 // 3. user defined onMouseDown handler fires
1913 e.preventDefault();
1914
1915 this._docHandler = this.connect(this.ownerDocument, "mouseup", "_onDropDownMouseUp");
1916
1917 this.toggleDropDown();
1918 },
1919
1920 _onDropDownMouseUp: function(/*Event?*/ e){
1921 // summary:
1922 // Callback when the user lifts their mouse after mouse down on the arrow icon.
1923 // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
1924 // drop down widget. If the event is missing, then we are not
1925 // a mouseup event.
1926 //
1927 // This is useful for the common mouse movement pattern
1928 // with native browser `<select>` nodes:
1929 //
1930 // 1. mouse down on the select node (probably on the arrow)
1931 // 2. move mouse to a menu item while holding down the mouse button
1932 // 3. mouse up. this selects the menu item as though the user had clicked it.
1933 if(e && this._docHandler){
1934 this.disconnect(this._docHandler);
1935 }
1936 var dropDown = this.dropDown, overMenu = false;
1937
1938 if(e && this._opened){
1939 // This code deals with the corner-case when the drop down covers the original widget,
1940 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
1941 // Find out if our target is somewhere in our dropdown widget,
1942 // but not over our _buttonNode (the clickable node)
1943 var c = domGeometry.position(this._buttonNode, true);
1944 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
1945 !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
1946 var t = e.target;
1947 while(t && !overMenu){
1948 if(domClass.contains(t, "dijitPopup")){
1949 overMenu = true;
1950 }else{
1951 t = t.parentNode;
1952 }
1953 }
1954 if(overMenu){
1955 t = e.target;
1956 if(dropDown.onItemClick){
1957 var menuItem;
1958 while(t && !(menuItem = registry.byNode(t))){
1959 t = t.parentNode;
1960 }
1961 if(menuItem && menuItem.onClick && menuItem.getParent){
1962 menuItem.getParent().onItemClick(menuItem, e);
1963 }
1964 }
1965 return;
1966 }
1967 }
1968 }
1969 if(this._opened){
1970 if(dropDown.focus && dropDown.autoFocus !== false){
1971 // Focus the dropdown widget - do it on a delay so that we
1972 // don't steal back focus from the dropdown.
1973 this._focusDropDownTimer = this.defer(function(){
1974 dropDown.focus();
1975 delete this._focusDropDownTimer;
1976 });
1977 }
1978 }else{
1979 // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
1980 // defer() needed to make it work on IE (test DateTextBox)
1981 this.defer("focus");
1982 }
1983
1984 if(has("touch")){
1985 this._justGotMouseUp = true;
1986 this.defer(function(){
1987 this._justGotMouseUp = false;
1988 });
1989 }
1990 },
1991
1992 _onDropDownClick: function(/*Event*/ e){
1993 if(has("touch") && !this._justGotMouseUp){
1994 // If there was no preceding mousedown/mouseup (like on android), then simulate them to
1995 // toggle the drop down.
1996 //
1997 // The if(has("touch") is necessary since IE and desktop safari get spurious onclick events
1998 // when there are nested tables (specifically, clicking on a table that holds a dijit/form/Select,
1999 // but not on the Select itself, causes an onclick event on the Select)
2000 this._onDropDownMouseDown(e);
2001 this._onDropDownMouseUp(e);
2002 }
2003
2004 // The drop down was already opened on mousedown/keydown; just need to call stopEvent().
2005 if(this._stopClickEvents){
2006 event.stop(e);
2007 }
2008 },
2009
2010 buildRendering: function(){
2011 this.inherited(arguments);
2012
2013 this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
2014 this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
2015
2016 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
2017 // based on where drop down will normally appear
2018 var defaultPos = {
2019 "after" : this.isLeftToRight() ? "Right" : "Left",
2020 "before" : this.isLeftToRight() ? "Left" : "Right",
2021 "above" : "Up",
2022 "below" : "Down",
2023 "left" : "Left",
2024 "right" : "Right"
2025 }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
2026 domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
2027 },
2028
2029 postCreate: function(){
2030 // summary:
2031 // set up nodes and connect our mouse and keyboard events
2032
2033 this.inherited(arguments);
2034
2035 var keyboardEventNode = this.focusNode || this.domNode;
2036 this.own(
2037 on(this._buttonNode, "mousedown", lang.hitch(this, "_onDropDownMouseDown")),
2038 on(this._buttonNode, "click", lang.hitch(this, "_onDropDownClick")),
2039 on(keyboardEventNode, "keydown", lang.hitch(this, "_onKey")),
2040 on(keyboardEventNode, "keyup", lang.hitch(this, "_onKeyUp"))
2041 );
2042 },
2043
2044 destroy: function(){
2045 if(this.dropDown){
2046 // Destroy the drop down, unless it's already been destroyed. This can happen because
2047 // the drop down is a direct child of <body> even though it's logically my child.
2048 if(!this.dropDown._destroyed){
2049 this.dropDown.destroyRecursive();
2050 }
2051 delete this.dropDown;
2052 }
2053 this.inherited(arguments);
2054 },
2055
2056 _onKey: function(/*Event*/ e){
2057 // summary:
2058 // Callback when the user presses a key while focused on the button node
2059
2060 if(this.disabled || this.readOnly){ return; }
2061 var d = this.dropDown, target = e.target;
2062 if(d && this._opened && d.handleKey){
2063 if(d.handleKey(e) === false){
2064 /* false return code means that the drop down handled the key */
2065 event.stop(e);
2066 return;
2067 }
2068 }
2069 if(d && this._opened && e.keyCode == keys.ESCAPE){
2070 this.closeDropDown();
2071 event.stop(e);
2072 }else if(!this._opened &&
2073 (e.keyCode == keys.DOWN_ARROW ||
2074 ( (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
2075 //ignore enter and space if the event is for a text input
2076 ((target.tagName || "").toLowerCase() !== 'input' ||
2077 (target.type && target.type.toLowerCase() !== 'text'))))){
2078 // Toggle the drop down, but wait until keyup so that the drop down doesn't
2079 // get a stray keyup event, or in the case of key-repeat (because user held
2080 // down key for too long), stray keydown events
2081 this._toggleOnKeyUp = true;
2082 event.stop(e);
2083 }
2084 },
2085
2086 _onKeyUp: function(){
2087 if(this._toggleOnKeyUp){
2088 delete this._toggleOnKeyUp;
2089 this.toggleDropDown();
2090 var d = this.dropDown; // drop down may not exist until toggleDropDown() call
2091 if(d && d.focus){
2092 this.defer(lang.hitch(d, "focus"), 1);
2093 }
2094 }
2095 },
2096
2097 _onBlur: function(){
2098 // summary:
2099 // Called magically when focus has shifted away from this widget and it's dropdown
2100
2101 // Don't focus on button if the user has explicitly focused on something else (happens
2102 // when user clicks another control causing the current popup to close)..
2103 // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
2104 // it when you display:none a node with focus.
2105 var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode);
2106
2107 this.closeDropDown(focusMe);
2108
2109 this.inherited(arguments);
2110 },
2111
2112 isLoaded: function(){
2113 // summary:
2114 // Returns true if the dropdown exists and it's data is loaded. This can
2115 // be overridden in order to force a call to loadDropDown().
2116 // tags:
2117 // protected
2118
2119 return true;
2120 },
2121
2122 loadDropDown: function(/*Function*/ loadCallback){
2123 // summary:
2124 // Creates the drop down if it doesn't exist, loads the data
2125 // if there's an href and it hasn't been loaded yet, and then calls
2126 // the given callback.
2127 // tags:
2128 // protected
2129
2130 // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
2131 loadCallback();
2132 },
2133
2134 loadAndOpenDropDown: function(){
2135 // summary:
2136 // Creates the drop down if it doesn't exist, loads the data
2137 // if there's an href and it hasn't been loaded yet, and
2138 // then opens the drop down. This is basically a callback when the
2139 // user presses the down arrow button to open the drop down.
2140 // returns: Deferred
2141 // Deferred for the drop down widget that
2142 // fires when drop down is created and loaded
2143 // tags:
2144 // protected
2145 var d = new Deferred(),
2146 afterLoad = lang.hitch(this, function(){
2147 this.openDropDown();
2148 d.resolve(this.dropDown);
2149 });
2150 if(!this.isLoaded()){
2151 this.loadDropDown(afterLoad);
2152 }else{
2153 afterLoad();
2154 }
2155 return d;
2156 },
2157
2158 toggleDropDown: function(){
2159 // summary:
2160 // Callback when the user presses the down arrow button or presses
2161 // the down arrow key to open/close the drop down.
2162 // Toggle the drop-down widget; if it is up, close it, if not, open it
2163 // tags:
2164 // protected
2165
2166 if(this.disabled || this.readOnly){ return; }
2167 if(!this._opened){
2168 this.loadAndOpenDropDown();
2169 }else{
2170 this.closeDropDown();
2171 }
2172 },
2173
2174 openDropDown: function(){
2175 // summary:
2176 // Opens the dropdown for this widget. To be called only when this.dropDown
2177 // has been created and is ready to display (ie, it's data is loaded).
2178 // returns:
2179 // return value of dijit/popup.open()
2180 // tags:
2181 // protected
2182
2183 var dropDown = this.dropDown,
2184 ddNode = dropDown.domNode,
2185 aroundNode = this._aroundNode || this.domNode,
2186 self = this;
2187
2188 // Prepare our popup's height and honor maxHeight if it exists.
2189
2190 // TODO: isn't maxHeight dependent on the return value from dijit/popup.open(),
2191 // ie, dependent on how much space is available (BK)
2192
2193 if(!this._preparedNode){
2194 this._preparedNode = true;
2195 // Check if we have explicitly set width and height on the dropdown widget dom node
2196 if(ddNode.style.width){
2197 this._explicitDDWidth = true;
2198 }
2199 if(ddNode.style.height){
2200 this._explicitDDHeight = true;
2201 }
2202 }
2203
2204 // Code for resizing dropdown (height limitation, or increasing width to match my width)
2205 if(this.maxHeight || this.forceWidth || this.autoWidth){
2206 var myStyle = {
2207 display: "",
2208 visibility: "hidden"
2209 };
2210 if(!this._explicitDDWidth){
2211 myStyle.width = "";
2212 }
2213 if(!this._explicitDDHeight){
2214 myStyle.height = "";
2215 }
2216 domStyle.set(ddNode, myStyle);
2217
2218 // Figure out maximum height allowed (if there is a height restriction)
2219 var maxHeight = this.maxHeight;
2220 if(maxHeight == -1){
2221 // limit height to space available in viewport either above or below my domNode
2222 // (whichever side has more room)
2223 var viewport = winUtils.getBox(this.ownerDocument),
2224 position = domGeometry.position(aroundNode, false);
2225 maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
2226 }
2227
2228 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
2229 // so we call startup() and also get the size
2230 popup.moveOffScreen(dropDown);
2231
2232 if(dropDown.startup && !dropDown._started){
2233 dropDown.startup(); // this has to be done after being added to the DOM
2234 }
2235 // Get size of drop down, and determine if vertical scroll bar needed. If no scroll bar needed,
2236 // use overflow:visible rather than overflow:hidden so off-by-one errors don't hide drop down border.
2237 var mb = domGeometry.getMarginSize(ddNode);
2238 var overHeight = (maxHeight && mb.h > maxHeight);
2239 domStyle.set(ddNode, {
2240 overflowX: "visible",
2241 overflowY: overHeight ? "auto" : "visible"
2242 });
2243 if(overHeight){
2244 mb.h = maxHeight;
2245 if("w" in mb){
2246 mb.w += 16; // room for vertical scrollbar
2247 }
2248 }else{
2249 delete mb.h;
2250 }
2251
2252 // Adjust dropdown width to match or be larger than my width
2253 if(this.forceWidth){
2254 mb.w = aroundNode.offsetWidth;
2255 }else if(this.autoWidth){
2256 mb.w = Math.max(mb.w, aroundNode.offsetWidth);
2257 }else{
2258 delete mb.w;
2259 }
2260
2261 // And finally, resize the dropdown to calculated height and width
2262 if(lang.isFunction(dropDown.resize)){
2263 dropDown.resize(mb);
2264 }else{
2265 domGeometry.setMarginBox(ddNode, mb);
2266 }
2267 }
2268
2269 var retVal = popup.open({
2270 parent: this,
2271 popup: dropDown,
2272 around: aroundNode,
2273 orient: this.dropDownPosition,
2274 onExecute: function(){
2275 self.closeDropDown(true);
2276 },
2277 onCancel: function(){
2278 self.closeDropDown(true);
2279 },
2280 onClose: function(){
2281 domAttr.set(self._popupStateNode, "popupActive", false);
2282 domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
2283 self._set("_opened", false); // use set() because _CssStateMixin is watching
2284 }
2285 });
2286 domAttr.set(this._popupStateNode, "popupActive", "true");
2287 domClass.add(this._popupStateNode, "dijitHasDropDownOpen");
2288 this._set("_opened", true); // use set() because _CssStateMixin is watching
2289 this.domNode.setAttribute("aria-expanded", "true");
2290
2291 return retVal;
2292 },
2293
2294 closeDropDown: function(/*Boolean*/ focus){
2295 // summary:
2296 // Closes the drop down on this widget
2297 // focus:
2298 // If true, refocuses the button widget
2299 // tags:
2300 // protected
2301
2302 if(this._focusDropDownTimer){
2303 this._focusDropDownTimer.remove();
2304 delete this._focusDropDownTimer;
2305 }
2306 if(this._opened){
2307 this.domNode.setAttribute("aria-expanded", "false");
2308 if(focus){ this.focus(); }
2309 popup.close(this.dropDown);
2310 this._opened = false;
2311 }
2312 }
2313
2314 });
2315 });
2316
2317 },
2318 'dijit/tree/TreeStoreModel':function(){
2319 define("dijit/tree/TreeStoreModel", [
2320 "dojo/_base/array", // array.filter array.forEach array.indexOf array.some
2321 "dojo/aspect", // aspect.after
2322 "dojo/_base/declare", // declare
2323 "dojo/_base/lang" // lang.hitch
2324 ], function(array, aspect, declare, lang){
2325
2326 // module:
2327 // dijit/tree/TreeStoreModel
2328
2329 return declare("dijit.tree.TreeStoreModel", null, {
2330 // summary:
2331 // Implements dijit/Tree/model connecting to a dojo.data store with a single
2332 // root item. Any methods passed into the constructor will override
2333 // the ones defined here.
2334
2335 // store: dojo/data/api/Read
2336 // Underlying store
2337 store: null,
2338
2339 // childrenAttrs: String[]
2340 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
2341 childrenAttrs: ["children"],
2342
2343 // newItemIdAttr: String
2344 // Name of attribute in the Object passed to newItem() that specifies the id.
2345 //
2346 // If newItemIdAttr is set then it's used when newItem() is called to see if an
2347 // item with the same id already exists, and if so just links to the old item
2348 // (so that the old item ends up with two parents).
2349 //
2350 // Setting this to null or "" will make every drop create a new item.
2351 newItemIdAttr: "id",
2352
2353 // labelAttr: String
2354 // If specified, get label for tree node from this attribute, rather
2355 // than by calling store.getLabel()
2356 labelAttr: "",
2357
2358 // root: [readonly] dojo/data/Item
2359 // Pointer to the root item (read only, not a parameter)
2360 root: null,
2361
2362 // query: anything
2363 // Specifies datastore query to return the root item for the tree.
2364 // Must only return a single item. Alternately can just pass in pointer
2365 // to root item.
2366 // example:
2367 // | {id:'ROOT'}
2368 query: null,
2369
2370 // deferItemLoadingUntilExpand: Boolean
2371 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
2372 // until they are expanded. This allows for lazying loading where only one
2373 // loadItem (and generally one network call, consequently) per expansion
2374 // (rather than one for each child).
2375 // This relies on partial loading of the children items; each children item of a
2376 // fully loaded item should contain the label and info about having children.
2377 deferItemLoadingUntilExpand: false,
2378
2379 constructor: function(/* Object */ args){
2380 // summary:
2381 // Passed the arguments listed above (store, etc)
2382 // tags:
2383 // private
2384
2385 lang.mixin(this, args);
2386
2387 this.connects = [];
2388
2389 var store = this.store;
2390 if(!store.getFeatures()['dojo.data.api.Identity']){
2391 throw new Error("dijit.tree.TreeStoreModel: store must support dojo.data.Identity");
2392 }
2393
2394 // if the store supports Notification, subscribe to the notification events
2395 if(store.getFeatures()['dojo.data.api.Notification']){
2396 this.connects = this.connects.concat([
2397 aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true),
2398 aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true),
2399 aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true)
2400 ]);
2401 }
2402 },
2403
2404 destroy: function(){
2405 var h;
2406 while(h = this.connects.pop()){ h.remove(); }
2407 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
2408 },
2409
2410 // =======================================================================
2411 // Methods for traversing hierarchy
2412
2413 getRoot: function(onItem, onError){
2414 // summary:
2415 // Calls onItem with the root item for the tree, possibly a fabricated item.
2416 // Calls onError on error.
2417 if(this.root){
2418 onItem(this.root);
2419 }else{
2420 this.store.fetch({
2421 query: this.query,
2422 onComplete: lang.hitch(this, function(items){
2423 if(items.length != 1){
2424 throw new Error("dijit.tree.TreeStoreModel: root query returned " + items.length +
2425 " items, but must return exactly one");
2426 }
2427 this.root = items[0];
2428 onItem(this.root);
2429 }),
2430 onError: onError
2431 });
2432 }
2433 },
2434
2435 mayHaveChildren: function(/*dojo/data/Item*/ item){
2436 // summary:
2437 // Tells if an item has or may have children. Implementing logic here
2438 // avoids showing +/- expando icon for nodes that we know don't have children.
2439 // (For efficiency reasons we may not want to check if an element actually
2440 // has children until user clicks the expando node)
2441 return array.some(this.childrenAttrs, function(attr){
2442 return this.store.hasAttribute(item, attr);
2443 }, this);
2444 },
2445
2446 getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
2447 // summary:
2448 // Calls onComplete() with array of child items of given parent item, all loaded.
2449
2450 var store = this.store;
2451 if(!store.isItemLoaded(parentItem)){
2452 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
2453 // mode, so we will load it and just return the children (without loading each
2454 // child item)
2455 var getChildren = lang.hitch(this, arguments.callee);
2456 store.loadItem({
2457 item: parentItem,
2458 onItem: function(parentItem){
2459 getChildren(parentItem, onComplete, onError);
2460 },
2461 onError: onError
2462 });
2463 return;
2464 }
2465 // get children of specified item
2466 var childItems = [];
2467 for(var i=0; i<this.childrenAttrs.length; i++){
2468 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
2469 childItems = childItems.concat(vals);
2470 }
2471
2472 // count how many items need to be loaded
2473 var _waitCount = 0;
2474 if(!this.deferItemLoadingUntilExpand){
2475 array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
2476 }
2477
2478 if(_waitCount == 0){
2479 // all items are already loaded (or we aren't loading them). proceed...
2480 onComplete(childItems);
2481 }else{
2482 // still waiting for some or all of the items to load
2483 array.forEach(childItems, function(item, idx){
2484 if(!store.isItemLoaded(item)){
2485 store.loadItem({
2486 item: item,
2487 onItem: function(item){
2488 childItems[idx] = item;
2489 if(--_waitCount == 0){
2490 // all nodes have been loaded, send them to the tree
2491 onComplete(childItems);
2492 }
2493 },
2494 onError: onError
2495 });
2496 }
2497 });
2498 }
2499 },
2500
2501 // =======================================================================
2502 // Inspecting items
2503
2504 isItem: function(/* anything */ something){
2505 return this.store.isItem(something); // Boolean
2506 },
2507
2508 fetchItemByIdentity: function(/* object */ keywordArgs){
2509 this.store.fetchItemByIdentity(keywordArgs);
2510 },
2511
2512 getIdentity: function(/* item */ item){
2513 return this.store.getIdentity(item); // Object
2514 },
2515
2516 getLabel: function(/*dojo/data/Item*/ item){
2517 // summary:
2518 // Get the label for an item
2519 if(this.labelAttr){
2520 return this.store.getValue(item,this.labelAttr); // String
2521 }else{
2522 return this.store.getLabel(item); // String
2523 }
2524 },
2525
2526 // =======================================================================
2527 // Write interface
2528
2529 newItem: function(/* dijit/tree/dndSource.__Item */ args, /*dojo/data/api/Item*/ parent, /*int?*/ insertIndex){
2530 // summary:
2531 // Creates a new item. See `dojo/data/api/Write` for details on args.
2532 // Used in drag & drop when item from external source dropped onto tree.
2533 // description:
2534 // Developers will need to override this method if new items get added
2535 // to parents with multiple children attributes, in order to define which
2536 // children attribute points to the new item.
2537
2538 var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
2539
2540 if(this.newItemIdAttr && args[this.newItemIdAttr]){
2541 // Maybe there's already a corresponding item in the store; if so, reuse it.
2542 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
2543 if(item){
2544 // There's already a matching item in store, use it
2545 this.pasteItem(item, null, parent, true, insertIndex);
2546 }else{
2547 // Create new item in the tree, based on the drag source.
2548 LnewItem=this.store.newItem(args, pInfo);
2549 if(LnewItem && (insertIndex!=undefined)){
2550 // Move new item to desired position
2551 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
2552 }
2553 }
2554 }});
2555 }else{
2556 // [as far as we know] there is no id so we must assume this is a new item
2557 LnewItem=this.store.newItem(args, pInfo);
2558 if(LnewItem && (insertIndex!=undefined)){
2559 // Move new item to desired position
2560 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
2561 }
2562 }
2563 },
2564
2565 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
2566 // summary:
2567 // Move or copy an item from one parent item to another.
2568 // Used in drag & drop
2569 var store = this.store,
2570 parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
2571
2572 // remove child from source item, and record the attribute that child occurred in
2573 if(oldParentItem){
2574 array.forEach(this.childrenAttrs, function(attr){
2575 if(store.containsValue(oldParentItem, attr, childItem)){
2576 if(!bCopy){
2577 var values = array.filter(store.getValues(oldParentItem, attr), function(x){
2578 return x != childItem;
2579 });
2580 store.setValues(oldParentItem, attr, values);
2581 }
2582 parentAttr = attr;
2583 }
2584 });
2585 }
2586
2587 // modify target item's children attribute to include this item
2588 if(newParentItem){
2589 if(typeof insertIndex == "number"){
2590 // call slice() to avoid modifying the original array, confusing the data store
2591 var childItems = store.getValues(newParentItem, parentAttr).slice();
2592 childItems.splice(insertIndex, 0, childItem);
2593 store.setValues(newParentItem, parentAttr, childItems);
2594 }else{
2595 store.setValues(newParentItem, parentAttr,
2596 store.getValues(newParentItem, parentAttr).concat(childItem));
2597 }
2598 }
2599 },
2600
2601 // =======================================================================
2602 // Callbacks
2603
2604 onChange: function(/*dojo/data/Item*/ /*===== item =====*/){
2605 // summary:
2606 // Callback whenever an item has changed, so that Tree
2607 // can update the label, icon, etc. Note that changes
2608 // to an item's children or parent(s) will trigger an
2609 // onChildrenChange() so you can ignore those changes here.
2610 // tags:
2611 // callback
2612 },
2613
2614 onChildrenChange: function(/*===== parent, newChildrenList =====*/){
2615 // summary:
2616 // Callback to do notifications about new, updated, or deleted items.
2617 // parent: dojo/data/Item
2618 // newChildrenList: dojo/data/Item[]
2619 // tags:
2620 // callback
2621 },
2622
2623 onDelete: function(/*dojo/data/Item*/ /*===== item =====*/){
2624 // summary:
2625 // Callback when an item has been deleted.
2626 // description:
2627 // Note that there will also be an onChildrenChange() callback for the parent
2628 // of this item.
2629 // tags:
2630 // callback
2631 },
2632
2633 // =======================================================================
2634 // Events from data store
2635
2636 onNewItem: function(/* dojo/data/Item */ item, /* Object */ parentInfo){
2637 // summary:
2638 // Handler for when new items appear in the store, either from a drop operation
2639 // or some other way. Updates the tree view (if necessary).
2640 // description:
2641 // If the new item is a child of an existing item,
2642 // calls onChildrenChange() with the new list of children
2643 // for that existing item.
2644 //
2645 // tags:
2646 // extension
2647
2648 // We only care about the new item if it has a parent that corresponds to a TreeNode
2649 // we are currently displaying
2650 if(!parentInfo){
2651 return;
2652 }
2653
2654 // Call onChildrenChange() on parent (ie, existing) item with new list of children
2655 // In the common case, the new list of children is simply parentInfo.newValue or
2656 // [ parentInfo.newValue ], although if items in the store has multiple
2657 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
2658 // so call getChildren() to be sure to get right answer.
2659 this.getChildren(parentInfo.item, lang.hitch(this, function(children){
2660 this.onChildrenChange(parentInfo.item, children);
2661 }));
2662 },
2663
2664 onDeleteItem: function(/*Object*/ item){
2665 // summary:
2666 // Handler for delete notifications from underlying store
2667 this.onDelete(item);
2668 },
2669
2670 onSetItem: function(item, attribute /*===== , oldValue, newValue =====*/){
2671 // summary:
2672 // Updates the tree view according to changes in the data store.
2673 // description:
2674 // Handles updates to an item's children by calling onChildrenChange(), and
2675 // other updates to an item by calling onChange().
2676 //
2677 // See `onNewItem` for more details on handling updates to an item's children.
2678 // item: Item
2679 // attribute: attribute-name-string
2680 // oldValue: Object|Array
2681 // newValue: Object|Array
2682 // tags:
2683 // extension
2684
2685 if(array.indexOf(this.childrenAttrs, attribute) != -1){
2686 // item's children list changed
2687 this.getChildren(item, lang.hitch(this, function(children){
2688 // See comments in onNewItem() about calling getChildren()
2689 this.onChildrenChange(item, children);
2690 }));
2691 }else{
2692 // item's label/icon/etc. changed.
2693 this.onChange(item);
2694 }
2695 }
2696 });
2697 });
2698
2699 },
2700 'dijit/_MenuBase':function(){
2701 define("dijit/_MenuBase", [
2702 "dojo/_base/array", // array.indexOf
2703 "dojo/_base/declare", // declare
2704 "dojo/dom", // dom.isDescendant domClass.replace
2705 "dojo/dom-attr",
2706 "dojo/dom-class", // domClass.replace
2707 "dojo/_base/lang", // lang.hitch
2708 "dojo/mouse", // mouse.enter, mouse.leave
2709 "dojo/on",
2710 "dojo/window",
2711 "./a11yclick",
2712 "./popup",
2713 "./registry",
2714 "./_Widget",
2715 "./_KeyNavContainer",
2716 "./_TemplatedMixin"
2717 ], function(array, declare, dom, domAttr, domClass, lang, mouse, on, winUtils,
2718 a11yclick, pm, registry, _Widget, _KeyNavContainer, _TemplatedMixin){
2719
2720
2721 // module:
2722 // dijit/_MenuBase
2723
2724 return declare("dijit._MenuBase",
2725 [_Widget, _TemplatedMixin, _KeyNavContainer],
2726 {
2727 // summary:
2728 // Base class for Menu and MenuBar
2729
2730 // parentMenu: [readonly] Widget
2731 // pointer to menu that displayed me
2732 parentMenu: null,
2733
2734 // popupDelay: Integer
2735 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
2736 popupDelay: 500,
2737
2738 // autoFocus: Boolean
2739 // A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
2740 // or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
2741 autoFocus: false,
2742
2743 childSelector: function(/*DOMNode*/ node){
2744 // summary:
2745 // Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
2746 // like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
2747 // tags:
2748 // protected
2749
2750 var widget = registry.byNode(node);
2751 return node.parentNode == this.containerNode && widget && widget.focus;
2752 },
2753
2754 postCreate: function(){
2755 var self = this,
2756 matches = typeof this.childSelector == "string" ? this.childSelector : lang.hitch(this, "childSelector");
2757 this.own(
2758 on(this.containerNode, on.selector(matches, mouse.enter), function(){
2759 self.onItemHover(registry.byNode(this));
2760 }),
2761 on(this.containerNode, on.selector(matches, mouse.leave), function(){
2762 self.onItemUnhover(registry.byNode(this));
2763 }),
2764 on(this.containerNode, on.selector(matches, a11yclick), function(evt){
2765 self.onItemClick(registry.byNode(this), evt);
2766 evt.stopPropagation();
2767 evt.preventDefault();
2768 })
2769 );
2770 this.inherited(arguments);
2771 },
2772
2773 onExecute: function(){
2774 // summary:
2775 // Attach point for notification about when a menu item has been executed.
2776 // This is an internal mechanism used for Menus to signal to their parent to
2777 // close them, because they are about to execute the onClick handler. In
2778 // general developers should not attach to or override this method.
2779 // tags:
2780 // protected
2781 },
2782
2783 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
2784 // summary:
2785 // Attach point for notification about when the user cancels the current menu
2786 // This is an internal mechanism used for Menus to signal to their parent to
2787 // close them. In general developers should not attach to or override this method.
2788 // tags:
2789 // protected
2790 },
2791
2792 _moveToPopup: function(/*Event*/ evt){
2793 // summary:
2794 // This handles the right arrow key (left arrow key on RTL systems),
2795 // which will either open a submenu, or move to the next item in the
2796 // ancestor MenuBar
2797 // tags:
2798 // private
2799
2800 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
2801 this.onItemClick(this.focusedChild, evt);
2802 }else{
2803 var topMenu = this._getTopMenu();
2804 if(topMenu && topMenu._isMenuBar){
2805 topMenu.focusNext();
2806 }
2807 }
2808 },
2809
2810 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
2811 // summary:
2812 // This handler is called when the mouse moves over the popup.
2813 // tags:
2814 // private
2815
2816 // if the mouse hovers over a menu popup that is in pending-close state,
2817 // then stop the close operation.
2818 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
2819 if(this.currentPopup && this.currentPopup._pendingClose_timer){
2820 var parentMenu = this.currentPopup.parentMenu;
2821 // highlight the parent menu item pointing to this popup
2822 if(parentMenu.focusedChild){
2823 parentMenu.focusedChild._setSelected(false);
2824 }
2825 parentMenu.focusedChild = this.currentPopup.from_item;
2826 parentMenu.focusedChild._setSelected(true);
2827 // cancel the pending close
2828 this._stopPendingCloseTimer(this.currentPopup);
2829 }
2830 },
2831
2832 onItemHover: function(/*MenuItem*/ item){
2833 // summary:
2834 // Called when cursor is over a MenuItem.
2835 // tags:
2836 // protected
2837
2838 // Don't do anything unless user has "activated" the menu by:
2839 // 1) clicking it
2840 // 2) opening it from a parent menu (which automatically focuses it)
2841 if(this.isActive){
2842 this.focusChild(item);
2843 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
2844 this.hover_timer = this.defer("_openPopup", this.popupDelay);
2845 }
2846 }
2847 // if the user is mixing mouse and keyboard navigation,
2848 // then the menu may not be active but a menu item has focus,
2849 // but it's not the item that the mouse just hovered over.
2850 // To avoid both keyboard and mouse selections, use the latest.
2851 if(this.focusedChild){
2852 this.focusChild(item);
2853 }
2854 this._hoveredChild = item;
2855
2856 item._set("hovering", true);
2857 },
2858
2859 _onChildBlur: function(item){
2860 // summary:
2861 // Called when a child MenuItem becomes inactive because focus
2862 // has been removed from the MenuItem *and* it's descendant menus.
2863 // tags:
2864 // private
2865 this._stopPopupTimer();
2866 item._setSelected(false);
2867 // Close all popups that are open and descendants of this menu
2868 var itemPopup = item.popup;
2869 if(itemPopup){
2870 this._stopPendingCloseTimer(itemPopup);
2871 itemPopup._pendingClose_timer = this.defer(function(){
2872 itemPopup._pendingClose_timer = null;
2873 if(itemPopup.parentMenu){
2874 itemPopup.parentMenu.currentPopup = null;
2875 }
2876 pm.close(itemPopup); // this calls onClose
2877 }, this.popupDelay);
2878 }
2879 },
2880
2881 onItemUnhover: function(/*MenuItem*/ item){
2882 // summary:
2883 // Callback fires when mouse exits a MenuItem
2884 // tags:
2885 // protected
2886
2887 if(this.isActive){
2888 this._stopPopupTimer();
2889 }
2890 if(this._hoveredChild == item){ this._hoveredChild = null; }
2891
2892 item._set("hovering", false);
2893 },
2894
2895 _stopPopupTimer: function(){
2896 // summary:
2897 // Cancels the popup timer because the user has stop hovering
2898 // on the MenuItem, etc.
2899 // tags:
2900 // private
2901 if(this.hover_timer){
2902 this.hover_timer = this.hover_timer.remove();
2903 }
2904 },
2905
2906 _stopPendingCloseTimer: function(/*dijit/_WidgetBase*/ popup){
2907 // summary:
2908 // Cancels the pending-close timer because the close has been preempted
2909 // tags:
2910 // private
2911 if(popup._pendingClose_timer){
2912 popup._pendingClose_timer = popup._pendingClose_timer.remove();
2913 }
2914 },
2915
2916 _stopFocusTimer: function(){
2917 // summary:
2918 // Cancels the pending-focus timer because the menu was closed before focus occured
2919 // tags:
2920 // private
2921 if(this._focus_timer){
2922 this._focus_timer = this._focus_timer.remove();
2923 }
2924 },
2925
2926 _getTopMenu: function(){
2927 // summary:
2928 // Returns the top menu in this chain of Menus
2929 // tags:
2930 // private
2931 for(var top=this; top.parentMenu; top=top.parentMenu);
2932 return top;
2933 },
2934
2935 onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
2936 // summary:
2937 // Handle clicks on an item.
2938 // tags:
2939 // private
2940
2941 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
2942 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
2943 this._markActive();
2944 }
2945
2946 this.focusChild(item);
2947
2948 if(item.disabled){ return false; }
2949
2950 if(item.popup){
2951 this._openPopup(evt.type == "keypress");
2952 }else{
2953 // before calling user defined handler, close hierarchy of menus
2954 // and restore focus to place it was when menu was opened
2955 this.onExecute();
2956
2957 // user defined handler for click
2958 item._onClick ? item._onClick(evt) : item.onClick(evt);
2959 }
2960 },
2961
2962 _openPopup: function(/*Boolean*/ focus){
2963 // summary:
2964 // Open the popup to the side of/underneath the current menu item, and optionally focus first item
2965 // tags:
2966 // protected
2967
2968 this._stopPopupTimer();
2969 var from_item = this.focusedChild;
2970 if(!from_item){ return; } // the focused child lost focus since the timer was started
2971 var popup = from_item.popup;
2972 if(!popup.isShowingNow){
2973 if(this.currentPopup){
2974 this._stopPendingCloseTimer(this.currentPopup);
2975 pm.close(this.currentPopup);
2976 }
2977 popup.parentMenu = this;
2978 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
2979 var self = this;
2980 pm.open({
2981 parent: this,
2982 popup: popup,
2983 around: from_item.domNode,
2984 orient: this._orient || ["after", "before"],
2985 onCancel: function(){ // called when the child menu is canceled
2986 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
2987 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
2988 self.focusChild(from_item); // put focus back on my node
2989 self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
2990 from_item._setSelected(true); // oops, _cleanUp() deselected the item
2991 self.focusedChild = from_item; // and unset focusedChild
2992 },
2993 onExecute: lang.hitch(this, "_cleanUp")
2994 });
2995
2996 this.currentPopup = popup;
2997 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
2998 popup.connect(popup.domNode, "onmouseenter", lang.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
2999 }
3000
3001 if(focus && popup.focus){
3002 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar), then focus the popup.
3003 // If the cursor happens to collide with the popup, it will generate an onmouseover event
3004 // even though the mouse wasn't moved. Use defer() to call popup.focus so that
3005 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
3006 popup._focus_timer = this.defer(lang.hitch(popup, function(){
3007 this._focus_timer = null;
3008 this.focus();
3009 }));
3010 }
3011 },
3012
3013 _markActive: function(){
3014 // summary:
3015 // Mark this menu's state as active.
3016 // Called when this Menu gets focus from:
3017 //
3018 // 1. clicking it (mouse or via space/arrow key)
3019 // 2. being opened by a parent menu.
3020 //
3021 // This is not called just from mouse hover.
3022 // Focusing a menu via TAB does NOT automatically set isActive
3023 // since TAB is a navigation operation and not a selection one.
3024 // For Windows apps, pressing the ALT key focuses the menubar
3025 // menus (similar to TAB navigation) but the menu is not active
3026 // (ie no dropdown) until an item is clicked.
3027 this.isActive = true;
3028 domClass.replace(this.domNode, "dijitMenuActive", "dijitMenuPassive");
3029 },
3030
3031 onOpen: function(/*Event*/ /*===== e =====*/){
3032 // summary:
3033 // Callback when this menu is opened.
3034 // This is called by the popup manager as notification that the menu
3035 // was opened.
3036 // tags:
3037 // private
3038
3039 this.isShowingNow = true;
3040 this._markActive();
3041 },
3042
3043 _markInactive: function(){
3044 // summary:
3045 // Mark this menu's state as inactive.
3046 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
3047 domClass.replace(this.domNode, "dijitMenuPassive", "dijitMenuActive");
3048 },
3049
3050 onClose: function(){
3051 // summary:
3052 // Callback when this menu is closed.
3053 // This is called by the popup manager as notification that the menu
3054 // was closed.
3055 // tags:
3056 // private
3057
3058 this._stopFocusTimer();
3059 this._markInactive();
3060 this.isShowingNow = false;
3061 this.parentMenu = null;
3062 },
3063
3064 _closeChild: function(){
3065 // summary:
3066 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
3067 // tags:
3068 // private
3069 this._stopPopupTimer();
3070
3071 if(this.currentPopup){
3072 // If focus is on a descendant MenuItem then move focus to me,
3073 // because IE doesn't like it when you display:none a node with focus,
3074 // and also so keyboard users don't lose control.
3075 // Likely, immediately after a user defined onClick handler will move focus somewhere
3076 // else, like a Dialog.
3077 if(array.indexOf(this._focusManager.activeStack, this.id) >= 0){
3078 domAttr.set(this.focusedChild.focusNode, "tabIndex", this.tabIndex);
3079 this.focusedChild.focusNode.focus();
3080 }
3081 // Close all popups that are open and descendants of this menu
3082 pm.close(this.currentPopup);
3083 this.currentPopup = null;
3084 }
3085
3086 if(this.focusedChild){ // unhighlight the focused item
3087 this.focusedChild._setSelected(false);
3088 this.onItemUnhover(this.focusedChild);
3089 this.focusedChild = null;
3090 }
3091 },
3092
3093 _onItemFocus: function(/*MenuItem*/ item){
3094 // summary:
3095 // Called when child of this Menu gets focus from:
3096 //
3097 // 1. clicking it
3098 // 2. tabbing into it
3099 // 3. being opened by a parent menu.
3100 //
3101 // This is not called just from mouse hover.
3102 if(this._hoveredChild && this._hoveredChild != item){
3103 this.onItemUnhover(this._hoveredChild); // any previous mouse movement is trumped by focus selection
3104 }
3105 },
3106
3107 _onBlur: function(){
3108 // summary:
3109 // Called when focus is moved away from this Menu and it's submenus.
3110 // tags:
3111 // protected
3112 this._cleanUp();
3113 this.inherited(arguments);
3114 },
3115
3116 _cleanUp: function(){
3117 // summary:
3118 // Called when the user is done with this menu. Closes hierarchy of menus.
3119 // tags:
3120 // private
3121
3122 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
3123 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
3124 this._markInactive();
3125 }
3126 }
3127 });
3128
3129 });
3130
3131 },
3132 'dijit/focus':function(){
3133 define("dijit/focus", [
3134 "dojo/aspect",
3135 "dojo/_base/declare", // declare
3136 "dojo/dom", // domAttr.get dom.isDescendant
3137 "dojo/dom-attr", // domAttr.get dom.isDescendant
3138 "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
3139 "dojo/Evented",
3140 "dojo/_base/lang", // lang.hitch
3141 "dojo/on",
3142 "dojo/ready",
3143 "dojo/sniff", // has("ie")
3144 "dojo/Stateful",
3145 "dojo/_base/unload", // unload.addOnWindowUnload
3146 "dojo/_base/window", // win.body
3147 "dojo/window", // winUtils.get
3148 "./a11y", // a11y.isTabNavigable
3149 "./registry", // registry.byId
3150 "./main" // to set dijit.focus
3151 ], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils,
3152 a11y, registry, dijit){
3153
3154 // module:
3155 // dijit/focus
3156
3157 var FocusManager = declare([Stateful, Evented], {
3158 // summary:
3159 // Tracks the currently focused node, and which widgets are currently "active".
3160 // Access via require(["dijit/focus"], function(focus){ ... }).
3161 //
3162 // A widget is considered active if it or a descendant widget has focus,
3163 // or if a non-focusable node of this widget or a descendant was recently clicked.
3164 //
3165 // Call focus.watch("curNode", callback) to track the current focused DOMNode,
3166 // or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
3167 //
3168 // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
3169 // when widgets become active/inactive
3170 //
3171 // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
3172
3173 // curNode: DomNode
3174 // Currently focused item on screen
3175 curNode: null,
3176
3177 // activeStack: dijit/_WidgetBase[]
3178 // List of currently active widgets (focused widget and it's ancestors)
3179 activeStack: [],
3180
3181 constructor: function(){
3182 // Don't leave curNode/prevNode pointing to bogus elements
3183 var check = lang.hitch(this, function(node){
3184 if(dom.isDescendant(this.curNode, node)){
3185 this.set("curNode", null);
3186 }
3187 if(dom.isDescendant(this.prevNode, node)){
3188 this.set("prevNode", null);
3189 }
3190 });
3191 aspect.before(domConstruct, "empty", check);
3192 aspect.before(domConstruct, "destroy", check);
3193 },
3194
3195 registerIframe: function(/*DomNode*/ iframe){
3196 // summary:
3197 // Registers listeners on the specified iframe so that any click
3198 // or focus event on that iframe (or anything in it) is reported
3199 // as a focus/click event on the `<iframe>` itself.
3200 // description:
3201 // Currently only used by editor.
3202 // returns:
3203 // Handle with remove() method to deregister.
3204 return this.registerWin(iframe.contentWindow, iframe);
3205 },
3206
3207 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
3208 // summary:
3209 // Registers listeners on the specified window (either the main
3210 // window or an iframe's window) to detect when the user has clicked somewhere
3211 // or focused somewhere.
3212 // description:
3213 // Users should call registerIframe() instead of this method.
3214 // targetWindow:
3215 // If specified this is the window associated with the iframe,
3216 // i.e. iframe.contentWindow.
3217 // effectiveNode:
3218 // If specified, report any focus events inside targetWindow as
3219 // an event on effectiveNode, rather than on evt.target.
3220 // returns:
3221 // Handle with remove() method to deregister.
3222
3223 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
3224
3225 var _this = this;
3226 var mousedownListener = function(evt){
3227 _this._justMouseDowned = true;
3228 setTimeout(function(){ _this._justMouseDowned = false; }, 0);
3229
3230 // workaround weird IE bug where the click is on an orphaned node
3231 // (first time clicking a Select/DropDownButton inside a TooltipDialog)
3232 if(has("ie") && evt && evt.srcElement && evt.srcElement.parentNode == null){
3233 return;
3234 }
3235
3236 _this._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
3237 };
3238
3239 // Listen for blur and focus events on targetWindow's document.
3240 // Using attachEvent()/addEventListener() rather than on() to try to catch mouseDown events even
3241 // if other code calls evt.stopPropagation(). But rethink for 2.0 since that doesn't work for attachEvent(),
3242 // which watches events at the bubbling phase rather than capturing phase, like addEventListener(..., false).
3243 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
3244 // (at least for FF) the focus event doesn't fire on <html> or <body>.
3245 var doc = has("ie") ? targetWindow.document.documentElement : targetWindow.document;
3246 if(doc){
3247 if(has("ie")){
3248 targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
3249 var focusinListener = function(evt){
3250 // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
3251 // ignore those events
3252 var tag = evt.srcElement.tagName.toLowerCase();
3253 if(tag == "#document" || tag == "body"){ return; }
3254
3255 // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
3256 // probably just ignore such an event as it will be handled by onmousedown handler above, but
3257 // leaving the code for now.
3258 if(a11y.isTabNavigable(evt.srcElement)){
3259 _this._onFocusNode(effectiveNode || evt.srcElement);
3260 }else{
3261 _this._onTouchNode(effectiveNode || evt.srcElement);
3262 }
3263 };
3264 doc.attachEvent('onfocusin', focusinListener);
3265 var focusoutListener = function(evt){
3266 _this._onBlurNode(effectiveNode || evt.srcElement);
3267 };
3268 doc.attachEvent('onfocusout', focusoutListener);
3269
3270 return {
3271 remove: function(){
3272 targetWindow.document.detachEvent('onmousedown', mousedownListener);
3273 doc.detachEvent('onfocusin', focusinListener);
3274 doc.detachEvent('onfocusout', focusoutListener);
3275 doc = null; // prevent memory leak (apparent circular reference via closure)
3276 }
3277 };
3278 }else{
3279 doc.body.addEventListener('mousedown', mousedownListener, true);
3280 doc.body.addEventListener('touchstart', mousedownListener, true);
3281 var focusListener = function(evt){
3282 _this._onFocusNode(effectiveNode || evt.target);
3283 };
3284 doc.addEventListener('focus', focusListener, true);
3285 var blurListener = function(evt){
3286 _this._onBlurNode(effectiveNode || evt.target);
3287 };
3288 doc.addEventListener('blur', blurListener, true);
3289
3290 return {
3291 remove: function(){
3292 doc.body.removeEventListener('mousedown', mousedownListener, true);
3293 doc.body.removeEventListener('touchstart', mousedownListener, true);
3294 doc.removeEventListener('focus', focusListener, true);
3295 doc.removeEventListener('blur', blurListener, true);
3296 doc = null; // prevent memory leak (apparent circular reference via closure)
3297 }
3298 };
3299 }
3300 }
3301 },
3302
3303 _onBlurNode: function(/*DomNode*/ node){
3304 // summary:
3305 // Called when focus leaves a node.
3306 // Usually ignored, _unless_ it *isn't* followed by touching another node,
3307 // which indicates that we tabbed off the last field on the page,
3308 // in which case every widget is marked inactive
3309
3310 // If the blur event isn't followed by a focus event, it means the user clicked on something unfocusable,
3311 // so clear focus.
3312 if(this._clearFocusTimer){
3313 clearTimeout(this._clearFocusTimer);
3314 }
3315 this._clearFocusTimer = setTimeout(lang.hitch(this, function(){
3316 this.set("prevNode", this.curNode);
3317 this.set("curNode", null);
3318 }), 0);
3319
3320 if(this._justMouseDowned){
3321 // the mouse down caused a new widget to be marked as active; this blur event
3322 // is coming late, so ignore it.
3323 return;
3324 }
3325
3326 // If the blur event isn't followed by a focus or touch event then mark all widgets as inactive.
3327 if(this._clearActiveWidgetsTimer){
3328 clearTimeout(this._clearActiveWidgetsTimer);
3329 }
3330 this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){
3331 delete this._clearActiveWidgetsTimer;
3332 this._setStack([]);
3333 }), 0);
3334 },
3335
3336 _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
3337 // summary:
3338 // Callback when node is focused or mouse-downed
3339 // node:
3340 // The node that was touched.
3341 // by:
3342 // "mouse" if the focus/touch was caused by a mouse down event
3343
3344 // ignore the recent blurNode event
3345 if(this._clearActiveWidgetsTimer){
3346 clearTimeout(this._clearActiveWidgetsTimer);
3347 delete this._clearActiveWidgetsTimer;
3348 }
3349
3350 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
3351 var newStack=[];
3352 try{
3353 while(node){
3354 var popupParent = domAttr.get(node, "dijitPopupParent");
3355 if(popupParent){
3356 node=registry.byId(popupParent).domNode;
3357 }else if(node.tagName && node.tagName.toLowerCase() == "body"){
3358 // is this the root of the document or just the root of an iframe?
3359 if(node === win.body()){
3360 // node is the root of the main document
3361 break;
3362 }
3363 // otherwise, find the iframe this node refers to (can't access it via parentNode,
3364 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
3365 node=winUtils.get(node.ownerDocument).frameElement;
3366 }else{
3367 // if this node is the root node of a widget, then add widget id to stack,
3368 // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
3369 // to support MenuItem)
3370 var id = node.getAttribute && node.getAttribute("widgetId"),
3371 widget = id && registry.byId(id);
3372 if(widget && !(by == "mouse" && widget.get("disabled"))){
3373 newStack.unshift(id);
3374 }
3375 node=node.parentNode;
3376 }
3377 }
3378 }catch(e){ /* squelch */ }
3379
3380 this._setStack(newStack, by);
3381 },
3382
3383 _onFocusNode: function(/*DomNode*/ node){
3384 // summary:
3385 // Callback when node is focused
3386
3387 if(!node){
3388 return;
3389 }
3390
3391 if(node.nodeType == 9){
3392 // Ignore focus events on the document itself. This is here so that
3393 // (for example) clicking the up/down arrows of a spinner
3394 // (which don't get focus) won't cause that widget to blur. (FF issue)
3395 return;
3396 }
3397
3398 // There was probably a blur event right before this event, but since we have a new focus, don't
3399 // do anything with the blur
3400 if(this._clearFocusTimer){
3401 clearTimeout(this._clearFocusTimer);
3402 delete this._clearFocusTimer;
3403 }
3404
3405 this._onTouchNode(node);
3406
3407 if(node == this.curNode){ return; }
3408 this.set("prevNode", this.curNode);
3409 this.set("curNode", node);
3410 },
3411
3412 _setStack: function(/*String[]*/ newStack, /*String*/ by){
3413 // summary:
3414 // The stack of active widgets has changed. Send out appropriate events and records new stack.
3415 // newStack:
3416 // array of widget id's, starting from the top (outermost) widget
3417 // by:
3418 // "mouse" if the focus/touch was caused by a mouse down event
3419
3420 var oldStack = this.activeStack;
3421 this.set("activeStack", newStack);
3422
3423 // compare old stack to new stack to see how many elements they have in common
3424 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
3425 if(oldStack[nCommon] != newStack[nCommon]){
3426 break;
3427 }
3428 }
3429
3430 var widget;
3431 // for all elements that have gone out of focus, set focused=false
3432 for(var i=oldStack.length-1; i>=nCommon; i--){
3433 widget = registry.byId(oldStack[i]);
3434 if(widget){
3435 widget._hasBeenBlurred = true; // TODO: used by form widgets, should be moved there
3436 widget.set("focused", false);
3437 if(widget._focusManager == this){
3438 widget._onBlur(by);
3439 }
3440 this.emit("widget-blur", widget, by);
3441 }
3442 }
3443
3444 // for all element that have come into focus, set focused=true
3445 for(i=nCommon; i<newStack.length; i++){
3446 widget = registry.byId(newStack[i]);
3447 if(widget){
3448 widget.set("focused", true);
3449 if(widget._focusManager == this){
3450 widget._onFocus(by);
3451 }
3452 this.emit("widget-focus", widget, by);
3453 }
3454 }
3455 },
3456
3457 focus: function(node){
3458 // summary:
3459 // Focus the specified node, suppressing errors if they occur
3460 if(node){
3461 try{ node.focus(); }catch(e){/*quiet*/}
3462 }
3463 }
3464 });
3465
3466 var singleton = new FocusManager();
3467
3468 // register top window and all the iframes it contains
3469 ready(function(){
3470 var handle = singleton.registerWin(winUtils.get(win.doc));
3471 if(has("ie")){
3472 unload.addOnWindowUnload(function(){
3473 if(handle){ // because this gets called twice when doh.robot is running
3474 handle.remove();
3475 handle = null;
3476 }
3477 });
3478 }
3479 });
3480
3481 // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
3482 // as a function to set focus. Remove for 2.0.
3483 dijit.focus = function(node){
3484 singleton.focus(node); // indirection here allows dijit/_base/focus.js to override behavior
3485 };
3486 for(var attr in singleton){
3487 if(!/^_/.test(attr)){
3488 dijit.focus[attr] = typeof singleton[attr] == "function" ? lang.hitch(singleton, attr) : singleton[attr];
3489 }
3490 }
3491 singleton.watch(function(attr, oldVal, newVal){
3492 dijit.focus[attr] = newVal;
3493 });
3494
3495 return singleton;
3496 });
3497
3498 },
3499 'dojo/i18n':function(){
3500 define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json", "module"],
3501 function(dojo, require, has, array, config, lang, xhr, json, module){
3502
3503 // module:
3504 // dojo/i18n
3505
3506 has.add("dojo-preload-i18n-Api",
3507 // if true, define the preload localizations machinery
3508 1
3509 );
3510
3511 1 || has.add("dojo-v1x-i18n-Api",
3512 // if true, define the v1.x i18n functions
3513 1
3514 );
3515
3516 var
3517 thisModule = dojo.i18n =
3518 {
3519 // summary:
3520 // This module implements the dojo/i18n! plugin and the v1.6- i18n API
3521 // description:
3522 // We choose to include our own plugin to leverage functionality already contained in dojo
3523 // and thereby reduce the size of the plugin compared to various loader implementations. Also, this
3524 // allows foreign AMD loaders to be used without their plugins.
3525 },
3526
3527 nlsRe =
3528 // regexp for reconstructing the master bundle name from parts of the regexp match
3529 // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
3530 // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
3531 // nlsRe.exec("foo/bar/baz/nls/foo") gives:
3532 // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
3533 // so, if match[5] is blank, it means this is the top bundle definition.
3534 // courtesy of http://requirejs.org
3535 /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
3536
3537 getAvailableLocales = function(
3538 root,
3539 locale,
3540 bundlePath,
3541 bundleName
3542 ){
3543 // summary:
3544 // return a vector of module ids containing all available locales with respect to the target locale
3545 // For example, assuming:
3546 //
3547 // - the root bundle indicates specific bundles for "fr" and "fr-ca",
3548 // - bundlePath is "myPackage/nls"
3549 // - bundleName is "myBundle"
3550 //
3551 // Then a locale argument of "fr-ca" would return
3552 //
3553 // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
3554 //
3555 // Notice that bundles are returned least-specific to most-specific, starting with the root.
3556 //
3557 // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
3558 // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
3559
3560 for(var result = [bundlePath + bundleName], localeParts = locale.split("-"), current = "", i = 0; i<localeParts.length; i++){
3561 current += (current ? "-" : "") + localeParts[i];
3562 if(!root || root[current]){
3563 result.push(bundlePath + current + "/" + bundleName);
3564 }
3565 }
3566 return result;
3567 },
3568
3569 cache = {},
3570
3571 getBundleName = function(moduleName, bundleName, locale){
3572 locale = locale ? locale.toLowerCase() : dojo.locale;
3573 moduleName = moduleName.replace(/\./g, "/");
3574 bundleName = bundleName.replace(/\./g, "/");
3575 return (/root/i.test(locale)) ?
3576 (moduleName + "/nls/" + bundleName) :
3577 (moduleName + "/nls/" + locale + "/" + bundleName);
3578 },
3579
3580 getL10nName = dojo.getL10nName = function(moduleName, bundleName, locale){
3581 return moduleName = module.id + "!" + getBundleName(moduleName, bundleName, locale);
3582 },
3583
3584 doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){
3585 // summary:
3586 // get the root bundle which instructs which other bundles are required to construct the localized bundle
3587 require([bundlePathAndName], function(root){
3588 var current = lang.clone(root.root),
3589 availableLocales = getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName);
3590 require(availableLocales, function(){
3591 for (var i = 1; i<availableLocales.length; i++){
3592 current = lang.mixin(lang.clone(current), arguments[i]);
3593 }
3594 // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
3595 var target = bundlePathAndName + "/" + locale;
3596 cache[target] = current;
3597 load();
3598 });
3599 });
3600 },
3601
3602 normalize = function(id, toAbsMid){
3603 // summary:
3604 // id may be relative.
3605 // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and
3606 // therefore never looks like a relative
3607 return /^\./.test(id) ? toAbsMid(id) : id;
3608 },
3609
3610 getLocalesToLoad = function(targetLocale){
3611 var list = config.extraLocale || [];
3612 list = lang.isArray(list) ? list : [list];
3613 list.push(targetLocale);
3614 return list;
3615 },
3616
3617 load = function(id, require, load){
3618 // summary:
3619 // id is in one of the following formats
3620 //
3621 // 1. <path>/nls/<bundle>
3622 // => load the bundle, localized to config.locale; load all bundles localized to
3623 // config.extraLocale (if any); return the loaded bundle localized to config.locale.
3624 //
3625 // 2. <path>/nls/<locale>/<bundle>
3626 // => load then return the bundle localized to <locale>
3627 //
3628 // 3. *preload*<path>/nls/<module>*<JSON array of available locales>
3629 // => for config.locale and all config.extraLocale, load all bundles found
3630 // in the best-matching bundle rollup. A value of 1 is returned, which
3631 // is meaningless other than to say the plugin is executing the requested
3632 // preloads
3633 //
3634 // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
3635 // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder.
3636 //
3637 // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
3638 // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
3639 //
3640 // <path>/nls/<bundle>/<locale>
3641 //
3642 // will hold the value. Similarly, then plugin will publish this value to the loader by
3643 //
3644 // define("<path>/nls/<bundle>/<locale>", <bundle-value>);
3645 //
3646 // Given this algorithm, other machinery can provide fast load paths be preplacing
3647 // values in the plugin's cache, which is public. When a load is demanded the
3648 // cache is inspected before starting any loading. Explicitly placing values in the plugin
3649 // cache is an advanced/experimental feature that should not be needed; use at your own risk.
3650 //
3651 // For the normal AMD algorithm, the root bundle is loaded first, which instructs the
3652 // plugin what additional localized bundles are required for a particular locale. These
3653 // additional locales are loaded and a mix of the root and each progressively-specific
3654 // locale is returned. For example:
3655 //
3656 // 1. The client demands "dojo/i18n!some/path/nls/someBundle
3657 //
3658 // 2. The loader demands load(some/path/nls/someBundle)
3659 //
3660 // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
3661 //
3662 // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
3663 // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
3664 // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
3665 //
3666 // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
3667 // ab-cd-ef as...
3668 //
3669 // mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
3670 // require("some/path/nls/ab/someBundle")),
3671 // require("some/path/nls/ab-cd-ef/someBundle"));
3672 //
3673 // This value is inserted into the cache and published to the loader at the
3674 // key/module-id some/path/nls/someBundle/ab-cd-ef.
3675 //
3676 // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
3677 // (further preload requests will be serviced) until all ongoing preloading has completed.
3678 //
3679 // The preload signature instructs the plugin that a special rollup module is available that contains
3680 // one or more flattened, localized bundles. The JSON array of available locales indicates which locales
3681 // are available. Here is an example:
3682 //
3683 // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
3684 //
3685 // This indicates the following rollup modules are available:
3686 //
3687 // some/path/nls/someModule_ROOT
3688 // some/path/nls/someModule_ab
3689 // some/path/nls/someModule_ab-cd-ef
3690 //
3691 // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
3692 // For example, assume someModule contained the bundles some/bundle/path/someBundle and
3693 // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows:
3694 //
3695 // define({
3696 // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
3697 // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
3698 // });
3699 //
3700 // E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
3701 //
3702 // require(["some/path/nls/someModule_ab"], function(rollup){
3703 // for(var p in rollup){
3704 // var id = p + "/ab",
3705 // cache[id] = rollup[p];
3706 // define(id, rollup[p]);
3707 // }
3708 // });
3709 //
3710 // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
3711 // load accordingly.
3712 //
3713 // The builder will write such rollups for every layer if a non-empty localeList profile property is
3714 // provided. Further, the builder will include the following cache entry in the cache associated with
3715 // any layer.
3716 //
3717 // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
3718 //
3719 // The *now special cache module instructs the loader to apply the provided function to context-require
3720 // with respect to the particular layer being defined. This causes the plugin to hold all normal service
3721 // requests until all preloading is complete.
3722 //
3723 // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
3724 // where the target locale has a single segment and a layer depends on a single bundle:
3725 //
3726 // Without Preloads:
3727 //
3728 // 1. Layer loads root bundle.
3729 // 2. bundle is demanded; plugin loads single localized bundle.
3730 //
3731 // With Preloads:
3732 //
3733 // 1. Layer causes preloading of target bundle.
3734 // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
3735 //
3736 // In each case a single transaction is required to load the target bundle. In cases where multiple bundles
3737 // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
3738 // the normal path requires an additional transaction for each additional bundle/locale-segment. However all
3739 // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
3740 // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
3741
3742 if(has("dojo-preload-i18n-Api")){
3743 var split = id.split("*"),
3744 preloadDemand = split[1] == "preload";
3745 if(preloadDemand){
3746 if(!cache[id]){
3747 // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
3748 // who knows what over-aggressive human optimizers may attempt
3749 cache[id] = 1;
3750 preloadL10n(split[2], json.parse(split[3]), 1, require);
3751 }
3752 // don't stall the loader!
3753 load(1);
3754 }
3755 if(preloadDemand || waitForPreloads(id, require, load)){
3756 return;
3757 }
3758 }
3759
3760 var match = nlsRe.exec(id),
3761 bundlePath = match[1] + "/",
3762 bundleName = match[5] || match[4],
3763 bundlePathAndName = bundlePath + bundleName,
3764 localeSpecified = (match[5] && match[4]),
3765 targetLocale = localeSpecified || dojo.locale,
3766 loadTarget = bundlePathAndName + "/" + targetLocale,
3767 loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale),
3768 remaining = loadList.length,
3769 finish = function(){
3770 if(!--remaining){
3771 load(lang.delegate(cache[loadTarget]));
3772 }
3773 };
3774 array.forEach(loadList, function(locale){
3775 var target = bundlePathAndName + "/" + locale;
3776 if(has("dojo-preload-i18n-Api")){
3777 checkForLegacyModules(target);
3778 }
3779 if(!cache[target]){
3780 doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish);
3781 }else{
3782 finish();
3783 }
3784 });
3785 };
3786
3787 if(has("dojo-unit-tests")){
3788 var unitTests = thisModule.unitTests = [];
3789 }
3790
3791 if(has("dojo-preload-i18n-Api") || 1 ){
3792 var normalizeLocale = thisModule.normalizeLocale = function(locale){
3793 var result = locale ? locale.toLowerCase() : dojo.locale;
3794 return result == "root" ? "ROOT" : result;
3795 },
3796
3797 isXd = function(mid, contextRequire){
3798 return ( 1 && 1 ) ?
3799 contextRequire.isXdUrl(require.toUrl(mid + ".js")) :
3800 true;
3801 },
3802
3803 preloading = 0,
3804
3805 preloadWaitQueue = [],
3806
3807 preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean?*/ guaranteedAmdFormat, /*function?*/ contextRequire){
3808 // summary:
3809 // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any)
3810 // description:
3811 // Only called by built layer files. The entire locale hierarchy is loaded. For example,
3812 // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
3813 // in that the v1.6- would only load ab-cd...which was *always* flattened.
3814 //
3815 // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
3816 // and the extra possible extra transaction.
3817
3818 // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function
3819 // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which
3820 // itself may have been mapped.
3821 contextRequire = contextRequire || require;
3822
3823 function doRequire(mid, callback){
3824 if(isXd(mid, contextRequire) || guaranteedAmdFormat){
3825 contextRequire([mid], callback);
3826 }else{
3827 syncRequire([mid], callback, contextRequire);
3828 }
3829 }
3830
3831 function forEachLocale(locale, func){
3832 // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
3833 var parts = locale.split("-");
3834 while(parts.length){
3835 if(func(parts.join("-"))){
3836 return;
3837 }
3838 parts.pop();
3839 }
3840 func("ROOT");
3841 }
3842
3843 function preload(locale){
3844 locale = normalizeLocale(locale);
3845 forEachLocale(locale, function(loc){
3846 if(array.indexOf(localesGenerated, loc)>=0){
3847 var mid = bundlePrefix.replace(/\./g, "/")+"_"+loc;
3848 preloading++;
3849 doRequire(mid, function(rollup){
3850 for(var p in rollup){
3851 cache[require.toAbsMid(p) + "/" + loc] = rollup[p];
3852 }
3853 --preloading;
3854 while(!preloading && preloadWaitQueue.length){
3855 load.apply(null, preloadWaitQueue.shift());
3856 }
3857 });
3858 return true;
3859 }
3860 return false;
3861 });
3862 }
3863
3864 preload();
3865 array.forEach(dojo.config.extraLocale, preload);
3866 },
3867
3868 waitForPreloads = function(id, require, load){
3869 if(preloading){
3870 preloadWaitQueue.push([id, require, load]);
3871 }
3872 return preloading;
3873 },
3874
3875 checkForLegacyModules = function()
3876 {};
3877 }
3878
3879 if( 1 ){
3880 // this code path assumes the dojo loader and won't work with a standard AMD loader
3881 var amdValue = {},
3882 evalBundle =
3883 // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
3884 new Function(
3885 "__bundle", // the bundle to evalutate
3886 "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
3887 "__mid", // the mid that __bundle is intended to define
3888 "__amdValue",
3889
3890 // returns one of:
3891 // 1 => the bundle was an AMD bundle
3892 // a legacy bundle object that is the value of __mid
3893 // instance of Error => could not figure out how to evaluate bundle
3894
3895 // used to detect when __bundle calls define
3896 "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;},"
3897 + " require = function(){define.called = 1;};"
3898
3899 + "try{"
3900 + "define.called = 0;"
3901 + "eval(__bundle);"
3902 + "if(define.called==1)"
3903 // bundle called define; therefore signal it's an AMD bundle
3904 + "return __amdValue;"
3905
3906 + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
3907 // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
3908 + "return __checkForLegacyModules;"
3909
3910 + "}catch(e){}"
3911 // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
3912 // either way, re-eval *after* surrounding with parentheses
3913
3914 + "try{"
3915 + "return eval('('+__bundle+')');"
3916 + "}catch(e){"
3917 + "return e;"
3918 + "}"
3919 ),
3920
3921 syncRequire = function(deps, callback, require){
3922 var results = [];
3923 array.forEach(deps, function(mid){
3924 var url = require.toUrl(mid + ".js");
3925
3926 function load(text){
3927 var result = evalBundle(text, checkForLegacyModules, mid, amdValue);
3928 if(result===amdValue){
3929 // the bundle was an AMD module; re-inject it through the normal AMD path
3930 // we gotta do this since it could be an anonymous module and simply evaluating
3931 // the text here won't provide the loader with the context to know what
3932 // module is being defined()'d. With browser caching, this should be free; further
3933 // this entire code path can be circumvented by using the AMD format to begin with
3934 results.push(cache[url] = amdValue.result);
3935 }else{
3936 if(result instanceof Error){
3937 console.error("failed to evaluate i18n bundle; url=" + url, result);
3938 result = {};
3939 }
3940 // nls/<locale>/<bundle-name> indicates not the root.
3941 results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1}));
3942 }
3943 }
3944
3945 if(cache[url]){
3946 results.push(cache[url]);
3947 }else{
3948 var bundle = require.syncLoadNls(mid);
3949 // don't need to check for legacy since syncLoadNls returns a module if the module
3950 // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
3951 // from getLocalization --> load, then load will have called checkForLegacyModules() before
3952 // calling syncRequire; if syncRequire is called from preloadLocalizations, then we
3953 // don't care about checkForLegacyModules() because that will be done when a particular
3954 // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
3955 // because cached modules are always v1.7+ built modules.
3956 if(bundle){
3957 results.push(bundle);
3958 }else{
3959 if(!xhr){
3960 try{
3961 require.getText(url, true, load);
3962 }catch(e){
3963 results.push(cache[url] = {});
3964 }
3965 }else{
3966 xhr.get({
3967 url:url,
3968 sync:true,
3969 load:load,
3970 error:function(){
3971 results.push(cache[url] = {});
3972 }
3973 });
3974 }
3975 }
3976 }
3977 });
3978 callback && callback.apply(null, results);
3979 };
3980
3981 checkForLegacyModules = function(target){
3982 // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
3983 for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){}
3984 if(object){
3985 result = object[names[i]];
3986 if(!result){
3987 // fallback for incorrect bundle build of 1.6
3988 result = object[names[i].replace(/-/g,"_")];
3989 }
3990 if(result){
3991 cache[target] = result;
3992 }
3993 }
3994 return result;
3995 };
3996
3997 thisModule.getLocalization = function(moduleName, bundleName, locale){
3998 var result,
3999 l10nName = getBundleName(moduleName, bundleName, locale);
4000 load(
4001 l10nName,
4002
4003 // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module.
4004 // Since this legacy function does not have the concept of a reference module, resolve with respect to this
4005 // dojo/i18n module, which, itself may have been mapped.
4006 (!isXd(l10nName, require) ? function(deps, callback){ syncRequire(deps, callback, require); } : require),
4007
4008 function(result_){ result = result_; }
4009 );
4010 return result;
4011 };
4012
4013 if(has("dojo-unit-tests")){
4014 unitTests.push(function(doh){
4015 doh.register("tests.i18n.unit", function(t){
4016 var check;
4017
4018 check = evalBundle("{prop:1}", checkForLegacyModules, "nonsense", amdValue);
4019 t.is({prop:1}, check); t.is(undefined, check[1]);
4020
4021 check = evalBundle("({prop:1})", checkForLegacyModules, "nonsense", amdValue);
4022 t.is({prop:1}, check); t.is(undefined, check[1]);
4023
4024 check = evalBundle("{'prop-x':1}", checkForLegacyModules, "nonsense", amdValue);
4025 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
4026
4027 check = evalBundle("({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4028 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
4029
4030 check = evalBundle("define({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4031 t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
4032
4033 check = evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4034 t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
4035
4036 check = evalBundle("this is total nonsense and should throw an error", checkForLegacyModules, "nonsense", amdValue);
4037 t.is(check instanceof Error, true);
4038 });
4039 });
4040 }
4041 }
4042
4043 return lang.mixin(thisModule, {
4044 dynamic:true,
4045 normalize:normalize,
4046 load:load,
4047 cache:cache
4048 });
4049 });
4050
4051 },
4052 'dijit/hccss':function(){
4053 define("dijit/hccss", ["dojo/dom-class", "dojo/hccss", "dojo/ready", "dojo/_base/window"], function(domClass, has, ready, win){
4054
4055 // module:
4056 // dijit/hccss
4057
4058 /*=====
4059 return function(){
4060 // summary:
4061 // Test if computer is in high contrast mode, and sets `dijit_a11y` flag on `<body>` if it is.
4062 // Deprecated, use ``dojo/hccss`` instead.
4063 };
4064 =====*/
4065
4066 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
4067 // change this module to depend on dojo/domReady!
4068 ready(90, function(){
4069 if(has("highcontrast")){
4070 domClass.add(win.body(), "dijit_a11y");
4071 }
4072 });
4073
4074 return has;
4075 });
4076
4077 },
4078 'dijit/tree/ForestStoreModel':function(){
4079 define("dijit/tree/ForestStoreModel", [
4080 "dojo/_base/array", // array.indexOf array.some
4081 "dojo/_base/declare", // declare
4082 "dojo/_base/kernel", // global
4083 "dojo/_base/lang", // lang.hitch
4084 "./TreeStoreModel"
4085 ], function(array, declare, kernel, lang, TreeStoreModel){
4086
4087 // module:
4088 // dijit/tree/ForestStoreModel
4089
4090 return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
4091 // summary:
4092 // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4093 // a.k.a. a store that has multiple "top level" items.
4094 //
4095 // description:
4096 // Use this class to wrap a dojo.data store, making all the items matching the specified query
4097 // appear as children of a fabricated "root item". If no query is specified then all the
4098 // items returned by fetch() on the underlying store become children of the root item.
4099 // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
4100 //
4101 // When using this class the developer must override a number of methods according to their app and
4102 // data, including:
4103 //
4104 // - onNewRootItem
4105 // - onAddToRoot
4106 // - onLeaveRoot
4107 // - onNewItem
4108 // - onSetItem
4109
4110 // Parameters to constructor
4111
4112 // rootId: String
4113 // ID of fabricated root item
4114 rootId: "$root$",
4115
4116 // rootLabel: String
4117 // Label of fabricated root item
4118 rootLabel: "ROOT",
4119
4120 // query: String
4121 // Specifies the set of children of the root item.
4122 // example:
4123 // | {type:'continent'}
4124 query: null,
4125
4126 // End of parameters to constructor
4127
4128 constructor: function(params){
4129 // summary:
4130 // Sets up variables, etc.
4131 // tags:
4132 // private
4133
4134 // Make dummy root item
4135 this.root = {
4136 store: this,
4137 root: true,
4138 id: params.rootId,
4139 label: params.rootLabel,
4140 children: params.rootChildren // optional param
4141 };
4142 },
4143
4144 // =======================================================================
4145 // Methods for traversing hierarchy
4146
4147 mayHaveChildren: function(/*dojo/data/Item*/ item){
4148 // summary:
4149 // Tells if an item has or may have children. Implementing logic here
4150 // avoids showing +/- expando icon for nodes that we know don't have children.
4151 // (For efficiency reasons we may not want to check if an element actually
4152 // has children until user clicks the expando node)
4153 // tags:
4154 // extension
4155 return item === this.root || this.inherited(arguments);
4156 },
4157
4158 getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
4159 // summary:
4160 // Calls onComplete() with array of child items of given parent item, all loaded.
4161 if(parentItem === this.root){
4162 if(this.root.children){
4163 // already loaded, just return
4164 callback(this.root.children);
4165 }else{
4166 this.store.fetch({
4167 query: this.query,
4168 onComplete: lang.hitch(this, function(items){
4169 this.root.children = items;
4170 callback(items);
4171 }),
4172 onError: onError
4173 });
4174 }
4175 }else{
4176 this.inherited(arguments);
4177 }
4178 },
4179
4180 // =======================================================================
4181 // Inspecting items
4182
4183 isItem: function(/* anything */ something){
4184 return (something === this.root) ? true : this.inherited(arguments);
4185 },
4186
4187 fetchItemByIdentity: function(/* object */ keywordArgs){
4188 if(keywordArgs.identity == this.root.id){
4189 var scope = keywordArgs.scope || kernel.global;
4190 if(keywordArgs.onItem){
4191 keywordArgs.onItem.call(scope, this.root);
4192 }
4193 }else{
4194 this.inherited(arguments);
4195 }
4196 },
4197
4198 getIdentity: function(/* item */ item){
4199 return (item === this.root) ? this.root.id : this.inherited(arguments);
4200 },
4201
4202 getLabel: function(/* item */ item){
4203 return (item === this.root) ? this.root.label : this.inherited(arguments);
4204 },
4205
4206 // =======================================================================
4207 // Write interface
4208
4209 newItem: function(/* dijit/tree/dndSource.__Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
4210 // summary:
4211 // Creates a new item. See dojo/data/api/Write for details on args.
4212 // Used in drag & drop when item from external source dropped onto tree.
4213 if(parent === this.root){
4214 this.onNewRootItem(args);
4215 return this.store.newItem(args);
4216 }else{
4217 return this.inherited(arguments);
4218 }
4219 },
4220
4221 onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
4222 // summary:
4223 // User can override this method to modify a new element that's being
4224 // added to the root of the tree, for example to add a flag like root=true
4225 },
4226
4227 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
4228 // summary:
4229 // Move or copy an item from one parent item to another.
4230 // Used in drag & drop
4231 if(oldParentItem === this.root){
4232 if(!bCopy){
4233 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
4234 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4235 // that this element is no longer a child of the root node
4236 this.onLeaveRoot(childItem);
4237 }
4238 }
4239 this.inherited(arguments, [childItem,
4240 oldParentItem === this.root ? null : oldParentItem,
4241 newParentItem === this.root ? null : newParentItem,
4242 bCopy,
4243 insertIndex
4244 ]);
4245 if(newParentItem === this.root){
4246 // It's onAddToRoot()'s responsibility to modify the item so it matches
4247 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4248 // that this element is now a child of the root node
4249 this.onAddToRoot(childItem);
4250 }
4251 },
4252
4253 // =======================================================================
4254 // Handling for top level children
4255
4256 onAddToRoot: function(/* item */ item){
4257 // summary:
4258 // Called when item added to root of tree; user must override this method
4259 // to modify the item so that it matches the query for top level items
4260 // example:
4261 // | store.setValue(item, "root", true);
4262 // tags:
4263 // extension
4264 console.log(this, ": item ", item, " added to root");
4265 },
4266
4267 onLeaveRoot: function(/* item */ item){
4268 // summary:
4269 // Called when item removed from root of tree; user must override this method
4270 // to modify the item so it doesn't match the query for top level items
4271 // example:
4272 // | store.unsetAttribute(item, "root");
4273 // tags:
4274 // extension
4275 console.log(this, ": item ", item, " removed from root");
4276 },
4277
4278 // =======================================================================
4279 // Events from data store
4280
4281 _requeryTop: function(){
4282 // reruns the query for the children of the root node,
4283 // sending out an onSet notification if those children have changed
4284 var oldChildren = this.root.children || [];
4285 this.store.fetch({
4286 query: this.query,
4287 onComplete: lang.hitch(this, function(newChildren){
4288 this.root.children = newChildren;
4289
4290 // If the list of children or the order of children has changed...
4291 if(oldChildren.length != newChildren.length ||
4292 array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
4293 this.onChildrenChange(this.root, newChildren);
4294 }
4295 })
4296 });
4297 },
4298
4299 onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo){
4300 // summary:
4301 // Handler for when new items appear in the store. Developers should override this
4302 // method to be more efficient based on their app/data.
4303 // description:
4304 // Note that the default implementation requeries the top level items every time
4305 // a new item is created, since any new item could be a top level item (even in
4306 // addition to being a child of another item, since items can have multiple parents).
4307 //
4308 // If developers can detect which items are possible top level items (based on the item and the
4309 // parentInfo parameters), they should override this method to only call _requeryTop() for top
4310 // level items. Often all top level items have parentInfo==null, but
4311 // that will depend on which store you use and what your data is like.
4312 // tags:
4313 // extension
4314 this._requeryTop();
4315
4316 this.inherited(arguments);
4317 },
4318
4319 onDeleteItem: function(/*Object*/ item){
4320 // summary:
4321 // Handler for delete notifications from underlying store
4322
4323 // check if this was a child of root, and if so send notification that root's children
4324 // have changed
4325 if(array.indexOf(this.root.children, item) != -1){
4326 this._requeryTop();
4327 }
4328
4329 this.inherited(arguments);
4330 },
4331
4332 onSetItem: function(/* item */ item,
4333 /* attribute-name-string */ attribute,
4334 /* Object|Array */ oldValue,
4335 /* Object|Array */ newValue){
4336 // summary:
4337 // Updates the tree view according to changes to an item in the data store.
4338 // Developers should override this method to be more efficient based on their app/data.
4339 // description:
4340 // Handles updates to an item's children by calling onChildrenChange(), and
4341 // other updates to an item by calling onChange().
4342 //
4343 // Also, any change to any item re-executes the query for the tree's top-level items,
4344 // since this modified item may have started/stopped matching the query for top level items.
4345 //
4346 // If possible, developers should override this function to only call _requeryTop() when
4347 // the change to the item has caused it to stop/start being a top level item in the tree.
4348 // tags:
4349 // extension
4350
4351 this._requeryTop();
4352 this.inherited(arguments);
4353 }
4354
4355 });
4356
4357 });
4358
4359 },
4360 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='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\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n",
4361 'dijit/form/_ComboBoxMenuMixin':function(){
4362 define("dijit/form/_ComboBoxMenuMixin", [
4363 "dojo/_base/array", // array.forEach
4364 "dojo/_base/declare", // declare
4365 "dojo/dom-attr", // domAttr.set
4366 "dojo/i18n", // i18n.getLocalization
4367 "dojo/i18n!./nls/ComboBox"
4368 ], function(array, declare, domAttr, i18n){
4369
4370 // module:
4371 // dijit/form/_ComboBoxMenuMixin
4372
4373 return declare( "dijit.form._ComboBoxMenuMixin", null, {
4374 // summary:
4375 // Focus-less menu for internal use in `dijit/form/ComboBox`
4376 // tags:
4377 // private
4378
4379 // _messages: Object
4380 // Holds "next" and "previous" text for paging buttons on drop down
4381 _messages: null,
4382
4383 postMixInProperties: function(){
4384 this.inherited(arguments);
4385 this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
4386 },
4387
4388 buildRendering: function(){
4389 this.inherited(arguments);
4390
4391 // fill in template with i18n messages
4392 this.previousButton.innerHTML = this._messages["previousMessage"];
4393 this.nextButton.innerHTML = this._messages["nextMessage"];
4394 },
4395
4396 _setValueAttr: function(/*Object*/ value){
4397 this.value = value;
4398 this.onChange(value);
4399 },
4400
4401 onClick: function(/*DomNode*/ node){
4402 if(node == this.previousButton){
4403 this._setSelectedAttr(null);
4404 this.onPage(-1);
4405 }else if(node == this.nextButton){
4406 this._setSelectedAttr(null);
4407 this.onPage(1);
4408 }else{
4409 this.onChange(node);
4410 }
4411 },
4412
4413 // stubs
4414 onChange: function(/*Number*/ /*===== direction =====*/){
4415 // summary:
4416 // Notifies ComboBox/FilteringSelect that user selected an option.
4417 // tags:
4418 // callback
4419 },
4420
4421 onPage: function(/*Number*/ /*===== direction =====*/){
4422 // summary:
4423 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
4424 // tags:
4425 // callback
4426 },
4427
4428 onClose: function(){
4429 // summary:
4430 // Callback from dijit.popup code to this widget, notifying it that it closed
4431 // tags:
4432 // private
4433 this._setSelectedAttr(null);
4434 },
4435
4436 _createOption: function(/*Object*/ item, labelFunc){
4437 // summary:
4438 // Creates an option to appear on the popup menu subclassed by
4439 // `dijit/form/FilteringSelect`.
4440
4441 var menuitem = this._createMenuItem();
4442 var labelObject = labelFunc(item);
4443 if(labelObject.html){
4444 menuitem.innerHTML = labelObject.label;
4445 }else{
4446 menuitem.appendChild(
4447 menuitem.ownerDocument.createTextNode(labelObject.label)
4448 );
4449 }
4450 // #3250: in blank options, assign a normal height
4451 if(menuitem.innerHTML == ""){
4452 menuitem.innerHTML = "&#160;"; // &nbsp;
4453 }
4454
4455 // update menuitem.dir if BidiSupport was required
4456 this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
4457
4458 return menuitem;
4459 },
4460
4461 createOptions: function(results, options, labelFunc){
4462 // summary:
4463 // Fills in the items in the drop down list
4464 // results:
4465 // Array of items
4466 // options:
4467 // The options to the query function of the store
4468 //
4469 // labelFunc:
4470 // Function to produce a label in the drop down list from a dojo.data item
4471
4472 this.items = results;
4473
4474 // display "Previous . . ." button
4475 this.previousButton.style.display = (options.start == 0) ? "none" : "";
4476 domAttr.set(this.previousButton, "id", this.id + "_prev");
4477 // create options using _createOption function defined by parent
4478 // ComboBox (or FilteringSelect) class
4479 // #2309:
4480 // iterate over cache nondestructively
4481 array.forEach(results, function(item, i){
4482 var menuitem = this._createOption(item, labelFunc);
4483 menuitem.setAttribute("item", i); // index to this.items; use indirection to avoid mem leak
4484 domAttr.set(menuitem, "id", this.id + i);
4485 this.nextButton.parentNode.insertBefore(menuitem, this.nextButton);
4486 }, this);
4487 // display "Next . . ." button
4488 var displayMore = false;
4489 // Try to determine if we should show 'more'...
4490 if(results.total && !results.total.then && results.total != -1){
4491 if((options.start + options.count) < results.total){
4492 displayMore = true;
4493 }else if((options.start + options.count) > results.total && options.count == results.length){
4494 // Weird return from a data store, where a start + count > maxOptions
4495 // implies maxOptions isn't really valid and we have to go into faking it.
4496 // And more or less assume more if count == results.length
4497 displayMore = true;
4498 }
4499 }else if(options.count == results.length){
4500 //Don't know the size, so we do the best we can based off count alone.
4501 //So, if we have an exact match to count, assume more.
4502 displayMore = true;
4503 }
4504
4505 this.nextButton.style.display = displayMore ? "" : "none";
4506 domAttr.set(this.nextButton,"id", this.id + "_next");
4507 },
4508
4509 clearResultList: function(){
4510 // summary:
4511 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
4512 var container = this.containerNode;
4513 while(container.childNodes.length > 2){
4514 container.removeChild(container.childNodes[container.childNodes.length-2]);
4515 }
4516 this._setSelectedAttr(null);
4517 },
4518
4519 highlightFirstOption: function(){
4520 // summary:
4521 // Highlight the first real item in the list (not Previous Choices).
4522 this.selectFirstNode();
4523 },
4524
4525 highlightLastOption: function(){
4526 // summary:
4527 // Highlight the last real item in the list (not More Choices).
4528 this.selectLastNode();
4529 },
4530
4531 selectFirstNode: function(){
4532 this.inherited(arguments);
4533 if(this.getHighlightedOption() == this.previousButton){
4534 this.selectNextNode();
4535 }
4536 },
4537
4538 selectLastNode: function(){
4539 this.inherited(arguments);
4540 if(this.getHighlightedOption() == this.nextButton){
4541 this.selectPreviousNode();
4542 }
4543 },
4544
4545 getHighlightedOption: function(){
4546 return this.selected;
4547 }
4548 });
4549
4550 });
4551
4552 },
4553 'dijit/form/_SearchMixin':function(){
4554 define("dijit/form/_SearchMixin", [
4555 "dojo/data/util/filter", // patternToRegExp
4556 "dojo/_base/declare", // declare
4557 "dojo/_base/event", // event.stop
4558 "dojo/keys", // keys
4559 "dojo/_base/lang", // lang.clone lang.hitch
4560 "dojo/query", // query
4561 "dojo/sniff", // has("ie")
4562 "dojo/string", // string.substitute
4563 "dojo/when",
4564 "../registry" // registry.byId
4565 ], function(filter, declare, event, keys, lang, query, has, string, when, registry){
4566
4567 // module:
4568 // dijit/form/_SearchMixin
4569
4570
4571 return declare("dijit.form._SearchMixin", null, {
4572 // summary:
4573 // A mixin that implements the base functionality to search a store based upon user-entered text such as
4574 // with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
4575 // tags:
4576 // protected
4577
4578 // pageSize: Integer
4579 // Argument to data provider.
4580 // Specifies maximum number of search results to return per query
4581 pageSize: Infinity,
4582
4583 // store: [const] dojo/store/api/Store
4584 // Reference to data provider object used by this ComboBox.
4585 // The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
4586 store: null,
4587
4588 // fetchProperties: Object
4589 // Mixin to the store's fetch.
4590 // For example, to set the sort order of the ComboBox menu, pass:
4591 // | { sort: [{attribute:"name",descending: true}] }
4592 // To override the default queryOptions so that deep=false, do:
4593 // | { queryOptions: {ignoreCase: true, deep: false} }
4594 fetchProperties:{},
4595
4596 // query: Object
4597 // A query that can be passed to `store` to initially filter the items.
4598 // ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
4599 query: {},
4600
4601 // searchDelay: Integer
4602 // Delay in milliseconds between when user types something and we start
4603 // searching based on that value
4604 searchDelay: 200,
4605
4606 // searchAttr: String
4607 // Search for items in the data store where this attribute (in the item)
4608 // matches what the user typed
4609 searchAttr: "name",
4610
4611 // queryExpr: String
4612 // This specifies what query is sent to the data store,
4613 // based on what the user has typed. Changing this expression will modify
4614 // whether the results are only exact matches, a "starting with" match,
4615 // etc.
4616 // dojo.data query expression pattern.
4617 // `${0}` will be substituted for the user text.
4618 // `*` is used for wildcards.
4619 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
4620 queryExpr: "${0}*",
4621
4622 // ignoreCase: Boolean
4623 // Set true if the query should ignore case when matching possible items
4624 ignoreCase: true,
4625
4626 _abortQuery: function(){
4627 // stop in-progress query
4628 if(this.searchTimer){
4629 this.searchTimer = this.searchTimer.remove();
4630 }
4631 if(this._queryDeferHandle){
4632 this._queryDeferHandle = this._queryDeferHandle.remove();
4633 }
4634 if(this._fetchHandle){
4635 if(this._fetchHandle.abort){
4636 this._cancelingQuery = true;
4637 this._fetchHandle.abort();
4638 this._cancelingQuery = false;
4639 }
4640 if(this._fetchHandle.cancel){
4641 this._cancelingQuery = true;
4642 this._fetchHandle.cancel();
4643 this._cancelingQuery = false;
4644 }
4645 this._fetchHandle = null;
4646 }
4647 },
4648
4649 _processInput: function(/*Event*/ evt){
4650 // summary:
4651 // Handles input (keyboard/paste) events
4652 if(this.disabled || this.readOnly){ return; }
4653 var key = evt.charOrCode;
4654
4655 // except for cutting/pasting case - ctrl + x/v
4656 if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
4657 return; // throw out weird key combinations and spurious events
4658 }
4659
4660 var doSearch = false;
4661 this._prev_key_backspace = false;
4662
4663 switch(key){
4664 case keys.DELETE:
4665 case keys.BACKSPACE:
4666 this._prev_key_backspace = true;
4667 this._maskValidSubsetError = true;
4668 doSearch = true;
4669 break;
4670
4671 default:
4672 // Non char keys (F1-F12 etc..) shouldn't start a search..
4673 // Ascii characters and IME input (Chinese, Japanese etc.) should.
4674 //IME input produces keycode == 229.
4675 doSearch = typeof key == 'string' || key == 229;
4676 }
4677 if(doSearch){
4678 // need to wait a tad before start search so that the event
4679 // bubbles through DOM and we have value visible
4680 if(!this.store){
4681 this.onSearch();
4682 }else{
4683 this.searchTimer = this.defer("_startSearchFromInput", 1);
4684 }
4685 }
4686 },
4687
4688 onSearch: function(/*===== results, query, options =====*/){
4689 // summary:
4690 // Callback when a search completes.
4691 //
4692 // results: Object
4693 // An array of items from the originating _SearchMixin's store.
4694 //
4695 // query: Object
4696 // A copy of the originating _SearchMixin's query property.
4697 //
4698 // options: Object
4699 // The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
4700 //
4701 // tags:
4702 // callback
4703 },
4704
4705 _startSearchFromInput: function(){
4706 this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
4707 },
4708
4709 _startSearch: function(/*String*/ text){
4710 // summary:
4711 // Starts a search for elements matching text (text=="" means to return all items),
4712 // and calls onSearch(...) when the search completes, to display the results.
4713
4714 this._abortQuery();
4715 var
4716 _this = this,
4717 // Setup parameters to be passed to store.query().
4718 // Create a new query to prevent accidentally querying for a hidden
4719 // value from FilteringSelect's keyField
4720 query = lang.clone(this.query), // #5970
4721 options = {
4722 start: 0,
4723 count: this.pageSize,
4724 queryOptions: { // remove for 2.0
4725 ignoreCase: this.ignoreCase,
4726 deep: true
4727 }
4728 },
4729 qs = string.substitute(this.queryExpr, [text]),
4730 q,
4731 startQuery = function(){
4732 var resPromise = _this._fetchHandle = _this.store.query(query, options);
4733 if(_this.disabled || _this.readOnly || (q !== _this._lastQuery)){
4734 return;
4735 } // avoid getting unwanted notify
4736 when(resPromise, function(res){
4737 _this._fetchHandle = null;
4738 if(!_this.disabled && !_this.readOnly && (q === _this._lastQuery)){ // avoid getting unwanted notify
4739 when(resPromise.total, function(total){
4740 res.total = total;
4741 var pageSize = _this.pageSize;
4742 if(isNaN(pageSize) || pageSize > res.total){ pageSize = res.total; }
4743 // Setup method to fetching the next page of results
4744 res.nextPage = function(direction){
4745 // tell callback the direction of the paging so the screen
4746 // reader knows which menu option to shout
4747 options.direction = direction = direction !== false;
4748 options.count = pageSize;
4749 if(direction){
4750 options.start += res.length;
4751 if(options.start >= res.total){
4752 options.count = 0;
4753 }
4754 }else{
4755 options.start -= pageSize;
4756 if(options.start < 0){
4757 options.count = Math.max(pageSize + options.start, 0);
4758 options.start = 0;
4759 }
4760 }
4761 if(options.count <= 0){
4762 res.length = 0;
4763 _this.onSearch(res, query, options);
4764 }else{
4765 startQuery();
4766 }
4767 };
4768 _this.onSearch(res, query, options);
4769 });
4770 }
4771 }, function(err){
4772 _this._fetchHandle = null;
4773 if(!_this._cancelingQuery){ // don't treat canceled query as an error
4774 console.error(_this.declaredClass + ' ' + err.toString());
4775 }
4776 });
4777 };
4778
4779 lang.mixin(options, this.fetchProperties);
4780
4781 // Generate query
4782 if(this.store._oldAPI){
4783 // remove this branch for 2.0
4784 q = qs;
4785 }else{
4786 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
4787 // but with a toString() method to help dojo/store/JsonRest.
4788 // Search string like "Co*" converted to regex like /^Co.*$/i.
4789 q = filter.patternToRegExp(qs, this.ignoreCase);
4790 q.toString = function(){ return qs; };
4791 }
4792
4793 // set _lastQuery, *then* start the timeout
4794 // otherwise, if the user types and the last query returns before the timeout,
4795 // _lastQuery won't be set and their input gets rewritten
4796 this._lastQuery = query[this.searchAttr] = q;
4797 this._queryDeferHandle = this.defer(startQuery, this.searchDelay);
4798 },
4799
4800 //////////// INITIALIZATION METHODS ///////////////////////////////////////
4801
4802 constructor: function(){
4803 this.query={};
4804 this.fetchProperties={};
4805 },
4806
4807 postMixInProperties: function(){
4808 if(!this.store){
4809 var list = this.list;
4810 if(list){
4811 this.store = registry.byId(list);
4812 }
4813 }
4814 this.inherited(arguments);
4815 }
4816 });
4817 });
4818
4819 },
4820 'dojo/parser':function(){
4821 define(
4822 "dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window",
4823 "./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"],
4824 function(require, dojo, dlang, darray, config, dhtml, dwindow, _Url, djson, aspect, dates, Deferred, has, query, don, ready){
4825
4826 // module:
4827 // dojo/parser
4828
4829 new Date("X"); // workaround for #11279, new Date("") == NaN
4830
4831
4832 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
4833 // If BorderContainer is loaded after _Widget's parameter list has been cached,
4834 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
4835 var extendCnt = 0;
4836 aspect.after(dlang, "extend", function(){
4837 extendCnt++;
4838 }, true);
4839
4840 function getNameMap(ctor){
4841 // summary:
4842 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
4843 var map = ctor._nameCaseMap, proto = ctor.prototype;
4844
4845 // Create the map if it's undefined.
4846 // Refresh the map if a superclass was possibly extended with new methods since the map was created.
4847 if(!map || map._extendCnt < extendCnt){
4848 map = ctor._nameCaseMap = {};
4849 for(var name in proto){
4850 if(name.charAt(0) === "_"){ continue; } // skip internal properties
4851 map[name.toLowerCase()] = name;
4852 }
4853 map._extendCnt = extendCnt;
4854 }
4855 return map;
4856 }
4857
4858 // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor.
4859 var _ctorMap = {};
4860
4861 function getCtor(/*String[]*/ types){
4862 // summary:
4863 // Retrieves a constructor. If the types array contains more than one class/MID then the
4864 // subsequent classes will be mixed into the first class and a unique constructor will be
4865 // returned for that array.
4866
4867 var ts = types.join();
4868 if(!_ctorMap[ts]){
4869 var mixins = [];
4870 for(var i = 0, l = types.length; i < l; i++){
4871 var t = types[i];
4872 // TODO: Consider swapping getObject and require in the future
4873 mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && require(t))));
4874 }
4875 var ctor = mixins.shift();
4876 _ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor;
4877 }
4878
4879 return _ctorMap[ts];
4880 }
4881
4882 var parser = {
4883 // summary:
4884 // The Dom/Widget parsing package
4885
4886 _clearCache: function(){
4887 // summary:
4888 // Clear cached data. Used mainly for benchmarking.
4889 extendCnt++;
4890 _ctorMap = {};
4891 },
4892
4893 _functionFromScript: function(script, attrData){
4894 // summary:
4895 // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>`
4896 // into a function
4897 // script: DOMNode
4898 // The `<script>` DOMNode
4899 // attrData: String
4900 // For HTML5 compliance, searches for attrData + "args" (typically
4901 // "data-dojo-args") instead of "args"
4902 var preamble = "",
4903 suffix = "",
4904 argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")),
4905 withStr = script.getAttribute("with");
4906
4907 // Convert any arguments supplied in script tag into an array to be passed to the
4908 var fnArgs = (argsStr || "").split(/\s*,\s*/);
4909
4910 if(withStr && withStr.length){
4911 darray.forEach(withStr.split(/\s*,\s*/), function(part){
4912 preamble += "with("+part+"){";
4913 suffix += "}";
4914 });
4915 }
4916
4917 return new Function(fnArgs, preamble + script.innerHTML + suffix);
4918 },
4919
4920 instantiate: function(nodes, mixin, options){
4921 // summary:
4922 // Takes array of nodes, and turns them into class instances and
4923 // potentially calls a startup method to allow them to connect with
4924 // any children.
4925 // nodes: Array
4926 // Array of DOM nodes
4927 // mixin: Object?
4928 // An object that will be mixed in with each node in the array.
4929 // Values in the mixin will override values in the node, if they
4930 // exist.
4931 // options: Object?
4932 // An object used to hold kwArgs for instantiation.
4933 // See parse.options argument for details.
4934
4935 mixin = mixin || {};
4936 options = options || {};
4937
4938 var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
4939 attrData = "data-" + (options.scope || dojo._scopeName) + "-",// typically "data-dojo-"
4940 dataDojoType = attrData + "type", // typically "data-dojo-type"
4941 dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
4942
4943 var list = [];
4944 darray.forEach(nodes, function(node){
4945 var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
4946 if(type){
4947 var mixinsValue = node.getAttribute(dataDojoMixins),
4948 types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
4949
4950 list.push({
4951 node: node,
4952 types: types
4953 });
4954 }
4955 });
4956
4957 // Instantiate the nodes and return the objects
4958 return this._instantiate(list, mixin, options);
4959 },
4960
4961 _instantiate: function(nodes, mixin, options){
4962 // summary:
4963 // Takes array of objects representing nodes, and turns them into class instances and
4964 // potentially calls a startup method to allow them to connect with
4965 // any children.
4966 // nodes: Array
4967 // Array of objects like
4968 // | {
4969 // | ctor: Function (may be null)
4970 // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified)
4971 // | node: DOMNode,
4972 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
4973 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
4974 // | }
4975 // mixin: Object
4976 // An object that will be mixed in with each node in the array.
4977 // Values in the mixin will override values in the node, if they
4978 // exist.
4979 // options: Object
4980 // An options object used to hold kwArgs for instantiation.
4981 // See parse.options argument for details.
4982
4983 // Call widget constructors
4984 var thelist = darray.map(nodes, function(obj){
4985 var ctor = obj.ctor || getCtor(obj.types);
4986 // If we still haven't resolved a ctor, it is fatal now
4987 if(!ctor){
4988 throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'");
4989 }
4990 return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited);
4991 }, this);
4992
4993 // Call startup on each top level instance if it makes sense (as for
4994 // widgets). Parent widgets will recursively call startup on their
4995 // (non-top level) children
4996 if(!mixin._started && !options.noStart){
4997 darray.forEach(thelist, function(instance){
4998 if(typeof instance.startup === "function" && !instance._started){
4999 instance.startup();
5000 }
5001 });
5002 }
5003
5004 return thelist;
5005 },
5006
5007 construct: function(ctor, node, mixin, options, scripts, inherited){
5008 // summary:
5009 // Calls new ctor(params, node), where params is the hash of parameters specified on the node,
5010 // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget.
5011 // ctor: Function
5012 // Widget constructor.
5013 // node: DOMNode
5014 // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor.
5015 // mixin: Object?
5016 // Attributes in this object will be passed as parameters to ctor,
5017 // overriding attributes specified on the node.
5018 // options: Object?
5019 // An options object used to hold kwArgs for instantiation. See parse.options argument for details.
5020 // scripts: DomNode[]?
5021 // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node.
5022 // inherited: Object?
5023 // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited.
5024
5025 var proto = ctor && ctor.prototype;
5026 options = options || {};
5027
5028 // Setup hash to hold parameter settings for this widget. Start with the parameter
5029 // settings inherited from ancestors ("dir" and "lang").
5030 // Inherited setting may later be overridden by explicit settings on node itself.
5031 var params = {};
5032
5033 if(options.defaults){
5034 // settings for the document itself (or whatever subtree is being parsed)
5035 dlang.mixin(params, options.defaults);
5036 }
5037 if(inherited){
5038 // settings from dir=rtl or lang=... on a node above this node
5039 dlang.mixin(params, inherited);
5040 }
5041
5042 // Get list of attributes explicitly listed in the markup
5043 var attributes;
5044 if(has("dom-attributes-explicit")){
5045 // Standard path to get list of user specified attributes
5046 attributes = node.attributes;
5047 }else if(has("dom-attributes-specified-flag")){
5048 // Special processing needed for IE8, to skip a few faux values in attributes[]
5049 attributes = darray.filter(node.attributes, function(a){ return a.specified;});
5050 }else{
5051 // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes
5052 var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false),
5053 attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, "");
5054
5055 attributes = darray.map(attrs.split(/\s+/), function(name){
5056 var lcName = name.toLowerCase();
5057 return {
5058 name: name,
5059 // getAttribute() doesn't work for button.value, returns innerHTML of button.
5060 // but getAttributeNode().value doesn't work for the form.encType or li.value
5061 value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ?
5062 node.getAttribute(lcName) : node.getAttributeNode(lcName).value
5063 };
5064 });
5065 }
5066
5067 // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params)
5068 // TODO: remove scope for 2.0
5069 var scope = options.scope || dojo._scopeName,
5070 attrData = "data-" + scope + "-", // typically "data-dojo-"
5071 hash = {};
5072 if(scope !== "dojo"){
5073 hash[attrData + "props"] = "data-dojo-props";
5074 hash[attrData + "type"] = "data-dojo-type";
5075 hash[attrData + "mixins"] = "data-dojo-mixins";
5076 hash[scope + "type"] = "dojoType";
5077 hash[attrData + "id"] = "data-dojo-id";
5078 }
5079
5080 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
5081 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
5082 var i=0, item, funcAttrs=[], jsname, extra;
5083 while(item = attributes[i++]){
5084 var name = item.name,
5085 lcName = name.toLowerCase(),
5086 value = item.value;
5087
5088 switch(hash[lcName] || lcName){
5089 // Already processed, just ignore
5090 case "data-dojo-type":
5091 case "dojotype":
5092 case "data-dojo-mixins":
5093 break;
5094
5095 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
5096 case "data-dojo-props":
5097 extra = value;
5098 break;
5099
5100 // data-dojo-id or jsId. TODO: drop jsId in 2.0
5101 case "data-dojo-id":
5102 case "jsid":
5103 jsname = value;
5104 break;
5105
5106 // For the benefit of _Templated
5107 case "data-dojo-attach-point":
5108 case "dojoattachpoint":
5109 params.dojoAttachPoint = value;
5110 break;
5111 case "data-dojo-attach-event":
5112 case "dojoattachevent":
5113 params.dojoAttachEvent = value;
5114 break;
5115
5116 // Special parameter handling needed for IE
5117 case "class":
5118 params["class"] = node.className;
5119 break;
5120 case "style":
5121 params["style"] = node.style && node.style.cssText;
5122 break;
5123 default:
5124 // Normal attribute, ex: value="123"
5125
5126 // Find attribute in widget corresponding to specified name.
5127 // May involve case conversion, ex: onclick --> onClick
5128 if(!(name in proto)){
5129 var map = getNameMap(ctor);
5130 name = map[lcName] || name;
5131 }
5132
5133 // Set params[name] to value, doing type conversion
5134 if(name in proto){
5135 switch(typeof proto[name]){
5136 case "string":
5137 params[name] = value;
5138 break;
5139 case "number":
5140 params[name] = value.length ? Number(value) : NaN;
5141 break;
5142 case "boolean":
5143 // for checked/disabled value might be "" or "checked". interpret as true.
5144 params[name] = value.toLowerCase() != "false";
5145 break;
5146 case "function":
5147 if(value === "" || value.search(/[^\w\.]+/i) != -1){
5148 // The user has specified some text for a function like "return x+5"
5149 params[name] = new Function(value);
5150 }else{
5151 // The user has specified the name of a global function like "myOnClick"
5152 // or a single word function "return"
5153 params[name] = dlang.getObject(value, false) || new Function(value);
5154 }
5155 funcAttrs.push(name); // prevent "double connect", see #15026
5156 break;
5157 default:
5158 var pVal = proto[name];
5159 params[name] =
5160 (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array
5161 (pVal instanceof Date) ?
5162 (value == "" ? new Date("") : // the NaN of dates
5163 value == "now" ? new Date() : // current date
5164 dates.fromISOString(value)) :
5165 (pVal instanceof _Url) ? (dojo.baseUrl + value) :
5166 djson.fromJson(value);
5167 }
5168 }else{
5169 params[name] = value;
5170 }
5171 }
5172 }
5173
5174 // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026.
5175 // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though).
5176 for(var j=0; j<funcAttrs.length; j++){
5177 var lcfname = funcAttrs[j].toLowerCase();
5178 node.removeAttribute(lcfname);
5179 node[lcfname] = null;
5180 }
5181
5182 // Mix things found in data-dojo-props into the params, overriding any direct settings
5183 if(extra){
5184 try{
5185 extra = djson.fromJson.call(options.propsThis, "{" + extra + "}");
5186 dlang.mixin(params, extra);
5187 }catch(e){
5188 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
5189 throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
5190 }
5191 }
5192
5193 // Any parameters specified in "mixin" override everything else.
5194 dlang.mixin(params, mixin);
5195
5196 // Get <script> nodes associated with this widget, if they weren't specified explicitly
5197 if(!scripts){
5198 scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node));
5199 }
5200
5201 // Process <script type="dojo/*"> script tags
5202 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
5203 // the widget on instantiation.
5204 // <script type="dojo/method"> tags (with no event) are executed after instantiation
5205 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
5206 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
5207 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
5208 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
5209 var aspects = [], // aspects to connect after instantiation
5210 calls = [], // functions to call after instantiation
5211 watches = [], // functions to watch after instantiation
5212 ons = []; // functions to on after instantiation
5213
5214 if(scripts){
5215 for(i=0; i<scripts.length; i++){
5216 var script = scripts[i];
5217 node.removeChild(script);
5218 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
5219 var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
5220 prop = script.getAttribute(attrData + "prop"),
5221 method = script.getAttribute(attrData + "method"),
5222 advice = script.getAttribute(attrData + "advice"),
5223 scriptType = script.getAttribute("type"),
5224 nf = this._functionFromScript(script, attrData);
5225 if(event){
5226 if(scriptType == "dojo/connect"){
5227 aspects.push({ method: event, func: nf });
5228 }else if(scriptType == "dojo/on"){
5229 ons.push({ event: event, func: nf });
5230 }else{
5231 params[event] = nf;
5232 }
5233 }else if(scriptType == "dojo/aspect"){
5234 aspects.push({ method: method, advice: advice, func: nf });
5235 }else if(scriptType == "dojo/watch"){
5236 watches.push({ prop: prop, func: nf });
5237 }else{
5238 calls.push(nf);
5239 }
5240 }
5241 }
5242
5243 // create the instance
5244 var markupFactory = ctor.markupFactory || proto.markupFactory;
5245 var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node);
5246
5247 // map it to the JS namespace if that makes sense
5248 if(jsname){
5249 dlang.setObject(jsname, instance);
5250 }
5251
5252 // process connections and startup functions
5253 for(i=0; i<aspects.length; i++){
5254 aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true);
5255 }
5256 for(i=0; i<calls.length; i++){
5257 calls[i].call(instance);
5258 }
5259 for(i=0; i<watches.length; i++){
5260 instance.watch(watches[i].prop, watches[i].func);
5261 }
5262 for(i=0; i<ons.length; i++){
5263 don(instance, ons[i].event, ons[i].func);
5264 }
5265
5266 return instance;
5267 },
5268
5269 scan: function(root, options){
5270 // summary:
5271 // Scan a DOM tree and return an array of objects representing the DOMNodes
5272 // that need to be turned into widgets.
5273 // description:
5274 // Search specified node (or document root node) recursively for class instances
5275 // and return an array of objects that represent potential widgets to be
5276 // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where
5277 // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name
5278 // like "dijit/form/Button". If the MID is not currently available, scan will
5279 // attempt to require() in the module.
5280 //
5281 // See parser.parse() for details of markup.
5282 // root: DomNode?
5283 // A default starting root node from which to start the parsing. Can be
5284 // omitted, defaulting to the entire document. If omitted, the `options`
5285 // object can be passed in this place. If the `options` object has a
5286 // `rootNode` member, that is used.
5287 // options: Object
5288 // a kwArgs options object, see parse() for details
5289 //
5290 // returns: Promise
5291 // A promise that is resolved with the nodes that have been parsed.
5292
5293 var list = [], // Output List
5294 mids = [], // An array of modules that are not yet loaded
5295 midsHash = {}; // Used to keep the mids array unique
5296
5297 var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
5298 attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-"
5299 dataDojoType = attrData + "type", // typically "data-dojo-type"
5300 dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir"
5301 dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
5302
5303 // Info on DOMNode currently being processed
5304 var node = root.firstChild;
5305
5306 // Info on parent of DOMNode currently being processed
5307 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
5308 // - parent: pointer to identical structure for my parent (or null if no parent)
5309 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
5310 var inherited = options.inherited;
5311 if(!inherited){
5312 function findAncestorAttr(node, attr){
5313 return (node.getAttribute && node.getAttribute(attr)) ||
5314 (node.parentNode && findAncestorAttr(node.parentNode, attr));
5315 }
5316 inherited = {
5317 dir: findAncestorAttr(root, "dir"),
5318 lang: findAncestorAttr(root, "lang"),
5319 textDir: findAncestorAttr(root, dataDojoTextDir)
5320 };
5321 for(var key in inherited){
5322 if(!inherited[key]){ delete inherited[key]; }
5323 }
5324 }
5325
5326 // Metadata about parent node
5327 var parent = {
5328 inherited: inherited
5329 };
5330
5331 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
5332 var scripts;
5333
5334 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
5335 var scriptsOnly;
5336
5337 function getEffective(parent){
5338 // summary:
5339 // Get effective dir, lang, textDir settings for specified obj
5340 // (matching "parent" object structure above), and do caching.
5341 // Take care not to return null entries.
5342 if(!parent.inherited){
5343 parent.inherited = {};
5344 var node = parent.node,
5345 grandparent = getEffective(parent.parent);
5346 var inherited = {
5347 dir: node.getAttribute("dir") || grandparent.dir,
5348 lang: node.getAttribute("lang") || grandparent.lang,
5349 textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir
5350 };
5351 for(var key in inherited){
5352 if(inherited[key]){
5353 parent.inherited[key] = inherited[key];
5354 }
5355 }
5356 }
5357 return parent.inherited;
5358 }
5359
5360 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
5361 while(true){
5362 if(!node){
5363 // Finished this level, continue to parent's next sibling
5364 if(!parent || !parent.node){
5365 break;
5366 }
5367 node = parent.node.nextSibling;
5368 scriptsOnly = false;
5369 parent = parent.parent;
5370 scripts = parent.scripts;
5371 continue;
5372 }
5373
5374 if(node.nodeType != 1){
5375 // Text or comment node, skip to next sibling
5376 node = node.nextSibling;
5377 continue;
5378 }
5379
5380 if(scripts && node.nodeName.toLowerCase() == "script"){
5381 // Save <script type="dojo/..."> for parent, then continue to next sibling
5382 type = node.getAttribute("type");
5383 if(type && /^dojo\/\w/i.test(type)){
5384 scripts.push(node);
5385 }
5386 node = node.nextSibling;
5387 continue;
5388 }
5389 if(scriptsOnly){
5390 // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't
5391 // continue further analysis of the node and will continue to the next sibling
5392 node = node.nextSibling;
5393 continue;
5394 }
5395
5396 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
5397 // TODO: Remove dojoType in 2.0
5398 var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
5399
5400 // Short circuit for leaf nodes containing nothing [but text]
5401 var firstChild = node.firstChild;
5402 if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){
5403 node = node.nextSibling;
5404 continue;
5405 }
5406
5407 // Meta data about current node
5408 var current;
5409
5410 var ctor = null;
5411 if(type){
5412 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate.
5413 var mixinsValue = node.getAttribute(dataDojoMixins),
5414 types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
5415
5416 // Note: won't find classes declared via dojo/Declaration or any modules that haven't been
5417 // loaded yet so use try/catch to avoid throw from require()
5418 try{
5419 ctor = getCtor(types);
5420 }catch(e){}
5421
5422 // If the constructor was not found, check to see if it has modules that can be loaded
5423 if(!ctor){
5424 darray.forEach(types, function(t){
5425 if(~t.indexOf('/') && !midsHash[t]){
5426 // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it.
5427 midsHash[t] = true;
5428 mids[mids.length] = t;
5429 }
5430 });
5431 }
5432
5433 var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children
5434
5435 // Setup meta data about this widget node, and save it to list of nodes to instantiate
5436 current = {
5437 types: types,
5438 ctor: ctor,
5439 parent: parent,
5440 node: node,
5441 scripts: childScripts
5442 };
5443 current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited
5444 list.push(current);
5445 }else{
5446 // Meta data about this non-widget node
5447 current = {
5448 node: node,
5449 scripts: scripts,
5450 parent: parent
5451 };
5452 }
5453
5454 // Recurse, collecting <script type="dojo/..."> children, and also looking for
5455 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
5456 // When finished with children, go to my next sibling.
5457 node = firstChild;
5458 scripts = childScripts;
5459 scriptsOnly = ctor && ctor.prototype.stopParser && !(options.template);
5460 parent = current;
5461 }
5462
5463 var d = new Deferred();
5464
5465 // If there are modules to load then require them in
5466 if(mids.length){
5467 // Warn that there are modules being auto-required
5468 if(has("dojo-debug-messages")){
5469 console.warn("WARNING: Modules being Auto-Required: " + mids.join(", "));
5470 }
5471 require(mids, function(){
5472 // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't
5473 // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to
5474 // auto-require of a module like ContentPane. Assumes list is in DFS order.
5475 d.resolve(darray.filter(list, function(widget){
5476 if(!widget.ctor){
5477 // Attempt to find the constructor again. Still won't find classes defined via
5478 // dijit/Declaration so need to try/catch.
5479 try{
5480 widget.ctor = getCtor(widget.types);
5481 }catch(e){}
5482 }
5483
5484 // Get the parent widget
5485 var parent = widget.parent;
5486 while(parent && !parent.types){
5487 parent = parent.parent;
5488 }
5489
5490 // Return false if this node should be skipped due to stopParser on an ancestor.
5491 // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before
5492 // trying to compute widget.instantiate.
5493 var proto = widget.ctor && widget.ctor.prototype;
5494 widget.instantiateChildren = !(proto && proto.stopParser && !(options.template));
5495 widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren);
5496 return widget.instantiate;
5497 }));
5498 });
5499 }else{
5500 // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for
5501 // efficiency, to avoid running the require() and the callback code above.
5502 d.resolve(list);
5503 }
5504
5505 // Return the promise
5506 return d.promise;
5507 },
5508
5509 _require: function(/*DOMNode*/ script){
5510 // summary:
5511 // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node,
5512 // calls require() to load the specified modules and (asynchronously) assign them to the specified global
5513 // variables, and returns a Promise for when that operation completes.
5514 //
5515 // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
5516
5517 var hash = djson.fromJson("{" + script.innerHTML + "}"),
5518 vars = [],
5519 mids = [],
5520 d = new Deferred();
5521
5522 for(var name in hash){
5523 vars.push(name);
5524 mids.push(hash[name]);
5525 }
5526
5527 require(mids, function(){
5528 for(var i=0; i<vars.length; i++){
5529 dlang.setObject(vars[i], arguments[i]);
5530 }
5531 d.resolve(arguments);
5532 });
5533
5534 return d.promise;
5535 },
5536
5537 _scanAmd: function(root){
5538 // summary:
5539 // Scans the DOM for any declarative requires and returns their values.
5540 // description:
5541 // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the
5542 // specified modules and (asynchronously) assign them to the specified global variables,
5543 // and returns a Promise for when those operations complete.
5544 // root: DomNode
5545 // The node to base the scan from.
5546
5547 // Promise that resolves when all the <script type=dojo/require> nodes have finished loading.
5548 var deferred = new Deferred(),
5549 promise = deferred.promise;
5550 deferred.resolve(true);
5551
5552 var self = this;
5553 query("script[type='dojo/require']", root).forEach(function(node){
5554 // Fire off require() call for specified modules. Chain this require to fire after
5555 // any previous requires complete, so that layers can be loaded before individual module require()'s fire.
5556 promise = promise.then(function(){ return self._require(node); });
5557
5558 // Remove from DOM so it isn't seen again
5559 node.parentNode.removeChild(node);
5560 });
5561
5562 return promise;
5563 },
5564
5565 parse: function(rootNode, options){
5566 // summary:
5567 // Scan the DOM for class instances, and instantiate them.
5568 // description:
5569 // Search specified node (or root node) recursively for class instances,
5570 // and instantiate them. Searches for either data-dojo-type="Class" or
5571 // dojoType="Class" where "Class" is a a fully qualified class name,
5572 // like `dijit/form/Button`
5573 //
5574 // Using `data-dojo-type`:
5575 // Attributes using can be mixed into the parameters used to instantiate the
5576 // Class by using a `data-dojo-props` attribute on the node being converted.
5577 // `data-dojo-props` should be a string attribute to be converted from JSON.
5578 //
5579 // Using `dojoType`:
5580 // Attributes are read from the original domNode and converted to appropriate
5581 // types by looking up the Class prototype values. This is the default behavior
5582 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
5583 // go away in Dojo 2.0.
5584 // rootNode: DomNode?
5585 // A default starting root node from which to start the parsing. Can be
5586 // omitted, defaulting to the entire document. If omitted, the `options`
5587 // object can be passed in this place. If the `options` object has a
5588 // `rootNode` member, that is used.
5589 // options: Object?
5590 // A hash of options.
5591 //
5592 // - noStart: Boolean?:
5593 // when set will prevent the parser from calling .startup()
5594 // when locating the nodes.
5595 // - rootNode: DomNode?:
5596 // identical to the function's `rootNode` argument, though
5597 // allowed to be passed in via this `options object.
5598 // - template: Boolean:
5599 // If true, ignores ContentPane's stopParser flag and parses contents inside of
5600 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
5601 // nested inside the ContentPane to work.
5602 // - inherited: Object:
5603 // Hash possibly containing dir and lang settings to be applied to
5604 // parsed widgets, unless there's another setting on a sub-node that overrides
5605 // - scope: String:
5606 // Root for attribute names to search for. If scopeName is dojo,
5607 // will search for data-dojo-type (or dojoType). For backwards compatibility
5608 // reasons defaults to dojo._scopeName (which is "dojo" except when
5609 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
5610 // - propsThis: Object:
5611 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
5612 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
5613 // returns: Mixed
5614 // Returns a blended object that is an array of the instantiated objects, but also can include
5615 // a promise that is resolved with the instantiated objects. This is done for backwards
5616 // compatibility. If the parser auto-requires modules, it will always behave in a promise
5617 // fashion and `parser.parse().then(function(instances){...})` should be used.
5618 // example:
5619 // Parse all widgets on a page:
5620 // | parser.parse();
5621 // example:
5622 // Parse all classes within the node with id="foo"
5623 // | parser.parse(dojo.byId('foo'));
5624 // example:
5625 // Parse all classes in a page, but do not call .startup() on any
5626 // child
5627 // | parser.parse({ noStart: true })
5628 // example:
5629 // Parse all classes in a node, but do not call .startup()
5630 // | parser.parse(someNode, { noStart:true });
5631 // | // or
5632 // | parser.parse({ noStart:true, rootNode: someNode });
5633
5634 // determine the root node and options based on the passed arguments.
5635 var root;
5636 if(!options && rootNode && rootNode.rootNode){
5637 options = rootNode;
5638 root = options.rootNode;
5639 }else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){
5640 options = rootNode;
5641 }else{
5642 root = rootNode;
5643 }
5644 root = root ? dhtml.byId(root) : dwindow.body();
5645
5646 options = options || {};
5647
5648 var mixin = options.template ? { template: true } : {},
5649 instances = [],
5650 self = this;
5651
5652 // First scan for any <script type=dojo/require> nodes, and execute.
5653 // Then scan for all nodes with data-dojo-type, and load any unloaded modules.
5654 // Then build the object instances. Add instances to already existing (but empty) instances[] array,
5655 // which may already have been returned to caller. Also, use otherwise to collect and throw any errors
5656 // that occur during the parse().
5657 var p =
5658 this._scanAmd(root, options).then(function(){
5659 return self.scan(root, options);
5660 }).then(function(parsedNodes){
5661 return instances = instances.concat(self._instantiate(parsedNodes, mixin, options));
5662 }).otherwise(function(e){
5663 // TODO Modify to follow better pattern for promise error managment when available
5664 console.error("dojo/parser::parse() error", e);
5665 throw e;
5666 });
5667
5668 // Blend the array with the promise
5669 dlang.mixin(instances, p);
5670 return instances;
5671 }
5672 };
5673
5674 if( 1 ){
5675 dojo.parser = parser;
5676 }
5677
5678 // Register the parser callback. It should be the first callback
5679 // after the a11y test.
5680 if(config.parseOnLoad){
5681 ready(100, parser, "parse");
5682 }
5683
5684 return parser;
5685 });
5686
5687 },
5688 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"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\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"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\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n",
5689 'dojo/dnd/Manager':function(){
5690 define("dojo/dnd/Manager", [
5691 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window",
5692 "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch",
5693 "./common", "./autoscroll", "./Avatar"
5694 ], function(array, declare, event, lang, win, domClass, Evented, has, keys, on, topic, touch,
5695 dnd, autoscroll, Avatar){
5696
5697 // module:
5698 // dojo/dnd/Manager
5699
5700 var Manager = declare("dojo.dnd.Manager", [Evented], {
5701 // summary:
5702 // the manager of DnD operations (usually a singleton)
5703 constructor: function(){
5704 this.avatar = null;
5705 this.source = null;
5706 this.nodes = [];
5707 this.copy = true;
5708 this.target = null;
5709 this.canDropFlag = false;
5710 this.events = [];
5711 },
5712
5713 // avatar's offset from the mouse
5714 OFFSET_X: has("touch") ? 0 : 16,
5715 OFFSET_Y: has("touch") ? -64 : 16,
5716
5717 // methods
5718 overSource: function(source){
5719 // summary:
5720 // called when a source detected a mouse-over condition
5721 // source: Object
5722 // the reporter
5723 if(this.avatar){
5724 this.target = (source && source.targetState != "Disabled") ? source : null;
5725 this.canDropFlag = Boolean(this.target);
5726 this.avatar.update();
5727 }
5728 topic.publish("/dnd/source/over", source);
5729 },
5730 outSource: function(source){
5731 // summary:
5732 // called when a source detected a mouse-out condition
5733 // source: Object
5734 // the reporter
5735 if(this.avatar){
5736 if(this.target == source){
5737 this.target = null;
5738 this.canDropFlag = false;
5739 this.avatar.update();
5740 topic.publish("/dnd/source/over", null);
5741 }
5742 }else{
5743 topic.publish("/dnd/source/over", null);
5744 }
5745 },
5746 startDrag: function(source, nodes, copy){
5747 // summary:
5748 // called to initiate the DnD operation
5749 // source: Object
5750 // the source which provides items
5751 // nodes: Array
5752 // the list of transferred items
5753 // copy: Boolean
5754 // copy items, if true, move items otherwise
5755
5756 // Tell autoscroll that a drag is starting
5757 autoscroll.autoScrollStart(win.doc);
5758
5759 this.source = source;
5760 this.nodes = nodes;
5761 this.copy = Boolean(copy); // normalizing to true boolean
5762 this.avatar = this.makeAvatar();
5763 win.body().appendChild(this.avatar.node);
5764 topic.publish("/dnd/start", source, nodes, this.copy);
5765 this.events = [
5766 on(win.doc, touch.move, lang.hitch(this, "onMouseMove")),
5767 on(win.doc, touch.release, lang.hitch(this, "onMouseUp")),
5768 on(win.doc, "keydown", lang.hitch(this, "onKeyDown")),
5769 on(win.doc, "keyup", lang.hitch(this, "onKeyUp")),
5770 // cancel text selection and text dragging
5771 on(win.doc, "dragstart", event.stop),
5772 on(win.body(), "selectstart", event.stop)
5773 ];
5774 var c = "dojoDnd" + (copy ? "Copy" : "Move");
5775 domClass.add(win.body(), c);
5776 },
5777 canDrop: function(flag){
5778 // summary:
5779 // called to notify if the current target can accept items
5780 var canDropFlag = Boolean(this.target && flag);
5781 if(this.canDropFlag != canDropFlag){
5782 this.canDropFlag = canDropFlag;
5783 this.avatar.update();
5784 }
5785 },
5786 stopDrag: function(){
5787 // summary:
5788 // stop the DnD in progress
5789 domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]);
5790 array.forEach(this.events, function(handle){ handle.remove(); });
5791 this.events = [];
5792 this.avatar.destroy();
5793 this.avatar = null;
5794 this.source = this.target = null;
5795 this.nodes = [];
5796 },
5797 makeAvatar: function(){
5798 // summary:
5799 // makes the avatar; it is separate to be overwritten dynamically, if needed
5800 return new Avatar(this);
5801 },
5802 updateAvatar: function(){
5803 // summary:
5804 // updates the avatar; it is separate to be overwritten dynamically, if needed
5805 this.avatar.update();
5806 },
5807
5808 // mouse event processors
5809 onMouseMove: function(e){
5810 // summary:
5811 // event processor for onmousemove
5812 // e: Event
5813 // mouse event
5814 var a = this.avatar;
5815 if(a){
5816 autoscroll.autoScrollNodes(e);
5817 //autoscroll.autoScroll(e);
5818 var s = a.node.style;
5819 s.left = (e.pageX + this.OFFSET_X) + "px";
5820 s.top = (e.pageY + this.OFFSET_Y) + "px";
5821 var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
5822 if(this.copy != copy){
5823 this._setCopyStatus(copy);
5824 }
5825 }
5826 if(has("touch")){
5827 // Prevent page from scrolling so that user can drag instead.
5828 e.preventDefault();
5829 }
5830 },
5831 onMouseUp: function(e){
5832 // summary:
5833 // event processor for onmouseup
5834 // e: Event
5835 // mouse event
5836 if(this.avatar){
5837 if(this.target && this.canDropFlag){
5838 var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
5839 topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e);
5840 topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e);
5841 }else{
5842 topic.publish("/dnd/cancel");
5843 }
5844 this.stopDrag();
5845 }
5846 },
5847
5848 // keyboard event processors
5849 onKeyDown: function(e){
5850 // summary:
5851 // event processor for onkeydown:
5852 // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
5853 // e: Event
5854 // keyboard event
5855 if(this.avatar){
5856 switch(e.keyCode){
5857 case keys.CTRL:
5858 var copy = Boolean(this.source.copyState(true));
5859 if(this.copy != copy){
5860 this._setCopyStatus(copy);
5861 }
5862 break;
5863 case keys.ESCAPE:
5864 topic.publish("/dnd/cancel");
5865 this.stopDrag();
5866 break;
5867 }
5868 }
5869 },
5870 onKeyUp: function(e){
5871 // summary:
5872 // event processor for onkeyup, watching for CTRL for copy/move status
5873 // e: Event
5874 // keyboard event
5875 if(this.avatar && e.keyCode == keys.CTRL){
5876 var copy = Boolean(this.source.copyState(false));
5877 if(this.copy != copy){
5878 this._setCopyStatus(copy);
5879 }
5880 }
5881 },
5882
5883 // utilities
5884 _setCopyStatus: function(copy){
5885 // summary:
5886 // changes the copy status
5887 // copy: Boolean
5888 // the copy status
5889 this.copy = copy;
5890 this.source._markDndStatus(this.copy);
5891 this.updateAvatar();
5892 domClass.replace(win.body(),
5893 "dojoDnd" + (this.copy ? "Copy" : "Move"),
5894 "dojoDnd" + (this.copy ? "Move" : "Copy"));
5895 }
5896 });
5897
5898 // dnd._manager:
5899 // The manager singleton variable. Can be overwritten if needed.
5900 dnd._manager = null;
5901
5902 Manager.manager = dnd.manager = function(){
5903 // summary:
5904 // Returns the current DnD manager. Creates one if it is not created yet.
5905 if(!dnd._manager){
5906 dnd._manager = new Manager();
5907 }
5908 return dnd._manager; // Object
5909 };
5910
5911 return Manager;
5912 });
5913
5914 },
5915 'dijit/form/ToggleButton':function(){
5916 define("dijit/form/ToggleButton", [
5917 "dojo/_base/declare", // declare
5918 "dojo/_base/kernel", // kernel.deprecated
5919 "./Button",
5920 "./_ToggleButtonMixin"
5921 ], function(declare, kernel, Button, _ToggleButtonMixin){
5922
5923 // module:
5924 // dijit/form/ToggleButton
5925
5926
5927 return declare("dijit.form.ToggleButton", [Button, _ToggleButtonMixin], {
5928 // summary:
5929 // A templated button widget that can be in two states (checked or not).
5930 // Can be base class for things like tabs or checkbox or radio buttons.
5931
5932 baseClass: "dijitToggleButton",
5933
5934 setChecked: function(/*Boolean*/ checked){
5935 // summary:
5936 // Deprecated. Use set('checked', true/false) instead.
5937 kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
5938 this.set('checked', checked);
5939 }
5940 });
5941 });
5942
5943 },
5944 'dojo/date/stamp':function(){
5945 define("dojo/date/stamp", ["../_base/lang", "../_base/array"], function(lang, array){
5946
5947 // module:
5948 // dojo/date/stamp
5949
5950 var stamp = {
5951 // summary:
5952 // TODOC
5953 };
5954 lang.setObject("dojo.date.stamp", stamp);
5955
5956 // Methods to convert dates to or from a wire (string) format using well-known conventions
5957
5958 stamp.fromISOString = function(/*String*/ formattedString, /*Number?*/ defaultTime){
5959 // summary:
5960 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
5961 //
5962 // description:
5963 // Accepts a string formatted according to a profile of ISO8601 as defined by
5964 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
5965 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
5966 // The following combinations are valid:
5967 //
5968 // - dates only
5969 // - yyyy
5970 // - yyyy-MM
5971 // - yyyy-MM-dd
5972 // - times only, with an optional time zone appended
5973 // - THH:mm
5974 // - THH:mm:ss
5975 // - THH:mm:ss.SSS
5976 // - and "datetimes" which could be any combination of the above
5977 //
5978 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
5979 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
5980 // input may return null. Arguments which are out of bounds will be handled
5981 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
5982 // Only years between 100 and 9999 are supported.
5983 // formattedString:
5984 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
5985 // defaultTime:
5986 // Used for defaults for fields omitted in the formattedString.
5987 // Uses 1970-01-01T00:00:00.0Z by default.
5988
5989 if(!stamp._isoRegExp){
5990 stamp._isoRegExp =
5991 //TODO: could be more restrictive and check for 00-59, etc.
5992 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
5993 }
5994
5995 var match = stamp._isoRegExp.exec(formattedString),
5996 result = null;
5997
5998 if(match){
5999 match.shift();
6000 if(match[1]){match[1]--;} // Javascript Date months are 0-based
6001 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
6002
6003 if(defaultTime){
6004 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
6005 defaultTime = new Date(defaultTime);
6006 array.forEach(array.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
6007 return defaultTime["get" + prop]();
6008 }), function(value, index){
6009 match[index] = match[index] || value;
6010 });
6011 }
6012 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
6013 if(match[0] < 100){
6014 result.setFullYear(match[0] || 1970);
6015 }
6016
6017 var offset = 0,
6018 zoneSign = match[7] && match[7].charAt(0);
6019 if(zoneSign != 'Z'){
6020 offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
6021 if(zoneSign != '-'){ offset *= -1; }
6022 }
6023 if(zoneSign){
6024 offset -= result.getTimezoneOffset();
6025 }
6026 if(offset){
6027 result.setTime(result.getTime() + offset * 60000);
6028 }
6029 }
6030
6031 return result; // Date or null
6032 };
6033
6034 /*=====
6035 var __Options = {
6036 // selector: String
6037 // "date" or "time" for partial formatting of the Date object.
6038 // Both date and time will be formatted by default.
6039 // zulu: Boolean
6040 // if true, UTC/GMT is used for a timezone
6041 // milliseconds: Boolean
6042 // if true, output milliseconds
6043 };
6044 =====*/
6045
6046 stamp.toISOString = function(/*Date*/ dateObject, /*__Options?*/ options){
6047 // summary:
6048 // Format a Date object as a string according a subset of the ISO-8601 standard
6049 //
6050 // description:
6051 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
6052 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
6053 // Does not check bounds. Only years between 100 and 9999 are supported.
6054 //
6055 // dateObject:
6056 // A Date object
6057
6058 var _ = function(n){ return (n < 10) ? "0" + n : n; };
6059 options = options || {};
6060 var formattedDate = [],
6061 getter = options.zulu ? "getUTC" : "get",
6062 date = "";
6063 if(options.selector != "time"){
6064 var year = dateObject[getter+"FullYear"]();
6065 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
6066 }
6067 formattedDate.push(date);
6068 if(options.selector != "date"){
6069 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
6070 var millis = dateObject[getter+"Milliseconds"]();
6071 if(options.milliseconds){
6072 time += "."+ (millis < 100 ? "0" : "") + _(millis);
6073 }
6074 if(options.zulu){
6075 time += "Z";
6076 }else if(options.selector != "time"){
6077 var timezoneOffset = dateObject.getTimezoneOffset();
6078 var absOffset = Math.abs(timezoneOffset);
6079 time += (timezoneOffset > 0 ? "-" : "+") +
6080 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
6081 }
6082 formattedDate.push(time);
6083 }
6084 return formattedDate.join('T'); // String
6085 };
6086
6087 return stamp;
6088 });
6089
6090 },
6091 'dojo/Stateful':function(){
6092 define("dojo/Stateful", ["./_base/declare", "./_base/lang", "./_base/array", "dojo/when"], function(declare, lang, array, when){
6093 // module:
6094 // dojo/Stateful
6095
6096 return declare("dojo.Stateful", null, {
6097 // summary:
6098 // Base class for objects that provide named properties with optional getter/setter
6099 // control and the ability to watch for property changes
6100 //
6101 // The class also provides the functionality to auto-magically manage getters
6102 // and setters for object attributes/properties.
6103 //
6104 // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
6105 // the xxx is a name of the attribute to handle. So an attribute of "foo"
6106 // would have a custom getter of _fooGetter and a custom setter of _fooSetter.
6107 //
6108 // example:
6109 // | var obj = new dojo.Stateful();
6110 // | obj.watch("foo", function(){
6111 // | console.log("foo changed to " + this.get("foo"));
6112 // | });
6113 // | obj.set("foo","bar");
6114
6115 // _attrPairNames: Hash
6116 // Used across all instances a hash to cache attribute names and their getter
6117 // and setter names.
6118 _attrPairNames: {},
6119
6120 _getAttrNames: function(name){
6121 // summary:
6122 // Helper function for get() and set().
6123 // Caches attribute name values so we don't do the string ops every time.
6124 // tags:
6125 // private
6126
6127 var apn = this._attrPairNames;
6128 if(apn[name]){ return apn[name]; }
6129 return (apn[name] = {
6130 s: "_" + name + "Setter",
6131 g: "_" + name + "Getter"
6132 });
6133 },
6134
6135 postscript: function(/*Object?*/ params){
6136 // Automatic setting of params during construction
6137 if (params){ this.set(params); }
6138 },
6139
6140 _get: function(name, names){
6141 // summary:
6142 // Private function that does a get based off a hash of names
6143 // names:
6144 // Hash of names of custom attributes
6145 return typeof this[names.g] === "function" ? this[names.g]() : this[name];
6146 },
6147 get: function(/*String*/name){
6148 // summary:
6149 // Get a property on a Stateful instance.
6150 // name:
6151 // The property to get.
6152 // returns:
6153 // The property value on this Stateful instance.
6154 // description:
6155 // Get a named property on a Stateful object. The property may
6156 // potentially be retrieved via a getter method in subclasses. In the base class
6157 // this just retrieves the object's property.
6158 // For example:
6159 // | stateful = new dojo.Stateful({foo: 3});
6160 // | stateful.get("foo") // returns 3
6161 // | stateful.foo // returns 3
6162
6163 return this._get(name, this._getAttrNames(name)); //Any
6164 },
6165 set: function(/*String*/name, /*Object*/value){
6166 // summary:
6167 // Set a property on a Stateful instance
6168 // name:
6169 // The property to set.
6170 // value:
6171 // The value to set in the property.
6172 // returns:
6173 // The function returns this dojo.Stateful instance.
6174 // description:
6175 // Sets named properties on a stateful object and notifies any watchers of
6176 // the property. A programmatic setter may be defined in subclasses.
6177 // For example:
6178 // | stateful = new dojo.Stateful();
6179 // | stateful.watch(function(name, oldValue, value){
6180 // | // this will be called on the set below
6181 // | }
6182 // | stateful.set(foo, 5);
6183 //
6184 // set() may also be called with a hash of name/value pairs, ex:
6185 // | myObj.set({
6186 // | foo: "Howdy",
6187 // | bar: 3
6188 // | })
6189 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
6190
6191 // If an object is used, iterate through object
6192 if(typeof name === "object"){
6193 for(var x in name){
6194 if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
6195 this.set(x, name[x]);
6196 }
6197 }
6198 return this;
6199 }
6200
6201 var names = this._getAttrNames(name),
6202 oldValue = this._get(name, names),
6203 setter = this[names.s],
6204 result;
6205 if(typeof setter === "function"){
6206 // use the explicit setter
6207 result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
6208 }else{
6209 // no setter so set attribute directly
6210 this[name] = value;
6211 }
6212 if(this._watchCallbacks){
6213 var self = this;
6214 // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
6215 when(result, function(){
6216 self._watchCallbacks(name, oldValue, value);
6217 });
6218 }
6219 return this; // dojo/Stateful
6220 },
6221 _changeAttrValue: function(name, value){
6222 // summary:
6223 // Internal helper for directly changing an attribute value.
6224 //
6225 // name: String
6226 // The property to set.
6227 // value: Mixed
6228 // The value to set in the property.
6229 //
6230 // description:
6231 // Directly change the value of an attribute on an object, bypassing any
6232 // accessor setter. Also handles the calling of watch and emitting events.
6233 // It is designed to be used by descendent class when there are two values
6234 // of attributes that are linked, but calling .set() is not appropriate.
6235
6236 var oldValue = this.get(name);
6237 this[name] = value;
6238 if(this._watchCallbacks){
6239 this._watchCallbacks(name, oldValue, value);
6240 }
6241 return this; // dojo/Stateful
6242 },
6243 watch: function(/*String?*/name, /*Function*/callback){
6244 // summary:
6245 // Watches a property for changes
6246 // name:
6247 // Indicates the property to watch. This is optional (the callback may be the
6248 // only parameter), and if omitted, all the properties will be watched
6249 // returns:
6250 // An object handle for the watch. The unwatch method of this object
6251 // can be used to discontinue watching this property:
6252 // | var watchHandle = obj.watch("foo", callback);
6253 // | watchHandle.unwatch(); // callback won't be called now
6254 // callback:
6255 // The function to execute when the property changes. This will be called after
6256 // the property has been changed. The callback will be called with the |this|
6257 // set to the instance, the first argument as the name of the property, the
6258 // second argument as the old value and the third argument as the new value.
6259
6260 var callbacks = this._watchCallbacks;
6261 if(!callbacks){
6262 var self = this;
6263 callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
6264 var notify = function(propertyCallbacks){
6265 if(propertyCallbacks){
6266 propertyCallbacks = propertyCallbacks.slice();
6267 for(var i = 0, l = propertyCallbacks.length; i < l; i++){
6268 propertyCallbacks[i].call(self, name, oldValue, value);
6269 }
6270 }
6271 };
6272 notify(callbacks['_' + name]);
6273 if(!ignoreCatchall){
6274 notify(callbacks["*"]); // the catch-all
6275 }
6276 }; // we use a function instead of an object so it will be ignored by JSON conversion
6277 }
6278 if(!callback && typeof name === "function"){
6279 callback = name;
6280 name = "*";
6281 }else{
6282 // prepend with dash to prevent name conflicts with function (like "name" property)
6283 name = '_' + name;
6284 }
6285 var propertyCallbacks = callbacks[name];
6286 if(typeof propertyCallbacks !== "object"){
6287 propertyCallbacks = callbacks[name] = [];
6288 }
6289 propertyCallbacks.push(callback);
6290
6291 // TODO: Remove unwatch in 2.0
6292 var handle = {};
6293 handle.unwatch = handle.remove = function(){
6294 var index = array.indexOf(propertyCallbacks, callback);
6295 if(index > -1){
6296 propertyCallbacks.splice(index, 1);
6297 }
6298 };
6299 return handle; //Object
6300 }
6301
6302 });
6303
6304 });
6305
6306 },
6307 'dijit/layout/AccordionContainer':function(){
6308 require({cache:{
6309 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='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\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
6310 define("dijit/layout/AccordionContainer", [
6311 "require",
6312 "dojo/_base/array", // array.forEach array.map
6313 "dojo/_base/declare", // declare
6314 "dojo/_base/event", // event.stop
6315 "dojo/_base/fx", // fx.Animation
6316 "dojo/dom", // dom.setSelectable
6317 "dojo/dom-attr", // domAttr.attr
6318 "dojo/dom-class", // domClass.remove
6319 "dojo/dom-construct", // domConstruct.place
6320 "dojo/dom-geometry",
6321 "dojo/keys", // keys
6322 "dojo/_base/lang", // lang.getObject lang.hitch
6323 "dojo/sniff", // has("ie") has("dijit-legacy-requires")
6324 "dojo/topic", // publish
6325 "../focus", // focus.focus()
6326 "../_base/manager", // manager.defaultDuration
6327 "dojo/ready",
6328 "../_Widget",
6329 "../_Container",
6330 "../_TemplatedMixin",
6331 "../_CssStateMixin",
6332 "./StackContainer",
6333 "./ContentPane",
6334 "dojo/text!./templates/AccordionButton.html"
6335 ], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
6336 keys, lang, has, topic, focus, manager, ready,
6337 _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
6338
6339 // module:
6340 // dijit/layout/AccordionContainer
6341
6342
6343 // Design notes:
6344 //
6345 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
6346 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
6347 //
6348 // The resulting markup will look like:
6349 //
6350 // <div class=dijitAccordionContainer>
6351 // <div class=dijitAccordionInnerContainer> (one pane)
6352 // <div class=dijitAccordionTitle> (title bar) ... </div>
6353 // <div class=dijtAccordionChildWrapper> (content pane) </div>
6354 // </div>
6355 // </div>
6356 //
6357 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
6358 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
6359 // which on claro has a 1px border plus a 2px bottom margin.
6360 //
6361 // During animation there are two dijtAccordionChildWrapper's shown, so we need
6362 // to compensate for that.
6363
6364
6365 var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
6366 // summary:
6367 // The title bar to click to open up an accordion pane.
6368 // Internal widget used by AccordionContainer.
6369 // tags:
6370 // private
6371
6372 templateString: template,
6373
6374 // label: String
6375 // Title of the pane
6376 label: "",
6377 _setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
6378
6379 // title: String
6380 // Tooltip that appears on hover
6381 title: "",
6382 _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
6383
6384 // iconClassAttr: String
6385 // CSS class for icon to left of label
6386 iconClassAttr: "",
6387 _setIconClassAttr: { node: "iconNode", type: "class" },
6388
6389 baseClass: "dijitAccordionTitle",
6390
6391 getParent: function(){
6392 // summary:
6393 // Returns the AccordionContainer parent.
6394 // tags:
6395 // private
6396 return this.parent;
6397 },
6398
6399 buildRendering: function(){
6400 this.inherited(arguments);
6401 var titleTextNodeId = this.id.replace(' ','_');
6402 domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
6403 this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
6404 dom.setSelectable(this.domNode, false);
6405 },
6406
6407 getTitleHeight: function(){
6408 // summary:
6409 // Returns the height of the title dom node.
6410 return domGeometry.getMarginSize(this.domNode).h; // Integer
6411 },
6412
6413 // TODO: maybe the parent should set these methods directly rather than forcing the code
6414 // into the button widget?
6415 _onTitleClick: function(){
6416 // summary:
6417 // Callback when someone clicks my title.
6418 var parent = this.getParent();
6419 parent.selectChild(this.contentWidget, true);
6420 focus.focus(this.focusNode);
6421 },
6422
6423 _onTitleKeyPress: function(/*Event*/ evt){
6424 return this.getParent()._onKeyPress(evt, this.contentWidget);
6425 },
6426
6427 _setSelectedAttr: function(/*Boolean*/ isSelected){
6428 this._set("selected", isSelected);
6429 this.focusNode.setAttribute("aria-expanded", isSelected ? "true" : "false");
6430 this.focusNode.setAttribute("aria-selected", isSelected ? "true" : "false");
6431 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
6432 }
6433 });
6434
6435 var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
6436 // summary:
6437 // Internal widget placed as direct child of AccordionContainer.containerNode.
6438 // When other widgets are added as children to an AccordionContainer they are wrapped in
6439 // this widget.
6440
6441 /*=====
6442 // buttonWidget: Function|String
6443 // Class to use to instantiate title
6444 // (Wish we didn't have a separate widget for just the title but maintaining it
6445 // for backwards compatibility, is it worth it?)
6446 buttonWidget: null,
6447 =====*/
6448
6449 /*=====
6450 // contentWidget: dijit/_WidgetBase
6451 // Pointer to the real child widget
6452 contentWidget: null,
6453 =====*/
6454
6455 baseClass: "dijitAccordionInnerContainer",
6456
6457 // tell nested layout widget that we will take care of sizing
6458 isLayoutContainer: true,
6459
6460 buildRendering: function(){
6461 // Builds a template like:
6462 // <div class=dijitAccordionInnerContainer>
6463 // Button
6464 // <div class=dijitAccordionChildWrapper>
6465 // ContentPane
6466 // </div>
6467 // </div>
6468
6469 // Create wrapper div, placed where the child is now
6470 this.domNode = domConstruct.place("<div class='" + this.baseClass +
6471 "' role='presentation'>", this.contentWidget.domNode, "after");
6472
6473 // wrapper div's first child is the button widget (ie, the title bar)
6474 var child = this.contentWidget,
6475 cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
6476 this.button = child._buttonWidget = (new cls({
6477 contentWidget: child,
6478 label: child.title,
6479 title: child.tooltip,
6480 dir: child.dir,
6481 lang: child.lang,
6482 textDir: child.textDir,
6483 iconClass: child.iconClass,
6484 id: child.id + "_button",
6485 parent: this.parent
6486 })).placeAt(this.domNode);
6487
6488 // and then the actual content widget (changing it from prior-sibling to last-child),
6489 // wrapped by a <div class=dijitAccordionChildWrapper>
6490 this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
6491 domConstruct.place(this.contentWidget.domNode, this.containerNode);
6492 },
6493
6494 postCreate: function(){
6495 this.inherited(arguments);
6496
6497 // Map changes in content widget's title etc. to changes in the button
6498 var button = this.button;
6499 this._contentWidgetWatches = [
6500 this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
6501 button.set("label", newValue);
6502 })),
6503 this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
6504 button.set("title", newValue);
6505 })),
6506 this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
6507 button.set("iconClass", newValue);
6508 }))
6509 ];
6510 },
6511
6512 _setSelectedAttr: function(/*Boolean*/ isSelected){
6513 this._set("selected", isSelected);
6514 this.button.set("selected", isSelected);
6515 if(isSelected){
6516 var cw = this.contentWidget;
6517 if(cw.onSelected){ cw.onSelected(); }
6518 }
6519 },
6520
6521 startup: function(){
6522 // Called by _Container.addChild()
6523 this.contentWidget.startup();
6524 },
6525
6526 destroy: function(){
6527 this.button.destroyRecursive();
6528
6529 array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
6530
6531 delete this.contentWidget._buttonWidget;
6532 delete this.contentWidget._wrapperWidget;
6533
6534 this.inherited(arguments);
6535 },
6536
6537 destroyDescendants: function(/*Boolean*/ preserveDom){
6538 // since getChildren isn't working for me, have to code this manually
6539 this.contentWidget.destroyRecursive(preserveDom);
6540 }
6541 });
6542
6543 var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
6544 // summary:
6545 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
6546 // and switching between panes is visualized by sliding the other panes up/down.
6547 // example:
6548 // | <div data-dojo-type="dijit/layout/AccordionContainer">
6549 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
6550 // | </div>
6551 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
6552 // | <p>This is some text</p>
6553 // | </div>
6554 // | </div>
6555
6556 // duration: Integer
6557 // Amount of time (in ms) it takes to slide panes
6558 duration: manager.defaultDuration,
6559
6560 // buttonWidget: [const] String
6561 // The name of the widget used to display the title of each pane
6562 buttonWidget: AccordionButton,
6563
6564 /*=====
6565 // _verticalSpace: Number
6566 // Pixels of space available for the open pane
6567 // (my content box size minus the cumulative size of all the title bars)
6568 _verticalSpace: 0,
6569 =====*/
6570 baseClass: "dijitAccordionContainer",
6571
6572 buildRendering: function(){
6573 this.inherited(arguments);
6574 this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
6575 this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
6576 },
6577
6578 startup: function(){
6579 if(this._started){ return; }
6580 this.inherited(arguments);
6581 if(this.selectedChildWidget){
6582 this.selectedChildWidget._wrapperWidget.set("selected", true);
6583 }
6584 },
6585
6586 layout: function(){
6587 // Implement _LayoutWidget.layout() virtual method.
6588 // Set the height of the open pane based on what room remains.
6589
6590 var openPane = this.selectedChildWidget;
6591
6592 if(!openPane){ return;}
6593
6594 // space taken up by title, plus wrapper div (with border/margin) for open pane
6595 var wrapperDomNode = openPane._wrapperWidget.domNode,
6596 wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
6597 wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
6598 wrapperContainerNode = openPane._wrapperWidget.containerNode,
6599 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
6600 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
6601 mySize = this._contentBox;
6602
6603 // get cumulative height of all the unselected title bars
6604 var totalCollapsedHeight = 0;
6605 array.forEach(this.getChildren(), function(child){
6606 if(child != openPane){
6607 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
6608 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
6609 // margin below the bottom pane (even though we don't want one).
6610 totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
6611 }
6612 });
6613 this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
6614 - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
6615 - openPane._buttonWidget.getTitleHeight();
6616
6617 // Memo size to make displayed child
6618 this._containerContentBox = {
6619 h: this._verticalSpace,
6620 w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
6621 - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
6622 };
6623
6624 if(openPane){
6625 openPane.resize(this._containerContentBox);
6626 }
6627 },
6628
6629 _setupChild: function(child){
6630 // Overrides _LayoutWidget._setupChild().
6631 // Put wrapper widget around the child widget, showing title
6632
6633 child._wrapperWidget = AccordionInnerContainer({
6634 contentWidget: child,
6635 buttonWidget: this.buttonWidget,
6636 id: child.id + "_wrapper",
6637 dir: child.dir,
6638 lang: child.lang,
6639 textDir: child.textDir,
6640 parent: this
6641 });
6642
6643 this.inherited(arguments);
6644 },
6645
6646 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
6647 // Overrides _LayoutWidget.addChild().
6648 if(this._started){
6649 // Adding a child to a started Accordion is complicated because children have
6650 // wrapper widgets. Default code path (calling this.inherited()) would add
6651 // the new child inside another child's wrapper.
6652
6653 // First add in child as a direct child of this AccordionContainer
6654 var refNode = this.containerNode;
6655 if(insertIndex && typeof insertIndex == "number"){
6656 var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
6657 if(children && children.length >= insertIndex){
6658 refNode = children[insertIndex-1].domNode;
6659 insertIndex = "after";
6660 }
6661 }
6662 domConstruct.place(child.domNode, refNode, insertIndex);
6663
6664 if(!child._started){
6665 child.startup();
6666 }
6667
6668 // Then stick the wrapper widget around the child widget
6669 this._setupChild(child);
6670
6671 // Code below copied from StackContainer
6672 topic.publish(this.id+"-addChild", child, insertIndex); // publish
6673 this.layout();
6674 if(!this.selectedChildWidget){
6675 this.selectChild(child);
6676 }
6677 }else{
6678 // We haven't been started yet so just add in the child widget directly,
6679 // and the wrapper will be created on startup()
6680 this.inherited(arguments);
6681 }
6682 },
6683
6684 removeChild: function(child){
6685 // Overrides _LayoutWidget.removeChild().
6686
6687 // Destroy wrapper widget first, before StackContainer.getChildren() call.
6688 // Replace wrapper widget with true child widget (ContentPane etc.).
6689 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
6690 if(child._wrapperWidget){
6691 domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
6692 child._wrapperWidget.destroy();
6693 delete child._wrapperWidget;
6694 }
6695
6696 domClass.remove(child.domNode, "dijitHidden");
6697
6698 this.inherited(arguments);
6699 },
6700
6701 getChildren: function(){
6702 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
6703 return array.map(this.inherited(arguments), function(child){
6704 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
6705 }, this);
6706 },
6707
6708 destroy: function(){
6709 if(this._animation){
6710 this._animation.stop();
6711 }
6712 array.forEach(this.getChildren(), function(child){
6713 // If AccordionContainer has been started, then each child has a wrapper widget which
6714 // also needs to be destroyed.
6715 if(child._wrapperWidget){
6716 child._wrapperWidget.destroy();
6717 }else{
6718 child.destroyRecursive();
6719 }
6720 });
6721 this.inherited(arguments);
6722 },
6723
6724 _showChild: function(child){
6725 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6726 child._wrapperWidget.containerNode.style.display="block";
6727 return this.inherited(arguments);
6728 },
6729
6730 _hideChild: function(child){
6731 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6732 child._wrapperWidget.containerNode.style.display="none";
6733 this.inherited(arguments);
6734 },
6735
6736 _transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate){
6737 // Overrides StackContainer._transition() to provide sliding of title bars etc.
6738
6739 if(has("ie") < 8){
6740 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
6741 animate = false;
6742 }
6743
6744 if(this._animation){
6745 // there's an in-progress animation. speedily end it so we can do the newly requested one
6746 this._animation.stop(true);
6747 delete this._animation;
6748 }
6749
6750 var self = this;
6751
6752 if(newWidget){
6753 newWidget._wrapperWidget.set("selected", true);
6754
6755 var d = this._showChild(newWidget); // prepare widget to be slid in
6756
6757 // Size the new widget, in case this is the first time it's being shown,
6758 // or I have been resized since the last time it was shown.
6759 // Note that page must be visible for resizing to work.
6760 if(this.doLayout && newWidget.resize){
6761 newWidget.resize(this._containerContentBox);
6762 }
6763 }
6764
6765 if(oldWidget){
6766 oldWidget._wrapperWidget.set("selected", false);
6767 if(!animate){
6768 this._hideChild(oldWidget);
6769 }
6770 }
6771
6772 if(animate){
6773 var newContents = newWidget._wrapperWidget.containerNode,
6774 oldContents = oldWidget._wrapperWidget.containerNode;
6775
6776 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
6777 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
6778 // Have to compensate for that by immediately shrinking the pane being closed.
6779 var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
6780 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
6781 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
6782 animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
6783
6784 oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
6785
6786 this._animation = new fx.Animation({
6787 node: newContents,
6788 duration: this.duration,
6789 curve: [1, this._verticalSpace - animationHeightOverhead - 1],
6790 onAnimate: function(value){
6791 value = Math.floor(value); // avoid fractional values
6792 newContents.style.height = value + "px";
6793 oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
6794 },
6795 onEnd: function(){
6796 delete self._animation;
6797 newContents.style.height = "auto";
6798 oldWidget._wrapperWidget.containerNode.style.display = "none";
6799 oldContents.style.height = "auto";
6800 self._hideChild(oldWidget);
6801 }
6802 });
6803 this._animation.onStop = this._animation.onEnd;
6804 this._animation.play();
6805 }
6806
6807 return d; // If child has an href, promise that fires when the widget has finished loading
6808 },
6809
6810 // note: we are treating the container as controller here
6811 _onKeyPress: function(/*Event*/ e, /*dijit/_WidgetBase*/ fromTitle){
6812 // summary:
6813 // Handle keypress events
6814 // description:
6815 // This is called from a handler on AccordionContainer.domNode
6816 // (setup in StackContainer), and is also called directly from
6817 // the click handler for accordion labels
6818 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
6819 return;
6820 }
6821 var c = e.charOrCode;
6822 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
6823 (e.ctrlKey && c == keys.PAGE_UP)){
6824 this._adjacent(false)._buttonWidget._onTitleClick();
6825 event.stop(e);
6826 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
6827 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
6828 this._adjacent(true)._buttonWidget._onTitleClick();
6829 event.stop(e);
6830 }
6831 }
6832 });
6833
6834 // Back compat w/1.6, remove for 2.0
6835 if(has("dijit-legacy-requires")){
6836 ready(0, function(){
6837 var requires = ["dijit/layout/AccordionPane"];
6838 require(requires); // use indirection so modules not rolled into a build
6839 });
6840 }
6841
6842 // For monkey patching
6843 AccordionContainer._InnerContainer = AccordionInnerContainer;
6844 AccordionContainer._Button = AccordionButton;
6845
6846 return AccordionContainer;
6847 });
6848
6849 },
6850 'dijit/form/ComboButton':function(){
6851 require({cache:{
6852 'url: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\" data-dojo-attach-point=\"buttonNode\" data-dojo-attach-event=\"ondijitclick:_onClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" data-dojo-attach-point=\"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\tdata-dojo-attach-point=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdata-dojo-attach-event=\"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}\" data-dojo-attach-point=\"valueNode\" role=\"presentation\"\n\t\t/></td></tr></tbody\n></table>\n"}});
6853 define("dijit/form/ComboButton", [
6854 "dojo/_base/declare", // declare
6855 "dojo/_base/event", // event.stop
6856 "dojo/keys", // keys
6857 "../focus", // focus.focus()
6858 "./DropDownButton",
6859 "dojo/text!./templates/ComboButton.html"
6860 ], function(declare, event, keys, focus, DropDownButton, template){
6861
6862 // module:
6863 // dijit/form/ComboButton
6864
6865 return declare("dijit.form.ComboButton", DropDownButton, {
6866 // summary:
6867 // A combination button and drop-down button.
6868 // Users can click one side to "press" the button, or click an arrow
6869 // icon to display the drop down.
6870 //
6871 // example:
6872 // | <button data-dojo-type="dijit/form/ComboButton" onClick="...">
6873 // | <span>Hello world</span>
6874 // | <div data-dojo-type="dijit/Menu">...</div>
6875 // | </button>
6876 //
6877 // example:
6878 // | var button1 = new ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
6879 // | dojo.body().appendChild(button1.domNode);
6880 //
6881
6882 templateString: template,
6883
6884 // Map widget attributes to DOMNode attributes.
6885 _setIdAttr: "", // override _FormWidgetMixin which puts id on the focusNode
6886 _setTabIndexAttr: ["focusNode", "titleNode"],
6887 _setTitleAttr: "titleNode",
6888
6889 // optionsTitle: String
6890 // Text that describes the options menu (accessibility)
6891 optionsTitle: "",
6892
6893 baseClass: "dijitComboButton",
6894
6895 // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
6896 // mouse action over specified node
6897 cssStateNodes: {
6898 "buttonNode": "dijitButtonNode",
6899 "titleNode": "dijitButtonContents",
6900 "_popupStateNode": "dijitDownArrowButton"
6901 },
6902
6903 _focusedNode: null,
6904
6905 _onButtonKeyPress: function(/*Event*/ evt){
6906 // summary:
6907 // Handler for right arrow key when focus is on left part of button
6908 if(evt.charOrCode == keys[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
6909 focus.focus(this._popupStateNode);
6910 event.stop(evt);
6911 }
6912 },
6913
6914 _onArrowKeyPress: function(/*Event*/ evt){
6915 // summary:
6916 // Handler for left arrow key when focus is on right part of button
6917 if(evt.charOrCode == keys[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
6918 focus.focus(this.titleNode);
6919 event.stop(evt);
6920 }
6921 },
6922
6923 focus: function(/*String*/ position){
6924 // summary:
6925 // Focuses this widget to according to position, if specified,
6926 // otherwise on arrow node
6927 // position:
6928 // "start" or "end"
6929 if(!this.disabled){
6930 focus.focus(position == "start" ? this.titleNode : this._popupStateNode);
6931 }
6932 }
6933 });
6934
6935 });
6936
6937 },
6938 'dijit/form/_AutoCompleterMixin':function(){
6939 define("dijit/form/_AutoCompleterMixin", [
6940 "dojo/data/util/filter", // patternToRegExp
6941 "dojo/_base/declare", // declare
6942 "dojo/dom-attr", // domAttr.get
6943 "dojo/_base/event", // event.stop
6944 "dojo/keys",
6945 "dojo/_base/lang", // lang.clone lang.hitch
6946 "dojo/query", // query
6947 "dojo/regexp", // regexp.escapeString
6948 "dojo/sniff", // has("ie")
6949 "dojo/string", // string.substitute
6950 "./DataList",
6951 "../registry", // registry.byId
6952 "./_TextBoxMixin", // defines _TextBoxMixin.selectInputText
6953 "./_SearchMixin"
6954 ], function(filter, declare, domAttr, event, keys, lang, query, regexp, has, string,
6955 DataList, registry, _TextBoxMixin, SearchMixin){
6956
6957 // module:
6958 // dijit/form/_AutoCompleterMixin
6959
6960 return declare("dijit.form._AutoCompleterMixin", SearchMixin, {
6961 // summary:
6962 // A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect`
6963 // description:
6964 // All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`.
6965 // tags:
6966 // protected
6967
6968 // item: Object
6969 // This is the item returned by the dojo/store/api/Store implementation that
6970 // provides the data for this ComboBox, it's the currently selected item.
6971 item: null,
6972
6973 // autoComplete: Boolean
6974 // If user types in a partial string, and then tab out of the `<input>` box,
6975 // automatically copy the first entry displayed in the drop down list to
6976 // the `<input>` field
6977 autoComplete: true,
6978
6979 // highlightMatch: String
6980 // One of: "first", "all" or "none".
6981 //
6982 // If the ComboBox/FilteringSelect opens with the search results and the searched
6983 // string can be found, it will be highlighted. If set to "all"
6984 // then will probably want to change `queryExpr` parameter to '*${0}*'
6985 //
6986 // Highlighting is only performed when `labelType` is "text", so as to not
6987 // interfere with any HTML markup an HTML label might contain.
6988 highlightMatch: "first",
6989
6990 // labelAttr: String?
6991 // The entries in the drop down list come from this attribute in the
6992 // dojo.data items.
6993 // If not specified, the searchAttr attribute is used instead.
6994 labelAttr: "",
6995
6996 // labelType: String
6997 // Specifies how to interpret the labelAttr in the data store items.
6998 // Can be "html" or "text".
6999 labelType: "text",
7000
7001 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
7002 maxHeight: -1,
7003
7004 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
7005 _stopClickEvents: false,
7006
7007 _getCaretPos: function(/*DomNode*/ element){
7008 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
7009 var pos = 0;
7010 if(typeof(element.selectionStart) == "number"){
7011 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
7012 pos = element.selectionStart;
7013 }else if(has("ie")){
7014 // in the case of a mouse click in a popup being handled,
7015 // then the win.doc.selection is not the textarea, but the popup
7016 // var r = win.doc.selection.createRange();
7017 // hack to get IE 6 to play nice. What a POS browser.
7018 var tr = element.ownerDocument.selection.createRange().duplicate();
7019 var ntr = element.createTextRange();
7020 tr.move("character",0);
7021 ntr.move("character",0);
7022 try{
7023 // If control doesn't have focus, you get an exception.
7024 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
7025 // There appears to be no workaround for this - googled for quite a while.
7026 ntr.setEndPoint("EndToEnd", tr);
7027 pos = String(ntr.text).replace(/\r/g,"").length;
7028 }catch(e){
7029 // If focus has shifted, 0 is fine for caret pos.
7030 }
7031 }
7032 return pos;
7033 },
7034
7035 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
7036 location = parseInt(location);
7037 _TextBoxMixin.selectInputText(element, location, location);
7038 },
7039
7040 _setDisabledAttr: function(/*Boolean*/ value){
7041 // Additional code to set disabled state of ComboBox node.
7042 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
7043 this.inherited(arguments);
7044 this.domNode.setAttribute("aria-disabled", value ? "true" : "false");
7045 },
7046
7047 _onKey: function(/*Event*/ evt){
7048 // summary:
7049 // Handles keyboard events
7050
7051 if(evt.charCode >= 32){ return; } // alphanumeric reserved for searching
7052
7053 var key = evt.charCode || evt.keyCode;
7054
7055 // except for cutting/pasting case - ctrl + x/v
7056 if(key == keys.ALT || key == keys.CTRL || key == keys.META || key == keys.SHIFT){
7057 return; // throw out spurious events
7058 }
7059
7060 var pw = this.dropDown;
7061 var highlighted = null;
7062 this._abortQuery();
7063
7064 // _HasDropDown will do some of the work:
7065 //
7066 // 1. when drop down is not yet shown:
7067 // - if user presses the down arrow key, call loadDropDown()
7068 // 2. when drop down is already displayed:
7069 // - on ESC key, call closeDropDown()
7070 // - otherwise, call dropDown.handleKey() to process the keystroke
7071 this.inherited(arguments);
7072
7073 if(evt.altKey || evt.ctrlKey || evt.metaKey){ return; } // don't process keys with modifiers - but we want shift+TAB
7074
7075 if(this._opened){
7076 highlighted = pw.getHighlightedOption();
7077 }
7078 switch(key){
7079 case keys.PAGE_DOWN:
7080 case keys.DOWN_ARROW:
7081 case keys.PAGE_UP:
7082 case keys.UP_ARROW:
7083 // Keystroke caused ComboBox_menu to move to a different item.
7084 // Copy new item to <input> box.
7085 if(this._opened){
7086 this._announceOption(highlighted);
7087 }
7088 event.stop(evt);
7089 break;
7090
7091 case keys.ENTER:
7092 // prevent submitting form if user presses enter. Also
7093 // prevent accepting the value if either Next or Previous
7094 // are selected
7095 if(highlighted){
7096 // only stop event on prev/next
7097 if(highlighted == pw.nextButton){
7098 this._nextSearch(1);
7099 event.stop(evt); // prevent submit
7100 break;
7101 }else if(highlighted == pw.previousButton){
7102 this._nextSearch(-1);
7103 event.stop(evt); // prevent submit
7104 break;
7105 }
7106 event.stop(evt); // prevent submit if ENTER was to choose an item
7107 }else{
7108 // Update 'value' (ex: KY) according to currently displayed text
7109 this._setBlurValue(); // set value if needed
7110 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
7111 }
7112 // fall through
7113
7114 case keys.TAB:
7115 var newvalue = this.get('displayedValue');
7116 // if the user had More Choices selected fall into the
7117 // _onBlur handler
7118 if(pw && (
7119 newvalue == pw._messages["previousMessage"] ||
7120 newvalue == pw._messages["nextMessage"])
7121 ){
7122 break;
7123 }
7124 if(highlighted){
7125 this._selectOption(highlighted);
7126 }
7127 // fall through
7128
7129 case keys.ESCAPE:
7130 if(this._opened){
7131 this._lastQuery = null; // in case results come back later
7132 this.closeDropDown();
7133 }
7134 break;
7135 }
7136 },
7137
7138 _autoCompleteText: function(/*String*/ text){
7139 // summary:
7140 // Fill in the textbox with the first item from the drop down
7141 // list, and highlight the characters that were
7142 // auto-completed. For example, if user typed "CA" and the
7143 // drop down list appeared, the textbox would be changed to
7144 // "California" and "ifornia" would be highlighted.
7145
7146 var fn = this.focusNode;
7147
7148 // IE7: clear selection so next highlight works all the time
7149 _TextBoxMixin.selectInputText(fn, fn.value.length);
7150 // does text autoComplete the value in the textbox?
7151 var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
7152 if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
7153 var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length;
7154 // only try to extend if we added the last character at the end of the input
7155 if((cpos+1) > fn.value.length){
7156 // only add to input node as we would overwrite Capitalisation of chars
7157 // actually, that is ok
7158 fn.value = text;//.substr(cpos);
7159 // visually highlight the autocompleted characters
7160 _TextBoxMixin.selectInputText(fn, cpos);
7161 }
7162 }else{
7163 // text does not autoComplete; replace the whole value and highlight
7164 fn.value = text;
7165 _TextBoxMixin.selectInputText(fn);
7166 }
7167 },
7168
7169 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
7170 // summary:
7171 // Callback when a search completes.
7172 // description:
7173 // 1. generates drop-down list and calls _showResultList() to display it
7174 // 2. if this result list is from user pressing "more choices"/"previous choices"
7175 // then tell screen reader to announce new option
7176 var wasSelected = this.dropDown.getHighlightedOption();
7177 this.dropDown.clearResultList();
7178 if(!results.length && options.start == 0){ // if no results and not just the previous choices button
7179 this.closeDropDown();
7180 return;
7181 }
7182 this._nextSearch = this.dropDown.onPage = lang.hitch(this, function(direction){
7183 results.nextPage(direction !== -1);
7184 this.focus();
7185 });
7186
7187 // Fill in the textbox with the first item from the drop down list,
7188 // and highlight the characters that were auto-completed. For
7189 // example, if user typed "CA" and the drop down list appeared, the
7190 // textbox would be changed to "California" and "ifornia" would be
7191 // highlighted.
7192
7193 this.dropDown.createOptions(
7194 results,
7195 options,
7196 lang.hitch(this, "_getMenuLabelFromItem")
7197 );
7198
7199 // show our list (only if we have content, else nothing)
7200 this._showResultList();
7201
7202 // #4091:
7203 // tell the screen reader that the paging callback finished by
7204 // shouting the next choice
7205 if("direction" in options){
7206 if(options.direction){
7207 this.dropDown.highlightFirstOption();
7208 }else if(!options.direction){
7209 this.dropDown.highlightLastOption();
7210 }
7211 if(wasSelected){
7212 this._announceOption(this.dropDown.getHighlightedOption());
7213 }
7214 }else if(this.autoComplete && !this._prev_key_backspace
7215 // when the user clicks the arrow button to show the full list,
7216 // startSearch looks for "*".
7217 // it does not make sense to autocomplete
7218 // if they are just previewing the options available.
7219 && !/^[*]+$/.test(query[this.searchAttr].toString())){
7220 this._announceOption(this.dropDown.containerNode.firstChild.nextSibling); // 1st real item
7221 }
7222 },
7223
7224 _showResultList: function(){
7225 // summary:
7226 // Display the drop down if not already displayed, or if it is displayed, then
7227 // reposition it if necessary (reposition may be necessary if drop down's height changed).
7228 this.closeDropDown(true);
7229 this.openDropDown();
7230 this.domNode.setAttribute("aria-expanded", "true");
7231 },
7232
7233 loadDropDown: function(/*Function*/ /*===== callback =====*/){
7234 // Overrides _HasDropDown.loadDropDown().
7235 // This is called when user has pressed button icon or pressed the down arrow key
7236 // to open the drop down.
7237 this._startSearchAll();
7238 },
7239
7240 isLoaded: function(){
7241 // signal to _HasDropDown that it needs to call loadDropDown() to load the
7242 // drop down asynchronously before displaying it
7243 return false;
7244 },
7245
7246 closeDropDown: function(){
7247 // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
7248 // This method is the callback when the user types ESC or clicking
7249 // the button icon while the drop down is open. It's also called by other code.
7250 this._abortQuery();
7251 if(this._opened){
7252 this.inherited(arguments);
7253 this.domNode.setAttribute("aria-expanded", "false");
7254 this.focusNode.removeAttribute("aria-activedescendant");
7255 }
7256 },
7257
7258 _setBlurValue: function(){
7259 // if the user clicks away from the textbox OR tabs away, set the
7260 // value to the textbox value
7261 // #4617:
7262 // if value is now more choices or previous choices, revert
7263 // the value
7264 var newvalue = this.get('displayedValue');
7265 var pw = this.dropDown;
7266 if(pw && (
7267 newvalue == pw._messages["previousMessage"] ||
7268 newvalue == pw._messages["nextMessage"]
7269 )
7270 ){
7271 this._setValueAttr(this._lastValueReported, true);
7272 }else if(typeof this.item == "undefined"){
7273 // Update 'value' (ex: KY) according to currently displayed text
7274 this.item = null;
7275 this.set('displayedValue', newvalue);
7276 }else{
7277 if(this.value != this._lastValueReported){
7278 this._handleOnChange(this.value, true);
7279 }
7280 this._refreshState();
7281 }
7282 },
7283
7284 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
7285 // summary:
7286 // Set the displayed valued in the input box, and the hidden value
7287 // that gets submitted, based on a dojo.data store item.
7288 // description:
7289 // Users shouldn't call this function; they should be calling
7290 // set('item', value)
7291 // tags:
7292 // private
7293 var value = '';
7294 if(item){
7295 if(!displayedValue){
7296 displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
7297 this.store.getValue(item, this.searchAttr) : item[this.searchAttr];
7298 }
7299 value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue;
7300 }
7301 this.set('value', value, priorityChange, displayedValue, item);
7302 },
7303
7304 _announceOption: function(/*Node*/ node){
7305 // summary:
7306 // a11y code that puts the highlighted option in the textbox.
7307 // This way screen readers will know what is happening in the
7308 // menu.
7309
7310 if(!node){
7311 return;
7312 }
7313 // pull the text value from the item attached to the DOM node
7314 var newValue;
7315 if(node == this.dropDown.nextButton ||
7316 node == this.dropDown.previousButton){
7317 newValue = node.innerHTML;
7318 this.item = undefined;
7319 this.value = '';
7320 }else{
7321 var item = this.dropDown.items[node.getAttribute("item")];
7322 newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
7323 this.store.getValue(item, this.searchAttr) : item[this.searchAttr]).toString();
7324 this.set('item', item, false, newValue);
7325 }
7326 // get the text that the user manually entered (cut off autocompleted text)
7327 this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
7328 // set up ARIA activedescendant
7329 this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
7330 // autocomplete the rest of the option to announce change
7331 this._autoCompleteText(newValue);
7332 },
7333
7334 _selectOption: function(/*DomNode*/ target){
7335 // summary:
7336 // Menu callback function, called when an item in the menu is selected.
7337 this.closeDropDown();
7338 if(target){
7339 this._announceOption(target);
7340 }
7341 this._setCaretPos(this.focusNode, this.focusNode.value.length);
7342 this._handleOnChange(this.value, true);
7343 },
7344
7345 _startSearchAll: function(){
7346 this._startSearch('');
7347 },
7348
7349 _startSearchFromInput: function(){
7350 this.item = undefined; // undefined means item needs to be set
7351 this.inherited(arguments);
7352 },
7353
7354 _startSearch: function(/*String*/ key){
7355 // summary:
7356 // Starts a search for elements matching key (key=="" means to return all items),
7357 // and calls _openResultList() when the search completes, to display the results.
7358 if(!this.dropDown){
7359 var popupId = this.id + "_popup",
7360 dropDownConstructor = lang.isString(this.dropDownClass) ?
7361 lang.getObject(this.dropDownClass, false) : this.dropDownClass;
7362 this.dropDown = new dropDownConstructor({
7363 onChange: lang.hitch(this, this._selectOption),
7364 id: popupId,
7365 dir: this.dir,
7366 textDir: this.textDir
7367 });
7368 this.focusNode.removeAttribute("aria-activedescendant");
7369 this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox
7370 }
7371 this._lastInput = key; // Store exactly what was entered by the user.
7372 this.inherited(arguments);
7373 },
7374
7375 _getValueField: function(){
7376 // summary:
7377 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
7378 // Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value.
7379 return this.searchAttr;
7380 },
7381
7382 //////////// INITIALIZATION METHODS ///////////////////////////////////////
7383
7384 postMixInProperties: function(){
7385 this.inherited(arguments);
7386 if(!this.store){
7387 var srcNodeRef = this.srcNodeRef;
7388 // if user didn't specify store, then assume there are option tags
7389 this.store = new DataList({}, srcNodeRef);
7390
7391 // if there is no value set and there is an option list, set
7392 // the value to the first value to be consistent with native Select
7393 // Firefox and Safari set value
7394 // IE6 and Opera set selectedIndex, which is automatically set
7395 // by the selected attribute of an option tag
7396 // IE6 does not set value, Opera sets value = selectedIndex
7397 if(!("value" in this.params)){
7398 var item = (this.item = this.store.fetchSelectedItem());
7399 if(item){
7400 var valueField = this._getValueField();
7401 // remove getValue() for 2.0 (old dojo.data API)
7402 this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField];
7403 }
7404 }
7405 }
7406 },
7407
7408 postCreate: function(){
7409 // summary:
7410 // Subclasses must call this method from their postCreate() methods
7411 // tags:
7412 // protected
7413
7414 // find any associated label element and add to ComboBox node.
7415 var label=query('label[for="'+this.id+'"]');
7416 if(label.length){
7417 if(!label[0].id){ label[0].id = this.id + "_label"; }
7418 this.domNode.setAttribute("aria-labelledby", label[0].id);
7419
7420 }
7421 this.inherited(arguments);
7422 this.connect(this, "onSearch", "_openResultList");
7423 },
7424
7425 _getMenuLabelFromItem: function(/*Item*/ item){
7426 var label = this.labelFunc(item, this.store),
7427 labelType = this.labelType;
7428 // If labelType is not "text" we don't want to screw any markup ot whatever.
7429 if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
7430 label = this.doHighlight(label, this._lastInput);
7431 labelType = "html";
7432 }
7433 return {html: labelType == "html", label: label};
7434 },
7435
7436 doHighlight: function(/*String*/ label, /*String*/ find){
7437 // summary:
7438 // Highlights the string entered by the user in the menu. By default this
7439 // highlights the first occurrence found. Override this method
7440 // to implement your custom highlighting.
7441 // tags:
7442 // protected
7443
7444 var
7445 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
7446 modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
7447 i = this.queryExpr.indexOf("${0}");
7448 find = regexp.escapeString(find); // escape regexp special chars
7449 //If < appears in label, and user presses t, we don't want to highlight the t in the escaped "&lt;"
7450 //first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which
7451 //should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the
7452 //HTML highlight markup.
7453 return this._escapeHtml(label.replace(
7454 new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
7455 '\uFFFF$1\uFFFF')).replace(
7456 /\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>'
7457 ); // returns String, (almost) valid HTML (entities encoded)
7458 },
7459
7460 _escapeHtml: function(/*String*/ str){
7461 // TODO Should become dojo.html.entities(), when exists use instead
7462 // summary:
7463 // Adds escape sequences for special characters in XML: `&<>"'`
7464 str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
7465 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); //balance"
7466 return str; // string
7467 },
7468
7469 reset: function(){
7470 // Overrides the _FormWidget.reset().
7471 // Additionally reset the .item (to clean up).
7472 this.item = null;
7473 this.inherited(arguments);
7474 },
7475
7476 labelFunc: function(item, store){
7477 // summary:
7478 // Computes the label to display based on the dojo.data store item.
7479 // item: Object
7480 // The item from the store
7481 // store: dojo/store/api/Store
7482 // The store.
7483 // returns:
7484 // The label that the ComboBox should display
7485 // tags:
7486 // private
7487
7488 // Use toString() because XMLStore returns an XMLItem whereas this
7489 // method is expected to return a String (#9354).
7490 // Remove getValue() for 2.0 (old dojo.data API)
7491 return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) :
7492 item[this.labelAttr || this.searchAttr]).toString(); // String
7493 },
7494
7495 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
7496 // summary:
7497 // Hook so set('value', value) works.
7498 // description:
7499 // Sets the value of the select.
7500 this._set("item", item||null); // value not looked up in store
7501 if(value == null /* or undefined */){ value = ''; } // null translates to blank
7502 this.inherited(arguments);
7503 },
7504 _setTextDirAttr: function(/*String*/ textDir){
7505 // summary:
7506 // Setter for textDir, needed for the dropDown's textDir update.
7507 // description:
7508 // Users shouldn't call this function; they should be calling
7509 // set('textDir', value)
7510 // tags:
7511 // private
7512 this.inherited(arguments);
7513 // update the drop down also (_ComboBoxMenuMixin)
7514 if(this.dropDown){
7515 this.dropDown._set("textDir", textDir);
7516 }
7517 }
7518 });
7519 });
7520
7521 },
7522 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n",
7523 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>",
7524 'dijit/form/MappedTextBox':function(){
7525 define("dijit/form/MappedTextBox", [
7526 "dojo/_base/declare", // declare
7527 "dojo/dom-construct", // domConstruct.place
7528 "./ValidationTextBox"
7529 ], function(declare, domConstruct, ValidationTextBox){
7530
7531 // module:
7532 // dijit/form/MappedTextBox
7533
7534 return declare("dijit.form.MappedTextBox", ValidationTextBox, {
7535 // summary:
7536 // A dijit/form/ValidationTextBox subclass which provides a base class for widgets that have
7537 // a visible formatted display value, and a serializable
7538 // value in a hidden input field which is actually sent to the server.
7539 // description:
7540 // The visible display may
7541 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
7542 // input field which uses the `name` attribute declared by the original widget. That value sent
7543 // to the server is defined by the dijit/form/MappedTextBox.serialize() method and is typically
7544 // locale-neutral.
7545 // tags:
7546 // protected
7547
7548 postMixInProperties: function(){
7549 this.inherited(arguments);
7550
7551 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
7552 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
7553 this.nameAttrSetting = "";
7554 },
7555
7556 // Override default behavior to assign name to focusNode
7557 _setNameAttr: null,
7558
7559 serialize: function(val /*=====, options =====*/){
7560 // summary:
7561 // Overridable function used to convert the get('value') result to a canonical
7562 // (non-localized) string. For example, will print dates in ISO format, and
7563 // numbers the same way as they are represented in javascript.
7564 // val: anything
7565 // options: Object?
7566 // tags:
7567 // protected extension
7568 return val.toString ? val.toString() : ""; // String
7569 },
7570
7571 toString: function(){
7572 // summary:
7573 // Returns widget as a printable string using the widget's value
7574 // tags:
7575 // protected
7576 var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
7577 return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
7578 },
7579
7580 validate: function(){
7581 // Overrides `dijit/form/TextBox.validate`
7582 this.valueNode.value = this.toString();
7583 return this.inherited(arguments);
7584 },
7585
7586 buildRendering: function(){
7587 // Overrides `dijit/_TemplatedMixin/buildRendering`
7588
7589 this.inherited(arguments);
7590
7591 // Create a hidden <input> node with the serialized value used for submit
7592 // (as opposed to the displayed value).
7593 // Passing in name as markup rather than calling domConstruct.create() with an attrs argument
7594 // to make query(input[name=...]) work on IE. (see #8660)
7595 this.valueNode = domConstruct.place("<input type='hidden'" + (this.name ? ' name="' + this.name.replace(/"/g, "&quot;") + '"' : "") + "/>", this.textbox, "after");
7596 },
7597
7598 reset: function(){
7599 // Overrides `dijit/form/ValidationTextBox.reset` to
7600 // reset the hidden textbox value to ''
7601 this.valueNode.value = '';
7602 this.inherited(arguments);
7603 }
7604 });
7605 });
7606
7607 },
7608 'dijit/form/ComboBoxMixin':function(){
7609 require({cache:{
7610 'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"button presentation\" aria-hidden=\"true\"\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\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"}});
7611 define("dijit/form/ComboBoxMixin", [
7612 "dojo/_base/declare", // declare
7613 "dojo/_base/Deferred",
7614 "dojo/_base/kernel", // kernel.deprecated
7615 "dojo/_base/lang", // lang.mixin
7616 "dojo/store/util/QueryResults",
7617 "./_AutoCompleterMixin",
7618 "./_ComboBoxMenu",
7619 "../_HasDropDown",
7620 "dojo/text!./templates/DropDownBox.html"
7621 ], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
7622
7623
7624 // module:
7625 // dijit/form/ComboBoxMixin
7626
7627 return declare("dijit.form.ComboBoxMixin", [_HasDropDown, _AutoCompleterMixin], {
7628 // summary:
7629 // Provides main functionality of ComboBox widget
7630
7631 // dropDownClass: [protected extension] Function String
7632 // Dropdown widget class used to select a date/time.
7633 // Subclasses should specify this.
7634 dropDownClass: _ComboBoxMenu,
7635
7636 // hasDownArrow: Boolean
7637 // Set this textbox to have a down arrow button, to display the drop down list.
7638 // Defaults to true.
7639 hasDownArrow: true,
7640
7641 templateString: template,
7642
7643 baseClass: "dijitTextBox dijitComboBox",
7644
7645 /*=====
7646 // store: [const] dojo/store/api/Store|dojo/data/api/Read
7647 // Reference to data provider object used by this ComboBox.
7648 //
7649 // Should be dojo/store/api/Store, but dojo/data/api/Read supported
7650 // for backwards compatibility.
7651 store: null,
7652 =====*/
7653
7654 // Set classes like dijitDownArrowButtonHover depending on
7655 // mouse action over button node
7656 cssStateNodes: {
7657 "_buttonNode": "dijitDownArrowButton"
7658 },
7659
7660 _setHasDownArrowAttr: function(/*Boolean*/ val){
7661 this._set("hasDownArrow", val);
7662 this._buttonNode.style.display = val ? "" : "none";
7663 },
7664
7665 _showResultList: function(){
7666 // hide the tooltip
7667 this.displayMessage("");
7668 this.inherited(arguments);
7669 },
7670
7671 _setStoreAttr: function(store){
7672 // For backwards-compatibility, accept dojo.data store in addition to dojo/store/api/Store. Remove in 2.0.
7673 if(!store.get){
7674 lang.mixin(store, {
7675 _oldAPI: true,
7676 get: function(id){
7677 // summary:
7678 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
7679 // Like dojo/store/DataStore.get() except returns native item.
7680 var deferred = new Deferred();
7681 this.fetchItemByIdentity({
7682 identity: id,
7683 onItem: function(object){
7684 deferred.resolve(object);
7685 },
7686 onError: function(error){
7687 deferred.reject(error);
7688 }
7689 });
7690 return deferred.promise;
7691 },
7692 query: function(query, options){
7693 // summary:
7694 // Queries the store for objects. Like dojo/store/DataStore.query()
7695 // except returned Deferred contains array of native items.
7696 var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
7697 deferred.total = new Deferred();
7698 var fetchHandle = this.fetch(lang.mixin({
7699 query: query,
7700 onBegin: function(count){
7701 deferred.total.resolve(count);
7702 },
7703 onComplete: function(results){
7704 deferred.resolve(results);
7705 },
7706 onError: function(error){
7707 deferred.reject(error);
7708 }
7709 }, options));
7710 return QueryResults(deferred);
7711 }
7712 });
7713 }
7714 this._set("store", store);
7715 },
7716
7717 postMixInProperties: function(){
7718 // Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
7719 // Unfortunately, without special code, it ends up executing second.
7720 var store = this.params.store || this.store;
7721 if(store){
7722 this._setStoreAttr(store);
7723 }
7724
7725 this.inherited(arguments);
7726
7727 // User may try to access this.store.getValue() etc. in a custom labelFunc() function.
7728 // It's not available with the new data store for handling inline <option> tags, so add it.
7729 if(!this.params.store && !this.store._oldAPI){
7730 var clazz = this.declaredClass;
7731 lang.mixin(this.store, {
7732 getValue: function(item, attr){
7733 kernel.deprecated(clazz + ".store.getValue(item, attr) is deprecated for builtin store. Use item.attr directly", "", "2.0");
7734 return item[attr];
7735 },
7736 getLabel: function(item){
7737 kernel.deprecated(clazz + ".store.getLabel(item) is deprecated for builtin store. Use item.label directly", "", "2.0");
7738 return item.name;
7739 },
7740 fetch: function(args){
7741 kernel.deprecated(clazz + ".store.fetch() is deprecated for builtin store.", "Use store.query()", "2.0");
7742 var shim = ["dojo/data/ObjectStore"]; // indirection so it doesn't get rolled into a build
7743 require(shim, lang.hitch(this, function(ObjectStore){
7744 new ObjectStore({objectStore: this}).fetch(args);
7745 }));
7746 }
7747 });
7748 }
7749 }
7750 });
7751 });
7752
7753 },
7754 'dijit/form/_TextBoxMixin':function(){
7755 define("dijit/form/_TextBoxMixin", [
7756 "dojo/_base/array", // array.forEach
7757 "dojo/_base/declare", // declare
7758 "dojo/dom", // dom.byId
7759 "dojo/_base/event", // event.stop
7760 "dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
7761 "dojo/_base/lang", // lang.mixin
7762 "dojo/on", // on
7763 "../main" // for exporting dijit._setSelectionRange, dijit.selectInputText
7764 ], function(array, declare, dom, event, keys, lang, on, dijit){
7765
7766 // module:
7767 // dijit/form/_TextBoxMixin
7768
7769 var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
7770 // summary:
7771 // A mixin for textbox form input widgets
7772
7773 // trim: Boolean
7774 // Removes leading and trailing whitespace if true. Default is false.
7775 trim: false,
7776
7777 // uppercase: Boolean
7778 // Converts all characters to uppercase if true. Default is false.
7779 uppercase: false,
7780
7781 // lowercase: Boolean
7782 // Converts all characters to lowercase if true. Default is false.
7783 lowercase: false,
7784
7785 // propercase: Boolean
7786 // Converts the first character of each word to uppercase if true.
7787 propercase: false,
7788
7789 // maxLength: String
7790 // HTML INPUT tag maxLength declaration.
7791 maxLength: "",
7792
7793 // selectOnClick: [const] Boolean
7794 // If true, all text will be selected when focused with mouse
7795 selectOnClick: false,
7796
7797 // placeHolder: String
7798 // Defines a hint to help users fill out the input field (as defined in HTML 5).
7799 // This should only contain plain text (no html markup).
7800 placeHolder: "",
7801
7802 _getValueAttr: function(){
7803 // summary:
7804 // Hook so get('value') works as we like.
7805 // description:
7806 // For `dijit/form/TextBox` this basically returns the value of the `<input>`.
7807 //
7808 // For `dijit/form/MappedTextBox` subclasses, which have both
7809 // a "displayed value" and a separate "submit value",
7810 // This treats the "displayed value" as the master value, computing the
7811 // submit value from it via this.parse().
7812 return this.parse(this.get('displayedValue'), this.constraints);
7813 },
7814
7815 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
7816 // summary:
7817 // Hook so set('value', ...) works.
7818 //
7819 // description:
7820 // Sets the value of the widget to "value" which can be of
7821 // any type as determined by the widget.
7822 //
7823 // value:
7824 // The visual element value is also set to a corresponding,
7825 // but not necessarily the same, value.
7826 //
7827 // formattedValue:
7828 // If specified, used to set the visual element value,
7829 // otherwise a computed visual value is used.
7830 //
7831 // priorityChange:
7832 // If true, an onChange event is fired immediately instead of
7833 // waiting for the next blur event.
7834
7835 var filteredValue;
7836 if(value !== undefined){
7837 // TODO: this is calling filter() on both the display value and the actual value.
7838 // I added a comment to the filter() definition about this, but it should be changed.
7839 filteredValue = this.filter(value);
7840 if(typeof formattedValue != "string"){
7841 if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
7842 formattedValue = this.filter(this.format(filteredValue, this.constraints));
7843 }else{ formattedValue = ''; }
7844 }
7845 }
7846 if(formattedValue != null /* and !undefined */ && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
7847 this.textbox.value = formattedValue;
7848 this._set("displayedValue", this.get("displayedValue"));
7849 }
7850
7851 if(this.textDir == "auto"){
7852 this.applyTextDir(this.focusNode, formattedValue);
7853 }
7854
7855 this.inherited(arguments, [filteredValue, priorityChange]);
7856 },
7857
7858 // displayedValue: String
7859 // For subclasses like ComboBox where the displayed value
7860 // (ex: Kentucky) and the serialized value (ex: KY) are different,
7861 // this represents the displayed value.
7862 //
7863 // Setting 'displayedValue' through set('displayedValue', ...)
7864 // updates 'value', and vice-versa. Otherwise 'value' is updated
7865 // from 'displayedValue' periodically, like onBlur etc.
7866 //
7867 // TODO: move declaration to MappedTextBox?
7868 // Problem is that ComboBox references displayedValue,
7869 // for benefit of FilteringSelect.
7870 displayedValue: "",
7871
7872 _getDisplayedValueAttr: function(){
7873 // summary:
7874 // Hook so get('displayedValue') works.
7875 // description:
7876 // Returns the displayed value (what the user sees on the screen),
7877 // after filtering (ie, trimming spaces etc.).
7878 //
7879 // For some subclasses of TextBox (like ComboBox), the displayed value
7880 // is different from the serialized value that's actually
7881 // sent to the server (see `dijit/form/ValidationTextBox.serialize()`)
7882
7883 // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
7884 // this method
7885 // TODO: this isn't really the displayed value when the user is typing
7886 return this.filter(this.textbox.value);
7887 },
7888
7889 _setDisplayedValueAttr: function(/*String*/ value){
7890 // summary:
7891 // Hook so set('displayedValue', ...) works.
7892 // description:
7893 // Sets the value of the visual element to the string "value".
7894 // The widget value is also set to a corresponding,
7895 // but not necessarily the same, value.
7896
7897 if(value == null /* or undefined */){ value = '' }
7898 else if(typeof value != "string"){ value = String(value) }
7899
7900 this.textbox.value = value;
7901
7902 // sets the serialized value to something corresponding to specified displayedValue
7903 // (if possible), and also updates the textbox.value, for example converting "123"
7904 // to "123.00"
7905 this._setValueAttr(this.get('value'), undefined);
7906
7907 this._set("displayedValue", this.get('displayedValue'));
7908
7909 // textDir support
7910 if(this.textDir == "auto"){
7911 this.applyTextDir(this.focusNode, value);
7912 }
7913 },
7914
7915 format: function(value /*=====, constraints =====*/){
7916 // summary:
7917 // Replaceable function to convert a value to a properly formatted string.
7918 // value: String
7919 // constraints: Object
7920 // tags:
7921 // protected extension
7922 return value == null /* or undefined */ ? "" : (value.toString ? value.toString() : value);
7923 },
7924
7925 parse: function(value /*=====, constraints =====*/){
7926 // summary:
7927 // Replaceable function to convert a formatted string to a value
7928 // value: String
7929 // constraints: Object
7930 // tags:
7931 // protected extension
7932
7933 return value; // String
7934 },
7935
7936 _refreshState: function(){
7937 // summary:
7938 // After the user types some characters, etc., this method is
7939 // called to check the field for validity etc. The base method
7940 // in `dijit/form/TextBox` does nothing, but subclasses override.
7941 // tags:
7942 // protected
7943 },
7944
7945 /*=====
7946 onInput: function(event){
7947 // summary:
7948 // Connect to this function to receive notifications of various user data-input events.
7949 // Return false to cancel the event and prevent it from being processed.
7950 // event:
7951 // keydown | keypress | cut | paste | input
7952 // tags:
7953 // callback
7954 },
7955 =====*/
7956 onInput: function(){},
7957
7958 __skipInputEvent: false,
7959 _onInput: function(/*Event*/ evt){
7960 // summary:
7961 // Called AFTER the input event has happened
7962
7963 // set text direction according to textDir that was defined in creation
7964 if(this.textDir == "auto"){
7965 this.applyTextDir(this.focusNode, this.focusNode.value);
7966 }
7967
7968 this._processInput(evt);
7969 },
7970
7971 _processInput: function(/*Event*/ evt){
7972 // summary:
7973 // Default action handler for user input events
7974
7975 this._refreshState();
7976
7977 // In case someone is watch()'ing for changes to displayedValue
7978 this._set("displayedValue", this.get("displayedValue"));
7979 },
7980
7981 postCreate: function(){
7982 // setting the value here is needed since value="" in the template causes "undefined"
7983 // and setting in the DOM (instead of the JS object) helps with form reset actions
7984 this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
7985
7986 this.inherited(arguments);
7987
7988 // normalize input events to reduce spurious event processing
7989 // onkeydown: do not forward modifier keys
7990 // set charOrCode to numeric keycode
7991 // onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
7992 // onpaste & oncut: set charOrCode to 229 (IME)
7993 // oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
7994 var handleEvent = function(e){
7995 var charOrCode;
7996 if(e.type == "keydown"){
7997 charOrCode = e.keyCode;
7998 switch(charOrCode){ // ignore state keys
7999 case keys.SHIFT:
8000 case keys.ALT:
8001 case keys.CTRL:
8002 case keys.META:
8003 case keys.CAPS_LOCK:
8004 case keys.NUM_LOCK:
8005 case keys.SCROLL_LOCK:
8006 return;
8007 }
8008 if(!e.ctrlKey && !e.metaKey && !e.altKey){ // no modifiers
8009 switch(charOrCode){ // ignore location keys
8010 case keys.NUMPAD_0:
8011 case keys.NUMPAD_1:
8012 case keys.NUMPAD_2:
8013 case keys.NUMPAD_3:
8014 case keys.NUMPAD_4:
8015 case keys.NUMPAD_5:
8016 case keys.NUMPAD_6:
8017 case keys.NUMPAD_7:
8018 case keys.NUMPAD_8:
8019 case keys.NUMPAD_9:
8020 case keys.NUMPAD_MULTIPLY:
8021 case keys.NUMPAD_PLUS:
8022 case keys.NUMPAD_ENTER:
8023 case keys.NUMPAD_MINUS:
8024 case keys.NUMPAD_PERIOD:
8025 case keys.NUMPAD_DIVIDE:
8026 return;
8027 }
8028 if((charOrCode >= 65 && charOrCode <= 90) || (charOrCode >= 48 && charOrCode <= 57) || charOrCode == keys.SPACE){
8029 return; // keypress will handle simple non-modified printable keys
8030 }
8031 var named = false;
8032 for(var i in keys){
8033 if(keys[i] === e.keyCode){
8034 named = true;
8035 break;
8036 }
8037 }
8038 if(!named){ return; } // only allow named ones through
8039 }
8040 }
8041 charOrCode = e.charCode >= 32 ? String.fromCharCode(e.charCode) : e.charCode;
8042 if(!charOrCode){
8043 charOrCode = (e.keyCode >= 65 && e.keyCode <= 90) || (e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode == keys.SPACE ? String.fromCharCode(e.keyCode) : e.keyCode;
8044 }
8045 if(!charOrCode){
8046 charOrCode = 229; // IME
8047 }
8048 if(e.type == "keypress"){
8049 if(typeof charOrCode != "string"){ return; }
8050 if((charOrCode >= 'a' && charOrCode <= 'z') || (charOrCode >= 'A' && charOrCode <= 'Z') || (charOrCode >= '0' && charOrCode <= '9') || (charOrCode === ' ')){
8051 if(e.ctrlKey || e.metaKey || e.altKey){ return; } // can only be stopped reliably in keydown
8052 }
8053 }
8054 if(e.type == "input"){
8055 if(this.__skipInputEvent){ // duplicate event
8056 this.__skipInputEvent = false;
8057 return;
8058 }
8059 }else{
8060 this.__skipInputEvent = true;
8061 }
8062 // create fake event to set charOrCode and to know if preventDefault() was called
8063 var faux = { faux: true }, attr;
8064 for(attr in e){
8065 if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
8066 var v = e[attr];
8067 if(typeof v != "function" && typeof v != "undefined"){ faux[attr] = v; }
8068 }
8069 }
8070 lang.mixin(faux, {
8071 charOrCode: charOrCode,
8072 _wasConsumed: false,
8073 preventDefault: function(){
8074 faux._wasConsumed = true;
8075 e.preventDefault();
8076 },
8077 stopPropagation: function(){ e.stopPropagation(); }
8078 });
8079 // give web page author a chance to consume the event
8080 //console.log(faux.type + ', charOrCode = (' + (typeof charOrCode) + ') ' + charOrCode + ', ctrl ' + !!faux.ctrlKey + ', alt ' + !!faux.altKey + ', meta ' + !!faux.metaKey + ', shift ' + !!faux.shiftKey);
8081 if(this.onInput(faux) === false){ // return false means stop
8082 faux.preventDefault();
8083 faux.stopPropagation();
8084 }
8085 if(faux._wasConsumed){ return; } // if preventDefault was called
8086 this.defer(function(){ this._onInput(faux); }); // widget notification after key has posted
8087 };
8088 this.own(on(this.textbox, "keydown, keypress, paste, cut, input, compositionend", lang.hitch(this, handleEvent)));
8089 },
8090
8091 _blankValue: '', // if the textbox is blank, what value should be reported
8092 filter: function(val){
8093 // summary:
8094 // Auto-corrections (such as trimming) that are applied to textbox
8095 // value on blur or form submit.
8096 // description:
8097 // For MappedTextBox subclasses, this is called twice
8098 //
8099 // - once with the display value
8100 // - once the value as set/returned by set('value', ...)
8101 //
8102 // and get('value'), ex: a Number for NumberTextBox.
8103 //
8104 // In the latter case it does corrections like converting null to NaN. In
8105 // the former case the NumberTextBox.filter() method calls this.inherited()
8106 // to execute standard trimming code in TextBox.filter().
8107 //
8108 // TODO: break this into two methods in 2.0
8109 //
8110 // tags:
8111 // protected extension
8112 if(val === null){ return this._blankValue; }
8113 if(typeof val != "string"){ return val; }
8114 if(this.trim){
8115 val = lang.trim(val);
8116 }
8117 if(this.uppercase){
8118 val = val.toUpperCase();
8119 }
8120 if(this.lowercase){
8121 val = val.toLowerCase();
8122 }
8123 if(this.propercase){
8124 val = val.replace(/[^\s]+/g, function(word){
8125 return word.substring(0,1).toUpperCase() + word.substring(1);
8126 });
8127 }
8128 return val;
8129 },
8130
8131 _setBlurValue: function(){
8132 this._setValueAttr(this.get('value'), true);
8133 },
8134
8135 _onBlur: function(e){
8136 if(this.disabled){ return; }
8137 this._setBlurValue();
8138 this.inherited(arguments);
8139 },
8140
8141 _isTextSelected: function(){
8142 return this.textbox.selectionStart != this.textbox.selectionEnd;
8143 },
8144
8145 _onFocus: function(/*String*/ by){
8146 if(this.disabled || this.readOnly){ return; }
8147
8148 // Select all text on focus via click if nothing already selected.
8149 // Since mouse-up will clear the selection, need to defer selection until after mouse-up.
8150 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
8151 if(this.selectOnClick && by == "mouse"){
8152 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
8153 // Only select all text on first click; otherwise users would have no way to clear
8154 // the selection.
8155 this.disconnect(this._selectOnClickHandle);
8156 this._selectOnClickHandle = null;
8157
8158 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
8159 // and if not, then select all the text
8160 if(!this._isTextSelected()){
8161 _TextBoxMixin.selectInputText(this.textbox);
8162 }
8163 });
8164 // in case the mouseup never comes
8165 this.defer(function(){
8166 if(this._selectOnClickHandle){
8167 this.disconnect(this._selectOnClickHandle);
8168 this._selectOnClickHandle = null;
8169 }
8170 }, 500); // if mouseup not received soon, then treat it as some gesture
8171 }
8172 // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
8173 // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
8174 this.inherited(arguments);
8175
8176 this._refreshState();
8177 },
8178
8179 reset: function(){
8180 // Overrides `dijit/_FormWidget/reset()`.
8181 // Additionally resets the displayed textbox value to ''
8182 this.textbox.value = '';
8183 this.inherited(arguments);
8184 },
8185
8186 _setTextDirAttr: function(/*String*/ textDir){
8187 // summary:
8188 // Setter for textDir.
8189 // description:
8190 // Users shouldn't call this function; they should be calling
8191 // set('textDir', value)
8192 // tags:
8193 // private
8194
8195 // only if new textDir is different from the old one
8196 // and on widgets creation.
8197 if(!this._created
8198 || this.textDir != textDir){
8199 this._set("textDir", textDir);
8200 // so the change of the textDir will take place immediately.
8201 this.applyTextDir(this.focusNode, this.focusNode.value);
8202 }
8203 }
8204 });
8205
8206
8207 _TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
8208 if(element.setSelectionRange){
8209 element.setSelectionRange(start, stop);
8210 }
8211 };
8212
8213 _TextBoxMixin.selectInputText = dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
8214 // summary:
8215 // Select text in the input element argument, from start (default 0), to stop (default end).
8216
8217 // TODO: use functions in _editor/selection.js?
8218 element = dom.byId(element);
8219 if(isNaN(start)){ start = 0; }
8220 if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
8221 try{
8222 element.focus();
8223 _TextBoxMixin._setSelectionRange(element, start, stop);
8224 }catch(e){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
8225 };
8226
8227 return _TextBoxMixin;
8228 });
8229
8230 },
8231 'dijit/form/SimpleTextarea':function(){
8232 define("dijit/form/SimpleTextarea", [
8233 "dojo/_base/declare", // declare
8234 "dojo/dom-class", // domClass.add
8235 "dojo/sniff", // has("ie") has("opera")
8236 "./TextBox"
8237 ], function(declare, domClass, has, TextBox){
8238
8239 // module:
8240 // dijit/form/SimpleTextarea
8241
8242
8243 return declare("dijit.form.SimpleTextarea", TextBox, {
8244 // summary:
8245 // A simple textarea that degrades, and responds to
8246 // minimal LayoutContainer usage, and works with dijit/form/Form.
8247 // Doesn't automatically size according to input, like Textarea.
8248 //
8249 // example:
8250 // | <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
8251 //
8252 // example:
8253 // | new SimpleTextarea({ rows:20, cols:30 }, "foo");
8254
8255 baseClass: "dijitTextBox dijitTextArea",
8256
8257 // rows: Number
8258 // The number of rows of text.
8259 rows: "3",
8260
8261 // rows: Number
8262 // The number of characters per line.
8263 cols: "20",
8264
8265 templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
8266
8267 postMixInProperties: function(){
8268 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
8269 // TODO: parser will handle this in 2.0
8270 if(!this.value && this.srcNodeRef){
8271 this.value = this.srcNodeRef.value;
8272 }
8273 this.inherited(arguments);
8274 },
8275
8276 buildRendering: function(){
8277 this.inherited(arguments);
8278 if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
8279 domClass.add(this.textbox, "dijitTextAreaCols");
8280 }
8281 },
8282
8283 filter: function(/*String*/ value){
8284 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
8285 // as \r\n instead of just \n
8286 if(value){
8287 value = value.replace(/\r/g,"");
8288 }
8289 return this.inherited(arguments);
8290 },
8291
8292 _onInput: function(/*Event?*/ e){
8293 // Override TextBox._onInput() to enforce maxLength restriction
8294 if(this.maxLength){
8295 var maxLength = parseInt(this.maxLength);
8296 var value = this.textbox.value.replace(/\r/g,'');
8297 var overflow = value.length - maxLength;
8298 if(overflow > 0){
8299 var textarea = this.textbox;
8300 if(textarea.selectionStart){
8301 var pos = textarea.selectionStart;
8302 var cr = 0;
8303 if(has("opera")){
8304 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
8305 }
8306 this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
8307 textarea.setSelectionRange(pos-overflow, pos-overflow);
8308 }else if(this.ownerDocument.selection){ //IE
8309 textarea.focus();
8310 var range = this.ownerDocument.selection.createRange();
8311 // delete overflow characters
8312 range.moveStart("character", -overflow);
8313 range.text = '';
8314 // show cursor
8315 range.select();
8316 }
8317 }
8318 }
8319 this.inherited(arguments);
8320 }
8321 });
8322
8323 });
8324
8325 },
8326 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n",
8327 'dijit/_base/window':function(){
8328 define("dijit/_base/window", [
8329 "dojo/window", // windowUtils.get
8330 "../main" // export symbol to dijit
8331 ], function(windowUtils, dijit){
8332 // module:
8333 // dijit/_base/window
8334
8335 /*=====
8336 return {
8337 // summary:
8338 // Back compatibility module, new code should use windowUtils directly instead of using this module.
8339 };
8340 =====*/
8341
8342 dijit.getDocumentWindow = function(doc){
8343 return windowUtils.get(doc);
8344 };
8345 });
8346
8347 },
8348 'dijit/PopupMenuItem':function(){
8349 define("dijit/PopupMenuItem", [
8350 "dojo/_base/declare", // declare
8351 "dojo/dom-style", // domStyle.set
8352 "dojo/query", // query
8353 "./registry", // registry.byNode
8354 "./MenuItem",
8355 "./hccss"
8356 ], function(declare, domStyle, query, registry, MenuItem){
8357
8358 // module:
8359 // dijit/PopupMenuItem
8360
8361 return declare("dijit.PopupMenuItem", MenuItem, {
8362 // summary:
8363 // An item in a Menu that spawn a drop down (usually a drop down menu)
8364
8365 _fillContent: function(){
8366 // summary:
8367 // When Menu is declared in markup, this code gets the menu label and
8368 // the popup widget from the srcNodeRef.
8369 // description:
8370 // srcNodeRefinnerHTML contains both the menu item text and a popup widget
8371 // The first part holds the menu item text and the second part is the popup
8372 // example:
8373 // | <div data-dojo-type="dijit/PopupMenuItem">
8374 // | <span>pick me</span>
8375 // | <popup> ... </popup>
8376 // | </div>
8377 // tags:
8378 // protected
8379
8380 if(this.srcNodeRef){
8381 var nodes = query("*", this.srcNodeRef);
8382 this.inherited(arguments, [nodes[0]]);
8383
8384 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
8385 this.dropDownContainer = this.srcNodeRef;
8386 }
8387 },
8388
8389 startup: function(){
8390 if(this._started){ return; }
8391 this.inherited(arguments);
8392
8393 // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
8394 // land now. move it to win.doc.body.
8395 if(!this.popup){
8396 var node = query("[widgetId]", this.dropDownContainer)[0];
8397 this.popup = registry.byNode(node);
8398 }
8399 this.ownerDocumentBody.appendChild(this.popup.domNode);
8400 this.popup.startup();
8401
8402 this.popup.domNode.style.display="none";
8403 if(this.arrowWrapper){
8404 domStyle.set(this.arrowWrapper, "visibility", "");
8405 }
8406 this.focusNode.setAttribute("aria-haspopup", "true");
8407 },
8408
8409 destroyDescendants: function(/*Boolean*/ preserveDom){
8410 if(this.popup){
8411 // Destroy the popup, unless it's already been destroyed. This can happen because
8412 // the popup is a direct child of <body> even though it's logically my child.
8413 if(!this.popup._destroyed){
8414 this.popup.destroyRecursive(preserveDom);
8415 }
8416 delete this.popup;
8417 }
8418 this.inherited(arguments);
8419 }
8420 });
8421 });
8422
8423 },
8424 'dojo/hccss':function(){
8425 define("dojo/hccss", [
8426 "require", // require.toUrl
8427 "./_base/config", // config.blankGif
8428 "./dom-class", // domClass.add
8429 "./dom-style", // domStyle.getComputedStyle
8430 "./has",
8431 "./ready", // ready
8432 "./_base/window" // win.body
8433 ], function(require, config, domClass, domStyle, has, ready, win){
8434
8435 // module:
8436 // dojo/hccss
8437
8438 /*=====
8439 return function(){
8440 // summary:
8441 // Test if computer is in high contrast mode (i.e. if browser is not displaying background images).
8442 // Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode.
8443 // Returns `has()` method;
8444 };
8445 =====*/
8446
8447 // Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!.
8448 has.add("highcontrast", function(){
8449 // note: if multiple documents, doesn't matter which one we use
8450 var div = win.doc.createElement("div");
8451 div.style.cssText = "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" +
8452 "background-image: url(" + (config.blankGif || require.toUrl("./resources/blank.gif")) + ");";
8453 win.body().appendChild(div);
8454
8455 var cs = domStyle.getComputedStyle(div),
8456 bkImg = cs.backgroundImage,
8457 hc = (cs.borderTopColor == cs.borderRightColor) ||
8458 (bkImg && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
8459
8460 if(has("ie") <= 8){
8461 div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
8462 }else{
8463 win.body().removeChild(div);
8464 }
8465
8466 return hc;
8467 });
8468
8469 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
8470 // change this module to depend on dojo/domReady!
8471 ready(90, function(){
8472 if(has("highcontrast")){
8473 domClass.add(win.body(), "dj_a11y");
8474 }
8475 });
8476
8477 return has;
8478 });
8479
8480 },
8481 'dijit/form/RadioButton':function(){
8482 define("dijit/form/RadioButton", [
8483 "dojo/_base/declare", // declare
8484 "./CheckBox",
8485 "./_RadioButtonMixin"
8486 ], function(declare, CheckBox, _RadioButtonMixin){
8487
8488 // module:
8489 // dijit/form/RadioButton
8490
8491 return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
8492 // summary:
8493 // Same as an HTML radio, but with fancy styling.
8494
8495 baseClass: "dijitRadio"
8496 });
8497 });
8498
8499 },
8500 'dijit/main':function(){
8501 define("dijit/main", [
8502 "dojo/_base/kernel"
8503 ], function(dojo){
8504 // module:
8505 // dijit/main
8506
8507 /*=====
8508 return {
8509 // summary:
8510 // The dijit package main module.
8511 // Deprecated. Users should access individual modules (ex: dijit/registry) directly.
8512 };
8513 =====*/
8514
8515 return dojo.dijit;
8516 });
8517
8518 },
8519 'dijit/_OnDijitClickMixin':function(){
8520 define("dijit/_OnDijitClickMixin", [
8521 "dojo/on",
8522 "dojo/_base/array", // array.forEach
8523 "dojo/keys", // keys.ENTER keys.SPACE
8524 "dojo/_base/declare", // declare
8525 "dojo/has", // has("dom-addeventlistener")
8526 "dojo/_base/unload", // unload.addOnWindowUnload
8527 "dojo/_base/window", // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
8528 "./a11yclick"
8529 ], function(on, array, keys, declare, has, unload, win, a11yclick){
8530
8531 // module:
8532 // dijit/_OnDijitClickMixin
8533
8534 var ret = declare("dijit._OnDijitClickMixin", null, {
8535 connect: function(
8536 /*Object|null*/ obj,
8537 /*String|Function*/ event,
8538 /*String|Function*/ method){
8539 // summary:
8540 // Connects specified obj/event to specified method of this object
8541 // and registers for disconnect() on widget destroy.
8542 // description:
8543 // Provide widget-specific analog to connect.connect, except with the
8544 // implicit use of this widget as the target object.
8545 // This version of connect also provides a special "ondijitclick"
8546 // event which triggers on a click or space or enter keyup.
8547 // Events connected with `this.connect` are disconnected upon
8548 // destruction.
8549 // returns:
8550 // A handle that can be passed to `disconnect` in order to disconnect before
8551 // the widget is destroyed.
8552 // example:
8553 // | var btn = new Button();
8554 // | // when foo.bar() is called, call the listener we're going to
8555 // | // provide in the scope of btn
8556 // | btn.connect(foo, "bar", function(){
8557 // | console.debug(this.toString());
8558 // | });
8559 // tags:
8560 // protected
8561
8562 return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
8563 }
8564 });
8565
8566 ret.a11yclick = a11yclick; // back compat
8567
8568 return ret;
8569 });
8570
8571 },
8572 'dijit/InlineEditBox':function(){
8573 require({cache:{
8574 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\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"}});
8575 define("dijit/InlineEditBox", [
8576 "require",
8577 "dojo/_base/array", // array.forEach
8578 "dojo/_base/declare", // declare
8579 "dojo/dom-attr", // domAttr.set domAttr.get
8580 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
8581 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
8582 "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
8583 "dojo/_base/event", // event.stop
8584 "dojo/i18n", // i18n.getLocalization
8585 "dojo/_base/kernel", // kernel.deprecated
8586 "dojo/keys", // keys.ENTER keys.ESCAPE
8587 "dojo/_base/lang", // lang.getObject
8588 "dojo/sniff", // has("ie")
8589 "dojo/when",
8590 "./focus",
8591 "./_Widget",
8592 "./_TemplatedMixin",
8593 "./_WidgetsInTemplateMixin",
8594 "./_Container",
8595 "./form/Button",
8596 "./form/_TextBoxMixin",
8597 "./form/TextBox",
8598 "dojo/text!./templates/InlineEditBox.html",
8599 "dojo/i18n!./nls/common"
8600 ], function(require, array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has, when,
8601 fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
8602
8603 // module:
8604 // dijit/InlineEditBox
8605
8606 var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
8607 // summary:
8608 // Internal widget used by InlineEditBox, displayed when in editing mode
8609 // to display the editor and maybe save/cancel buttons. Calling code should
8610 // connect to save/cancel methods to detect when editing is finished
8611 //
8612 // Has mainly the same parameters as InlineEditBox, plus these values:
8613 //
8614 // style: Object
8615 // Set of CSS attributes of display node, to replicate in editor
8616 //
8617 // value: String
8618 // Value as an HTML string or plain text string, depending on renderAsHTML flag
8619
8620 templateString: template,
8621
8622 postMixInProperties: function(){
8623 this.inherited(arguments);
8624 this.messages = i18n.getLocalization("dijit", "common", this.lang);
8625 array.forEach(["buttonSave", "buttonCancel"], function(prop){
8626 if(!this[prop]){
8627 this[prop] = this.messages[prop];
8628 }
8629 }, this);
8630 },
8631
8632 buildRendering: function(){
8633 this.inherited(arguments);
8634
8635 // Create edit widget in place in the template
8636 // TODO: remove getObject() for 2.0
8637 var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor;
8638
8639 // Copy the style from the source
8640 // Don't copy ALL properties though, just the necessary/applicable ones.
8641 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
8642 // is a relative value like 200%, rather than an absolute value like 24px, and
8643 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
8644 var srcStyle = this.sourceStyle,
8645 editStyle = "line-height:" + srcStyle.lineHeight + ";",
8646 destStyle = domStyle.getComputedStyle(this.domNode);
8647 array.forEach(["Weight", "Family", "Size", "Style"], function(prop){
8648 var textStyle = srcStyle["font" + prop],
8649 wrapperStyle = destStyle["font" + prop];
8650 if(wrapperStyle != textStyle){
8651 editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";";
8652 }
8653 }, this);
8654 array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){
8655 this.domNode.style[prop] = srcStyle[prop];
8656 }, this);
8657 var width = this.inlineEditBox.width;
8658 if(width == "100%"){
8659 // block mode
8660 editStyle += "width:100%;";
8661 this.domNode.style.display = "block";
8662 }else{
8663 // inline-block mode
8664 editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
8665 }
8666 var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
8667 style: editStyle,
8668 dir: this.dir,
8669 lang: this.lang,
8670 textDir: this.textDir
8671 });
8672 editorParams[ "displayedValue" in Cls.prototype ? "displayedValue" : "value"] = this.value;
8673 this.editWidget = new Cls(editorParams, this.editorPlaceholder);
8674
8675 if(this.inlineEditBox.autoSave){
8676 // Remove the save/cancel buttons since saving is done by simply tabbing away or
8677 // selecting a value from the drop down list
8678 domConstruct.destroy(this.buttonContainer);
8679 }
8680 },
8681
8682 postCreate: function(){
8683 this.inherited(arguments);
8684
8685 var ew = this.editWidget;
8686
8687 if(this.inlineEditBox.autoSave){
8688 // Selecting a value from a drop down list causes an onChange event and then we save
8689 this.connect(ew, "onChange", "_onChange");
8690
8691 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
8692 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
8693 // so this is the only way we can see the key press event.
8694 this.connect(ew, "onKeyPress", "_onKeyPress");
8695 }else{
8696 // If possible, enable/disable save button based on whether the user has changed the value
8697 if("intermediateChanges" in ew){
8698 ew.set("intermediateChanges", true);
8699 this.connect(ew, "onChange", "_onIntermediateChange");
8700 this.saveButton.set("disabled", true);
8701 }
8702 }
8703 },
8704
8705 startup: function(){
8706 this.editWidget.startup();
8707 this.inherited(arguments);
8708 },
8709
8710 _onIntermediateChange: function(/*===== val =====*/){
8711 // summary:
8712 // Called for editor widgets that support the intermediateChanges=true flag as a way
8713 // to detect when to enable/disabled the save button
8714 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
8715 },
8716
8717 destroy: function(){
8718 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
8719 this.inherited(arguments);
8720 },
8721
8722 getValue: function(){
8723 // summary:
8724 // Return the [display] value of the edit widget
8725 var ew = this.editWidget;
8726 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
8727 },
8728
8729 _onKeyPress: function(e){
8730 // summary:
8731 // Handler for keypress in the edit box in autoSave mode.
8732 // description:
8733 // For autoSave widgets, if Esc/Enter, call cancel/save.
8734 // tags:
8735 // private
8736
8737 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8738 if(e.altKey || e.ctrlKey){
8739 return;
8740 }
8741 // If Enter/Esc pressed, treat as save/cancel.
8742 if(e.charOrCode == keys.ESCAPE){
8743 event.stop(e);
8744 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
8745 }else if(e.charOrCode == keys.ENTER && e.target.tagName == "INPUT"){
8746 event.stop(e);
8747 this._onChange(); // fire _onBlur and then save
8748 }
8749
8750 // _onBlur will handle TAB automatically by allowing
8751 // the TAB to change focus before we mess with the DOM: #6227
8752 // Expounding by request:
8753 // The current focus is on the edit widget input field.
8754 // save() will hide and destroy this widget.
8755 // We want the focus to jump from the currently hidden
8756 // displayNode, but since it's hidden, it's impossible to
8757 // unhide it, focus it, and then have the browser focus
8758 // away from it to the next focusable element since each
8759 // of these events is asynchronous and the focus-to-next-element
8760 // is already queued.
8761 // So we allow the browser time to unqueue the move-focus event
8762 // before we do all the hide/show stuff.
8763 }
8764 },
8765
8766 _onBlur: function(){
8767 // summary:
8768 // Called when focus moves outside the editor
8769 // tags:
8770 // private
8771
8772 this.inherited(arguments);
8773 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8774 if(this.getValue() == this._resetValue){
8775 this.cancel(false);
8776 }else if(this.enableSave()){
8777 this.save(false);
8778 }
8779 }
8780 },
8781
8782 _onChange: function(){
8783 // summary:
8784 // Called when the underlying widget fires an onChange event,
8785 // such as when the user selects a value from the drop down list of a ComboBox,
8786 // which means that the user has finished entering the value and we should save.
8787 // tags:
8788 // private
8789
8790 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
8791 fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
8792 }
8793 },
8794
8795 enableSave: function(){
8796 // summary:
8797 // User overridable function returning a Boolean to indicate
8798 // if the Save button should be enabled or not - usually due to invalid conditions
8799 // tags:
8800 // extension
8801 return this.editWidget.isValid ? this.editWidget.isValid() : true;
8802 },
8803
8804 focus: function(){
8805 // summary:
8806 // Focus the edit widget.
8807 // tags:
8808 // protected
8809
8810 this.editWidget.focus();
8811
8812 if(this.editWidget.focusNode){
8813 // IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
8814 fm._onFocusNode(this.editWidget.focusNode);
8815
8816 if(this.editWidget.focusNode.tagName == "INPUT"){
8817 this.defer(function(){
8818 _TextBoxMixin.selectInputText(this.editWidget.focusNode);
8819 });
8820 }
8821 }
8822 }
8823 });
8824
8825
8826 var InlineEditBox = declare("dijit.InlineEditBox", _Widget, {
8827 // summary:
8828 // An element with in-line edit capabilities
8829 //
8830 // description:
8831 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
8832 // when you click it, an editor shows up in place of the original
8833 // text. Optionally, Save and Cancel button are displayed below the edit widget.
8834 // When Save is clicked, the text is pulled from the edit
8835 // widget and redisplayed and the edit widget is again hidden.
8836 // By default a plain Textarea widget is used as the editor (or for
8837 // inline values a TextBox), but you can specify an editor such as
8838 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
8839 // An edit widget must support the following API to be used:
8840 //
8841 // - displayedValue or value as initialization parameter,
8842 // and available through set('displayedValue') / set('value')
8843 // - void focus()
8844 // - DOM-node focusNode = node containing editable text
8845
8846 // editing: [readonly] Boolean
8847 // Is the node currently in edit mode?
8848 editing: false,
8849
8850 // autoSave: Boolean
8851 // Changing the value automatically saves it; don't have to push save button
8852 // (and save button isn't even displayed)
8853 autoSave: true,
8854
8855 // buttonSave: String
8856 // Save button label
8857 buttonSave: "",
8858
8859 // buttonCancel: String
8860 // Cancel button label
8861 buttonCancel: "",
8862
8863 // renderAsHtml: Boolean
8864 // Set this to true if the specified Editor's value should be interpreted as HTML
8865 // rather than plain text (ex: `dijit.Editor`)
8866 renderAsHtml: false,
8867
8868 // editor: String|Function
8869 // MID (ex: "dijit/form/TextBox") or constructor for editor widget
8870 editor: TextBox,
8871
8872 // editorWrapper: String|Function
8873 // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
8874 // buttons.
8875 editorWrapper: InlineEditor,
8876
8877 // editorParams: Object
8878 // Set of parameters for editor, like {required: true}
8879 editorParams: {},
8880
8881 // disabled: Boolean
8882 // If true, clicking the InlineEditBox to edit it will have no effect.
8883 disabled: false,
8884
8885 onChange: function(/*===== value =====*/){
8886 // summary:
8887 // Set this handler to be notified of changes to value.
8888 // tags:
8889 // callback
8890 },
8891
8892 onCancel: function(){
8893 // summary:
8894 // Set this handler to be notified when editing is cancelled.
8895 // tags:
8896 // callback
8897 },
8898
8899 // width: String
8900 // Width of editor. By default it's width=100% (ie, block mode).
8901 width: "100%",
8902
8903 // value: String
8904 // The display value of the widget in read-only mode
8905 value: "",
8906
8907 // noValueIndicator: [const] String
8908 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
8909 noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
8910 "<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
8911 "<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // &#160; == &nbsp;
8912
8913 constructor: function(/*===== params, srcNodeRef =====*/){
8914 // summary:
8915 // Create the widget.
8916 // params: Object|null
8917 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
8918 // and functions, typically callbacks like onClick.
8919 // The hash can contain any of the widget's properties, excluding read-only properties.
8920 // srcNodeRef: DOMNode|String?
8921 // If a srcNodeRef (DOM node) is specified:
8922 //
8923 // - use srcNodeRef.innerHTML as my value
8924 // - replace srcNodeRef with my generated DOM tree
8925
8926 this.editorParams = {};
8927 },
8928
8929 postMixInProperties: function(){
8930 this.inherited(arguments);
8931
8932 // save pointer to original source node, since Widget nulls-out srcNodeRef
8933 this.displayNode = this.srcNodeRef;
8934
8935 // connect handlers to the display node
8936 var events = {
8937 ondijitclick: "_onClick",
8938 onmouseover: "_onMouseOver",
8939 onmouseout: "_onMouseOut",
8940 onfocus: "_onMouseOver",
8941 onblur: "_onMouseOut"
8942 };
8943 for(var name in events){
8944 this.connect(this.displayNode, name, events[name]);
8945 }
8946 this.displayNode.setAttribute("role", "button");
8947 if(!this.displayNode.getAttribute("tabIndex")){
8948 this.displayNode.setAttribute("tabIndex", 0);
8949 }
8950
8951 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
8952 this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
8953 (this.displayNode.innerText || this.displayNode.textContent || ""));
8954 }
8955 if(!this.value){
8956 this.displayNode.innerHTML = this.noValueIndicator;
8957 }
8958
8959 domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
8960 },
8961
8962 setDisabled: function(/*Boolean*/ disabled){
8963 // summary:
8964 // Deprecated. Use set('disabled', ...) instead.
8965 // tags:
8966 // deprecated
8967 kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
8968 this.set('disabled', disabled);
8969 },
8970
8971 _setDisabledAttr: function(/*Boolean*/ disabled){
8972 // summary:
8973 // Hook to make set("disabled", ...) work.
8974 // Set disabled state of widget.
8975 this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false");
8976 if(disabled){
8977 this.displayNode.removeAttribute("tabIndex");
8978 }else{
8979 this.displayNode.setAttribute("tabIndex", 0);
8980 }
8981 domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
8982 this._set("disabled", disabled);
8983 },
8984
8985 _onMouseOver: function(){
8986 // summary:
8987 // Handler for onmouseover and onfocus event.
8988 // tags:
8989 // private
8990 if(!this.disabled){
8991 domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
8992 }
8993 },
8994
8995 _onMouseOut: function(){
8996 // summary:
8997 // Handler for onmouseout and onblur event.
8998 // tags:
8999 // private
9000 domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
9001 },
9002
9003 _onClick: function(/*Event*/ e){
9004 // summary:
9005 // Handler for onclick event.
9006 // tags:
9007 // private
9008 if(this.disabled){
9009 return;
9010 }
9011 if(e){
9012 event.stop(e);
9013 }
9014 this._onMouseOut();
9015
9016 // Since FF gets upset if you move a node while in an event handler for that node...
9017 this.defer("edit");
9018 },
9019
9020 edit: function(){
9021 // summary:
9022 // Display the editor widget in place of the original (read only) markup.
9023 // tags:
9024 // private
9025
9026 if(this.disabled || this.editing){
9027 return;
9028 }
9029 this._set('editing', true);
9030
9031 // save some display node values that can be restored later
9032 this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
9033
9034 if(this.wrapperWidget){
9035 var ew = this.wrapperWidget.editWidget;
9036 ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
9037 }else{
9038 // Placeholder for edit widget
9039 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
9040 // when Calendar dropdown appears, which happens automatically on focus.
9041 var placeholder = domConstruct.create("span", null, this.domNode, "before");
9042
9043 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
9044 var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
9045 this.wrapperWidget = new Ewc({
9046 value: this.value,
9047 buttonSave: this.buttonSave,
9048 buttonCancel: this.buttonCancel,
9049 dir: this.dir,
9050 lang: this.lang,
9051 tabIndex: this._savedTabIndex,
9052 editor: this.editor,
9053 inlineEditBox: this,
9054 sourceStyle: domStyle.getComputedStyle(this.displayNode),
9055 save: lang.hitch(this, "save"),
9056 cancel: lang.hitch(this, "cancel"),
9057 textDir: this.textDir
9058 }, placeholder);
9059 if(!this.wrapperWidget._started){
9060 this.wrapperWidget.startup();
9061 }
9062 if(!this._started){
9063 this.startup();
9064 }
9065 }
9066 var ww = this.wrapperWidget;
9067
9068 // to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
9069 // and then when it's finished rendering, we switch from display mode to editor
9070 // position: absolute releases screen space allocated to the display node
9071 // opacity:0 is the same as visibility: hidden but is still focusable
9072 // visibility: hidden removes focus outline
9073
9074 domClass.add(this.displayNode, "dijitOffScreen");
9075 domClass.remove(ww.domNode, "dijitOffScreen");
9076 domStyle.set(ww.domNode, { visibility: "visible" });
9077 domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
9078
9079 // After edit widget has finished initializing (in particular need to wait for dijit.Editor),
9080 // or immediately if there is no onLoadDeferred Deferred,
9081 // replace the display widget with edit widget, leaving them both displayed for a brief time so that
9082 // focus can be shifted without incident.
9083 when(ww.editWidget.onLoadDeferred, lang.hitch(ww, function(){
9084 this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
9085 this.focus(); // both nodes are showing, so we can switch focus safely
9086 this._resetValue = this.getValue();
9087 });
9088 }));
9089 },
9090
9091 _onBlur: function(){
9092 // summary:
9093 // Called when focus moves outside the InlineEditBox.
9094 // Performs garbage collection.
9095 // tags:
9096 // private
9097
9098 this.inherited(arguments);
9099 if(!this.editing){
9100 /* causes IE focus problems, see TooltipDialog_a11y.html...
9101 this.defer(function(){
9102 if(this.wrapperWidget){
9103 this.wrapperWidget.destroy();
9104 delete this.wrapperWidget;
9105 }
9106 });
9107 */
9108 }
9109 },
9110
9111 destroy: function(){
9112 if(this.wrapperWidget && !this.wrapperWidget._destroyed){
9113 this.wrapperWidget.destroy();
9114 delete this.wrapperWidget;
9115 }
9116 this.inherited(arguments);
9117 },
9118
9119 _showText: function(/*Boolean*/ focus){
9120 // summary:
9121 // Revert to display mode, and optionally focus on display node
9122 // tags:
9123 // private
9124
9125 var ww = this.wrapperWidget;
9126 domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events
9127 domClass.add(ww.domNode, "dijitOffScreen");
9128 domClass.remove(this.displayNode, "dijitOffScreen");
9129 domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
9130 if(focus){
9131 fm.focus(this.displayNode);
9132 }
9133 },
9134
9135 save: function(/*Boolean*/ focus){
9136 // summary:
9137 // Save the contents of the editor and revert to display mode.
9138 // focus: Boolean
9139 // Focus on the display mode text
9140 // tags:
9141 // private
9142
9143 if(this.disabled || !this.editing){
9144 return;
9145 }
9146 this._set('editing', false);
9147
9148 var ww = this.wrapperWidget;
9149 var value = ww.getValue();
9150 this.set('value', value); // display changed, formatted value
9151
9152 this._showText(focus); // set focus as needed
9153 },
9154
9155 setValue: function(/*String*/ val){
9156 // summary:
9157 // Deprecated. Use set('value', ...) instead.
9158 // tags:
9159 // deprecated
9160 kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
9161 return this.set("value", val);
9162 },
9163
9164 _setValueAttr: function(/*String*/ val){
9165 // summary:
9166 // Hook to make set("value", ...) work.
9167 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
9168
9169 val = lang.trim(val);
9170 var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
9171 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
9172 this._set("value", val);
9173
9174 if(this._started){
9175 // tell the world that we have changed
9176 this.defer(function(){
9177 this.onChange(val);
9178 }); // defer prevents browser freeze for long-running event handlers
9179 }
9180 // contextual (auto) text direction depends on the text value
9181 if(this.textDir == "auto"){
9182 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9183 }
9184 },
9185
9186 getValue: function(){
9187 // summary:
9188 // Deprecated. Use get('value') instead.
9189 // tags:
9190 // deprecated
9191 kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
9192 return this.get("value");
9193 },
9194
9195 cancel: function(/*Boolean*/ focus){
9196 // summary:
9197 // Revert to display mode, discarding any changes made in the editor
9198 // tags:
9199 // private
9200
9201 if(this.disabled || !this.editing){
9202 return;
9203 }
9204 this._set('editing', false);
9205
9206 // tell the world that we have no changes
9207 this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers
9208
9209 this._showText(focus);
9210 },
9211
9212 _setTextDirAttr: function(/*String*/ textDir){
9213 // summary:
9214 // Setter for textDir.
9215 // description:
9216 // Users shouldn't call this function; they should be calling
9217 // set('textDir', value)
9218 // tags:
9219 // private
9220 if(!this._created || this.textDir != textDir){
9221 this._set("textDir", textDir);
9222 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9223 this.displayNode.align = this.dir == "rtl" ? "right" : "left"; //fix the text alignment
9224 }
9225 }
9226 });
9227
9228 InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
9229
9230 return InlineEditBox;
9231 });
9232 },
9233 'dojo/selector/acme':function(){
9234 define("dojo/selector/acme", [
9235 "../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window"
9236 ], function(dom, has, array, lang, win){
9237
9238 // module:
9239 // dojo/selector/acme
9240
9241 /*
9242 acme architectural overview:
9243
9244 acme is a relatively full-featured CSS3 query library. It is
9245 designed to take any valid CSS3 selector and return the nodes matching
9246 the selector. To do this quickly, it processes queries in several
9247 steps, applying caching where profitable.
9248
9249 The steps (roughly in reverse order of the way they appear in the code):
9250 1.) check to see if we already have a "query dispatcher"
9251 - if so, use that with the given parameterization. Skip to step 4.
9252 2.) attempt to determine which branch to dispatch the query to:
9253 - JS (optimized DOM iteration)
9254 - native (FF3.1+, Safari 3.1+, IE 8+)
9255 3.) tokenize and convert to executable "query dispatcher"
9256 - this is where the lion's share of the complexity in the
9257 system lies. In the DOM version, the query dispatcher is
9258 assembled as a chain of "yes/no" test functions pertaining to
9259 a section of a simple query statement (".blah:nth-child(odd)"
9260 but not "div div", which is 2 simple statements). Individual
9261 statement dispatchers are cached (to prevent re-definition)
9262 as are entire dispatch chains (to make re-execution of the
9263 same query fast)
9264 4.) the resulting query dispatcher is called in the passed scope
9265 (by default the top-level document)
9266 - for DOM queries, this results in a recursive, top-down
9267 evaluation of nodes based on each simple query section
9268 - for native implementations, this may mean working around spec
9269 bugs. So be it.
9270 5.) matched nodes are pruned to ensure they are unique (if necessary)
9271 */
9272
9273
9274 ////////////////////////////////////////////////////////////////////////
9275 // Toolkit aliases
9276 ////////////////////////////////////////////////////////////////////////
9277
9278 // if you are extracting acme for use in your own system, you will
9279 // need to provide these methods and properties. No other porting should be
9280 // necessary, save for configuring the system to use a class other than
9281 // dojo/NodeList as the return instance instantiator
9282 var trim = lang.trim;
9283 var each = array.forEach;
9284
9285 var getDoc = function(){ return win.doc; };
9286 // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
9287 var cssCaseBug = (getDoc().compatMode) == "BackCompat";
9288
9289 ////////////////////////////////////////////////////////////////////////
9290 // Global utilities
9291 ////////////////////////////////////////////////////////////////////////
9292
9293
9294 var specials = ">~+";
9295
9296 // global thunk to determine whether we should treat the current query as
9297 // case sensitive or not. This switch is flipped by the query evaluator
9298 // based on the document passed as the context to search.
9299 var caseSensitive = false;
9300
9301 // how high?
9302 var yesman = function(){ return true; };
9303
9304 ////////////////////////////////////////////////////////////////////////
9305 // Tokenizer
9306 ////////////////////////////////////////////////////////////////////////
9307
9308 var getQueryParts = function(query){
9309 // summary:
9310 // state machine for query tokenization
9311 // description:
9312 // instead of using a brittle and slow regex-based CSS parser,
9313 // acme implements an AST-style query representation. This
9314 // representation is only generated once per query. For example,
9315 // the same query run multiple times or under different root nodes
9316 // does not re-parse the selector expression but instead uses the
9317 // cached data structure. The state machine implemented here
9318 // terminates on the last " " (space) character and returns an
9319 // ordered array of query component structures (or "parts"). Each
9320 // part represents an operator or a simple CSS filtering
9321 // expression. The structure for parts is documented in the code
9322 // below.
9323
9324
9325 // NOTE:
9326 // this code is designed to run fast and compress well. Sacrifices
9327 // to readability and maintainability have been made. Your best
9328 // bet when hacking the tokenizer is to put The Donnas on *really*
9329 // loud (may we recommend their "Spend The Night" release?) and
9330 // just assume you're gonna make mistakes. Keep the unit tests
9331 // open and run them frequently. Knowing is half the battle ;-)
9332 if(specials.indexOf(query.slice(-1)) >= 0){
9333 // if we end with a ">", "+", or "~", that means we're implicitly
9334 // searching all children, so make it explicit
9335 query += " * ";
9336 }else{
9337 // if you have not provided a terminator, one will be provided for
9338 // you...
9339 query += " ";
9340 }
9341
9342 var ts = function(/*Integer*/ s, /*Integer*/ e){
9343 // trim and slice.
9344
9345 // take an index to start a string slice from and an end position
9346 // and return a trimmed copy of that sub-string
9347 return trim(query.slice(s, e));
9348 };
9349
9350 // the overall data graph of the full query, as represented by queryPart objects
9351 var queryParts = [];
9352
9353
9354 // state keeping vars
9355 var inBrackets = -1, inParens = -1, inMatchFor = -1,
9356 inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
9357 lc = "", cc = "", pStart;
9358
9359 // iteration vars
9360 var x = 0, // index in the query
9361 ql = query.length,
9362 currentPart = null, // data structure representing the entire clause
9363 _cp = null; // the current pseudo or attr matcher
9364
9365 // several temporary variables are assigned to this structure during a
9366 // potential sub-expression match:
9367 // attr:
9368 // a string representing the current full attribute match in a
9369 // bracket expression
9370 // type:
9371 // if there's an operator in a bracket expression, this is
9372 // used to keep track of it
9373 // value:
9374 // the internals of parenthetical expression for a pseudo. for
9375 // :nth-child(2n+1), value might be "2n+1"
9376
9377 var endTag = function(){
9378 // called when the tokenizer hits the end of a particular tag name.
9379 // Re-sets state variables for tag matching and sets up the matcher
9380 // to handle the next type of token (tag or operator).
9381 if(inTag >= 0){
9382 var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
9383 currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
9384 inTag = -1;
9385 }
9386 };
9387
9388 var endId = function(){
9389 // called when the tokenizer might be at the end of an ID portion of a match
9390 if(inId >= 0){
9391 currentPart.id = ts(inId, x).replace(/\\/g, "");
9392 inId = -1;
9393 }
9394 };
9395
9396 var endClass = function(){
9397 // called when the tokenizer might be at the end of a class name
9398 // match. CSS allows for multiple classes, so we augment the
9399 // current item with another class in its list
9400 if(inClass >= 0){
9401 currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
9402 inClass = -1;
9403 }
9404 };
9405
9406 var endAll = function(){
9407 // at the end of a simple fragment, so wall off the matches
9408 endId();
9409 endTag();
9410 endClass();
9411 };
9412
9413 var endPart = function(){
9414 endAll();
9415 if(inPseudo >= 0){
9416 currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
9417 }
9418 // hint to the selector engine to tell it whether or not it
9419 // needs to do any iteration. Many simple selectors don't, and
9420 // we can avoid significant construction-time work by advising
9421 // the system to skip them
9422 currentPart.loops = (
9423 currentPart.pseudos.length ||
9424 currentPart.attrs.length ||
9425 currentPart.classes.length );
9426
9427 currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
9428
9429
9430 // otag/tag are hints to suggest to the system whether or not
9431 // it's an operator or a tag. We save a copy of otag since the
9432 // tag name is cast to upper-case in regular HTML matches. The
9433 // system has a global switch to figure out if the current
9434 // expression needs to be case sensitive or not and it will use
9435 // otag or tag accordingly
9436 currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
9437
9438 if(currentPart.tag){
9439 // if we're in a case-insensitive HTML doc, we likely want
9440 // the toUpperCase when matching on element.tagName. If we
9441 // do it here, we can skip the string op per node
9442 // comparison
9443 currentPart.tag = currentPart.tag.toUpperCase();
9444 }
9445
9446 // add the part to the list
9447 if(queryParts.length && (queryParts[queryParts.length-1].oper)){
9448 // operators are always infix, so we remove them from the
9449 // list and attach them to the next match. The evaluator is
9450 // responsible for sorting out how to handle them.
9451 currentPart.infixOper = queryParts.pop();
9452 currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
9453 /*
9454 console.debug( "swapping out the infix",
9455 currentPart.infixOper,
9456 "and attaching it to",
9457 currentPart);
9458 */
9459 }
9460 queryParts.push(currentPart);
9461
9462 currentPart = null;
9463 };
9464
9465 // iterate over the query, character by character, building up a
9466 // list of query part objects
9467 for(; lc=cc, cc=query.charAt(x), x < ql; x++){
9468 // cc: the current character in the match
9469 // lc: the last character (if any)
9470
9471 // someone is trying to escape something, so don't try to match any
9472 // fragments. We assume we're inside a literal.
9473 if(lc == "\\"){ continue; }
9474 if(!currentPart){ // a part was just ended or none has yet been created
9475 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
9476 pStart = x;
9477 // rules describe full CSS sub-expressions, like:
9478 // #someId
9479 // .className:first-child
9480 // but not:
9481 // thinger > div.howdy[type=thinger]
9482 // the indidual components of the previous query would be
9483 // split into 3 parts that would be represented a structure like:
9484 // [
9485 // {
9486 // query: "thinger",
9487 // tag: "thinger",
9488 // },
9489 // {
9490 // query: "div.howdy[type=thinger]",
9491 // classes: ["howdy"],
9492 // infixOper: {
9493 // query: ">",
9494 // oper: ">",
9495 // }
9496 // },
9497 // ]
9498 currentPart = {
9499 query: null, // the full text of the part's rule
9500 pseudos: [], // CSS supports multiple pseud-class matches in a single rule
9501 attrs: [], // CSS supports multi-attribute match, so we need an array
9502 classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
9503 tag: null, // only one tag...
9504 oper: null, // ...or operator per component. Note that these wind up being exclusive.
9505 id: null, // the id component of a rule
9506 getTag: function(){
9507 return caseSensitive ? this.otag : this.tag;
9508 }
9509 };
9510
9511 // if we don't have a part, we assume we're going to start at
9512 // the beginning of a match, which should be a tag name. This
9513 // might fault a little later on, but we detect that and this
9514 // iteration will still be fine.
9515 inTag = x;
9516 }
9517
9518 // Skip processing all quoted characters.
9519 // If we are inside quoted text then currentQuoteChar stores the character that began the quote,
9520 // thus that character that will end it.
9521 if(currentQuoteChar){
9522 if(cc == currentQuoteChar){
9523 currentQuoteChar = null;
9524 }
9525 continue;
9526 }else if (cc == "'" || cc == '"'){
9527 currentQuoteChar = cc;
9528 continue;
9529 }
9530
9531 if(inBrackets >= 0){
9532 // look for a the close first
9533 if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
9534 if(!_cp.attr){
9535 // no attribute match was previously begun, so we
9536 // assume this is an attribute existence match in the
9537 // form of [someAttributeName]
9538 _cp.attr = ts(inBrackets+1, x);
9539 }else{
9540 // we had an attribute already, so we know that we're
9541 // matching some sort of value, as in [attrName=howdy]
9542 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
9543 }
9544 var cmf = _cp.matchFor;
9545 if(cmf){
9546 // try to strip quotes from the matchFor value. We want
9547 // [attrName=howdy] to match the same
9548 // as [attrName = 'howdy' ]
9549 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
9550 _cp.matchFor = cmf.slice(1, -1);
9551 }
9552 }
9553 // remove backslash escapes from an attribute match, since DOM
9554 // querying will get attribute values without backslashes
9555 if(_cp.matchFor){
9556 _cp.matchFor = _cp.matchFor.replace(/\\/g, "");
9557 }
9558
9559 // end the attribute by adding it to the list of attributes.
9560 currentPart.attrs.push(_cp);
9561 _cp = null; // necessary?
9562 inBrackets = inMatchFor = -1;
9563 }else if(cc == "="){
9564 // if the last char was an operator prefix, make sure we
9565 // record it along with the "=" operator.
9566 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
9567 _cp.type = addToCc+cc;
9568 _cp.attr = ts(inBrackets+1, x-addToCc.length);
9569 inMatchFor = x+1;
9570 }
9571 // now look for other clause parts
9572 }else if(inParens >= 0){
9573 // if we're in a parenthetical expression, we need to figure
9574 // out if it's attached to a pseudo-selector rule like
9575 // :nth-child(1)
9576 if(cc == ")"){
9577 if(inPseudo >= 0){
9578 _cp.value = ts(inParens+1, x);
9579 }
9580 inPseudo = inParens = -1;
9581 }
9582 }else if(cc == "#"){
9583 // start of an ID match
9584 endAll();
9585 inId = x+1;
9586 }else if(cc == "."){
9587 // start of a class match
9588 endAll();
9589 inClass = x;
9590 }else if(cc == ":"){
9591 // start of a pseudo-selector match
9592 endAll();
9593 inPseudo = x;
9594 }else if(cc == "["){
9595 // start of an attribute match.
9596 endAll();
9597 inBrackets = x;
9598 // provide a new structure for the attribute match to fill-in
9599 _cp = {
9600 /*=====
9601 attr: null, type: null, matchFor: null
9602 =====*/
9603 };
9604 }else if(cc == "("){
9605 // we really only care if we've entered a parenthetical
9606 // expression if we're already inside a pseudo-selector match
9607 if(inPseudo >= 0){
9608 // provide a new structure for the pseudo match to fill-in
9609 _cp = {
9610 name: ts(inPseudo+1, x),
9611 value: null
9612 };
9613 currentPart.pseudos.push(_cp);
9614 }
9615 inParens = x;
9616 }else if(
9617 (cc == " ") &&
9618 // if it's a space char and the last char is too, consume the
9619 // current one without doing more work
9620 (lc != cc)
9621 ){
9622 endPart();
9623 }
9624 }
9625 return queryParts;
9626 };
9627
9628
9629 ////////////////////////////////////////////////////////////////////////
9630 // DOM query infrastructure
9631 ////////////////////////////////////////////////////////////////////////
9632
9633 var agree = function(first, second){
9634 // the basic building block of the yes/no chaining system. agree(f1,
9635 // f2) generates a new function which returns the boolean results of
9636 // both of the passed functions to a single logical-anded result. If
9637 // either are not passed, the other is used exclusively.
9638 if(!first){ return second; }
9639 if(!second){ return first; }
9640
9641 return function(){
9642 return first.apply(window, arguments) && second.apply(window, arguments);
9643 };
9644 };
9645
9646 var getArr = function(i, arr){
9647 // helps us avoid array alloc when we don't need it
9648 var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
9649 if(i){ r.push(i); }
9650 return r;
9651 };
9652
9653 var _isElement = function(n){ return (1 == n.nodeType); };
9654
9655 // FIXME: need to coalesce _getAttr with defaultGetter
9656 var blank = "";
9657 var _getAttr = function(elem, attr){
9658 if(!elem){ return blank; }
9659 if(attr == "class"){
9660 return elem.className || blank;
9661 }
9662 if(attr == "for"){
9663 return elem.htmlFor || blank;
9664 }
9665 if(attr == "style"){
9666 return elem.style.cssText || blank;
9667 }
9668 return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
9669 };
9670
9671 var attrs = {
9672 "*=": function(attr, value){
9673 return function(elem){
9674 // E[foo*="bar"]
9675 // an E element whose "foo" attribute value contains
9676 // the substring "bar"
9677 return (_getAttr(elem, attr).indexOf(value)>=0);
9678 };
9679 },
9680 "^=": function(attr, value){
9681 // E[foo^="bar"]
9682 // an E element whose "foo" attribute value begins exactly
9683 // with the string "bar"
9684 return function(elem){
9685 return (_getAttr(elem, attr).indexOf(value)==0);
9686 };
9687 },
9688 "$=": function(attr, value){
9689 // E[foo$="bar"]
9690 // an E element whose "foo" attribute value ends exactly
9691 // with the string "bar"
9692 return function(elem){
9693 var ea = " "+_getAttr(elem, attr);
9694 var lastIndex = ea.lastIndexOf(value);
9695 return lastIndex > -1 && (lastIndex==(ea.length-value.length));
9696 };
9697 },
9698 "~=": function(attr, value){
9699 // E[foo~="bar"]
9700 // an E element whose "foo" attribute value is a list of
9701 // space-separated values, one of which is exactly equal
9702 // to "bar"
9703
9704 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
9705 var tval = " "+value+" ";
9706 return function(elem){
9707 var ea = " "+_getAttr(elem, attr)+" ";
9708 return (ea.indexOf(tval)>=0);
9709 };
9710 },
9711 "|=": function(attr, value){
9712 // E[hreflang|="en"]
9713 // an E element whose "hreflang" attribute has a
9714 // hyphen-separated list of values beginning (from the
9715 // left) with "en"
9716 var valueDash = value+"-";
9717 return function(elem){
9718 var ea = _getAttr(elem, attr);
9719 return (
9720 (ea == value) ||
9721 (ea.indexOf(valueDash)==0)
9722 );
9723 };
9724 },
9725 "=": function(attr, value){
9726 return function(elem){
9727 return (_getAttr(elem, attr) == value);
9728 };
9729 }
9730 };
9731
9732 // avoid testing for node type if we can. Defining this in the negative
9733 // here to avoid negation in the fast path.
9734 var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
9735 var _ns = !_noNES ? "nextElementSibling" : "nextSibling";
9736 var _ps = !_noNES ? "previousElementSibling" : "previousSibling";
9737 var _simpleNodeTest = (_noNES ? _isElement : yesman);
9738
9739 var _lookLeft = function(node){
9740 // look left
9741 while(node = node[_ps]){
9742 if(_simpleNodeTest(node)){ return false; }
9743 }
9744 return true;
9745 };
9746
9747 var _lookRight = function(node){
9748 // look right
9749 while(node = node[_ns]){
9750 if(_simpleNodeTest(node)){ return false; }
9751 }
9752 return true;
9753 };
9754
9755 var getNodeIndex = function(node){
9756 var root = node.parentNode;
9757 root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
9758 var i = 0,
9759 tret = root.children || root.childNodes,
9760 ci = (node["_i"]||node.getAttribute("_i")||-1),
9761 cl = (root["_l"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l") : -1));
9762
9763 if(!tret){ return -1; }
9764 var l = tret.length;
9765
9766 // we calculate the parent length as a cheap way to invalidate the
9767 // cache. It's not 100% accurate, but it's much more honest than what
9768 // other libraries do
9769 if( cl == l && ci >= 0 && cl >= 0 ){
9770 // if it's legit, tag and release
9771 return ci;
9772 }
9773
9774 // else re-key things
9775 if(has("ie") && typeof root.setAttribute !== "undefined"){
9776 root.setAttribute("_l", l);
9777 }else{
9778 root["_l"] = l;
9779 }
9780 ci = -1;
9781 for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
9782 if(_simpleNodeTest(te)){
9783 if(has("ie")){
9784 te.setAttribute("_i", ++i);
9785 }else{
9786 te["_i"] = ++i;
9787 }
9788 if(node === te){
9789 // NOTE:
9790 // shortcutting the return at this step in indexing works
9791 // very well for benchmarking but we avoid it here since
9792 // it leads to potential O(n^2) behavior in sequential
9793 // getNodexIndex operations on a previously un-indexed
9794 // parent. We may revisit this at a later time, but for
9795 // now we just want to get the right answer more often
9796 // than not.
9797 ci = i;
9798 }
9799 }
9800 }
9801 return ci;
9802 };
9803
9804 var isEven = function(elem){
9805 return !((getNodeIndex(elem)) % 2);
9806 };
9807
9808 var isOdd = function(elem){
9809 return ((getNodeIndex(elem)) % 2);
9810 };
9811
9812 var pseudos = {
9813 "checked": function(name, condition){
9814 return function(elem){
9815 return !!("checked" in elem ? elem.checked : elem.selected);
9816 };
9817 },
9818 "disabled": function(name, condition){
9819 return function(elem){
9820 return elem.disabled;
9821 };
9822 },
9823 "enabled": function(name, condition){
9824 return function(elem){
9825 return !elem.disabled;
9826 };
9827 },
9828 "first-child": function(){ return _lookLeft; },
9829 "last-child": function(){ return _lookRight; },
9830 "only-child": function(name, condition){
9831 return function(node){
9832 return _lookLeft(node) && _lookRight(node);
9833 };
9834 },
9835 "empty": function(name, condition){
9836 return function(elem){
9837 // DomQuery and jQuery get this wrong, oddly enough.
9838 // The CSS 3 selectors spec is pretty explicit about it, too.
9839 var cn = elem.childNodes;
9840 var cnl = elem.childNodes.length;
9841 // if(!cnl){ return true; }
9842 for(var x=cnl-1; x >= 0; x--){
9843 var nt = cn[x].nodeType;
9844 if((nt === 1)||(nt == 3)){ return false; }
9845 }
9846 return true;
9847 };
9848 },
9849 "contains": function(name, condition){
9850 var cz = condition.charAt(0);
9851 if( cz == '"' || cz == "'" ){ //remove quote
9852 condition = condition.slice(1, -1);
9853 }
9854 return function(elem){
9855 return (elem.innerHTML.indexOf(condition) >= 0);
9856 };
9857 },
9858 "not": function(name, condition){
9859 var p = getQueryParts(condition)[0];
9860 var ignores = { el: 1 };
9861 if(p.tag != "*"){
9862 ignores.tag = 1;
9863 }
9864 if(!p.classes.length){
9865 ignores.classes = 1;
9866 }
9867 var ntf = getSimpleFilterFunc(p, ignores);
9868 return function(elem){
9869 return (!ntf(elem));
9870 };
9871 },
9872 "nth-child": function(name, condition){
9873 var pi = parseInt;
9874 // avoid re-defining function objects if we can
9875 if(condition == "odd"){
9876 return isOdd;
9877 }else if(condition == "even"){
9878 return isEven;
9879 }
9880 // FIXME: can we shorten this?
9881 if(condition.indexOf("n") != -1){
9882 var tparts = condition.split("n", 2);
9883 var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
9884 var idx = tparts[1] ? pi(tparts[1]) : 0;
9885 var lb = 0, ub = -1;
9886 if(pred > 0){
9887 if(idx < 0){
9888 idx = (idx % pred) && (pred + (idx % pred));
9889 }else if(idx>0){
9890 if(idx >= pred){
9891 lb = idx - idx % pred;
9892 }
9893 idx = idx % pred;
9894 }
9895 }else if(pred<0){
9896 pred *= -1;
9897 // idx has to be greater than 0 when pred is negative;
9898 // shall we throw an error here?
9899 if(idx > 0){
9900 ub = idx;
9901 idx = idx % pred;
9902 }
9903 }
9904 if(pred > 0){
9905 return function(elem){
9906 var i = getNodeIndex(elem);
9907 return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
9908 };
9909 }else{
9910 condition = idx;
9911 }
9912 }
9913 var ncount = pi(condition);
9914 return function(elem){
9915 return (getNodeIndex(elem) == ncount);
9916 };
9917 }
9918 };
9919
9920 var defaultGetter = (has("ie") < 9 || has("ie") == 9 && has("quirks")) ? function(cond){
9921 var clc = cond.toLowerCase();
9922 if(clc == "class"){ cond = "className"; }
9923 return function(elem){
9924 return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
9925 };
9926 } : function(cond){
9927 return function(elem){
9928 return (elem && elem.getAttribute && elem.hasAttribute(cond));
9929 };
9930 };
9931
9932 var getSimpleFilterFunc = function(query, ignores){
9933 // generates a node tester function based on the passed query part. The
9934 // query part is one of the structures generated by the query parser
9935 // when it creates the query AST. The "ignores" object specifies which
9936 // (if any) tests to skip, allowing the system to avoid duplicating
9937 // work where it may have already been taken into account by other
9938 // factors such as how the nodes to test were fetched in the first
9939 // place
9940 if(!query){ return yesman; }
9941 ignores = ignores||{};
9942
9943 var ff = null;
9944
9945 if(!("el" in ignores)){
9946 ff = agree(ff, _isElement);
9947 }
9948
9949 if(!("tag" in ignores)){
9950 if(query.tag != "*"){
9951 ff = agree(ff, function(elem){
9952 return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
9953 });
9954 }
9955 }
9956
9957 if(!("classes" in ignores)){
9958 each(query.classes, function(cname, idx, arr){
9959 // get the class name
9960 /*
9961 var isWildcard = cname.charAt(cname.length-1) == "*";
9962 if(isWildcard){
9963 cname = cname.substr(0, cname.length-1);
9964 }
9965 // I dislike the regex thing, even if memoized in a cache, but it's VERY short
9966 var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
9967 */
9968 var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)");
9969 ff = agree(ff, function(elem){
9970 return re.test(elem.className);
9971 });
9972 ff.count = idx;
9973 });
9974 }
9975
9976 if(!("pseudos" in ignores)){
9977 each(query.pseudos, function(pseudo){
9978 var pn = pseudo.name;
9979 if(pseudos[pn]){
9980 ff = agree(ff, pseudos[pn](pn, pseudo.value));
9981 }
9982 });
9983 }
9984
9985 if(!("attrs" in ignores)){
9986 each(query.attrs, function(attr){
9987 var matcher;
9988 var a = attr.attr;
9989 // type, attr, matchFor
9990 if(attr.type && attrs[attr.type]){
9991 matcher = attrs[attr.type](a, attr.matchFor);
9992 }else if(a.length){
9993 matcher = defaultGetter(a);
9994 }
9995 if(matcher){
9996 ff = agree(ff, matcher);
9997 }
9998 });
9999 }
10000
10001 if(!("id" in ignores)){
10002 if(query.id){
10003 ff = agree(ff, function(elem){
10004 return (!!elem && (elem.id == query.id));
10005 });
10006 }
10007 }
10008
10009 if(!ff){
10010 if(!("default" in ignores)){
10011 ff = yesman;
10012 }
10013 }
10014 return ff;
10015 };
10016
10017 var _nextSibling = function(filterFunc){
10018 return function(node, ret, bag){
10019 while(node = node[_ns]){
10020 if(_noNES && (!_isElement(node))){ continue; }
10021 if(
10022 (!bag || _isUnique(node, bag)) &&
10023 filterFunc(node)
10024 ){
10025 ret.push(node);
10026 }
10027 break;
10028 }
10029 return ret;
10030 };
10031 };
10032
10033 var _nextSiblings = function(filterFunc){
10034 return function(root, ret, bag){
10035 var te = root[_ns];
10036 while(te){
10037 if(_simpleNodeTest(te)){
10038 if(bag && !_isUnique(te, bag)){
10039 break;
10040 }
10041 if(filterFunc(te)){
10042 ret.push(te);
10043 }
10044 }
10045 te = te[_ns];
10046 }
10047 return ret;
10048 };
10049 };
10050
10051 // get an array of child *elements*, skipping text and comment nodes
10052 var _childElements = function(filterFunc){
10053 filterFunc = filterFunc||yesman;
10054 return function(root, ret, bag){
10055 // get an array of child elements, skipping text and comment nodes
10056 var te, x = 0, tret = root.children || root.childNodes;
10057 while(te = tret[x++]){
10058 if(
10059 _simpleNodeTest(te) &&
10060 (!bag || _isUnique(te, bag)) &&
10061 (filterFunc(te, x))
10062 ){
10063 ret.push(te);
10064 }
10065 }
10066 return ret;
10067 };
10068 };
10069
10070 // test to see if node is below root
10071 var _isDescendant = function(node, root){
10072 var pn = node.parentNode;
10073 while(pn){
10074 if(pn == root){
10075 break;
10076 }
10077 pn = pn.parentNode;
10078 }
10079 return !!pn;
10080 };
10081
10082 var _getElementsFuncCache = {};
10083
10084 var getElementsFunc = function(query){
10085 var retFunc = _getElementsFuncCache[query.query];
10086 // if we've got a cached dispatcher, just use that
10087 if(retFunc){ return retFunc; }
10088 // else, generate a new on
10089
10090 // NOTE:
10091 // this function returns a function that searches for nodes and
10092 // filters them. The search may be specialized by infix operators
10093 // (">", "~", or "+") else it will default to searching all
10094 // descendants (the " " selector). Once a group of children is
10095 // found, a test function is applied to weed out the ones we
10096 // don't want. Many common cases can be fast-pathed. We spend a
10097 // lot of cycles to create a dispatcher that doesn't do more work
10098 // than necessary at any point since, unlike this function, the
10099 // dispatchers will be called every time. The logic of generating
10100 // efficient dispatchers looks like this in pseudo code:
10101 //
10102 // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
10103 // if infixOperator == " ":
10104 // if only(id):
10105 // return def(root):
10106 // return d.byId(id, root);
10107 //
10108 // elif id:
10109 // return def(root):
10110 // return filter(d.byId(id, root));
10111 //
10112 // elif cssClass && getElementsByClassName:
10113 // return def(root):
10114 // return filter(root.getElementsByClassName(cssClass));
10115 //
10116 // elif only(tag):
10117 // return def(root):
10118 // return root.getElementsByTagName(tagName);
10119 //
10120 // else:
10121 // # search by tag name, then filter
10122 // return def(root):
10123 // return filter(root.getElementsByTagName(tagName||"*"));
10124 //
10125 // elif infixOperator == ">":
10126 // # search direct children
10127 // return def(root):
10128 // return filter(root.children);
10129 //
10130 // elif infixOperator == "+":
10131 // # search next sibling
10132 // return def(root):
10133 // return filter(root.nextElementSibling);
10134 //
10135 // elif infixOperator == "~":
10136 // # search rightward siblings
10137 // return def(root):
10138 // return filter(nextSiblings(root));
10139
10140 var io = query.infixOper;
10141 var oper = (io ? io.oper : "");
10142 // the default filter func which tests for all conditions in the query
10143 // part. This is potentially inefficient, so some optimized paths may
10144 // re-define it to test fewer things.
10145 var filterFunc = getSimpleFilterFunc(query, { el: 1 });
10146 var qt = query.tag;
10147 var wildcardTag = ("*" == qt);
10148 var ecs = getDoc()["getElementsByClassName"];
10149
10150 if(!oper){
10151 // if there's no infix operator, then it's a descendant query. ID
10152 // and "elements by class name" variants can be accelerated so we
10153 // call them out explicitly:
10154 if(query.id){
10155 // testing shows that the overhead of yesman() is acceptable
10156 // and can save us some bytes vs. re-defining the function
10157 // everywhere.
10158 filterFunc = (!query.loops && wildcardTag) ?
10159 yesman :
10160 getSimpleFilterFunc(query, { el: 1, id: 1 });
10161
10162 retFunc = function(root, arr){
10163 var te = dom.byId(query.id, (root.ownerDocument||root));
10164 if(!te || !filterFunc(te)){ return; }
10165 if(9 == root.nodeType){ // if root's a doc, we just return directly
10166 return getArr(te, arr);
10167 }else{ // otherwise check ancestry
10168 if(_isDescendant(te, root)){
10169 return getArr(te, arr);
10170 }
10171 }
10172 };
10173 }else if(
10174 ecs &&
10175 // isAlien check. Workaround for Prototype.js being totally evil/dumb.
10176 /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
10177 query.classes.length &&
10178 !cssCaseBug
10179 ){
10180 // it's a class-based query and we've got a fast way to run it.
10181
10182 // ignore class and ID filters since we will have handled both
10183 filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
10184 var classesString = query.classes.join(" ");
10185 retFunc = function(root, arr, bag){
10186 var ret = getArr(0, arr), te, x=0;
10187 var tret = root.getElementsByClassName(classesString);
10188 while((te = tret[x++])){
10189 if(filterFunc(te, root) && _isUnique(te, bag)){
10190 ret.push(te);
10191 }
10192 }
10193 return ret;
10194 };
10195
10196 }else if(!wildcardTag && !query.loops){
10197 // it's tag only. Fast-path it.
10198 retFunc = function(root, arr, bag){
10199 var ret = getArr(0, arr), te, x=0;
10200 var tag = query.getTag(),
10201 tret = tag ? root.getElementsByTagName(tag) : [];
10202 while((te = tret[x++])){
10203 if(_isUnique(te, bag)){
10204 ret.push(te);
10205 }
10206 }
10207 return ret;
10208 };
10209 }else{
10210 // the common case:
10211 // a descendant selector without a fast path. By now it's got
10212 // to have a tag selector, even if it's just "*" so we query
10213 // by that and filter
10214 filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
10215 retFunc = function(root, arr, bag){
10216 var ret = getArr(0, arr), te, x=0;
10217 // we use getTag() to avoid case sensitivity issues
10218 var tag = query.getTag(),
10219 tret = tag ? root.getElementsByTagName(tag) : [];
10220 while((te = tret[x++])){
10221 if(filterFunc(te, root) && _isUnique(te, bag)){
10222 ret.push(te);
10223 }
10224 }
10225 return ret;
10226 };
10227 }
10228 }else{
10229 // the query is scoped in some way. Instead of querying by tag we
10230 // use some other collection to find candidate nodes
10231 var skipFilters = { el: 1 };
10232 if(wildcardTag){
10233 skipFilters.tag = 1;
10234 }
10235 filterFunc = getSimpleFilterFunc(query, skipFilters);
10236 if("+" == oper){
10237 retFunc = _nextSibling(filterFunc);
10238 }else if("~" == oper){
10239 retFunc = _nextSiblings(filterFunc);
10240 }else if(">" == oper){
10241 retFunc = _childElements(filterFunc);
10242 }
10243 }
10244 // cache it and return
10245 return _getElementsFuncCache[query.query] = retFunc;
10246 };
10247
10248 var filterDown = function(root, queryParts){
10249 // NOTE:
10250 // this is the guts of the DOM query system. It takes a list of
10251 // parsed query parts and a root and finds children which match
10252 // the selector represented by the parts
10253 var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
10254
10255 for(var i = 0; i < qpl; i++){
10256 ret = [];
10257 qp = queryParts[i];
10258 x = candidates.length - 1;
10259 if(x > 0){
10260 // if we have more than one root at this level, provide a new
10261 // hash to use for checking group membership but tell the
10262 // system not to post-filter us since we will already have been
10263 // guaranteed to be unique
10264 bag = {};
10265 ret.nozip = true;
10266 }
10267 var gef = getElementsFunc(qp);
10268 for(var j = 0; (te = candidates[j]); j++){
10269 // for every root, get the elements that match the descendant
10270 // selector, adding them to the "ret" array and filtering them
10271 // via membership in this level's bag. If there are more query
10272 // parts, then this level's return will be used as the next
10273 // level's candidates
10274 gef(te, ret, bag);
10275 }
10276 if(!ret.length){ break; }
10277 candidates = ret;
10278 }
10279 return ret;
10280 };
10281
10282 ////////////////////////////////////////////////////////////////////////
10283 // the query runner
10284 ////////////////////////////////////////////////////////////////////////
10285
10286 // these are the primary caches for full-query results. The query
10287 // dispatcher functions are generated then stored here for hash lookup in
10288 // the future
10289 var _queryFuncCacheDOM = {},
10290 _queryFuncCacheQSA = {};
10291
10292 // this is the second level of splitting, from full-length queries (e.g.,
10293 // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
10294 // ".bar"])
10295 var getStepQueryFunc = function(query){
10296 var qparts = getQueryParts(trim(query));
10297
10298 // if it's trivial, avoid iteration and zipping costs
10299 if(qparts.length == 1){
10300 // we optimize this case here to prevent dispatch further down the
10301 // chain, potentially slowing things down. We could more elegantly
10302 // handle this in filterDown(), but it's slower for simple things
10303 // that need to be fast (e.g., "#someId").
10304 var tef = getElementsFunc(qparts[0]);
10305 return function(root){
10306 var r = tef(root, []);
10307 if(r){ r.nozip = true; }
10308 return r;
10309 };
10310 }
10311
10312 // otherwise, break it up and return a runner that iterates over the parts recursively
10313 return function(root){
10314 return filterDown(root, qparts);
10315 };
10316 };
10317
10318 // NOTES:
10319 // * we can't trust QSA for anything but document-rooted queries, so
10320 // caching is split into DOM query evaluators and QSA query evaluators
10321 // * caching query results is dirty and leak-prone (or, at a minimum,
10322 // prone to unbounded growth). Other toolkits may go this route, but
10323 // they totally destroy their own ability to manage their memory
10324 // footprint. If we implement it, it should only ever be with a fixed
10325 // total element reference # limit and an LRU-style algorithm since JS
10326 // has no weakref support. Caching compiled query evaluators is also
10327 // potentially problematic, but even on large documents the size of the
10328 // query evaluators is often < 100 function objects per evaluator (and
10329 // LRU can be applied if it's ever shown to be an issue).
10330 // * since IE's QSA support is currently only for HTML documents and even
10331 // then only in IE 8's "standards mode", we have to detect our dispatch
10332 // route at query time and keep 2 separate caches. Ugg.
10333
10334 // we need to determine if we think we can run a given query via
10335 // querySelectorAll or if we'll need to fall back on DOM queries to get
10336 // there. We need a lot of information about the environment and the query
10337 // to make the determination (e.g. does it support QSA, does the query in
10338 // question work in the native QSA impl, etc.).
10339
10340 // IE QSA queries may incorrectly include comment nodes, so we throw the
10341 // zipping function into "remove" comments mode instead of the normal "skip
10342 // it" which every other QSA-clued browser enjoys
10343 var noZip = has("ie") ? "commentStrip" : "nozip";
10344
10345 var qsa = "querySelectorAll";
10346 var qsaAvail = !!getDoc()[qsa];
10347
10348 //Don't bother with n+3 type of matches, IE complains if we modify those.
10349 var infixSpaceRe = /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
10350 var infixSpaceFunc = function(match, pre, ch, post){
10351 return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match;
10352 };
10353
10354 //Don't apply the infixSpaceRe to attribute value selectors
10355 var attRe = /([^[]*)([^\]]*])?/g;
10356 var attFunc = function(match, nonAtt, att){
10357 return nonAtt.replace(infixSpaceRe, infixSpaceFunc) + (att||"");
10358 };
10359 var getQueryFunc = function(query, forceDOM){
10360 //Normalize query. The CSS3 selectors spec allows for omitting spaces around
10361 //infix operators, >, ~ and +
10362 //Do the work here since detection for spaces is used as a simple "not use QSA"
10363 //test below.
10364 query = query.replace(attRe, attFunc);
10365
10366 if(qsaAvail){
10367 // if we've got a cached variant and we think we can do it, run it!
10368 var qsaCached = _queryFuncCacheQSA[query];
10369 if(qsaCached && !forceDOM){ return qsaCached; }
10370 }
10371
10372 // else if we've got a DOM cached variant, assume that we already know
10373 // all we need to and use it
10374 var domCached = _queryFuncCacheDOM[query];
10375 if(domCached){ return domCached; }
10376
10377 // TODO:
10378 // today we're caching DOM and QSA branches separately so we
10379 // recalc useQSA every time. If we had a way to tag root+query
10380 // efficiently, we'd be in good shape to do a global cache.
10381
10382 var qcz = query.charAt(0);
10383 var nospace = (-1 == query.indexOf(" "));
10384
10385 // byId searches are wicked fast compared to QSA, even when filtering
10386 // is required
10387 if( (query.indexOf("#") >= 0) && (nospace) ){
10388 forceDOM = true;
10389 }
10390
10391 var useQSA = (
10392 qsaAvail && (!forceDOM) &&
10393 // as per CSS 3, we can't currently start w/ combinator:
10394 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
10395 (specials.indexOf(qcz) == -1) &&
10396 // IE's QSA impl sucks on pseudos
10397 (!has("ie") || (query.indexOf(":") == -1)) &&
10398
10399 (!(cssCaseBug && (query.indexOf(".") >= 0))) &&
10400
10401 // FIXME:
10402 // need to tighten up browser rules on ":contains" and "|=" to
10403 // figure out which aren't good
10404 // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
10405 // elements, even though according to spec, selected options should
10406 // match :checked. So go nonQSA for it:
10407 // http://bugs.dojotoolkit.org/ticket/5179
10408 (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) &&
10409 (query.indexOf("|=") == -1) // some browsers don't grok it
10410 );
10411
10412 // TODO:
10413 // if we've got a descendant query (e.g., "> .thinger" instead of
10414 // just ".thinger") in a QSA-able doc, but are passed a child as a
10415 // root, it should be possible to give the item a synthetic ID and
10416 // trivially rewrite the query to the form "#synid > .thinger" to
10417 // use the QSA branch
10418
10419
10420 if(useQSA){
10421 var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ?
10422 (query + " *") : query;
10423 return _queryFuncCacheQSA[query] = function(root){
10424 try{
10425 // the QSA system contains an egregious spec bug which
10426 // limits us, effectively, to only running QSA queries over
10427 // entire documents. See:
10428 // http://ejohn.org/blog/thoughts-on-queryselectorall/
10429 // despite this, we can also handle QSA runs on simple
10430 // selectors, but we don't want detection to be expensive
10431 // so we're just checking for the presence of a space char
10432 // right now. Not elegant, but it's cheaper than running
10433 // the query parser when we might not need to
10434 if(!((9 == root.nodeType) || nospace)){ throw ""; }
10435 var r = root[qsa](tq);
10436 // skip expensive duplication checks and just wrap in a NodeList
10437 r[noZip] = true;
10438 return r;
10439 }catch(e){
10440 // else run the DOM branch on this query, ensuring that we
10441 // default that way in the future
10442 return getQueryFunc(query, true)(root);
10443 }
10444 };
10445 }else{
10446 // DOM branch
10447 var parts = query.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
10448 return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
10449 // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
10450 getStepQueryFunc(query) :
10451 // if it *is* a complex query, break it up into its
10452 // constituent parts and return a dispatcher that will
10453 // merge the parts when run
10454 function(root){
10455 var pindex = 0, // avoid array alloc for every invocation
10456 ret = [],
10457 tp;
10458 while((tp = parts[pindex++])){
10459 ret = ret.concat(getStepQueryFunc(tp)(root));
10460 }
10461 return ret;
10462 }
10463 );
10464 }
10465 };
10466
10467 var _zipIdx = 0;
10468
10469 // NOTE:
10470 // this function is Moo inspired, but our own impl to deal correctly
10471 // with XML in IE
10472 var _nodeUID = has("ie") ? function(node){
10473 if(caseSensitive){
10474 // XML docs don't have uniqueID on their nodes
10475 return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
10476
10477 }else{
10478 return node.uniqueID;
10479 }
10480 } :
10481 function(node){
10482 return (node._uid || (node._uid = ++_zipIdx));
10483 };
10484
10485 // determine if a node in is unique in a "bag". In this case we don't want
10486 // to flatten a list of unique items, but rather just tell if the item in
10487 // question is already in the bag. Normally we'd just use hash lookup to do
10488 // this for us but IE's DOM is busted so we can't really count on that. On
10489 // the upside, it gives us a built in unique ID function.
10490 var _isUnique = function(node, bag){
10491 if(!bag){ return 1; }
10492 var id = _nodeUID(node);
10493 if(!bag[id]){ return bag[id] = 1; }
10494 return 0;
10495 };
10496
10497 // attempt to efficiently determine if an item in a list is a dupe,
10498 // returning a list of "uniques", hopefully in document order
10499 var _zipIdxName = "_zipIdx";
10500 var _zip = function(arr){
10501 if(arr && arr.nozip){
10502 return arr;
10503 }
10504 var ret = [];
10505 if(!arr || !arr.length){ return ret; }
10506 if(arr[0]){
10507 ret.push(arr[0]);
10508 }
10509 if(arr.length < 2){ return ret; }
10510
10511 _zipIdx++;
10512
10513 // we have to fork here for IE and XML docs because we can't set
10514 // expandos on their nodes (apparently). *sigh*
10515 var x, te;
10516 if(has("ie") && caseSensitive){
10517 var szidx = _zipIdx+"";
10518 arr[0].setAttribute(_zipIdxName, szidx);
10519 for(x = 1; te = arr[x]; x++){
10520 if(arr[x].getAttribute(_zipIdxName) != szidx){
10521 ret.push(te);
10522 }
10523 te.setAttribute(_zipIdxName, szidx);
10524 }
10525 }else if(has("ie") && arr.commentStrip){
10526 try{
10527 for(x = 1; te = arr[x]; x++){
10528 if(_isElement(te)){
10529 ret.push(te);
10530 }
10531 }
10532 }catch(e){ /* squelch */ }
10533 }else{
10534 if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; }
10535 for(x = 1; te = arr[x]; x++){
10536 if(arr[x][_zipIdxName] != _zipIdx){
10537 ret.push(te);
10538 }
10539 te[_zipIdxName] = _zipIdx;
10540 }
10541 }
10542 return ret;
10543 };
10544
10545 // the main executor
10546 var query = function(/*String*/ query, /*String|DOMNode?*/ root){
10547 // summary:
10548 // Returns nodes which match the given CSS3 selector, searching the
10549 // entire document by default but optionally taking a node to scope
10550 // the search by. Returns an array.
10551 // description:
10552 // dojo.query() is the swiss army knife of DOM node manipulation in
10553 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
10554 // "$" function, dojo.query provides robust, high-performance
10555 // CSS-based node selector support with the option of scoping searches
10556 // to a particular sub-tree of a document.
10557 //
10558 // Supported Selectors:
10559 // --------------------
10560 //
10561 // acme supports a rich set of CSS3 selectors, including:
10562 //
10563 // - class selectors (e.g., `.foo`)
10564 // - node type selectors like `span`
10565 // - ` ` descendant selectors
10566 // - `>` child element selectors
10567 // - `#foo` style ID selectors
10568 // - `*` universal selector
10569 // - `~`, the preceded-by sibling selector
10570 // - `+`, the immediately preceded-by sibling selector
10571 // - attribute queries:
10572 // - `[foo]` attribute presence selector
10573 // - `[foo='bar']` attribute value exact match
10574 // - `[foo~='bar']` attribute value list item match
10575 // - `[foo^='bar']` attribute start match
10576 // - `[foo$='bar']` attribute end match
10577 // - `[foo*='bar']` attribute substring match
10578 // - `:first-child`, `:last-child`, and `:only-child` positional selectors
10579 // - `:empty` content emtpy selector
10580 // - `:checked` pseudo selector
10581 // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
10582 // - `:nth-child(even)`, `:nth-child(odd)` positional selectors
10583 // - `:not(...)` negation pseudo selectors
10584 //
10585 // Any legal combination of these selectors will work with
10586 // `dojo.query()`, including compound selectors ("," delimited).
10587 // Very complex and useful searches can be constructed with this
10588 // palette of selectors and when combined with functions for
10589 // manipulation presented by dojo/NodeList, many types of DOM
10590 // manipulation operations become very straightforward.
10591 //
10592 // Unsupported Selectors:
10593 // ----------------------
10594 //
10595 // While dojo.query handles many CSS3 selectors, some fall outside of
10596 // what's reasonable for a programmatic node querying engine to
10597 // handle. Currently unsupported selectors include:
10598 //
10599 // - namespace-differentiated selectors of any form
10600 // - all `::` pseduo-element selectors
10601 // - certain pseudo-selectors which don't get a lot of day-to-day use:
10602 // - `:root`, `:lang()`, `:target`, `:focus`
10603 // - all visual and state selectors:
10604 // - `:root`, `:active`, `:hover`, `:visited`, `:link`,
10605 // `:enabled`, `:disabled`
10606 // - `:*-of-type` pseudo selectors
10607 //
10608 // dojo.query and XML Documents:
10609 // -----------------------------
10610 //
10611 // `dojo.query` (as of dojo 1.2) supports searching XML documents
10612 // in a case-sensitive manner. If an HTML document is served with
10613 // a doctype that forces case-sensitivity (e.g., XHTML 1.1
10614 // Strict), dojo.query() will detect this and "do the right
10615 // thing". Case sensitivity is dependent upon the document being
10616 // searched and not the query used. It is therefore possible to
10617 // use case-sensitive queries on strict sub-documents (iframes,
10618 // etc.) or XML documents while still assuming case-insensitivity
10619 // for a host/root document.
10620 //
10621 // Non-selector Queries:
10622 // ---------------------
10623 //
10624 // If something other than a String is passed for the query,
10625 // `dojo.query` will return a new `dojo/NodeList` instance
10626 // constructed from that parameter alone and all further
10627 // processing will stop. This means that if you have a reference
10628 // to a node or NodeList, you can quickly construct a new NodeList
10629 // from the original by calling `dojo.query(node)` or
10630 // `dojo.query(list)`.
10631 //
10632 // query:
10633 // The CSS3 expression to match against. For details on the syntax of
10634 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
10635 // root:
10636 // A DOMNode (or node id) to scope the search from. Optional.
10637 // returns: Array
10638 // example:
10639 // search the entire document for elements with the class "foo":
10640 // | dojo.query(".foo");
10641 // these elements will match:
10642 // | <span class="foo"></span>
10643 // | <span class="foo bar"></span>
10644 // | <p class="thud foo"></p>
10645 // example:
10646 // search the entire document for elements with the classes "foo" *and* "bar":
10647 // | dojo.query(".foo.bar");
10648 // these elements will match:
10649 // | <span class="foo bar"></span>
10650 // while these will not:
10651 // | <span class="foo"></span>
10652 // | <p class="thud foo"></p>
10653 // example:
10654 // find `<span>` elements which are descendants of paragraphs and
10655 // which have a "highlighted" class:
10656 // | dojo.query("p span.highlighted");
10657 // the innermost span in this fragment matches:
10658 // | <p class="foo">
10659 // | <span>...
10660 // | <span class="highlighted foo bar">...</span>
10661 // | </span>
10662 // | </p>
10663 // example:
10664 // set an "odd" class on all odd table rows inside of the table
10665 // `#tabular_data`, using the `>` (direct child) selector to avoid
10666 // affecting any nested tables:
10667 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
10668 // example:
10669 // remove all elements with the class "error" from the document
10670 // and store them in a list:
10671 // | var errors = dojo.query(".error").orphan();
10672 // example:
10673 // add an onclick handler to every submit button in the document
10674 // which causes the form to be sent via Ajax instead:
10675 // | dojo.query("input[type='submit']").onclick(function(e){
10676 // | dojo.stopEvent(e); // prevent sending the form
10677 // | var btn = e.target;
10678 // | dojo.xhrPost({
10679 // | form: btn.form,
10680 // | load: function(data){
10681 // | // replace the form with the response
10682 // | var div = dojo.doc.createElement("div");
10683 // | dojo.place(div, btn.form, "after");
10684 // | div.innerHTML = data;
10685 // | dojo.style(btn.form, "display", "none");
10686 // | }
10687 // | });
10688 // | });
10689
10690 root = root || getDoc();
10691
10692 // throw the big case sensitivity switch
10693 var od = root.ownerDocument || root; // root is either Document or a node inside the document
10694 caseSensitive = (od.createElement("div").tagName === "div");
10695
10696 // NOTE:
10697 // adding "true" as the 2nd argument to getQueryFunc is useful for
10698 // testing the DOM branch without worrying about the
10699 // behavior/performance of the QSA branch.
10700 var r = getQueryFunc(query)(root);
10701
10702 // FIXME:
10703 // need to investigate this branch WRT #8074 and #8075
10704 if(r && r.nozip){
10705 return r;
10706 }
10707 return _zip(r); // dojo/NodeList
10708 };
10709 query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
10710 // summary:
10711 // function for filtering a NodeList based on a selector, optimized for simple selectors
10712 var tmpNodeList = [],
10713 parts = getQueryParts(filter),
10714 filterFunc =
10715 (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
10716 getSimpleFilterFunc(parts[0]) :
10717 function(node){
10718 return array.indexOf(query(filter, dom.byId(root)), node) != -1;
10719 };
10720 for(var x = 0, te; te = nodeList[x]; x++){
10721 if(filterFunc(te)){ tmpNodeList.push(te); }
10722 }
10723 return tmpNodeList;
10724 };
10725 return query;
10726 });
10727
10728 },
10729 'dojo/dnd/autoscroll':function(){
10730 define("dojo/dnd/autoscroll", ["../_base/lang", "../sniff", "../_base/window", "../dom-geometry", "../dom-style", "../window"],
10731 function(lang, has, win, domGeom, domStyle, winUtils){
10732
10733 // module:
10734 // dojo/dnd/autoscroll
10735
10736 var exports = {
10737 // summary:
10738 // Used by dojo/dnd/Manager to scroll document or internal node when the user
10739 // drags near the edge of the viewport or a scrollable node
10740 };
10741 lang.setObject("dojo.dnd.autoscroll", exports);
10742
10743 exports.getViewport = winUtils.getBox;
10744
10745 exports.V_TRIGGER_AUTOSCROLL = 32;
10746 exports.H_TRIGGER_AUTOSCROLL = 32;
10747
10748 exports.V_AUTOSCROLL_VALUE = 16;
10749 exports.H_AUTOSCROLL_VALUE = 16;
10750
10751 // These are set by autoScrollStart().
10752 // Set to default values in case autoScrollStart() isn't called. (back-compat, remove for 2.0)
10753 var viewport,
10754 doc = win.doc,
10755 maxScrollTop = Infinity,
10756 maxScrollLeft = Infinity;
10757
10758 exports.autoScrollStart = function(d){
10759 // summary:
10760 // Called at the start of a drag.
10761 // d: Document
10762 // The document of the node being dragged.
10763
10764 doc = d;
10765 viewport = winUtils.getBox(doc);
10766
10767 // Save height/width of document at start of drag, before it gets distorted by a user dragging an avatar past
10768 // the document's edge
10769 var html = win.body(doc).parentNode;
10770 maxScrollTop = Math.max(html.scrollHeight - viewport.h, 0);
10771 maxScrollLeft = Math.max(html.scrollWidth - viewport.w, 0); // usually 0
10772 };
10773
10774 exports.autoScroll = function(e){
10775 // summary:
10776 // a handler for mousemove and touchmove events, which scrolls the window, if
10777 // necessary
10778 // e: Event
10779 // mousemove/touchmove event
10780
10781 // FIXME: needs more docs!
10782 var v = viewport || winUtils.getBox(doc), // getBox() call for back-compat, in case autoScrollStart() wasn't called
10783 html = win.body(doc).parentNode,
10784 dx = 0, dy = 0;
10785 if(e.clientX < exports.H_TRIGGER_AUTOSCROLL){
10786 dx = -exports.H_AUTOSCROLL_VALUE;
10787 }else if(e.clientX > v.w - exports.H_TRIGGER_AUTOSCROLL){
10788 dx = Math.min(exports.H_AUTOSCROLL_VALUE, maxScrollLeft - html.scrollLeft); // don't scroll past edge of doc
10789 }
10790 if(e.clientY < exports.V_TRIGGER_AUTOSCROLL){
10791 dy = -exports.V_AUTOSCROLL_VALUE;
10792 }else if(e.clientY > v.h - exports.V_TRIGGER_AUTOSCROLL){
10793 dy = Math.min(exports.V_AUTOSCROLL_VALUE, maxScrollTop - html.scrollTop); // don't scroll past edge of doc
10794 }
10795 window.scrollBy(dx, dy);
10796 };
10797
10798 exports._validNodes = {"div": 1, "p": 1, "td": 1};
10799 exports._validOverflow = {"auto": 1, "scroll": 1};
10800
10801 exports.autoScrollNodes = function(e){
10802 // summary:
10803 // a handler for mousemove and touchmove events, which scrolls the first available
10804 // Dom element, it falls back to exports.autoScroll()
10805 // e: Event
10806 // mousemove/touchmove event
10807
10808 // FIXME: needs more docs!
10809
10810 var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
10811
10812 for(var n = e.target; n;){
10813 if(n.nodeType == 1 && (n.tagName.toLowerCase() in exports._validNodes)){
10814 var s = domStyle.getComputedStyle(n),
10815 overflow = (s.overflow.toLowerCase() in exports._validOverflow),
10816 overflowX = (s.overflowX.toLowerCase() in exports._validOverflow),
10817 overflowY = (s.overflowY.toLowerCase() in exports._validOverflow);
10818 if(overflow || overflowX || overflowY){
10819 b = domGeom.getContentBox(n, s);
10820 t = domGeom.position(n, true);
10821 }
10822 // overflow-x
10823 if(overflow || overflowX){
10824 w = Math.min(exports.H_TRIGGER_AUTOSCROLL, b.w / 2);
10825 rx = e.pageX - t.x;
10826 if(has("webkit") || has("opera")){
10827 // FIXME: this code should not be here, it should be taken into account
10828 // either by the event fixing code, or the domGeom.position()
10829 // FIXME: this code doesn't work on Opera 9.5 Beta
10830 rx += win.body().scrollLeft;
10831 }
10832 dx = 0;
10833 if(rx > 0 && rx < b.w){
10834 if(rx < w){
10835 dx = -w;
10836 }else if(rx > b.w - w){
10837 dx = w;
10838 }
10839 oldLeft = n.scrollLeft;
10840 n.scrollLeft = n.scrollLeft + dx;
10841 }
10842 }
10843 // overflow-y
10844 if(overflow || overflowY){
10845 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
10846 h = Math.min(exports.V_TRIGGER_AUTOSCROLL, b.h / 2);
10847 ry = e.pageY - t.y;
10848 if(has("webkit") || has("opera")){
10849 // FIXME: this code should not be here, it should be taken into account
10850 // either by the event fixing code, or the domGeom.position()
10851 // FIXME: this code doesn't work on Opera 9.5 Beta
10852 ry += win.body().scrollTop;
10853 }
10854 dy = 0;
10855 if(ry > 0 && ry < b.h){
10856 if(ry < h){
10857 dy = -h;
10858 }else if(ry > b.h - h){
10859 dy = h;
10860 }
10861 oldTop = n.scrollTop;
10862 n.scrollTop = n.scrollTop + dy;
10863 }
10864 }
10865 if(dx || dy){ return; }
10866 }
10867 try{
10868 n = n.parentNode;
10869 }catch(x){
10870 n = null;
10871 }
10872 }
10873 exports.autoScroll(e);
10874 };
10875
10876 return exports;
10877
10878 });
10879
10880 },
10881 'dijit/form/_RadioButtonMixin':function(){
10882 define("dijit/form/_RadioButtonMixin", [
10883 "dojo/_base/array", // array.forEach
10884 "dojo/_base/declare", // declare
10885 "dojo/dom-attr", // domAttr.set
10886 "dojo/_base/event", // event.stop
10887 "dojo/_base/lang", // lang.hitch
10888 "dojo/query", // query
10889 "../registry" // registry.getEnclosingWidget
10890 ], function(array, declare, domAttr, event, lang, query, registry){
10891
10892 // module:
10893 // dijit/form/_RadioButtonMixin
10894
10895 return declare("dijit.form._RadioButtonMixin", null, {
10896 // summary:
10897 // Mixin to provide widget functionality for an HTML radio button
10898
10899 // type: [private] String
10900 // type attribute on `<input>` node.
10901 // Users should not change this value.
10902 type: "radio",
10903
10904 _getRelatedWidgets: function(){
10905 // Private function needed to help iterate over all radio buttons in a group.
10906 var ary = [];
10907 query("input[type=radio]", this.focusNode.form || this.ownerDocument).forEach( // can't use name= since query doesn't support [] in the name
10908 lang.hitch(this, function(inputNode){
10909 if(inputNode.name == this.name && inputNode.form == this.focusNode.form){
10910 var widget = registry.getEnclosingWidget(inputNode);
10911 if(widget){
10912 ary.push(widget);
10913 }
10914 }
10915 })
10916 );
10917 return ary;
10918 },
10919
10920 _setCheckedAttr: function(/*Boolean*/ value){
10921 // If I am being checked then have to deselect currently checked radio button
10922 this.inherited(arguments);
10923 if(!this._created){ return; }
10924 if(value){
10925 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
10926 if(widget != this && widget.checked){
10927 widget.set('checked', false);
10928 }
10929 }));
10930 }
10931 },
10932
10933 _getSubmitValue: function(/*String*/ value){
10934 return value === null ? "on" : value;
10935 },
10936
10937 _onClick: function(/*Event*/ e){
10938 if(this.checked || this.disabled){ // nothing to do
10939 event.stop(e);
10940 return false;
10941 }
10942 if(this.readOnly){ // ignored by some browsers so we have to resync the DOM elements with widget values
10943 event.stop(e);
10944 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
10945 domAttr.set(this.focusNode || this.domNode, 'checked', widget.checked);
10946 }));
10947 return false;
10948 }
10949 return this.inherited(arguments);
10950 }
10951 });
10952 });
10953
10954 },
10955 'dojo/data/ItemFileWriteStore':function(){
10956 define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/kernel",
10957 "./ItemFileReadStore", "../date/stamp"
10958 ], function(lang, declare, arrayUtil, jsonUtil, kernel, ItemFileReadStore, dateStamp){
10959
10960 // module:
10961 // dojo/data/ItemFileWriteStore
10962
10963 return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore, {
10964 // summary:
10965 // TODOC
10966
10967 constructor: function(/* object */ keywordParameters){
10968 // keywordParameters:
10969 // The structure of the typeMap object is as follows:
10970 // | {
10971 // | type0: function || object,
10972 // | type1: function || object,
10973 // | ...
10974 // | typeN: function || object
10975 // | }
10976 // Where if it is a function, it is assumed to be an object constructor that takes the
10977 // value of _value as the initialization parameters. It is serialized assuming object.toString()
10978 // serialization. If it is an object, then it is assumed
10979 // to be an object of general form:
10980 // | {
10981 // | type: function, //constructor.
10982 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
10983 // | serialize: function(object) //The function that converts the object back into the proper file format form.
10984 // | }
10985
10986 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
10987 this._features['dojo.data.api.Write'] = true;
10988 this._features['dojo.data.api.Notification'] = true;
10989
10990 // For keeping track of changes so that we can implement isDirty and revert
10991 this._pending = {
10992 _newItems:{},
10993 _modifiedItems:{},
10994 _deletedItems:{}
10995 };
10996
10997 if(!this._datatypeMap['Date'].serialize){
10998 this._datatypeMap['Date'].serialize = function(obj){
10999 return dateStamp.toISOString(obj, {zulu:true});
11000 };
11001 }
11002 //Disable only if explicitly set to false.
11003 if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
11004 this.referenceIntegrity = false;
11005 }
11006
11007 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
11008 this._saveInProgress = false;
11009 },
11010
11011 referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
11012
11013 _assert: function(/* boolean */ condition){
11014 if(!condition){
11015 throw new Error("assertion failed in ItemFileWriteStore");
11016 }
11017 },
11018
11019 _getIdentifierAttribute: function(){
11020 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
11021 return this.getFeatures()['dojo.data.api.Identity'];
11022 },
11023
11024
11025 /* dojo/data/api/Write */
11026
11027 newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
11028 // summary:
11029 // See dojo/data/api/Write.newItem()
11030
11031 this._assert(!this._saveInProgress);
11032
11033 if(!this._loadFinished){
11034 // We need to do this here so that we'll be able to find out what
11035 // identifierAttribute was specified in the data file.
11036 this._forceLoad();
11037 }
11038
11039 if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
11040 throw new Error("newItem() was passed something other than an object");
11041 }
11042 var newIdentity = null;
11043 var identifierAttribute = this._getIdentifierAttribute();
11044 if(identifierAttribute === Number){
11045 newIdentity = this._arrayOfAllItems.length;
11046 }else{
11047 newIdentity = keywordArgs[identifierAttribute];
11048 if(typeof newIdentity === "undefined"){
11049 throw new Error("newItem() was not passed an identity for the new item");
11050 }
11051 if(lang.isArray(newIdentity)){
11052 throw new Error("newItem() was not passed an single-valued identity");
11053 }
11054 }
11055
11056 // make sure this identity is not already in use by another item, if identifiers were
11057 // defined in the file. Otherwise it would be the item count,
11058 // which should always be unique in this case.
11059 if(this._itemsByIdentity){
11060 this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
11061 }
11062 this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
11063 this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
11064
11065 var newItem = {};
11066 newItem[this._storeRefPropName] = this;
11067 newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
11068 if(this._itemsByIdentity){
11069 this._itemsByIdentity[newIdentity] = newItem;
11070 //We have to set the identifier now, otherwise we can't look it
11071 //up at calls to setValueorValues in parentInfo handling.
11072 newItem[identifierAttribute] = [newIdentity];
11073 }
11074 this._arrayOfAllItems.push(newItem);
11075
11076 //We need to construct some data for the onNew call too...
11077 var pInfo = null;
11078
11079 // Now we need to check to see where we want to assign this thingm if any.
11080 if(parentInfo && parentInfo.parent && parentInfo.attribute){
11081 pInfo = {
11082 item: parentInfo.parent,
11083 attribute: parentInfo.attribute,
11084 oldValue: undefined
11085 };
11086
11087 //See if it is multi-valued or not and handle appropriately
11088 //Generally, all attributes are multi-valued for this store
11089 //So, we only need to append if there are already values present.
11090 var values = this.getValues(parentInfo.parent, parentInfo.attribute);
11091 if(values && values.length > 0){
11092 var tempValues = values.slice(0, values.length);
11093 if(values.length === 1){
11094 pInfo.oldValue = values[0];
11095 }else{
11096 pInfo.oldValue = values.slice(0, values.length);
11097 }
11098 tempValues.push(newItem);
11099 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
11100 pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
11101 }else{
11102 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
11103 pInfo.newValue = newItem;
11104 }
11105 }else{
11106 //Toplevel item, add to both top list as well as all list.
11107 newItem[this._rootItemPropName]=true;
11108 this._arrayOfTopLevelItems.push(newItem);
11109 }
11110
11111 this._pending._newItems[newIdentity] = newItem;
11112
11113 //Clone over the properties to the new item
11114 for(var key in keywordArgs){
11115 if(key === this._storeRefPropName || key === this._itemNumPropName){
11116 // Bummer, the user is trying to do something like
11117 // newItem({_S:"foo"}). Unfortunately, our superclass,
11118 // ItemFileReadStore, is already using _S in each of our items
11119 // to hold private info. To avoid a naming collision, we
11120 // need to move all our private info to some other property
11121 // of all the items/objects. So, we need to iterate over all
11122 // the items and do something like:
11123 // item.__S = item._S;
11124 // item._S = undefined;
11125 // But first we have to make sure the new "__S" variable is
11126 // not in use, which means we have to iterate over all the
11127 // items checking for that.
11128 throw new Error("encountered bug in ItemFileWriteStore.newItem");
11129 }
11130 var value = keywordArgs[key];
11131 if(!lang.isArray(value)){
11132 value = [value];
11133 }
11134 newItem[key] = value;
11135 if(this.referenceIntegrity){
11136 for(var i = 0; i < value.length; i++){
11137 var val = value[i];
11138 if(this.isItem(val)){
11139 this._addReferenceToMap(val, newItem, key);
11140 }
11141 }
11142 }
11143 }
11144 this.onNew(newItem, pInfo); // dojo/data/api/Notification call
11145 return newItem; // item
11146 },
11147
11148 _removeArrayElement: function(/* Array */ array, /* anything */ element){
11149 var index = arrayUtil.indexOf(array, element);
11150 if(index != -1){
11151 array.splice(index, 1);
11152 return true;
11153 }
11154 return false;
11155 },
11156
11157 deleteItem: function(/* dojo/data/api/Item */ item){
11158 // summary:
11159 // See dojo/data/api/Write.deleteItem()
11160 this._assert(!this._saveInProgress);
11161 this._assertIsItem(item);
11162
11163 // Remove this item from the _arrayOfAllItems, but leave a null value in place
11164 // of the item, so as not to change the length of the array, so that in newItem()
11165 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
11166 var indexInArrayOfAllItems = item[this._itemNumPropName];
11167 var identity = this.getIdentity(item);
11168
11169 //If we have reference integrity on, we need to do reference cleanup for the deleted item
11170 if(this.referenceIntegrity){
11171 //First scan all the attributes of this items for references and clean them up in the map
11172 //As this item is going away, no need to track its references anymore.
11173
11174 //Get the attributes list before we generate the backup so it
11175 //doesn't pollute the attributes list.
11176 var attributes = this.getAttributes(item);
11177
11178 //Backup the map, we'll have to restore it potentially, in a revert.
11179 if(item[this._reverseRefMap]){
11180 item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
11181 }
11182
11183 //TODO: This causes a reversion problem. This list won't be restored on revert since it is
11184 //attached to the 'value'. item, not ours. Need to back tese up somehow too.
11185 //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
11186 //later. Or just record them and call _addReferenceToMap on them in revert.
11187 arrayUtil.forEach(attributes, function(attribute){
11188 arrayUtil.forEach(this.getValues(item, attribute), function(value){
11189 if(this.isItem(value)){
11190 //We have to back up all the references we had to others so they can be restored on a revert.
11191 if(!item["backupRefs_" + this._reverseRefMap]){
11192 item["backupRefs_" + this._reverseRefMap] = [];
11193 }
11194 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
11195 this._removeReferenceFromMap(value, item, attribute);
11196 }
11197 }, this);
11198 }, this);
11199
11200 //Next, see if we have references to this item, if we do, we have to clean them up too.
11201 var references = item[this._reverseRefMap];
11202 if(references){
11203 //Look through all the items noted as references to clean them up.
11204 for(var itemId in references){
11205 var containingItem = null;
11206 if(this._itemsByIdentity){
11207 containingItem = this._itemsByIdentity[itemId];
11208 }else{
11209 containingItem = this._arrayOfAllItems[itemId];
11210 }
11211 //We have a reference to a containing item, now we have to process the
11212 //attributes and clear all references to the item being deleted.
11213 if(containingItem){
11214 for(var attribute in references[itemId]){
11215 var oldValues = this.getValues(containingItem, attribute) || [];
11216 var newValues = arrayUtil.filter(oldValues, function(possibleItem){
11217 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
11218 }, this);
11219 //Remove the note of the reference to the item and set the values on the modified attribute.
11220 this._removeReferenceFromMap(item, containingItem, attribute);
11221 if(newValues.length < oldValues.length){
11222 this._setValueOrValues(containingItem, attribute, newValues, true);
11223 }
11224 }
11225 }
11226 }
11227 }
11228 }
11229
11230 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
11231
11232 item[this._storeRefPropName] = null;
11233 if(this._itemsByIdentity){
11234 delete this._itemsByIdentity[identity];
11235 }
11236 this._pending._deletedItems[identity] = item;
11237
11238 //Remove from the toplevel items, if necessary...
11239 if(item[this._rootItemPropName]){
11240 this._removeArrayElement(this._arrayOfTopLevelItems, item);
11241 }
11242 this.onDelete(item); // dojo/data/api/Notification call
11243 return true;
11244 },
11245
11246 setValue: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
11247 // summary:
11248 // See dojo/data/api/Write.set()
11249 return this._setValueOrValues(item, attribute, value, true); // boolean
11250 },
11251
11252 setValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* array */ values){
11253 // summary:
11254 // See dojo/data/api/Write.setValues()
11255 return this._setValueOrValues(item, attribute, values, true); // boolean
11256 },
11257
11258 unsetAttribute: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
11259 // summary:
11260 // See dojo/data/api/Write.unsetAttribute()
11261 return this._setValueOrValues(item, attribute, [], true);
11262 },
11263
11264 _setValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
11265 this._assert(!this._saveInProgress);
11266
11267 // Check for valid arguments
11268 this._assertIsItem(item);
11269 this._assert(lang.isString(attribute));
11270 this._assert(typeof newValueOrValues !== "undefined");
11271
11272 // Make sure the user isn't trying to change the item's identity
11273 var identifierAttribute = this._getIdentifierAttribute();
11274 if(attribute == identifierAttribute){
11275 throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
11276 }
11277
11278 // To implement the Notification API, we need to make a note of what
11279 // the old attribute value was, so that we can pass that info when
11280 // we call the onSet method.
11281 var oldValueOrValues = this._getValueOrValues(item, attribute);
11282
11283 var identity = this.getIdentity(item);
11284 if(!this._pending._modifiedItems[identity]){
11285 // Before we actually change the item, we make a copy of it to
11286 // record the original state, so that we'll be able to revert if
11287 // the revert method gets called. If the item has already been
11288 // modified then there's no need to do this now, since we already
11289 // have a record of the original state.
11290 var copyOfItemState = {};
11291 for(var key in item){
11292 if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
11293 copyOfItemState[key] = item[key];
11294 }else if(key === this._reverseRefMap){
11295 copyOfItemState[key] = lang.clone(item[key]);
11296 }else{
11297 copyOfItemState[key] = item[key].slice(0, item[key].length);
11298 }
11299 }
11300 // Now mark the item as dirty, and save the copy of the original state
11301 this._pending._modifiedItems[identity] = copyOfItemState;
11302 }
11303
11304 // Okay, now we can actually change this attribute on the item
11305 var success = false;
11306
11307 if(lang.isArray(newValueOrValues) && newValueOrValues.length === 0){
11308
11309 // If we were passed an empty array as the value, that counts
11310 // as "unsetting" the attribute, so we need to remove this
11311 // attribute from the item.
11312 success = delete item[attribute];
11313 newValueOrValues = undefined; // used in the onSet Notification call below
11314
11315 if(this.referenceIntegrity && oldValueOrValues){
11316 var oldValues = oldValueOrValues;
11317 if(!lang.isArray(oldValues)){
11318 oldValues = [oldValues];
11319 }
11320 for(var i = 0; i < oldValues.length; i++){
11321 var value = oldValues[i];
11322 if(this.isItem(value)){
11323 this._removeReferenceFromMap(value, item, attribute);
11324 }
11325 }
11326 }
11327 }else{
11328 var newValueArray;
11329 if(lang.isArray(newValueOrValues)){
11330 // Unfortunately, it's not safe to just do this:
11331 // newValueArray = newValueOrValues;
11332 // Instead, we need to copy the array, which slice() does very nicely.
11333 // This is so that our internal data structure won't
11334 // get corrupted if the user mucks with the values array *after*
11335 // calling setValues().
11336 newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
11337 }else{
11338 newValueArray = [newValueOrValues];
11339 }
11340
11341 //We need to handle reference integrity if this is on.
11342 //In the case of set, we need to see if references were added or removed
11343 //and update the reference tracking map accordingly.
11344 if(this.referenceIntegrity){
11345 if(oldValueOrValues){
11346 var oldValues = oldValueOrValues;
11347 if(!lang.isArray(oldValues)){
11348 oldValues = [oldValues];
11349 }
11350 //Use an associative map to determine what was added/removed from the list.
11351 //Should be O(n) performant. First look at all the old values and make a list of them
11352 //Then for any item not in the old list, we add it. If it was already present, we remove it.
11353 //Then we pass over the map and any references left it it need to be removed (IE, no match in
11354 //the new values list).
11355 var map = {};
11356 arrayUtil.forEach(oldValues, function(possibleItem){
11357 if(this.isItem(possibleItem)){
11358 var id = this.getIdentity(possibleItem);
11359 map[id.toString()] = true;
11360 }
11361 }, this);
11362 arrayUtil.forEach(newValueArray, function(possibleItem){
11363 if(this.isItem(possibleItem)){
11364 var id = this.getIdentity(possibleItem);
11365 if(map[id.toString()]){
11366 delete map[id.toString()];
11367 }else{
11368 this._addReferenceToMap(possibleItem, item, attribute);
11369 }
11370 }
11371 }, this);
11372 for(var rId in map){
11373 var removedItem;
11374 if(this._itemsByIdentity){
11375 removedItem = this._itemsByIdentity[rId];
11376 }else{
11377 removedItem = this._arrayOfAllItems[rId];
11378 }
11379 this._removeReferenceFromMap(removedItem, item, attribute);
11380 }
11381 }else{
11382 //Everything is new (no old values) so we have to just
11383 //insert all the references, if any.
11384 for(var i = 0; i < newValueArray.length; i++){
11385 var value = newValueArray[i];
11386 if(this.isItem(value)){
11387 this._addReferenceToMap(value, item, attribute);
11388 }
11389 }
11390 }
11391 }
11392 item[attribute] = newValueArray;
11393 success = true;
11394 }
11395
11396 // Now we make the dojo/data/api/Notification call
11397 if(callOnSet){
11398 this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
11399 }
11400 return success; // boolean
11401 },
11402
11403 _addReferenceToMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
11404 // summary:
11405 // Method to add an reference map entry for an item and attribute.
11406 // description:
11407 // Method to add an reference map entry for an item and attribute.
11408 // refItem:
11409 // The item that is referenced.
11410 // parentItem:
11411 // The item that holds the new reference to refItem.
11412 // attribute:
11413 // The attribute on parentItem that contains the new reference.
11414
11415 var parentId = this.getIdentity(parentItem);
11416 var references = refItem[this._reverseRefMap];
11417
11418 if(!references){
11419 references = refItem[this._reverseRefMap] = {};
11420 }
11421 var itemRef = references[parentId];
11422 if(!itemRef){
11423 itemRef = references[parentId] = {};
11424 }
11425 itemRef[attribute] = true;
11426 },
11427
11428 _removeReferenceFromMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
11429 // summary:
11430 // Method to remove an reference map entry for an item and attribute.
11431 // description:
11432 // Method to remove an reference map entry for an item and attribute. This will
11433 // also perform cleanup on the map such that if there are no more references at all to
11434 // the item, its reference object and entry are removed.
11435 // refItem:
11436 // The item that is referenced.
11437 // parentItem:
11438 // The item holding a reference to refItem.
11439 // attribute:
11440 // The attribute on parentItem that contains the reference.
11441 var identity = this.getIdentity(parentItem);
11442 var references = refItem[this._reverseRefMap];
11443 var itemId;
11444 if(references){
11445 for(itemId in references){
11446 if(itemId == identity){
11447 delete references[itemId][attribute];
11448 if(this._isEmpty(references[itemId])){
11449 delete references[itemId];
11450 }
11451 }
11452 }
11453 if(this._isEmpty(references)){
11454 delete refItem[this._reverseRefMap];
11455 }
11456 }
11457 },
11458
11459 _dumpReferenceMap: function(){
11460 // summary:
11461 // Function to dump the reverse reference map of all items in the store for debug purposes.
11462 // description:
11463 // Function to dump the reverse reference map of all items in the store for debug purposes.
11464 var i;
11465 for(i = 0; i < this._arrayOfAllItems.length; i++){
11466 var item = this._arrayOfAllItems[i];
11467 if(item && item[this._reverseRefMap]){
11468 console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + jsonUtil.toJson(item[this._reverseRefMap]));
11469 }
11470 }
11471 },
11472
11473 _getValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
11474 var valueOrValues = undefined;
11475 if(this.hasAttribute(item, attribute)){
11476 var valueArray = this.getValues(item, attribute);
11477 if(valueArray.length == 1){
11478 valueOrValues = valueArray[0];
11479 }else{
11480 valueOrValues = valueArray;
11481 }
11482 }
11483 return valueOrValues;
11484 },
11485
11486 _flatten: function(/* anything */ value){
11487 if(this.isItem(value)){
11488 // Given an item, return an serializable object that provides a
11489 // reference to the item.
11490 // For example, given kermit:
11491 // var kermit = store.newItem({id:2, name:"Kermit"});
11492 // we want to return
11493 // {_reference:2}
11494 return {_reference: this.getIdentity(value)};
11495 }else{
11496 if(typeof value === "object"){
11497 for(var type in this._datatypeMap){
11498 var typeMap = this._datatypeMap[type];
11499 if(lang.isObject(typeMap) && !lang.isFunction(typeMap)){
11500 if(value instanceof typeMap.type){
11501 if(!typeMap.serialize){
11502 throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
11503 }
11504 return {_type: type, _value: typeMap.serialize(value)};
11505 }
11506 }else if(value instanceof typeMap){
11507 //SImple mapping, therefore, return as a toString serialization.
11508 return {_type: type, _value: value.toString()};
11509 }
11510 }
11511 }
11512 return value;
11513 }
11514 },
11515
11516 _getNewFileContentString: function(){
11517 // summary:
11518 // Generate a string that can be saved to a file.
11519 // The result should look similar to:
11520 // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
11521 var serializableStructure = {};
11522
11523 var identifierAttribute = this._getIdentifierAttribute();
11524 if(identifierAttribute !== Number){
11525 serializableStructure.identifier = identifierAttribute;
11526 }
11527 if(this._labelAttr){
11528 serializableStructure.label = this._labelAttr;
11529 }
11530 serializableStructure.items = [];
11531 for(var i = 0; i < this._arrayOfAllItems.length; ++i){
11532 var item = this._arrayOfAllItems[i];
11533 if(item !== null){
11534 var serializableItem = {};
11535 for(var key in item){
11536 if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
11537 var valueArray = this.getValues(item, key);
11538 if(valueArray.length == 1){
11539 serializableItem[key] = this._flatten(valueArray[0]);
11540 }else{
11541 var serializableArray = [];
11542 for(var j = 0; j < valueArray.length; ++j){
11543 serializableArray.push(this._flatten(valueArray[j]));
11544 serializableItem[key] = serializableArray;
11545 }
11546 }
11547 }
11548 }
11549 serializableStructure.items.push(serializableItem);
11550 }
11551 }
11552 var prettyPrint = true;
11553 return jsonUtil.toJson(serializableStructure, prettyPrint);
11554 },
11555
11556 _isEmpty: function(something){
11557 // summary:
11558 // Function to determine if an array or object has no properties or values.
11559 // something:
11560 // The array or object to examine.
11561 var empty = true;
11562 if(lang.isObject(something)){
11563 var i;
11564 for(i in something){
11565 empty = false;
11566 break;
11567 }
11568 }else if(lang.isArray(something)){
11569 if(something.length > 0){
11570 empty = false;
11571 }
11572 }
11573 return empty; //boolean
11574 },
11575
11576 save: function(/* object */ keywordArgs){
11577 // summary:
11578 // See dojo/data/api/Write.save()
11579 this._assert(!this._saveInProgress);
11580
11581 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
11582 this._saveInProgress = true;
11583
11584 var self = this;
11585 var saveCompleteCallback = function(){
11586 self._pending = {
11587 _newItems:{},
11588 _modifiedItems:{},
11589 _deletedItems:{}
11590 };
11591
11592 self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
11593 if(keywordArgs && keywordArgs.onComplete){
11594 var scope = keywordArgs.scope || kernel.global;
11595 keywordArgs.onComplete.call(scope);
11596 }
11597 };
11598 var saveFailedCallback = function(err){
11599 self._saveInProgress = false;
11600 if(keywordArgs && keywordArgs.onError){
11601 var scope = keywordArgs.scope || kernel.global;
11602 keywordArgs.onError.call(scope, err);
11603 }
11604 };
11605
11606 if(this._saveEverything){
11607 var newFileContentString = this._getNewFileContentString();
11608 this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
11609 }
11610 if(this._saveCustom){
11611 this._saveCustom(saveCompleteCallback, saveFailedCallback);
11612 }
11613 if(!this._saveEverything && !this._saveCustom){
11614 // Looks like there is no user-defined save-handler function.
11615 // That's fine, it just means the datastore is acting as a "mock-write"
11616 // store -- changes get saved in memory but don't get saved to disk.
11617 saveCompleteCallback();
11618 }
11619 },
11620
11621 revert: function(){
11622 // summary:
11623 // See dojo/data/api/Write.revert()
11624 this._assert(!this._saveInProgress);
11625
11626 var identity;
11627 for(identity in this._pending._modifiedItems){
11628 // find the original item and the modified item that replaced it
11629 var copyOfItemState = this._pending._modifiedItems[identity];
11630 var modifiedItem = null;
11631 if(this._itemsByIdentity){
11632 modifiedItem = this._itemsByIdentity[identity];
11633 }else{
11634 modifiedItem = this._arrayOfAllItems[identity];
11635 }
11636
11637 // Restore the original item into a full-fledged item again, we want to try to
11638 // keep the same object instance as if we don't it, causes bugs like #9022.
11639 copyOfItemState[this._storeRefPropName] = this;
11640 for(var key in modifiedItem){
11641 delete modifiedItem[key];
11642 }
11643 lang.mixin(modifiedItem, copyOfItemState);
11644 }
11645 var deletedItem;
11646 for(identity in this._pending._deletedItems){
11647 deletedItem = this._pending._deletedItems[identity];
11648 deletedItem[this._storeRefPropName] = this;
11649 var index = deletedItem[this._itemNumPropName];
11650
11651 //Restore the reverse refererence map, if any.
11652 if(deletedItem["backup_" + this._reverseRefMap]){
11653 deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
11654 delete deletedItem["backup_" + this._reverseRefMap];
11655 }
11656 this._arrayOfAllItems[index] = deletedItem;
11657 if(this._itemsByIdentity){
11658 this._itemsByIdentity[identity] = deletedItem;
11659 }
11660 if(deletedItem[this._rootItemPropName]){
11661 this._arrayOfTopLevelItems.push(deletedItem);
11662 }
11663 }
11664 //We have to pass through it again and restore the reference maps after all the
11665 //undeletes have occurred.
11666 for(identity in this._pending._deletedItems){
11667 deletedItem = this._pending._deletedItems[identity];
11668 if(deletedItem["backupRefs_" + this._reverseRefMap]){
11669 arrayUtil.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
11670 var refItem;
11671 if(this._itemsByIdentity){
11672 refItem = this._itemsByIdentity[reference.id];
11673 }else{
11674 refItem = this._arrayOfAllItems[reference.id];
11675 }
11676 this._addReferenceToMap(refItem, deletedItem, reference.attr);
11677 }, this);
11678 delete deletedItem["backupRefs_" + this._reverseRefMap];
11679 }
11680 }
11681
11682 for(identity in this._pending._newItems){
11683 var newItem = this._pending._newItems[identity];
11684 newItem[this._storeRefPropName] = null;
11685 // null out the new item, but don't change the array index so
11686 // so we can keep using _arrayOfAllItems.length.
11687 this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
11688 if(newItem[this._rootItemPropName]){
11689 this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
11690 }
11691 if(this._itemsByIdentity){
11692 delete this._itemsByIdentity[identity];
11693 }
11694 }
11695
11696 this._pending = {
11697 _newItems:{},
11698 _modifiedItems:{},
11699 _deletedItems:{}
11700 };
11701 return true; // boolean
11702 },
11703
11704 isDirty: function(/* item? */ item){
11705 // summary:
11706 // See dojo/data/api/Write.isDirty()
11707 if(item){
11708 // return true if the item is dirty
11709 var identity = this.getIdentity(item);
11710 return new Boolean(this._pending._newItems[identity] ||
11711 this._pending._modifiedItems[identity] ||
11712 this._pending._deletedItems[identity]).valueOf(); // boolean
11713 }else{
11714 // return true if the store is dirty -- which means return true
11715 // if there are any new items, dirty items, or modified items
11716 return !this._isEmpty(this._pending._newItems) ||
11717 !this._isEmpty(this._pending._modifiedItems) ||
11718 !this._isEmpty(this._pending._deletedItems); // boolean
11719 }
11720 },
11721
11722 /* dojo/data/api/Notification */
11723
11724 onSet: function(/* dojo/data/api/Item */ item,
11725 /*attribute-name-string*/ attribute,
11726 /*object|array*/ oldValue,
11727 /*object|array*/ newValue){
11728 // summary:
11729 // See dojo/data/api/Notification.onSet()
11730
11731 // No need to do anything. This method is here just so that the
11732 // client code can connect observers to it.
11733 },
11734
11735 onNew: function(/* dojo/data/api/Item */ newItem, /*object?*/ parentInfo){
11736 // summary:
11737 // See dojo/data/api/Notification.onNew()
11738
11739 // No need to do anything. This method is here just so that the
11740 // client code can connect observers to it.
11741 },
11742
11743 onDelete: function(/* dojo/data/api/Item */ deletedItem){
11744 // summary:
11745 // See dojo/data/api/Notification.onDelete()
11746
11747 // No need to do anything. This method is here just so that the
11748 // client code can connect observers to it.
11749 },
11750
11751 close: function(/* object? */ request){
11752 // summary:
11753 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11754 // description:
11755 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11756 // If the store is still dirty (unsaved changes), then an error will be thrown instead of
11757 // clearing the internal state for reload from the url.
11758
11759 //Clear if not dirty ... or throw an error
11760 if(this.clearOnClose){
11761 if(!this.isDirty()){
11762 this.inherited(arguments);
11763 }else{
11764 //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
11765 throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
11766 }
11767 }
11768 }
11769 });
11770
11771 });
11772
11773 },
11774 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
11775 'dojo/dnd/TimedMoveable':function(){
11776 define("dojo/dnd/TimedMoveable", ["../_base/declare", "./Moveable" /*=====, "./Mover" =====*/], function(declare, Moveable /*=====, Mover =====*/){
11777 // module:
11778 // dojo/dnd/TimedMoveable
11779
11780 /*=====
11781 var __TimedMoveableArgs = declare([Moveable.__MoveableArgs], {
11782 // timeout: Number
11783 // delay move by this number of ms,
11784 // accumulating position changes during the timeout
11785 timeout: 0
11786 });
11787 =====*/
11788
11789 // precalculate long expressions
11790 var oldOnMove = Moveable.prototype.onMove;
11791
11792 return declare("dojo.dnd.TimedMoveable", Moveable, {
11793 // summary:
11794 // A specialized version of Moveable to support an FPS throttling.
11795 // This class puts an upper restriction on FPS, which may reduce
11796 // the CPU load. The additional parameter "timeout" regulates
11797 // the delay before actually moving the moveable object.
11798
11799 // object attributes (for markup)
11800 timeout: 40, // in ms, 40ms corresponds to 25 fps
11801
11802 constructor: function(node, params){
11803 // summary:
11804 // an object that makes a node moveable with a timer
11805 // node: Node||String
11806 // a node (or node's id) to be moved
11807 // params: __TimedMoveableArgs
11808 // object with additional parameters.
11809
11810 // sanitize parameters
11811 if(!params){ params = {}; }
11812 if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
11813 this.timeout = params.timeout;
11814 }
11815 },
11816
11817 onMoveStop: function(/*Mover*/ mover){
11818 if(mover._timer){
11819 // stop timer
11820 clearTimeout(mover._timer);
11821 // reflect the last received position
11822 oldOnMove.call(this, mover, mover._leftTop);
11823 }
11824 Moveable.prototype.onMoveStop.apply(this, arguments);
11825 },
11826 onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
11827 mover._leftTop = leftTop;
11828 if(!mover._timer){
11829 var _t = this; // to avoid using dojo.hitch()
11830 mover._timer = setTimeout(function(){
11831 // we don't have any pending requests
11832 mover._timer = null;
11833 // reflect the last received position
11834 oldOnMove.call(_t, mover, mover._leftTop);
11835 }, this.timeout);
11836 }
11837 }
11838 });
11839 });
11840
11841 },
11842 'dojo/NodeList-fx':function(){
11843 define("dojo/NodeList-fx", ["./query", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
11844 function(query, lang, connectLib, baseFx, coreFx){
11845
11846 // module:
11847 // dojo/NodeList-fx
11848
11849 /*=====
11850 return function(){
11851 // summary:
11852 // Adds dojo.fx animation support to dojo.query() by extending the NodeList class
11853 // with additional FX functions. NodeList is the array-like object used to hold query results.
11854 };
11855 =====*/
11856
11857 var NodeList = query.NodeList;
11858
11859 lang.extend(NodeList, {
11860 _anim: function(obj, method, args){
11861 args = args||{};
11862 var a = coreFx.combine(
11863 this.map(function(item){
11864 var tmpArgs = { node: item };
11865 lang.mixin(tmpArgs, args);
11866 return obj[method](tmpArgs);
11867 })
11868 );
11869 return args.auto ? a.play() && this : a; // dojo/_base/fx.Animation|dojo/NodeList
11870 },
11871
11872 wipeIn: function(args){
11873 // summary:
11874 // wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
11875 //
11876 // args: Object?
11877 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11878 // an `auto` parameter.
11879 //
11880 // returns: dojo/_base/fx.Animation|dojo/NodeList
11881 // A special args member `auto` can be passed to automatically play the animation.
11882 // If args.auto is present, the original dojo/NodeList will be returned for further
11883 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11884 //
11885 // example:
11886 // Fade in all tables with class "blah":
11887 // | dojo.query("table.blah").wipeIn().play();
11888 //
11889 // example:
11890 // Utilizing `auto` to get the NodeList back:
11891 // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
11892 //
11893 return this._anim(coreFx, "wipeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
11894 },
11895
11896 wipeOut: function(args){
11897 // summary:
11898 // wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
11899 //
11900 // args: Object?
11901 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11902 // an `auto` parameter.
11903 //
11904 // returns: dojo/_base/fx.Animation|dojo/NodeList
11905 // A special args member `auto` can be passed to automatically play the animation.
11906 // If args.auto is present, the original dojo/NodeList will be returned for further
11907 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11908 //
11909 // example:
11910 // Wipe out all tables with class "blah":
11911 // | dojo.query("table.blah").wipeOut().play();
11912 return this._anim(coreFx, "wipeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
11913 },
11914
11915 slideTo: function(args){
11916 // summary:
11917 // slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
11918 //
11919 // args: Object?
11920 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11921 // an `auto` parameter.
11922 //
11923 // returns: dojo/_base/fx.Animation|dojo/NodeList
11924 // A special args member `auto` can be passed to automatically play the animation.
11925 // If args.auto is present, the original dojo/NodeList will be returned for further
11926 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11927 //
11928 // example:
11929 // | Move all tables with class "blah" to 300/300:
11930 // | dojo.query("table.blah").slideTo({
11931 // | left: 40,
11932 // | top: 50
11933 // | }).play();
11934 return this._anim(coreFx, "slideTo", args); // dojo/_base/fx.Animation|dojo/NodeList
11935 },
11936
11937
11938 fadeIn: function(args){
11939 // summary:
11940 // fade in all elements of this NodeList via `dojo.fadeIn`
11941 //
11942 // args: Object?
11943 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11944 // an `auto` parameter.
11945 //
11946 // returns: dojo/_base/fx.Animation|dojo/NodeList
11947 // A special args member `auto` can be passed to automatically play the animation.
11948 // If args.auto is present, the original dojo/NodeList will be returned for further
11949 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11950 //
11951 // example:
11952 // Fade in all tables with class "blah":
11953 // | dojo.query("table.blah").fadeIn().play();
11954 return this._anim(baseFx, "fadeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
11955 },
11956
11957 fadeOut: function(args){
11958 // summary:
11959 // fade out all elements of this NodeList via `dojo.fadeOut`
11960 //
11961 // args: Object?
11962 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11963 // an `auto` parameter.
11964 //
11965 // returns: dojo/_base/fx.Animation|dojo/NodeList
11966 // A special args member `auto` can be passed to automatically play the animation.
11967 // If args.auto is present, the original dojo/NodeList will be returned for further
11968 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11969 //
11970 // example:
11971 // Fade out all elements with class "zork":
11972 // | dojo.query(".zork").fadeOut().play();
11973 // example:
11974 // Fade them on a delay and do something at the end:
11975 // | var fo = dojo.query(".zork").fadeOut();
11976 // | dojo.connect(fo, "onEnd", function(){ /*...*/ });
11977 // | fo.play();
11978 // example:
11979 // Using `auto`:
11980 // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
11981 //
11982 return this._anim(baseFx, "fadeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
11983 },
11984
11985 animateProperty: function(args){
11986 // summary:
11987 // Animate all elements of this NodeList across the properties specified.
11988 // syntax identical to `dojo.animateProperty`
11989 //
11990 // args: Object?
11991 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11992 // an `auto` parameter.
11993 //
11994 // returns: dojo/_base/fx.Animation|dojo/NodeList
11995 // A special args member `auto` can be passed to automatically play the animation.
11996 // If args.auto is present, the original dojo/NodeList will be returned for further
11997 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11998 //
11999 // example:
12000 // | dojo.query(".zork").animateProperty({
12001 // | duration: 500,
12002 // | properties: {
12003 // | color: { start: "black", end: "white" },
12004 // | left: { end: 300 }
12005 // | }
12006 // | }).play();
12007 //
12008 // example:
12009 // | dojo.query(".grue").animateProperty({
12010 // | auto:true,
12011 // | properties: {
12012 // | height:240
12013 // | }
12014 // | }).onclick(handler);
12015 return this._anim(baseFx, "animateProperty", args); // dojo/_base/fx.Animation|dojo/NodeList
12016 },
12017
12018 anim: function( /*Object*/ properties,
12019 /*Integer?*/ duration,
12020 /*Function?*/ easing,
12021 /*Function?*/ onEnd,
12022 /*Integer?*/ delay){
12023 // summary:
12024 // Animate one or more CSS properties for all nodes in this list.
12025 // The returned animation object will already be playing when it
12026 // is returned. See the docs for `dojo.anim` for full details.
12027 // properties: Object
12028 // the properties to animate. does NOT support the `auto` parameter like other
12029 // NodeList-fx methods.
12030 // duration: Integer?
12031 // Optional. The time to run the animations for
12032 // easing: Function?
12033 // Optional. The easing function to use.
12034 // onEnd: Function?
12035 // A function to be called when the animation ends
12036 // delay:
12037 // how long to delay playing the returned animation
12038 // example:
12039 // Another way to fade out:
12040 // | dojo.query(".thinger").anim({ opacity: 0 });
12041 // example:
12042 // animate all elements with the "thigner" class to a width of 500
12043 // pixels over half a second
12044 // | dojo.query(".thinger").anim({ width: 500 }, 700);
12045 var canim = coreFx.combine(
12046 this.map(function(item){
12047 return baseFx.animateProperty({
12048 node: item,
12049 properties: properties,
12050 duration: duration||350,
12051 easing: easing
12052 });
12053 })
12054 );
12055 if(onEnd){
12056 connectLib.connect(canim, "onEnd", onEnd);
12057 }
12058 return canim.play(delay||0); // dojo/_base/fx.Animation
12059 }
12060 });
12061
12062 return NodeList;
12063 });
12064
12065 },
12066 'dijit/form/_ListMouseMixin':function(){
12067 define("dijit/form/_ListMouseMixin", [
12068 "dojo/_base/declare", // declare
12069 "dojo/mouse",
12070 "dojo/on",
12071 "dojo/touch",
12072 "./_ListBase"
12073 ], function(declare, mouse, on, touch, _ListBase){
12074
12075 // module:
12076 // dijit/form/_ListMouseMixin
12077
12078 return declare( "dijit.form._ListMouseMixin", _ListBase, {
12079 // summary:
12080 // a Mixin to handle mouse or touch events for a focus-less menu
12081 // Abstract methods that must be defined externally:
12082 //
12083 // - onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
12084 // tags:
12085 // private
12086
12087 postCreate: function(){
12088 this.inherited(arguments);
12089
12090 this.own(on(this.domNode, touch.press, function(evt){ evt.preventDefault(); })); // prevent focus shift on list scrollbar press
12091
12092 this._listConnect(touch.press, "_onMouseDown");
12093 this._listConnect(touch.release, "_onMouseUp");
12094 this._listConnect(mouse.enter, "_onMouseOver");
12095 this._listConnect(mouse.leave, "_onMouseOut");
12096 },
12097
12098 _onMouseDown: function(/*Event*/ evt, /*DomNode*/ target){
12099 if(this._hoveredNode){
12100 this.onUnhover(this._hoveredNode);
12101 this._hoveredNode = null;
12102 }
12103 this._isDragging = true;
12104 this._setSelectedAttr(target);
12105 },
12106
12107 _onMouseUp: function(/*Event*/ evt, /*DomNode*/ target){
12108 this._isDragging = false;
12109 var selectedNode = this.selected;
12110 var hoveredNode = this._hoveredNode;
12111 if(selectedNode && target == selectedNode){
12112 this.onClick(selectedNode);
12113 }else if(hoveredNode && target == hoveredNode){ // drag to select
12114 this._setSelectedAttr(hoveredNode);
12115 this.onClick(hoveredNode);
12116 }
12117 },
12118
12119 _onMouseOut: function(/*Event*/ evt, /*DomNode*/ target){
12120 if(this._hoveredNode){
12121 this.onUnhover(this._hoveredNode);
12122 this._hoveredNode = null;
12123 }
12124 if(this._isDragging){
12125 this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
12126 }
12127 },
12128
12129 _onMouseOver: function(/*Event*/ evt, /*DomNode*/ target){
12130 if(this._cancelDrag){
12131 var time = (new Date()).getTime();
12132 if(time > this._cancelDrag){
12133 this._isDragging = false;
12134 }
12135 this._cancelDrag = null;
12136 }
12137 this._hoveredNode = target;
12138 this.onHover(target);
12139 if(this._isDragging){
12140 this._setSelectedAttr(target);
12141 }
12142 }
12143 });
12144
12145 });
12146
12147 },
12148 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
12149 'dojo/cookie':function(){
12150 define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo, regexp){
12151
12152 // module:
12153 // dojo/cookie
12154
12155 /*=====
12156 var __cookieProps = {
12157 // expires: Date|String|Number?
12158 // If a number, the number of days from today at which the cookie
12159 // will expire. If a date, the date past which the cookie will expire.
12160 // If expires is in the past, the cookie will be deleted.
12161 // If expires is omitted or is 0, the cookie will expire when the browser closes.
12162 // path: String?
12163 // The path to use for the cookie.
12164 // domain: String?
12165 // The domain to use for the cookie.
12166 // secure: Boolean?
12167 // Whether to only send the cookie on secure connections
12168 };
12169 =====*/
12170
12171
12172 dojo.cookie = function(/*String*/name, /*String?*/ value, /*__cookieProps?*/ props){
12173 // summary:
12174 // Get or set a cookie.
12175 // description:
12176 // If one argument is passed, returns the value of the cookie
12177 // For two or more arguments, acts as a setter.
12178 // name:
12179 // Name of the cookie
12180 // value:
12181 // Value for the cookie
12182 // props:
12183 // Properties for the cookie
12184 // example:
12185 // set a cookie with the JSON-serialized contents of an object which
12186 // will expire 5 days from now:
12187 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12188 // | cookie("configObj", json.stringify(config, {expires: 5 }));
12189 // | });
12190 //
12191 // example:
12192 // de-serialize a cookie back into a JavaScript object:
12193 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12194 // | config = json.parse(cookie("configObj"));
12195 // | });
12196 //
12197 // example:
12198 // delete a cookie:
12199 // | require(["dojo/cookie"], function(cookie){
12200 // | cookie("configObj", null, {expires: -1});
12201 // | });
12202 var c = document.cookie, ret;
12203 if(arguments.length == 1){
12204 var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)"));
12205 ret = matches ? decodeURIComponent(matches[1]) : undefined;
12206 }else{
12207 props = props || {};
12208 // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
12209 var exp = props.expires;
12210 if(typeof exp == "number"){
12211 var d = new Date();
12212 d.setTime(d.getTime() + exp*24*60*60*1000);
12213 exp = props.expires = d;
12214 }
12215 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
12216
12217 value = encodeURIComponent(value);
12218 var updatedCookie = name + "=" + value, propName;
12219 for(propName in props){
12220 updatedCookie += "; " + propName;
12221 var propValue = props[propName];
12222 if(propValue !== true){ updatedCookie += "=" + propValue; }
12223 }
12224 document.cookie = updatedCookie;
12225 }
12226 return ret; // String|undefined
12227 };
12228
12229 dojo.cookie.isSupported = function(){
12230 // summary:
12231 // Use to determine if the current browser supports cookies or not.
12232 //
12233 // Returns true if user allows cookies.
12234 // Returns false if user doesn't allow cookies.
12235
12236 if(!("cookieEnabled" in navigator)){
12237 this("__djCookieTest__", "CookiesAllowed");
12238 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
12239 if(navigator.cookieEnabled){
12240 this("__djCookieTest__", "", {expires: -1});
12241 }
12242 }
12243 return navigator.cookieEnabled;
12244 };
12245
12246 return dojo.cookie;
12247 });
12248
12249 },
12250 'dojo/cache':function(){
12251 define("dojo/cache", ["./_base/kernel", "./text"], function(dojo){
12252 // module:
12253 // dojo/cache
12254
12255 // dojo.cache is defined in dojo/text
12256 return dojo.cache;
12257 });
12258
12259 },
12260 'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"button presentation\" aria-hidden=\"true\"\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\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n",
12261 'dijit/ProgressBar':function(){
12262 require({cache:{
12263 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"}});
12264 define("dijit/ProgressBar", [
12265 "require", // require.toUrl
12266 "dojo/_base/declare", // declare
12267 "dojo/dom-class", // domClass.toggle
12268 "dojo/_base/lang", // lang.mixin
12269 "dojo/number", // number.format
12270 "./_Widget",
12271 "./_TemplatedMixin",
12272 "dojo/text!./templates/ProgressBar.html"
12273 ], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
12274
12275 // module:
12276 // dijit/ProgressBar
12277
12278
12279 return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
12280 // summary:
12281 // A progress indication widget, showing the amount completed
12282 // (often the percentage completed) of a task.
12283 //
12284 // example:
12285 // | <div data-dojo-type="ProgressBar"
12286 // | places="0"
12287 // | value="..." maximum="...">
12288 // | </div>
12289
12290 // progress: [const] String (Percentage or Number)
12291 // Number or percentage indicating amount of task completed.
12292 // Deprecated. Use "value" instead.
12293 progress: "0",
12294
12295 // value: String (Percentage or Number)
12296 // Number or percentage indicating amount of task completed.
12297 // With "%": percentage value, 0% <= progress <= 100%, or
12298 // without "%": absolute value, 0 <= progress <= maximum.
12299 // Infinity means that the progress bar is indeterminate.
12300 value: "",
12301
12302 // maximum: [const] Float
12303 // Max sample number
12304 maximum: 100,
12305
12306 // places: [const] Number
12307 // Number of places to show in values; 0 by default
12308 places: 0,
12309
12310 // indeterminate: [const] Boolean
12311 // If false: show progress value (number or percentage).
12312 // If true: show that a process is underway but that the amount completed is unknown.
12313 // Deprecated. Use "value" instead.
12314 indeterminate: false,
12315
12316 // label: String?
12317 // Label on progress bar. Defaults to percentage for determinate progress bar and
12318 // blank for indeterminate progress bar.
12319 label:"",
12320
12321 // name: String
12322 // this is the field name (for a form) if set. This needs to be set if you want to use
12323 // this widget in a dijit/form/Form widget (such as dijit/Dialog)
12324 name: '',
12325
12326 templateString: template,
12327
12328 // _indeterminateHighContrastImagePath: [private] URL
12329 // URL to image to use for indeterminate progress bar when display is in high contrast mode
12330 _indeterminateHighContrastImagePath:
12331 require.toUrl("./themes/a11y/indeterminate_progress.gif"),
12332
12333 postMixInProperties: function(){
12334 this.inherited(arguments);
12335
12336 // Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
12337 if(!(this.params && "value" in this.params)){
12338 this.value = this.indeterminate ? Infinity : this.progress;
12339 }
12340 },
12341
12342 buildRendering: function(){
12343 this.inherited(arguments);
12344 this.indeterminateHighContrastImage.setAttribute("src",
12345 this._indeterminateHighContrastImagePath.toString());
12346 this.update();
12347 },
12348
12349 update: function(/*Object?*/attributes){
12350 // summary:
12351 // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
12352 // set("value", ...) rather than calling this method directly.
12353 // attributes:
12354 // May provide progress and/or maximum properties on this parameter;
12355 // see attribute specs for details.
12356 // example:
12357 // | myProgressBar.update({'indeterminate': true});
12358 // | myProgressBar.update({'progress': 80});
12359 // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
12360 // tags:
12361 // private
12362
12363 // TODO: deprecate this method and use set() instead
12364
12365 lang.mixin(this, attributes || {});
12366 var tip = this.internalProgress, ap = this.domNode;
12367 var percent = 1;
12368 if(this.indeterminate){
12369 ap.removeAttribute("aria-valuenow");
12370 }else{
12371 if(String(this.progress).indexOf("%") != -1){
12372 percent = Math.min(parseFloat(this.progress)/100, 1);
12373 this.progress = percent * this.maximum;
12374 }else{
12375 this.progress = Math.min(this.progress, this.maximum);
12376 percent = this.maximum ? this.progress / this.maximum : 0;
12377 }
12378 ap.setAttribute("aria-valuenow", this.progress);
12379 }
12380
12381 // Even indeterminate ProgressBars should have these attributes
12382 ap.setAttribute("aria-describedby", this.labelNode.id);
12383 ap.setAttribute("aria-valuemin", 0);
12384 ap.setAttribute("aria-valuemax", this.maximum);
12385
12386 this.labelNode.innerHTML = this.report(percent);
12387
12388 domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
12389 tip.style.width = (percent * 100) + "%";
12390 this.onChange();
12391 },
12392
12393 _setValueAttr: function(v){
12394 this._set("value", v);
12395 if(v == Infinity){
12396 this.update({indeterminate:true});
12397 }else{
12398 this.update({indeterminate:false, progress:v});
12399 }
12400 },
12401
12402 _setLabelAttr: function(label){
12403 this._set("label", label);
12404 this.update();
12405 },
12406
12407 _setIndeterminateAttr: function(indeterminate){
12408 // Deprecated, use set("value", ...) instead
12409 this.indeterminate = indeterminate;
12410 this.update();
12411 },
12412
12413 report: function(/*float*/percent){
12414 // summary:
12415 // Generates message to show inside progress bar (normally indicating amount of task completed).
12416 // May be overridden.
12417 // tags:
12418 // extension
12419
12420 return this.label ? this.label :
12421 (this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
12422 },
12423
12424 onChange: function(){
12425 // summary:
12426 // Callback fired when progress updates.
12427 // tags:
12428 // extension
12429 }
12430 });
12431
12432 });
12433
12434 },
12435 'dijit/_base/popup':function(){
12436 define("dijit/_base/popup", [
12437 "dojo/dom-class", // domClass.contains
12438 "dojo/_base/window",
12439 "../popup",
12440 "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
12441 ], function(domClass, win, popup){
12442
12443 // module:
12444 // dijit/_base/popup
12445
12446 /*=====
12447 return {
12448 // summary:
12449 // Deprecated. Old module for popups, new code should use dijit/popup directly.
12450 };
12451 =====*/
12452
12453
12454 // Hack support for old API passing in node instead of a widget (to various methods)
12455 var origCreateWrapper = popup._createWrapper;
12456 popup._createWrapper = function(widget){
12457 if(!widget.declaredClass){
12458 // make fake widget to pass to new API
12459 widget = {
12460 _popupWrapper: (widget.parentNode && domClass.contains(widget.parentNode, "dijitPopup")) ?
12461 widget.parentNode : null,
12462 domNode: widget,
12463 destroy: function(){},
12464 ownerDocument: widget.ownerDocument,
12465 ownerDocumentBody: win.body(widget.ownerDocument)
12466 };
12467 }
12468 return origCreateWrapper.call(this, widget);
12469 };
12470
12471 // Support old format of orient parameter
12472 var origOpen = popup.open;
12473 popup.open = function(/*__OpenArgs*/ args){
12474 // Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
12475 // Don't do conversion for:
12476 // - null parameter (that means to use the default positioning)
12477 // - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
12478 // - new format, ex: ["below", "above"]
12479 // - return value from deprecated dijit.getPopupAroundAlignment() method,
12480 // ex: ["below", "above"]
12481 if(args.orient && typeof args.orient != "string" && !("length" in args.orient)){
12482 var ary = [];
12483 for(var key in args.orient){
12484 ary.push({aroundCorner: key, corner: args.orient[key]});
12485 }
12486 args.orient = ary;
12487 }
12488
12489 return origOpen.call(this, args);
12490 };
12491
12492 return popup;
12493 });
12494
12495 },
12496 'dijit/ColorPalette':function(){
12497 require({cache:{
12498 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
12499 define("dijit/ColorPalette", [
12500 "require", // require.toUrl
12501 "dojo/text!./templates/ColorPalette.html",
12502 "./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
12503 "./_TemplatedMixin",
12504 "./_PaletteMixin",
12505 "./hccss", // has("highcontrast")
12506 "dojo/i18n", // i18n.getLocalization
12507 "dojo/_base/Color", // dojo.Color dojo.Color.named
12508 "dojo/_base/declare", // declare
12509 "dojo/dom-construct", // domConstruct.place
12510 "dojo/string", // string.substitute
12511 "dojo/i18n!dojo/nls/colors", // translations
12512 "dojo/colors" // extend dojo.Color w/names of other colors
12513 ], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, has, i18n, Color,
12514 declare, domConstruct, string){
12515
12516 // module:
12517 // dijit/ColorPalette
12518
12519 var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
12520 // summary:
12521 // A keyboard accessible color-picking widget
12522 // description:
12523 // Grid showing various colors, so the user can pick a certain color.
12524 // Can be used standalone, or as a popup.
12525 //
12526 // example:
12527 // | <div data-dojo-type="dijit/ColorPalette"></div>
12528 //
12529 // example:
12530 // | var picker = new dijit.ColorPalette({ },srcNode);
12531 // | picker.startup();
12532
12533
12534 // palette: [const] String
12535 // Size of grid, either "7x10" or "3x4".
12536 palette: "7x10",
12537
12538 // _palettes: [protected] Map
12539 // This represents the value of the colors.
12540 // The first level is a hashmap of the different palettes available.
12541 // The next two dimensions represent the columns and rows of colors.
12542 _palettes: {
12543 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
12544 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
12545 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
12546 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
12547 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
12548 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
12549 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
12550
12551 "3x4": [["white", "lime", "green", "blue"],
12552 ["silver", "yellow", "fuchsia", "navy"],
12553 ["gray", "red", "purple", "black"]]
12554 },
12555
12556 // templateString: String
12557 // The template of this widget.
12558 templateString: template,
12559
12560 baseClass: "dijitColorPalette",
12561
12562 _dyeFactory: function(value, row, col, title){
12563 // Overrides _PaletteMixin._dyeFactory().
12564 return new this._dyeClass(value, row, col, title);
12565 },
12566
12567 buildRendering: function(){
12568 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
12569 // <img> nodes
12570 this.inherited(arguments);
12571
12572 // Creates customized constructor for dye class (color of a single cell) for
12573 // specified palette and high-contrast vs. normal mode. Used in _getDye().
12574 this._dyeClass = declare(ColorPalette._Color, {
12575 palette: this.palette
12576 });
12577
12578 // Creates <img> nodes in each cell of the template.
12579 this._preparePalette(
12580 this._palettes[this.palette],
12581 i18n.getLocalization("dojo", "colors", this.lang));
12582 }
12583 });
12584
12585 ColorPalette._Color = declare("dijit._Color", Color, {
12586 // summary:
12587 // Object associated with each cell in a ColorPalette palette.
12588 // Implements dijit/Dye.
12589
12590 // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
12591 // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
12592 // for showing the color.
12593 template:
12594 "<span class='dijitInline dijitPaletteImg'>" +
12595 "<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
12596 "</span>",
12597
12598 // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
12599 // but scrolled and clipped to show the correct color only
12600 hcTemplate:
12601 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
12602 "<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
12603 "</span>",
12604
12605 // _imagePaths: [protected] Map
12606 // This is stores the path to the palette images used for high-contrast mode display
12607 _imagePaths: {
12608 "7x10": require.toUrl("./themes/a11y/colors7x10.png"),
12609 "3x4": require.toUrl("./themes/a11y/colors3x4.png")
12610 },
12611
12612 constructor: function(alias, row, col, title){
12613 // summary:
12614 // Constructor for ColorPalette._Color
12615 // alias: String
12616 // English name of the color.
12617 // row: Number
12618 // Vertical position in grid.
12619 // column: Number
12620 // Horizontal position in grid.
12621 // title: String
12622 // Localized name of the color.
12623 this._title = title;
12624 this._row = row;
12625 this._col = col;
12626 this.setColor(Color.named[alias]);
12627 },
12628
12629 getValue: function(){
12630 // summary:
12631 // Note that although dijit._Color is initialized with a value like "white" getValue() always
12632 // returns a hex value
12633 return this.toHex();
12634 },
12635
12636 fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
12637 var html = string.substitute(has("highcontrast") ? this.hcTemplate : this.template, {
12638 // substitution variables for normal mode
12639 color: this.toHex(),
12640 blankGif: blankGif,
12641 alt: this._title,
12642 title: this._title,
12643
12644 // variables used for high contrast mode
12645 image: this._imagePaths[this.palette].toString(),
12646 left: this._col * -20 - 5,
12647 top: this._row * -20 - 5,
12648 size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
12649 });
12650
12651 domConstruct.place(html, cell);
12652 }
12653 });
12654
12655
12656 return ColorPalette;
12657 });
12658
12659 },
12660 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"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\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
12661 'dojo/_base/url':function(){
12662 define("dojo/_base/url", ["./kernel"], function(dojo){
12663 // module:
12664 // dojo/url
12665
12666 var
12667 ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
12668 ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
12669 _Url = function(){
12670 var n = null,
12671 _a = arguments,
12672 uri = [_a[0]];
12673 // resolve uri components relative to each other
12674 for(var i = 1; i<_a.length; i++){
12675 if(!_a[i]){ continue; }
12676
12677 // Safari doesn't support this.constructor so we have to be explicit
12678 // FIXME: Tracked (and fixed) in Webkit bug 3537.
12679 // http://bugs.webkit.org/show_bug.cgi?id=3537
12680 var relobj = new _Url(_a[i]+""),
12681 uriobj = new _Url(uri[0]+"");
12682
12683 if(
12684 relobj.path == "" &&
12685 !relobj.scheme &&
12686 !relobj.authority &&
12687 !relobj.query
12688 ){
12689 if(relobj.fragment != n){
12690 uriobj.fragment = relobj.fragment;
12691 }
12692 relobj = uriobj;
12693 }else if(!relobj.scheme){
12694 relobj.scheme = uriobj.scheme;
12695
12696 if(!relobj.authority){
12697 relobj.authority = uriobj.authority;
12698
12699 if(relobj.path.charAt(0) != "/"){
12700 var path = uriobj.path.substring(0,
12701 uriobj.path.lastIndexOf("/") + 1) + relobj.path;
12702
12703 var segs = path.split("/");
12704 for(var j = 0; j < segs.length; j++){
12705 if(segs[j] == "."){
12706 // flatten "./" references
12707 if(j == segs.length - 1){
12708 segs[j] = "";
12709 }else{
12710 segs.splice(j, 1);
12711 j--;
12712 }
12713 }else if(j > 0 && !(j == 1 && segs[0] == "") &&
12714 segs[j] == ".." && segs[j-1] != ".."){
12715 // flatten "../" references
12716 if(j == (segs.length - 1)){
12717 segs.splice(j, 1);
12718 segs[j - 1] = "";
12719 }else{
12720 segs.splice(j - 1, 2);
12721 j -= 2;
12722 }
12723 }
12724 }
12725 relobj.path = segs.join("/");
12726 }
12727 }
12728 }
12729
12730 uri = [];
12731 if(relobj.scheme){
12732 uri.push(relobj.scheme, ":");
12733 }
12734 if(relobj.authority){
12735 uri.push("//", relobj.authority);
12736 }
12737 uri.push(relobj.path);
12738 if(relobj.query){
12739 uri.push("?", relobj.query);
12740 }
12741 if(relobj.fragment){
12742 uri.push("#", relobj.fragment);
12743 }
12744 }
12745
12746 this.uri = uri.join("");
12747
12748 // break the uri into its main components
12749 var r = this.uri.match(ore);
12750
12751 this.scheme = r[2] || (r[1] ? "" : n);
12752 this.authority = r[4] || (r[3] ? "" : n);
12753 this.path = r[5]; // can never be undefined
12754 this.query = r[7] || (r[6] ? "" : n);
12755 this.fragment = r[9] || (r[8] ? "" : n);
12756
12757 if(this.authority != n){
12758 // server based naming authority
12759 r = this.authority.match(ire);
12760
12761 this.user = r[3] || n;
12762 this.password = r[4] || n;
12763 this.host = r[6] || r[7]; // ipv6 || ipv4
12764 this.port = r[9] || n;
12765 }
12766 };
12767 _Url.prototype.toString = function(){ return this.uri; };
12768
12769 return dojo._Url = _Url;
12770 });
12771
12772 },
12773 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"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",
12774 'dojo/text':function(){
12775 define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo, require, has, xhr){
12776 // module:
12777 // dojo/text
12778
12779 var getText;
12780 if( 1 ){
12781 getText= function(url, sync, load){
12782 xhr("GET", {url: url, sync:!!sync, load: load, headers: dojo.config.textPluginHeaders || {}});
12783 };
12784 }else{
12785 // TODOC: only works for dojo AMD loader
12786 if(require.getText){
12787 getText= require.getText;
12788 }else{
12789 console.error("dojo/text plugin failed to load because loader does not support getText");
12790 }
12791 }
12792
12793 var
12794 theCache = {},
12795
12796 strip= function(text){
12797 //Strips <?xml ...?> declarations so that external SVG and XML
12798 //documents can be added to a document without worry. Also, if the string
12799 //is an HTML document, only the part inside the body tag is returned.
12800 if(text){
12801 text= text.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
12802 var matches= text.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
12803 if(matches){
12804 text= matches[1];
12805 }
12806 }else{
12807 text = "";
12808 }
12809 return text;
12810 },
12811
12812 notFound = {},
12813
12814 pending = {};
12815
12816 dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
12817 // summary:
12818 // A getter and setter for storing the string content associated with the
12819 // module and url arguments.
12820 // description:
12821 // If module is a string that contains slashes, then it is interpretted as a fully
12822 // resolved path (typically a result returned by require.toUrl), and url should not be
12823 // provided. This is the preferred signature. If module is a string that does not
12824 // contain slashes, then url must also be provided and module and url are used to
12825 // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
12826 // If value is specified, the cache value for the moduleUrl will be set to
12827 // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
12828 // in its internal cache and return that cached value for the URL. To clear
12829 // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
12830 // the URL contents, only modules on the same domain of the page can use this capability.
12831 // The build system can inline the cache values though, to allow for xdomain hosting.
12832 // module: String||Object
12833 // If a String with slashes, a fully resolved path; if a String without slashes, the
12834 // module name to use for the base part of the URL, similar to module argument
12835 // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
12836 // generates a valid path for the cache item. For example, a dojo._Url object.
12837 // url: String
12838 // The rest of the path to append to the path derived from the module argument. If
12839 // module is an object, then this second argument should be the "value" argument instead.
12840 // value: String||Object?
12841 // If a String, the value to use in the cache for the module/url combination.
12842 // If an Object, it can have two properties: value and sanitize. The value property
12843 // should be the value to use in the cache, and sanitize can be set to true or false,
12844 // to indicate if XML declarations should be removed from the value and if the HTML
12845 // inside a body tag in the value should be extracted as the real value. The value argument
12846 // or the value property on the value argument are usually only used by the build system
12847 // as it inlines cache content.
12848 // example:
12849 // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
12850 // of call is used to avoid an issue with the build system erroneously trying to intern
12851 // this example. To get the build system to intern your dojo.cache calls, use the
12852 // "dojo.cache" style of call):
12853 // | //If template.html contains "<h1>Hello</h1>" that will be
12854 // | //the value for the text variable.
12855 // | var text = dojo["cache"]("my.module", "template.html");
12856 // example:
12857 // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
12858 // (the dojo["cache"] style of call is used to avoid an issue with the build system
12859 // erroneously trying to intern this example. To get the build system to intern your
12860 // dojo.cache calls, use the "dojo.cache" style of call):
12861 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12862 // | //text variable will contain just "<h1>Hello</h1>".
12863 // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
12864 // example:
12865 // Same example as previous, but demonstrates how an object can be passed in as
12866 // the first argument, then the value argument can then be the second argument.
12867 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12868 // | //text variable will contain just "<h1>Hello</h1>".
12869 // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
12870
12871 // * (string string [value]) => (module, url, value)
12872 // * (object [value]) => (module, value), url defaults to ""
12873 //
12874 // * if module is an object, then it must be convertable to a string
12875 // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
12876 // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
12877 var key;
12878 if(typeof module=="string"){
12879 if(/\//.test(module)){
12880 // module is a version 1.7+ resolved path
12881 key = module;
12882 value = url;
12883 }else{
12884 // module is a version 1.6- argument to dojo.moduleUrl
12885 key = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : ""));
12886 }
12887 }else{
12888 key = module + "";
12889 value = url;
12890 }
12891 var
12892 val = (value != undefined && typeof value != "string") ? value.value : value,
12893 sanitize = value && value.sanitize;
12894
12895 if(typeof val == "string"){
12896 //We have a string, set cache value
12897 theCache[key] = val;
12898 return sanitize ? strip(val) : val;
12899 }else if(val === null){
12900 //Remove cached value
12901 delete theCache[key];
12902 return null;
12903 }else{
12904 //Allow cache values to be empty strings. If key property does
12905 //not exist, fetch it.
12906 if(!(key in theCache)){
12907 getText(key, true, function(text){
12908 theCache[key]= text;
12909 });
12910 }
12911 return sanitize ? strip(theCache[key]) : theCache[key];
12912 }
12913 };
12914
12915 return {
12916 // summary:
12917 // This module implements the dojo/text! plugin and the dojo.cache API.
12918 // description:
12919 // We choose to include our own plugin to leverage functionality already contained in dojo
12920 // and thereby reduce the size of the plugin compared to various foreign loader implementations.
12921 // Also, this allows foreign AMD loaders to be used without their plugins.
12922 //
12923 // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
12924 // loader. This feature is outside the scope of the CommonJS plugins specification.
12925
12926 // the dojo/text caches it's own resources because of dojo.cache
12927 dynamic: true,
12928
12929 normalize: function(id, toAbsMid){
12930 // id is something like (path may be relative):
12931 //
12932 // "path/to/text.html"
12933 // "path/to/text.html!strip"
12934 var parts= id.split("!"),
12935 url= parts[0];
12936 return (/^\./.test(url) ? toAbsMid(url) : url) + (parts[1] ? "!" + parts[1] : "");
12937 },
12938
12939 load: function(id, require, load){
12940 // id: String
12941 // Path to the resource.
12942 // require: Function
12943 // Object that include the function toUrl with given id returns a valid URL from which to load the text.
12944 // load: Function
12945 // Callback function which will be called, when the loading finished.
12946
12947 // id is something like (path is always absolute):
12948 //
12949 // "path/to/text.html"
12950 // "path/to/text.html!strip"
12951 var
12952 parts= id.split("!"),
12953 stripFlag= parts.length>1,
12954 absMid= parts[0],
12955 url = require.toUrl(parts[0]),
12956 requireCacheUrl = "url:" + url,
12957 text = notFound,
12958 finish = function(text){
12959 load(stripFlag ? strip(text) : text);
12960 };
12961 if(absMid in theCache){
12962 text = theCache[absMid];
12963 }else if(requireCacheUrl in require.cache){
12964 text = require.cache[requireCacheUrl];
12965 }else if(url in theCache){
12966 text = theCache[url];
12967 }
12968 if(text===notFound){
12969 if(pending[url]){
12970 pending[url].push(finish);
12971 }else{
12972 var pendingList = pending[url] = [finish];
12973 getText(url, !require.async, function(text){
12974 theCache[absMid]= theCache[url]= text;
12975 for(var i = 0; i<pendingList.length;){
12976 pendingList[i++](text);
12977 }
12978 delete pending[url];
12979 });
12980 }
12981 }else{
12982 finish(text);
12983 }
12984 }
12985 };
12986
12987 });
12988
12989
12990 },
12991 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
12992 'dojo/uacss':function(){
12993 define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./sniff", "./_base/window"],
12994 function(geometry, lang, ready, has, baseWindow){
12995
12996 // module:
12997 // dojo/uacss
12998
12999 /*=====
13000 return {
13001 // summary:
13002 // Applies pre-set CSS classes to the top-level HTML node, based on:
13003 //
13004 // - browser (ex: dj_ie)
13005 // - browser version (ex: dj_ie6)
13006 // - box model (ex: dj_contentBox)
13007 // - text direction (ex: dijitRtl)
13008 //
13009 // In addition, browser, browser version, and box model are
13010 // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
13011 //
13012 // Returns the has() method.
13013 };
13014 =====*/
13015
13016 var
13017 html = baseWindow.doc.documentElement,
13018 ie = has("ie"),
13019 opera = has("opera"),
13020 maj = Math.floor,
13021 ff = has("ff"),
13022 boxModel = geometry.boxModel.replace(/-/,''),
13023
13024 classes = {
13025 "dj_quirks": has("quirks"),
13026
13027 // NOTE: Opera not supported by dijit
13028 "dj_opera": opera,
13029
13030 "dj_khtml": has("khtml"),
13031
13032 "dj_webkit": has("webkit"),
13033 "dj_safari": has("safari"),
13034 "dj_chrome": has("chrome"),
13035
13036 "dj_gecko": has("mozilla")
13037 }; // no dojo unsupported browsers
13038
13039 if(ie){
13040 classes["dj_ie"] = true;
13041 classes["dj_ie" + maj(ie)] = true;
13042 classes["dj_iequirks"] = has("quirks");
13043 }
13044 if(ff){
13045 classes["dj_ff" + maj(ff)] = true;
13046 }
13047
13048 classes["dj_" + boxModel] = true;
13049
13050 // apply browser, browser version, and box model class names
13051 var classStr = "";
13052 for(var clz in classes){
13053 if(classes[clz]){
13054 classStr += clz + " ";
13055 }
13056 }
13057 html.className = lang.trim(html.className + " " + classStr);
13058
13059 // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
13060 // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
13061 // priority is 90 to run ahead of parser priority of 100
13062 ready(90, function(){
13063 if(!geometry.isBodyLtr()){
13064 var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ");
13065 html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "));
13066 }
13067 });
13068 return has;
13069 });
13070
13071 },
13072 'dijit/Tooltip':function(){
13073 require({cache:{
13074 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}});
13075 define("dijit/Tooltip", [
13076 "dojo/_base/array", // array.forEach array.indexOf array.map
13077 "dojo/_base/declare", // declare
13078 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
13079 "dojo/dom", // dom.byId
13080 "dojo/dom-class", // domClass.add
13081 "dojo/dom-geometry", // domGeometry.position
13082 "dojo/dom-style", // domStyle.set, domStyle.get
13083 "dojo/_base/lang", // lang.hitch lang.isArrayLike
13084 "dojo/mouse",
13085 "dojo/on",
13086 "dojo/sniff", // has("ie")
13087 "./_base/manager", // manager.defaultDuration
13088 "./place",
13089 "./_Widget",
13090 "./_TemplatedMixin",
13091 "./BackgroundIframe",
13092 "dojo/text!./templates/Tooltip.html",
13093 "./main" // sets dijit.showTooltip etc. for back-compat
13094 ], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, mouse, on, has,
13095 manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
13096
13097 // module:
13098 // dijit/Tooltip
13099
13100
13101 // TODO: Tooltip should really share more positioning code with TooltipDialog, like:
13102 // - the orient() method
13103 // - the connector positioning code in show()
13104 // - the dijitTooltip[Dialog] class
13105 //
13106 // The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
13107 // with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
13108
13109 var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
13110 // summary:
13111 // Internal widget that holds the actual tooltip markup,
13112 // which occurs once per page.
13113 // Called by Tooltip widgets which are just containers to hold
13114 // the markup
13115 // tags:
13116 // protected
13117
13118 // duration: Integer
13119 // Milliseconds to fade in/fade out
13120 duration: manager.defaultDuration,
13121
13122 templateString: template,
13123
13124 postCreate: function(){
13125 this.ownerDocumentBody.appendChild(this.domNode);
13126
13127 this.bgIframe = new BackgroundIframe(this.domNode);
13128
13129 // Setup fade-in and fade-out functions.
13130 this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
13131 this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
13132 },
13133
13134 show: function(innerHTML, aroundNode, position, rtl, textDir){
13135 // summary:
13136 // Display tooltip w/specified contents to right of specified node
13137 // (To left if there's no space on the right, or if rtl == true)
13138 // innerHTML: String
13139 // Contents of the tooltip
13140 // aroundNode: DomNode|dijit/place.__Rectangle
13141 // Specifies that tooltip should be next to this node / area
13142 // position: String[]?
13143 // List of positions to try to position tooltip (ex: ["right", "above"])
13144 // rtl: Boolean?
13145 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13146 // means "rtl"; specifies GUI direction, not text direction.
13147 // textDir: String?
13148 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13149
13150
13151 if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
13152 return;
13153 }
13154
13155 if(this.fadeOut.status() == "playing"){
13156 // previous tooltip is being hidden; wait until the hide completes then show new one
13157 this._onDeck=arguments;
13158 return;
13159 }
13160 this.containerNode.innerHTML=innerHTML;
13161
13162 if(textDir){
13163 this.set("textDir", textDir);
13164 }
13165
13166 this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
13167
13168 var pos = place.around(this.domNode, aroundNode,
13169 position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
13170
13171 // Position the tooltip connector for middle alignment.
13172 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
13173 var aroundNodeCoords = pos.aroundNodePos;
13174 if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
13175 this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
13176 this.connectorNode.style.left = "";
13177 }else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
13178 this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
13179 }else{
13180 // Not *-centered, but just above/below/after/before
13181 this.connectorNode.style.left = "";
13182 this.connectorNode.style.top = "";
13183 }
13184
13185 // show it
13186 domStyle.set(this.domNode, "opacity", 0);
13187 this.fadeIn.play();
13188 this.isShowingNow = true;
13189 this.aroundNode = aroundNode;
13190 },
13191
13192 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
13193 // summary:
13194 // Private function to set CSS for tooltip node based on which position it's in.
13195 // This is called by the dijit popup code. It will also reduce the tooltip's
13196 // width to whatever width is available
13197 // tags:
13198 // protected
13199
13200 this.connectorNode.style.top = ""; //reset to default
13201
13202 var heightAvailable = spaceAvailable.h,
13203 widthAvailable = spaceAvailable.w;
13204
13205 node.className = "dijitTooltip " +
13206 {
13207 "MR-ML": "dijitTooltipRight",
13208 "ML-MR": "dijitTooltipLeft",
13209 "TM-BM": "dijitTooltipAbove",
13210 "BM-TM": "dijitTooltipBelow",
13211 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
13212 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
13213 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
13214 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
13215 "BR-BL": "dijitTooltipRight",
13216 "BL-BR": "dijitTooltipLeft"
13217 }[aroundCorner + "-" + tooltipCorner];
13218
13219 // reset width; it may have been set by orient() on a previous tooltip show()
13220 this.domNode.style.width = "auto";
13221
13222 // Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
13223 // Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
13224 // negative number since that causes an exception on IE.
13225 var size = domGeometry.position(this.domNode);
13226 if(has("ie") == 9){
13227 // workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
13228 size.w += 2;
13229 }
13230
13231 var width = Math.min((Math.max(widthAvailable,1)), size.w);
13232
13233 domGeometry.setMarginBox(this.domNode, {w: width});
13234
13235 // Reposition the tooltip connector.
13236 if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
13237 var bb = domGeometry.position(node);
13238 var tooltipConnectorHeight = this.connectorNode.offsetHeight;
13239 if(bb.h > heightAvailable){
13240 // The tooltip starts at the top of the page and will extend past the aroundNode
13241 var aroundNodePlacement = heightAvailable - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
13242 this.connectorNode.style.top = aroundNodePlacement + "px";
13243 this.connectorNode.style.bottom = "";
13244 }else{
13245 // Align center of connector with center of aroundNode, except don't let bottom
13246 // of connector extend below bottom of tooltip content, or top of connector
13247 // extend past top of tooltip content
13248 this.connectorNode.style.bottom = Math.min(
13249 Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
13250 bb.h - tooltipConnectorHeight) + "px";
13251 this.connectorNode.style.top = "";
13252 }
13253 }else{
13254 // reset the tooltip back to the defaults
13255 this.connectorNode.style.top = "";
13256 this.connectorNode.style.bottom = "";
13257 }
13258
13259 return Math.max(0, size.w - widthAvailable);
13260 },
13261
13262 _onShow: function(){
13263 // summary:
13264 // Called at end of fade-in operation
13265 // tags:
13266 // protected
13267 if(has("ie")){
13268 // the arrow won't show up on a node w/an opacity filter
13269 this.domNode.style.filter="";
13270 }
13271 },
13272
13273 hide: function(aroundNode){
13274 // summary:
13275 // Hide the tooltip
13276
13277 if(this._onDeck && this._onDeck[1] == aroundNode){
13278 // this hide request is for a show() that hasn't even started yet;
13279 // just cancel the pending show()
13280 this._onDeck=null;
13281 }else if(this.aroundNode === aroundNode){
13282 // this hide request is for the currently displayed tooltip
13283 this.fadeIn.stop();
13284 this.isShowingNow = false;
13285 this.aroundNode = null;
13286 this.fadeOut.play();
13287 }else{
13288 // just ignore the call, it's for a tooltip that has already been erased
13289 }
13290 },
13291
13292 _onHide: function(){
13293 // summary:
13294 // Called at end of fade-out operation
13295 // tags:
13296 // protected
13297
13298 this.domNode.style.cssText=""; // to position offscreen again
13299 this.containerNode.innerHTML="";
13300 if(this._onDeck){
13301 // a show request has been queued up; do it now
13302 this.show.apply(this, this._onDeck);
13303 this._onDeck=null;
13304 }
13305 },
13306
13307 _setAutoTextDir: function(/*Object*/node){
13308 // summary:
13309 // Resolve "auto" text direction for children nodes
13310 // tags:
13311 // private
13312
13313 this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
13314 array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
13315 },
13316
13317 _setTextDirAttr: function(/*String*/ textDir){
13318 // summary:
13319 // Setter for textDir.
13320 // description:
13321 // Users shouldn't call this function; they should be calling
13322 // set('textDir', value)
13323 // tags:
13324 // private
13325
13326 this._set("textDir", textDir);
13327
13328 if (textDir == "auto"){
13329 this._setAutoTextDir(this.containerNode);
13330 }else{
13331 this.containerNode.dir = this.textDir;
13332 }
13333 }
13334 });
13335
13336 dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
13337 // summary:
13338 // Static method to display tooltip w/specified contents in specified position.
13339 // See description of dijit/Tooltip.defaultPosition for details on position parameter.
13340 // If position is not specified then dijit/Tooltip.defaultPosition is used.
13341 // innerHTML: String
13342 // Contents of the tooltip
13343 // aroundNode: place.__Rectangle
13344 // Specifies that tooltip should be next to this node / area
13345 // position: String[]?
13346 // List of positions to try to position tooltip (ex: ["right", "above"])
13347 // rtl: Boolean?
13348 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13349 // means "rtl"; specifies GUI direction, not text direction.
13350 // textDir: String?
13351 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13352
13353 // After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
13354 // Possibly remove this in 2.0. Alternately, get before/after to work.
13355 if(position){
13356 position = array.map(position, function(val){
13357 return {after: "after-centered", before: "before-centered"}[val] || val;
13358 });
13359 }
13360
13361 if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
13362 return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
13363 };
13364
13365 dijit.hideTooltip = function(aroundNode){
13366 // summary:
13367 // Static method to hide the tooltip displayed via showTooltip()
13368 return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
13369 };
13370
13371 var Tooltip = declare("dijit.Tooltip", _Widget, {
13372 // summary:
13373 // Pops up a tooltip (a help message) when you hover over a node.
13374 // Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
13375
13376 // label: String
13377 // Text to display in the tooltip.
13378 // Specified as innerHTML when creating the widget from markup.
13379 label: "",
13380
13381 // showDelay: Integer
13382 // Number of milliseconds to wait after hovering over/focusing on the object, before
13383 // the tooltip is displayed.
13384 showDelay: 400,
13385
13386 // connectId: String|String[]|DomNode|DomNode[]
13387 // Id of domNode(s) to attach the tooltip to.
13388 // When user hovers over specified dom node(s), the tooltip will appear.
13389 connectId: [],
13390
13391 // position: String[]
13392 // See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
13393 position: [],
13394
13395 // selector: String?
13396 // CSS expression to apply this Tooltip to descendants of connectIds, rather than to
13397 // the nodes specified by connectIds themselves. Useful for applying a Tooltip to
13398 // a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
13399 // Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
13400 //
13401 // The application must require() an appropriate level of dojo/query to handle the selector.
13402 selector: "",
13403
13404 // TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
13405 // So, change connectId to a "", remove addTarget()/removeTarget(), etc.
13406
13407 _setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId){
13408 // summary:
13409 // Connect to specified node(s)
13410
13411 // Remove connections to old nodes (if there are any)
13412 array.forEach(this._connections || [], function(nested){
13413 array.forEach(nested, function(handle){ handle.remove(); });
13414 }, this);
13415
13416 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
13417 this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
13418 function(id){ return dom.byId(id, this.ownerDocument); }, this);
13419
13420 // Make connections
13421 this._connections = array.map(this._connectIds, function(id){
13422 var node = dom.byId(id, this.ownerDocument),
13423 selector = this.selector,
13424 delegatedEvent = selector ?
13425 function(eventType){ return on.selector(selector, eventType); } :
13426 function(eventType){ return eventType; },
13427 self = this;
13428 return [
13429 on(node, delegatedEvent(mouse.enter), function(){
13430 self._onHover(this);
13431 }),
13432 on(node, delegatedEvent("focusin"), function(){
13433 self._onHover(this);
13434 }),
13435 on(node, delegatedEvent(mouse.leave), lang.hitch(self, "_onUnHover")),
13436 on(node, delegatedEvent("focusout"), lang.hitch(self, "_onUnHover"))
13437 ];
13438 }, this);
13439
13440 this._set("connectId", newId);
13441 },
13442
13443 addTarget: function(/*OomNode|String*/ node){
13444 // summary:
13445 // Attach tooltip to specified node if it's not already connected
13446
13447 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13448
13449 var id = node.id || node;
13450 if(array.indexOf(this._connectIds, id) == -1){
13451 this.set("connectId", this._connectIds.concat(id));
13452 }
13453 },
13454
13455 removeTarget: function(/*DomNode|String*/ node){
13456 // summary:
13457 // Detach tooltip from specified node
13458
13459 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13460
13461 var id = node.id || node, // map from DOMNode back to plain id string
13462 idx = array.indexOf(this._connectIds, id);
13463 if(idx >= 0){
13464 // remove id (modifies original this._connectIds but that's OK in this case)
13465 this._connectIds.splice(idx, 1);
13466 this.set("connectId", this._connectIds);
13467 }
13468 },
13469
13470 buildRendering: function(){
13471 this.inherited(arguments);
13472 domClass.add(this.domNode,"dijitTooltipData");
13473 },
13474
13475 startup: function(){
13476 this.inherited(arguments);
13477
13478 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
13479 // didn't exist during the widget's initialization, then connect now.
13480 var ids = this.connectId;
13481 array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
13482 },
13483
13484 getContent: function(/*DomNode*/ node){
13485 // summary:
13486 // User overridable function that return the text to display in the tooltip.
13487 // tags:
13488 // extension
13489 return this.label || this.domNode.innerHTML;
13490 },
13491
13492 _onHover: function(/*DomNode*/ target){
13493 // summary:
13494 // Despite the name of this method, it actually handles both hover and focus
13495 // events on the target node, setting a timer to show the tooltip.
13496 // tags:
13497 // private
13498 if(!this._showTimer){
13499 this._showTimer = this.defer(function(){ this.open(target); }, this.showDelay);
13500 }
13501 },
13502
13503 _onUnHover: function(){
13504 // summary:
13505 // Despite the name of this method, it actually handles both mouseleave and blur
13506 // events on the target node, hiding the tooltip.
13507 // tags:
13508 // private
13509
13510 if(this._showTimer){
13511 this._showTimer.remove();
13512 delete this._showTimer;
13513 }
13514 this.close();
13515 },
13516
13517 open: function(/*DomNode*/ target){
13518 // summary:
13519 // Display the tooltip; usually not called directly.
13520 // tags:
13521 // private
13522
13523 if(this._showTimer){
13524 this._showTimer.remove();
13525 delete this._showTimer;
13526 }
13527
13528 var content = this.getContent(target);
13529 if(!content){
13530 return;
13531 }
13532 Tooltip.show(content, target, this.position, !this.isLeftToRight(), this.textDir);
13533
13534 this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
13535 this.onShow(target, this.position);
13536 },
13537
13538 close: function(){
13539 // summary:
13540 // Hide the tooltip or cancel timer for show of tooltip
13541 // tags:
13542 // private
13543
13544 if(this._connectNode){
13545 // if tooltip is currently shown
13546 Tooltip.hide(this._connectNode);
13547 delete this._connectNode;
13548 this.onHide();
13549 }
13550 if(this._showTimer){
13551 // if tooltip is scheduled to be shown (after a brief delay)
13552 this._showTimer.remove();
13553 delete this._showTimer;
13554 }
13555 },
13556
13557 onShow: function(/*===== target, position =====*/){
13558 // summary:
13559 // Called when the tooltip is shown
13560 // tags:
13561 // callback
13562 },
13563
13564 onHide: function(){
13565 // summary:
13566 // Called when the tooltip is hidden
13567 // tags:
13568 // callback
13569 },
13570
13571 destroy: function(){
13572 this.close();
13573
13574 // Remove connections manually since they aren't registered to be removed by _WidgetBase
13575 array.forEach(this._connections || [], function(nested){
13576 array.forEach(nested, function(handle){ handle.remove(); });
13577 }, this);
13578
13579 this.inherited(arguments);
13580 }
13581 });
13582
13583 Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
13584 Tooltip.show = dijit.showTooltip; // export function through module return value
13585 Tooltip.hide = dijit.hideTooltip; // export function through module return value
13586
13587 Tooltip.defaultPosition = ["after-centered", "before-centered"];
13588
13589 /*=====
13590 lang.mixin(Tooltip, {
13591 // defaultPosition: String[]
13592 // This variable controls the position of tooltips, if the position is not specified to
13593 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
13594 // possible for `dijit/place.around()`. The recommended values are:
13595 //
13596 // - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
13597 // in the case of RTL scripts like Hebrew and Arabic
13598 // - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
13599 // in the case of RTL scripts like Hebrew and Arabic
13600 // - above-centered: tooltip is centered above anchor node
13601 // - below-centered: tooltip is centered above anchor node
13602 //
13603 // The list is positions is tried, in order, until a position is found where the tooltip fits
13604 // within the viewport.
13605 //
13606 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
13607 // the screen so that there's no room above the target node. Nodes with drop downs, like
13608 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
13609 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
13610 // is only room below (or above) the target node, but not both.
13611 });
13612 =====*/
13613 return Tooltip;
13614 });
13615
13616 },
13617 'dojo/string':function(){
13618 define("dojo/string", [
13619 "./_base/kernel", // kernel.global
13620 "./_base/lang"
13621 ], function(kernel, lang){
13622
13623 // module:
13624 // dojo/string
13625
13626 var string = {
13627 // summary:
13628 // String utilities for Dojo
13629 };
13630 lang.setObject("dojo.string", string);
13631
13632 string.rep = function(/*String*/str, /*Integer*/num){
13633 // summary:
13634 // Efficiently replicate a string `n` times.
13635 // str:
13636 // the string to replicate
13637 // num:
13638 // number of times to replicate the string
13639
13640 if(num <= 0 || !str){ return ""; }
13641
13642 var buf = [];
13643 for(;;){
13644 if(num & 1){
13645 buf.push(str);
13646 }
13647 if(!(num >>= 1)){ break; }
13648 str += str;
13649 }
13650 return buf.join(""); // String
13651 };
13652
13653 string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
13654 // summary:
13655 // Pad a string to guarantee that it is at least `size` length by
13656 // filling with the character `ch` at either the start or end of the
13657 // string. Pads at the start, by default.
13658 // text:
13659 // the string to pad
13660 // size:
13661 // length to provide padding
13662 // ch:
13663 // character to pad, defaults to '0'
13664 // end:
13665 // adds padding at the end if true, otherwise pads at start
13666 // example:
13667 // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
13668 // | string.pad("Dojo", 10, "+", true);
13669
13670 if(!ch){
13671 ch = '0';
13672 }
13673 var out = String(text),
13674 pad = string.rep(ch, Math.ceil((size - out.length) / ch.length));
13675 return end ? out + pad : pad + out; // String
13676 };
13677
13678 string.substitute = function( /*String*/ template,
13679 /*Object|Array*/map,
13680 /*Function?*/ transform,
13681 /*Object?*/ thisObject){
13682 // summary:
13683 // Performs parameterized substitutions on a string. Throws an
13684 // exception if any parameter is unmatched.
13685 // template:
13686 // a string with expressions in the form `${key}` to be replaced or
13687 // `${key:format}` which specifies a format function. keys are case-sensitive.
13688 // map:
13689 // hash to search for substitutions
13690 // transform:
13691 // a function to process all parameters before substitution takes
13692 // place, e.g. mylib.encodeXML
13693 // thisObject:
13694 // where to look for optional format function; default to the global
13695 // namespace
13696 // example:
13697 // Substitutes two expressions in a string from an Array or Object
13698 // | // returns "File 'foo.html' is not found in directory '/temp'."
13699 // | // by providing substitution data in an Array
13700 // | string.substitute(
13701 // | "File '${0}' is not found in directory '${1}'.",
13702 // | ["foo.html","/temp"]
13703 // | );
13704 // |
13705 // | // also returns "File 'foo.html' is not found in directory '/temp'."
13706 // | // but provides substitution data in an Object structure. Dotted
13707 // | // notation may be used to traverse the structure.
13708 // | string.substitute(
13709 // | "File '${name}' is not found in directory '${info.dir}'.",
13710 // | { name: "foo.html", info: { dir: "/temp" } }
13711 // | );
13712 // example:
13713 // Use a transform function to modify the values:
13714 // | // returns "file 'foo.html' is not found in directory '/temp'."
13715 // | string.substitute(
13716 // | "${0} is not found in ${1}.",
13717 // | ["foo.html","/temp"],
13718 // | function(str){
13719 // | // try to figure out the type
13720 // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
13721 // | return prefix + " '" + str + "'";
13722 // | }
13723 // | );
13724 // example:
13725 // Use a formatter
13726 // | // returns "thinger -- howdy"
13727 // | string.substitute(
13728 // | "${0:postfix}", ["thinger"], null, {
13729 // | postfix: function(value, key){
13730 // | return value + " -- howdy";
13731 // | }
13732 // | }
13733 // | );
13734
13735 thisObject = thisObject || kernel.global;
13736 transform = transform ?
13737 lang.hitch(thisObject, transform) : function(v){ return v; };
13738
13739 return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
13740 function(match, key, format){
13741 var value = lang.getObject(key, false, map);
13742 if(format){
13743 value = lang.getObject(format, false, thisObject).call(thisObject, value, key);
13744 }
13745 return transform(value, key).toString();
13746 }); // String
13747 };
13748
13749 string.trim = String.prototype.trim ?
13750 lang.trim : // aliasing to the native function
13751 function(str){
13752 str = str.replace(/^\s+/, '');
13753 for(var i = str.length - 1; i >= 0; i--){
13754 if(/\S/.test(str.charAt(i))){
13755 str = str.substring(0, i + 1);
13756 break;
13757 }
13758 }
13759 return str;
13760 };
13761
13762 /*=====
13763 string.trim = function(str){
13764 // summary:
13765 // Trims whitespace from both sides of the string
13766 // str: String
13767 // String to be trimmed
13768 // returns: String
13769 // Returns the trimmed string
13770 // description:
13771 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
13772 // The short yet performant version of this function is dojo.trim(),
13773 // which is part of Dojo base. Uses String.prototype.trim instead, if available.
13774 return ""; // String
13775 };
13776 =====*/
13777
13778 return string;
13779 });
13780
13781 },
13782 'url: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>",
13783 'dijit/layout/AccordionPane':function(){
13784 define("dijit/layout/AccordionPane", [
13785 "dojo/_base/declare", // declare
13786 "dojo/_base/kernel", // kernel.deprecated
13787 "./ContentPane"
13788 ], function(declare, kernel, ContentPane){
13789
13790 // module:
13791 // dijit/layout/AccordionPane
13792
13793 return declare("dijit.layout.AccordionPane", ContentPane, {
13794 // summary:
13795 // Deprecated widget. Use `dijit/layout/ContentPane` instead.
13796 // tags:
13797 // deprecated
13798
13799 constructor: function(){
13800 kernel.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
13801 },
13802
13803 onSelected: function(){
13804 // summary:
13805 // called when this pane is selected
13806 }
13807 });
13808 });
13809
13810 },
13811 'dijit/dijit':function(){
13812 define("dijit/dijit", [
13813 "./main",
13814 "./_base",
13815 "dojo/parser",
13816 "./_Widget",
13817 "./_TemplatedMixin",
13818 "./_Container",
13819 "./layout/_LayoutWidget",
13820 "./form/_FormWidget",
13821 "./form/_FormValueWidget"
13822 ], function(dijit){
13823
13824 // module:
13825 // dijit/dijit
13826
13827 /*=====
13828 return {
13829 // summary:
13830 // A roll-up for common dijit methods
13831 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
13832 // And some other stuff that we tend to pull in all the time anyway
13833 };
13834 =====*/
13835
13836 return dijit;
13837 });
13838
13839 },
13840 'dijit/form/DropDownButton':function(){
13841 require({cache:{
13842 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"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\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"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\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n"}});
13843 define("dijit/form/DropDownButton", [
13844 "dojo/_base/declare", // declare
13845 "dojo/_base/lang", // hitch
13846 "dojo/query", // query
13847 "../registry", // registry.byNode
13848 "../popup", // dijit.popup2.hide
13849 "./Button",
13850 "../_Container",
13851 "../_HasDropDown",
13852 "dojo/text!./templates/DropDownButton.html"
13853 ], function(declare, lang, query, registry, popup, Button, _Container, _HasDropDown, template){
13854
13855 // module:
13856 // dijit/form/DropDownButton
13857
13858
13859 return declare("dijit.form.DropDownButton", [Button, _Container, _HasDropDown], {
13860 // summary:
13861 // A button with a drop down
13862 //
13863 // example:
13864 // | <button data-dojo-type="dijit/form/DropDownButton">
13865 // | Hello world
13866 // | <div data-dojo-type="dijit/Menu">...</div>
13867 // | </button>
13868 //
13869 // example:
13870 // | var button1 = new DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
13871 // | win.body().appendChild(button1);
13872 //
13873
13874 baseClass : "dijitDropDownButton",
13875
13876 templateString: template,
13877
13878 _fillContent: function(){
13879 // Overrides Button._fillContent().
13880 //
13881 // My inner HTML contains both the button contents and a drop down widget, like
13882 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
13883 // The first node is assumed to be the button content. The widget is the popup.
13884
13885 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
13886 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
13887 // content, not just nodes[0]
13888 var nodes = query("*", this.srcNodeRef);
13889 this.inherited(arguments, [nodes[0]]);
13890
13891 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
13892 this.dropDownContainer = this.srcNodeRef;
13893 }
13894 },
13895
13896 startup: function(){
13897 if(this._started){ return; }
13898
13899 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
13900 // make it invisible, and store a reference to pass to the popup code.
13901 if(!this.dropDown && this.dropDownContainer){
13902 var dropDownNode = query("[widgetId]", this.dropDownContainer)[0];
13903 this.dropDown = registry.byNode(dropDownNode);
13904 delete this.dropDownContainer;
13905 }
13906 if(this.dropDown){
13907 popup.hide(this.dropDown);
13908 }
13909
13910 this.inherited(arguments);
13911 },
13912
13913 isLoaded: function(){
13914 // Returns whether or not we are loaded - if our dropdown has an href,
13915 // then we want to check that.
13916 var dropDown = this.dropDown;
13917 return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
13918 },
13919
13920 loadDropDown: function(/*Function*/ callback){
13921 // Default implementation assumes that drop down already exists,
13922 // but hasn't loaded it's data (ex: ContentPane w/href).
13923 // App must override if the drop down is lazy-created.
13924 var dropDown = this.dropDown;
13925 var handler = dropDown.on("load", lang.hitch(this, function(){
13926 handler.remove();
13927 callback();
13928 }));
13929 dropDown.refresh(); // tell it to load
13930 },
13931
13932 isFocusable: function(){
13933 // Overridden so that focus is handled by the _HasDropDown mixin, not by
13934 // the _FormWidget mixin.
13935 return this.inherited(arguments) && !this._mouseDown;
13936 }
13937 });
13938
13939 });
13940
13941 },
13942 'dijit/form/_FormValueMixin':function(){
13943 define("dijit/form/_FormValueMixin", [
13944 "dojo/_base/declare", // declare
13945 "dojo/dom-attr", // domAttr.set
13946 "dojo/keys", // keys.ESCAPE
13947 "dojo/sniff", // has("ie"), has("quirks")
13948 "./_FormWidgetMixin"
13949 ], function(declare, domAttr, keys, has, _FormWidgetMixin){
13950
13951 // module:
13952 // dijit/form/_FormValueMixin
13953
13954 return declare("dijit.form._FormValueMixin", _FormWidgetMixin, {
13955 // summary:
13956 // Mixin for widgets corresponding to native HTML elements such as `<input>` or `<select>`
13957 // that have user changeable values.
13958 // description:
13959 // Each _FormValueMixin represents a single input value, and has a (possibly hidden) `<input>` element,
13960 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
13961 // works as expected.
13962
13963 // readOnly: Boolean
13964 // Should this widget respond to user input?
13965 // In markup, this is specified as "readOnly".
13966 // Similar to disabled except readOnly form values are submitted.
13967 readOnly: false,
13968
13969 _setReadOnlyAttr: function(/*Boolean*/ value){
13970 domAttr.set(this.focusNode, 'readOnly', value);
13971 this._set("readOnly", value);
13972 },
13973
13974 postCreate: function(){
13975 this.inherited(arguments);
13976
13977 if(has("ie")){ // IE won't stop the event with keypress
13978 this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
13979 }
13980 // Update our reset value if it hasn't yet been set (because this.set()
13981 // is only called when there *is* a value)
13982 if(this._resetValue === undefined){
13983 this._lastValueReported = this._resetValue = this.value;
13984 }
13985 },
13986
13987 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13988 // summary:
13989 // Hook so set('value', value) works.
13990 // description:
13991 // Sets the value of the widget.
13992 // If the value has changed, then fire onChange event, unless priorityChange
13993 // is specified as null (or false?)
13994 this._handleOnChange(newValue, priorityChange);
13995 },
13996
13997 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13998 // summary:
13999 // Called when the value of the widget has changed. Saves the new value in this.value,
14000 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
14001 this._set("value", newValue);
14002 this.inherited(arguments);
14003 },
14004
14005 undo: function(){
14006 // summary:
14007 // Restore the value to the last value passed to onChange
14008 this._setValueAttr(this._lastValueReported, false);
14009 },
14010
14011 reset: function(){
14012 // summary:
14013 // Reset the widget's value to what it was at initialization time
14014 this._hasBeenBlurred = false;
14015 this._setValueAttr(this._resetValue, true);
14016 },
14017
14018 _onKeyDown: function(e){
14019 if(e.keyCode == keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
14020 if(has("ie") < 9 || (has("ie") && has("quirks"))){
14021 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
14022 var node = e.srcElement,
14023 te = node.ownerDocument.createEventObject();
14024 te.keyCode = keys.ESCAPE;
14025 te.shiftKey = e.shiftKey;
14026 node.fireEvent('onkeypress', te);
14027 }
14028 }
14029 }
14030 });
14031 });
14032
14033 },
14034 'dijit/form/_FormWidgetMixin':function(){
14035 define("dijit/form/_FormWidgetMixin", [
14036 "dojo/_base/array", // array.forEach
14037 "dojo/_base/declare", // declare
14038 "dojo/dom-attr", // domAttr.set
14039 "dojo/dom-style", // domStyle.get
14040 "dojo/_base/lang", // lang.hitch lang.isArray
14041 "dojo/mouse", // mouse.isLeft
14042 "dojo/sniff", // has("webkit")
14043 "dojo/window", // winUtils.scrollIntoView
14044 "../a11y" // a11y.hasDefaultTabStop
14045 ], function(array, declare, domAttr, domStyle, lang, mouse, has, winUtils, a11y){
14046
14047 // module:
14048 // dijit/form/_FormWidgetMixin
14049
14050 return declare("dijit.form._FormWidgetMixin", null, {
14051 // summary:
14052 // Mixin for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
14053 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
14054 //
14055 // description:
14056 // Represents a single HTML element.
14057 // All these widgets should have these attributes just like native HTML input elements.
14058 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
14059 //
14060 // They also share some common methods.
14061
14062 // name: [const] String
14063 // Name used when submitting form; same as "name" attribute or plain HTML elements
14064 name: "",
14065
14066 // alt: String
14067 // Corresponds to the native HTML `<input>` element's attribute.
14068 alt: "",
14069
14070 // value: String
14071 // Corresponds to the native HTML `<input>` element's attribute.
14072 value: "",
14073
14074 // type: [const] String
14075 // Corresponds to the native HTML `<input>` element's attribute.
14076 type: "text",
14077
14078 // type: String
14079 // Apply aria-label in markup to the widget's focusNode
14080 "aria-label": "focusNode",
14081
14082 // tabIndex: String
14083 // Order fields are traversed when user hits the tab key
14084 tabIndex: "0",
14085 _setTabIndexAttr: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
14086
14087 // disabled: Boolean
14088 // Should this widget respond to user input?
14089 // In markup, this is specified as "disabled='disabled'", or just "disabled".
14090 disabled: false,
14091
14092 // intermediateChanges: Boolean
14093 // Fires onChange for each value change or only on demand
14094 intermediateChanges: false,
14095
14096 // scrollOnFocus: Boolean
14097 // On focus, should this widget scroll into view?
14098 scrollOnFocus: true,
14099
14100 // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
14101 // works with screen reader
14102 _setIdAttr: "focusNode",
14103
14104 _setDisabledAttr: function(/*Boolean*/ value){
14105 this._set("disabled", value);
14106 domAttr.set(this.focusNode, 'disabled', value);
14107 if(this.valueNode){
14108 domAttr.set(this.valueNode, 'disabled', value);
14109 }
14110 this.focusNode.setAttribute("aria-disabled", value ? "true" : "false");
14111
14112 if(value){
14113 // reset these, because after the domNode is disabled, we can no longer receive
14114 // mouse related events, see #4200
14115 this._set("hovering", false);
14116 this._set("active", false);
14117
14118 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
14119 var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex :
14120 ("_setTabIndexAttr" in this) ? this._setTabIndexAttr : "focusNode";
14121 array.forEach(lang.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
14122 var node = this[attachPointName];
14123 // complex code because tabIndex=-1 on a <div> doesn't work on FF
14124 if(has("webkit") || a11y.hasDefaultTabStop(node)){ // see #11064 about webkit bug
14125 node.setAttribute('tabIndex', "-1");
14126 }else{
14127 node.removeAttribute('tabIndex');
14128 }
14129 }, this);
14130 }else{
14131 if(this.tabIndex != ""){
14132 this.set('tabIndex', this.tabIndex);
14133 }
14134 }
14135 },
14136
14137 _onFocus: function(/*String*/ by){
14138 // If user clicks on the widget, even if the mouse is released outside of it,
14139 // this widget's focusNode should get focus (to mimic native browser hehavior).
14140 // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
14141 if(by == "mouse" && this.isFocusable()){
14142 // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
14143 var focusConnector = this.connect(this.focusNode, "onfocus", function(){
14144 this.disconnect(mouseUpConnector);
14145 this.disconnect(focusConnector);
14146 });
14147 // Set a global event to handle mouseup, so it fires properly
14148 // even if the cursor leaves this.domNode before the mouse up event.
14149 var mouseUpConnector = this.connect(this.ownerDocumentBody, "onmouseup", function(){
14150 this.disconnect(mouseUpConnector);
14151 this.disconnect(focusConnector);
14152 // if here, then the mousedown did not focus the focusNode as the default action
14153 if(this.focused){
14154 this.focus();
14155 }
14156 });
14157 }
14158 if(this.scrollOnFocus){
14159 this.defer(function(){ winUtils.scrollIntoView(this.domNode); }); // without defer, the input caret position can change on mouse click
14160 }
14161 this.inherited(arguments);
14162 },
14163
14164 isFocusable: function(){
14165 // summary:
14166 // Tells if this widget is focusable or not. Used internally by dijit.
14167 // tags:
14168 // protected
14169 return !this.disabled && this.focusNode && (domStyle.get(this.domNode, "display") != "none");
14170 },
14171
14172 focus: function(){
14173 // summary:
14174 // Put focus on this widget
14175 if(!this.disabled && this.focusNode.focus){
14176 try{ this.focusNode.focus(); }catch(e){}/*squelch errors from hidden nodes*/
14177 }
14178 },
14179
14180 compare: function(/*anything*/ val1, /*anything*/ val2){
14181 // summary:
14182 // Compare 2 values (as returned by get('value') for this widget).
14183 // tags:
14184 // protected
14185 if(typeof val1 == "number" && typeof val2 == "number"){
14186 return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
14187 }else if(val1 > val2){
14188 return 1;
14189 }else if(val1 < val2){
14190 return -1;
14191 }else{
14192 return 0;
14193 }
14194 },
14195
14196 onChange: function(/*===== newValue =====*/){
14197 // summary:
14198 // Callback when this widget's value is changed.
14199 // tags:
14200 // callback
14201 },
14202
14203 // _onChangeActive: [private] Boolean
14204 // Indicates that changes to the value should call onChange() callback.
14205 // This is false during widget initialization, to avoid calling onChange()
14206 // when the initial value is set.
14207 _onChangeActive: false,
14208
14209 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
14210 // summary:
14211 // Called when the value of the widget is set. Calls onChange() if appropriate
14212 // newValue:
14213 // the new value
14214 // priorityChange:
14215 // For a slider, for example, dragging the slider is priorityChange==false,
14216 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
14217 // onChange is only called form priorityChange=true events.
14218 // tags:
14219 // private
14220 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
14221 // this block executes not for a change, but during initialization,
14222 // and is used to store away the original value (or for ToggleButton, the original checked state)
14223 this._resetValue = this._lastValueReported = newValue;
14224 }
14225 this._pendingOnChange = this._pendingOnChange
14226 || (typeof newValue != typeof this._lastValueReported)
14227 || (this.compare(newValue, this._lastValueReported) != 0);
14228 if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
14229 this._lastValueReported = newValue;
14230 this._pendingOnChange = false;
14231 if(this._onChangeActive){
14232 if(this._onChangeHandle){
14233 this._onChangeHandle.remove();
14234 }
14235 // defer allows hidden value processing to run and
14236 // also the onChange handler can safely adjust focus, etc
14237 this._onChangeHandle = this.defer(
14238 function(){
14239 this._onChangeHandle = null;
14240 this.onChange(newValue);
14241 }); // try to collapse multiple onChange's fired faster than can be processed
14242 }
14243 }
14244 },
14245
14246 create: function(){
14247 // Overrides _Widget.create()
14248 this.inherited(arguments);
14249 this._onChangeActive = true;
14250 },
14251
14252 destroy: function(){
14253 if(this._onChangeHandle){ // destroy called before last onChange has fired
14254 this._onChangeHandle.remove();
14255 this.onChange(this._lastValueReported);
14256 }
14257 this.inherited(arguments);
14258 }
14259 });
14260
14261 });
14262
14263 },
14264 'dijit/a11yclick':function(){
14265 define("dijit/a11yclick", [
14266 "dojo/on",
14267 "dojo/_base/array", // array.forEach
14268 "dojo/keys", // keys.ENTER keys.SPACE
14269 "dojo/_base/declare", // declare
14270 "dojo/has", // has("dom-addeventlistener")
14271 "dojo/_base/unload", // unload.addOnWindowUnload
14272 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
14273 ], function(on, array, keys, declare, has, unload, win){
14274
14275 // module:
14276 // dijit/a11yclick
14277
14278 // Keep track of where the last keydown event was, to help avoid generating
14279 // spurious ondijitclick events when:
14280 // 1. focus is on a <button> or <a>
14281 // 2. user presses then releases the ENTER key
14282 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
14283 // 4. onkeyup event fires, causing the ondijitclick handler to fire
14284 var lastKeyDownNode = null;
14285 if(has("dom-addeventlistener")){
14286 win.doc.addEventListener('keydown', function(evt){
14287 lastKeyDownNode = evt.target;
14288 }, true);
14289 }else{
14290 // Fallback path for IE6-8
14291 (function(){
14292 var keydownCallback = function(evt){
14293 lastKeyDownNode = evt.srcElement;
14294 };
14295 win.doc.attachEvent('onkeydown', keydownCallback);
14296 unload.addOnWindowUnload(function(){
14297 win.doc.detachEvent('onkeydown', keydownCallback);
14298 });
14299 })();
14300 }
14301
14302 function clickKey(/*Event*/ e){
14303 return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
14304 !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
14305 }
14306
14307 return function(node, listener){
14308 // summary:
14309 // Custom a11yclick (a.k.a. ondijitclick) event
14310 // which triggers on a mouse click, touch, or space/enter keyup.
14311
14312 if(/input|button/i.test(node.nodeName)){
14313 // pass through, the browser already generates click event on SPACE/ENTER key
14314 return on(node, "click", listener);
14315 }else{
14316 // Don't fire the click event unless both the keydown and keyup occur on this node.
14317 // Avoids problems where focus shifted to this node or away from the node on keydown,
14318 // either causing this node to process a stray keyup event, or causing another node
14319 // to get a stray keyup event.
14320
14321 var handles = [
14322 on(node, "keydown", function(e){
14323 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14324 if(clickKey(e)){
14325 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
14326 lastKeyDownNode = e.target;
14327
14328 // Prevent viewport scrolling on space key in IE<9.
14329 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
14330 e.preventDefault();
14331 }
14332 }),
14333
14334 on(node, "keyup", function(e){
14335 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14336 if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
14337 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
14338 lastKeyDownNode = null;
14339 on.emit(e.target, "click", {
14340 cancelable: true,
14341 bubbles: true
14342 });
14343 }
14344 }),
14345
14346 on(node, "click", function(e){
14347 // catch mouse clicks, plus the on.emit() calls from above and below
14348 listener.call(this, e);
14349 })
14350 ];
14351
14352 if(has("touch")){
14353 // touchstart-->touchend will automatically generate a click event, but there are problems
14354 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
14355 // if click doesn't fire naturally.
14356
14357 var clickTimer;
14358 handles.push(
14359 on(node, "touchend", function(e){
14360 var target = e.target;
14361 clickTimer = setTimeout(function(){
14362 clickTimer = null;
14363 on.emit(target, "click", {
14364 cancelable: true,
14365 bubbles: true
14366 });
14367 }, 600);
14368 }),
14369 on(node, "click", function(e){
14370 // If browser generates a click naturally, clear the timer to fire a synthetic click event
14371 if(clickTimer){
14372 clearTimeout(clickTimer);
14373 }
14374 })
14375 // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
14376 // event <300ms after the touchend event, then clear the synthetic click timer, because user
14377 // is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
14378 // zoom level has changed.
14379 );
14380 }
14381
14382 return {
14383 remove: function(){
14384 array.forEach(handles, function(h){ h.remove(); });
14385 if(clickTimer){
14386 clearTimeout(clickTimer);
14387 clickTimer = null;
14388 }
14389 }
14390 };
14391 }
14392 };
14393
14394 return ret;
14395 });
14396
14397 },
14398 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
14399 'dijit/Destroyable':function(){
14400 define("dijit/Destroyable", [
14401 "dojo/_base/array", // array.forEach array.map
14402 "dojo/aspect",
14403 "dojo/_base/declare"
14404 ], function(array, aspect, declare){
14405
14406 // module:
14407 // dijit/Destroyable
14408
14409 return declare("dijit.Destroyable", null, {
14410 // summary:
14411 // Mixin to track handles and release them when instance is destroyed.
14412 // description:
14413 // Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
14414 // dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
14415 // Then call destroy() later to destroy this instance and release the resources.
14416
14417 destroy: function(/*Boolean*/ preserveDom){
14418 // summary:
14419 // Destroy this class, releasing any resources registered via own().
14420 this._destroyed = true;
14421 },
14422
14423 own: function(){
14424 // summary:
14425 // Track specified handles and remove/destroy them when this instance is destroyed, unless they were
14426 // already removed/destroyed manually.
14427 // tags:
14428 // protected
14429 // returns:
14430 // The array of specified handles, so you can do for example:
14431 // | var handle = this.own(on(...))[0];
14432
14433 array.forEach(arguments, function(handle){
14434 var destroyMethodName =
14435 "destroyRecursive" in handle ? "destroyRecursive" : // remove "destroyRecursive" for 2.0
14436 "destroy" in handle ? "destroy" :
14437 "remove";
14438
14439 // When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
14440 // the handle will be destroyed before a subclass's destroy() method starts running, before it calls
14441 // this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
14442 // onDestroy() method and connect to that instead.
14443 var odh = aspect.before(this, "destroy", function(preserveDom){
14444 handle[destroyMethodName](preserveDom);
14445 });
14446
14447 // If handle is destroyed manually before this.destroy() is called, remove the listener set directly above.
14448 var hdh = aspect.after(handle, destroyMethodName, function(){
14449 odh.remove();
14450 hdh.remove();
14451 }, true);
14452 }, this);
14453
14454 return arguments; // handle
14455 }
14456 });
14457
14458 });
14459
14460 },
14461 'dijit/layout/_ContentPaneResizeMixin':function(){
14462 define("dijit/layout/_ContentPaneResizeMixin", [
14463 "dojo/_base/array", // array.filter array.forEach
14464 "dojo/_base/declare", // declare
14465 "dojo/dom-class", // domClass.contains domClass.toggle
14466 "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
14467 "dojo/dom-style",
14468 "dojo/_base/lang", // lang.mixin
14469 "dojo/query", // query
14470 "dojo/sniff", // has("ie")
14471 "../registry", // registry.byId
14472 "../Viewport",
14473 "./utils" // marginBox2contextBox
14474 ], function(array, declare, domClass, domGeometry, domStyle, lang, query, has,
14475 registry, Viewport, layoutUtils){
14476
14477 // module:
14478 // dijit/layout/_ContentPaneResizeMixin
14479
14480
14481 return declare("dijit.layout._ContentPaneResizeMixin", null, {
14482 // summary:
14483 // Resize() functionality of ContentPane. If there's a single layout widget
14484 // child then it will call resize() with the same dimensions as the ContentPane.
14485 // Otherwise just calls resize on each child.
14486 //
14487 // Also implements basic startup() functionality, where starting the parent
14488 // will start the children
14489
14490 // doLayout: Boolean
14491 // - false - don't adjust size of children
14492 // - true - if there is a single visible child widget, set it's size to however big the ContentPane is
14493 doLayout: true,
14494
14495 // isLayoutContainer: [protected] Boolean
14496 // Indicates that this widget will call resize() on it's child widgets
14497 // when they become visible.
14498 isLayoutContainer: true,
14499
14500 startup: function(){
14501 // summary:
14502 // See `dijit/layout/_LayoutWidget.startup()` for description.
14503 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14504 // the same API.
14505
14506 if(this._started){ return; }
14507
14508 var parent = this.getParent();
14509 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
14510
14511 // I need to call resize() on my child/children (when I become visible), unless
14512 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
14513 this._needLayout = !this._childOfLayoutWidget;
14514
14515 this.inherited(arguments);
14516
14517 if(this._isShown()){
14518 this._onShow();
14519 }
14520
14521 if(!this._childOfLayoutWidget){
14522 // Since my parent isn't a layout container, and my style *may be* width=height=100%
14523 // or something similar (either set directly or via a CSS class),
14524 // monitor when viewport size changes so that I can re-layout.
14525 // This is more for subclasses of ContentPane than ContentPane itself, although it
14526 // could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size.
14527 this.own(Viewport.on("resize", lang.hitch(this, "resize")));
14528 }
14529 },
14530
14531 _checkIfSingleChild: function(){
14532 // summary:
14533 // Test if we have exactly one visible widget as a child,
14534 // and if so assume that we are a container for that widget,
14535 // and should propagate startup() and resize() calls to it.
14536 // Skips over things like data stores since they aren't visible.
14537
14538 var candidateWidgets = [],
14539 otherVisibleNodes = false;
14540
14541 query("> *", this.containerNode).some(function(node){
14542 var widget = registry.byNode(node);
14543 if(widget && widget.resize){
14544 candidateWidgets.push(widget);
14545 }else if(node.offsetHeight){
14546 otherVisibleNodes = true;
14547 }
14548 });
14549
14550 this._singleChild = candidateWidgets.length == 1 && !otherVisibleNodes ?
14551 candidateWidgets[0] : null;
14552
14553 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
14554 domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
14555 },
14556
14557 resize: function(changeSize, resultSize){
14558 // summary:
14559 // See `dijit/layout/_LayoutWidget.resize()` for description.
14560 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14561 // the same API.
14562
14563 this._resizeCalled = true;
14564
14565 this._scheduleLayout(changeSize, resultSize);
14566 },
14567
14568 _scheduleLayout: function(changeSize, resultSize){
14569 // summary:
14570 // Resize myself, and call resize() on each of my child layout widgets, either now
14571 // (if I'm currently visible) or when I become visible
14572 if(this._isShown()){
14573 this._layout(changeSize, resultSize);
14574 }else{
14575 this._needLayout = true;
14576 this._changeSize = changeSize;
14577 this._resultSize = resultSize;
14578 }
14579 },
14580
14581 _layout: function(changeSize, resultSize){
14582 // summary:
14583 // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
14584 // Also, since I am an isLayoutContainer widget, each of my children expects me to
14585 // call resize() or layout() on it.
14586 //
14587 // Should be called on initialization and also whenever we get new content
14588 // (from an href, or from set('content', ...))... but deferred until
14589 // the ContentPane is visible
14590
14591 delete this._needLayout;
14592
14593 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
14594 // never called directly, so resize() is our trigger to do the initial href download (see [20099]).
14595 // However, don't load href for closed TitlePanes.
14596 if(!this._wasShown && this.open !== false){
14597 this._onShow();
14598 }
14599
14600 // Set margin box size, unless it wasn't specified, in which case use current size.
14601 if(changeSize){
14602 domGeometry.setMarginBox(this.domNode, changeSize);
14603 }
14604
14605 // Compute content box size of containerNode in case we [later] need to size our single child.
14606 var cn = this.containerNode;
14607 if(cn === this.domNode){
14608 // If changeSize or resultSize was passed to this method and this.containerNode ==
14609 // this.domNode then we can compute the content-box size without querying the node,
14610 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
14611 var mb = resultSize || {};
14612 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
14613 if(!("h" in mb) || !("w" in mb)){
14614 mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
14615 }
14616 this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
14617 }else{
14618 this._contentBox = domGeometry.getContentBox(cn);
14619 }
14620
14621 this._layoutChildren();
14622 },
14623
14624 _layoutChildren: function(){
14625 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
14626 // of the ContentPane (rather than changing it through the set("content", ...) API.
14627 if(this.doLayout){
14628 this._checkIfSingleChild();
14629 }
14630
14631 if(this._singleChild && this._singleChild.resize){
14632 var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
14633
14634 // note: if widget has padding this._contentBox will have l and t set,
14635 // but don't pass them to resize() or it will doubly-offset the child
14636 this._singleChild.resize({w: cb.w, h: cb.h});
14637 }else{
14638 // All my child widgets are independently sized (rather than matching my size),
14639 // but I still need to call resize() on each child to make it layout.
14640 array.forEach(this.getChildren(), function(widget){
14641 if(widget.resize){
14642 widget.resize();
14643 }
14644 });
14645 }
14646 },
14647
14648 _isShown: function(){
14649 // summary:
14650 // Returns true if the content is currently shown.
14651 // description:
14652 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
14653 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
14654 // tree every call, and at least solves the performance problem on page load by deferring loading
14655 // hidden ContentPanes until they are first shown
14656
14657 if(this._childOfLayoutWidget){
14658 // If we are TitlePane, etc - we return that only *IF* we've been resized
14659 if(this._resizeCalled && "open" in this){
14660 return this.open;
14661 }
14662 return this._resizeCalled;
14663 }else if("open" in this){
14664 return this.open; // for TitlePane, etc.
14665 }else{
14666 var node = this.domNode, parent = this.domNode.parentNode;
14667 return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
14668 parent && parent.style && (parent.style.display != 'none');
14669 }
14670 },
14671
14672 _onShow: function(){
14673 // summary:
14674 // Called when the ContentPane is made visible
14675 // description:
14676 // For a plain ContentPane, this is called on initialization, from startup().
14677 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
14678 // called whenever the pane is made visible.
14679 //
14680 // Does layout/resize of child widget(s)
14681
14682 // Need to keep track of whether ContentPane has been shown (which is different than
14683 // whether or not it's currently visible).
14684 this._wasShown = true;
14685
14686 if(this._needLayout){
14687 // If a layout has been scheduled for when we become visible, do it now
14688 this._layout(this._changeSize, this._resultSize);
14689 }
14690
14691 this.inherited(arguments);
14692 }
14693 });
14694
14695 });
14696
14697 },
14698 'dijit/WidgetSet':function(){
14699 define("dijit/WidgetSet", [
14700 "dojo/_base/array", // array.forEach array.map
14701 "dojo/_base/declare", // declare
14702 "dojo/_base/kernel", // kernel.global
14703 "./registry" // to add functions to dijit.registry
14704 ], function(array, declare, kernel, registry){
14705
14706 // module:
14707 // dijit/WidgetSet
14708
14709 var WidgetSet = declare("dijit.WidgetSet", null, {
14710 // summary:
14711 // A set of widgets indexed by id.
14712 // Deprecated, will be removed in 2.0.
14713 //
14714 // example:
14715 // Create a small list of widgets:
14716 // | require(["dijit/WidgetSet", "dijit/registry"],
14717 // | function(WidgetSet, registry){
14718 // | var ws = new WidgetSet();
14719 // | ws.add(registry.byId("one"));
14720 // | ws.add(registry.byId("two"));
14721 // | // destroy both:
14722 // | ws.forEach(function(w){ w.destroy(); });
14723 // | });
14724
14725 constructor: function(){
14726 this._hash = {};
14727 this.length = 0;
14728 },
14729
14730 add: function(/*dijit/_WidgetBase*/ widget){
14731 // summary:
14732 // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
14733 //
14734 // widget: dijit/_WidgetBase
14735 // Any dijit/_WidgetBase subclass.
14736 if(this._hash[widget.id]){
14737 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
14738 }
14739 this._hash[widget.id] = widget;
14740 this.length++;
14741 },
14742
14743 remove: function(/*String*/ id){
14744 // summary:
14745 // Remove a widget from this WidgetSet. Does not destroy the widget; simply
14746 // removes the reference.
14747 if(this._hash[id]){
14748 delete this._hash[id];
14749 this.length--;
14750 }
14751 },
14752
14753 forEach: function(/*Function*/ func, /* Object? */thisObj){
14754 // summary:
14755 // Call specified function for each widget in this set.
14756 //
14757 // func:
14758 // A callback function to run for each item. Is passed the widget, the index
14759 // in the iteration, and the full hash, similar to `array.forEach`.
14760 //
14761 // thisObj:
14762 // An optional scope parameter
14763 //
14764 // example:
14765 // Using the default `dijit.registry` instance:
14766 // | require(["dijit/WidgetSet", "dijit/registry"],
14767 // | function(WidgetSet, registry){
14768 // | registry.forEach(function(widget){
14769 // | console.log(widget.declaredClass);
14770 // | });
14771 // | });
14772 //
14773 // returns:
14774 // Returns self, in order to allow for further chaining.
14775
14776 thisObj = thisObj || kernel.global;
14777 var i = 0, id;
14778 for(id in this._hash){
14779 func.call(thisObj, this._hash[id], i++, this._hash);
14780 }
14781 return this; // dijit/WidgetSet
14782 },
14783
14784 filter: function(/*Function*/ filter, /* Object? */thisObj){
14785 // summary:
14786 // Filter down this WidgetSet to a smaller new WidgetSet
14787 // Works the same as `array.filter` and `NodeList.filter`
14788 //
14789 // filter:
14790 // Callback function to test truthiness. Is passed the widget
14791 // reference and the pseudo-index in the object.
14792 //
14793 // thisObj: Object?
14794 // Option scope to use for the filter function.
14795 //
14796 // example:
14797 // Arbitrary: select the odd widgets in this list
14798 // |
14799 // |
14800 // |
14801 // | require(["dijit/WidgetSet", "dijit/registry"],
14802 // | function(WidgetSet, registry){
14803 // | registry.filter(function(w, i){
14804 // | return i % 2 == 0;
14805 // | }).forEach(function(w){ /* odd ones */ });
14806 // | });
14807
14808 thisObj = thisObj || kernel.global;
14809 var res = new WidgetSet(), i = 0, id;
14810 for(id in this._hash){
14811 var w = this._hash[id];
14812 if(filter.call(thisObj, w, i++, this._hash)){
14813 res.add(w);
14814 }
14815 }
14816 return res; // dijit/WidgetSet
14817 },
14818
14819 byId: function(/*String*/ id){
14820 // summary:
14821 // Find a widget in this list by it's id.
14822 // example:
14823 // Test if an id is in a particular WidgetSet
14824 // | require(["dijit/WidgetSet", "dijit/registry"],
14825 // | function(WidgetSet, registry){
14826 // | var ws = new WidgetSet();
14827 // | ws.add(registry.byId("bar"));
14828 // | var t = ws.byId("bar") // returns a widget
14829 // | var x = ws.byId("foo"); // returns undefined
14830 // | });
14831
14832 return this._hash[id]; // dijit/_WidgetBase
14833 },
14834
14835 byClass: function(/*String*/ cls){
14836 // summary:
14837 // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
14838 //
14839 // cls: String
14840 // The Class to scan for. Full dot-notated string.
14841 //
14842 // example:
14843 // Find all `dijit.TitlePane`s in a page:
14844 // | require(["dijit/WidgetSet", "dijit/registry"],
14845 // | function(WidgetSet, registry){
14846 // | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
14847 // | });
14848
14849 var res = new WidgetSet(), id, widget;
14850 for(id in this._hash){
14851 widget = this._hash[id];
14852 if(widget.declaredClass == cls){
14853 res.add(widget);
14854 }
14855 }
14856 return res; // dijit/WidgetSet
14857 },
14858
14859 toArray: function(){
14860 // summary:
14861 // Convert this WidgetSet into a true Array
14862 //
14863 // example:
14864 // Work with the widget .domNodes in a real Array
14865 // | require(["dijit/WidgetSet", "dijit/registry"],
14866 // | function(WidgetSet, registry){
14867 // | array.map(registry.toArray(), function(w){ return w.domNode; });
14868 // | });
14869
14870
14871 var ar = [];
14872 for(var id in this._hash){
14873 ar.push(this._hash[id]);
14874 }
14875 return ar; // dijit/_WidgetBase[]
14876 },
14877
14878 map: function(/* Function */func, /* Object? */thisObj){
14879 // summary:
14880 // Create a new Array from this WidgetSet, following the same rules as `array.map`
14881 // example:
14882 // | require(["dijit/WidgetSet", "dijit/registry"],
14883 // | function(WidgetSet, registry){
14884 // | var nodes = registry.map(function(w){ return w.domNode; });
14885 // | });
14886 //
14887 // returns:
14888 // A new array of the returned values.
14889 return array.map(this.toArray(), func, thisObj); // Array
14890 },
14891
14892 every: function(func, thisObj){
14893 // summary:
14894 // A synthetic clone of `array.every` acting explicitly on this WidgetSet
14895 //
14896 // func: Function
14897 // A callback function run for every widget in this list. Exits loop
14898 // when the first false return is encountered.
14899 //
14900 // thisObj: Object?
14901 // Optional scope parameter to use for the callback
14902
14903 thisObj = thisObj || kernel.global;
14904 var x = 0, i;
14905 for(i in this._hash){
14906 if(!func.call(thisObj, this._hash[i], x++, this._hash)){
14907 return false; // Boolean
14908 }
14909 }
14910 return true; // Boolean
14911 },
14912
14913 some: function(func, thisObj){
14914 // summary:
14915 // A synthetic clone of `array.some` acting explicitly on this WidgetSet
14916 //
14917 // func: Function
14918 // A callback function run for every widget in this list. Exits loop
14919 // when the first true return is encountered.
14920 //
14921 // thisObj: Object?
14922 // Optional scope parameter to use for the callback
14923
14924 thisObj = thisObj || kernel.global;
14925 var x = 0, i;
14926 for(i in this._hash){
14927 if(func.call(thisObj, this._hash[i], x++, this._hash)){
14928 return true; // Boolean
14929 }
14930 }
14931 return false; // Boolean
14932 }
14933
14934 });
14935
14936 // Add in 1.x compatibility methods to dijit/registry.
14937 // These functions won't show up in the API doc but since they are deprecated anyway,
14938 // that's probably for the best.
14939 array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
14940 registry[func] = WidgetSet.prototype[func];
14941 });
14942
14943
14944 return WidgetSet;
14945 });
14946
14947 },
14948 'dojo/dnd/Moveable':function(){
14949 define("dojo/dnd/Moveable", [
14950 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang",
14951 "../dom", "../dom-class", "../Evented", "../on", "../topic", "../touch", "./common", "./Mover", "../_base/window"
14952 ], function(array, declare, event, lang, dom, domClass, Evented, on, topic, touch, dnd, Mover, win){
14953
14954 // module:
14955 // dojo/dnd/Moveable
14956
14957
14958 var Moveable = declare("dojo.dnd.Moveable", [Evented], {
14959 // summary:
14960 // an object, which makes a node movable
14961
14962 // object attributes (for markup)
14963 handle: "",
14964 delay: 0,
14965 skip: false,
14966
14967 constructor: function(node, params){
14968 // node: Node
14969 // a node (or node's id) to be moved
14970 // params: Moveable.__MoveableArgs?
14971 // optional parameters
14972 this.node = dom.byId(node);
14973 if(!params){ params = {}; }
14974 this.handle = params.handle ? dom.byId(params.handle) : null;
14975 if(!this.handle){ this.handle = this.node; }
14976 this.delay = params.delay > 0 ? params.delay : 0;
14977 this.skip = params.skip;
14978 this.mover = params.mover ? params.mover : Mover;
14979 this.events = [
14980 on(this.handle, touch.press, lang.hitch(this, "onMouseDown")),
14981 // cancel text selection and text dragging
14982 on(this.handle, "dragstart", lang.hitch(this, "onSelectStart")),
14983 on(this.handle, "selectstart", lang.hitch(this, "onSelectStart"))
14984 ];
14985 },
14986
14987 // markup methods
14988 markupFactory: function(params, node, Ctor){
14989 return new Ctor(node, params);
14990 },
14991
14992 // methods
14993 destroy: function(){
14994 // summary:
14995 // stops watching for possible move, deletes all references, so the object can be garbage-collected
14996 array.forEach(this.events, function(handle){ handle.remove(); });
14997 this.events = this.node = this.handle = null;
14998 },
14999
15000 // mouse event processors
15001 onMouseDown: function(e){
15002 // summary:
15003 // event processor for onmousedown/ontouchstart, creates a Mover for the node
15004 // e: Event
15005 // mouse/touch event
15006 if(this.skip && dnd.isFormElement(e)){ return; }
15007 if(this.delay){
15008 this.events.push(
15009 on(this.handle, touch.move, lang.hitch(this, "onMouseMove")),
15010 on(this.handle, touch.release, lang.hitch(this, "onMouseUp"))
15011 );
15012 this._lastX = e.pageX;
15013 this._lastY = e.pageY;
15014 }else{
15015 this.onDragDetected(e);
15016 }
15017 event.stop(e);
15018 },
15019 onMouseMove: function(e){
15020 // summary:
15021 // event processor for onmousemove/ontouchmove, used only for delayed drags
15022 // e: Event
15023 // mouse/touch event
15024 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
15025 this.onMouseUp(e);
15026 this.onDragDetected(e);
15027 }
15028 event.stop(e);
15029 },
15030 onMouseUp: function(e){
15031 // summary:
15032 // event processor for onmouseup, used only for delayed drags
15033 // e: Event
15034 // mouse event
15035 for(var i = 0; i < 2; ++i){
15036 this.events.pop().remove();
15037 }
15038 event.stop(e);
15039 },
15040 onSelectStart: function(e){
15041 // summary:
15042 // event processor for onselectevent and ondragevent
15043 // e: Event
15044 // mouse event
15045 if(!this.skip || !dnd.isFormElement(e)){
15046 event.stop(e);
15047 }
15048 },
15049
15050 // local events
15051 onDragDetected: function(/*Event*/ e){
15052 // summary:
15053 // called when the drag is detected;
15054 // responsible for creation of the mover
15055 new this.mover(this.node, e, this);
15056 },
15057 onMoveStart: function(/*Mover*/ mover){
15058 // summary:
15059 // called before every move operation
15060 topic.publish("/dnd/move/start", mover);
15061 domClass.add(win.body(), "dojoMove");
15062 domClass.add(this.node, "dojoMoveItem");
15063 },
15064 onMoveStop: function(/*Mover*/ mover){
15065 // summary:
15066 // called after every move operation
15067 topic.publish("/dnd/move/stop", mover);
15068 domClass.remove(win.body(), "dojoMove");
15069 domClass.remove(this.node, "dojoMoveItem");
15070 },
15071 onFirstMove: function(/*===== mover, e =====*/){
15072 // summary:
15073 // called during the very first move notification;
15074 // can be used to initialize coordinates, can be overwritten.
15075 // mover: Mover
15076 // e: Event
15077
15078 // default implementation does nothing
15079 },
15080 onMove: function(mover, leftTop /*=====, e =====*/){
15081 // summary:
15082 // called during every move notification;
15083 // should actually move the node; can be overwritten.
15084 // mover: Mover
15085 // leftTop: Object
15086 // e: Event
15087 this.onMoving(mover, leftTop);
15088 var s = mover.node.style;
15089 s.left = leftTop.l + "px";
15090 s.top = leftTop.t + "px";
15091 this.onMoved(mover, leftTop);
15092 },
15093 onMoving: function(/*===== mover, leftTop =====*/){
15094 // summary:
15095 // called before every incremental move; can be overwritten.
15096 // mover: Mover
15097 // leftTop: Object
15098
15099 // default implementation does nothing
15100 },
15101 onMoved: function(/*===== mover, leftTop =====*/){
15102 // summary:
15103 // called after every incremental move; can be overwritten.
15104 // mover: Mover
15105 // leftTop: Object
15106
15107 // default implementation does nothing
15108 }
15109 });
15110
15111 /*=====
15112 Moveable.__MoveableArgs = declare([], {
15113 // handle: Node||String
15114 // A node (or node's id), which is used as a mouse handle.
15115 // If omitted, the node itself is used as a handle.
15116 handle: null,
15117
15118 // delay: Number
15119 // delay move by this number of pixels
15120 delay: 0,
15121
15122 // skip: Boolean
15123 // skip move of form elements
15124 skip: false,
15125
15126 // mover: Object
15127 // a constructor of custom Mover
15128 mover: dnd.Mover
15129 });
15130 =====*/
15131
15132 return Moveable;
15133 });
15134
15135 },
15136 'dijit/TooltipDialog':function(){
15137 require({cache:{
15138 'url:dijit/templates/TooltipDialog.html':"<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" data-dojo-attach-point=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n"}});
15139 define("dijit/TooltipDialog", [
15140 "dojo/_base/declare", // declare
15141 "dojo/dom-class", // domClass.replace
15142 "dojo/_base/event", // event.stop
15143 "dojo/keys", // keys
15144 "dojo/_base/lang", // lang.hitch
15145 "./focus",
15146 "./layout/ContentPane",
15147 "./_DialogMixin",
15148 "./form/_FormMixin",
15149 "./_TemplatedMixin",
15150 "dojo/text!./templates/TooltipDialog.html",
15151 "./main" // exports methods to dijit global
15152 ], function(declare, domClass, event, keys, lang,
15153 focus, ContentPane, _DialogMixin, _FormMixin, _TemplatedMixin, template, dijit){
15154
15155 // module:
15156 // dijit/TooltipDialog
15157
15158
15159 return declare("dijit.TooltipDialog",
15160 [ContentPane, _TemplatedMixin, _FormMixin, _DialogMixin], {
15161 // summary:
15162 // Pops up a dialog that appears like a Tooltip
15163
15164 // title: String
15165 // Description of tooltip dialog (required for a11y)
15166 title: "",
15167
15168 // doLayout: [protected] Boolean
15169 // Don't change this parameter from the default value.
15170 // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
15171 // is never a child of a layout container, nor can you specify the size of
15172 // TooltipDialog in order to control the size of an inner widget.
15173 doLayout: false,
15174
15175 // autofocus: Boolean
15176 // A Toggle to modify the default focus behavior of a Dialog, which
15177 // is to focus on the first dialog element after opening the dialog.
15178 // False will disable autofocusing. Default: true.
15179 autofocus: true,
15180
15181 // baseClass: [protected] String
15182 // The root className to use for the various states of this widget
15183 baseClass: "dijitTooltipDialog",
15184
15185 // _firstFocusItem: [private readonly] DomNode
15186 // The pointer to the first focusable node in the dialog.
15187 // Set by `dijit/_DialogMixin._getFocusItems()`.
15188 _firstFocusItem: null,
15189
15190 // _lastFocusItem: [private readonly] DomNode
15191 // The pointer to which node has focus prior to our dialog.
15192 // Set by `dijit/_DialogMixin._getFocusItems()`.
15193 _lastFocusItem: null,
15194
15195 templateString: template,
15196
15197 _setTitleAttr: function(/*String*/ title){
15198 this.containerNode.title = title;
15199 this._set("title", title);
15200 },
15201
15202 postCreate: function(){
15203 this.inherited(arguments);
15204 this.connect(this.containerNode, "onkeypress", "_onKey");
15205 },
15206
15207 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner){
15208 // summary:
15209 // Configure widget to be displayed in given position relative to the button.
15210 // This is called from the dijit.popup code, and should not be called
15211 // directly.
15212 // tags:
15213 // protected
15214
15215 // Note: intentionally not using dijitTooltip class since that sets position:absolute, which
15216 // confuses dijit/popup trying to get the size of the tooltip.
15217 var newC = {
15218 "MR-ML": "dijitTooltipRight",
15219 "ML-MR": "dijitTooltipLeft",
15220 "TM-BM": "dijitTooltipAbove",
15221 "BM-TM": "dijitTooltipBelow",
15222 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
15223 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
15224 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
15225 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
15226 "BR-BL": "dijitTooltipRight",
15227 "BL-BR": "dijitTooltipLeft"
15228 }[aroundCorner + "-" + tooltipCorner];
15229
15230 domClass.replace(this.domNode, newC, this._currentOrientClass || "");
15231 this._currentOrientClass = newC;
15232
15233 // Tooltip.orient() has code to reposition connector for when Tooltip is before/after anchor.
15234 // Not putting here to avoid code bloat, and since TooltipDialogs are generally above/below.
15235 // Should combine code from Tooltip and TooltipDialog.
15236 },
15237
15238 focus: function(){
15239 // summary:
15240 // Focus on first field
15241 this._getFocusItems(this.containerNode);
15242 focus.focus(this._firstFocusItem);
15243 },
15244
15245 onOpen: function(/*Object*/ pos){
15246 // summary:
15247 // Called when dialog is displayed.
15248 // This is called from the dijit.popup code, and should not be called directly.
15249 // tags:
15250 // protected
15251
15252 this.orient(this.domNode,pos.aroundCorner, pos.corner);
15253
15254 // Position the tooltip connector for middle alignment.
15255 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
15256 var aroundNodeCoords = pos.aroundNodePos;
15257 if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
15258 this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
15259 this.connectorNode.style.left = "";
15260 }else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
15261 this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
15262 }
15263
15264 this._onShow(); // lazy load trigger (TODO: shouldn't we load before positioning?)
15265 },
15266
15267 onClose: function(){
15268 // summary:
15269 // Called when dialog is hidden.
15270 // This is called from the dijit.popup code, and should not be called directly.
15271 // tags:
15272 // protected
15273 this.onHide();
15274 },
15275
15276 _onKey: function(/*Event*/ evt){
15277 // summary:
15278 // Handler for keyboard events
15279 // description:
15280 // Keep keyboard focus in dialog; close dialog on escape key
15281 // tags:
15282 // private
15283
15284 var node = evt.target;
15285 if(evt.charOrCode === keys.TAB){
15286 this._getFocusItems(this.containerNode);
15287 }
15288 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
15289 if(evt.charOrCode == keys.ESCAPE){
15290 // Use defer to avoid crash on IE, see #10396.
15291 this.defer("onCancel");
15292 event.stop(evt);
15293 }else if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
15294 if(!singleFocusItem){
15295 focus.focus(this._lastFocusItem); // send focus to last item in dialog
15296 }
15297 event.stop(evt);
15298 }else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
15299 if(!singleFocusItem){
15300 focus.focus(this._firstFocusItem); // send focus to first item in dialog
15301 }
15302 event.stop(evt);
15303 }else if(evt.charOrCode === keys.TAB){
15304 // we want the browser's default tab handling to move focus
15305 // but we don't want the tab to propagate upwards
15306 evt.stopPropagation();
15307 }
15308 }
15309 });
15310 });
15311
15312 },
15313 'dojo/store/util/SimpleQueryEngine':function(){
15314 define("dojo/store/util/SimpleQueryEngine", ["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil /*=====, Store =====*/){
15315
15316 // module:
15317 // dojo/store/util/SimpleQueryEngine
15318
15319 return function(query, options){
15320 // summary:
15321 // Simple query engine that matches using filter functions, named filter
15322 // functions or objects by name-value on a query object hash
15323 //
15324 // description:
15325 // The SimpleQueryEngine provides a way of getting a QueryResults through
15326 // the use of a simple object hash as a filter. The hash will be used to
15327 // match properties on data objects with the corresponding value given. In
15328 // other words, only exact matches will be returned.
15329 //
15330 // This function can be used as a template for more complex query engines;
15331 // for example, an engine can be created that accepts an object hash that
15332 // contains filtering functions, or a string that gets evaluated, etc.
15333 //
15334 // When creating a new dojo.store, simply set the store's queryEngine
15335 // field as a reference to this function.
15336 //
15337 // query: Object
15338 // An object hash with fields that may match fields of items in the store.
15339 // Values in the hash will be compared by normal == operator, but regular expressions
15340 // or any object that provides a test() method are also supported and can be
15341 // used to match strings by more complex expressions
15342 // (and then the regex's or object's test() method will be used to match values).
15343 //
15344 // options: dojo/store/api/Store.QueryOptions?
15345 // An object that contains optional information such as sort, start, and count.
15346 //
15347 // returns: Function
15348 // A function that caches the passed query under the field "matches". See any
15349 // of the "query" methods on dojo.stores.
15350 //
15351 // example:
15352 // Define a store with a reference to this engine, and set up a query method.
15353 //
15354 // | var myStore = function(options){
15355 // | // ...more properties here
15356 // | this.queryEngine = SimpleQueryEngine;
15357 // | // define our query method
15358 // | this.query = function(query, options){
15359 // | return QueryResults(this.queryEngine(query, options)(this.data));
15360 // | };
15361 // | };
15362
15363 // create our matching query function
15364 switch(typeof query){
15365 default:
15366 throw new Error("Can not query with a " + typeof query);
15367 case "object": case "undefined":
15368 var queryObject = query;
15369 query = function(object){
15370 for(var key in queryObject){
15371 var required = queryObject[key];
15372 if(required && required.test){
15373 // an object can provide a test method, which makes it work with regex
15374 if(!required.test(object[key], object)){
15375 return false;
15376 }
15377 }else if(required != object[key]){
15378 return false;
15379 }
15380 }
15381 return true;
15382 };
15383 break;
15384 case "string":
15385 // named query
15386 if(!this[query]){
15387 throw new Error("No filter function " + query + " was found in store");
15388 }
15389 query = this[query];
15390 // fall through
15391 case "function":
15392 // fall through
15393 }
15394 function execute(array){
15395 // execute the whole query, first we filter
15396 var results = arrayUtil.filter(array, query);
15397 // next we sort
15398 var sortSet = options && options.sort;
15399 if(sortSet){
15400 results.sort(typeof sortSet == "function" ? sortSet : function(a, b){
15401 for(var sort, i=0; sort = sortSet[i]; i++){
15402 var aValue = a[sort.attribute];
15403 var bValue = b[sort.attribute];
15404 if (aValue != bValue){
15405 return !!sort.descending == (aValue == null || aValue > bValue) ? -1 : 1;
15406 }
15407 }
15408 return 0;
15409 });
15410 }
15411 // now we paginate
15412 if(options && (options.start || options.count)){
15413 var total = results.length;
15414 results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
15415 results.total = total;
15416 }
15417 return results;
15418 }
15419 execute.matches = query;
15420 return execute;
15421 };
15422
15423 });
15424
15425 },
15426 'dijit/typematic':function(){
15427 define("dijit/typematic", [
15428 "dojo/_base/array", // array.forEach
15429 "dojo/_base/connect", // connect.connect
15430 "dojo/_base/event", // event.stop
15431 "dojo/_base/kernel", // kernel.deprecated
15432 "dojo/_base/lang", // lang.mixin, lang.hitch
15433 "dojo/on",
15434 "dojo/sniff", // has("ie")
15435 "./main" // setting dijit.typematic global
15436 ], function(array, connect, event, kernel, lang, on, has, dijit){
15437
15438 // module:
15439 // dijit/typematic
15440
15441 var typematic = (dijit.typematic = {
15442 // summary:
15443 // These functions are used to repetitively call a user specified callback
15444 // method when a specific key or mouse click over a specific DOM node is
15445 // held down for a specific amount of time.
15446 // Only 1 such event is allowed to occur on the browser page at 1 time.
15447
15448 _fireEventAndReload: function(){
15449 this._timer = null;
15450 this._callback(++this._count, this._node, this._evt);
15451
15452 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
15453 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
15454 this._currentTimeout = Math.max(
15455 this._currentTimeout < 0 ? this._initialDelay :
15456 (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
15457 this._minDelay);
15458 this._timer = setTimeout(lang.hitch(this, "_fireEventAndReload"), this._currentTimeout);
15459 },
15460
15461 trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number?*/ subsequentDelay, /*Number?*/ initialDelay, /*Number?*/ minDelay){
15462 // summary:
15463 // Start a timed, repeating callback sequence.
15464 // If already started, the function call is ignored.
15465 // This method is not normally called by the user but can be
15466 // when the normal listener code is insufficient.
15467 // evt:
15468 // key or mouse event object to pass to the user callback
15469 // _this:
15470 // pointer to the user's widget space.
15471 // node:
15472 // the DOM node object to pass the the callback function
15473 // callback:
15474 // function to call until the sequence is stopped called with 3 parameters:
15475 // count:
15476 // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
15477 // node:
15478 // the DOM node object passed in
15479 // evt:
15480 // key or mouse event object
15481 // obj:
15482 // user space object used to uniquely identify each typematic sequence
15483 // subsequentDelay:
15484 // if > 1, the number of milliseconds until the 3->n events occur
15485 // or else the fractional time multiplier for the next event's delay, default=0.9
15486 // initialDelay:
15487 // the number of milliseconds until the 2nd event occurs, default=500ms
15488 // minDelay:
15489 // the maximum delay in milliseconds for event to fire, default=10ms
15490 if(obj != this._obj){
15491 this.stop();
15492 this._initialDelay = initialDelay || 500;
15493 this._subsequentDelay = subsequentDelay || 0.90;
15494 this._minDelay = minDelay || 10;
15495 this._obj = obj;
15496 this._node = node;
15497 this._currentTimeout = -1;
15498 this._count = -1;
15499 this._callback = lang.hitch(_this, callback);
15500 this._evt = { faux: true };
15501 for(var attr in evt){
15502 if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
15503 var v = evt[attr];
15504 if(typeof v != "function" && typeof v != "undefined"){ this._evt[attr] = v }
15505 }
15506 }
15507 this._fireEventAndReload();
15508 }
15509 },
15510
15511 stop: function(){
15512 // summary:
15513 // Stop an ongoing timed, repeating callback sequence.
15514 if(this._timer){
15515 clearTimeout(this._timer);
15516 this._timer = null;
15517 }
15518 if(this._obj){
15519 this._callback(-1, this._node, this._evt);
15520 this._obj = null;
15521 }
15522 },
15523
15524 addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15525 // summary:
15526 // Start listening for a specific typematic key.
15527 // See also the trigger method for other parameters.
15528 // keyObject:
15529 // an object defining the key to listen for:
15530 //
15531 // - charOrCode: the printable character (string) or keyCode (number) to listen for.
15532 // - keyCode: (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
15533 // - charCode: (deprecated - use charOrCode) the charCode (number) to listen for.
15534 // - ctrlKey: desired ctrl key state to initiate the callback sequence:
15535 // - pressed (true)
15536 // - released (false)
15537 // - either (unspecified)
15538 // - altKey: same as ctrlKey but for the alt key
15539 // - shiftKey: same as ctrlKey but for the shift key
15540 // returns:
15541 // a connection handle
15542
15543 if(keyObject.keyCode){
15544 keyObject.charOrCode = keyObject.keyCode;
15545 kernel.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15546 }else if(keyObject.charCode){
15547 keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
15548 kernel.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15549 }
15550 var handles = [
15551 on(node, connect._keypress, lang.hitch(this, function(evt){
15552 if(evt.charOrCode == keyObject.charOrCode &&
15553 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
15554 (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
15555 (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
15556 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
15557 event.stop(evt);
15558 typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
15559 }else if(typematic._obj == keyObject){
15560 typematic.stop();
15561 }
15562 })),
15563 on(node, "keyup", lang.hitch(this, function(){
15564 if(typematic._obj == keyObject){
15565 typematic.stop();
15566 }
15567 }))
15568 ];
15569 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15570 },
15571
15572 addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15573 // summary:
15574 // Start listening for a typematic mouse click.
15575 // See the trigger method for other parameters.
15576 // returns:
15577 // a connection handle
15578 var handles = [
15579 on(node, "mousedown", lang.hitch(this, function(evt){
15580 evt.preventDefault();
15581 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
15582 })),
15583 on(node, "mouseup", lang.hitch(this, function(evt){
15584 if(this._obj){
15585 evt.preventDefault();
15586 }
15587 typematic.stop();
15588 })),
15589 on(node, "mouseout", lang.hitch(this, function(evt){
15590 if(this._obj){
15591 evt.preventDefault();
15592 }
15593 typematic.stop();
15594 })),
15595 on(node, "dblclick", lang.hitch(this, function(evt){
15596 evt.preventDefault();
15597 if(has("ie") < 9){
15598 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
15599 setTimeout(lang.hitch(this, typematic.stop), 50);
15600 }
15601 }))
15602 ];
15603 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15604 },
15605
15606 addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15607 // summary:
15608 // Start listening for a specific typematic key and mouseclick.
15609 // This is a thin wrapper to addKeyListener and addMouseListener.
15610 // See the addMouseListener and addKeyListener methods for other parameters.
15611 // mouseNode:
15612 // the DOM node object to listen on for mouse events.
15613 // keyNode:
15614 // the DOM node object to listen on for key events.
15615 // returns:
15616 // a connection handle
15617 var handles = [
15618 this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay),
15619 this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)
15620 ];
15621 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15622 }
15623 });
15624
15625 return typematic;
15626
15627 });
15628
15629 },
15630 'dijit/MenuItem':function(){
15631 require({cache:{
15632 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"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"}});
15633 define("dijit/MenuItem", [
15634 "dojo/_base/declare", // declare
15635 "dojo/dom", // dom.setSelectable
15636 "dojo/dom-attr", // domAttr.set
15637 "dojo/dom-class", // domClass.toggle
15638 "dojo/_base/kernel", // kernel.deprecated
15639 "dojo/sniff", // has("ie")
15640 "./_Widget",
15641 "./_TemplatedMixin",
15642 "./_Contained",
15643 "./_CssStateMixin",
15644 "dojo/text!./templates/MenuItem.html"
15645 ], function(declare, dom, domAttr, domClass, kernel, has,
15646 _Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
15647
15648 // module:
15649 // dijit/MenuItem
15650
15651 return declare("dijit.MenuItem",
15652 [_Widget, _TemplatedMixin, _Contained, _CssStateMixin],
15653 {
15654 // summary:
15655 // A line item in a Menu Widget
15656
15657 // Make 3 columns
15658 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15659 templateString: template,
15660
15661 baseClass: "dijitMenuItem",
15662
15663 // label: String
15664 // Menu text
15665 label: "",
15666 _setLabelAttr: function(val){
15667 this.containerNode.innerHTML = val;
15668 this._set("label", val);
15669 if(this.textDir === "auto"){
15670 this.applyTextDir(this.focusNode, this.label);
15671 }
15672 },
15673
15674 // iconClass: String
15675 // Class to apply to DOMNode to make it display an icon.
15676 iconClass: "dijitNoIcon",
15677 _setIconClassAttr: { node: "iconNode", type: "class" },
15678
15679 // accelKey: String
15680 // Text for the accelerator (shortcut) key combination.
15681 // Note that although Menu can display accelerator keys there
15682 // is no infrastructure to actually catch and execute these
15683 // accelerators.
15684 accelKey: "",
15685
15686 // disabled: Boolean
15687 // If true, the menu item is disabled.
15688 // If false, the menu item is enabled.
15689 disabled: false,
15690
15691 _fillContent: function(/*DomNode*/ source){
15692 // If button label is specified as srcNodeRef.innerHTML rather than
15693 // this.params.label, handle it here.
15694 if(source && !("label" in this.params)){
15695 this.set('label', source.innerHTML);
15696 }
15697 },
15698
15699 buildRendering: function(){
15700 this.inherited(arguments);
15701 var label = this.id+"_text";
15702 domAttr.set(this.containerNode, "id", label);
15703 if(this.accelKeyNode){
15704 domAttr.set(this.accelKeyNode, "id", this.id + "_accel");
15705 label += " " + this.id + "_accel";
15706 }
15707 this.domNode.setAttribute("aria-labelledby", label);
15708 dom.setSelectable(this.domNode, false);
15709 },
15710
15711 onClick: function(/*Event*/){
15712 // summary:
15713 // User defined function to handle clicks
15714 // tags:
15715 // callback
15716 },
15717
15718 focus: function(){
15719 // summary:
15720 // Focus on this MenuItem
15721 try{
15722 if(has("ie") == 8){
15723 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15724 this.containerNode.focus();
15725 }
15726 this.focusNode.focus();
15727 }catch(e){
15728 // this throws on IE (at least) in some scenarios
15729 }
15730 },
15731
15732 _onFocus: function(){
15733 // summary:
15734 // This is called by the focus manager when focus
15735 // goes to this MenuItem or a child menu.
15736 // tags:
15737 // protected
15738 this._setSelected(true);
15739 this.getParent()._onItemFocus(this);
15740
15741 this.inherited(arguments);
15742 },
15743
15744 _setSelected: function(selected){
15745 // summary:
15746 // Indicate that this node is the currently selected one
15747 // tags:
15748 // private
15749
15750 /***
15751 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15752 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15753 * That's not supposed to happen, but the problem is:
15754 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15755 * points to the parent Menu, bypassing the parent MenuItem... thus the
15756 * MenuItem is not in the chain of active widgets and gets a premature call to
15757 * _onBlur()
15758 */
15759
15760 domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
15761 },
15762
15763 setLabel: function(/*String*/ content){
15764 // summary:
15765 // Deprecated. Use set('label', ...) instead.
15766 // tags:
15767 // deprecated
15768 kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
15769 this.set("label", content);
15770 },
15771
15772 setDisabled: function(/*Boolean*/ disabled){
15773 // summary:
15774 // Deprecated. Use set('disabled', bool) instead.
15775 // tags:
15776 // deprecated
15777 kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
15778 this.set('disabled', disabled);
15779 },
15780 _setDisabledAttr: function(/*Boolean*/ value){
15781 // summary:
15782 // Hook for attr('disabled', ...) to work.
15783 // Enable or disable this menu item.
15784
15785 this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
15786 this._set("disabled", value);
15787 },
15788 _setAccelKeyAttr: function(/*String*/ value){
15789 // summary:
15790 // Hook for attr('accelKey', ...) to work.
15791 // Set accelKey on this menu item.
15792
15793 this.accelKeyNode.style.display=value?"":"none";
15794 this.accelKeyNode.innerHTML=value;
15795 //have to use colSpan to make it work in IE
15796 domAttr.set(this.containerNode,'colSpan',value?"1":"2");
15797
15798 this._set("accelKey", value);
15799 },
15800 _setTextDirAttr: function(/*String*/ textDir){
15801 // summary:
15802 // Setter for textDir.
15803 // description:
15804 // Users shouldn't call this function; they should be calling
15805 // set('textDir', value)
15806 // tags:
15807 // private
15808
15809 // only if new textDir is different from the old one
15810 // and on widgets creation.
15811 if(!this._created || this.textDir != textDir){
15812 this._set("textDir", textDir);
15813 this.applyTextDir(this.focusNode, this.label);
15814 }
15815 }
15816 });
15817 });
15818
15819 },
15820 'dijit/layout/TabController':function(){
15821 require({cache:{
15822 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n"}});
15823 define("dijit/layout/TabController", [
15824 "dojo/_base/declare", // declare
15825 "dojo/dom", // dom.setSelectable
15826 "dojo/dom-attr", // domAttr.attr
15827 "dojo/dom-class", // domClass.toggle
15828 "dojo/i18n", // i18n.getLocalization
15829 "dojo/_base/lang", // lang.hitch lang.trim
15830 "./StackController",
15831 "../registry",
15832 "../Menu",
15833 "../MenuItem",
15834 "dojo/text!./templates/_TabButton.html",
15835 "dojo/i18n!../nls/common"
15836 ], function(declare, dom, domAttr, domClass, i18n, lang, StackController, registry, Menu, MenuItem, template){
15837
15838 // module:
15839 // dijit/layout/TabController
15840
15841 var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, {
15842 // summary:
15843 // A tab (the thing you click to select a pane).
15844 // description:
15845 // Contains the title of the pane, and optionally a close-button to destroy the pane.
15846 // This is an internal widget and should not be instantiated directly.
15847 // tags:
15848 // private
15849
15850 // baseClass: String
15851 // The CSS class applied to the domNode.
15852 baseClass: "dijitTab",
15853
15854 // Apply dijitTabCloseButtonHover when close button is hovered
15855 cssStateNodes: {
15856 closeNode: "dijitTabCloseButton"
15857 },
15858
15859 templateString: template,
15860
15861 // Override _FormWidget.scrollOnFocus.
15862 // Don't scroll the whole tab container into view when the button is focused.
15863 scrollOnFocus: false,
15864
15865 buildRendering: function(){
15866 this.inherited(arguments);
15867
15868 dom.setSelectable(this.containerNode, false);
15869 },
15870
15871 startup: function(){
15872 this.inherited(arguments);
15873 var n = this.domNode;
15874
15875 // Required to give IE6 a kick, as it initially hides the
15876 // tabs until they are focused on.
15877 this.defer(function(){
15878 n.className = n.className;
15879 }, 1);
15880 },
15881
15882 _setCloseButtonAttr: function(/*Boolean*/ disp){
15883 // summary:
15884 // Hide/show close button
15885 this._set("closeButton", disp);
15886 domClass.toggle(this.domNode, "dijitClosable", disp);
15887 this.closeNode.style.display = disp ? "" : "none";
15888 if(disp){
15889 var _nlsResources = i18n.getLocalization("dijit", "common");
15890 if(this.closeNode){
15891 domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
15892 }
15893 }
15894 },
15895
15896 _setDisabledAttr: function(/*Boolean*/ disabled){
15897 // summary:
15898 // Make tab selected/unselectable
15899
15900 this.inherited(arguments);
15901
15902 // Don't show tooltip for close button when tab is disabled
15903 if(this.closeNode){
15904 if(disabled){
15905 domAttr.remove(this.closeNode, "title");
15906 }else{
15907 var _nlsResources = i18n.getLocalization("dijit", "common");
15908 domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
15909 }
15910 }
15911 },
15912
15913 _setLabelAttr: function(/*String*/ content){
15914 // summary:
15915 // Hook for set('label', ...) to work.
15916 // description:
15917 // takes an HTML string.
15918 // Inherited ToggleButton implementation will Set the label (text) of the button;
15919 // Need to set the alt attribute of icon on tab buttons if no label displayed
15920 this.inherited(arguments);
15921 if(!this.showLabel && !this.params.title){
15922 this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
15923 }
15924 }
15925 });
15926
15927 var TabController = declare("dijit.layout.TabController", StackController, {
15928 // summary:
15929 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
15930 // Used internally by `dijit/layout/TabContainer`.
15931 // description:
15932 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
15933 // TabController also monitors the TabContainer, and whenever a pane is
15934 // added or deleted updates itself accordingly.
15935 // tags:
15936 // private
15937
15938 baseClass: "dijitTabController",
15939
15940 templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
15941
15942 // tabPosition: String
15943 // Defines where tabs go relative to the content.
15944 // "top", "bottom", "left-h", "right-h"
15945 tabPosition: "top",
15946
15947 // buttonWidget: Constructor
15948 // The tab widget to create to correspond to each page
15949 buttonWidget: TabButton,
15950
15951 // buttonWidgetCloseClass: String
15952 // Class of [x] close icon, used by event delegation code to tell when close button was clicked
15953 buttonWidgetCloseClass: "dijitTabCloseButton",
15954
15955 postCreate: function(){
15956 this.inherited(arguments);
15957
15958 // Setup a close menu to be shared between all the closable tabs (excluding disabled tabs)
15959 var closeMenu = new Menu({
15960 id: this.id+"_Menu",
15961 ownerDocument: this.ownerDocument,
15962 dir: this.dir,
15963 lang: this.lang,
15964 textDir: this.textDir,
15965 targetNodeIds: [this.domNode],
15966 selector: function(node){
15967 return domClass.contains(node, "dijitClosable") && !domClass.contains(node, "dijitTabDisabled");
15968 }
15969 });
15970 this.own(closeMenu);
15971
15972 var _nlsResources = i18n.getLocalization("dijit", "common"),
15973 controller = this;
15974 closeMenu.addChild(new MenuItem({
15975 label: _nlsResources.itemClose,
15976 ownerDocument: this.ownerDocument,
15977 dir: this.dir,
15978 lang: this.lang,
15979 textDir: this.textDir,
15980 onClick: function(evt){
15981 var button = registry.byNode(this.getParent().currentTarget);
15982 controller.onCloseButtonClick(button.page);
15983 }
15984 }));
15985 }
15986 });
15987
15988 TabController.TabButton = TabButton; // for monkey patching
15989
15990 return TabController;
15991 });
15992
15993 },
15994 'dijit/ToolbarSeparator':function(){
15995 define("dijit/ToolbarSeparator", [
15996 "dojo/_base/declare", // declare
15997 "dojo/dom", // dom.setSelectable
15998 "./_Widget",
15999 "./_TemplatedMixin"
16000 ], function(declare, dom, _Widget, _TemplatedMixin){
16001
16002 // module:
16003 // dijit/ToolbarSeparator
16004
16005
16006 return declare("dijit.ToolbarSeparator", [_Widget, _TemplatedMixin], {
16007 // summary:
16008 // A spacer between two `dijit.Toolbar` items
16009
16010 templateString: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
16011
16012 buildRendering: function(){
16013 this.inherited(arguments);
16014 dom.setSelectable(this.domNode, false);
16015 },
16016
16017 isFocusable: function(){
16018 // summary:
16019 // This widget isn't focusable, so pass along that fact.
16020 // tags:
16021 // protected
16022 return false;
16023 }
16024 });
16025 });
16026
16027 },
16028 'dijit/layout/_LayoutWidget':function(){
16029 define("dijit/layout/_LayoutWidget", [
16030 "dojo/_base/lang", // lang.mixin
16031 "../_Widget",
16032 "../_Container",
16033 "../_Contained",
16034 "../Viewport",
16035 "dojo/_base/declare", // declare
16036 "dojo/dom-class", // domClass.add domClass.remove
16037 "dojo/dom-geometry", // domGeometry.marginBox
16038 "dojo/dom-style" // domStyle.getComputedStyle
16039 ], function(lang, _Widget, _Container, _Contained, Viewport,
16040 declare, domClass, domGeometry, domStyle){
16041
16042 // module:
16043 // dijit/layout/_LayoutWidget
16044
16045
16046 return declare("dijit.layout._LayoutWidget", [_Widget, _Container, _Contained], {
16047 // summary:
16048 // Base class for a _Container widget which is responsible for laying out its children.
16049 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
16050
16051 // baseClass: [protected extension] String
16052 // This class name is applied to the widget's domNode
16053 // and also may be used to generate names for sub nodes,
16054 // for example dijitTabContainer-content.
16055 baseClass: "dijitLayoutContainer",
16056
16057 // isLayoutContainer: [protected] Boolean
16058 // Indicates that this widget is going to call resize() on its
16059 // children widgets, setting their size, when they become visible.
16060 isLayoutContainer: true,
16061
16062 buildRendering: function(){
16063 this.inherited(arguments);
16064 domClass.add(this.domNode, "dijitContainer");
16065 },
16066
16067 startup: function(){
16068 // summary:
16069 // Called after all the widgets have been instantiated and their
16070 // dom nodes have been inserted somewhere under win.doc.body.
16071 //
16072 // Widgets should override this method to do any initialization
16073 // dependent on other widgets existing, and then call
16074 // this superclass method to finish things off.
16075 //
16076 // startup() in subclasses shouldn't do anything
16077 // size related because the size of the widget hasn't been set yet.
16078
16079 if(this._started){ return; }
16080
16081 // Need to call inherited first - so that child widgets get started
16082 // up correctly
16083 this.inherited(arguments);
16084
16085 // If I am a not being controlled by a parent layout widget...
16086 var parent = this.getParent && this.getParent();
16087 if(!(parent && parent.isLayoutContainer)){
16088 // Do recursive sizing and layout of all my descendants
16089 // (passing in no argument to resize means that it has to glean the size itself)
16090 this.resize();
16091
16092 // Since my parent isn't a layout container, and my style *may be* width=height=100%
16093 // or something similar (either set directly or via a CSS class),
16094 // monitor when viewport size changes so that I can re-layout.
16095 this.own(Viewport.on("resize", lang.hitch(this, "resize")));
16096 }
16097 },
16098
16099 resize: function(changeSize, resultSize){
16100 // summary:
16101 // Call this to resize a widget, or after its size has changed.
16102 // description:
16103 // ####Change size mode:
16104 //
16105 // When changeSize is specified, changes the marginBox of this widget
16106 // and forces it to re-layout its contents accordingly.
16107 // changeSize may specify height, width, or both.
16108 //
16109 // If resultSize is specified it indicates the size the widget will
16110 // become after changeSize has been applied.
16111 //
16112 // ####Notification mode:
16113 //
16114 // When changeSize is null, indicates that the caller has already changed
16115 // the size of the widget, or perhaps it changed because the browser
16116 // window was resized. Tells widget to re-layout its contents accordingly.
16117 //
16118 // If resultSize is also specified it indicates the size the widget has
16119 // become.
16120 //
16121 // In either mode, this method also:
16122 //
16123 // 1. Sets this._borderBox and this._contentBox to the new size of
16124 // the widget. Queries the current domNode size if necessary.
16125 // 2. Calls layout() to resize contents (and maybe adjust child widgets).
16126 // changeSize: Object?
16127 // Sets the widget to this margin-box size and position.
16128 // May include any/all of the following properties:
16129 // | {w: int, h: int, l: int, t: int}
16130 // resultSize: Object?
16131 // The margin-box size of this widget after applying changeSize (if
16132 // changeSize is specified). If caller knows this size and
16133 // passes it in, we don't need to query the browser to get the size.
16134 // | {w: int, h: int}
16135
16136 var node = this.domNode;
16137
16138 // set margin box size, unless it wasn't specified, in which case use current size
16139 if(changeSize){
16140 domGeometry.setMarginBox(node, changeSize);
16141 }
16142
16143 // If either height or width wasn't specified by the user, then query node for it.
16144 // But note that setting the margin box and then immediately querying dimensions may return
16145 // inaccurate results, so try not to depend on it.
16146 var mb = resultSize || {};
16147 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
16148 if( !("h" in mb) || !("w" in mb) ){
16149 mb = lang.mixin(domGeometry.getMarginBox(node), mb); // just use domGeometry.marginBox() to fill in missing values
16150 }
16151
16152 // Compute and save the size of my border box and content box
16153 // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
16154 var cs = domStyle.getComputedStyle(node);
16155 var me = domGeometry.getMarginExtents(node, cs);
16156 var be = domGeometry.getBorderExtents(node, cs);
16157 var bb = (this._borderBox = {
16158 w: mb.w - (me.w + be.w),
16159 h: mb.h - (me.h + be.h)
16160 });
16161 var pe = domGeometry.getPadExtents(node, cs);
16162 this._contentBox = {
16163 l: domStyle.toPixelValue(node, cs.paddingLeft),
16164 t: domStyle.toPixelValue(node, cs.paddingTop),
16165 w: bb.w - pe.w,
16166 h: bb.h - pe.h
16167 };
16168
16169 // Callback for widget to adjust size of its children
16170 this.layout();
16171 },
16172
16173 layout: function(){
16174 // summary:
16175 // Widgets override this method to size and position their contents/children.
16176 // When this is called this._contentBox is guaranteed to be set (see resize()).
16177 //
16178 // This is called after startup(), and also when the widget's size has been
16179 // changed.
16180 // tags:
16181 // protected extension
16182 },
16183
16184 _setupChild: function(/*dijit/_WidgetBase*/child){
16185 // summary:
16186 // Common setup for initial children and children which are added after startup
16187 // tags:
16188 // protected extension
16189
16190 var cls = this.baseClass + "-child "
16191 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
16192 domClass.add(child.domNode, cls);
16193 },
16194
16195 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
16196 // Overrides _Container.addChild() to call _setupChild()
16197 this.inherited(arguments);
16198 if(this._started){
16199 this._setupChild(child);
16200 }
16201 },
16202
16203 removeChild: function(/*dijit/_WidgetBase*/ child){
16204 // Overrides _Container.removeChild() to remove class added by _setupChild()
16205 var cls = this.baseClass + "-child"
16206 + (child.baseClass ?
16207 " " + this.baseClass + "-" + child.baseClass : "");
16208 domClass.remove(child.domNode, cls);
16209
16210 this.inherited(arguments);
16211 }
16212 });
16213 });
16214
16215 },
16216 'dijit/popup':function(){
16217 define("dijit/popup", [
16218 "dojo/_base/array", // array.forEach array.some
16219 "dojo/aspect",
16220 "dojo/_base/connect", // connect._keypress
16221 "dojo/_base/declare", // declare
16222 "dojo/dom", // dom.isDescendant
16223 "dojo/dom-attr", // domAttr.set
16224 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
16225 "dojo/dom-geometry", // domGeometry.isBodyLtr
16226 "dojo/dom-style", // domStyle.set
16227 "dojo/_base/event", // event.stop
16228 "dojo/keys",
16229 "dojo/_base/lang", // lang.hitch
16230 "dojo/on",
16231 "dojo/sniff", // has("ie") has("mozilla")
16232 "./place",
16233 "./BackgroundIframe",
16234 "./main" // dijit (defining dijit.popup to match API doc)
16235 ], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has,
16236 place, BackgroundIframe, dijit){
16237
16238 // module:
16239 // dijit/popup
16240
16241 /*=====
16242 var __OpenArgs = {
16243 // popup: Widget
16244 // widget to display
16245 // parent: Widget
16246 // the button etc. that is displaying this popup
16247 // around: DomNode
16248 // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
16249 // x: Integer
16250 // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
16251 // y: Integer
16252 // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
16253 // orient: Object|String
16254 // When the around parameter is specified, orient should be a list of positions to try, ex:
16255 // | [ "below", "above" ]
16256 // For backwards compatibility it can also be an (ordered) hash of tuples of the form
16257 // (around-node-corner, popup-node-corner), ex:
16258 // | { "BL": "TL", "TL": "BL" }
16259 // where BL means "bottom left" and "TL" means "top left", etc.
16260 //
16261 // dijit/popup.open() tries to position the popup according to each specified position, in order,
16262 // until the popup appears fully within the viewport.
16263 //
16264 // The default value is ["below", "above"]
16265 //
16266 // When an (x,y) position is specified rather than an around node, orient is either
16267 // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
16268 // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
16269 // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
16270 // and the top-right corner.
16271 // onCancel: Function
16272 // callback when user has canceled the popup by:
16273 //
16274 // 1. hitting ESC or
16275 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
16276 // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
16277 // onClose: Function
16278 // callback whenever this popup is closed
16279 // onExecute: Function
16280 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
16281 // padding: place.__Position
16282 // adding a buffer around the opening position. This is only useful when around is not set.
16283 };
16284 =====*/
16285
16286 function destroyWrapper(){
16287 // summary:
16288 // Function to destroy wrapper when popup widget is destroyed.
16289 // Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
16290 if(this._popupWrapper){
16291 domConstruct.destroy(this._popupWrapper);
16292 delete this._popupWrapper;
16293 }
16294 }
16295
16296 var PopupManager = declare(null, {
16297 // summary:
16298 // Used to show drop downs (ex: the select list of a ComboBox)
16299 // or popups (ex: right-click context menus).
16300
16301 // _stack: dijit/_WidgetBase[]
16302 // Stack of currently popped up widgets.
16303 // (someone opened _stack[0], and then it opened _stack[1], etc.)
16304 _stack: [],
16305
16306 // _beginZIndex: Number
16307 // Z-index of the first popup. (If first popup opens other
16308 // popups they get a higher z-index.)
16309 _beginZIndex: 1000,
16310
16311 _idGen: 1,
16312
16313 _createWrapper: function(/*Widget*/ widget){
16314 // summary:
16315 // Initialization for widgets that will be used as popups.
16316 // Puts widget inside a wrapper DIV (if not already in one),
16317 // and returns pointer to that wrapper DIV.
16318
16319 var wrapper = widget._popupWrapper,
16320 node = widget.domNode;
16321
16322 if(!wrapper){
16323 // Create wrapper <div> for when this widget [in the future] will be used as a popup.
16324 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
16325 // to go wonky, see tests/robot/Toolbar.html to reproduce
16326 wrapper = domConstruct.create("div", {
16327 "class":"dijitPopup",
16328 style:{ display: "none"},
16329 role: "presentation"
16330 }, widget.ownerDocumentBody);
16331 wrapper.appendChild(node);
16332
16333 var s = node.style;
16334 s.display = "";
16335 s.visibility = "";
16336 s.position = "";
16337 s.top = "0px";
16338
16339 widget._popupWrapper = wrapper;
16340 aspect.after(widget, "destroy", destroyWrapper, true);
16341 }
16342
16343 return wrapper;
16344 },
16345
16346 moveOffScreen: function(/*Widget*/ widget){
16347 // summary:
16348 // Moves the popup widget off-screen.
16349 // Do not use this method to hide popups when not in use, because
16350 // that will create an accessibility issue: the offscreen popup is
16351 // still in the tabbing order.
16352
16353 // Create wrapper if not already there
16354 var wrapper = this._createWrapper(widget);
16355
16356 domStyle.set(wrapper, {
16357 visibility: "hidden",
16358 top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
16359 display: ""
16360 });
16361 },
16362
16363 hide: function(/*Widget*/ widget){
16364 // summary:
16365 // Hide this popup widget (until it is ready to be shown).
16366 // Initialization for widgets that will be used as popups
16367 //
16368 // Also puts widget inside a wrapper DIV (if not already in one)
16369 //
16370 // If popup widget needs to layout it should
16371 // do so when it is made visible, and popup._onShow() is called.
16372
16373 // Create wrapper if not already there
16374 var wrapper = this._createWrapper(widget);
16375
16376 domStyle.set(wrapper, "display", "none");
16377 },
16378
16379 getTopPopup: function(){
16380 // summary:
16381 // Compute the closest ancestor popup that's *not* a child of another popup.
16382 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
16383 var stack = this._stack;
16384 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
16385 /* do nothing, just trying to get right value for pi */
16386 }
16387 return stack[pi];
16388 },
16389
16390 open: function(/*__OpenArgs*/ args){
16391 // summary:
16392 // Popup the widget at the specified position
16393 //
16394 // example:
16395 // opening at the mouse position
16396 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
16397 //
16398 // example:
16399 // opening the widget as a dropdown
16400 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
16401 //
16402 // Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
16403 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
16404
16405 var stack = this._stack,
16406 widget = args.popup,
16407 orient = args.orient || ["below", "below-alt", "above", "above-alt"],
16408 ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(widget.ownerDocument),
16409 around = args.around,
16410 id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
16411
16412 // If we are opening a new popup that isn't a child of a currently opened popup, then
16413 // close currently opened popup(s). This should happen automatically when the old popups
16414 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
16415 while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
16416 this.close(stack[stack.length-1].widget);
16417 }
16418
16419 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
16420 var wrapper = this._createWrapper(widget);
16421
16422
16423 domAttr.set(wrapper, {
16424 id: id,
16425 style: {
16426 zIndex: this._beginZIndex + stack.length
16427 },
16428 "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
16429 dijitPopupParent: args.parent ? args.parent.id : ""
16430 });
16431
16432 if(has("ie") || has("mozilla")){
16433 if(!widget.bgIframe){
16434 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
16435 widget.bgIframe = new BackgroundIframe(wrapper);
16436 }
16437 }
16438
16439 // position the wrapper node and make it visible
16440 var best = around ?
16441 place.around(wrapper, around, orient, ltr, widget.orient ? lang.hitch(widget, "orient") : null) :
16442 place.at(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
16443
16444 wrapper.style.display = "";
16445 wrapper.style.visibility = "visible";
16446 widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown
16447
16448 var handlers = [];
16449
16450 // provide default escape and tab key handling
16451 // (this will work for any widget, not just menu)
16452 handlers.push(on(wrapper, connect._keypress, lang.hitch(this, function(evt){
16453 if(evt.charOrCode == keys.ESCAPE && args.onCancel){
16454 event.stop(evt);
16455 args.onCancel();
16456 }else if(evt.charOrCode === keys.TAB){
16457 event.stop(evt);
16458 var topPopup = this.getTopPopup();
16459 if(topPopup && topPopup.onCancel){
16460 topPopup.onCancel();
16461 }
16462 }
16463 })));
16464
16465 // watch for cancel/execute events on the popup and notify the caller
16466 // (for a menu, "execute" means clicking an item)
16467 if(widget.onCancel && args.onCancel){
16468 handlers.push(widget.on("cancel", args.onCancel));
16469 }
16470
16471 handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
16472 var topPopup = this.getTopPopup();
16473 if(topPopup && topPopup.onExecute){
16474 topPopup.onExecute();
16475 }
16476 })));
16477
16478 stack.push({
16479 widget: widget,
16480 parent: args.parent,
16481 onExecute: args.onExecute,
16482 onCancel: args.onCancel,
16483 onClose: args.onClose,
16484 handlers: handlers
16485 });
16486
16487 if(widget.onOpen){
16488 // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
16489 widget.onOpen(best);
16490 }
16491
16492 return best;
16493 },
16494
16495 close: function(/*Widget?*/ popup){
16496 // summary:
16497 // Close specified popup and any popups that it parented.
16498 // If no popup is specified, closes all popups.
16499
16500 var stack = this._stack;
16501
16502 // Basically work backwards from the top of the stack closing popups
16503 // until we hit the specified popup, but IIRC there was some issue where closing
16504 // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
16505 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
16506 // so the while condition is constructed defensively.
16507 while((popup && array.some(stack, function(elem){return elem.widget == popup;})) ||
16508 (!popup && stack.length)){
16509 var top = stack.pop(),
16510 widget = top.widget,
16511 onClose = top.onClose;
16512
16513 if(widget.onClose){
16514 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
16515 widget.onClose();
16516 }
16517
16518 var h;
16519 while(h = top.handlers.pop()){ h.remove(); }
16520
16521 // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
16522 if(widget && widget.domNode){
16523 this.hide(widget);
16524 }
16525
16526 if(onClose){
16527 onClose();
16528 }
16529 }
16530 }
16531 });
16532
16533 return (dijit.popup = new PopupManager());
16534 });
16535
16536 },
16537 'dijit/_base/manager':function(){
16538 define("dijit/_base/manager", [
16539 "dojo/_base/array",
16540 "dojo/_base/config", // defaultDuration
16541 "dojo/_base/lang",
16542 "../registry",
16543 "../main" // for setting exports to dijit namespace
16544 ], function(array, config, lang, registry, dijit){
16545
16546 // module:
16547 // dijit/_base/manager
16548
16549 var exports = {
16550 // summary:
16551 // Deprecated. Shim to methods on registry, plus a few other declarations.
16552 // New code should access dijit/registry directly when possible.
16553 };
16554
16555 array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){
16556 exports[name] = registry[name];
16557 });
16558
16559 lang.mixin(exports, {
16560 // defaultDuration: Integer
16561 // The default fx.animation speed (in ms) to use for all Dijit
16562 // transitional fx.animations, unless otherwise specified
16563 // on a per-instance basis. Defaults to 200, overrided by
16564 // `djConfig.defaultDuration`
16565 defaultDuration: config["defaultDuration"] || 200
16566 });
16567
16568 lang.mixin(dijit, exports);
16569
16570 /*===== return exports; =====*/
16571 return dijit; // for back compat :-(
16572 });
16573
16574 },
16575 'dijit/layout/StackController':function(){
16576 define("dijit/layout/StackController", [
16577 "dojo/_base/array", // array.forEach array.indexOf array.map
16578 "dojo/_base/declare", // declare
16579 "dojo/dom-class",
16580 "dojo/_base/event", // event.stop
16581 "dojo/keys", // keys
16582 "dojo/_base/lang", // lang.getObject
16583 "dojo/on",
16584 "../focus", // focus.focus()
16585 "../registry", // registry.byId
16586 "../_Widget",
16587 "../_TemplatedMixin",
16588 "../_Container",
16589 "../form/ToggleButton",
16590 "dojo/i18n!../nls/common"
16591 ], function(array, declare, domClass, event, keys, lang, on,
16592 focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
16593
16594 // module:
16595 // dijit/layout/StackController
16596
16597 var StackButton = declare("dijit.layout._StackButton", ToggleButton, {
16598 // summary:
16599 // Internal widget used by StackContainer.
16600 // description:
16601 // The button-like or tab-like object you click to select or delete a page
16602 // tags:
16603 // private
16604
16605 // Override _FormWidget.tabIndex.
16606 // StackContainer buttons are not in the tab order by default.
16607 // Probably we should be calling this.startupKeyNavChildren() instead.
16608 tabIndex: "-1",
16609
16610 // closeButton: Boolean
16611 // When true, display close button for this tab
16612 closeButton: false,
16613
16614 _aria_attr: "aria-selected",
16615
16616 buildRendering: function(/*Event*/ evt){
16617 this.inherited(arguments);
16618 (this.focusNode || this.domNode).setAttribute("role", "tab");
16619 }
16620 });
16621
16622
16623 var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], {
16624 // summary:
16625 // Set of buttons to select a page in a `dijit/layout/StackContainer`
16626 // description:
16627 // Monitors the specified StackContainer, and whenever a page is
16628 // added, deleted, or selected, updates itself accordingly.
16629
16630 baseClass: "dijitStackController",
16631
16632 templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
16633
16634 // containerId: [const] String
16635 // The id of the page container that I point to
16636 containerId: "",
16637
16638 // buttonWidget: [const] Constructor
16639 // The button widget to create to correspond to each page
16640 buttonWidget: StackButton,
16641
16642 // buttonWidgetCloseClass: String
16643 // CSS class of [x] close icon, used by event delegation code to tell when close button was clicked
16644 buttonWidgetCloseClass: "dijitStackCloseButton",
16645
16646 constructor: function(params /*===== , srcNodeRef =====*/){
16647 // summary:
16648 // Create the widget.
16649 // params: Object|null
16650 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
16651 // and functions, typically callbacks like onClick.
16652 // The hash can contain any of the widget's properties, excluding read-only properties.
16653 // srcNodeRef: DOMNode|String?
16654 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
16655
16656 this.pane2button = {}; // mapping from pane id to buttons
16657 },
16658
16659 postCreate: function(){
16660 this.inherited(arguments);
16661
16662 // Listen to notifications from StackContainer.
16663 // TODO: do this through bubbled events instead of topics
16664 this.subscribe(this.containerId+"-startup", "onStartup");
16665 this.subscribe(this.containerId+"-addChild", "onAddChild");
16666 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
16667 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
16668 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
16669
16670 // Listen for click events to select or close tabs.
16671 // No need to worry about ENTER/SPACE key handling: tabs are selected via left/right arrow keys,
16672 // and closed via shift-F10 (to show the close menu).
16673 this.connect(this.containerNode, 'click', function(evt){
16674 var button = registry.getEnclosingWidget(evt.target);
16675 if(button != this.containerNode && !button.disabled && button.page){
16676 for(var target = evt.target; target !== this.containerNode; target = target.parentNode){
16677 if(domClass.contains(target, this.buttonWidgetCloseClass)){
16678 this.onCloseButtonClick(button.page);
16679 break;
16680 }else if(target == button.domNode){
16681 this.onButtonClick(button.page);
16682 break;
16683 }
16684 }
16685 }
16686 });
16687 },
16688
16689 onStartup: function(/*Object*/ info){
16690 // summary:
16691 // Called after StackContainer has finished initializing
16692 // tags:
16693 // private
16694 array.forEach(info.children, this.onAddChild, this);
16695 if(info.selected){
16696 // Show button corresponding to selected pane (unless selected
16697 // is null because there are no panes)
16698 this.onSelectChild(info.selected);
16699 }
16700
16701 // Reflect events like page title changes to tab buttons
16702 var containerNode = registry.byId(this.containerId).containerNode,
16703 pane2button = this.pane2button,
16704 paneToButtonAttr = {
16705 "title": "label",
16706 "showtitle": "showLabel",
16707 "iconclass": "iconClass",
16708 "closable": "closeButton",
16709 "tooltip": "title",
16710 "disabled": "disabled"
16711 },
16712 connectFunc = function(attr, buttonAttr){
16713 return on(containerNode, "attrmodified-" + attr, function(evt){
16714 var button = pane2button[evt.detail && evt.detail.widget && evt.detail.widget.id];
16715 if(button){
16716 button.set(buttonAttr, evt.detail.newValue);
16717 }
16718 });
16719 };
16720 for(var attr in paneToButtonAttr){
16721 this.own(connectFunc(attr, paneToButtonAttr[attr]));
16722 }
16723 },
16724
16725 destroy: function(){
16726 // Since the buttons are internal to the StackController widget, destroy() should remove them, which is
16727 // done by calling onRemoveChild().
16728 for(var pane in this.pane2button){
16729 this.onRemoveChild(registry.byId(pane));
16730 }
16731
16732 // TODO: destroyRecursive() will call destroy() on each child button twice. Once from the above code,
16733 // and once because _WidgetBase.destroyDescendants() deletes anything inside of this.containerNode.
16734 // Probably shouldn't attach that DOMNode as this.containerNode.
16735
16736 this.inherited(arguments);
16737 },
16738
16739 onAddChild: function(/*dijit/_WidgetBase*/ page, /*Integer?*/ insertIndex){
16740 // summary:
16741 // Called whenever a page is added to the container.
16742 // Create button corresponding to the page.
16743 // tags:
16744 // private
16745
16746 // create an instance of the button widget
16747 // (remove typeof buttonWidget == string support in 2.0)
16748 var Cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
16749 var button = new Cls({
16750 id: this.id + "_" + page.id,
16751 name: this.id + "_" + page.id,
16752 label: page.title,
16753 disabled: page.disabled,
16754 ownerDocument: this.ownerDocument,
16755 dir: page.dir,
16756 lang: page.lang,
16757 textDir: page.textDir,
16758 showLabel: page.showTitle,
16759 iconClass: page.iconClass,
16760 closeButton: page.closable,
16761 title: page.tooltip,
16762 page: page
16763 });
16764
16765 this.addChild(button, insertIndex);
16766 this.pane2button[page.id] = button;
16767 page.controlButton = button; // this value might be overwritten if two tabs point to same container
16768 if(!this._currentChild){
16769 // If this is the first child then StackContainer will soon publish that it's selected,
16770 // but before that StackContainer calls layout(), and before layout() is called the
16771 // StackController needs to have the proper height... which means that the button needs
16772 // to be marked as selected now. See test_TabContainer_CSS.html for test.
16773 this.onSelectChild(page);
16774 }
16775 },
16776
16777 onRemoveChild: function(/*dijit/_WidgetBase*/ page){
16778 // summary:
16779 // Called whenever a page is removed from the container.
16780 // Remove the button corresponding to the page.
16781 // tags:
16782 // private
16783
16784 if(this._currentChild === page){ this._currentChild = null; }
16785
16786 var button = this.pane2button[page.id];
16787 if(button){
16788 this.removeChild(button);
16789 delete this.pane2button[page.id];
16790 button.destroy();
16791 }
16792 delete page.controlButton;
16793 },
16794
16795 onSelectChild: function(/*dijit/_WidgetBase*/ page){
16796 // summary:
16797 // Called when a page has been selected in the StackContainer, either by me or by another StackController
16798 // tags:
16799 // private
16800
16801 if(!page){ return; }
16802
16803 if(this._currentChild){
16804 var oldButton=this.pane2button[this._currentChild.id];
16805 oldButton.set('checked', false);
16806 oldButton.focusNode.setAttribute("tabIndex", "-1");
16807 }
16808
16809 var newButton=this.pane2button[page.id];
16810 newButton.set('checked', true);
16811 this._currentChild = page;
16812 newButton.focusNode.setAttribute("tabIndex", "0");
16813 var container = registry.byId(this.containerId);
16814 container.containerNode.setAttribute("aria-labelledby", newButton.id);
16815 },
16816
16817 onButtonClick: function(/*dijit/_WidgetBase*/ page){
16818 // summary:
16819 // Called whenever one of my child buttons is pressed in an attempt to select a page
16820 // tags:
16821 // private
16822
16823 var button = this.pane2button[page.id];
16824
16825 // For TabContainer where the tabs are <span>, need to set focus explicitly when left/right arrow
16826 focus.focus(button.focusNode);
16827
16828 if(this._currentChild && this._currentChild.id === page.id) {
16829 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
16830 button.set('checked', true);
16831 }
16832 var container = registry.byId(this.containerId);
16833 container.selectChild(page);
16834 },
16835
16836 onCloseButtonClick: function(/*dijit/_WidgetBase*/ page){
16837 // summary:
16838 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
16839 // tags:
16840 // private
16841
16842 var container = registry.byId(this.containerId);
16843 container.closeChild(page);
16844 if(this._currentChild){
16845 var b = this.pane2button[this._currentChild.id];
16846 if(b){
16847 focus.focus(b.focusNode || b.domNode);
16848 }
16849 }
16850 },
16851
16852 // TODO: this is a bit redundant with forward, back api in StackContainer
16853 adjacent: function(/*Boolean*/ forward){
16854 // summary:
16855 // Helper for onkeypress to find next/previous button
16856 // tags:
16857 // private
16858
16859 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
16860 // find currently focused button in children array
16861 var children = this.getChildren();
16862 var idx = array.indexOf(children, this.pane2button[this._currentChild.id]),
16863 current = children[idx];
16864
16865 // Pick next/previous non-disabled button to focus on. If we get back to the original button it means
16866 // that all buttons must be disabled, so return current child to avoid an infinite loop.
16867 var child;
16868 do{
16869 idx = (idx + (forward ? 1 : children.length - 1)) % children.length;
16870 child = children[idx];
16871 }while(child.disabled && child != current);
16872
16873 return child; // dijit/_WidgetBase
16874 },
16875
16876 onkeypress: function(/*Event*/ e){
16877 // summary:
16878 // Handle keystrokes on the page list, for advancing to next/previous button
16879 // and closing the current page if the page is closable.
16880 // tags:
16881 // private
16882
16883 if(this.disabled || e.altKey ){ return; }
16884 var forward = null;
16885 if(e.ctrlKey || !e._djpage){
16886 switch(e.charOrCode){
16887 case keys.LEFT_ARROW:
16888 case keys.UP_ARROW:
16889 if(!e._djpage){ forward = false; }
16890 break;
16891 case keys.PAGE_UP:
16892 if(e.ctrlKey){ forward = false; }
16893 break;
16894 case keys.RIGHT_ARROW:
16895 case keys.DOWN_ARROW:
16896 if(!e._djpage){ forward = true; }
16897 break;
16898 case keys.PAGE_DOWN:
16899 if(e.ctrlKey){ forward = true; }
16900 break;
16901 case keys.HOME:
16902 // Navigate to first non-disabled child
16903 var children = this.getChildren();
16904 for(var idx = 0; idx < children.length; idx++){
16905 var child = children[idx];
16906 if(!child.disabled){
16907 this.onButtonClick(child.page);
16908 break;
16909 }
16910 }
16911 event.stop(e);
16912 break;
16913 case keys.END:
16914 // Navigate to last non-disabled child
16915 var children = this.getChildren();
16916 for(var idx = children.length-1; idx >= 0; idx--){
16917 var child = children[idx];
16918 if(!child.disabled){
16919 this.onButtonClick(child.page);
16920 break;
16921 }
16922 }
16923 event.stop(e);
16924 break;
16925 case keys.DELETE:
16926 if(this._currentChild.closable){
16927 this.onCloseButtonClick(this._currentChild);
16928 }
16929 event.stop(e);
16930 break;
16931 default:
16932 if(e.ctrlKey){
16933 if(e.charOrCode === keys.TAB){
16934 this.onButtonClick(this.adjacent(!e.shiftKey).page);
16935 event.stop(e);
16936 }else if(e.charOrCode == "w"){
16937 if(this._currentChild.closable){
16938 this.onCloseButtonClick(this._currentChild);
16939 }
16940 event.stop(e); // avoid browser tab closing.
16941 }
16942 }
16943 }
16944 // handle next/previous page navigation (left/right arrow, etc.)
16945 if(forward !== null){
16946 this.onButtonClick(this.adjacent(forward).page);
16947 event.stop(e);
16948 }
16949 }
16950 },
16951
16952 onContainerKeyPress: function(/*Object*/ info){
16953 // summary:
16954 // Called when there was a keypress on the container
16955 // tags:
16956 // private
16957 info.e._djpage = info.page;
16958 this.onkeypress(info.e);
16959 }
16960 });
16961
16962 StackController.StackButton = StackButton; // for monkey patching
16963
16964 return StackController;
16965 });
16966
16967 },
16968 'url:dijit/templates/TooltipDialog.html':"<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" data-dojo-attach-point=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n",
16969 'dojo/dnd/Mover':function(){
16970 define("dojo/dnd/Mover", [
16971 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window",
16972 "../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll"
16973 ], function(array, declare, event, lang, has, win, dom, domGeom, domStyle, Evented, on, touch, dnd, autoscroll){
16974
16975 // module:
16976 // dojo/dnd/Mover
16977
16978 return declare("dojo.dnd.Mover", [Evented], {
16979 // summary:
16980 // an object which makes a node follow the mouse, or touch-drag on touch devices.
16981 // Used as a default mover, and as a base class for custom movers.
16982
16983 constructor: function(node, e, host){
16984 // node: Node
16985 // a node (or node's id) to be moved
16986 // e: Event
16987 // a mouse event, which started the move;
16988 // only pageX and pageY properties are used
16989 // host: Object?
16990 // object which implements the functionality of the move,
16991 // and defines proper events (onMoveStart and onMoveStop)
16992 this.node = dom.byId(node);
16993 this.marginBox = {l: e.pageX, t: e.pageY};
16994 this.mouseButton = e.button;
16995 var h = (this.host = host), d = node.ownerDocument;
16996 this.events = [
16997 // At the start of a drag, onFirstMove is called, and then the following
16998 // listener is disconnected.
16999 on(d, touch.move, lang.hitch(this, "onFirstMove")),
17000
17001 // These are called continually during the drag
17002 on(d, touch.move, lang.hitch(this, "onMouseMove")),
17003
17004 // And these are called at the end of the drag
17005 on(d, touch.release, lang.hitch(this, "onMouseUp")),
17006
17007 // cancel text selection and text dragging
17008 on(d, "dragstart", event.stop),
17009 on(d.body, "selectstart", event.stop)
17010 ];
17011
17012 // Tell autoscroll that a drag is starting
17013 autoscroll.autoScrollStart(d);
17014
17015 // notify that the move has started
17016 if(h && h.onMoveStart){
17017 h.onMoveStart(this);
17018 }
17019 },
17020 // mouse event processors
17021 onMouseMove: function(e){
17022 // summary:
17023 // event processor for onmousemove/ontouchmove
17024 // e: Event
17025 // mouse/touch event
17026 autoscroll.autoScroll(e);
17027 var m = this.marginBox;
17028 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
17029 event.stop(e);
17030 },
17031 onMouseUp: function(e){
17032 if(has("webkit") && has("mac") && this.mouseButton == 2 ?
17033 e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
17034 this.destroy();
17035 }
17036 event.stop(e);
17037 },
17038 // utilities
17039 onFirstMove: function(e){
17040 // summary:
17041 // makes the node absolute; it is meant to be called only once.
17042 // relative and absolutely positioned nodes are assumed to use pixel units
17043 var s = this.node.style, l, t, h = this.host;
17044 switch(s.position){
17045 case "relative":
17046 case "absolute":
17047 // assume that left and top values are in pixels already
17048 l = Math.round(parseFloat(s.left)) || 0;
17049 t = Math.round(parseFloat(s.top)) || 0;
17050 break;
17051 default:
17052 s.position = "absolute"; // enforcing the absolute mode
17053 var m = domGeom.getMarginBox(this.node);
17054 // event.pageX/pageY (which we used to generate the initial
17055 // margin box) includes padding and margin set on the body.
17056 // However, setting the node's position to absolute and then
17057 // doing domGeom.marginBox on it *doesn't* take that additional
17058 // space into account - so we need to subtract the combined
17059 // padding and margin. We use getComputedStyle and
17060 // _getMarginBox/_getContentBox to avoid the extra lookup of
17061 // the computed style.
17062 var b = win.doc.body;
17063 var bs = domStyle.getComputedStyle(b);
17064 var bm = domGeom.getMarginBox(b, bs);
17065 var bc = domGeom.getContentBox(b, bs);
17066 l = m.l - (bc.l - bm.l);
17067 t = m.t - (bc.t - bm.t);
17068 break;
17069 }
17070 this.marginBox.l = l - this.marginBox.l;
17071 this.marginBox.t = t - this.marginBox.t;
17072 if(h && h.onFirstMove){
17073 h.onFirstMove(this, e);
17074 }
17075
17076 // Disconnect touch.move that call this function
17077 this.events.shift().remove();
17078 },
17079 destroy: function(){
17080 // summary:
17081 // stops the move, deletes all references, so the object can be garbage-collected
17082 array.forEach(this.events, function(handle){ handle.remove(); });
17083 // undo global settings
17084 var h = this.host;
17085 if(h && h.onMoveStop){
17086 h.onMoveStop(this);
17087 }
17088 // destroy objects
17089 this.events = this.node = this.host = null;
17090 }
17091 });
17092
17093 });
17094
17095 },
17096 'dijit/layout/TabContainer':function(){
17097 define("dijit/layout/TabContainer", [
17098 "dojo/_base/lang", // lang.getObject
17099 "dojo/_base/declare", // declare
17100 "./_TabContainerBase",
17101 "./TabController",
17102 "./ScrollingTabController"
17103 ], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){
17104
17105 // module:
17106 // dijit/layout/TabContainer
17107
17108
17109 return declare("dijit.layout.TabContainer", _TabContainerBase, {
17110 // summary:
17111 // A Container with tabs to select each child (only one of which is displayed at a time).
17112 // description:
17113 // A TabContainer is a container that has multiple panes, but shows only
17114 // one pane at a time. There are a set of tabs corresponding to each pane,
17115 // where each tab has the name (aka title) of the pane, and optionally a close button.
17116 //
17117 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
17118 // children of a `TabContainer`.
17119
17120 // useMenu: [const] Boolean
17121 // True if a menu should be used to select tabs when they are too
17122 // wide to fit the TabContainer, false otherwise.
17123 useMenu: true,
17124
17125 // useSlider: [const] Boolean
17126 // True if a slider should be used to select tabs when they are too
17127 // wide to fit the TabContainer, false otherwise.
17128 useSlider: true,
17129
17130 // controllerWidget: Class
17131 // An optional parameter to override the widget used to display the tab labels
17132 controllerWidget: "",
17133
17134 _makeController: function(/*DomNode*/ srcNode){
17135 // summary:
17136 // Instantiate tablist controller widget and return reference to it.
17137 // Callback from _TabContainerBase.postCreate().
17138 // tags:
17139 // protected extension
17140
17141 // "string" branch for back-compat, remove for 2.0
17142 var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
17143 TabController = typeof this.controllerWidget == "string" ? lang.getObject(this.controllerWidget) :
17144 this.controllerWidget;
17145
17146 return new TabController({
17147 id: this.id + "_tablist",
17148 ownerDocument: this.ownerDocument,
17149 dir: this.dir,
17150 lang: this.lang,
17151 textDir: this.textDir,
17152 tabPosition: this.tabPosition,
17153 doLayout: this.doLayout,
17154 containerId: this.id,
17155 "class": cls,
17156 nested: this.nested,
17157 useMenu: this.useMenu,
17158 useSlider: this.useSlider,
17159 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
17160 }, srcNode);
17161 },
17162
17163 postMixInProperties: function(){
17164 this.inherited(arguments);
17165
17166 // Scrolling controller only works for horizontal non-nested tabs
17167 if(!this.controllerWidget){
17168 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
17169 ScrollingTabController : TabController;
17170 }
17171 }
17172 });
17173 });
17174
17175 },
17176 'dijit/BackgroundIframe':function(){
17177 define("dijit/BackgroundIframe", [
17178 "require", // require.toUrl
17179 "./main", // to export dijit.BackgroundIframe
17180 "dojo/_base/config",
17181 "dojo/dom-construct", // domConstruct.create
17182 "dojo/dom-style", // domStyle.set
17183 "dojo/_base/lang", // lang.extend lang.hitch
17184 "dojo/on",
17185 "dojo/sniff", // has("ie"), has("mozilla"), has("quirks")
17186 "dojo/_base/window" // win.doc.createElement
17187 ], function(require, dijit, config, domConstruct, domStyle, lang, on, has, win){
17188
17189 // module:
17190 // dijit/BackgroundIFrame
17191
17192 // TODO: remove _frames, it isn't being used much, since popups never release their
17193 // iframes (see [22236])
17194 var _frames = new function(){
17195 // summary:
17196 // cache of iframes
17197
17198 var queue = [];
17199
17200 this.pop = function(){
17201 var iframe;
17202 if(queue.length){
17203 iframe = queue.pop();
17204 iframe.style.display="";
17205 }else{
17206 if(has("ie") < 9){
17207 var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
17208 var html="<iframe src='" + burl + "' role='presentation'"
17209 + " style='position: absolute; left: 0px; top: 0px;"
17210 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
17211 iframe = win.doc.createElement(html);
17212 }else{
17213 iframe = domConstruct.create("iframe");
17214 iframe.src = 'javascript:""';
17215 iframe.className = "dijitBackgroundIframe";
17216 iframe.setAttribute("role", "presentation");
17217 domStyle.set(iframe, "opacity", 0.1);
17218 }
17219 iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
17220 }
17221 return iframe;
17222 };
17223
17224 this.push = function(iframe){
17225 iframe.style.display="none";
17226 queue.push(iframe);
17227 }
17228 }();
17229
17230
17231 dijit.BackgroundIframe = function(/*DomNode*/ node){
17232 // summary:
17233 // For IE/FF z-index schenanigans. id attribute is required.
17234 //
17235 // description:
17236 // new dijit.BackgroundIframe(node).
17237 //
17238 // Makes a background iframe as a child of node, that fills
17239 // area (and position) of node
17240
17241 if(!node.id){ throw new Error("no id"); }
17242 if(has("ie") || has("mozilla")){
17243 var iframe = (this.iframe = _frames.pop());
17244 node.appendChild(iframe);
17245 if(has("ie")<7 || has("quirks")){
17246 this.resize(node);
17247 this._conn = on(node, 'resize', lang.hitch(this, function(){
17248 this.resize(node);
17249 }));
17250 }else{
17251 domStyle.set(iframe, {
17252 width: '100%',
17253 height: '100%'
17254 });
17255 }
17256 }
17257 };
17258
17259 lang.extend(dijit.BackgroundIframe, {
17260 resize: function(node){
17261 // summary:
17262 // Resize the iframe so it's the same size as node.
17263 // Needed on IE6 and IE/quirks because height:100% doesn't work right.
17264 if(this.iframe){
17265 domStyle.set(this.iframe, {
17266 width: node.offsetWidth + 'px',
17267 height: node.offsetHeight + 'px'
17268 });
17269 }
17270 },
17271 destroy: function(){
17272 // summary:
17273 // destroy the iframe
17274 if(this._conn){
17275 this._conn.remove();
17276 this._conn = null;
17277 }
17278 if(this.iframe){
17279 _frames.push(this.iframe);
17280 delete this.iframe;
17281 }
17282 }
17283 });
17284
17285 return dijit.BackgroundIframe;
17286 });
17287
17288 },
17289 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
17290 'dojo/dnd/Avatar':function(){
17291 define("dojo/dnd/Avatar", [
17292 "../_base/declare",
17293 "../_base/window",
17294 "../dom",
17295 "../dom-attr",
17296 "../dom-class",
17297 "../dom-construct",
17298 "../hccss",
17299 "../query"
17300 ], function(declare, win, dom, domAttr, domClass, domConstruct, has, query){
17301
17302 // module:
17303 // dojo/dnd/Avatar
17304
17305 return declare("dojo.dnd.Avatar", null, {
17306 // summary:
17307 // Object that represents transferred DnD items visually
17308 // manager: Object
17309 // a DnD manager object
17310
17311 constructor: function(manager){
17312 this.manager = manager;
17313 this.construct();
17314 },
17315
17316 // methods
17317 construct: function(){
17318 // summary:
17319 // constructor function;
17320 // it is separate so it can be (dynamically) overwritten in case of need
17321
17322 var a = domConstruct.create("table", {
17323 "class": "dojoDndAvatar",
17324 style: {
17325 position: "absolute",
17326 zIndex: "1999",
17327 margin: "0px"
17328 }
17329 }),
17330 source = this.manager.source, node,
17331 b = domConstruct.create("tbody", null, a),
17332 tr = domConstruct.create("tr", null, b),
17333 td = domConstruct.create("td", null, tr),
17334 k = Math.min(5, this.manager.nodes.length), i = 0;
17335
17336 if(has("highcontrast")){
17337 domConstruct.create("span", {
17338 id : "a11yIcon",
17339 innerHTML : this.manager.copy ? '+' : "<"
17340 }, td)
17341 }
17342 domConstruct.create("span", {
17343 innerHTML: source.generateText ? this._generateText() : ""
17344 }, td);
17345
17346 // we have to set the opacity on IE only after the node is live
17347 domAttr.set(tr, {
17348 "class": "dojoDndAvatarHeader",
17349 style: {opacity: 0.9}
17350 });
17351 for(; i < k; ++i){
17352 if(source.creator){
17353 // create an avatar representation of the node
17354 node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
17355 }else{
17356 // or just clone the node and hope it works
17357 node = this.manager.nodes[i].cloneNode(true);
17358 if(node.tagName.toLowerCase() == "tr"){
17359 // insert extra table nodes
17360 var table = domConstruct.create("table"),
17361 tbody = domConstruct.create("tbody", null, table);
17362 tbody.appendChild(node);
17363 node = table;
17364 }
17365 }
17366 node.id = "";
17367 tr = domConstruct.create("tr", null, b);
17368 td = domConstruct.create("td", null, tr);
17369 td.appendChild(node);
17370 domAttr.set(tr, {
17371 "class": "dojoDndAvatarItem",
17372 style: {opacity: (9 - i) / 10}
17373 });
17374 }
17375 this.node = a;
17376 },
17377 destroy: function(){
17378 // summary:
17379 // destructor for the avatar; called to remove all references so it can be garbage-collected
17380 domConstruct.destroy(this.node);
17381 this.node = false;
17382 },
17383 update: function(){
17384 // summary:
17385 // updates the avatar to reflect the current DnD state
17386 domClass.toggle(this.node, "dojoDndAvatarCanDrop", this.manager.canDropFlag);
17387 if(has("highcontrast")){
17388 var icon = dom.byId("a11yIcon");
17389 var text = '+'; // assume canDrop && copy
17390 if (this.manager.canDropFlag && !this.manager.copy){
17391 text = '< '; // canDrop && move
17392 }else if (!this.manager.canDropFlag && !this.manager.copy){
17393 text = "o"; //!canDrop && move
17394 }else if(!this.manager.canDropFlag){
17395 text = 'x'; // !canDrop && copy
17396 }
17397 icon.innerHTML=text;
17398 }
17399 // replace text
17400 query(("tr.dojoDndAvatarHeader td span" +(has("highcontrast") ? " span" : "")), this.node).forEach(
17401 function(node){
17402 node.innerHTML = this.manager.source.generateText ? this._generateText() : "";
17403 }, this);
17404 },
17405 _generateText: function(){
17406 // summary:
17407 // generates a proper text to reflect copying or moving of items
17408 return this.manager.nodes.length.toString();
17409 }
17410 });
17411
17412 });
17413
17414 },
17415 'dijit/form/Button':function(){
17416 require({cache:{
17417 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"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\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
17418 define("dijit/form/Button", [
17419 "require",
17420 "dojo/_base/declare", // declare
17421 "dojo/dom-class", // domClass.toggle
17422 "dojo/has", // has("dijit-legacy-requires")
17423 "dojo/_base/kernel", // kernel.deprecated
17424 "dojo/_base/lang", // lang.trim
17425 "dojo/ready",
17426 "./_FormWidget",
17427 "./_ButtonMixin",
17428 "dojo/text!./templates/Button.html"
17429 ], function(require, declare, domClass, has, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
17430
17431 // module:
17432 // dijit/form/Button
17433
17434 // Back compat w/1.6, remove for 2.0
17435 if(has("dijit-legacy-requires")){
17436 ready(0, function(){
17437 var requires = ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
17438 require(requires); // use indirection so modules not rolled into a build
17439 });
17440 }
17441
17442 return declare("dijit.form.Button", [_FormWidget, _ButtonMixin], {
17443 // summary:
17444 // Basically the same thing as a normal HTML button, but with special styling.
17445 // description:
17446 // Buttons can display a label, an icon, or both.
17447 // A label should always be specified (through innerHTML) or the label
17448 // attribute. It can be hidden via showLabel=false.
17449 // example:
17450 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
17451 //
17452 // example:
17453 // | var button1 = new Button({label: "hello world", onClick: foo});
17454 // | dojo.body().appendChild(button1.domNode);
17455
17456 // showLabel: Boolean
17457 // Set this to true to hide the label text and display only the icon.
17458 // (If showLabel=false then iconClass must be specified.)
17459 // Especially useful for toolbars.
17460 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
17461 //
17462 // The exception case is for computers in high-contrast mode, where the label
17463 // will still be displayed, since the icon doesn't appear.
17464 showLabel: true,
17465
17466 // iconClass: String
17467 // Class to apply to DOMNode in button to make it display an icon
17468 iconClass: "dijitNoIcon",
17469 _setIconClassAttr: { node: "iconNode", type: "class" },
17470
17471 baseClass: "dijitButton",
17472
17473 templateString: template,
17474
17475 // Map widget attributes to DOMNode attributes.
17476 _setValueAttr: "valueNode",
17477
17478 _onClick: function(/*Event*/ e){
17479 // summary:
17480 // Internal function to handle click actions
17481 var ok = this.inherited(arguments);
17482 if(ok){
17483 if(this.valueNode){
17484 this.valueNode.click();
17485 e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
17486 e.stopPropagation(); // avoid two events bubbling from Button widget
17487 // leave ok = true so that subclasses can do what they need to do
17488 }
17489 }
17490 return ok;
17491 },
17492
17493 _fillContent: function(/*DomNode*/ source){
17494 // Overrides _Templated._fillContent().
17495 // If button label is specified as srcNodeRef.innerHTML rather than
17496 // this.params.label, handle it here.
17497 // TODO: remove the method in 2.0, parser will do it all for me
17498 if(source && (!this.params || !("label" in this.params))){
17499 var sourceLabel = lang.trim(source.innerHTML);
17500 if(sourceLabel){
17501 this.label = sourceLabel; // _applyAttributes will be called after buildRendering completes to update the DOM
17502 }
17503 }
17504 },
17505
17506 _setShowLabelAttr: function(val){
17507 if(this.containerNode){
17508 domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
17509 }
17510 this._set("showLabel", val);
17511 },
17512
17513 setLabel: function(/*String*/ content){
17514 // summary:
17515 // Deprecated. Use set('label', ...) instead.
17516 kernel.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
17517 this.set("label", content);
17518 },
17519
17520 _setLabelAttr: function(/*String*/ content){
17521 // summary:
17522 // Hook for set('label', ...) to work.
17523 // description:
17524 // Set the label (text) of the button; takes an HTML string.
17525 // If the label is hidden (showLabel=false) then and no title has
17526 // been specified, then label is also set as title attribute of icon.
17527 this.inherited(arguments);
17528 if(!this.showLabel && !("title" in this.params)){
17529 this.titleNode.title = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
17530 }
17531 }
17532 });
17533
17534
17535 });
17536
17537
17538 },
17539 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n",
17540 'dojo/dnd/move':function(){
17541 define("dojo/dnd/move", [
17542 "../_base/declare",
17543 "../dom-geometry", "../dom-style",
17544 "./common", "./Mover", "./Moveable"
17545 ], function(declare, domGeom, domStyle, dnd, Mover, Moveable){
17546
17547 // module:
17548 // dojo/dnd/move
17549
17550 /*=====
17551 var __constrainedMoveableArgs = declare([Moveable.__MoveableArgs], {
17552 // constraints: Function
17553 // Calculates a constraint box.
17554 // It is called in a context of the moveable object.
17555 constraints: function(){},
17556
17557 // within: Boolean
17558 // restrict move within boundaries.
17559 within: false
17560 });
17561 =====*/
17562
17563 var constrainedMoveable = declare("dojo.dnd.move.constrainedMoveable", Moveable, {
17564 // object attributes (for markup)
17565 constraints: function(){},
17566 within: false,
17567
17568 constructor: function(node, params){
17569 // summary:
17570 // an object that makes a node moveable
17571 // node: Node
17572 // a node (or node's id) to be moved
17573 // params: __constrainedMoveableArgs?
17574 // an optional object with additional parameters;
17575 // the rest is passed to the base class
17576 if(!params){ params = {}; }
17577 this.constraints = params.constraints;
17578 this.within = params.within;
17579 },
17580 onFirstMove: function(/*Mover*/ mover){
17581 // summary:
17582 // called during the very first move notification;
17583 // can be used to initialize coordinates, can be overwritten.
17584 var c = this.constraintBox = this.constraints.call(this, mover);
17585 c.r = c.l + c.w;
17586 c.b = c.t + c.h;
17587 if(this.within){
17588 var mb = domGeom.getMarginSize(mover.node);
17589 c.r -= mb.w;
17590 c.b -= mb.h;
17591 }
17592 },
17593 onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
17594 // summary:
17595 // called during every move notification;
17596 // should actually move the node; can be overwritten.
17597 var c = this.constraintBox, s = mover.node.style;
17598 this.onMoving(mover, leftTop);
17599 leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
17600 leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
17601 s.left = leftTop.l + "px";
17602 s.top = leftTop.t + "px";
17603 this.onMoved(mover, leftTop);
17604 }
17605 });
17606
17607 /*=====
17608 var __boxConstrainedMoveableArgs = declare([__constrainedMoveableArgs], {
17609 // box: Object
17610 // a constraint box
17611 box: {}
17612 });
17613 =====*/
17614
17615 var boxConstrainedMoveable = declare("dojo.dnd.move.boxConstrainedMoveable", constrainedMoveable, {
17616 // box:
17617 // object attributes (for markup)
17618 box: {},
17619
17620 constructor: function(node, params){
17621 // summary:
17622 // an object, which makes a node moveable
17623 // node: Node
17624 // a node (or node's id) to be moved
17625 // params: __boxConstrainedMoveableArgs?
17626 // an optional object with parameters
17627 var box = params && params.box;
17628 this.constraints = function(){ return box; };
17629 }
17630 });
17631
17632 /*=====
17633 var __parentConstrainedMoveableArgs = declare( [__constrainedMoveableArgs], {
17634 // area: String
17635 // A parent's area to restrict the move.
17636 // Can be "margin", "border", "padding", or "content".
17637 area: ""
17638 });
17639 =====*/
17640
17641 var parentConstrainedMoveable = declare("dojo.dnd.move.parentConstrainedMoveable", constrainedMoveable, {
17642 // area:
17643 // object attributes (for markup)
17644 area: "content",
17645
17646 constructor: function(node, params){
17647 // summary:
17648 // an object, which makes a node moveable
17649 // node: Node
17650 // a node (or node's id) to be moved
17651 // params: __parentConstrainedMoveableArgs?
17652 // an optional object with parameters
17653 var area = params && params.area;
17654 this.constraints = function(){
17655 var n = this.node.parentNode,
17656 s = domStyle.getComputedStyle(n),
17657 mb = domGeom.getMarginBox(n, s);
17658 if(area == "margin"){
17659 return mb; // Object
17660 }
17661 var t = domGeom.getMarginExtents(n, s);
17662 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17663 if(area == "border"){
17664 return mb; // Object
17665 }
17666 t = domGeom.getBorderExtents(n, s);
17667 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17668 if(area == "padding"){
17669 return mb; // Object
17670 }
17671 t = domGeom.getPadExtents(n, s);
17672 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17673 return mb; // Object
17674 };
17675 }
17676 });
17677
17678
17679 return {
17680 // summary:
17681 // TODOC
17682 constrainedMoveable: constrainedMoveable,
17683 boxConstrainedMoveable: boxConstrainedMoveable,
17684 parentConstrainedMoveable: parentConstrainedMoveable
17685 };
17686
17687 });
17688
17689 },
17690 'dijit/_WidgetBase':function(){
17691 define("dijit/_WidgetBase", [
17692 "require", // require.toUrl
17693 "dojo/_base/array", // array.forEach array.map
17694 "dojo/aspect",
17695 "dojo/_base/config", // config.blankGif
17696 "dojo/_base/connect", // connect.connect
17697 "dojo/_base/declare", // declare
17698 "dojo/dom", // dom.byId
17699 "dojo/dom-attr", // domAttr.set domAttr.remove
17700 "dojo/dom-class", // domClass.add domClass.replace
17701 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
17702 "dojo/dom-geometry", // isBodyLtr
17703 "dojo/dom-style", // domStyle.set, domStyle.get
17704 "dojo/has",
17705 "dojo/_base/kernel",
17706 "dojo/_base/lang", // mixin(), isArray(), etc.
17707 "dojo/on",
17708 "dojo/ready",
17709 "dojo/Stateful", // Stateful
17710 "dojo/topic",
17711 "dojo/_base/window", // win.doc, win.body()
17712 "./Destroyable",
17713 "./registry" // registry.getUniqueId(), registry.findWidgets()
17714 ], function(require, array, aspect, config, connect, declare,
17715 dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel,
17716 lang, on, ready, Stateful, topic, win, Destroyable, registry){
17717
17718 // module:
17719 // dijit/_WidgetBase
17720
17721 // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
17722 has.add("dijit-legacy-requires", !kernel.isAsync);
17723
17724 // For back-compat, remove in 2.0.
17725 if(has("dijit-legacy-requires")){
17726 ready(0, function(){
17727 var requires = ["dijit/_base/manager"];
17728 require(requires); // use indirection so modules not rolled into a build
17729 });
17730 }
17731
17732 // Nested hash listing attributes for each tag, all strings in lowercase.
17733 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
17734 var tagAttrs = {};
17735 function getAttrs(obj){
17736 var ret = {};
17737 for(var attr in obj){
17738 ret[attr.toLowerCase()] = true;
17739 }
17740 return ret;
17741 }
17742
17743 function nonEmptyAttrToDom(attr){
17744 // summary:
17745 // Returns a setter function that copies the attribute to this.domNode,
17746 // or removes the attribute from this.domNode, depending on whether the
17747 // value is defined or not.
17748 return function(val){
17749 domAttr[val ? "set" : "remove"](this.domNode, attr, val);
17750 this._set(attr, val);
17751 };
17752 }
17753
17754 return declare("dijit._WidgetBase", [Stateful, Destroyable], {
17755 // summary:
17756 // Future base class for all Dijit widgets.
17757 // description:
17758 // Future base class for all Dijit widgets.
17759 // _Widget extends this class adding support for various features needed by desktop.
17760 //
17761 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
17762 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
17763 //
17764 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
17765 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
17766 //
17767 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
17768 //
17769 // - DOM node attribute
17770 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
17771 // | _setFocusAttr: "focusNode" (shorthand)
17772 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
17773 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
17774 //
17775 // - DOM node innerHTML
17776 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
17777 // Maps this.title to this.titleNode.innerHTML
17778 //
17779 // - DOM node innerText
17780 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
17781 // Maps this.title to this.titleNode.innerText
17782 //
17783 // - DOM node CSS class
17784 // | _setMyClassAttr: { node: "domNode", type: "class" }
17785 // Maps this.myClass to this.domNode.className
17786 //
17787 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
17788 // formats of the above list.
17789 //
17790 // If the custom setter is null, no action is performed other than saving the new value
17791 // in the widget (in this).
17792 //
17793 // If no custom setter is defined for an attribute, then it will be copied
17794 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
17795 // That's only done though for attributes that match DOMNode attributes (title,
17796 // alt, aria-labelledby, etc.)
17797
17798 // id: [const] String
17799 // A unique, opaque ID string that can be assigned by users or by the
17800 // system. If the developer passes an ID which is known not to be
17801 // unique, the specified ID is ignored and the system-generated ID is
17802 // used instead.
17803 id: "",
17804 _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's
17805
17806 // lang: [const] String
17807 // Rarely used. Overrides the default Dojo locale used to render this widget,
17808 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
17809 // Value must be among the list of locales specified during by the Dojo bootstrap,
17810 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
17811 lang: "",
17812 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
17813 _setLangAttr: nonEmptyAttrToDom("lang"),
17814
17815 // dir: [const] String
17816 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
17817 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
17818 // default direction.
17819 dir: "",
17820 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
17821 _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
17822
17823 // textDir: String
17824 // Bi-directional support, the main variable which is responsible for the direction of the text.
17825 // The text direction can be different than the GUI direction by using this parameter in creation
17826 // of a widget.
17827 //
17828 // Allowed values:
17829 //
17830 // 1. "ltr"
17831 // 2. "rtl"
17832 // 3. "auto" - contextual the direction of a text defined by first strong letter.
17833 //
17834 // By default is as the page direction.
17835 textDir: "",
17836
17837 // class: String
17838 // HTML class attribute
17839 "class": "",
17840 _setClassAttr: { node: "domNode", type: "class" },
17841
17842 // style: String||Object
17843 // HTML style attributes as cssText string or name/value hash
17844 style: "",
17845
17846 // title: String
17847 // HTML title attribute.
17848 //
17849 // For form widgets this specifies a tooltip to display when hovering over
17850 // the widget (just like the native HTML title attribute).
17851 //
17852 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
17853 // etc., it's used to specify the tab label, accordion pane title, etc.
17854 title: "",
17855
17856 // tooltip: String
17857 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
17858 // this specifies the tooltip to appear when the mouse is hovered over that text.
17859 tooltip: "",
17860
17861 // baseClass: [protected] String
17862 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
17863 // widget state.
17864 baseClass: "",
17865
17866 // srcNodeRef: [readonly] DomNode
17867 // pointer to original DOM node
17868 srcNodeRef: null,
17869
17870 // domNode: [readonly] DomNode
17871 // This is our visible representation of the widget! Other DOM
17872 // Nodes may by assigned to other properties, usually through the
17873 // template system's data-dojo-attach-point syntax, but the domNode
17874 // property is the canonical "top level" node in widget UI.
17875 domNode: null,
17876
17877 // containerNode: [readonly] DomNode
17878 // Designates where children of the source DOM node will be placed.
17879 // "Children" in this case refers to both DOM nodes and widgets.
17880 // For example, for myWidget:
17881 //
17882 // | <div data-dojo-type=myWidget>
17883 // | <b> here's a plain DOM node
17884 // | <span data-dojo-type=subWidget>and a widget</span>
17885 // | <i> and another plain DOM node </i>
17886 // | </div>
17887 //
17888 // containerNode would point to:
17889 //
17890 // | <b> here's a plain DOM node
17891 // | <span data-dojo-type=subWidget>and a widget</span>
17892 // | <i> and another plain DOM node </i>
17893 //
17894 // In templated widgets, "containerNode" is set via a
17895 // data-dojo-attach-point assignment.
17896 //
17897 // containerNode must be defined for any widget that accepts innerHTML
17898 // (like ContentPane or BorderContainer or even Button), and conversely
17899 // is null for widgets that don't, like TextBox.
17900 containerNode: null,
17901
17902 // ownerDocument: [const] Document?
17903 // The document this widget belongs to. If not specified to constructor, will default to
17904 // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
17905 ownerDocument: null,
17906 _setOwnerDocumentAttr: function(val){
17907 // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
17908 this._set("ownerDocument", val);
17909 },
17910
17911 /*=====
17912 // _started: [readonly] Boolean
17913 // startup() has completed.
17914 _started: false,
17915 =====*/
17916
17917 // attributeMap: [protected] Object
17918 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
17919 // for each XXX attribute to be mapped to the DOM.
17920 //
17921 // attributeMap sets up a "binding" between attributes (aka properties)
17922 // of the widget and the widget's DOM.
17923 // Changes to widget attributes listed in attributeMap will be
17924 // reflected into the DOM.
17925 //
17926 // For example, calling set('title', 'hello')
17927 // on a TitlePane will automatically cause the TitlePane's DOM to update
17928 // with the new title.
17929 //
17930 // attributeMap is a hash where the key is an attribute of the widget,
17931 // and the value reflects a binding to a:
17932 //
17933 // - DOM node attribute
17934 // | focus: {node: "focusNode", type: "attribute"}
17935 // Maps this.focus to this.focusNode.focus
17936 //
17937 // - DOM node innerHTML
17938 // | title: { node: "titleNode", type: "innerHTML" }
17939 // Maps this.title to this.titleNode.innerHTML
17940 //
17941 // - DOM node innerText
17942 // | title: { node: "titleNode", type: "innerText" }
17943 // Maps this.title to this.titleNode.innerText
17944 //
17945 // - DOM node CSS class
17946 // | myClass: { node: "domNode", type: "class" }
17947 // Maps this.myClass to this.domNode.className
17948 //
17949 // If the value is an array, then each element in the array matches one of the
17950 // formats of the above list.
17951 //
17952 // There are also some shorthands for backwards compatibility:
17953 //
17954 // - string --> { node: string, type: "attribute" }, for example:
17955 //
17956 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
17957 //
17958 // - "" --> { node: "domNode", type: "attribute" }
17959 attributeMap: {},
17960
17961 // _blankGif: [protected] String
17962 // Path to a blank 1x1 image.
17963 // Used by `<img>` nodes in templates that really get their image via CSS background-image.
17964 _blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"),
17965
17966 //////////// INITIALIZATION METHODS ///////////////////////////////////////
17967
17968 /*=====
17969 constructor: function(params, srcNodeRef){
17970 // summary:
17971 // Create the widget.
17972 // params: Object|null
17973 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17974 // and functions, typically callbacks like onClick.
17975 // The hash can contain any of the widget's properties, excluding read-only properties.
17976 // srcNodeRef: DOMNode|String?
17977 // If a srcNodeRef (DOM node) is specified:
17978 //
17979 // - use srcNodeRef.innerHTML as my contents
17980 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17981 // - otherwise, replace srcNodeRef with my generated DOM tree
17982 },
17983 =====*/
17984
17985 postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
17986 // summary:
17987 // Kicks off widget instantiation. See create() for details.
17988 // tags:
17989 // private
17990 this.create(params, srcNodeRef);
17991 },
17992
17993 create: function(params, srcNodeRef){
17994 // summary:
17995 // Kick off the life-cycle of a widget
17996 // description:
17997 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
17998 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
17999 // for a discussion of the widget creation lifecycle.
18000 //
18001 // Of course, adventurous developers could override create entirely, but this should
18002 // only be done as a last resort.
18003 // params: Object|null
18004 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
18005 // and functions, typically callbacks like onClick.
18006 // The hash can contain any of the widget's properties, excluding read-only properties.
18007 // srcNodeRef: DOMNode|String?
18008 // If a srcNodeRef (DOM node) is specified:
18009 //
18010 // - use srcNodeRef.innerHTML as my contents
18011 // - if this is a behavioral widget then apply behavior to that srcNodeRef
18012 // - otherwise, replace srcNodeRef with my generated DOM tree
18013 // tags:
18014 // private
18015
18016 // store pointer to original DOM tree
18017 this.srcNodeRef = dom.byId(srcNodeRef);
18018
18019 // No longer used, remove for 2.0.
18020 this._connects = [];
18021 this._supportingWidgets = [];
18022
18023 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
18024 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
18025
18026 // mix in our passed parameters
18027 if(params){
18028 this.params = params;
18029 lang.mixin(this, params);
18030 }
18031 this.postMixInProperties();
18032
18033 // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
18034 // Do this before buildRendering() because it might expect the id to be there.
18035 if(!this.id){
18036 this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
18037 if(this.params){
18038 // if params contains {id: undefined}, prevent _applyAttributes() from processing it
18039 delete this.params.id;
18040 }
18041 }
18042
18043 // The document and <body> node this widget is associated with
18044 this.ownerDocument = this.ownerDocument || (this.srcNodeRef ? this.srcNodeRef.ownerDocument : win.doc);
18045 this.ownerDocumentBody = win.body(this.ownerDocument);
18046
18047 registry.add(this);
18048
18049 this.buildRendering();
18050
18051 var deleteSrcNodeRef;
18052
18053 if(this.domNode){
18054 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
18055 // Also calls custom setters for all attributes with custom setters.
18056 this._applyAttributes();
18057
18058 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
18059 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
18060 // widget being attached to the DOM since it isn't when a widget is created programmatically like
18061 // new MyWidget({}). See #11635.
18062 var source = this.srcNodeRef;
18063 if(source && source.parentNode && this.domNode !== source){
18064 source.parentNode.replaceChild(this.domNode, source);
18065 deleteSrcNodeRef = true;
18066 }
18067
18068 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
18069 // assuming that dojo._scopeName even exists in 2.0
18070 this.domNode.setAttribute("widgetId", this.id);
18071 }
18072 this.postCreate();
18073
18074 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
18075 // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
18076 if(deleteSrcNodeRef){
18077 delete this.srcNodeRef;
18078 }
18079
18080 this._created = true;
18081 },
18082
18083 _applyAttributes: function(){
18084 // summary:
18085 // Step during widget creation to copy widget attributes to the
18086 // DOM according to attributeMap and _setXXXAttr objects, and also to call
18087 // custom _setXXXAttr() methods.
18088 //
18089 // Skips over blank/false attribute values, unless they were explicitly specified
18090 // as parameters to the widget, since those are the default anyway,
18091 // and setting tabIndex="" is different than not setting tabIndex at all.
18092 //
18093 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
18094 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
18095 // tags:
18096 // private
18097
18098 // Get list of attributes where this.set(name, value) will do something beyond
18099 // setting this[name] = value. Specifically, attributes that have:
18100 // - associated _setXXXAttr() method/hash/string/array
18101 // - entries in attributeMap (remove this for 2.0);
18102 var ctor = this.constructor,
18103 list = ctor._setterAttrs;
18104 if(!list){
18105 list = (ctor._setterAttrs = []);
18106 for(var attr in this.attributeMap){
18107 list.push(attr);
18108 }
18109
18110 var proto = ctor.prototype;
18111 for(var fxName in proto){
18112 if(fxName in this.attributeMap){ continue; }
18113 var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr";
18114 if(setterName in proto){
18115 list.push(fxName);
18116 }
18117 }
18118 }
18119
18120 // Call this.set() for each property that was either specified as parameter to constructor,
18121 // or is in the list found above. For correlated properties like value and displayedValue, the one
18122 // specified as a parameter should take precedence.
18123 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
18124 // NaN and thus is not ignored like a default value of "".
18125
18126 // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
18127 // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
18128 var params = {};
18129 for(var key in this.params || {}){
18130 params[key] = this[key];
18131 }
18132
18133 // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
18134 array.forEach(list, function(attr){
18135 if(attr in params){
18136 // skip this one, do it below
18137 }else if(this[attr]){
18138 this.set(attr, this[attr]);
18139 }
18140 }, this);
18141
18142 // Step 3: Call set() for each property that was specified as parameter to constructor.
18143 // Use params hash created above to ignore side effects from step #2 above.
18144 for(key in params){
18145 this.set(key, params[key]);
18146 }
18147 },
18148
18149 postMixInProperties: function(){
18150 // summary:
18151 // Called after the parameters to the widget have been read-in,
18152 // but before the widget template is instantiated. Especially
18153 // useful to set properties that are referenced in the widget
18154 // template.
18155 // tags:
18156 // protected
18157 },
18158
18159 buildRendering: function(){
18160 // summary:
18161 // Construct the UI for this widget, setting this.domNode.
18162 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
18163 // tags:
18164 // protected
18165
18166 if(!this.domNode){
18167 // Create root node if it wasn't created by _Templated
18168 this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div");
18169 }
18170
18171 // baseClass is a single class name or occasionally a space-separated list of names.
18172 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
18173 // TODO: make baseClass custom setter
18174 if(this.baseClass){
18175 var classes = this.baseClass.split(" ");
18176 if(!this.isLeftToRight()){
18177 classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; }));
18178 }
18179 domClass.add(this.domNode, classes);
18180 }
18181 },
18182
18183 postCreate: function(){
18184 // summary:
18185 // Processing after the DOM fragment is created
18186 // description:
18187 // Called after the DOM fragment has been created, but not necessarily
18188 // added to the document. Do not include any operations which rely on
18189 // node dimensions or placement.
18190 // tags:
18191 // protected
18192 },
18193
18194 startup: function(){
18195 // summary:
18196 // Processing after the DOM fragment is added to the document
18197 // description:
18198 // Called after a widget and its children have been created and added to the page,
18199 // and all related widgets have finished their create() cycle, up through postCreate().
18200 // This is useful for composite widgets that need to control or layout sub-widgets.
18201 // Many layout widgets can use this as a wiring phase.
18202 if(this._started){ return; }
18203 this._started = true;
18204 array.forEach(this.getChildren(), function(obj){
18205 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
18206 obj.startup();
18207 obj._started = true;
18208 }
18209 });
18210 },
18211
18212 //////////// DESTROY FUNCTIONS ////////////////////////////////
18213
18214 destroyRecursive: function(/*Boolean?*/ preserveDom){
18215 // summary:
18216 // Destroy this widget and its descendants
18217 // description:
18218 // This is the generic "destructor" function that all widget users
18219 // should call to cleanly discard with a widget. Once a widget is
18220 // destroyed, it is removed from the manager object.
18221 // preserveDom:
18222 // If true, this method will leave the original DOM structure
18223 // alone of descendant Widgets. Note: This will NOT work with
18224 // dijit._Templated widgets.
18225
18226 this._beingDestroyed = true;
18227 this.destroyDescendants(preserveDom);
18228 this.destroy(preserveDom);
18229 },
18230
18231 destroy: function(/*Boolean*/ preserveDom){
18232 // summary:
18233 // Destroy this widget, but not its descendants.
18234 // This method will, however, destroy internal widgets such as those used within a template.
18235 // preserveDom: Boolean
18236 // If true, this method will leave the original DOM structure alone.
18237 // Note: This will not yet work with _Templated widgets
18238
18239 this._beingDestroyed = true;
18240 this.uninitialize();
18241
18242 function destroy(w){
18243 if(w.destroyRecursive){
18244 w.destroyRecursive(preserveDom);
18245 }else if(w.destroy){
18246 w.destroy(preserveDom);
18247 }
18248 }
18249
18250 // Back-compat, remove for 2.0
18251 array.forEach(this._connects, lang.hitch(this, "disconnect"));
18252 array.forEach(this._supportingWidgets, destroy);
18253
18254 // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
18255 // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
18256 if(this.domNode){
18257 array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy);
18258 }
18259
18260 this.destroyRendering(preserveDom);
18261 registry.remove(this.id);
18262 this._destroyed = true;
18263 },
18264
18265 destroyRendering: function(/*Boolean?*/ preserveDom){
18266 // summary:
18267 // Destroys the DOM nodes associated with this widget
18268 // preserveDom:
18269 // If true, this method will leave the original DOM structure alone
18270 // during tear-down. Note: this will not work with _Templated
18271 // widgets yet.
18272 // tags:
18273 // protected
18274
18275 if(this.bgIframe){
18276 this.bgIframe.destroy(preserveDom);
18277 delete this.bgIframe;
18278 }
18279
18280 if(this.domNode){
18281 if(preserveDom){
18282 domAttr.remove(this.domNode, "widgetId");
18283 }else{
18284 domConstruct.destroy(this.domNode);
18285 }
18286 delete this.domNode;
18287 }
18288
18289 if(this.srcNodeRef){
18290 if(!preserveDom){
18291 domConstruct.destroy(this.srcNodeRef);
18292 }
18293 delete this.srcNodeRef;
18294 }
18295 },
18296
18297 destroyDescendants: function(/*Boolean?*/ preserveDom){
18298 // summary:
18299 // Recursively destroy the children of this widget and their
18300 // descendants.
18301 // preserveDom:
18302 // If true, the preserveDom attribute is passed to all descendant
18303 // widget's .destroy() method. Not for use with _Templated
18304 // widgets.
18305
18306 // get all direct descendants and destroy them recursively
18307 array.forEach(this.getChildren(), function(widget){
18308 if(widget.destroyRecursive){
18309 widget.destroyRecursive(preserveDom);
18310 }
18311 });
18312 },
18313
18314 uninitialize: function(){
18315 // summary:
18316 // Deprecated. Override destroy() instead to implement custom widget tear-down
18317 // behavior.
18318 // tags:
18319 // protected
18320 return false;
18321 },
18322
18323 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
18324
18325 _setStyleAttr: function(/*String||Object*/ value){
18326 // summary:
18327 // Sets the style attribute of the widget according to value,
18328 // which is either a hash like {height: "5px", width: "3px"}
18329 // or a plain string
18330 // description:
18331 // Determines which node to set the style on based on style setting
18332 // in attributeMap.
18333 // tags:
18334 // protected
18335
18336 var mapNode = this.domNode;
18337
18338 // Note: technically we should revert any style setting made in a previous call
18339 // to his method, but that's difficult to keep track of.
18340
18341 if(lang.isObject(value)){
18342 domStyle.set(mapNode, value);
18343 }else{
18344 if(mapNode.style.cssText){
18345 mapNode.style.cssText += "; " + value;
18346 }else{
18347 mapNode.style.cssText = value;
18348 }
18349 }
18350
18351 this._set("style", value);
18352 },
18353
18354 _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
18355 // summary:
18356 // Reflect a widget attribute (title, tabIndex, duration etc.) to
18357 // the widget DOM, as specified by commands parameter.
18358 // If commands isn't specified then it's looked up from attributeMap.
18359 // Note some attributes like "type"
18360 // cannot be processed this way as they are not mutable.
18361 // attr:
18362 // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
18363 // to DOMNode inside the widget, or alternately pointing to a subwidget
18364 // tags:
18365 // private
18366
18367 commands = arguments.length >= 3 ? commands : this.attributeMap[attr];
18368
18369 array.forEach(lang.isArray(commands) ? commands : [commands], function(command){
18370
18371 // Get target node and what we are doing to that node
18372 var mapNode = this[command.node || command || "domNode"]; // DOM node
18373 var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
18374
18375 switch(type){
18376 case "attribute":
18377 if(lang.isFunction(value)){ // functions execute in the context of the widget
18378 value = lang.hitch(this, value);
18379 }
18380
18381 // Get the name of the DOM node attribute; usually it's the same
18382 // as the name of the attribute in the widget (attr), but can be overridden.
18383 // Also maps handler names to lowercase, like onSubmit --> onsubmit
18384 var attrName = command.attribute ? command.attribute :
18385 (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
18386
18387 if(mapNode.tagName){
18388 // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
18389 // method, but for consistency we still call domAttr
18390 domAttr.set(mapNode, attrName, value);
18391 }else{
18392 // mapping to a sub-widget
18393 mapNode.set(attrName, value);
18394 }
18395 break;
18396 case "innerText":
18397 mapNode.innerHTML = "";
18398 mapNode.appendChild(this.ownerDocument.createTextNode(value));
18399 break;
18400 case "innerHTML":
18401 mapNode.innerHTML = value;
18402 break;
18403 case "class":
18404 domClass.replace(mapNode, value, this[attr]);
18405 break;
18406 }
18407 }, this);
18408 },
18409
18410 get: function(name){
18411 // summary:
18412 // Get a property from a widget.
18413 // name:
18414 // The property to get.
18415 // description:
18416 // Get a named property from a widget. The property may
18417 // potentially be retrieved via a getter method. If no getter is defined, this
18418 // just retrieves the object's property.
18419 //
18420 // For example, if the widget has properties `foo` and `bar`
18421 // and a method named `_getFooAttr()`, calling:
18422 // `myWidget.get("foo")` would be equivalent to calling
18423 // `widget._getFooAttr()` and `myWidget.get("bar")`
18424 // would be equivalent to the expression
18425 // `widget.bar2`
18426 var names = this._getAttrNames(name);
18427 return this[names.g] ? this[names.g]() : this[name];
18428 },
18429
18430 set: function(name, value){
18431 // summary:
18432 // Set a property on a widget
18433 // name:
18434 // The property to set.
18435 // value:
18436 // The value to set in the property.
18437 // description:
18438 // Sets named properties on a widget which may potentially be handled by a
18439 // setter in the widget.
18440 //
18441 // For example, if the widget has properties `foo` and `bar`
18442 // and a method named `_setFooAttr()`, calling
18443 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
18444 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
18445 // would be equivalent to the statement `widget.bar = 3;`
18446 //
18447 // set() may also be called with a hash of name/value pairs, ex:
18448 //
18449 // | myWidget.set({
18450 // | foo: "Howdy",
18451 // | bar: 3
18452 // | });
18453 //
18454 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
18455
18456 if(typeof name === "object"){
18457 for(var x in name){
18458 this.set(x, name[x]);
18459 }
18460 return this;
18461 }
18462 var names = this._getAttrNames(name),
18463 setter = this[names.s];
18464 if(lang.isFunction(setter)){
18465 // use the explicit setter
18466 var result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
18467 }else{
18468 // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
18469 // Map according to:
18470 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
18471 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
18472 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
18473 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
18474 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
18475 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
18476 var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode",
18477 tag = this[defaultNode].tagName,
18478 attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])),
18479 map = name in this.attributeMap ? this.attributeMap[name] :
18480 names.s in this ? this[names.s] :
18481 ((names.l in attrsForTag && typeof value != "function") ||
18482 /^aria-|^data-|^role$/.test(name)) ? defaultNode : null;
18483 if(map != null){
18484 this._attrToDom(name, value, map);
18485 }
18486 this._set(name, value);
18487 }
18488 return result || this;
18489 },
18490
18491 _attrPairNames: {}, // shared between all widgets
18492 _getAttrNames: function(name){
18493 // summary:
18494 // Helper function for get() and set().
18495 // Caches attribute name values so we don't do the string ops every time.
18496 // tags:
18497 // private
18498
18499 var apn = this._attrPairNames;
18500 if(apn[name]){ return apn[name]; }
18501 var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); });
18502 return (apn[name] = {
18503 n: name+"Node",
18504 s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
18505 g: "_get"+uc+"Attr",
18506 l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
18507 });
18508 },
18509
18510 _set: function(/*String*/ name, /*anything*/ value){
18511 // summary:
18512 // Helper function to set new value for specified attribute, and call handlers
18513 // registered with watch() if the value has changed.
18514 var oldValue = this[name];
18515 this[name] = value;
18516 if(this._created && value !== oldValue){
18517 if(this._watchCallbacks){
18518 this._watchCallbacks(name, oldValue, value);
18519 }
18520 this.emit("attrmodified-" + name, {
18521 detail: {
18522 prevValue: oldValue,
18523 newValue: value
18524 }
18525 });
18526 }
18527 },
18528
18529 emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){
18530 // summary:
18531 // Used by widgets to signal that a synthetic event occurred, ex:
18532 // | myWidget.emit("attrmodified-selectedChildWidget", {}).
18533 //
18534 // Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
18535 // Also calls onType() method, if present, and returns value from that method.
18536 // By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
18537 // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
18538 // tags:
18539 // protected
18540
18541 // Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
18542 // Also set pointer to widget, although since we can't add a pointer to the widget for native events
18543 // (see #14729), maybe we shouldn't do it here?
18544 eventObj = eventObj || {};
18545 if(eventObj.bubbles === undefined){ eventObj.bubbles = true; }
18546 if(eventObj.cancelable === undefined){ eventObj.cancelable = true; }
18547 if(!eventObj.detail){ eventObj.detail = {}; }
18548 eventObj.detail.widget = this;
18549
18550 var ret, callback = this["on"+type];
18551 if(callback){
18552 ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]);
18553 }
18554
18555 // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
18556 if(this._started && !this._beingDestroyed){
18557 on.emit(this.domNode, type.toLowerCase(), eventObj);
18558 }
18559
18560 return ret;
18561 },
18562
18563 on: function(/*String|Function*/ type, /*Function*/ func){
18564 // summary:
18565 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
18566 // type:
18567 // Name of event (ex: "click") or extension event like touch.press.
18568 // description:
18569 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
18570 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
18571 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
18572
18573 // For backwards compatibility, if there's an onType() method in the widget then connect to that.
18574 // Remove in 2.0.
18575 var widgetMethod = this._onMap(type);
18576 if(widgetMethod){
18577 return aspect.after(this, widgetMethod, func, true);
18578 }
18579
18580 // Otherwise, just listen for the event on this.domNode.
18581 return this.own(on(this.domNode, type, func))[0];
18582 },
18583
18584 _onMap: function(/*String|Function*/ type){
18585 // summary:
18586 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
18587 // If type is a synthetic event like touch.press then returns undefined.
18588 var ctor = this.constructor, map = ctor._onMap;
18589 if(!map){
18590 map = (ctor._onMap = {});
18591 for(var attr in ctor.prototype){
18592 if(/^on/.test(attr)){
18593 map[attr.replace(/^on/, "").toLowerCase()] = attr;
18594 }
18595 }
18596 }
18597 return map[typeof type == "string" && type.toLowerCase()]; // String
18598 },
18599
18600 toString: function(){
18601 // summary:
18602 // Returns a string that represents the widget
18603 // description:
18604 // When a widget is cast to a string, this method will be used to generate the
18605 // output. Currently, it does not implement any sort of reversible
18606 // serialization.
18607 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
18608 },
18609
18610 getChildren: function(){
18611 // summary:
18612 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
18613 // Does not return nested widgets, nor widgets that are part of this widget's template.
18614 return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit/_WidgetBase[]
18615 },
18616
18617 getParent: function(){
18618 // summary:
18619 // Returns the parent widget of this widget
18620 return registry.getEnclosingWidget(this.domNode.parentNode);
18621 },
18622
18623 connect: function(
18624 /*Object|null*/ obj,
18625 /*String|Function*/ event,
18626 /*String|Function*/ method){
18627 // summary:
18628 // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
18629 //
18630 // Connects specified obj/event to specified method of this object
18631 // and registers for disconnect() on widget destroy.
18632 //
18633 // Provide widget-specific analog to dojo.connect, except with the
18634 // implicit use of this widget as the target object.
18635 // Events connected with `this.connect` are disconnected upon
18636 // destruction.
18637 // returns:
18638 // A handle that can be passed to `disconnect` in order to disconnect before
18639 // the widget is destroyed.
18640 // example:
18641 // | var btn = new Button();
18642 // | // when foo.bar() is called, call the listener we're going to
18643 // | // provide in the scope of btn
18644 // | btn.connect(foo, "bar", function(){
18645 // | console.debug(this.toString());
18646 // | });
18647 // tags:
18648 // protected
18649
18650 return this.own(connect.connect(obj, event, this, method))[0]; // handle
18651 },
18652
18653 disconnect: function(handle){
18654 // summary:
18655 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18656 //
18657 // Disconnects handle created by `connect`.
18658 // tags:
18659 // protected
18660
18661 handle.remove();
18662 },
18663
18664 subscribe: function(t, method){
18665 // summary:
18666 // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
18667 //
18668 // Subscribes to the specified topic and calls the specified method
18669 // of this object and registers for unsubscribe() on widget destroy.
18670 //
18671 // Provide widget-specific analog to dojo.subscribe, except with the
18672 // implicit use of this widget as the target object.
18673 // t: String
18674 // The topic
18675 // method: Function
18676 // The callback
18677 // example:
18678 // | var btn = new Button();
18679 // | // when /my/topic is published, this button changes its label to
18680 // | // be the parameter of the topic.
18681 // | btn.subscribe("/my/topic", function(v){
18682 // | this.set("label", v);
18683 // | });
18684 // tags:
18685 // protected
18686 return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle
18687 },
18688
18689 unsubscribe: function(/*Object*/ handle){
18690 // summary:
18691 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18692 //
18693 // Unsubscribes handle created by this.subscribe.
18694 // Also removes handle from this widget's list of subscriptions
18695 // tags:
18696 // protected
18697
18698 handle.remove();
18699 },
18700
18701 isLeftToRight: function(){
18702 // summary:
18703 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
18704 // tags:
18705 // protected
18706 return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean
18707 },
18708
18709 isFocusable: function(){
18710 // summary:
18711 // Return true if this widget can currently be focused
18712 // and false if not
18713 return this.focus && (domStyle.get(this.domNode, "display") != "none");
18714 },
18715
18716 placeAt: function(/* String|DomNode|_Widget */ reference, /* String|Int? */ position){
18717 // summary:
18718 // Place this widget somewhere in the DOM based
18719 // on standard domConstruct.place() conventions.
18720 // description:
18721 // A convenience function provided in all _Widgets, providing a simple
18722 // shorthand mechanism to put an existing (or newly created) Widget
18723 // somewhere in the dom, and allow chaining.
18724 // reference:
18725 // Widget, DOMNode, or id of widget or DOMNode
18726 // position:
18727 // If reference is a widget (or id of widget), and that widget has an ".addChild" method,
18728 // it will be called passing this widget instance into that method, supplying the optional
18729 // position index passed. In this case position (if specified) should be an integer.
18730 //
18731 // If reference is a DOMNode (or id matching a DOMNode but not a widget),
18732 // the position argument can be a numeric index or a string
18733 // "first", "last", "before", or "after", same as dojo/dom-construct::place().
18734 // returns: dijit/_WidgetBase
18735 // Provides a useful return of the newly created dijit._Widget instance so you
18736 // can "chain" this function by instantiating, placing, then saving the return value
18737 // to a variable.
18738 // example:
18739 // | // create a Button with no srcNodeRef, and place it in the body:
18740 // | var button = new Button({ label:"click" }).placeAt(win.body());
18741 // | // now, 'button' is still the widget reference to the newly created button
18742 // | button.on("click", function(e){ console.log('click'); }));
18743 // example:
18744 // | // create a button out of a node with id="src" and append it to id="wrapper":
18745 // | var button = new Button({},"src").placeAt("wrapper");
18746 // example:
18747 // | // place a new button as the first element of some div
18748 // | var button = new Button({ label:"click" }).placeAt("wrapper","first");
18749 // example:
18750 // | // create a contentpane and add it to a TabContainer
18751 // | var tc = dijit.byId("myTabs");
18752 // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
18753
18754 var refWidget = !reference.tagName && registry.byId(reference);
18755 if(refWidget && refWidget.addChild && (!position || typeof position === "number")){
18756 // Adding this to refWidget and can use refWidget.addChild() to handle everything.
18757 refWidget.addChild(this, position);
18758 }else{
18759 // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
18760 // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
18761 // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
18762 var ref = refWidget ?
18763 (refWidget.containerNode && !/after|before|replace/.test(position||"") ?
18764 refWidget.containerNode : refWidget.domNode) : dom.byId(reference, this.ownerDocument);
18765 domConstruct.place(this.domNode, ref, position);
18766
18767 // Start this iff it has a parent widget that's already started.
18768 if(!this._started && (this.getParent() || {})._started){
18769 this.startup();
18770 }
18771 }
18772 return this;
18773 },
18774
18775 getTextDir: function(/*String*/ text,/*String*/ originalDir){
18776 // summary:
18777 // Return direction of the text.
18778 // The function overridden in the _BidiSupport module,
18779 // its main purpose is to calculate the direction of the
18780 // text, if was defined by the programmer through textDir.
18781 // tags:
18782 // protected.
18783 return originalDir;
18784 },
18785
18786 applyTextDir: function(/*===== element, text =====*/){
18787 // summary:
18788 // The function overridden in the _BidiSupport module,
18789 // originally used for setting element.dir according to this.textDir.
18790 // In this case does nothing.
18791 // element: DOMNode
18792 // text: String
18793 // tags:
18794 // protected.
18795 },
18796
18797 defer: function(fcn, delay){
18798 // summary:
18799 // Wrapper to setTimeout to avoid deferred functions executing
18800 // after the originating widget has been destroyed.
18801 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
18802 // fcn: function reference
18803 // delay: Optional number (defaults to 0)
18804 // tags:
18805 // protected.
18806 var timer = setTimeout(lang.hitch(this,
18807 function(){
18808 timer = null;
18809 if(!this._destroyed){
18810 lang.hitch(this, fcn)();
18811 }
18812 }),
18813 delay || 0
18814 );
18815 return {
18816 remove: function(){
18817 if(timer){
18818 clearTimeout(timer);
18819 timer = null;
18820 }
18821 return null; // so this works well: handle = handle.remove();
18822 }
18823 };
18824 }
18825 });
18826
18827 });
18828
18829 },
18830 'dijit/layout/_TabContainerBase':function(){
18831 require({cache:{
18832 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}});
18833 define("dijit/layout/_TabContainerBase", [
18834 "dojo/text!./templates/TabContainer.html",
18835 "./StackContainer",
18836 "./utils", // marginBox2contextBox, layoutChildren
18837 "../_TemplatedMixin",
18838 "dojo/_base/declare", // declare
18839 "dojo/dom-class", // domClass.add
18840 "dojo/dom-geometry", // domGeometry.contentBox
18841 "dojo/dom-style" // domStyle.style
18842 ], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){
18843
18844 // module:
18845 // dijit/layout/_TabContainerBase
18846
18847
18848 return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
18849 // summary:
18850 // Abstract base class for TabContainer. Must define _makeController() to instantiate
18851 // and return the widget that displays the tab labels
18852 // description:
18853 // A TabContainer is a container that has multiple panes, but shows only
18854 // one pane at a time. There are a set of tabs corresponding to each pane,
18855 // where each tab has the name (aka title) of the pane, and optionally a close button.
18856
18857 // tabPosition: String
18858 // Defines where tabs go relative to tab content.
18859 // "top", "bottom", "left-h", "right-h"
18860 tabPosition: "top",
18861
18862 baseClass: "dijitTabContainer",
18863
18864 // tabStrip: [const] Boolean
18865 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
18866 // around the set of tabs. Not supported by claro theme.
18867 tabStrip: false,
18868
18869 // nested: [const] Boolean
18870 // If true, use styling for a TabContainer nested inside another TabContainer.
18871 // For tundra etc., makes tabs look like links, and hides the outer
18872 // border since the outer TabContainer already has a border.
18873 nested: false,
18874
18875 templateString: template,
18876
18877 postMixInProperties: function(){
18878 // set class name according to tab position, ex: dijitTabContainerTop
18879 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
18880
18881 this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden");
18882
18883 this.inherited(arguments);
18884 },
18885
18886 buildRendering: function(){
18887 this.inherited(arguments);
18888
18889 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
18890 this.tablist = this._makeController(this.tablistNode);
18891
18892 if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); }
18893
18894 if(this.nested){
18895 /* workaround IE's lack of support for "a > b" selectors by
18896 * tagging each node in the template.
18897 */
18898 domClass.add(this.domNode, "dijitTabContainerNested");
18899 domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested");
18900 domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested");
18901 domClass.add(this.containerNode, "dijitTabPaneWrapperNested");
18902 }else{
18903 domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
18904 }
18905 },
18906
18907 _setupChild: function(/*dijit/_WidgetBase*/ tab){
18908 // Overrides StackContainer._setupChild().
18909 domClass.add(tab.domNode, "dijitTabPane");
18910 this.inherited(arguments);
18911 },
18912
18913 startup: function(){
18914 if(this._started){ return; }
18915
18916 // wire up the tablist and its tabs
18917 this.tablist.startup();
18918
18919 this.inherited(arguments);
18920 },
18921
18922 layout: function(){
18923 // Overrides StackContainer.layout().
18924 // Configure the content pane to take up all the space except for where the tabs are
18925
18926 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
18927
18928 var sc = this.selectedChildWidget;
18929
18930 if(this.doLayout){
18931 // position and size the titles and the container node
18932 var titleAlign = this.tabPosition.replace(/-h/, "");
18933 this.tablist.layoutAlign = titleAlign;
18934 var children = [this.tablist, {
18935 domNode: this.tablistSpacer,
18936 layoutAlign: titleAlign
18937 }, {
18938 domNode: this.containerNode,
18939 layoutAlign: "client"
18940 }];
18941 layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
18942
18943 // Compute size to make each of my children.
18944 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
18945 this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
18946
18947 if(sc && sc.resize){
18948 sc.resize(this._containerContentBox);
18949 }
18950 }else{
18951 // just layout the tab controller, so it can position left/right buttons etc.
18952 if(this.tablist.resize){
18953 //make the tabs zero width so that they don't interfere with width calc, then reset
18954 var s = this.tablist.domNode.style;
18955 s.width="0";
18956 var width = domGeometry.getContentBox(this.domNode).w;
18957 s.width="";
18958 this.tablist.resize({w: width});
18959 }
18960
18961 // and call resize() on the selected pane just to tell it that it's been made visible
18962 if(sc && sc.resize){
18963 sc.resize();
18964 }
18965 }
18966 },
18967
18968 destroy: function(){
18969 if(this.tablist){
18970 this.tablist.destroy();
18971 }
18972 this.inherited(arguments);
18973 }
18974 });
18975
18976 });
18977
18978 },
18979 'dijit/form/Form':function(){
18980 define("dijit/form/Form", [
18981 "dojo/_base/declare", // declare
18982 "dojo/dom-attr", // domAttr.set
18983 "dojo/_base/event", // event.stop
18984 "dojo/_base/kernel", // kernel.deprecated
18985 "dojo/sniff", // has("ie")
18986 "../_Widget",
18987 "../_TemplatedMixin",
18988 "./_FormMixin",
18989 "../layout/_ContentPaneResizeMixin"
18990 ], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
18991
18992 // module:
18993 // dijit/form/Form
18994
18995
18996 return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
18997 // summary:
18998 // Widget corresponding to HTML form tag, for validation and serialization
18999 //
19000 // example:
19001 // | <form data-dojo-type="dijit/form/Form" id="myForm">
19002 // | Name: <input type="text" name="name" />
19003 // | </form>
19004 // | myObj = {name: "John Doe"};
19005 // | dijit.byId('myForm').set('value', myObj);
19006 // |
19007 // | myObj=dijit.byId('myForm').get('value');
19008
19009 // HTML <FORM> attributes
19010
19011 // name: String?
19012 // Name of form for scripting.
19013 name: "",
19014
19015 // action: String?
19016 // Server-side form handler.
19017 action: "",
19018
19019 // method: String?
19020 // HTTP method used to submit the form, either "GET" or "POST".
19021 method: "",
19022
19023 // encType: String?
19024 // Encoding type for the form, ex: application/x-www-form-urlencoded.
19025 encType: "",
19026
19027 // accept-charset: String?
19028 // List of supported charsets.
19029 "accept-charset": "",
19030
19031 // accept: String?
19032 // List of MIME types for file upload.
19033 accept: "",
19034
19035 // target: String?
19036 // Target frame for the document to be opened in.
19037 target: "",
19038
19039 templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
19040
19041 postMixInProperties: function(){
19042 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
19043 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
19044 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
19045 this.inherited(arguments);
19046 },
19047
19048 execute: function(/*Object*/ /*===== formContents =====*/){
19049 // summary:
19050 // Deprecated: use submit()
19051 // tags:
19052 // deprecated
19053 },
19054
19055 onExecute: function(){
19056 // summary:
19057 // Deprecated: use onSubmit()
19058 // tags:
19059 // deprecated
19060 },
19061
19062 _setEncTypeAttr: function(/*String*/ value){
19063 this.encType = value;
19064 domAttr.set(this.domNode, "encType", value);
19065 if(has("ie")){ this.domNode.encoding = value; }
19066 },
19067
19068 reset: function(/*Event?*/ e){
19069 // summary:
19070 // restores all widget values back to their init values,
19071 // calls onReset() which can cancel the reset by returning false
19072
19073 // create fake event so we can know if preventDefault() is called
19074 var faux = {
19075 returnValue: true, // the IE way
19076 preventDefault: function(){ // not IE
19077 this.returnValue = false;
19078 },
19079 stopPropagation: function(){},
19080 currentTarget: e ? e.target : this.domNode,
19081 target: e ? e.target : this.domNode
19082 };
19083 // if return value is not exactly false, and haven't called preventDefault(), then reset
19084 if(!(this.onReset(faux) === false) && faux.returnValue){
19085 this.inherited(arguments, []);
19086 }
19087 },
19088
19089 onReset: function(/*Event?*/ /*===== e =====*/){
19090 // summary:
19091 // Callback when user resets the form. This method is intended
19092 // to be over-ridden. When the `reset` method is called
19093 // programmatically, the return value from `onReset` is used
19094 // to compute whether or not resetting should proceed
19095 // tags:
19096 // callback
19097 return true; // Boolean
19098 },
19099
19100 _onReset: function(e){
19101 this.reset(e);
19102 event.stop(e);
19103 return false;
19104 },
19105
19106 _onSubmit: function(e){
19107 var fp = this.constructor.prototype;
19108 // TODO: remove this if statement beginning with 2.0
19109 if(this.execute != fp.execute || this.onExecute != fp.onExecute){
19110 kernel.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
19111 this.onExecute();
19112 this.execute(this.getValues());
19113 }
19114 if(this.onSubmit(e) === false){ // only exactly false stops submit
19115 event.stop(e);
19116 }
19117 },
19118
19119 onSubmit: function(/*Event?*/ /*===== e =====*/){
19120 // summary:
19121 // Callback when user submits the form.
19122 // description:
19123 // This method is intended to be over-ridden, but by default it checks and
19124 // returns the validity of form elements. When the `submit`
19125 // method is called programmatically, the return value from
19126 // `onSubmit` is used to compute whether or not submission
19127 // should proceed
19128 // tags:
19129 // extension
19130
19131 return this.isValid(); // Boolean
19132 },
19133
19134 submit: function(){
19135 // summary:
19136 // programmatically submit form if and only if the `onSubmit` returns true
19137 if(!(this.onSubmit() === false)){
19138 this.containerNode.submit();
19139 }
19140 }
19141 });
19142 });
19143
19144 },
19145 'dojo/store/Memory':function(){
19146 define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
19147 function(declare, QueryResults, SimpleQueryEngine /*=====, Store =====*/){
19148
19149 // module:
19150 // dojo/store/Memory
19151
19152 // No base class, but for purposes of documentation, the base class is dojo/store/api/Store
19153 var base = null;
19154 /*===== base = Store; =====*/
19155
19156 return declare("dojo.store.Memory", base, {
19157 // summary:
19158 // This is a basic in-memory object store. It implements dojo/store/api/Store.
19159 constructor: function(options){
19160 // summary:
19161 // Creates a memory object store.
19162 // options: dojo/store/Memory
19163 // This provides any configuration information that will be mixed into the store.
19164 // This should generally include the data property to provide the starting set of data.
19165 for(var i in options){
19166 this[i] = options[i];
19167 }
19168 this.setData(this.data || []);
19169 },
19170 // data: Array
19171 // The array of all the objects in the memory store
19172 data:null,
19173
19174 // idProperty: String
19175 // Indicates the property to use as the identity property. The values of this
19176 // property should be unique.
19177 idProperty: "id",
19178
19179 // index: Object
19180 // An index of data indices into the data array by id
19181 index:null,
19182
19183 // queryEngine: Function
19184 // Defines the query engine to use for querying the data store
19185 queryEngine: SimpleQueryEngine,
19186 get: function(id){
19187 // summary:
19188 // Retrieves an object by its identity
19189 // id: Number
19190 // The identity to use to lookup the object
19191 // returns: Object
19192 // The object in the store that matches the given id.
19193 return this.data[this.index[id]];
19194 },
19195 getIdentity: function(object){
19196 // summary:
19197 // Returns an object's identity
19198 // object: Object
19199 // The object to get the identity from
19200 // returns: Number
19201 return object[this.idProperty];
19202 },
19203 put: function(object, options){
19204 // summary:
19205 // Stores an object
19206 // object: Object
19207 // The object to store.
19208 // options: dojo/store/api/Store.PutDirectives?
19209 // Additional metadata for storing the data. Includes an "id"
19210 // property if a specific id is to be used.
19211 // returns: Number
19212 var data = this.data,
19213 index = this.index,
19214 idProperty = this.idProperty;
19215 var id = object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
19216 if(id in index){
19217 // object exists
19218 if(options && options.overwrite === false){
19219 throw new Error("Object already exists");
19220 }
19221 // replace the entry in data
19222 data[index[id]] = object;
19223 }else{
19224 // add the new object
19225 index[id] = data.push(object) - 1;
19226 }
19227 return id;
19228 },
19229 add: function(object, options){
19230 // summary:
19231 // Creates an object, throws an error if the object already exists
19232 // object: Object
19233 // The object to store.
19234 // options: dojo/store/api/Store.PutDirectives?
19235 // Additional metadata for storing the data. Includes an "id"
19236 // property if a specific id is to be used.
19237 // returns: Number
19238 (options = options || {}).overwrite = false;
19239 // call put with overwrite being false
19240 return this.put(object, options);
19241 },
19242 remove: function(id){
19243 // summary:
19244 // Deletes an object by its identity
19245 // id: Number
19246 // The identity to use to delete the object
19247 // returns: Boolean
19248 // Returns true if an object was removed, falsy (undefined) if no object matched the id
19249 var index = this.index;
19250 var data = this.data;
19251 if(id in index){
19252 data.splice(index[id], 1);
19253 // now we have to reindex
19254 this.setData(data);
19255 return true;
19256 }
19257 },
19258 query: function(query, options){
19259 // summary:
19260 // Queries the store for objects.
19261 // query: Object
19262 // The query to use for retrieving objects from the store.
19263 // options: dojo/store/api/Store.QueryOptions?
19264 // The optional arguments to apply to the resultset.
19265 // returns: dojo/store/api/Store.QueryResults
19266 // The results of the query, extended with iterative methods.
19267 //
19268 // example:
19269 // Given the following store:
19270 //
19271 // | var store = new Memory({
19272 // | data: [
19273 // | {id: 1, name: "one", prime: false },
19274 // | {id: 2, name: "two", even: true, prime: true},
19275 // | {id: 3, name: "three", prime: true},
19276 // | {id: 4, name: "four", even: true, prime: false},
19277 // | {id: 5, name: "five", prime: true}
19278 // | ]
19279 // | });
19280 //
19281 // ...find all items where "prime" is true:
19282 //
19283 // | var results = store.query({ prime: true });
19284 //
19285 // ...or find all items where "even" is true:
19286 //
19287 // | var results = store.query({ even: true });
19288 return QueryResults(this.queryEngine(query, options)(this.data));
19289 },
19290 setData: function(data){
19291 // summary:
19292 // Sets the given data as the source for this store, and indexes it
19293 // data: Object[]
19294 // An array of objects to use as the source of data.
19295 if(data.items){
19296 // just for convenience with the data format IFRS expects
19297 this.idProperty = data.identifier;
19298 data = this.data = data.items;
19299 }else{
19300 this.data = data;
19301 }
19302 this.index = {};
19303 for(var i = 0, l = data.length; i < l; i++){
19304 this.index[data[i][this.idProperty]] = i;
19305 }
19306 }
19307 });
19308
19309 });
19310
19311 },
19312 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n",
19313 'dijit/_base/sniff':function(){
19314 define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
19315
19316 // module:
19317 // dijit/_base/sniff
19318
19319 /*=====
19320 return {
19321 // summary:
19322 // Deprecated, back compatibility module, new code should require dojo/uacss directly instead of this module.
19323 };
19324 =====*/
19325 });
19326
19327 },
19328 'dijit/Toolbar':function(){
19329 define("dijit/Toolbar", [
19330 "require",
19331 "dojo/_base/declare", // declare
19332 "dojo/has",
19333 "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
19334 "dojo/ready",
19335 "./_Widget",
19336 "./_KeyNavContainer",
19337 "./_TemplatedMixin"
19338 ], function(require, declare, has, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
19339
19340 // module:
19341 // dijit/Toolbar
19342
19343
19344 // Back compat w/1.6, remove for 2.0
19345 if(has("dijit-legacy-requires")){
19346 ready(0, function(){
19347 var requires = ["dijit/ToolbarSeparator"];
19348 require(requires); // use indirection so modules not rolled into a build
19349 });
19350 }
19351
19352 return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
19353 // summary:
19354 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
19355
19356 templateString:
19357 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
19358 '</div>',
19359
19360 baseClass: "dijitToolbar",
19361
19362 postCreate: function(){
19363 this.inherited(arguments);
19364
19365 this.connectKeyNavHandlers(
19366 this.isLeftToRight() ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
19367 this.isLeftToRight() ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
19368 );
19369 }
19370 });
19371 });
19372
19373 },
19374 'dijit/layout/StackContainer':function(){
19375 define("dijit/layout/StackContainer", [
19376 "dojo/_base/array", // array.forEach array.indexOf array.some
19377 "dojo/cookie", // cookie
19378 "dojo/_base/declare", // declare
19379 "dojo/dom-class", // domClass.add domClass.replace
19380 "dojo/has", // has("dijit-legacy-requires")
19381 "dojo/_base/lang", // lang.extend
19382 "dojo/ready",
19383 "dojo/topic", // publish
19384 "../registry", // registry.byId
19385 "../_WidgetBase",
19386 "./_LayoutWidget",
19387 "dojo/i18n!../nls/common"
19388 ], function(array, cookie, declare, domClass, has, lang, ready, topic,
19389 registry, _WidgetBase, _LayoutWidget){
19390
19391 // module:
19392 // dijit/layout/StackContainer
19393
19394 // Back compat w/1.6, remove for 2.0
19395 if(has("dijit-legacy-requires")){
19396 ready(0, function(){
19397 var requires = ["dijit/layout/StackController"];
19398 require(requires); // use indirection so modules not rolled into a build
19399 });
19400 }
19401
19402 var StackContainer = declare("dijit.layout.StackContainer", _LayoutWidget, {
19403 // summary:
19404 // A container that has multiple children, but shows only
19405 // one child at a time
19406 //
19407 // description:
19408 // A container for widgets (ContentPanes, for example) That displays
19409 // only one Widget at a time.
19410 //
19411 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
19412 //
19413 // Can be base class for container, Wizard, Show, etc.
19414 //
19415 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
19416 // children of a `StackContainer`.
19417
19418 // doLayout: Boolean
19419 // If true, change the size of my currently displayed child to match my size
19420 doLayout: true,
19421
19422 // persist: Boolean
19423 // Remembers the selected child across sessions
19424 persist: false,
19425
19426 baseClass: "dijitStackContainer",
19427
19428 /*=====
19429 // selectedChildWidget: [readonly] dijit._Widget
19430 // References the currently selected child widget, if any.
19431 // Adjust selected child with selectChild() method.
19432 selectedChildWidget: null,
19433 =====*/
19434
19435 buildRendering: function(){
19436 this.inherited(arguments);
19437 domClass.add(this.domNode, "dijitLayoutContainer");
19438 this.containerNode.setAttribute("role", "tabpanel");
19439 },
19440
19441 postCreate: function(){
19442 this.inherited(arguments);
19443 this.connect(this.domNode, "onkeypress", this._onKeyPress);
19444 },
19445
19446 startup: function(){
19447 if(this._started){ return; }
19448
19449 var children = this.getChildren();
19450
19451 // Setup each page panel to be initially hidden
19452 array.forEach(children, this._setupChild, this);
19453
19454 // Figure out which child to initially display, defaulting to first one
19455 if(this.persist){
19456 this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
19457 }else{
19458 array.some(children, function(child){
19459 if(child.selected){
19460 this.selectedChildWidget = child;
19461 }
19462 return child.selected;
19463 }, this);
19464 }
19465 var selected = this.selectedChildWidget;
19466 if(!selected && children[0]){
19467 selected = this.selectedChildWidget = children[0];
19468 selected.selected = true;
19469 }
19470
19471 // Publish information about myself so any StackControllers can initialize.
19472 // This needs to happen before this.inherited(arguments) so that for
19473 // TabContainer, this._contentBox doesn't include the space for the tab labels.
19474 topic.publish(this.id+"-startup", {children: children, selected: selected});
19475
19476 // Startup each child widget, and do initial layout like setting this._contentBox,
19477 // then calls this.resize() which does the initial sizing on the selected child.
19478 this.inherited(arguments);
19479 },
19480
19481 resize: function(){
19482 // Overrides _LayoutWidget.resize()
19483 // Resize is called when we are first made visible (it's called from startup()
19484 // if we are initially visible). If this is the first time we've been made
19485 // visible then show our first child.
19486 if(!this._hasBeenShown){
19487 this._hasBeenShown = true;
19488 var selected = this.selectedChildWidget;
19489 if(selected){
19490 this._showChild(selected);
19491 }
19492 }
19493 this.inherited(arguments);
19494 },
19495
19496 _setupChild: function(/*dijit/_WidgetBase*/ child){
19497 // Overrides _LayoutWidget._setupChild()
19498
19499 this.inherited(arguments);
19500
19501 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
19502
19503 // remove the title attribute so it doesn't show up when i hover
19504 // over a node
19505 child.domNode.title = "";
19506 },
19507
19508 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
19509 // Overrides _Container.addChild() to do layout and publish events
19510
19511 this.inherited(arguments);
19512
19513 if(this._started){
19514 topic.publish(this.id+"-addChild", child, insertIndex); // publish
19515
19516 // in case the tab titles have overflowed from one line to two lines
19517 // (or, if this if first child, from zero lines to one line)
19518 // TODO: w/ScrollingTabController this is no longer necessary, although
19519 // ScrollTabController.resize() does need to get called to show/hide
19520 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
19521 // If this is updated to not layout [except for initial child added / last child removed], update
19522 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
19523 this.layout();
19524
19525 // if this is the first child, then select it
19526 if(!this.selectedChildWidget){
19527 this.selectChild(child);
19528 }
19529 }
19530 },
19531
19532 removeChild: function(/*dijit/_WidgetBase*/ page){
19533 // Overrides _Container.removeChild() to do layout and publish events
19534
19535 this.inherited(arguments);
19536
19537 if(this._started){
19538 // this will notify any tablists to remove a button; do this first because it may affect sizing
19539 topic.publish(this.id + "-removeChild", page); // publish
19540 }
19541
19542 // If all our children are being destroyed than don't run the code below (to select another page),
19543 // because we are deleting every page one by one
19544 if(this._descendantsBeingDestroyed){ return; }
19545
19546 // Select new page to display, also updating TabController to show the respective tab.
19547 // Do this before layout call because it can affect the height of the TabController.
19548 if(this.selectedChildWidget === page){
19549 this.selectedChildWidget = undefined;
19550 if(this._started){
19551 var children = this.getChildren();
19552 if(children.length){
19553 this.selectChild(children[0]);
19554 }
19555 }
19556 }
19557
19558 if(this._started){
19559 // In case the tab titles now take up one line instead of two lines
19560 // (note though that ScrollingTabController never overflows to multiple lines),
19561 // or the height has changed slightly because of addition/removal of tab which close icon
19562 this.layout();
19563 }
19564 },
19565
19566 selectChild: function(/*dijit/_WidgetBase|String*/ page, /*Boolean*/ animate){
19567 // summary:
19568 // Show the given widget (which must be one of my children)
19569 // page:
19570 // Reference to child widget or id of child widget
19571
19572 page = registry.byId(page);
19573
19574 if(this.selectedChildWidget != page){
19575 // Deselect old page and select new one
19576 var d = this._transition(page, this.selectedChildWidget, animate);
19577 this._set("selectedChildWidget", page);
19578 topic.publish(this.id+"-selectChild", page); // publish
19579
19580 if(this.persist){
19581 cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
19582 }
19583 }
19584
19585 return d; // If child has an href, promise that fires when the child's href finishes loading
19586 },
19587
19588 _transition: function(newWidget, oldWidget /*===== , animate =====*/){
19589 // summary:
19590 // Hide the old widget and display the new widget.
19591 // Subclasses should override this.
19592 // newWidget: dijit/_WidgetBase
19593 // The newly selected widget.
19594 // oldWidget: dijit/_WidgetBase
19595 // The previously selected widget.
19596 // animate: Boolean
19597 // Used by AccordionContainer to turn on/off slide effect.
19598 // tags:
19599 // protected extension
19600 if(oldWidget){
19601 this._hideChild(oldWidget);
19602 }
19603 var d = this._showChild(newWidget);
19604
19605 // Size the new widget, in case this is the first time it's being shown,
19606 // or I have been resized since the last time it was shown.
19607 // Note that page must be visible for resizing to work.
19608 if(newWidget.resize){
19609 if(this.doLayout){
19610 newWidget.resize(this._containerContentBox || this._contentBox);
19611 }else{
19612 // the child should pick it's own size but we still need to call resize()
19613 // (with no arguments) to let the widget lay itself out
19614 newWidget.resize();
19615 }
19616 }
19617
19618 return d; // If child has an href, promise that fires when the child's href finishes loading
19619 },
19620
19621 _adjacent: function(/*Boolean*/ forward){
19622 // summary:
19623 // Gets the next/previous child widget in this container from the current selection.
19624
19625 // TODO: remove for 2.0 if this isn't being used. Otherwise, fix to skip disabled tabs.
19626
19627 var children = this.getChildren();
19628 var index = array.indexOf(children, this.selectedChildWidget);
19629 index += forward ? 1 : children.length - 1;
19630 return children[ index % children.length ]; // dijit/_WidgetBase
19631 },
19632
19633 forward: function(){
19634 // summary:
19635 // Advance to next page.
19636 return this.selectChild(this._adjacent(true), true);
19637 },
19638
19639 back: function(){
19640 // summary:
19641 // Go back to previous page.
19642 return this.selectChild(this._adjacent(false), true);
19643 },
19644
19645 _onKeyPress: function(e){
19646 topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
19647 },
19648
19649 layout: function(){
19650 // Implement _LayoutWidget.layout() virtual method.
19651 var child = this.selectedChildWidget;
19652 if(child && child.resize){
19653 if(this.doLayout){
19654 child.resize(this._containerContentBox || this._contentBox);
19655 }else{
19656 child.resize();
19657 }
19658 }
19659 },
19660
19661 _showChild: function(/*dijit/_WidgetBase*/ page){
19662 // summary:
19663 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
19664 // it can do any updates it needs regarding loading href's etc.
19665 // returns:
19666 // Promise that fires when page has finished showing, or true if there's no href
19667 var children = this.getChildren();
19668 page.isFirstChild = (page == children[0]);
19669 page.isLastChild = (page == children[children.length-1]);
19670 page._set("selected", true);
19671
19672 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
19673
19674 return (page._onShow && page._onShow()) || true;
19675 },
19676
19677 _hideChild: function(/*dijit/_WidgetBase*/ page){
19678 // summary:
19679 // Hide the specified child by changing it's CSS, and call _onHide() so
19680 // it's notified.
19681 page._set("selected", false);
19682 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
19683
19684 page.onHide && page.onHide();
19685 },
19686
19687 closeChild: function(/*dijit/_WidgetBase*/ page){
19688 // summary:
19689 // Callback when user clicks the [X] to remove a page.
19690 // If onClose() returns true then remove and destroy the child.
19691 // tags:
19692 // private
19693 var remove = page.onClose(this, page);
19694 if(remove){
19695 this.removeChild(page);
19696 // makes sure we can clean up executeScripts in ContentPane onUnLoad
19697 page.destroyRecursive();
19698 }
19699 },
19700
19701 destroyDescendants: function(/*Boolean*/ preserveDom){
19702 this._descendantsBeingDestroyed = true;
19703 this.selectedChildWidget = undefined;
19704 array.forEach(this.getChildren(), function(child){
19705 if(!preserveDom){
19706 this.removeChild(child);
19707 }
19708 child.destroyRecursive(preserveDom);
19709 }, this);
19710 this._descendantsBeingDestroyed = false;
19711 }
19712 });
19713
19714 StackContainer.ChildWidgetProperties = {
19715 // summary:
19716 // These properties can be specified for the children of a StackContainer.
19717
19718 // selected: Boolean
19719 // Specifies that this widget should be the initially displayed pane.
19720 // Note: to change the selected child use `dijit/layout/StackContainer.selectChild`
19721 selected: false,
19722
19723 // disabled: Boolean
19724 // Specifies that the button to select this pane should be disabled.
19725 // Doesn't affect programmatic selection of the pane, nor does it deselect the pane if it is currently selected.
19726 disabled: false,
19727
19728 // closable: Boolean
19729 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
19730 closable: false,
19731
19732 // iconClass: String
19733 // CSS Class specifying icon to use in label associated with this pane.
19734 iconClass: "dijitNoIcon",
19735
19736 // showTitle: Boolean
19737 // When true, display title of this widget as tab label etc., rather than just using
19738 // icon specified in iconClass
19739 showTitle: true
19740 };
19741
19742 // Since any widget can be specified as a StackContainer child, mix them
19743 // into the base widget class. (This is a hack, but it's effective.)
19744 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
19745 lang.extend(_WidgetBase, /*===== {} || =====*/ StackContainer.ChildWidgetProperties);
19746
19747 return StackContainer;
19748 });
19749
19750 },
19751 'dojo/regexp':function(){
19752 define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang){
19753
19754 // module:
19755 // dojo/regexp
19756
19757 var regexp = {
19758 // summary:
19759 // Regular expressions and Builder resources
19760 };
19761 lang.setObject("dojo.regexp", regexp);
19762
19763 regexp.escapeString = function(/*String*/str, /*String?*/except){
19764 // summary:
19765 // Adds escape sequences for special characters in regular expressions
19766 // except:
19767 // a String with special characters to be left unescaped
19768
19769 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
19770 if(except && except.indexOf(ch) != -1){
19771 return ch;
19772 }
19773 return "\\" + ch;
19774 }); // String
19775 };
19776
19777 regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
19778 // summary:
19779 // Builds a regular expression that groups subexpressions
19780 // description:
19781 // A utility function used by some of the RE generators. The
19782 // subexpressions are constructed by the function, re, in the second
19783 // parameter. re builds one subexpression for each elem in the array
19784 // a, in the first parameter. Returns a string for a regular
19785 // expression that groups all the subexpressions.
19786 // arr:
19787 // A single value or an array of values.
19788 // re:
19789 // A function. Takes one parameter and converts it to a regular
19790 // expression.
19791 // nonCapture:
19792 // If true, uses non-capturing match, otherwise matches are retained
19793 // by regular expression. Defaults to false
19794
19795 // case 1: a is a single value.
19796 if(!(arr instanceof Array)){
19797 return re(arr); // String
19798 }
19799
19800 // case 2: a is an array
19801 var b = [];
19802 for(var i = 0; i < arr.length; i++){
19803 // convert each elem to a RE
19804 b.push(re(arr[i]));
19805 }
19806
19807 // join the REs as alternatives in a RE group.
19808 return regexp.group(b.join("|"), nonCapture); // String
19809 };
19810
19811 regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
19812 // summary:
19813 // adds group match to expression
19814 // nonCapture:
19815 // If true, uses non-capturing match, otherwise matches are retained
19816 // by regular expression.
19817 return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
19818 };
19819
19820 return regexp;
19821 });
19822
19823 },
19824 'dijit/DropDownMenu':function(){
19825 require({cache:{
19826 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
19827 define("dijit/DropDownMenu", [
19828 "dojo/_base/declare", // declare
19829 "dojo/_base/event", // event.stop
19830 "dojo/keys", // keys
19831 "dojo/text!./templates/Menu.html",
19832 "./_OnDijitClickMixin",
19833 "./_MenuBase"
19834 ], function(declare, event, keys, template, _OnDijitClickMixin, _MenuBase){
19835
19836 // module:
19837 // dijit/DropDownMenu
19838
19839 return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
19840 // summary:
19841 // A menu, without features for context menu (Meaning, drop down menu)
19842
19843 templateString: template,
19844
19845 baseClass: "dijitMenu",
19846
19847 postCreate: function(){
19848 this.inherited(arguments);
19849 var l = this.isLeftToRight();
19850 this._openSubMenuKey = l ? keys.RIGHT_ARROW : keys.LEFT_ARROW;
19851 this._closeSubMenuKey = l ? keys.LEFT_ARROW : keys.RIGHT_ARROW;
19852 this.connectKeyNavHandlers([keys.UP_ARROW], [keys.DOWN_ARROW]);
19853 },
19854
19855 _onKeyPress: function(/*Event*/ evt){
19856 // summary:
19857 // Handle keyboard based menu navigation.
19858 // tags:
19859 // protected
19860
19861 if(evt.ctrlKey || evt.altKey){ return; }
19862
19863 switch(evt.charOrCode){
19864 case this._openSubMenuKey:
19865 this._moveToPopup(evt);
19866 event.stop(evt);
19867 break;
19868 case this._closeSubMenuKey:
19869 if(this.parentMenu){
19870 if(this.parentMenu._isMenuBar){
19871 this.parentMenu.focusPrev();
19872 }else{
19873 this.onCancel(false);
19874 }
19875 }else{
19876 event.stop(evt);
19877 }
19878 break;
19879 }
19880 }
19881 });
19882 });
19883
19884 },
19885 'dijit/form/_FormMixin':function(){
19886 define("dijit/form/_FormMixin", [
19887 "dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
19888 "dojo/_base/declare", // declare
19889 "dojo/_base/kernel", // kernel.deprecated
19890 "dojo/_base/lang", // lang.hitch lang.isArray
19891 "dojo/on",
19892 "dojo/window" // winUtils.scrollIntoView
19893 ], function(array, declare, kernel, lang, on, winUtils){
19894
19895 // module:
19896 // dijit/form/_FormMixin
19897
19898 return declare("dijit.form._FormMixin", null, {
19899 // summary:
19900 // Mixin for containers of form widgets (i.e. widgets that represent a single value
19901 // and can be children of a `<form>` node or `dijit/form/Form` widget)
19902 // description:
19903 // Can extract all the form widgets
19904 // values and combine them into a single javascript object, or alternately
19905 // take such an object and set the values for all the contained
19906 // form widgets
19907
19908 /*=====
19909 // value: Object
19910 // Name/value hash for each child widget with a name and value.
19911 // Child widgets without names are not part of the hash.
19912 //
19913 // If there are multiple child widgets w/the same name, value is an array,
19914 // unless they are radio buttons in which case value is a scalar (since only
19915 // one radio button can be checked at a time).
19916 //
19917 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
19918 //
19919 // Example:
19920 // | { name: "John Smith", interests: ["sports", "movies"] }
19921 =====*/
19922
19923 // state: [readonly] String
19924 // Will be "Error" if one or more of the child widgets has an invalid value,
19925 // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
19926 // which indicates that the form is ready to be submitted.
19927 state: "",
19928
19929 // TODO:
19930 // * Repeater
19931 // * better handling for arrays. Often form elements have names with [] like
19932 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
19933
19934
19935 _getDescendantFormWidgets: function(/*dijit/_WidgetBase[]?*/ children){
19936 // summary:
19937 // Returns all form widget descendants, searching through non-form child widgets like BorderContainer
19938 var res = [];
19939 array.forEach(children || this.getChildren(), function(child){
19940 if("value" in child){
19941 res.push(child);
19942 }else{
19943 res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
19944 }
19945 }, this);
19946 return res;
19947 },
19948
19949 reset: function(){
19950 array.forEach(this._getDescendantFormWidgets(), function(widget){
19951 if(widget.reset){
19952 widget.reset();
19953 }
19954 });
19955 },
19956
19957 validate: function(){
19958 // summary:
19959 // returns if the form is valid - same as isValid - but
19960 // provides a few additional (ui-specific) features:
19961 //
19962 // 1. it will highlight any sub-widgets that are not valid
19963 // 2. it will call focus() on the first invalid sub-widget
19964 var didFocus = false;
19965 return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
19966 // Need to set this so that "required" widgets get their
19967 // state set.
19968 widget._hasBeenBlurred = true;
19969 var valid = widget.disabled || !widget.validate || widget.validate();
19970 if(!valid && !didFocus){
19971 // Set focus of the first non-valid widget
19972 winUtils.scrollIntoView(widget.containerNode || widget.domNode);
19973 widget.focus();
19974 didFocus = true;
19975 }
19976 return valid;
19977 }), function(item){ return item; });
19978 },
19979
19980 setValues: function(val){
19981 kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
19982 return this.set('value', val);
19983 },
19984 _setValueAttr: function(/*Object*/ obj){
19985 // summary:
19986 // Fill in form values from according to an Object (in the format returned by get('value'))
19987
19988 // generate map from name --> [list of widgets with that name]
19989 var map = { };
19990 array.forEach(this._getDescendantFormWidgets(), function(widget){
19991 if(!widget.name){ return; }
19992 var entry = map[widget.name] || (map[widget.name] = [] );
19993 entry.push(widget);
19994 });
19995
19996 for(var name in map){
19997 if(!map.hasOwnProperty(name)){
19998 continue;
19999 }
20000 var widgets = map[name], // array of widgets w/this name
20001 values = lang.getObject(name, false, obj); // list of values for those widgets
20002
20003 if(values === undefined){
20004 continue;
20005 }
20006 if(!lang.isArray(values)){
20007 values = [ values ];
20008 }
20009 if(typeof widgets[0].checked == 'boolean'){
20010 // for checkbox/radio, values is a list of which widgets should be checked
20011 array.forEach(widgets, function(w){
20012 w.set('value', array.indexOf(values, w.value) != -1);
20013 });
20014 }else if(widgets[0].multiple){
20015 // it takes an array (e.g. multi-select)
20016 widgets[0].set('value', values);
20017 }else{
20018 // otherwise, values is a list of values to be assigned sequentially to each widget
20019 array.forEach(widgets, function(w, i){
20020 w.set('value', values[i]);
20021 });
20022 }
20023 }
20024
20025 /***
20026 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
20027
20028 array.forEach(this.containerNode.elements, function(element){
20029 if(element.name == ''){return}; // like "continue"
20030 var namePath = element.name.split(".");
20031 var myObj=obj;
20032 var name=namePath[namePath.length-1];
20033 for(var j=1,len2=namePath.length;j<len2;++j){
20034 var p=namePath[j - 1];
20035 // repeater support block
20036 var nameA=p.split("[");
20037 if(nameA.length > 1){
20038 if(typeof(myObj[nameA[0]]) == "undefined"){
20039 myObj[nameA[0]]=[ ];
20040 } // if
20041
20042 nameIndex=parseInt(nameA[1]);
20043 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
20044 myObj[nameA[0]][nameIndex] = { };
20045 }
20046 myObj=myObj[nameA[0]][nameIndex];
20047 continue;
20048 } // repeater support ends
20049
20050 if(typeof(myObj[p]) == "undefined"){
20051 myObj=undefined;
20052 break;
20053 };
20054 myObj=myObj[p];
20055 }
20056
20057 if(typeof(myObj) == "undefined"){
20058 return; // like "continue"
20059 }
20060 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
20061 return; // like "continue"
20062 }
20063
20064 // TODO: widget values (just call set('value', ...) on the widget)
20065
20066 // TODO: maybe should call dojo.getNodeProp() instead
20067 switch(element.type){
20068 case "checkbox":
20069 element.checked = (name in myObj) &&
20070 array.some(myObj[name], function(val){ return val == element.value; });
20071 break;
20072 case "radio":
20073 element.checked = (name in myObj) && myObj[name] == element.value;
20074 break;
20075 case "select-multiple":
20076 element.selectedIndex=-1;
20077 array.forEach(element.options, function(option){
20078 option.selected = array.some(myObj[name], function(val){ return option.value == val; });
20079 });
20080 break;
20081 case "select-one":
20082 element.selectedIndex="0";
20083 array.forEach(element.options, function(option){
20084 option.selected = option.value == myObj[name];
20085 });
20086 break;
20087 case "hidden":
20088 case "text":
20089 case "textarea":
20090 case "password":
20091 element.value = myObj[name] || "";
20092 break;
20093 }
20094 });
20095 */
20096
20097 // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
20098 // which I am monitoring.
20099 },
20100
20101 getValues: function(){
20102 kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
20103 return this.get('value');
20104 },
20105 _getValueAttr: function(){
20106 // summary:
20107 // Returns Object representing form values. See description of `value` for details.
20108 // description:
20109
20110 // The value is updated into this.value every time a child has an onChange event,
20111 // so in the common case this function could just return this.value. However,
20112 // that wouldn't work when:
20113 //
20114 // 1. User presses return key to submit a form. That doesn't fire an onchange event,
20115 // and even if it did it would come too late due to the defer(...) in _handleOnChange()
20116 //
20117 // 2. app for some reason calls this.get("value") while the user is typing into a
20118 // form field. Not sure if that case needs to be supported or not.
20119
20120 // get widget values
20121 var obj = { };
20122 array.forEach(this._getDescendantFormWidgets(), function(widget){
20123 var name = widget.name;
20124 if(!name || widget.disabled){ return; }
20125
20126 // Single value widget (checkbox, radio, or plain <input> type widget)
20127 var value = widget.get('value');
20128
20129 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
20130 if(typeof widget.checked == 'boolean'){
20131 if(/Radio/.test(widget.declaredClass)){
20132 // radio button
20133 if(value !== false){
20134 lang.setObject(name, value, obj);
20135 }else{
20136 // give radio widgets a default of null
20137 value = lang.getObject(name, false, obj);
20138 if(value === undefined){
20139 lang.setObject(name, null, obj);
20140 }
20141 }
20142 }else{
20143 // checkbox/toggle button
20144 var ary=lang.getObject(name, false, obj);
20145 if(!ary){
20146 ary=[];
20147 lang.setObject(name, ary, obj);
20148 }
20149 if(value !== false){
20150 ary.push(value);
20151 }
20152 }
20153 }else{
20154 var prev=lang.getObject(name, false, obj);
20155 if(typeof prev != "undefined"){
20156 if(lang.isArray(prev)){
20157 prev.push(value);
20158 }else{
20159 lang.setObject(name, [prev, value], obj);
20160 }
20161 }else{
20162 // unique name
20163 lang.setObject(name, value, obj);
20164 }
20165 }
20166 });
20167
20168 /***
20169 * code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
20170 * but it doesn't understand [] notation, presumably)
20171 var obj = { };
20172 array.forEach(this.containerNode.elements, function(elm){
20173 if(!elm.name) {
20174 return; // like "continue"
20175 }
20176 var namePath = elm.name.split(".");
20177 var myObj=obj;
20178 var name=namePath[namePath.length-1];
20179 for(var j=1,len2=namePath.length;j<len2;++j){
20180 var nameIndex = null;
20181 var p=namePath[j - 1];
20182 var nameA=p.split("[");
20183 if(nameA.length > 1){
20184 if(typeof(myObj[nameA[0]]) == "undefined"){
20185 myObj[nameA[0]]=[ ];
20186 } // if
20187 nameIndex=parseInt(nameA[1]);
20188 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
20189 myObj[nameA[0]][nameIndex] = { };
20190 }
20191 }else if(typeof(myObj[nameA[0]]) == "undefined"){
20192 myObj[nameA[0]] = { }
20193 } // if
20194
20195 if(nameA.length == 1){
20196 myObj=myObj[nameA[0]];
20197 }else{
20198 myObj=myObj[nameA[0]][nameIndex];
20199 } // if
20200 } // for
20201
20202 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
20203 if(name == name.split("[")[0]){
20204 myObj[name]=elm.value;
20205 }else{
20206 // can not set value when there is no name
20207 }
20208 }else if(elm.type == "checkbox" && elm.checked){
20209 if(typeof(myObj[name]) == 'undefined'){
20210 myObj[name]=[ ];
20211 }
20212 myObj[name].push(elm.value);
20213 }else if(elm.type == "select-multiple"){
20214 if(typeof(myObj[name]) == 'undefined'){
20215 myObj[name]=[ ];
20216 }
20217 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
20218 if(elm.options[jdx].selected){
20219 myObj[name].push(elm.options[jdx].value);
20220 }
20221 }
20222 } // if
20223 name=undefined;
20224 }); // forEach
20225 ***/
20226 return obj;
20227 },
20228
20229 isValid: function(){
20230 // summary:
20231 // Returns true if all of the widgets are valid.
20232 // Deprecated, will be removed in 2.0. Use get("state") instead.
20233
20234 return this.state == "";
20235 },
20236
20237 onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
20238 // summary:
20239 // Stub function to connect to if you want to do something
20240 // (like disable/enable a submit button) when the valid
20241 // state changes on the form as a whole.
20242 //
20243 // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
20244 },
20245
20246 _getState: function(){
20247 // summary:
20248 // Compute what this.state should be based on state of children
20249 var states = array.map(this._descendants, function(w){
20250 return w.get("state") || "";
20251 });
20252
20253 return array.indexOf(states, "Error") >= 0 ? "Error" :
20254 array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
20255 },
20256
20257 disconnectChildren: function(){
20258 // summary:
20259 // Deprecated method. Applications no longer need to call this. Remove for 2.0.
20260 },
20261
20262 connectChildren: function(/*Boolean*/ inStartup){
20263 // summary:
20264 // You can call this function directly, ex. in the event that you
20265 // programmatically add a widget to the form *after* the form has been
20266 // initialized.
20267
20268 // TODO: rename for 2.0
20269
20270 this._descendants = this._getDescendantFormWidgets();
20271
20272 // To get notifications from children they need to be started. Children didn't used to need to be started,
20273 // so for back-compat, start them here
20274 array.forEach(this._descendants, function(child){
20275 if(!child._started){ child.startup(); }
20276 });
20277
20278 if(!inStartup){
20279 this._onChildChange();
20280 }
20281 },
20282
20283 _onChildChange: function(/*String*/ attr){
20284 // summary:
20285 // Called when child's value or disabled state changes
20286
20287 // The unit tests expect state update to be synchronous, so update it immediately.
20288 if(!attr || attr == "state" || attr == "disabled"){
20289 this._set("state", this._getState());
20290 }
20291
20292 // Use defer() to collapse value changes in multiple children into a single
20293 // update to my value. Multiple updates will occur on:
20294 // 1. Form.set()
20295 // 2. Form.reset()
20296 // 3. user selecting a radio button (which will de-select another radio button,
20297 // causing two onChange events)
20298 if(!attr || attr == "value" || attr == "disabled" || attr == "checked"){
20299 if(this._onChangeDelayTimer){
20300 this._onChangeDelayTimer.remove();
20301 }
20302 this._onChangeDelayTimer = this.defer(function(){
20303 delete this._onChangeDelayTimer;
20304 this._set("value", this.get("value"));
20305 }, 10);
20306 }
20307 },
20308
20309 startup: function(){
20310 this.inherited(arguments);
20311
20312 // Set initial this.value and this.state. Don't emit watch() notifications.
20313 this._descendants = this._getDescendantFormWidgets();
20314 this.value = this.get("value");
20315 this.state = this._getState();
20316
20317 // Initialize value and valid/invalid state tracking.
20318 var self = this;
20319 this.own(
20320 on(
20321 this.containerNode,
20322 "attrmodified-state, attrmodified-disabled, attrmodified-value, attrmodified-checked",
20323 function(evt){
20324 if(evt.target == self.domNode){
20325 return; // ignore events that I fire on myself because my children changed
20326 }
20327 self._onChildChange(evt.type.replace("attrmodified-", ""));
20328 }
20329 )
20330 );
20331
20332 // Make state change call onValidStateChange(), will be removed in 2.0
20333 this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
20334 },
20335
20336 destroy: function(){
20337 this.inherited(arguments);
20338 }
20339
20340 });
20341 });
20342
20343 },
20344 'dojo/data/util/simpleFetch':function(){
20345 define("dojo/data/util/simpleFetch", ["../../_base/lang", "../../_base/kernel", "./sorter"],
20346 function(lang, kernel, sorter){
20347 // module:
20348 // dojo/data/util/simpleFetch
20349 // summary:
20350 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20351 // be mixed into other datastore implementations to accelerate their development.
20352
20353 var simpleFetch = {};
20354 lang.setObject("dojo.data.util.simpleFetch", simpleFetch);
20355
20356 simpleFetch.errorHandler = function(/*Object*/ errorData, /*Object*/ requestObject){
20357 // summary:
20358 // The error handler when there is an error fetching items. This function should not be called
20359 // directly and is used by simpleFetch.fetch().
20360 if(requestObject.onError){
20361 var scope = requestObject.scope || kernel.global;
20362 requestObject.onError.call(scope, errorData, requestObject);
20363 }
20364 };
20365
20366 simpleFetch.fetchHandler = function(/*Array*/ items, /*Object*/ requestObject){
20367 // summary:
20368 // The handler when items are sucessfully fetched. This function should not be called directly
20369 // and is used by simpleFetch.fetch().
20370 var oldAbortFunction = requestObject.abort || null,
20371 aborted = false,
20372
20373 startIndex = requestObject.start?requestObject.start: 0,
20374 endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
20375
20376 requestObject.abort = function(){
20377 aborted = true;
20378 if(oldAbortFunction){
20379 oldAbortFunction.call(requestObject);
20380 }
20381 };
20382
20383 var scope = requestObject.scope || kernel.global;
20384 if(!requestObject.store){
20385 requestObject.store = this;
20386 }
20387 if(requestObject.onBegin){
20388 requestObject.onBegin.call(scope, items.length, requestObject);
20389 }
20390 if(requestObject.sort){
20391 items.sort(sorter.createSortFunction(requestObject.sort, this));
20392 }
20393 if(requestObject.onItem){
20394 for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
20395 var item = items[i];
20396 if(!aborted){
20397 requestObject.onItem.call(scope, item, requestObject);
20398 }
20399 }
20400 }
20401 if(requestObject.onComplete && !aborted){
20402 var subset = null;
20403 if(!requestObject.onItem){
20404 subset = items.slice(startIndex, endIndex);
20405 }
20406 requestObject.onComplete.call(scope, subset, requestObject);
20407 }
20408 };
20409
20410 simpleFetch.fetch = function(/* Object? */ request){
20411 // summary:
20412 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20413 // be mixed into other datastore implementations to accelerate their development.
20414 // description:
20415 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
20416 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
20417 // is not designed to work for datastores that respond to a fetch() call by incrementally
20418 // loading items, or sequentially loading partial batches of the result
20419 // set. For datastores that mixin simpleFetch, simpleFetch
20420 // implements a fetch method that automatically handles eight of the fetch()
20421 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
20422 // The class mixing in simpleFetch should not implement fetch(),
20423 // but should instead implement a _fetchItems() method. The _fetchItems()
20424 // method takes three arguments, the keywordArgs object that was passed
20425 // to fetch(), a callback function to be called when the result array is
20426 // available, and an error callback to be called if something goes wrong.
20427 // The _fetchItems() method should ignore any keywordArgs parameters for
20428 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
20429 // The _fetchItems() method needs to correctly handle any other keywordArgs
20430 // parameters, including the query parameter and any optional parameters
20431 // (such as includeChildren). The _fetchItems() method should create an array of
20432 // result items and pass it to the fetchHandler along with the original request object --
20433 // or, the _fetchItems() method may, if it wants to, create an new request object
20434 // with other specifics about the request that are specific to the datastore and pass
20435 // that as the request object to the handler.
20436 //
20437 // For more information on this specific function, see dojo/data/api/Read.fetch()
20438 //
20439 // request:
20440 // The keywordArgs parameter may either be an instance of
20441 // conforming to dojo/data/api/Request or may be a simple anonymous object
20442 // that may contain any of the following:
20443 // | {
20444 // | query: query-object or query-string,
20445 // | queryOptions: object,
20446 // | onBegin: Function,
20447 // | onItem: Function,
20448 // | onComplete: Function,
20449 // | onError: Function,
20450 // | scope: object,
20451 // | start: int
20452 // | count: int
20453 // | sort: array
20454 // | }
20455 // All implementations should accept keywordArgs objects with any of
20456 // the 9 standard properties: query, onBegin, onItem, onComplete, onError
20457 // scope, sort, start, and count. Some implementations may accept additional
20458 // properties in the keywordArgs object as valid parameters, such as
20459 // {includeOutliers:true}.
20460 //
20461 // ####The *query* parameter
20462 //
20463 // The query may be optional in some data store implementations.
20464 // The dojo/data/api/Read API does not specify the syntax or semantics
20465 // of the query itself -- each different data store implementation
20466 // may have its own notion of what a query should look like.
20467 // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data
20468 // and dojox.data support an object structure query, where the object is a set of
20469 // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the
20470 // dijit widgets, such as ComboBox assume this to be the case when working with a datastore
20471 // when they dynamically update the query. Therefore, for maximum compatibility with dijit
20472 // widgets the recommended query parameter is a key/value object. That does not mean that the
20473 // the datastore may not take alternative query forms, such as a simple string, a Date, a number,
20474 // or a mix of such. Ultimately, The dojo/data/api/Read API is agnostic about what the query
20475 // format.
20476 //
20477 // Further note: In general for query objects that accept strings as attribute
20478 // value matches, the store should also support basic filtering capability, such as *
20479 // (match any character) and ? (match single character). An example query that is a query object
20480 // would be like: { attrFoo: "value*"}. Which generally means match all items where they have
20481 // an attribute named attrFoo, with a value that starts with 'value'.
20482 //
20483 // ####The *queryOptions* parameter
20484 //
20485 // The queryOptions parameter is an optional parameter used to specify options that may modify
20486 // the query in some fashion, such as doing a case insensitive search, or doing a deep search
20487 // where all items in a hierarchical representation of data are scanned instead of just the root
20488 // items. It currently defines two options that all datastores should attempt to honor if possible:
20489 // | {
20490 // | ignoreCase: boolean, // Whether or not the query should match case sensitively or not. Default behaviour is false.
20491 // | deep: boolean // Whether or not a fetch should do a deep search of items and all child
20492 // | // items instead of just root-level items in a datastore. Default is false.
20493 // | }
20494 //
20495 // ####The *onBegin* parameter.
20496 //
20497 // function(size, request);
20498 // If an onBegin callback function is provided, the callback function
20499 // will be called just once, before the first onItem callback is called.
20500 // The onBegin callback function will be passed two arguments, the
20501 // the total number of items identified and the Request object. If the total number is
20502 // unknown, then size will be -1. Note that size is not necessarily the size of the
20503 // collection of items returned from the query, as the request may have specified to return only a
20504 // subset of the total set of items through the use of the start and count parameters.
20505 //
20506 // ####The *onItem* parameter.
20507 //
20508 // function(item, request);
20509 //
20510 // If an onItem callback function is provided, the callback function
20511 // will be called as each item in the result is received. The callback
20512 // function will be passed two arguments: the item itself, and the
20513 // Request object.
20514 //
20515 // ####The *onComplete* parameter.
20516 //
20517 // function(items, request);
20518 //
20519 // If an onComplete callback function is provided, the callback function
20520 // will be called just once, after the last onItem callback is called.
20521 // Note that if the onItem callback is not present, then onComplete will be passed
20522 // an array containing all items which matched the query and the request object.
20523 // If the onItem callback is present, then onComplete is called as:
20524 // onComplete(null, request).
20525 //
20526 // ####The *onError* parameter.
20527 //
20528 // function(errorData, request);
20529 //
20530 // If an onError callback function is provided, the callback function
20531 // will be called if there is any sort of error while attempting to
20532 // execute the query.
20533 // The onError callback function will be passed two arguments:
20534 // an Error object and the Request object.
20535 //
20536 // ####The *scope* parameter.
20537 //
20538 // If a scope object is provided, all of the callback functions (onItem,
20539 // onComplete, onError, etc) will be invoked in the context of the scope
20540 // object. In the body of the callback function, the value of the "this"
20541 // keyword will be the scope object. If no scope object is provided,
20542 // the callback functions will be called in the context of dojo.global().
20543 // For example, onItem.call(scope, item, request) vs.
20544 // onItem.call(dojo.global(), item, request)
20545 //
20546 // ####The *start* parameter.
20547 //
20548 // If a start parameter is specified, this is a indication to the datastore to
20549 // only start returning items once the start number of items have been located and
20550 // skipped. When this parameter is paired with 'count', the store should be able
20551 // to page across queries with millions of hits by only returning subsets of the
20552 // hits for each query
20553 //
20554 // ####The *count* parameter.
20555 //
20556 // If a count parameter is specified, this is a indication to the datastore to
20557 // only return up to that many items. This allows a fetch call that may have
20558 // millions of item matches to be paired down to something reasonable.
20559 //
20560 // ####The *sort* parameter.
20561 //
20562 // If a sort parameter is specified, this is a indication to the datastore to
20563 // sort the items in some manner before returning the items. The array is an array of
20564 // javascript objects that must conform to the following format to be applied to the
20565 // fetching of items:
20566 // | {
20567 // | attribute: attribute || attribute-name-string,
20568 // | descending: true|false; // Optional. Default is false.
20569 // | }
20570 // Note that when comparing attributes, if an item contains no value for the attribute
20571 // (undefined), then it the default ascending sort logic should push it to the bottom
20572 // of the list. In the descending order case, it such items should appear at the top of the list.
20573
20574 request = request || {};
20575 if(!request.store){
20576 request.store = this;
20577 }
20578
20579 this._fetchItems(request, lang.hitch(this, "fetchHandler"), lang.hitch(this, "errorHandler"));
20580 return request; // Object
20581 };
20582
20583 return simpleFetch;
20584 });
20585
20586 },
20587 'dijit/Menu':function(){
20588 define("dijit/Menu", [
20589 "require",
20590 "dojo/_base/array", // array.forEach
20591 "dojo/_base/declare", // declare
20592 "dojo/_base/event", // event.stop
20593 "dojo/dom", // dom.byId dom.isDescendant
20594 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
20595 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
20596 "dojo/dom-style", // domStyle.getComputedStyle
20597 "dojo/keys", // keys.F10
20598 "dojo/_base/lang", // lang.hitch
20599 "dojo/on",
20600 "dojo/sniff", // has("ie"), has("quirks")
20601 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames
20602 "dojo/window", // winUtils.get
20603 "./popup",
20604 "./DropDownMenu",
20605 "dojo/ready"
20606 ], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, keys, lang, on,
20607 has, win, winUtils, pm, DropDownMenu, ready){
20608
20609 // module:
20610 // dijit/Menu
20611
20612 // Back compat w/1.6, remove for 2.0
20613 if(has("dijit-legacy-requires")){
20614 ready(0, function(){
20615 var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
20616 require(requires); // use indirection so modules not rolled into a build
20617 });
20618 }
20619
20620 return declare("dijit.Menu", DropDownMenu, {
20621 // summary:
20622 // A context menu you can assign to multiple elements
20623
20624 constructor: function(/*===== params, srcNodeRef =====*/){
20625 // summary:
20626 // Create the widget.
20627 // params: Object|null
20628 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
20629 // and functions, typically callbacks like onClick.
20630 // The hash can contain any of the widget's properties, excluding read-only properties.
20631 // srcNodeRef: DOMNode|String?
20632 // If a srcNodeRef (DOM node) is specified:
20633 //
20634 // - use srcNodeRef.innerHTML as my contents
20635 // - replace srcNodeRef with my generated DOM tree
20636
20637 this._bindings = [];
20638 },
20639
20640 // targetNodeIds: [const] String[]
20641 // Array of dom node ids of nodes to attach to.
20642 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
20643 targetNodeIds: [],
20644
20645 // selector: String?
20646 // CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
20647 // the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
20648 // a range of rows in a table, tree, etc.
20649 //
20650 // The application must require() an appropriate level of dojo/query to handle the selector.
20651 selector: "",
20652
20653 // TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
20654 // So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
20655
20656 /*=====
20657 // currentTarget: [readonly] DOMNode
20658 // For context menus, set to the current node that the Menu is being displayed for.
20659 // Useful so that the menu actions can be tailored according to the node
20660 currentTarget: null,
20661 =====*/
20662
20663 // contextMenuForWindow: [const] Boolean
20664 // If true, right clicking anywhere on the window will cause this context menu to open.
20665 // If false, must specify targetNodeIds.
20666 contextMenuForWindow: false,
20667
20668 // leftClickToOpen: [const] Boolean
20669 // If true, menu will open on left click instead of right click, similar to a file menu.
20670 leftClickToOpen: false,
20671
20672 // refocus: Boolean
20673 // When this menu closes, re-focus the element which had focus before it was opened.
20674 refocus: true,
20675
20676 postCreate: function(){
20677 if(this.contextMenuForWindow){
20678 this.bindDomNode(this.ownerDocumentBody);
20679 }else{
20680 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
20681 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
20682 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
20683 array.forEach(this.targetNodeIds, this.bindDomNode, this);
20684 }
20685 this.inherited(arguments);
20686 },
20687
20688 // thanks burstlib!
20689 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
20690 // summary:
20691 // Returns the window reference of the passed iframe
20692 // tags:
20693 // private
20694 return winUtils.get(this._iframeContentDocument(iframe_el)) ||
20695 // Moz. TODO: is this available when defaultView isn't?
20696 this._iframeContentDocument(iframe_el)['__parent__'] ||
20697 (iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window
20698 },
20699
20700 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
20701 // summary:
20702 // Returns a reference to the document object inside iframe_el
20703 // tags:
20704 // protected
20705 return iframe_el.contentDocument // W3
20706 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
20707 || (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document)
20708 || null; // HTMLDocument
20709 },
20710
20711 bindDomNode: function(/*String|DomNode*/ node){
20712 // summary:
20713 // Attach menu to given node
20714 node = dom.byId(node, this.ownerDocument);
20715
20716 var cn; // Connect node
20717
20718 // Support context menus on iframes. Rather than binding to the iframe itself we need
20719 // to bind to the <body> node inside the iframe.
20720 if(node.tagName.toLowerCase() == "iframe"){
20721 var iframe = node,
20722 window = this._iframeContentWindow(iframe);
20723 cn = win.body(window.document);
20724 }else{
20725 // To capture these events at the top level, attach to <html>, not <body>.
20726 // Otherwise right-click context menu just doesn't work.
20727 cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node);
20728 }
20729
20730
20731 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
20732 var binding = {
20733 node: node,
20734 iframe: iframe
20735 };
20736
20737 // Save info about binding in _bindings[], and make node itself record index(+1) into
20738 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
20739 // start with a number, which fails on FF/safari.
20740 domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
20741
20742 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
20743 // loading yet, in which case we need to wait for the onload event first, and then connect
20744 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
20745 // we need to monitor keyboard events in addition to the oncontextmenu event.
20746 var doConnects = lang.hitch(this, function(cn){
20747 var selector = this.selector,
20748 delegatedEvent = selector ?
20749 function(eventType){ return on.selector(selector, eventType); } :
20750 function(eventType){ return eventType; },
20751 self = this;
20752 return [
20753 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
20754 // rather than shift-F10?
20755 on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){
20756 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
20757 event.stop(evt);
20758 self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY});
20759 }),
20760 on(cn, delegatedEvent("keydown"), function(evt){
20761 if(evt.shiftKey && evt.keyCode == keys.F10){
20762 event.stop(evt);
20763 self._scheduleOpen(this, iframe); // no coords - open near target node
20764 }
20765 })
20766 ];
20767 });
20768 binding.connects = cn ? doConnects(cn) : [];
20769
20770 if(iframe){
20771 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
20772 // and every time the contents change.
20773 // Need to do this b/c we are actually binding to the iframe's <body> node.
20774 // Note: can't use connect.connect(), see #9609.
20775
20776 binding.onloadHandler = lang.hitch(this, function(){
20777 // want to remove old connections, but IE throws exceptions when trying to
20778 // access the <body> node because it's already gone, or at least in a state of limbo
20779
20780 var window = this._iframeContentWindow(iframe);
20781 cn = win.body(window.document)
20782 binding.connects = doConnects(cn);
20783 });
20784 if(iframe.addEventListener){
20785 iframe.addEventListener("load", binding.onloadHandler, false);
20786 }else{
20787 iframe.attachEvent("onload", binding.onloadHandler);
20788 }
20789 }
20790 },
20791
20792 unBindDomNode: function(/*String|DomNode*/ nodeName){
20793 // summary:
20794 // Detach menu from given node
20795
20796 var node;
20797 try{
20798 node = dom.byId(nodeName, this.ownerDocument);
20799 }catch(e){
20800 // On IE the dom.byId() call will get an exception if the attach point was
20801 // the <body> node of an <iframe> that has since been reloaded (and thus the
20802 // <body> node is in a limbo state of destruction.
20803 return;
20804 }
20805
20806 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
20807 var attrName = "_dijitMenu" + this.id;
20808 if(node && domAttr.has(node, attrName)){
20809 var bid = domAttr.get(node, attrName)-1, b = this._bindings[bid], h;
20810 while((h = b.connects.pop())){
20811 h.remove();
20812 }
20813
20814 // Remove listener for iframe onload events
20815 var iframe = b.iframe;
20816 if(iframe){
20817 if(iframe.removeEventListener){
20818 iframe.removeEventListener("load", b.onloadHandler, false);
20819 }else{
20820 iframe.detachEvent("onload", b.onloadHandler);
20821 }
20822 }
20823
20824 domAttr.remove(node, attrName);
20825 delete this._bindings[bid];
20826 }
20827 },
20828
20829 _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
20830 // summary:
20831 // Set timer to display myself. Using a timer rather than displaying immediately solves
20832 // two problems:
20833 //
20834 // 1. IE: without the delay, focus work in "open" causes the system
20835 // context menu to appear in spite of stopEvent.
20836 //
20837 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
20838 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
20839 // oncontextmenu event.)
20840
20841 if(!this._openTimer){
20842 this._openTimer = this.defer(function(){
20843 delete this._openTimer;
20844 this._openMyself({
20845 target: target,
20846 iframe: iframe,
20847 coords: coords
20848 });
20849 }, 1);
20850 }
20851 },
20852
20853 _openMyself: function(args){
20854 // summary:
20855 // Internal function for opening myself when the user does a right-click or something similar.
20856 // args:
20857 // This is an Object containing:
20858 //
20859 // - target: The node that is being clicked
20860 // - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
20861 // - coords: Put menu at specified x/y position in viewport, or if iframe is
20862 // specified, then relative to iframe.
20863 //
20864 // _openMyself() formerly took the event object, and since various code references
20865 // evt.target (after connecting to _openMyself()), using an Object for parameters
20866 // (so that old code still works).
20867
20868 var target = args.target,
20869 iframe = args.iframe,
20870 coords = args.coords;
20871
20872 // To be used by MenuItem event handlers to tell which node the menu was opened on
20873 this.currentTarget = target;
20874
20875 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
20876 // then near the node the menu is assigned to.
20877 if(coords){
20878 if(iframe){
20879 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
20880 var ifc = domGeometry.position(iframe, true),
20881 window = this._iframeContentWindow(iframe),
20882 scroll = domGeometry.docScroll(window.document);
20883
20884 var cs = domStyle.getComputedStyle(iframe),
20885 tp = domStyle.toPixelValue,
20886 left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
20887 top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
20888
20889 coords.x += ifc.x + left - scroll.x;
20890 coords.y += ifc.y + top - scroll.y;
20891 }
20892 }else{
20893 coords = domGeometry.position(target, true);
20894 coords.x += 10;
20895 coords.y += 10;
20896 }
20897
20898 var self=this;
20899 var prevFocusNode = this._focusManager.get("prevNode");
20900 var curFocusNode = this._focusManager.get("curNode");
20901 var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
20902
20903 function closeAndRestoreFocus(){
20904 // user has clicked on a menu or popup
20905 if(self.refocus && savedFocusNode){
20906 savedFocusNode.focus();
20907 }
20908 pm.close(self);
20909 }
20910 pm.open({
20911 popup: this,
20912 x: coords.x,
20913 y: coords.y,
20914 onExecute: closeAndRestoreFocus,
20915 onCancel: closeAndRestoreFocus,
20916 orient: this.isLeftToRight() ? 'L' : 'R'
20917 });
20918 this.focus();
20919
20920 this._onBlur = function(){
20921 this.inherited('_onBlur', arguments);
20922 // Usually the parent closes the child widget but if this is a context
20923 // menu then there is no parent
20924 pm.close(this);
20925 // don't try to restore focus; user has clicked another part of the screen
20926 // and set focus there
20927 };
20928 },
20929
20930 destroy: function(){
20931 array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
20932 this.inherited(arguments);
20933 }
20934 });
20935
20936 });
20937
20938 },
20939 'dijit/form/_CheckBoxMixin':function(){
20940 define("dijit/form/_CheckBoxMixin", [
20941 "dojo/_base/declare", // declare
20942 "dojo/dom-attr", // domAttr.set
20943 "dojo/_base/event" // event.stop
20944 ], function(declare, domAttr, event){
20945
20946 // module:
20947 // dijit/form/_CheckBoxMixin
20948
20949 return declare("dijit.form._CheckBoxMixin", null, {
20950 // summary:
20951 // Mixin to provide widget functionality corresponding to an HTML checkbox
20952 //
20953 // description:
20954 // User interacts with real html inputs.
20955 // On onclick (which occurs by mouse click, space-bar, or
20956 // using the arrow keys to switch the selected radio button),
20957 // we update the state of the checkbox/radio.
20958 //
20959
20960 // type: [private] String
20961 // type attribute on `<input>` node.
20962 // Overrides `dijit/form/Button.type`. Users should not change this value.
20963 type: "checkbox",
20964
20965 // value: String
20966 // As an initialization parameter, equivalent to value field on normal checkbox
20967 // (if checked, the value is passed as the value when form is submitted).
20968 value: "on",
20969
20970 // readOnly: Boolean
20971 // Should this widget respond to user input?
20972 // In markup, this is specified as "readOnly".
20973 // Similar to disabled except readOnly form values are submitted.
20974 readOnly: false,
20975
20976 // aria-pressed for toggle buttons, and aria-checked for checkboxes
20977 _aria_attr: "aria-checked",
20978
20979 _setReadOnlyAttr: function(/*Boolean*/ value){
20980 this._set("readOnly", value);
20981 domAttr.set(this.focusNode, 'readOnly', value);
20982 },
20983
20984 // Override dijit/form/Button._setLabelAttr() since we don't even have a containerNode.
20985 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox/layout/TabContainer
20986 _setLabelAttr: undefined,
20987
20988 _getSubmitValue: function(/*String*/ value){
20989 return !value && value !== 0 ? "on" : value;
20990 },
20991
20992 _setValueAttr: function(newValue){
20993 newValue = this._getSubmitValue(newValue); // "on" to match browser native behavior when value unspecified
20994 this._set("value", newValue);
20995 domAttr.set(this.focusNode, "value", newValue);
20996 },
20997
20998 reset: function(){
20999 this.inherited(arguments);
21000 // Handle unlikely event that the <input type=checkbox> value attribute has changed
21001 this._set("value", this.params.value || "on");
21002 domAttr.set(this.focusNode, 'value', this.value);
21003 },
21004
21005 _onClick: function(/*Event*/ e){
21006 // summary:
21007 // Internal function to handle click actions - need to check
21008 // readOnly, since button no longer does that check.
21009 if(this.readOnly){
21010 event.stop(e);
21011 return false;
21012 }
21013 return this.inherited(arguments);
21014 }
21015 });
21016 });
21017
21018 },
21019 'dijit/layout/ContentPane':function(){
21020 define("dijit/layout/ContentPane", [
21021 "dojo/_base/kernel", // kernel.deprecated
21022 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
21023 "../_Widget",
21024 "../_Container",
21025 "./_ContentPaneResizeMixin",
21026 "dojo/string", // string.substitute
21027 "dojo/html", // html._ContentSetter
21028 "dojo/i18n!../nls/loading",
21029 "dojo/_base/array", // array.forEach
21030 "dojo/_base/declare", // declare
21031 "dojo/_base/Deferred", // Deferred
21032 "dojo/dom", // dom.byId
21033 "dojo/dom-attr", // domAttr.attr
21034 "dojo/dom-construct", // empty()
21035 "dojo/_base/xhr", // xhr.get
21036 "dojo/i18n", // i18n.getLocalization
21037 "dojo/when"
21038 ], function(kernel, lang, _Widget, _Container, _ContentPaneResizeMixin, string, html, nlsLoading,
21039 array, declare, Deferred, dom, domAttr, domConstruct, xhr, i18n, when){
21040
21041 // module:
21042 // dijit/layout/ContentPane
21043
21044
21045 return declare("dijit.layout.ContentPane", [_Widget, _Container, _ContentPaneResizeMixin], {
21046 // summary:
21047 // A widget containing an HTML fragment, specified inline
21048 // or by uri. Fragment may include widgets.
21049 //
21050 // description:
21051 // This widget embeds a document fragment in the page, specified
21052 // either by uri, javascript generated markup or DOM reference.
21053 // Any widgets within this content are instantiated and managed,
21054 // but laid out according to the HTML structure. Unlike IFRAME,
21055 // ContentPane embeds a document fragment as would be found
21056 // inside the BODY tag of a full HTML document. It should not
21057 // contain the HTML, HEAD, or BODY tags.
21058 // For more advanced functionality with scripts and
21059 // stylesheets, see dojox/layout/ContentPane. This widget may be
21060 // used stand alone or as a base class for other widgets.
21061 // ContentPane is useful as a child of other layout containers
21062 // such as BorderContainer or TabContainer, but note that those
21063 // widgets can contain any widget as a child.
21064 //
21065 // example:
21066 // Some quick samples:
21067 // To change the innerHTML:
21068 // | cp.set('content', '<b>new content</b>')`
21069 // Or you can send it a NodeList:
21070 // | cp.set('content', dojo.query('div [class=selected]', userSelection))
21071 // To do an ajax update:
21072 // | cp.set('href', url)
21073
21074 // href: String
21075 // The href of the content that displays now.
21076 // Set this at construction if you want to load data externally when the
21077 // pane is shown. (Set preload=true to load it immediately.)
21078 // Changing href after creation doesn't have any effect; Use set('href', ...);
21079 href: "",
21080
21081 // content: String|DomNode|NodeList|dijit/_Widget
21082 // The innerHTML of the ContentPane.
21083 // Note that the initialization parameter / argument to set("content", ...)
21084 // can be a String, DomNode, Nodelist, or _Widget.
21085 content: "",
21086
21087 // extractContent: Boolean
21088 // Extract visible content from inside of `<body> .... </body>`.
21089 // I.e., strip `<html>` and `<head>` (and it's contents) from the href
21090 extractContent: false,
21091
21092 // parseOnLoad: Boolean
21093 // Parse content and create the widgets, if any.
21094 parseOnLoad: true,
21095
21096 // parserScope: String
21097 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
21098 // will search for data-dojo-type (or dojoType). For backwards compatibility
21099 // reasons defaults to dojo._scopeName (which is "dojo" except when
21100 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
21101 parserScope: kernel._scopeName,
21102
21103 // preventCache: Boolean
21104 // Prevent caching of data from href's by appending a timestamp to the href.
21105 preventCache: false,
21106
21107 // preload: Boolean
21108 // Force load of data on initialization even if pane is hidden.
21109 preload: false,
21110
21111 // refreshOnShow: Boolean
21112 // Refresh (re-download) content when pane goes from hidden to shown
21113 refreshOnShow: false,
21114
21115 // loadingMessage: String
21116 // Message that shows while downloading
21117 loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
21118
21119 // errorMessage: String
21120 // Message that shows if an error occurs
21121 errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
21122
21123 // isLoaded: [readonly] Boolean
21124 // True if the ContentPane has data in it, either specified
21125 // during initialization (via href or inline content), or set
21126 // via set('content', ...) / set('href', ...)
21127 //
21128 // False if it doesn't have any content, or if ContentPane is
21129 // still in the process of downloading href.
21130 isLoaded: false,
21131
21132 baseClass: "dijitContentPane",
21133
21134 /*======
21135 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
21136 // Function that should grab the content specified via href.
21137 ioMethod: dojo.xhrGet,
21138 ======*/
21139
21140 // ioArgs: Object
21141 // Parameters to pass to xhrGet() request, for example:
21142 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
21143 ioArgs: {},
21144
21145 // onLoadDeferred: [readonly] dojo.Deferred
21146 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
21147 // Calling onLoadDeferred.then() registers your
21148 // callback to be called only once, when the prior set('href', ...) call or
21149 // the initial href parameter to the constructor finishes loading.
21150 //
21151 // This is different than an onLoad() handler which gets called any time any href
21152 // or content is loaded.
21153 onLoadDeferred: null,
21154
21155 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
21156 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
21157 // entire pane.
21158 _setTitleAttr: null,
21159
21160 // Flag to parser that I'll parse my contents, so it shouldn't.
21161 stopParser: true,
21162
21163 // template: [private] Boolean
21164 // Flag from the parser that this ContentPane is inside a template
21165 // so the contents are pre-parsed.
21166 // TODO: this declaration can be commented out in 2.0
21167 template: false,
21168
21169 create: function(params, srcNodeRef){
21170 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
21171 // processed in the same way as contents set via set("content", ...), calling the parser etc.
21172 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
21173 if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
21174 srcNodeRef = dom.byId(srcNodeRef);
21175 var df = srcNodeRef.ownerDocument.createDocumentFragment();
21176 while(srcNodeRef.firstChild){
21177 df.appendChild(srcNodeRef.firstChild);
21178 }
21179 params = lang.delegate(params, {content: df});
21180 }
21181 this.inherited(arguments, [params, srcNodeRef]);
21182 },
21183
21184 postMixInProperties: function(){
21185 this.inherited(arguments);
21186 var messages = i18n.getLocalization("dijit", "loading", this.lang);
21187 this.loadingMessage = string.substitute(this.loadingMessage, messages);
21188 this.errorMessage = string.substitute(this.errorMessage, messages);
21189 },
21190
21191 buildRendering: function(){
21192 this.inherited(arguments);
21193
21194 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
21195 // For subclasses of ContentPane that do have a template, does nothing.
21196 if(!this.containerNode){
21197 this.containerNode = this.domNode;
21198 }
21199
21200 // remove the title attribute so it doesn't show up when hovering
21201 // over a node (TODO: remove in 2.0, no longer needed after #11490)
21202 this.domNode.title = "";
21203
21204 if(!domAttr.get(this.domNode,"role")){
21205 this.domNode.setAttribute("role", "group");
21206 }
21207 },
21208
21209 startup: function(){
21210 // summary:
21211 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
21212
21213 // This starts all the widgets
21214 this.inherited(arguments);
21215
21216 // And this catches stuff like dojo/dnd/Source
21217 if(this._contentSetter){
21218 array.forEach(this._contentSetter.parseResults, function(obj){
21219 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
21220 obj.startup();
21221 obj._started = true;
21222 }
21223 }, this);
21224 }
21225 },
21226
21227 _startChildren: function(){
21228 // summary:
21229 // Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
21230 // itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
21231
21232 // This starts all the widgets
21233 array.forEach(this.getChildren(), function(obj){
21234 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
21235 obj.startup();
21236 obj._started = true;
21237 }
21238 });
21239
21240 // And this catches stuff like dojo/dnd/Source
21241 if(this._contentSetter){
21242 array.forEach(this._contentSetter.parseResults, function(obj){
21243 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
21244 obj.startup();
21245 obj._started = true;
21246 }
21247 }, this);
21248 }
21249 },
21250
21251 setHref: function(/*String|Uri*/ href){
21252 // summary:
21253 // Deprecated. Use set('href', ...) instead.
21254 kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
21255 return this.set("href", href);
21256 },
21257 _setHrefAttr: function(/*String|Uri*/ href){
21258 // summary:
21259 // Hook so set("href", ...) works.
21260 // description:
21261 // Reset the (external defined) content of this pane and replace with new url
21262 // Note: It delays the download until widget is shown if preload is false.
21263 // href:
21264 // url to the page you want to get, must be within the same domain as your mainpage
21265
21266 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
21267 this.cancel();
21268
21269 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
21270 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
21271
21272 this._set("href", href);
21273
21274 // _setHrefAttr() is called during creation and by the user, after creation.
21275 // Assuming preload == false, only in the second case do we actually load the URL;
21276 // otherwise it's done in startup(), and only if this widget is shown.
21277 if(this.preload || (this._created && this._isShown())){
21278 this._load();
21279 }else{
21280 // Set flag to indicate that href needs to be loaded the next time the
21281 // ContentPane is made visible
21282 this._hrefChanged = true;
21283 }
21284
21285 return this.onLoadDeferred; // Deferred
21286 },
21287
21288 setContent: function(/*String|DomNode|Nodelist*/data){
21289 // summary:
21290 // Deprecated. Use set('content', ...) instead.
21291 kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
21292 this.set("content", data);
21293 },
21294 _setContentAttr: function(/*String|DomNode|Nodelist*/data){
21295 // summary:
21296 // Hook to make set("content", ...) work.
21297 // Replaces old content with data content, include style classes from old content
21298 // data:
21299 // the new Content may be String, DomNode or NodeList
21300 //
21301 // if data is a NodeList (or an array of nodes) nodes are copied
21302 // so you can import nodes from another document implicitly
21303
21304 // clear href so we can't run refresh and clear content
21305 // refresh should only work if we downloaded the content
21306 this._set("href", "");
21307
21308 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
21309 this.cancel();
21310
21311 // Even though user is just setting content directly, still need to define an onLoadDeferred
21312 // because the _onLoadHandler() handler is still getting called from setContent()
21313 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
21314 if(this._created){
21315 // For back-compat reasons, call onLoad() for set('content', ...)
21316 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
21317 // or as initialization parameter (ie: new ContentPane({content: ...})
21318 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
21319 }
21320
21321 this._setContent(data || "");
21322
21323 this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
21324
21325 return this.onLoadDeferred; // Deferred
21326 },
21327 _getContentAttr: function(){
21328 // summary:
21329 // Hook to make get("content") work
21330 return this.containerNode.innerHTML;
21331 },
21332
21333 cancel: function(){
21334 // summary:
21335 // Cancels an in-flight download of content
21336 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
21337 this._xhrDfd.cancel();
21338 }
21339 delete this._xhrDfd; // garbage collect
21340
21341 this.onLoadDeferred = null;
21342 },
21343
21344 destroy: function(){
21345 this.cancel();
21346 this.inherited(arguments);
21347 },
21348
21349 destroyRecursive: function(/*Boolean*/ preserveDom){
21350 // summary:
21351 // Destroy the ContentPane and its contents
21352
21353 // if we have multiple controllers destroying us, bail after the first
21354 if(this._beingDestroyed){
21355 return;
21356 }
21357 this.inherited(arguments);
21358 },
21359
21360 _onShow: function(){
21361 // summary:
21362 // Called when the ContentPane is made visible
21363 // description:
21364 // For a plain ContentPane, this is called on initialization, from startup().
21365 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
21366 // called whenever the pane is made visible.
21367 //
21368 // Does necessary processing, including href download and layout/resize of
21369 // child widget(s)
21370
21371 this.inherited(arguments);
21372
21373 if(this.href){
21374 if(!this._xhrDfd && // if there's an href that isn't already being loaded
21375 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
21376 ){
21377 return this.refresh(); // If child has an href, promise that fires when the load is complete
21378 }
21379 }
21380 },
21381
21382 refresh: function(){
21383 // summary:
21384 // [Re]download contents of href and display
21385 // description:
21386 // 1. cancels any currently in-flight requests
21387 // 2. posts "loading..." message
21388 // 3. sends XHR to download new data
21389
21390 // Cancel possible prior in-flight request
21391 this.cancel();
21392
21393 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
21394 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
21395 this._load();
21396 return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
21397 },
21398
21399 _load: function(){
21400 // summary:
21401 // Load/reload the href specified in this.href
21402
21403 // display loading message
21404 this._setContent(this.onDownloadStart(), true);
21405
21406 var self = this;
21407 var getArgs = {
21408 preventCache: (this.preventCache || this.refreshOnShow),
21409 url: this.href,
21410 handleAs: "text"
21411 };
21412 if(lang.isObject(this.ioArgs)){
21413 lang.mixin(getArgs, this.ioArgs);
21414 }
21415
21416 var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)),
21417 returnedHtml;
21418
21419 hand.then(
21420 function(html){
21421 returnedHtml = html;
21422 try{
21423 self._isDownloaded = true;
21424 return self._setContent(html, false);
21425 }catch(err){
21426 self._onError('Content', err); // onContentError
21427 }
21428 },
21429 function(err){
21430 if(!hand.canceled){
21431 // show error message in the pane
21432 self._onError('Download', err); // onDownloadError
21433 }
21434 delete self._xhrDfd;
21435 return err;
21436 }
21437 ).then(function(){
21438 self.onDownloadEnd();
21439 delete self._xhrDfd;
21440 return returnedHtml;
21441 });
21442
21443 // Remove flag saying that a load is needed
21444 delete this._hrefChanged;
21445 },
21446
21447 _onLoadHandler: function(data){
21448 // summary:
21449 // This is called whenever new content is being loaded
21450 this._set("isLoaded", true);
21451 try{
21452 this.onLoadDeferred.resolve(data);
21453 }catch(e){
21454 console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
21455 }
21456 },
21457
21458 _onUnloadHandler: function(){
21459 // summary:
21460 // This is called whenever the content is being unloaded
21461 this._set("isLoaded", false);
21462 try{
21463 this.onUnload();
21464 }catch(e){
21465 console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
21466 }
21467 },
21468
21469 destroyDescendants: function(/*Boolean*/ preserveDom){
21470 // summary:
21471 // Destroy all the widgets inside the ContentPane and empty containerNode
21472
21473 // Make sure we call onUnload (but only when the ContentPane has real content)
21474 if(this.isLoaded){
21475 this._onUnloadHandler();
21476 }
21477
21478 // Even if this.isLoaded == false there might still be a "Loading..." message
21479 // to erase, so continue...
21480
21481 // For historical reasons we need to delete all widgets under this.containerNode,
21482 // even ones that the user has created manually.
21483 var setter = this._contentSetter;
21484 array.forEach(this.getChildren(), function(widget){
21485 if(widget.destroyRecursive){
21486 // All widgets will hit this branch
21487 widget.destroyRecursive(preserveDom);
21488 }else if(widget.destroy){
21489 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21490 widget.destroy(preserveDom);
21491 }
21492 widget._destroyed = true;
21493 });
21494 if(setter){
21495 // Most of the widgets in setter.parseResults have already been destroyed, but
21496 // things like Menu that have been moved to <body> haven't yet
21497 array.forEach(setter.parseResults, function(widget){
21498 if(!widget._destroyed){
21499 if(widget.destroyRecursive){
21500 // All widgets will hit this branch
21501 widget.destroyRecursive(preserveDom);
21502 }else if(widget.destroy){
21503 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21504 widget.destroy(preserveDom);
21505 }
21506 widget._destroyed = true;
21507 }
21508 });
21509 delete setter.parseResults;
21510 }
21511
21512 // And then clear away all the DOM nodes
21513 if(!preserveDom){
21514 domConstruct.empty(this.containerNode);
21515 }
21516
21517 // Delete any state information we have about current contents
21518 delete this._singleChild;
21519 },
21520
21521 _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
21522 // summary:
21523 // Insert the content into the container node
21524 // returns:
21525 // Returns a Deferred promise that is resolved when the content is parsed.
21526
21527 // first get rid of child widgets
21528 this.destroyDescendants();
21529
21530 // html.set will take care of the rest of the details
21531 // we provide an override for the error handling to ensure the widget gets the errors
21532 // configure the setter instance with only the relevant widget instance properties
21533 // NOTE: unless we hook into attr, or provide property setters for each property,
21534 // we need to re-configure the ContentSetter with each use
21535 var setter = this._contentSetter;
21536 if(! (setter && setter instanceof html._ContentSetter)){
21537 setter = this._contentSetter = new html._ContentSetter({
21538 node: this.containerNode,
21539 _onError: lang.hitch(this, this._onError),
21540 onContentError: lang.hitch(this, function(e){
21541 // fires if a domfault occurs when we are appending this.errorMessage
21542 // like for instance if domNode is a UL and we try append a DIV
21543 var errMess = this.onContentError(e);
21544 try{
21545 this.containerNode.innerHTML = errMess;
21546 }catch(e){
21547 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
21548 }
21549 })/*,
21550 _onError */
21551 });
21552 }
21553
21554 var setterParams = lang.mixin({
21555 cleanContent: this.cleanContent,
21556 extractContent: this.extractContent,
21557 parseContent: !cont.domNode && this.parseOnLoad,
21558 parserScope: this.parserScope,
21559 startup: false,
21560 dir: this.dir,
21561 lang: this.lang,
21562 textDir: this.textDir
21563 }, this._contentSetterParams || {});
21564
21565 var p = setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
21566
21567 // dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
21568 // dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
21569 // So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
21570 var self = this;
21571 return when(p && p.then ? p : setter.parseDeferred, function(){
21572 // setter params must be pulled afresh from the ContentPane each time
21573 delete self._contentSetterParams;
21574
21575 if(!isFakeContent){
21576 if(self._started){
21577 // Startup each top level child widget (and they will start their children, recursively)
21578 self._startChildren();
21579
21580 // Call resize() on each of my child layout widgets,
21581 // or resize() on my single child layout widget...
21582 // either now (if I'm currently visible) or when I become visible
21583 self._scheduleLayout();
21584 }
21585 self._onLoadHandler(cont);
21586 }
21587 });
21588 },
21589
21590 _onError: function(type, err, consoleText){
21591 this.onLoadDeferred.reject(err);
21592
21593 // shows user the string that is returned by on[type]Error
21594 // override on[type]Error and return your own string to customize
21595 var errText = this['on' + type + 'Error'].call(this, err);
21596 if(consoleText){
21597 console.error(consoleText, err);
21598 }else if(errText){// a empty string won't change current content
21599 this._setContent(errText, true);
21600 }
21601 },
21602
21603 // EVENT's, should be overide-able
21604 onLoad: function(/*===== data =====*/){
21605 // summary:
21606 // Event hook, is called after everything is loaded and widgetified
21607 // tags:
21608 // callback
21609 },
21610
21611 onUnload: function(){
21612 // summary:
21613 // Event hook, is called before old content is cleared
21614 // tags:
21615 // callback
21616 },
21617
21618 onDownloadStart: function(){
21619 // summary:
21620 // Called before download starts.
21621 // description:
21622 // The string returned by this function will be the html
21623 // that tells the user we are loading something.
21624 // Override with your own function if you want to change text.
21625 // tags:
21626 // extension
21627 return this.loadingMessage;
21628 },
21629
21630 onContentError: function(/*Error*/ /*===== error =====*/){
21631 // summary:
21632 // Called on DOM faults, require faults etc. in content.
21633 //
21634 // In order to display an error message in the pane, return
21635 // the error message from this method, as an HTML string.
21636 //
21637 // By default (if this method is not overriden), it returns
21638 // nothing, so the error message is just printed to the console.
21639 // tags:
21640 // extension
21641 },
21642
21643 onDownloadError: function(/*Error*/ /*===== error =====*/){
21644 // summary:
21645 // Called when download error occurs.
21646 //
21647 // In order to display an error message in the pane, return
21648 // the error message from this method, as an HTML string.
21649 //
21650 // Default behavior (if this method is not overriden) is to display
21651 // the error message inside the pane.
21652 // tags:
21653 // extension
21654 return this.errorMessage;
21655 },
21656
21657 onDownloadEnd: function(){
21658 // summary:
21659 // Called when download is finished.
21660 // tags:
21661 // callback
21662 }
21663 });
21664
21665 });
21666
21667 },
21668 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline 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\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21669 'url: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\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21670 'dijit/_KeyNavContainer':function(){
21671 define("dijit/_KeyNavContainer", [
21672 "dojo/_base/kernel", // kernel.deprecated
21673 "./_Container",
21674 "./_FocusMixin",
21675 "dojo/_base/array", // array.forEach
21676 "dojo/keys", // keys.END keys.HOME
21677 "dojo/_base/declare", // declare
21678 "dojo/_base/event", // event.stop
21679 "dojo/dom-attr", // domAttr.set
21680 "dojo/_base/lang" // lang.hitch
21681 ], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
21682
21683
21684 // module:
21685 // dijit/_KeyNavContainer
21686
21687 return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
21688 // summary:
21689 // A _Container with keyboard navigation of its children.
21690 // description:
21691 // To use this mixin, call connectKeyNavHandlers() in
21692 // postCreate().
21693 // It provides normalized keyboard and focusing code for Container
21694 // widgets.
21695
21696 /*=====
21697 // focusedChild: [protected] Widget
21698 // The currently focused child widget, or null if there isn't one
21699 focusedChild: null,
21700 =====*/
21701
21702 // tabIndex: String
21703 // Tab index of the container; same as HTML tabIndex attribute.
21704 // Note then when user tabs into the container, focus is immediately
21705 // moved to the first item in the container.
21706 tabIndex: "0",
21707
21708 connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
21709 // summary:
21710 // Call in postCreate() to attach the keyboard handlers
21711 // to the container.
21712 // preKeyCodes: keys[]
21713 // Key codes for navigating to the previous child.
21714 // nextKeyCodes: keys[]
21715 // Key codes for navigating to the next child.
21716 // tags:
21717 // protected
21718
21719 // TODO: call this automatically from my own postCreate()
21720
21721 var keyCodes = (this._keyNavCodes = {});
21722 var prev = lang.hitch(this, "focusPrev");
21723 var next = lang.hitch(this, "focusNext");
21724 array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
21725 array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
21726 keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
21727 keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
21728 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
21729 this.connect(this.domNode, "onfocus", "_onContainerFocus");
21730 },
21731
21732 startupKeyNavChildren: function(){
21733 kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
21734 },
21735
21736 startup: function(){
21737 this.inherited(arguments);
21738 array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
21739 },
21740
21741 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
21742 this.inherited(arguments);
21743 this._startupChild(widget);
21744 },
21745
21746 focus: function(){
21747 // summary:
21748 // Default focus() implementation: focus the first child.
21749 this.focusFirstChild();
21750 },
21751
21752 focusFirstChild: function(){
21753 // summary:
21754 // Focus the first focusable child in the container.
21755 // tags:
21756 // protected
21757 this.focusChild(this._getFirstFocusableChild());
21758 },
21759
21760 focusLastChild: function(){
21761 // summary:
21762 // Focus the last focusable child in the container.
21763 // tags:
21764 // protected
21765 this.focusChild(this._getLastFocusableChild());
21766 },
21767
21768 focusNext: function(){
21769 // summary:
21770 // Focus the next widget
21771 // tags:
21772 // protected
21773 this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
21774 },
21775
21776 focusPrev: function(){
21777 // summary:
21778 // Focus the last focusable node in the previous widget
21779 // (ex: go to the ComboButton icon section rather than button section)
21780 // tags:
21781 // protected
21782 this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
21783 },
21784
21785 focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last){
21786 // summary:
21787 // Focus specified child widget.
21788 // widget:
21789 // Reference to container's child widget
21790 // last:
21791 // If true and if widget has multiple focusable nodes, focus the
21792 // last one instead of the first one
21793 // tags:
21794 // protected
21795
21796 if(!widget){ return; }
21797
21798 if(this.focusedChild && widget !== this.focusedChild){
21799 this._onChildBlur(this.focusedChild); // used by _MenuBase
21800 }
21801 widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
21802 widget.focus(last ? "end" : "start");
21803 this._set("focusedChild", widget);
21804 },
21805
21806 _startupChild: function(/*dijit/_WidgetBase*/ widget){
21807 // summary:
21808 // Setup for each child widget
21809 // description:
21810 // Sets tabIndex=-1 on each child, so that the tab key will
21811 // leave the container rather than visiting each child.
21812 // tags:
21813 // private
21814
21815 widget.set("tabIndex", "-1");
21816
21817 this.connect(widget, "_onFocus", function(){
21818 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
21819 widget.set("tabIndex", this.tabIndex);
21820 });
21821 this.connect(widget, "_onBlur", function(){
21822 widget.set("tabIndex", "-1");
21823 });
21824 },
21825
21826 _onContainerFocus: function(evt){
21827 // summary:
21828 // Handler for when the container gets focus
21829 // description:
21830 // Initially the container itself has a tabIndex, but when it gets
21831 // focus, switch focus to first child...
21832 // tags:
21833 // private
21834
21835 // Note that we can't use _onFocus() because switching focus from the
21836 // _onFocus() handler confuses the focus.js code
21837 // (because it causes _onFocusNode() to be called recursively)
21838 // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
21839
21840 // Ignore spurious focus events:
21841 // 1. focus on a child widget bubbles on FF
21842 // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
21843 if(evt.target !== this.domNode || this.focusedChild){ return; }
21844
21845 this.focusFirstChild();
21846
21847 // and then set the container's tabIndex to -1,
21848 // (don't remove as that breaks Safari 4)
21849 // so that tab or shift-tab will go to the fields after/before
21850 // the container, rather than the container itself
21851 domAttr.set(this.domNode, "tabIndex", "-1");
21852 },
21853
21854 _onBlur: function(evt){
21855 // When focus is moved away the container, and its descendant (popup) widgets,
21856 // then restore the container's tabIndex so that user can tab to it again.
21857 // Note that using _onBlur() so that this doesn't happen when focus is shifted
21858 // to one of my child widgets (typically a popup)
21859 if(this.tabIndex){
21860 domAttr.set(this.domNode, "tabIndex", this.tabIndex);
21861 }
21862 this.focusedChild = null;
21863 this.inherited(arguments);
21864 },
21865
21866 _onContainerKeypress: function(evt){
21867 // summary:
21868 // When a key is pressed, if it's an arrow key etc. then
21869 // it's handled here.
21870 // tags:
21871 // private
21872 if(evt.ctrlKey || evt.altKey){ return; }
21873 var func = this._keyNavCodes[evt.charOrCode];
21874 if(func){
21875 func();
21876 event.stop(evt);
21877 }
21878 },
21879
21880 _onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
21881 // summary:
21882 // Called when focus leaves a child widget to go
21883 // to a sibling widget.
21884 // Used by MenuBase.js (TODO: move code there)
21885 // tags:
21886 // protected
21887 },
21888
21889 _getFirstFocusableChild: function(){
21890 // summary:
21891 // Returns first child that can be focused
21892 return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
21893 },
21894
21895 _getLastFocusableChild: function(){
21896 // summary:
21897 // Returns last child that can be focused
21898 return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
21899 },
21900
21901 _getNextFocusableChild: function(child, dir){
21902 // summary:
21903 // Returns the next or previous focusable child, compared
21904 // to "child"
21905 // child: Widget
21906 // The current widget
21907 // dir: Integer
21908 // - 1 = after
21909 // - -1 = before
21910 if(child){
21911 child = this._getSiblingOfChild(child, dir);
21912 }
21913 var children = this.getChildren();
21914 for(var i=0; i < children.length; i++){
21915 if(!child){
21916 child = children[(dir>0) ? 0 : (children.length-1)];
21917 }
21918 if(child.isFocusable()){
21919 return child; // dijit/_WidgetBase
21920 }
21921 child = this._getSiblingOfChild(child, dir);
21922 }
21923 // no focusable child found
21924 return null; // dijit/_WidgetBase
21925 }
21926 });
21927 });
21928
21929 },
21930 'dijit/layout/utils':function(){
21931 define("dijit/layout/utils", [
21932 "dojo/_base/array", // array.filter array.forEach
21933 "dojo/dom-class", // domClass.add domClass.remove
21934 "dojo/dom-geometry", // domGeometry.marginBox
21935 "dojo/dom-style", // domStyle.getComputedStyle
21936 "dojo/_base/lang", // lang.mixin
21937 "../main" // for exporting symbols to dijit, remove in 2.0
21938 ], function(array, domClass, domGeometry, domStyle, lang, dijit){
21939
21940 // module:
21941 // dijit/layout/utils
21942
21943 var layout = lang.getObject("layout", true, dijit);
21944 /*=====
21945 layout = {
21946 // summary:
21947 // marginBox2contentBox() and layoutChildren()
21948 };
21949 =====*/
21950
21951 layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
21952 // summary:
21953 // Given the margin-box size of a node, return its content box size.
21954 // Functions like domGeometry.contentBox() but is more reliable since it doesn't have
21955 // to wait for the browser to compute sizes.
21956 var cs = domStyle.getComputedStyle(node);
21957 var me = domGeometry.getMarginExtents(node, cs);
21958 var pb = domGeometry.getPadBorderExtents(node, cs);
21959 return {
21960 l: domStyle.toPixelValue(node, cs.paddingLeft),
21961 t: domStyle.toPixelValue(node, cs.paddingTop),
21962 w: mb.w - (me.w + pb.w),
21963 h: mb.h - (me.h + pb.h)
21964 };
21965 };
21966
21967 function capitalize(word){
21968 return word.substring(0,1).toUpperCase() + word.substring(1);
21969 }
21970
21971 function size(widget, dim){
21972 // size the child
21973 var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
21974
21975 // record child's size
21976 if(newSize){
21977 // if the child returned it's new size then use that
21978 lang.mixin(widget, newSize);
21979 }else{
21980 // otherwise, call getMarginBox(), but favor our own numbers when we have them.
21981 // the browser lies sometimes
21982 lang.mixin(widget, domGeometry.getMarginBox(widget.domNode));
21983 lang.mixin(widget, dim);
21984 }
21985 }
21986
21987 layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
21988 /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
21989 // summary:
21990 // Layout a bunch of child dom nodes within a parent dom node
21991 // container:
21992 // parent node
21993 // dim:
21994 // {l, t, w, h} object specifying dimensions of container into which to place children
21995 // children:
21996 // An array of Widgets or at least objects containing:
21997 //
21998 // - domNode: pointer to DOM node to position
21999 // - region or layoutAlign: position to place DOM node
22000 // - resize(): (optional) method to set size of node
22001 // - id: (optional) Id of widgets, referenced from resize object, below.
22002 // changedRegionId:
22003 // If specified, the slider for the region with the specified id has been dragged, and thus
22004 // the region's height or width should be adjusted according to changedRegionSize
22005 // changedRegionSize:
22006 // See changedRegionId.
22007
22008 // copy dim because we are going to modify it
22009 dim = lang.mixin({}, dim);
22010
22011 domClass.add(container, "dijitLayoutContainer");
22012
22013 // Move "client" elements to the end of the array for layout. a11y dictates that the author
22014 // needs to be able to put them in the document in tab-order, but this algorithm requires that
22015 // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
22016 children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
22017 .concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
22018
22019 // set positions/sizes
22020 array.forEach(children, function(child){
22021 var elm = child.domNode,
22022 pos = (child.region || child.layoutAlign);
22023 if(!pos){
22024 throw new Error("No region setting for " + child.id)
22025 }
22026
22027 // set elem to upper left corner of unused space; may move it later
22028 var elmStyle = elm.style;
22029 elmStyle.left = dim.l+"px";
22030 elmStyle.top = dim.t+"px";
22031 elmStyle.position = "absolute";
22032
22033 domClass.add(elm, "dijitAlign" + capitalize(pos));
22034
22035 // Size adjustments to make to this child widget
22036 var sizeSetting = {};
22037
22038 // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
22039 // panes and width adjustment for left/right align panes.
22040 if(changedRegionId && changedRegionId == child.id){
22041 sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
22042 }
22043
22044 // set size && adjust record of remaining space.
22045 // note that setting the width of a <div> may affect its height.
22046 if(pos == "top" || pos == "bottom"){
22047 sizeSetting.w = dim.w;
22048 size(child, sizeSetting);
22049 dim.h -= child.h;
22050 if(pos == "top"){
22051 dim.t += child.h;
22052 }else{
22053 elmStyle.top = dim.t + dim.h + "px";
22054 }
22055 }else if(pos == "left" || pos == "right"){
22056 sizeSetting.h = dim.h;
22057 size(child, sizeSetting);
22058 dim.w -= child.w;
22059 if(pos == "left"){
22060 dim.l += child.w;
22061 }else{
22062 elmStyle.left = dim.l + dim.w + "px";
22063 }
22064 }else if(pos == "client" || pos == "center"){
22065 size(child, dim);
22066 }
22067 });
22068 };
22069
22070
22071 return {
22072 marginBox2contentBox: layout.marginBox2contentBox,
22073 layoutChildren: layout.layoutChildren
22074 };
22075 });
22076
22077 },
22078 'dijit/_Contained':function(){
22079 define("dijit/_Contained", [
22080 "dojo/_base/declare", // declare
22081 "./registry" // registry.getEnclosingWidget(), registry.byNode()
22082 ], function(declare, registry){
22083
22084 // module:
22085 // dijit/_Contained
22086
22087 return declare("dijit._Contained", null, {
22088 // summary:
22089 // Mixin for widgets that are children of a container widget
22090 //
22091 // example:
22092 // | // make a basic custom widget that knows about it's parents
22093 // | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
22094
22095 _getSibling: function(/*String*/ which){
22096 // summary:
22097 // Returns next or previous sibling
22098 // which:
22099 // Either "next" or "previous"
22100 // tags:
22101 // private
22102 var node = this.domNode;
22103 do{
22104 node = node[which+"Sibling"];
22105 }while(node && node.nodeType != 1);
22106 return node && registry.byNode(node); // dijit/_WidgetBase
22107 },
22108
22109 getPreviousSibling: function(){
22110 // summary:
22111 // Returns null if this is the first child of the parent,
22112 // otherwise returns the next element sibling to the "left".
22113
22114 return this._getSibling("previous"); // dijit/_WidgetBase
22115 },
22116
22117 getNextSibling: function(){
22118 // summary:
22119 // Returns null if this is the last child of the parent,
22120 // otherwise returns the next element sibling to the "right".
22121
22122 return this._getSibling("next"); // dijit/_WidgetBase
22123 },
22124
22125 getIndexInParent: function(){
22126 // summary:
22127 // Returns the index of this widget within its container parent.
22128 // It returns -1 if the parent does not exist, or if the parent
22129 // is not a dijit._Container
22130
22131 var p = this.getParent();
22132 if(!p || !p.getIndexOfChild){
22133 return -1; // int
22134 }
22135 return p.getIndexOfChild(this); // int
22136 }
22137 });
22138 });
22139
22140 },
22141 'dijit/form/DataList':function(){
22142 define("dijit/form/DataList", [
22143 "dojo/_base/declare", // declare
22144 "dojo/dom", // dom.byId
22145 "dojo/_base/lang", // lang.trim
22146 "dojo/query", // query
22147 "dojo/store/Memory",
22148 "../registry" // registry.add registry.remove
22149 ], function(declare, dom, lang, query, MemoryStore, registry){
22150
22151 // module:
22152 // dijit/form/DataList
22153
22154 function toItem(/*DOMNode*/ option){
22155 // summary:
22156 // Convert `<option>` node to hash
22157 return {
22158 id: option.value,
22159 value: option.value,
22160 name: lang.trim(option.innerText || option.textContent || '')
22161 };
22162 }
22163
22164 return declare("dijit.form.DataList", MemoryStore, {
22165 // summary:
22166 // Inefficient but small data store specialized for inlined data via OPTION tags
22167 //
22168 // description:
22169 // Provides a store for inlined data like:
22170 //
22171 // | <datalist>
22172 // | <option value="AL">Alabama</option>
22173 // | ...
22174
22175 constructor: function(params, srcNodeRef){
22176 // summary:
22177 // Create the widget.
22178 // params: Object|null
22179 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
22180 // and functions, typically callbacks like onClick.
22181 // The hash can contain any of the widget's properties, excluding read-only properties.
22182 // srcNodeRef: DOMNode|String
22183 // Attach widget to this DOM node.
22184
22185 // store pointer to original DOM tree
22186 this.domNode = dom.byId(srcNodeRef);
22187
22188 lang.mixin(this, params);
22189 if(this.id){
22190 registry.add(this); // add to registry so it can be easily found by id
22191 }
22192 this.domNode.style.display = "none";
22193
22194 this.inherited(arguments, [{
22195 data: query("option", this.domNode).map(toItem)
22196 }]);
22197 },
22198
22199 destroy: function(){
22200 registry.remove(this.id);
22201 },
22202
22203 fetchSelectedItem: function(){
22204 // summary:
22205 // Get the option marked as selected, like `<option selected>`.
22206 // Not part of dojo.data API.
22207 var option = query("> option[selected]", this.domNode)[0] || query("> option", this.domNode)[0];
22208 return option && toItem(option);
22209 }
22210 });
22211 });
22212
22213 },
22214 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
22215 'dijit/form/CheckBox':function(){
22216 require({cache:{
22217 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n"}});
22218 define("dijit/form/CheckBox", [
22219 "require",
22220 "dojo/_base/declare", // declare
22221 "dojo/dom-attr", // domAttr.set
22222 "dojo/has", // has("dijit-legacy-requires")
22223 "dojo/query", // query
22224 "dojo/ready",
22225 "./ToggleButton",
22226 "./_CheckBoxMixin",
22227 "dojo/text!./templates/CheckBox.html",
22228 "dojo/NodeList-dom" // NodeList.addClass/removeClass
22229 ], function(require, declare, domAttr, has, query, ready, ToggleButton, _CheckBoxMixin, template){
22230
22231 // module:
22232 // dijit/form/CheckBox
22233
22234 // Back compat w/1.6, remove for 2.0
22235 if(has("dijit-legacy-requires")){
22236 ready(0, function(){
22237 var requires = ["dijit/form/RadioButton"];
22238 require(requires); // use indirection so modules not rolled into a build
22239 });
22240 }
22241
22242 return declare("dijit.form.CheckBox", [ToggleButton, _CheckBoxMixin], {
22243 // summary:
22244 // Same as an HTML checkbox, but with fancy styling.
22245 //
22246 // description:
22247 // User interacts with real html inputs.
22248 // On onclick (which occurs by mouse click, space-bar, or
22249 // using the arrow keys to switch the selected radio button),
22250 // we update the state of the checkbox/radio.
22251 //
22252 // There are two modes:
22253 //
22254 // 1. High contrast mode
22255 // 2. Normal mode
22256 //
22257 // In case 1, the regular html inputs are shown and used by the user.
22258 // In case 2, the regular html inputs are invisible but still used by
22259 // the user. They are turned quasi-invisible and overlay the background-image.
22260
22261 templateString: template,
22262
22263 baseClass: "dijitCheckBox",
22264
22265 _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
22266 // summary:
22267 // Handler for value= attribute to constructor, and also calls to
22268 // set('value', val).
22269 // description:
22270 // During initialization, just saves as attribute to the `<input type=checkbox>`.
22271 //
22272 // After initialization,
22273 // when passed a boolean, controls whether or not the CheckBox is checked.
22274 // If passed a string, changes the value attribute of the CheckBox (the one
22275 // specified as "value" when the CheckBox was constructed
22276 // (ex: `<input data-dojo-type="dijit/CheckBox" value="chicken">`).
22277 //
22278 // `widget.set('value', string)` will check the checkbox and change the value to the
22279 // specified string.
22280 //
22281 // `widget.set('value', boolean)` will change the checked state.
22282
22283 if(typeof newValue == "string"){
22284 this.inherited(arguments);
22285 newValue = true;
22286 }
22287 if(this._created){
22288 this.set('checked', newValue, priorityChange);
22289 }
22290 },
22291 _getValueAttr: function(){
22292 // summary:
22293 // Hook so get('value') works.
22294 // description:
22295 // If the CheckBox is checked, returns the value attribute.
22296 // Otherwise returns false.
22297 return (this.checked ? this.value : false);
22298 },
22299
22300 // Override behavior from Button, since we don't have an iconNode
22301 _setIconClassAttr: null,
22302
22303 postMixInProperties: function(){
22304 this.inherited(arguments);
22305
22306 // Need to set initial checked state as part of template, so that form submit works.
22307 // domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
22308 // to <body>, see #8666
22309 this.checkedAttrSetting = this.checked ? "checked" : "";
22310 },
22311
22312 _fillContent: function(){
22313 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
22314 // since CheckBox doesn't even have a container
22315 },
22316
22317 _onFocus: function(){
22318 if(this.id){
22319 query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
22320 }
22321 this.inherited(arguments);
22322 },
22323
22324 _onBlur: function(){
22325 if(this.id){
22326 query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
22327 }
22328 this.inherited(arguments);
22329 }
22330 });
22331 });
22332
22333 },
22334 'dijit/tree/_dndSelector':function(){
22335 define("dijit/tree/_dndSelector", [
22336 "dojo/_base/array", // array.filter array.forEach array.map
22337 "dojo/_base/connect", // connect.isCopyKey
22338 "dojo/_base/declare", // declare
22339 "dojo/_base/Deferred", // Deferred
22340 "dojo/_base/kernel", // global
22341 "dojo/_base/lang", // lang.hitch
22342 "dojo/cookie", // cookie
22343 "dojo/mouse", // mouse.isLeft
22344 "dojo/on",
22345 "dojo/touch",
22346 "./_dndContainer"
22347 ], function(array, connect, declare, Deferred, kernel, lang, cookie, mouse, on, touch, _dndContainer){
22348
22349 // module:
22350 // dijit/tree/_dndSelector
22351
22352
22353 return declare("dijit.tree._dndSelector", _dndContainer, {
22354 // summary:
22355 // This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
22356 // It's based on `dojo/dnd/Selector`.
22357 // tags:
22358 // protected
22359
22360 /*=====
22361 // selection: Object
22362 // (id to DomNode) map for every TreeNode that's currently selected.
22363 // The DOMNode is the TreeNode.rowNode.
22364 selection: {},
22365 =====*/
22366
22367 constructor: function(){
22368 // summary:
22369 // Initialization
22370 // tags:
22371 // private
22372
22373 this.selection={};
22374 this.anchor = null;
22375
22376 if(!this.cookieName && this.tree.id){
22377 this.cookieName = this.tree.id + "SaveSelectedCookie";
22378 }
22379
22380 this.events.push(
22381 on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
22382 on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
22383 on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
22384 );
22385 },
22386
22387 // singular: Boolean
22388 // Allows selection of only one element, if true.
22389 // Tree hasn't been tested in singular=true mode, unclear if it works.
22390 singular: false,
22391
22392 // methods
22393 getSelectedTreeNodes: function(){
22394 // summary:
22395 // Returns a list of selected node(s).
22396 // Used by dndSource on the start of a drag.
22397 // tags:
22398 // protected
22399 var nodes=[], sel = this.selection;
22400 for(var i in sel){
22401 nodes.push(sel[i]);
22402 }
22403 return nodes;
22404 },
22405
22406 selectNone: function(){
22407 // summary:
22408 // Unselects all items
22409 // tags:
22410 // private
22411
22412 this.setSelection([]);
22413 return this; // self
22414 },
22415
22416 destroy: function(){
22417 // summary:
22418 // Prepares the object to be garbage-collected
22419 this.inherited(arguments);
22420 this.selection = this.anchor = null;
22421 },
22422 addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor){
22423 // summary:
22424 // add node to current selection
22425 // node: Node
22426 // node to add
22427 // isAnchor: Boolean
22428 // Whether the node should become anchor.
22429
22430 this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
22431 if(isAnchor){ this.anchor = node; }
22432 return node;
22433 },
22434 removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
22435 // summary:
22436 // remove node from current selection
22437 // node: Node
22438 // node to remove
22439 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
22440 return node;
22441 },
22442 isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
22443 // summary:
22444 // return true if node is currently selected
22445 // node: Node
22446 // the node to check whether it's in the current selection
22447
22448 return node.id && !!this.selection[node.id];
22449 },
22450 setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
22451 // summary:
22452 // set the list of selected nodes to be exactly newSelection. All changes to the
22453 // selection should be passed through this function, which ensures that derived
22454 // attributes are kept up to date. Anchor will be deleted if it has been removed
22455 // from the selection, but no new anchor will be added by this function.
22456 // newSelection: Node[]
22457 // list of tree nodes to make selected
22458 var oldSelection = this.getSelectedTreeNodes();
22459 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
22460 node.setSelected(false);
22461 if(this.anchor == node){
22462 delete this.anchor;
22463 }
22464 delete this.selection[node.id];
22465 }));
22466 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
22467 node.setSelected(true);
22468 this.selection[node.id] = node;
22469 }));
22470 this._updateSelectionProperties();
22471 },
22472 _setDifference: function(xs,ys){
22473 // summary:
22474 // Returns a copy of xs which lacks any objects
22475 // occurring in ys. Checks for membership by
22476 // modifying and then reading the object, so it will
22477 // not properly handle sets of numbers or strings.
22478
22479 array.forEach(ys, function(y){ y.__exclude__ = true; });
22480 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
22481
22482 // clean up after ourselves.
22483 array.forEach(ys, function(y){ delete y['__exclude__'] });
22484 return ret;
22485 },
22486 _updateSelectionProperties: function(){
22487 // summary:
22488 // Update the following tree properties from the current selection:
22489 // path[s], selectedItem[s], selectedNode[s]
22490
22491 var selected = this.getSelectedTreeNodes();
22492 var paths = [], nodes = [], selects = [];
22493 array.forEach(selected, function(node){
22494 var ary = node.getTreePath(), model = this.tree.model;
22495 nodes.push(node);
22496 paths.push(ary);
22497 ary = array.map(ary, function(item){
22498 return model.getIdentity(item);
22499 }, this);
22500 selects.push(ary.join("/"))
22501 }, this);
22502 var items = array.map(nodes,function(node){ return node.item; });
22503 this.tree._set("paths", paths);
22504 this.tree._set("path", paths[0] || []);
22505 this.tree._set("selectedNodes", nodes);
22506 this.tree._set("selectedNode", nodes[0] || null);
22507 this.tree._set("selectedItems", items);
22508 this.tree._set("selectedItem", items[0] || null);
22509 if (this.tree.persist && selects.length > 0) {
22510 cookie(this.cookieName, selects.join(","), {expires:365});
22511 }
22512 },
22513 _getSavedPaths: function(){
22514 // summary:
22515 // Returns paths of nodes that were selected previously and saved in the cookie.
22516
22517 var tree = this.tree;
22518 if(tree.persist && tree.dndController.cookieName){
22519 var oreo, paths = [];
22520 oreo = cookie(tree.dndController.cookieName);
22521 if(oreo){
22522 paths = array.map(oreo.split(","), function(path){
22523 return path.split("/");
22524 })
22525 }
22526 return paths;
22527 }
22528 },
22529 // mouse events
22530 onMouseDown: function(e){
22531 // summary:
22532 // Event processor for onmousedown/ontouchstart
22533 // e: Event
22534 // onmousedown/ontouchstart event
22535 // tags:
22536 // protected
22537
22538 // ignore click on expando node
22539 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
22540
22541 if(mouse.isLeft(e)){
22542 // Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
22543 // for mobile because it will break things completely, see #15838.
22544 e.preventDefault();
22545 }else if(e.type != "touchstart"){
22546 // Ignore right click
22547 return;
22548 }
22549
22550 var treeNode = this.current,
22551 copy = connect.isCopyKey(e), id = treeNode.id;
22552
22553 // if shift key is not pressed, and the node is already in the selection,
22554 // delay deselection until onmouseup so in the case of DND, deselection
22555 // will be canceled by onmousemove.
22556 if(!this.singular && !e.shiftKey && this.selection[id]){
22557 this._doDeselect = true;
22558 return;
22559 }else{
22560 this._doDeselect = false;
22561 }
22562 this.userSelect(treeNode, copy, e.shiftKey);
22563 },
22564
22565 onMouseUp: function(e){
22566 // summary:
22567 // Event processor for onmouseup/ontouchend
22568 // e: Event
22569 // onmouseup/ontouchend event
22570 // tags:
22571 // protected
22572
22573 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
22574 // a already selected item (to deselect the item), or click on a not-yet selected item
22575 // (which should remove all current selection, and add the clicked item). This can not
22576 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
22577 // the deselection logic here, the user can drags an already selected item.
22578 if(!this._doDeselect){ return; }
22579 this._doDeselect = false;
22580 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
22581 },
22582 onMouseMove: function(/*===== e =====*/){
22583 // summary:
22584 // event processor for onmousemove/ontouchmove
22585 // e: Event
22586 // onmousemove/ontouchmove event
22587 this._doDeselect = false;
22588 },
22589
22590 _compareNodes: function(n1, n2){
22591 if(n1 === n2){
22592 return 0;
22593 }
22594
22595 if('sourceIndex' in document.documentElement){ //IE
22596 //TODO: does not yet work if n1 and/or n2 is a text node
22597 return n1.sourceIndex - n2.sourceIndex;
22598 }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
22599 return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
22600 }else if(document.createRange){ //Webkit
22601 var r1 = doc.createRange();
22602 r1.setStartBefore(n1);
22603
22604 var r2 = doc.createRange();
22605 r2.setStartBefore(n2);
22606
22607 return r1.compareBoundaryPoints(r1.END_TO_END, r2);
22608 }else{
22609 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
22610 }
22611 },
22612
22613 userSelect: function(node, multi, range){
22614 // summary:
22615 // Add or remove the given node from selection, responding
22616 // to a user action such as a click or keypress.
22617 // multi: Boolean
22618 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
22619 // range: Boolean
22620 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
22621 // tags:
22622 // protected
22623
22624 if(this.singular){
22625 if(this.anchor == node && multi){
22626 this.selectNone();
22627 }else{
22628 this.setSelection([node]);
22629 this.anchor = node;
22630 }
22631 }else{
22632 if(range && this.anchor){
22633 var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
22634 begin, end, anchor = this.anchor;
22635
22636 if(cr < 0){ //current is after anchor
22637 begin = anchor;
22638 end = node;
22639 }else{ //current is before anchor
22640 begin = node;
22641 end = anchor;
22642 }
22643 var nodes = [];
22644 //add everything betweeen begin and end inclusively
22645 while(begin != end){
22646 nodes.push(begin);
22647 begin = this.tree._getNextNode(begin);
22648 }
22649 nodes.push(end);
22650
22651 this.setSelection(nodes);
22652 }else{
22653 if( this.selection[ node.id ] && multi ){
22654 this.removeTreeNode( node );
22655 }else if(multi){
22656 this.addTreeNode(node, true);
22657 }else{
22658 this.setSelection([node]);
22659 this.anchor = node;
22660 }
22661 }
22662 }
22663 },
22664
22665 getItem: function(/*String*/ key){
22666 // summary:
22667 // Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
22668 // Called by dojo/dnd/Source.checkAcceptance().
22669 // tags:
22670 // protected
22671
22672 var widget = this.selection[key];
22673 return {
22674 data: widget,
22675 type: ["treeNode"]
22676 }; // dojo/dnd/Container._Item
22677 },
22678
22679 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
22680 // summary:
22681 // Iterates over selected items;
22682 // see `dojo/dnd/Container.forInItems()` for details
22683 o = o || kernel.global;
22684 for(var id in this.selection){
22685 // console.log("selected item id: " + id);
22686 f.call(o, this.getItem(id), id, this);
22687 }
22688 }
22689 });
22690 });
22691
22692 },
22693 'dijit/_Container':function(){
22694 define("dijit/_Container", [
22695 "dojo/_base/array", // array.forEach array.indexOf
22696 "dojo/_base/declare", // declare
22697 "dojo/dom-construct" // domConstruct.place
22698 ], function(array, declare, domConstruct){
22699
22700 // module:
22701 // dijit/_Container
22702
22703 return declare("dijit._Container", null, {
22704 // summary:
22705 // Mixin for widgets that contain HTML and/or a set of widget children.
22706
22707 buildRendering: function(){
22708 this.inherited(arguments);
22709 if(!this.containerNode){
22710 // all widgets with descendants must set containerNode
22711 this.containerNode = this.domNode;
22712 }
22713 },
22714
22715 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
22716 // summary:
22717 // Makes the given widget a child of this widget.
22718 // description:
22719 // Inserts specified child widget's dom node as a child of this widget's
22720 // container node, and possibly does other processing (such as layout).
22721 //
22722 // Functionality is undefined if this widget contains anything besides
22723 // a list of child widgets (ie, if it contains arbitrary non-widget HTML).
22724
22725 var refNode = this.containerNode;
22726 if(insertIndex && typeof insertIndex == "number"){
22727 var children = this.getChildren();
22728 if(children && children.length >= insertIndex){
22729 refNode = children[insertIndex-1].domNode;
22730 insertIndex = "after";
22731 }
22732 }
22733 domConstruct.place(widget.domNode, refNode, insertIndex);
22734
22735 // If I've been started but the child widget hasn't been started,
22736 // start it now. Make sure to do this after widget has been
22737 // inserted into the DOM tree, so it can see that it's being controlled by me,
22738 // so it doesn't try to size itself.
22739 if(this._started && !widget._started){
22740 widget.startup();
22741 }
22742 },
22743
22744 removeChild: function(/*Widget|int*/ widget){
22745 // summary:
22746 // Removes the passed widget instance from this widget but does
22747 // not destroy it. You can also pass in an integer indicating
22748 // the index within the container to remove (ie, removeChild(5) removes the sixth widget).
22749
22750 if(typeof widget == "number"){
22751 widget = this.getChildren()[widget];
22752 }
22753
22754 if(widget){
22755 var node = widget.domNode;
22756 if(node && node.parentNode){
22757 node.parentNode.removeChild(node); // detach but don't destroy
22758 }
22759 }
22760 },
22761
22762 hasChildren: function(){
22763 // summary:
22764 // Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
22765 return this.getChildren().length > 0; // Boolean
22766 },
22767
22768 _getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir){
22769 // summary:
22770 // Get the next or previous widget sibling of child
22771 // dir:
22772 // if 1, get the next sibling
22773 // if -1, get the previous sibling
22774 // tags:
22775 // private
22776 var children = this.getChildren(),
22777 idx = array.indexOf(this.getChildren(), child); // int
22778 return children[idx + dir];
22779 },
22780
22781 getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
22782 // summary:
22783 // Gets the index of the child in this container or -1 if not found
22784 return array.indexOf(this.getChildren(), child); // int
22785 }
22786 });
22787 });
22788
22789 },
22790 'dojo/data/ItemFileReadStore':function(){
22791 define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
22792 "../Evented", "./util/filter", "./util/simpleFetch", "../date/stamp"
22793 ], function(kernel, lang, declare, array, xhr, Evented, filterUtil, simpleFetch, dateStamp){
22794
22795 // module:
22796 // dojo/data/ItemFileReadStore
22797
22798 var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
22799 // summary:
22800 // The ItemFileReadStore implements the dojo/data/api/Read API and reads
22801 // data from JSON files that have contents in this format --
22802 // | { items: [
22803 // | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
22804 // | { name:'Fozzie Bear', wears:['hat', 'tie']},
22805 // | { name:'Miss Piggy', pets:'Foo-Foo'}
22806 // | ]}
22807 // Note that it can also contain an 'identifier' property that specified which attribute on the items
22808 // in the array of items that acts as the unique identifier for that item.
22809
22810 constructor: function(/* Object */ keywordParameters){
22811 // summary:
22812 // constructor
22813 // keywordParameters:
22814 // {url: String} {data: jsonObject} {typeMap: object}
22815 // The structure of the typeMap object is as follows:
22816 // | {
22817 // | type0: function || object,
22818 // | type1: function || object,
22819 // | ...
22820 // | typeN: function || object
22821 // | }
22822 // Where if it is a function, it is assumed to be an object constructor that takes the
22823 // value of _value as the initialization parameters. If it is an object, then it is assumed
22824 // to be an object of general form:
22825 // | {
22826 // | type: function, //constructor.
22827 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
22828 // | }
22829
22830 this._arrayOfAllItems = [];
22831 this._arrayOfTopLevelItems = [];
22832 this._loadFinished = false;
22833 this._jsonFileUrl = keywordParameters.url;
22834 this._ccUrl = keywordParameters.url;
22835 this.url = keywordParameters.url;
22836 this._jsonData = keywordParameters.data;
22837 this.data = null;
22838 this._datatypeMap = keywordParameters.typeMap || {};
22839 if(!this._datatypeMap['Date']){
22840 //If no default mapping for dates, then set this as default.
22841 //We use the dojo/date/stamp here because the ISO format is the 'dojo way'
22842 //of generically representing dates.
22843 this._datatypeMap['Date'] = {
22844 type: Date,
22845 deserialize: function(value){
22846 return dateStamp.fromISOString(value);
22847 }
22848 };
22849 }
22850 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
22851 this._itemsByIdentity = null;
22852 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
22853 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
22854 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
22855 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
22856 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
22857 this._queuedFetches = [];
22858 if(keywordParameters.urlPreventCache !== undefined){
22859 this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
22860 }
22861 if(keywordParameters.hierarchical !== undefined){
22862 this.hierarchical = keywordParameters.hierarchical?true:false;
22863 }
22864 if(keywordParameters.clearOnClose){
22865 this.clearOnClose = true;
22866 }
22867 if("failOk" in keywordParameters){
22868 this.failOk = keywordParameters.failOk?true:false;
22869 }
22870 },
22871
22872 url: "", // use "" rather than undefined for the benefit of the parser (#3539)
22873
22874 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
22875 //when clearOnClose and close is used.
22876 _ccUrl: "",
22877
22878 data: null, // define this so that the parser can populate it
22879
22880 typeMap: null, //Define so parser can populate.
22881
22882 // clearOnClose: Boolean
22883 // Parameter to allow users to specify if a close call should force a reload or not.
22884 // By default, it retains the old behavior of not clearing if close is called. But
22885 // if set true, the store will be reset to default state. Note that by doing this,
22886 // all item handles will become invalid and a new fetch must be issued.
22887 clearOnClose: false,
22888
22889 // urlPreventCache: Boolean
22890 // Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
22891 // Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
22892 // Added for tracker: #6072
22893 urlPreventCache: false,
22894
22895 // failOk: Boolean
22896 // Parameter for specifying that it is OK for the xhrGet call to fail silently.
22897 failOk: false,
22898
22899 // hierarchical: Boolean
22900 // Parameter to indicate to process data from the url as hierarchical
22901 // (data items can contain other data items in js form). Default is true
22902 // for backwards compatibility. False means only root items are processed
22903 // as items, all child objects outside of type-mapped objects and those in
22904 // specific reference format, are left straight JS data objects.
22905 hierarchical: true,
22906
22907 _assertIsItem: function(/* dojo/data/api/Item */ item){
22908 // summary:
22909 // This function tests whether the item passed in is indeed an item in the store.
22910 // item:
22911 // The item to test for being contained by the store.
22912 if(!this.isItem(item)){
22913 throw new Error(this.declaredClass + ": Invalid item argument.");
22914 }
22915 },
22916
22917 _assertIsAttribute: function(/* attribute-name-string */ attribute){
22918 // summary:
22919 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
22920 // attribute:
22921 // The attribute to test for being contained by the store.
22922 if(typeof attribute !== "string"){
22923 throw new Error(this.declaredClass + ": Invalid attribute argument.");
22924 }
22925 },
22926
22927 getValue: function( /* dojo/data/api/Item */ item,
22928 /* attribute-name-string */ attribute,
22929 /* value? */ defaultValue){
22930 // summary:
22931 // See dojo/data/api/Read.getValue()
22932 var values = this.getValues(item, attribute);
22933 return (values.length > 0)?values[0]:defaultValue; // mixed
22934 },
22935
22936 getValues: function(/* dojo/data/api/Item */ item,
22937 /* attribute-name-string */ attribute){
22938 // summary:
22939 // See dojo/data/api/Read.getValues()
22940
22941 this._assertIsItem(item);
22942 this._assertIsAttribute(attribute);
22943 // Clone it before returning. refs: #10474
22944 return (item[attribute] || []).slice(0); // Array
22945 },
22946
22947 getAttributes: function(/* dojo/data/api/Item */ item){
22948 // summary:
22949 // See dojo/data/api/Read.getAttributes()
22950 this._assertIsItem(item);
22951 var attributes = [];
22952 for(var key in item){
22953 // Save off only the real item attributes, not the special id marks for O(1) isItem.
22954 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
22955 attributes.push(key);
22956 }
22957 }
22958 return attributes; // Array
22959 },
22960
22961 hasAttribute: function( /* dojo/data/api/Item */ item,
22962 /* attribute-name-string */ attribute){
22963 // summary:
22964 // See dojo/data/api/Read.hasAttribute()
22965 this._assertIsItem(item);
22966 this._assertIsAttribute(attribute);
22967 return (attribute in item);
22968 },
22969
22970 containsValue: function(/* dojo/data/api/Item */ item,
22971 /* attribute-name-string */ attribute,
22972 /* anything */ value){
22973 // summary:
22974 // See dojo/data/api/Read.containsValue()
22975 var regexp = undefined;
22976 if(typeof value === "string"){
22977 regexp = filterUtil.patternToRegExp(value, false);
22978 }
22979 return this._containsValue(item, attribute, value, regexp); //boolean.
22980 },
22981
22982 _containsValue: function( /* dojo/data/api/Item */ item,
22983 /* attribute-name-string */ attribute,
22984 /* anything */ value,
22985 /* RegExp?*/ regexp){
22986 // summary:
22987 // Internal function for looking at the values contained by the item.
22988 // description:
22989 // Internal function for looking at the values contained by the item. This
22990 // function allows for denoting if the comparison should be case sensitive for
22991 // strings or not (for handling filtering cases where string case should not matter)
22992 // item:
22993 // The data item to examine for attribute values.
22994 // attribute:
22995 // The attribute to inspect.
22996 // value:
22997 // The value to match.
22998 // regexp:
22999 // Optional regular expression generated off value if value was of string type to handle wildcarding.
23000 // If present and attribute values are string, then it can be used for comparison instead of 'value'
23001 return array.some(this.getValues(item, attribute), function(possibleValue){
23002 if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
23003 if(possibleValue.toString().match(regexp)){
23004 return true; // Boolean
23005 }
23006 }else if(value === possibleValue){
23007 return true; // Boolean
23008 }
23009 });
23010 },
23011
23012 isItem: function(/* anything */ something){
23013 // summary:
23014 // See dojo/data/api/Read.isItem()
23015 if(something && something[this._storeRefPropName] === this){
23016 if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
23017 return true;
23018 }
23019 }
23020 return false; // Boolean
23021 },
23022
23023 isItemLoaded: function(/* anything */ something){
23024 // summary:
23025 // See dojo/data/api/Read.isItemLoaded()
23026 return this.isItem(something); //boolean
23027 },
23028
23029 loadItem: function(/* object */ keywordArgs){
23030 // summary:
23031 // See dojo/data/api/Read.loadItem()
23032 this._assertIsItem(keywordArgs.item);
23033 },
23034
23035 getFeatures: function(){
23036 // summary:
23037 // See dojo/data/api/Read.getFeatures()
23038 return this._features; //Object
23039 },
23040
23041 getLabel: function(/* dojo/data/api/Item */ item){
23042 // summary:
23043 // See dojo/data/api/Read.getLabel()
23044 if(this._labelAttr && this.isItem(item)){
23045 return this.getValue(item,this._labelAttr); //String
23046 }
23047 return undefined; //undefined
23048 },
23049
23050 getLabelAttributes: function(/* dojo/data/api/Item */ item){
23051 // summary:
23052 // See dojo/data/api/Read.getLabelAttributes()
23053 if(this._labelAttr){
23054 return [this._labelAttr]; //array
23055 }
23056 return null; //null
23057 },
23058
23059 filter: function(/* Object */ requestArgs, /* item[] */ arrayOfItems, /* Function */ findCallback){
23060 // summary:
23061 // This method handles the basic filtering needs for ItemFile* based stores.
23062 var items = [],
23063 i, key;
23064
23065 if(requestArgs.query){
23066 var value,
23067 ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
23068
23069 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
23070 //same value for each item examined. Much more efficient.
23071 var regexpList = {};
23072 for(key in requestArgs.query){
23073 value = requestArgs.query[key];
23074 if(typeof value === "string"){
23075 regexpList[key] = filterUtil.patternToRegExp(value, ignoreCase);
23076 }else if(value instanceof RegExp){
23077 regexpList[key] = value;
23078 }
23079 }
23080 for(i = 0; i < arrayOfItems.length; ++i){
23081 var match = true;
23082 var candidateItem = arrayOfItems[i];
23083 if(candidateItem === null){
23084 match = false;
23085 }else{
23086 for(key in requestArgs.query){
23087 value = requestArgs.query[key];
23088 if(!this._containsValue(candidateItem, key, value, regexpList[key])){
23089 match = false;
23090 }
23091 }
23092 }
23093 if(match){
23094 items.push(candidateItem);
23095 }
23096 }
23097 findCallback(items, requestArgs);
23098 }else{
23099 // We want a copy to pass back in case the parent wishes to sort the array.
23100 // We shouldn't allow resort of the internal list, so that multiple callers
23101 // can get lists and sort without affecting each other. We also need to
23102 // filter out any null values that have been left as a result of deleteItem()
23103 // calls in ItemFileWriteStore.
23104 for(i = 0; i < arrayOfItems.length; ++i){
23105 var item = arrayOfItems[i];
23106 if(item !== null){
23107 items.push(item);
23108 }
23109 }
23110 findCallback(items, requestArgs);
23111 }
23112 },
23113
23114 _fetchItems: function( /* Object */ keywordArgs,
23115 /* Function */ findCallback,
23116 /* Function */ errorCallback){
23117 // summary:
23118 // See dojo/data/util.simpleFetch.fetch()
23119 var self = this;
23120
23121 if(this._loadFinished){
23122 this.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
23123 }else{
23124 //Do a check on the JsonFileUrl and crosscheck it.
23125 //If it doesn't match the cross-check, it needs to be updated
23126 //This allows for either url or _jsonFileUrl to he changed to
23127 //reset the store load location. Done this way for backwards
23128 //compatibility. People use _jsonFileUrl (even though officially
23129 //private.
23130 if(this._jsonFileUrl !== this._ccUrl){
23131 kernel.deprecated(this.declaredClass + ": ",
23132 "To change the url, set the url property of the store," +
23133 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23134 this._ccUrl = this._jsonFileUrl;
23135 this.url = this._jsonFileUrl;
23136 }else if(this.url !== this._ccUrl){
23137 this._jsonFileUrl = this.url;
23138 this._ccUrl = this.url;
23139 }
23140
23141 //See if there was any forced reset of data.
23142 if(this.data != null){
23143 this._jsonData = this.data;
23144 this.data = null;
23145 }
23146
23147 if(this._jsonFileUrl){
23148 //If fetches come in before the loading has finished, but while
23149 //a load is in progress, we have to defer the fetching to be
23150 //invoked in the callback.
23151 if(this._loadInProgress){
23152 this._queuedFetches.push({args: keywordArgs, filter: lang.hitch(self, "filter"), findCallback: lang.hitch(self, findCallback)});
23153 }else{
23154 this._loadInProgress = true;
23155 var getArgs = {
23156 url: self._jsonFileUrl,
23157 handleAs: "json-comment-optional",
23158 preventCache: this.urlPreventCache,
23159 failOk: this.failOk
23160 };
23161 var getHandler = xhr.get(getArgs);
23162 getHandler.addCallback(function(data){
23163 try{
23164 self._getItemsFromLoadedData(data);
23165 self._loadFinished = true;
23166 self._loadInProgress = false;
23167
23168 self.filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions), findCallback);
23169 self._handleQueuedFetches();
23170 }catch(e){
23171 self._loadFinished = true;
23172 self._loadInProgress = false;
23173 errorCallback(e, keywordArgs);
23174 }
23175 });
23176 getHandler.addErrback(function(error){
23177 self._loadInProgress = false;
23178 errorCallback(error, keywordArgs);
23179 });
23180
23181 //Wire up the cancel to abort of the request
23182 //This call cancel on the deferred if it hasn't been called
23183 //yet and then will chain to the simple abort of the
23184 //simpleFetch keywordArgs
23185 var oldAbort = null;
23186 if(keywordArgs.abort){
23187 oldAbort = keywordArgs.abort;
23188 }
23189 keywordArgs.abort = function(){
23190 var df = getHandler;
23191 if(df && df.fired === -1){
23192 df.cancel();
23193 df = null;
23194 }
23195 if(oldAbort){
23196 oldAbort.call(keywordArgs);
23197 }
23198 };
23199 }
23200 }else if(this._jsonData){
23201 try{
23202 this._loadFinished = true;
23203 this._getItemsFromLoadedData(this._jsonData);
23204 this._jsonData = null;
23205 self.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
23206 }catch(e){
23207 errorCallback(e, keywordArgs);
23208 }
23209 }else{
23210 errorCallback(new Error(this.declaredClass + ": No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
23211 }
23212 }
23213 },
23214
23215 _handleQueuedFetches: function(){
23216 // summary:
23217 // Internal function to execute delayed request in the store.
23218
23219 //Execute any deferred fetches now.
23220 if(this._queuedFetches.length > 0){
23221 for(var i = 0; i < this._queuedFetches.length; i++){
23222 var fData = this._queuedFetches[i],
23223 delayedQuery = fData.args,
23224 delayedFilter = fData.filter,
23225 delayedFindCallback = fData.findCallback;
23226 if(delayedFilter){
23227 delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions), delayedFindCallback);
23228 }else{
23229 this.fetchItemByIdentity(delayedQuery);
23230 }
23231 }
23232 this._queuedFetches = [];
23233 }
23234 },
23235
23236 _getItemsArray: function(/*object?*/queryOptions){
23237 // summary:
23238 // Internal function to determine which list of items to search over.
23239 // queryOptions: The query options parameter, if any.
23240 if(queryOptions && queryOptions.deep){
23241 return this._arrayOfAllItems;
23242 }
23243 return this._arrayOfTopLevelItems;
23244 },
23245
23246 close: function(/*dojo/data/api/Request|Object?*/ request){
23247 // summary:
23248 // See dojo/data/api/Read.close()
23249 if(this.clearOnClose &&
23250 this._loadFinished &&
23251 !this._loadInProgress){
23252 //Reset all internalsback to default state. This will force a reload
23253 //on next fetch. This also checks that the data or url param was set
23254 //so that the store knows it can get data. Without one of those being set,
23255 //the next fetch will trigger an error.
23256
23257 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
23258 (this.url == "" || this.url == null)
23259 ) && this.data == null){
23260 console.debug(this.declaredClass + ": WARNING! Data reload " +
23261 " information has not been provided." +
23262 " Please set 'url' or 'data' to the appropriate value before" +
23263 " the next fetch");
23264 }
23265 this._arrayOfAllItems = [];
23266 this._arrayOfTopLevelItems = [];
23267 this._loadFinished = false;
23268 this._itemsByIdentity = null;
23269 this._loadInProgress = false;
23270 this._queuedFetches = [];
23271 }
23272 },
23273
23274 _getItemsFromLoadedData: function(/* Object */ dataObject){
23275 // summary:
23276 // Function to parse the loaded data into item format and build the internal items array.
23277 // description:
23278 // Function to parse the loaded data into item format and build the internal items array.
23279 // dataObject:
23280 // The JS data object containing the raw data to convery into item format.
23281 // returns: Array
23282 // Array of items in store item format.
23283
23284 // First, we define a couple little utility functions...
23285 var addingArrays = false,
23286 self = this;
23287
23288 function valueIsAnItem(/* anything */ aValue){
23289 // summary:
23290 // Given any sort of value that could be in the raw json data,
23291 // return true if we should interpret the value as being an
23292 // item itself, rather than a literal value or a reference.
23293 // example:
23294 // | false == valueIsAnItem("Kermit");
23295 // | false == valueIsAnItem(42);
23296 // | false == valueIsAnItem(new Date());
23297 // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
23298 // | false == valueIsAnItem({_reference:'Kermit'});
23299 // | true == valueIsAnItem({name:'Kermit', color:'green'});
23300 // | true == valueIsAnItem({iggy:'pop'});
23301 // | true == valueIsAnItem({foo:42});
23302 return (aValue !== null) &&
23303 (typeof aValue === "object") &&
23304 (!lang.isArray(aValue) || addingArrays) &&
23305 (!lang.isFunction(aValue)) &&
23306 (aValue.constructor == Object || lang.isArray(aValue)) &&
23307 (typeof aValue._reference === "undefined") &&
23308 (typeof aValue._type === "undefined") &&
23309 (typeof aValue._value === "undefined") &&
23310 self.hierarchical;
23311 }
23312
23313 function addItemAndSubItemsToArrayOfAllItems(/* dojo/data/api/Item */ anItem){
23314 self._arrayOfAllItems.push(anItem);
23315 for(var attribute in anItem){
23316 var valueForAttribute = anItem[attribute];
23317 if(valueForAttribute){
23318 if(lang.isArray(valueForAttribute)){
23319 var valueArray = valueForAttribute;
23320 for(var k = 0; k < valueArray.length; ++k){
23321 var singleValue = valueArray[k];
23322 if(valueIsAnItem(singleValue)){
23323 addItemAndSubItemsToArrayOfAllItems(singleValue);
23324 }
23325 }
23326 }else{
23327 if(valueIsAnItem(valueForAttribute)){
23328 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
23329 }
23330 }
23331 }
23332 }
23333 }
23334
23335 this._labelAttr = dataObject.label;
23336
23337 // We need to do some transformations to convert the data structure
23338 // that we read from the file into a format that will be convenient
23339 // to work with in memory.
23340
23341 // Step 1: Walk through the object hierarchy and build a list of all items
23342 var i,
23343 item;
23344 this._arrayOfAllItems = [];
23345 this._arrayOfTopLevelItems = dataObject.items;
23346
23347 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
23348 item = this._arrayOfTopLevelItems[i];
23349 if(lang.isArray(item)){
23350 addingArrays = true;
23351 }
23352 addItemAndSubItemsToArrayOfAllItems(item);
23353 item[this._rootItemPropName]=true;
23354 }
23355
23356 // Step 2: Walk through all the attribute values of all the items,
23357 // and replace single values with arrays. For example, we change this:
23358 // { name:'Miss Piggy', pets:'Foo-Foo'}
23359 // into this:
23360 // { name:['Miss Piggy'], pets:['Foo-Foo']}
23361 //
23362 // We also store the attribute names so we can validate our store
23363 // reference and item id special properties for the O(1) isItem
23364 var allAttributeNames = {},
23365 key;
23366
23367 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23368 item = this._arrayOfAllItems[i];
23369 for(key in item){
23370 if(key !== this._rootItemPropName){
23371 var value = item[key];
23372 if(value !== null){
23373 if(!lang.isArray(value)){
23374 item[key] = [value];
23375 }
23376 }else{
23377 item[key] = [null];
23378 }
23379 }
23380 allAttributeNames[key]=key;
23381 }
23382 }
23383
23384 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
23385 // This should go really fast, it will generally never even run the loop.
23386 while(allAttributeNames[this._storeRefPropName]){
23387 this._storeRefPropName += "_";
23388 }
23389 while(allAttributeNames[this._itemNumPropName]){
23390 this._itemNumPropName += "_";
23391 }
23392 while(allAttributeNames[this._reverseRefMap]){
23393 this._reverseRefMap += "_";
23394 }
23395
23396 // Step 4: Some data files specify an optional 'identifier', which is
23397 // the name of an attribute that holds the identity of each item.
23398 // If this data file specified an identifier attribute, then build a
23399 // hash table of items keyed by the identity of the items.
23400 var arrayOfValues;
23401
23402 var identifier = dataObject.identifier;
23403 if(identifier){
23404 this._itemsByIdentity = {};
23405 this._features['dojo.data.api.Identity'] = identifier;
23406 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23407 item = this._arrayOfAllItems[i];
23408 arrayOfValues = item[identifier];
23409 var identity = arrayOfValues[0];
23410 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23411 this._itemsByIdentity[identity] = item;
23412 }else{
23413 if(this._jsonFileUrl){
23414 throw new Error(this.declaredClass + ": The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23415 }else if(this._jsonData){
23416 throw new Error(this.declaredClass + ": The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23417 }
23418 }
23419 }
23420 }else{
23421 this._features['dojo.data.api.Identity'] = Number;
23422 }
23423
23424 // Step 5: Walk through all the items, and set each item's properties
23425 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
23426 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23427 item = this._arrayOfAllItems[i];
23428 item[this._storeRefPropName] = this;
23429 item[this._itemNumPropName] = i;
23430 }
23431
23432 // Step 6: We walk through all the attribute values of all the items,
23433 // looking for type/value literals and item-references.
23434 //
23435 // We replace item-references with pointers to items. For example, we change:
23436 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23437 // into this:
23438 // { name:['Kermit'], friends:[miss_piggy] }
23439 // (where miss_piggy is the object representing the 'Miss Piggy' item).
23440 //
23441 // We replace type/value pairs with typed-literals. For example, we change:
23442 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
23443 // into this:
23444 // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
23445 //
23446 // We also generate the associate map for all items for the O(1) isItem function.
23447 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23448 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23449 for(key in item){
23450 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
23451 for(var j = 0; j < arrayOfValues.length; ++j){
23452 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
23453 if(value !== null && typeof value == "object"){
23454 if(("_type" in value) && ("_value" in value)){
23455 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
23456 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
23457 if(!mappingObj){
23458 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
23459 }else if(lang.isFunction(mappingObj)){
23460 arrayOfValues[j] = new mappingObj(value._value);
23461 }else if(lang.isFunction(mappingObj.deserialize)){
23462 arrayOfValues[j] = mappingObj.deserialize(value._value);
23463 }else{
23464 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
23465 }
23466 }
23467 if(value._reference){
23468 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
23469 if(!lang.isObject(referenceDescription)){
23470 // example: 'Miss Piggy'
23471 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
23472 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
23473 }else{
23474 // example: {name:'Miss Piggy'}
23475 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23476 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
23477 var candidateItem = this._arrayOfAllItems[k],
23478 found = true;
23479 for(var refKey in referenceDescription){
23480 if(candidateItem[refKey] != referenceDescription[refKey]){
23481 found = false;
23482 }
23483 }
23484 if(found){
23485 arrayOfValues[j] = candidateItem;
23486 }
23487 }
23488 }
23489 if(this.referenceIntegrity){
23490 var refItem = arrayOfValues[j];
23491 if(this.isItem(refItem)){
23492 this._addReferenceToMap(refItem, item, key);
23493 }
23494 }
23495 }else if(this.isItem(value)){
23496 //It's a child item (not one referenced through _reference).
23497 //We need to treat this as a referenced item, so it can be cleaned up
23498 //in a write store easily.
23499 if(this.referenceIntegrity){
23500 this._addReferenceToMap(value, item, key);
23501 }
23502 }
23503 }
23504 }
23505 }
23506 }
23507 },
23508
23509 _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
23510 // summary:
23511 // Method to add an reference map entry for an item and attribute.
23512 // description:
23513 // Method to add an reference map entry for an item and attribute.
23514 // refItem:
23515 // The item that is referenced.
23516 // parentItem:
23517 // The item that holds the new reference to refItem.
23518 // attribute:
23519 // The attribute on parentItem that contains the new reference.
23520
23521 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
23522 },
23523
23524 getIdentity: function(/* dojo/data/api/Item */ item){
23525 // summary:
23526 // See dojo/data/api/Identity.getIdentity()
23527 var identifier = this._features['dojo.data.api.Identity'];
23528 if(identifier === Number){
23529 return item[this._itemNumPropName]; // Number
23530 }else{
23531 var arrayOfValues = item[identifier];
23532 if(arrayOfValues){
23533 return arrayOfValues[0]; // Object|String
23534 }
23535 }
23536 return null; // null
23537 },
23538
23539 fetchItemByIdentity: function(/* Object */ keywordArgs){
23540 // summary:
23541 // See dojo/data/api/Identity.fetchItemByIdentity()
23542
23543 // Hasn't loaded yet, we have to trigger the load.
23544 var item,
23545 scope;
23546 if(!this._loadFinished){
23547 var self = this;
23548 //Do a check on the JsonFileUrl and crosscheck it.
23549 //If it doesn't match the cross-check, it needs to be updated
23550 //This allows for either url or _jsonFileUrl to he changed to
23551 //reset the store load location. Done this way for backwards
23552 //compatibility. People use _jsonFileUrl (even though officially
23553 //private.
23554 if(this._jsonFileUrl !== this._ccUrl){
23555 kernel.deprecated(this.declaredClass + ": ",
23556 "To change the url, set the url property of the store," +
23557 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23558 this._ccUrl = this._jsonFileUrl;
23559 this.url = this._jsonFileUrl;
23560 }else if(this.url !== this._ccUrl){
23561 this._jsonFileUrl = this.url;
23562 this._ccUrl = this.url;
23563 }
23564
23565 //See if there was any forced reset of data.
23566 if(this.data != null && this._jsonData == null){
23567 this._jsonData = this.data;
23568 this.data = null;
23569 }
23570
23571 if(this._jsonFileUrl){
23572
23573 if(this._loadInProgress){
23574 this._queuedFetches.push({args: keywordArgs});
23575 }else{
23576 this._loadInProgress = true;
23577 var getArgs = {
23578 url: self._jsonFileUrl,
23579 handleAs: "json-comment-optional",
23580 preventCache: this.urlPreventCache,
23581 failOk: this.failOk
23582 };
23583 var getHandler = xhr.get(getArgs);
23584 getHandler.addCallback(function(data){
23585 var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23586 try{
23587 self._getItemsFromLoadedData(data);
23588 self._loadFinished = true;
23589 self._loadInProgress = false;
23590 item = self._getItemByIdentity(keywordArgs.identity);
23591 if(keywordArgs.onItem){
23592 keywordArgs.onItem.call(scope, item);
23593 }
23594 self._handleQueuedFetches();
23595 }catch(error){
23596 self._loadInProgress = false;
23597 if(keywordArgs.onError){
23598 keywordArgs.onError.call(scope, error);
23599 }
23600 }
23601 });
23602 getHandler.addErrback(function(error){
23603 self._loadInProgress = false;
23604 if(keywordArgs.onError){
23605 var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23606 keywordArgs.onError.call(scope, error);
23607 }
23608 });
23609 }
23610
23611 }else if(this._jsonData){
23612 // Passed in data, no need to xhr.
23613 self._getItemsFromLoadedData(self._jsonData);
23614 self._jsonData = null;
23615 self._loadFinished = true;
23616 item = self._getItemByIdentity(keywordArgs.identity);
23617 if(keywordArgs.onItem){
23618 scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23619 keywordArgs.onItem.call(scope, item);
23620 }
23621 }
23622 }else{
23623 // Already loaded. We can just look it up and call back.
23624 item = this._getItemByIdentity(keywordArgs.identity);
23625 if(keywordArgs.onItem){
23626 scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23627 keywordArgs.onItem.call(scope, item);
23628 }
23629 }
23630 },
23631
23632 _getItemByIdentity: function(/* Object */ identity){
23633 // summary:
23634 // Internal function to look an item up by its identity map.
23635 var item = null;
23636 if(this._itemsByIdentity){
23637 // If this map is defined, we need to just try to get it. If it fails
23638 // the item does not exist.
23639 if(Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23640 item = this._itemsByIdentity[identity];
23641 }
23642 }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
23643 item = this._arrayOfAllItems[identity];
23644 }
23645 if(item === undefined){
23646 item = null;
23647 }
23648 return item; // Object
23649 },
23650
23651 getIdentityAttributes: function(/* dojo/data/api/Item */ item){
23652 // summary:
23653 // See dojo/data/api/Identity.getIdentityAttributes()
23654
23655 var identifier = this._features['dojo.data.api.Identity'];
23656 if(identifier === Number){
23657 // If (identifier === Number) it means getIdentity() just returns
23658 // an integer item-number for each item. The dojo/data/api/Identity
23659 // spec says we need to return null if the identity is not composed
23660 // of attributes
23661 return null; // null
23662 }else{
23663 return [identifier]; // Array
23664 }
23665 },
23666
23667 _forceLoad: function(){
23668 // summary:
23669 // Internal function to force a load of the store if it hasn't occurred yet. This is required
23670 // for specific functions to work properly.
23671 var self = this;
23672 //Do a check on the JsonFileUrl and crosscheck it.
23673 //If it doesn't match the cross-check, it needs to be updated
23674 //This allows for either url or _jsonFileUrl to he changed to
23675 //reset the store load location. Done this way for backwards
23676 //compatibility. People use _jsonFileUrl (even though officially
23677 //private.
23678 if(this._jsonFileUrl !== this._ccUrl){
23679 kernel.deprecated(this.declaredClass + ": ",
23680 "To change the url, set the url property of the store," +
23681 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23682 this._ccUrl = this._jsonFileUrl;
23683 this.url = this._jsonFileUrl;
23684 }else if(this.url !== this._ccUrl){
23685 this._jsonFileUrl = this.url;
23686 this._ccUrl = this.url;
23687 }
23688
23689 //See if there was any forced reset of data.
23690 if(this.data != null){
23691 this._jsonData = this.data;
23692 this.data = null;
23693 }
23694
23695 if(this._jsonFileUrl){
23696 var getArgs = {
23697 url: this._jsonFileUrl,
23698 handleAs: "json-comment-optional",
23699 preventCache: this.urlPreventCache,
23700 failOk: this.failOk,
23701 sync: true
23702 };
23703 var getHandler = xhr.get(getArgs);
23704 getHandler.addCallback(function(data){
23705 try{
23706 //Check to be sure there wasn't another load going on concurrently
23707 //So we don't clobber data that comes in on it. If there is a load going on
23708 //then do not save this data. It will potentially clobber current data.
23709 //We mainly wanted to sync/wait here.
23710 //TODO: Revisit the loading scheme of this store to improve multi-initial
23711 //request handling.
23712 if(self._loadInProgress !== true && !self._loadFinished){
23713 self._getItemsFromLoadedData(data);
23714 self._loadFinished = true;
23715 }else if(self._loadInProgress){
23716 //Okay, we hit an error state we can't recover from. A forced load occurred
23717 //while an async load was occurring. Since we cannot block at this point, the best
23718 //that can be managed is to throw an error.
23719 throw new Error(this.declaredClass + ": Unable to perform a synchronous load, an async load is in progress.");
23720 }
23721 }catch(e){
23722 console.log(e);
23723 throw e;
23724 }
23725 });
23726 getHandler.addErrback(function(error){
23727 throw error;
23728 });
23729 }else if(this._jsonData){
23730 self._getItemsFromLoadedData(self._jsonData);
23731 self._jsonData = null;
23732 self._loadFinished = true;
23733 }
23734 }
23735 });
23736 //Mix in the simple fetch implementation to this class.
23737 lang.extend(ItemFileReadStore,simpleFetch);
23738
23739 return ItemFileReadStore;
23740
23741 });
23742
23743 },
23744 'dojo/html':function(){
23745 define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
23746 function(kernel, lang, darray, declare, dom, domConstruct, parser){
23747 // module:
23748 // dojo/html
23749
23750 var html = {
23751 // summary:
23752 // TODOC
23753 };
23754 lang.setObject("dojo.html", html);
23755
23756 // the parser might be needed..
23757
23758 // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
23759 var idCounter = 0;
23760
23761 html._secureForInnerHtml = function(/*String*/ cont){
23762 // summary:
23763 // removes !DOCTYPE and title elements from the html string.
23764 //
23765 // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
23766 // must go into head, so we need to cut out those tags
23767 // cont:
23768 // An html string for insertion into the dom
23769 //
23770 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
23771 };
23772
23773 html._emptyNode = domConstruct.empty;
23774 /*=====
23775 dojo.html._emptyNode = function(node){
23776 // summary:
23777 // Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
23778 // instead.
23779 // node: DOMNode
23780 // the parent element
23781 };
23782 =====*/
23783
23784 html._setNodeContent = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont){
23785 // summary:
23786 // inserts the given content into the given node
23787 // node:
23788 // the parent element
23789 // content:
23790 // the content to be set on the parent element.
23791 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23792
23793 // always empty
23794 domConstruct.empty(node);
23795
23796 if(cont){
23797 if(typeof cont == "string"){
23798 cont = domConstruct.toDom(cont, node.ownerDocument);
23799 }
23800 if(!cont.nodeType && lang.isArrayLike(cont)){
23801 // handle as enumerable, but it may shrink as we enumerate it
23802 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0){
23803 domConstruct.place( cont[i], node, "last");
23804 }
23805 }else{
23806 // pass nodes, documentFragments and unknowns through to dojo.place
23807 domConstruct.place(cont, node, "last");
23808 }
23809 }
23810
23811 // return DomNode
23812 return node;
23813 };
23814
23815 // we wrap up the content-setting operation in a object
23816 html._ContentSetter = declare("dojo.html._ContentSetter", null,
23817 {
23818 // node: DomNode|String
23819 // An node which will be the parent element that we set content into
23820 node: "",
23821
23822 // content: String|DomNode|DomNode[]
23823 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
23824 content: "",
23825
23826 // id: String?
23827 // Usually only used internally, and auto-generated with each instance
23828 id: "",
23829
23830 // cleanContent: Boolean
23831 // Should the content be treated as a full html document,
23832 // and the real content stripped of <html>, <body> wrapper before injection
23833 cleanContent: false,
23834
23835 // extractContent: Boolean
23836 // Should the content be treated as a full html document,
23837 // and the real content stripped of `<html> <body>` wrapper before injection
23838 extractContent: false,
23839
23840 // parseContent: Boolean
23841 // Should the node by passed to the parser after the new content is set
23842 parseContent: false,
23843
23844 // parserScope: String
23845 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
23846 // will search for data-dojo-type (or dojoType). For backwards compatibility
23847 // reasons defaults to dojo._scopeName (which is "dojo" except when
23848 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
23849 parserScope: kernel._scopeName,
23850
23851 // startup: Boolean
23852 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
23853 startup: true,
23854
23855 // lifecycle methods
23856 constructor: function(/*Object*/ params, /*String|DomNode*/ node){
23857 // summary:
23858 // Provides a configurable, extensible object to wrap the setting on content on a node
23859 // call the set() method to actually set the content..
23860
23861 // the original params are mixed directly into the instance "this"
23862 lang.mixin(this, params || {});
23863
23864 // give precedence to params.node vs. the node argument
23865 // and ensure its a node, not an id string
23866 node = this.node = dom.byId( this.node || node );
23867
23868 if(!this.id){
23869 this.id = [
23870 "Setter",
23871 (node) ? node.id || node.tagName : "",
23872 idCounter++
23873 ].join("_");
23874 }
23875 },
23876 set: function(/* String|DomNode|NodeList? */ cont, /*Object?*/ params){
23877 // summary:
23878 // front-end to the set-content sequence
23879 // cont:
23880 // An html string, node or enumerable list of nodes for insertion into the dom
23881 // If not provided, the object's content property will be used
23882 if(undefined !== cont){
23883 this.content = cont;
23884 }
23885 // in the re-use scenario, set needs to be able to mixin new configuration
23886 if(params){
23887 this._mixin(params);
23888 }
23889
23890 this.onBegin();
23891 this.setContent();
23892
23893 var ret = this.onEnd();
23894
23895 if(ret && ret.then){
23896 // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
23897 return ret;
23898 }else{
23899 // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
23900 // return a Deferred like above.
23901 return this.node;
23902 }
23903 },
23904
23905 setContent: function(){
23906 // summary:
23907 // sets the content on the node
23908
23909 var node = this.node;
23910 if(!node){
23911 // can't proceed
23912 throw new Error(this.declaredClass + ": setContent given no node");
23913 }
23914 try{
23915 node = html._setNodeContent(node, this.content);
23916 }catch(e){
23917 // check if a domfault occurs when we are appending this.errorMessage
23918 // like for instance if domNode is a UL and we try append a DIV
23919
23920 // FIXME: need to allow the user to provide a content error message string
23921 var errMess = this.onContentError(e);
23922 try{
23923 node.innerHTML = errMess;
23924 }catch(e){
23925 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
23926 }
23927 }
23928 // always put back the node for the next method
23929 this.node = node; // DomNode
23930 },
23931
23932 empty: function(){
23933 // summary:
23934 // cleanly empty out existing content
23935
23936 // If there is a parse in progress, cancel it.
23937 if(this.parseDeferred){
23938 if(!this.parseDeferred.isResolved()){
23939 this.parseDeferred.cancel();
23940 }
23941 delete this.parseDeferred;
23942 }
23943
23944 // destroy any widgets from a previous run
23945 // NOTE: if you don't want this you'll need to empty
23946 // the parseResults array property yourself to avoid bad things happening
23947 if(this.parseResults && this.parseResults.length){
23948 darray.forEach(this.parseResults, function(w){
23949 if(w.destroy){
23950 w.destroy();
23951 }
23952 });
23953 delete this.parseResults;
23954 }
23955 // this is fast, but if you know its already empty or safe, you could
23956 // override empty to skip this step
23957 domConstruct.empty(this.node);
23958 },
23959
23960 onBegin: function(){
23961 // summary:
23962 // Called after instantiation, but before set();
23963 // It allows modification of any of the object properties -
23964 // including the node and content provided - before the set operation actually takes place
23965 // This default implementation checks for cleanContent and extractContent flags to
23966 // optionally pre-process html string content
23967 var cont = this.content;
23968
23969 if(lang.isString(cont)){
23970 if(this.cleanContent){
23971 cont = html._secureForInnerHtml(cont);
23972 }
23973
23974 if(this.extractContent){
23975 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
23976 if(match){ cont = match[1]; }
23977 }
23978 }
23979
23980 // clean out the node and any cruft associated with it - like widgets
23981 this.empty();
23982
23983 this.content = cont;
23984 return this.node; // DomNode
23985 },
23986
23987 onEnd: function(){
23988 // summary:
23989 // Called after set(), when the new content has been pushed into the node
23990 // It provides an opportunity for post-processing before handing back the node to the caller
23991 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
23992 if(this.parseContent){
23993 // populates this.parseResults and this.parseDeferred if you need those..
23994 this._parse();
23995 }
23996 return this.node; // DomNode
23997 // TODO: for 2.0 return a Promise indicating that the parse completed.
23998 },
23999
24000 tearDown: function(){
24001 // summary:
24002 // manually reset the Setter instance if its being re-used for example for another set()
24003 // description:
24004 // tearDown() is not called automatically.
24005 // In normal use, the Setter instance properties are simply allowed to fall out of scope
24006 // but the tearDown method can be called to explicitly reset this instance.
24007 delete this.parseResults;
24008 delete this.parseDeferred;
24009 delete this.node;
24010 delete this.content;
24011 },
24012
24013 onContentError: function(err){
24014 return "Error occurred setting content: " + err;
24015 },
24016
24017 onExecError: function(err){
24018 return "Error occurred executing scripts: " + err;
24019 },
24020
24021 _mixin: function(params){
24022 // mix properties/methods into the instance
24023 // TODO: the intention with tearDown is to put the Setter's state
24024 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
24025 // so we could do something here to move the original properties aside for later restoration
24026 var empty = {}, key;
24027 for(key in params){
24028 if(key in empty){ continue; }
24029 // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
24030 // .. but history shows we'll almost always guess wrong
24031 this[key] = params[key];
24032 }
24033 },
24034 _parse: function(){
24035 // summary:
24036 // runs the dojo parser over the node contents, storing any results in this.parseResults
24037 // and the parse promise in this.parseDeferred
24038 // Any errors resulting from parsing are passed to _onError for handling
24039
24040 var rootNode = this.node;
24041 try{
24042 // store the results (widgets, whatever) for potential retrieval
24043 var inherited = {};
24044 darray.forEach(["dir", "lang", "textDir"], function(name){
24045 if(this[name]){
24046 inherited[name] = this[name];
24047 }
24048 }, this);
24049 var self = this;
24050 this.parseDeferred = parser.parse({
24051 rootNode: rootNode,
24052 noStart: !this.startup,
24053 inherited: inherited,
24054 scope: this.parserScope
24055 }).then(function(results){
24056 return self.parseResults = results;
24057 });
24058 }catch(e){
24059 this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
24060 }
24061 },
24062
24063 _onError: function(type, err, consoleText){
24064 // summary:
24065 // shows user the string that is returned by on[type]Error
24066 // override/implement on[type]Error and return your own string to customize
24067 var errText = this['on' + type + 'Error'].call(this, err);
24068 if(consoleText){
24069 console.error(consoleText, err);
24070 }else if(errText){ // a empty string won't change current content
24071 html._setNodeContent(this.node, errText, true);
24072 }
24073 }
24074 }); // end declare()
24075
24076 html.set = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont, /*Object?*/ params){
24077 // summary:
24078 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
24079 // may be a better choice for simple HTML insertion.
24080 // description:
24081 // Unless you need to use the params capabilities of this method, you should use
24082 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
24083 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
24084 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
24085 // or the other capabilities as defined by the params object for this method.
24086 // node:
24087 // the parent element that will receive the content
24088 // cont:
24089 // the content to be set on the parent element.
24090 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
24091 // params:
24092 // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
24093 // example:
24094 // A safe string/node/nodelist content replacement/injection with hooks for extension
24095 // Example Usage:
24096 // | html.set(node, "some string");
24097 // | html.set(node, contentNode, {options});
24098 // | html.set(node, myNode.childNodes, {options});
24099 if(undefined == cont){
24100 console.warn("dojo.html.set: no cont argument provided, using empty string");
24101 cont = "";
24102 }
24103 if(!params){
24104 // simple and fast
24105 return html._setNodeContent(node, cont, true);
24106 }else{
24107 // more options but slower
24108 // note the arguments are reversed in order, to match the convention for instantiation via the parser
24109 var op = new html._ContentSetter(lang.mixin(
24110 params,
24111 { content: cont, node: node }
24112 ));
24113 return op.set();
24114 }
24115 };
24116
24117 return html;
24118 });
24119
24120 },
24121 'dijit/_PaletteMixin':function(){
24122 define("dijit/_PaletteMixin", [
24123 "dojo/_base/declare", // declare
24124 "dojo/dom-attr", // domAttr.set
24125 "dojo/dom-class", // domClass.add domClass.remove
24126 "dojo/dom-construct", // domConstruct.create domConstruct.place
24127 "dojo/_base/event", // event.stop
24128 "dojo/keys", // keys
24129 "dojo/_base/lang", // lang.getObject
24130 "./_CssStateMixin",
24131 "./focus",
24132 "./typematic"
24133 ], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
24134
24135 // module:
24136 // dijit/_PaletteMixin
24137
24138 return declare("dijit._PaletteMixin", [_CssStateMixin], {
24139 // summary:
24140 // A keyboard accessible palette, for picking a color/emoticon/etc.
24141 // description:
24142 // A mixin for a grid showing various entities, so the user can pick a certain entity.
24143
24144 // defaultTimeout: Number
24145 // Number of milliseconds before a held key or button becomes typematic
24146 defaultTimeout: 500,
24147
24148 // timeoutChangeRate: Number
24149 // Fraction of time used to change the typematic timer between events
24150 // 1.0 means that each typematic event fires at defaultTimeout intervals
24151 // Less than 1.0 means that each typematic event fires at an increasing faster rate
24152 timeoutChangeRate: 0.90,
24153
24154 // value: String
24155 // Currently selected color/emoticon/etc.
24156 value: "",
24157
24158 // _selectedCell: [private] Integer
24159 // Index of the currently selected cell. Initially, none selected
24160 _selectedCell: -1,
24161
24162 /*=====
24163 // _currentFocus: [private] DomNode
24164 // The currently focused cell (if the palette itself has focus), or otherwise
24165 // the cell to be focused when the palette itself gets focus.
24166 // Different from value, which represents the selected (i.e. clicked) cell.
24167 _currentFocus: null,
24168 =====*/
24169
24170 /*=====
24171 // _xDim: [protected] Integer
24172 // This is the number of cells horizontally across.
24173 _xDim: null,
24174 =====*/
24175
24176 /*=====
24177 // _yDim: [protected] Integer
24178 // This is the number of cells vertically down.
24179 _yDim: null,
24180 =====*/
24181
24182 // tabIndex: String
24183 // Widget tab index.
24184 tabIndex: "0",
24185
24186 // cellClass: [protected] String
24187 // CSS class applied to each cell in the palette
24188 cellClass: "dijitPaletteCell",
24189
24190 // dyeClass: [protected] Constructor
24191 // Constructor for Object created for each cell of the palette.
24192 // dyeClass should implements dijit.Dye interface
24193 dyeClass: null,
24194
24195 // summary: String
24196 // Localized summary for the palette table
24197 summary: '',
24198 _setSummaryAttr: "paletteTableNode",
24199
24200 _dyeFactory: function(value /*===== , row, col, title =====*/){
24201 // summary:
24202 // Return instance of dijit.Dye for specified cell of palette
24203 // tags:
24204 // extension
24205
24206 // Remove string support for 2.0
24207 var dyeClassObj = typeof this.dyeClass == "string" ? lang.getObject(this.dyeClass) : this.dyeClass;
24208 return new dyeClassObj(value);
24209 },
24210
24211 _preparePalette: function(choices, titles) {
24212 // summary:
24213 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
24214 // for each cell
24215 // choices: String[][]
24216 // id's for each cell of the palette, used to create Dye JS object for each cell
24217 // titles: String[]
24218 // Localized tooltip for each cell
24219
24220 this._cells = [];
24221 var url = this._blankGif;
24222
24223 this.connect(this.gridNode, "ondijitclick", "_onCellClick");
24224
24225 for(var row=0; row < choices.length; row++){
24226 var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
24227 for(var col=0; col < choices[row].length; col++){
24228 var value = choices[row][col];
24229 if(value){
24230 var cellObject = this._dyeFactory(value, row, col, titles[value]);
24231
24232 var cellNode = domConstruct.create("td", {
24233 "class": this.cellClass,
24234 tabIndex: "-1",
24235 title: titles[value],
24236 role: "gridcell"
24237 }, rowNode);
24238
24239 // prepare cell inner structure
24240 cellObject.fillCell(cellNode, url);
24241
24242 cellNode.idx = this._cells.length;
24243
24244 // save cell info into _cells
24245 this._cells.push({node:cellNode, dye:cellObject});
24246 }
24247 }
24248 }
24249 this._xDim = choices[0].length;
24250 this._yDim = choices.length;
24251
24252 // Now set all events
24253 // The palette itself is navigated to with the tab key on the keyboard
24254 // Keyboard navigation within the Palette is with the arrow keys
24255 // Spacebar selects the cell.
24256 // For the up key the index is changed by negative the x dimension.
24257
24258 var keyIncrementMap = {
24259 UP_ARROW: -this._xDim,
24260 // The down key the index is increase by the x dimension.
24261 DOWN_ARROW: this._xDim,
24262 // Right and left move the index by 1.
24263 RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
24264 LEFT_ARROW: this.isLeftToRight() ? -1 : 1
24265 };
24266 for(var key in keyIncrementMap){
24267 this.own(
24268 typematic.addKeyListener(
24269 this.domNode,
24270 {charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
24271 this,
24272 function(){
24273 var increment = keyIncrementMap[key];
24274 return function(count){ this._navigateByKey(increment, count); };
24275 }(),
24276 this.timeoutChangeRate,
24277 this.defaultTimeout
24278 )
24279 );
24280 }
24281 },
24282
24283 postCreate: function(){
24284 this.inherited(arguments);
24285
24286 // Set initial navigable node.
24287 this._setCurrent(this._cells[0].node);
24288 },
24289
24290 focus: function(){
24291 // summary:
24292 // Focus this widget. Puts focus on the most recently focused cell.
24293
24294 // The cell already has tabIndex set, just need to set CSS and focus it
24295 focus.focus(this._currentFocus);
24296 },
24297
24298 _onCellClick: function(/*Event*/ evt){
24299 // summary:
24300 // Handler for click, enter key & space key. Selects the cell.
24301 // evt:
24302 // The event.
24303 // tags:
24304 // private
24305
24306 var target = evt.target;
24307
24308 // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
24309 while(target.tagName != "TD"){
24310 if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
24311 return;
24312 }
24313 target = target.parentNode;
24314 }
24315
24316 var value = this._getDye(target).getValue();
24317
24318 // First focus the clicked cell, and then send onChange() notification.
24319 // onChange() (via _setValueAttr) must be after the focus call, because
24320 // it may trigger a refocus to somewhere else (like the Editor content area), and that
24321 // second focus should win.
24322 this._setCurrent(target);
24323 focus.focus(target);
24324 this._setValueAttr(value, true);
24325
24326 event.stop(evt);
24327 },
24328
24329 _setCurrent: function(/*DomNode*/ node){
24330 // summary:
24331 // Sets which node is the focused cell.
24332 // description:
24333 // At any point in time there's exactly one
24334 // cell with tabIndex != -1. If focus is inside the palette then
24335 // focus is on that cell.
24336 //
24337 // After calling this method, arrow key handlers and mouse click handlers
24338 // should focus the cell in a setTimeout().
24339 // tags:
24340 // protected
24341 if("_currentFocus" in this){
24342 // Remove tabIndex on old cell
24343 domAttr.set(this._currentFocus, "tabIndex", "-1");
24344 }
24345
24346 // Set tabIndex of new cell
24347 this._currentFocus = node;
24348 if(node){
24349 domAttr.set(node, "tabIndex", this.tabIndex);
24350 }
24351 },
24352
24353 _setValueAttr: function(value, priorityChange){
24354 // summary:
24355 // This selects a cell. It triggers the onChange event.
24356 // value: String
24357 // Value of the cell to select
24358 // tags:
24359 // protected
24360 // priorityChange: Boolean?
24361 // Optional parameter used to tell the select whether or not to fire
24362 // onChange event.
24363
24364 // clear old selected cell
24365 if(this._selectedCell >= 0){
24366 domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
24367 }
24368 this._selectedCell = -1;
24369
24370 // search for cell matching specified value
24371 if(value){
24372 for(var i = 0; i < this._cells.length; i++){
24373 if(value == this._cells[i].dye.getValue()){
24374 this._selectedCell = i;
24375 domClass.add(this._cells[i].node, this.cellClass + "Selected");
24376 break;
24377 }
24378 }
24379 }
24380
24381 // record new value, or null if no matching cell
24382 this._set("value", this._selectedCell >= 0 ? value : null);
24383
24384 if(priorityChange || priorityChange === undefined){
24385 this.onChange(value);
24386 }
24387 },
24388
24389 onChange: function(/*===== value =====*/){
24390 // summary:
24391 // Callback when a cell is selected.
24392 // value: String
24393 // Value corresponding to cell.
24394 },
24395
24396 _navigateByKey: function(increment, typeCount){
24397 // summary:
24398 // This is the callback for typematic.
24399 // It changes the focus and the highlighed cell.
24400 // increment:
24401 // How much the key is navigated.
24402 // typeCount:
24403 // How many times typematic has fired.
24404 // tags:
24405 // private
24406
24407 // typecount == -1 means the key is released.
24408 if(typeCount == -1){ return; }
24409
24410 var newFocusIndex = this._currentFocus.idx + increment;
24411 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
24412 var focusNode = this._cells[newFocusIndex].node;
24413 this._setCurrent(focusNode);
24414
24415 // Actually focus the node, for the benefit of screen readers.
24416 // Use defer because IE doesn't like changing focus inside of an event handler
24417 this.defer(lang.hitch(focus, "focus", focusNode));
24418 }
24419 },
24420
24421 _getDye: function(/*DomNode*/ cell){
24422 // summary:
24423 // Get JS object for given cell DOMNode
24424
24425 return this._cells[cell.idx].dye;
24426 }
24427 });
24428
24429 /*=====
24430 declare("dijit.Dye",
24431 null,
24432 {
24433 // summary:
24434 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
24435
24436 constructor: function(alias, row, col){
24437 // summary:
24438 // Initialize according to value or alias like "white"
24439 // alias: String
24440 },
24441
24442 getValue: function(){
24443 // summary:
24444 // Return "value" of cell; meaning of "value" varies by subclass.
24445 // description:
24446 // For example color hex value, emoticon ascii value etc, entity hex value.
24447 },
24448
24449 fillCell: function(cell, blankGif){
24450 // summary:
24451 // Add cell DOMNode inner structure
24452 // cell: DomNode
24453 // The surrounding cell
24454 // blankGif: String
24455 // URL for blank cell image
24456 }
24457 }
24458 );
24459 =====*/
24460
24461 });
24462
24463 },
24464 'dijit/form/ValidationTextBox':function(){
24465 require({cache:{
24466 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline 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\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
24467 define("dijit/form/ValidationTextBox", [
24468 "dojo/_base/declare", // declare
24469 "dojo/_base/kernel", // kernel.deprecated
24470 "dojo/i18n", // i18n.getLocalization
24471 "./TextBox",
24472 "../Tooltip",
24473 "dojo/text!./templates/ValidationTextBox.html",
24474 "dojo/i18n!./nls/validate"
24475 ], function(declare, kernel, i18n, TextBox, Tooltip, template){
24476
24477 // module:
24478 // dijit/form/ValidationTextBox
24479
24480
24481 /*=====
24482 var __Constraints = {
24483 // locale: String
24484 // locale used for validation, picks up value from this widget's lang attribute
24485 // _flags_: anything
24486 // various flags passed to pattern function
24487 };
24488 =====*/
24489
24490 var ValidationTextBox;
24491 return ValidationTextBox = declare("dijit.form.ValidationTextBox", TextBox, {
24492 // summary:
24493 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
24494
24495 templateString: template,
24496
24497 // required: Boolean
24498 // User is required to enter data into this field.
24499 required: false,
24500
24501 // promptMessage: String
24502 // If defined, display this hint string immediately on focus to the textbox, if empty.
24503 // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
24504 // Think of this like a tooltip that tells the user what to do, not an error message
24505 // that tells the user what they've done wrong.
24506 //
24507 // Message disappears when user starts typing.
24508 promptMessage: "",
24509
24510 // invalidMessage: String
24511 // The message to display if value is invalid.
24512 // The translated string value is read from the message file by default.
24513 // Set to "" to use the promptMessage instead.
24514 invalidMessage: "$_unset_$",
24515
24516 // missingMessage: String
24517 // The message to display if value is empty and the field is required.
24518 // The translated string value is read from the message file by default.
24519 // Set to "" to use the invalidMessage instead.
24520 missingMessage: "$_unset_$",
24521
24522 // message: String
24523 // Currently error/prompt message.
24524 // When using the default tooltip implementation, this will only be
24525 // displayed when the field is focused.
24526 message: "",
24527
24528 // constraints: __Constraints
24529 // user-defined object needed to pass parameters to the validator functions
24530 constraints: {},
24531
24532 // pattern: [extension protected] String|Function(constraints) returning a string.
24533 // This defines the regular expression used to validate the input.
24534 // Do not add leading ^ or $ characters since the widget adds these.
24535 // A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
24536 // set('pattern', String|Function).
24537 pattern: ".*",
24538
24539 // regExp: Deprecated [extension protected] String. Use "pattern" instead.
24540 regExp: "",
24541
24542 regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
24543 // summary:
24544 // Deprecated. Use set('pattern', Function) instead.
24545 },
24546
24547 // state: [readonly] String
24548 // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
24549 state: "",
24550
24551 // tooltipPosition: String[]
24552 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
24553 tooltipPosition: [],
24554
24555 _deprecateRegExp: function(attr, value){
24556 if(value != ValidationTextBox.prototype[attr]){
24557 kernel.deprecated("ValidationTextBox id="+this.id+", set('" + attr + "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0");
24558 this.set('pattern', value);
24559 }
24560 },
24561 _setRegExpGenAttr: function(/*Function*/ newFcn){
24562 this._deprecateRegExp("regExpGen", newFcn);
24563 this.regExpGen = this._getPatternAttr; // backward compat with this.regExpGen(this.constraints)
24564 },
24565 _setRegExpAttr: function(/*String*/ value){
24566 this._deprecateRegExp("regExp", value);
24567 },
24568
24569 _setValueAttr: function(){
24570 // summary:
24571 // Hook so set('value', ...) works.
24572 this.inherited(arguments);
24573 this.validate(this.focused);
24574 },
24575
24576 validator: function(/*anything*/ value, /*__Constraints*/ constraints){
24577 // summary:
24578 // Overridable function used to validate the text input against the regular expression.
24579 // tags:
24580 // protected
24581 return (new RegExp("^(?:" + this._getPatternAttr(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
24582 (!this.required || !this._isEmpty(value)) &&
24583 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
24584 },
24585
24586 _isValidSubset: function(){
24587 // summary:
24588 // Returns true if the value is either already valid or could be made valid by appending characters.
24589 // This is used for validation while the user [may be] still typing.
24590 return this.textbox.value.search(this._partialre) == 0;
24591 },
24592
24593 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
24594 // summary:
24595 // Tests if value is valid.
24596 // Can override with your own routine in a subclass.
24597 // tags:
24598 // protected
24599 return this.validator(this.textbox.value, this.constraints);
24600 },
24601
24602 _isEmpty: function(value){
24603 // summary:
24604 // Checks for whitespace
24605 return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
24606 },
24607
24608 getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24609 // summary:
24610 // Return an error message to show if appropriate
24611 // tags:
24612 // protected
24613 var invalid = this.invalidMessage == "$_unset_$" ? this.messages.invalidMessage :
24614 !this.invalidMessage ? this.promptMessage : this.invalidMessage;
24615 var missing = this.missingMessage == "$_unset_$" ? this.messages.missingMessage :
24616 !this.missingMessage ? invalid : this.missingMessage;
24617 return (this.required && this._isEmpty(this.textbox.value)) ? missing : invalid; // String
24618 },
24619
24620 getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24621 // summary:
24622 // Return a hint message to show when widget is first focused
24623 // tags:
24624 // protected
24625 return this.promptMessage; // String
24626 },
24627
24628 _maskValidSubsetError: true,
24629 validate: function(/*Boolean*/ isFocused){
24630 // summary:
24631 // Called by oninit, onblur, and onkeypress.
24632 // description:
24633 // Show missing or invalid messages if appropriate, and highlight textbox field.
24634 // tags:
24635 // protected
24636 var message = "";
24637 var isValid = this.disabled || this.isValid(isFocused);
24638 if(isValid){ this._maskValidSubsetError = true; }
24639 var isEmpty = this._isEmpty(this.textbox.value);
24640 var isValidSubset = !isValid && isFocused && this._isValidSubset();
24641 this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && (this._maskValidSubsetError || (isValidSubset && !this._hasBeenBlurred && isFocused))) ? "Incomplete" : "Error"));
24642 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
24643
24644 if(this.state == "Error"){
24645 this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
24646 message = this.getErrorMessage(isFocused);
24647 }else if(this.state == "Incomplete"){
24648 message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
24649 this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
24650 }else if(isEmpty){
24651 message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
24652 }
24653 this.set("message", message);
24654
24655 return isValid;
24656 },
24657
24658 displayMessage: function(/*String*/ message){
24659 // summary:
24660 // Overridable method to display validation errors/hints.
24661 // By default uses a tooltip.
24662 // tags:
24663 // extension
24664 if(message && this.focused){
24665 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
24666 }else{
24667 Tooltip.hide(this.domNode);
24668 }
24669 },
24670
24671 _refreshState: function(){
24672 // Overrides TextBox._refreshState()
24673 if(this._created){
24674 this.validate(this.focused);
24675 }
24676 this.inherited(arguments);
24677 },
24678
24679 //////////// INITIALIZATION METHODS ///////////////////////////////////////
24680
24681 constructor: function(params /*===== , srcNodeRef =====*/){
24682 // summary:
24683 // Create the widget.
24684 // params: Object|null
24685 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
24686 // and functions, typically callbacks like onClick.
24687 // The hash can contain any of the widget's properties, excluding read-only properties.
24688 // srcNodeRef: DOMNode|String?
24689 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
24690
24691 this.constraints = {};
24692 this.baseClass += ' dijitValidationTextBox';
24693 },
24694
24695 startup: function(){
24696 this.inherited(arguments);
24697 this._refreshState(); // after all _set* methods have run
24698 },
24699
24700 _setConstraintsAttr: function(/*__Constraints*/ constraints){
24701 if(!constraints.locale && this.lang){
24702 constraints.locale = this.lang;
24703 }
24704 this._set("constraints", constraints);
24705 this._refreshState();
24706 },
24707
24708 _setPatternAttr: function(/*String|Function*/ pattern){
24709 this._set("pattern", pattern); // don't set on INPUT to avoid native HTML5 validation
24710 },
24711
24712 _getPatternAttr: function(/*__Constraints*/ constraints){
24713 // summary:
24714 // Hook to get the current regExp and to compute the partial validation RE.
24715 var p = this.pattern;
24716 var type = (typeof p).toLowerCase();
24717 if(type == "function"){
24718 p = this.pattern(constraints || this.constraints);
24719 }
24720 if(p != this._lastRegExp){
24721 var partialre = "";
24722 this._lastRegExp = p;
24723 // parse the regexp and produce a new regexp that matches valid subsets
24724 // if the regexp is .* then there's no use in matching subsets since everything is valid
24725 if(p != ".*"){
24726 p.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
24727 function(re){
24728 switch(re.charAt(0)){
24729 case '{':
24730 case '+':
24731 case '?':
24732 case '*':
24733 case '^':
24734 case '$':
24735 case '|':
24736 case '(':
24737 partialre += re;
24738 break;
24739 case ")":
24740 partialre += "|$)";
24741 break;
24742 default:
24743 partialre += "(?:"+re+"|$)";
24744 break;
24745 }
24746 });
24747 }
24748 try{ // this is needed for now since the above regexp parsing needs more test verification
24749 "".search(partialre);
24750 }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
24751 partialre = this.pattern;
24752 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.pattern);
24753 } // should never be here unless the original RE is bad or the parsing is bad
24754 this._partialre = "^(?:" + partialre + ")$";
24755 }
24756 return p;
24757 },
24758
24759 postMixInProperties: function(){
24760 this.inherited(arguments);
24761 this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
24762 this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
24763 },
24764
24765 _setDisabledAttr: function(/*Boolean*/ value){
24766 this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
24767 this._refreshState();
24768 },
24769
24770 _setRequiredAttr: function(/*Boolean*/ value){
24771 this._set("required", value);
24772 this.focusNode.setAttribute("aria-required", value);
24773 this._refreshState();
24774 },
24775
24776 _setMessageAttr: function(/*String*/ message){
24777 this._set("message", message);
24778 this.displayMessage(message);
24779 },
24780
24781 reset:function(){
24782 // Overrides dijit/form/TextBox.reset() by also
24783 // hiding errors about partial matches
24784 this._maskValidSubsetError = true;
24785 this.inherited(arguments);
24786 },
24787
24788 _onBlur: function(){
24789 // the message still exists but for back-compat, and to erase the tooltip
24790 // (if the message is being displayed as a tooltip), call displayMessage('')
24791 this.displayMessage('');
24792
24793 this.inherited(arguments);
24794 }
24795 });
24796 });
24797
24798 },
24799 'dijit/_base/typematic':function(){
24800 define("dijit/_base/typematic", ["../typematic"], function(){
24801
24802 /*=====
24803 return {
24804 // summary:
24805 // Deprecated, for back-compat, just loads top level module
24806 };
24807 =====*/
24808
24809 });
24810
24811 },
24812 'dijit/layout/BorderContainer':function(){
24813 define("dijit/layout/BorderContainer", [
24814 "dojo/_base/array", // array.filter array.forEach array.map
24815 "dojo/cookie", // cookie
24816 "dojo/_base/declare", // declare
24817 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
24818 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
24819 "dojo/dom-geometry", // domGeometry.marginBox
24820 "dojo/dom-style", // domStyle.style
24821 "dojo/_base/event", // event.stop
24822 "dojo/keys",
24823 "dojo/_base/lang", // lang.getObject lang.hitch
24824 "dojo/on",
24825 "dojo/touch",
24826 "../_WidgetBase",
24827 "../_Widget",
24828 "../_TemplatedMixin",
24829 "./_LayoutWidget",
24830 "./utils" // layoutUtils.layoutChildren
24831 ], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch,
24832 _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
24833
24834 // module:
24835 // dijit/layout/BorderContainer
24836
24837 var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ],
24838 {
24839 // summary:
24840 // A draggable spacer between two items in a `dijit/layout/BorderContainer`.
24841 // description:
24842 // This is instantiated by `dijit/layout/BorderContainer`. Users should not
24843 // create it directly.
24844 // tags:
24845 // private
24846
24847 /*=====
24848 // container: [const] dijit/layout/BorderContainer
24849 // Pointer to the parent BorderContainer
24850 container: null,
24851
24852 // child: [const] dijit/layout/_LayoutWidget
24853 // Pointer to the pane associated with this splitter
24854 child: null,
24855
24856 // region: [const] String
24857 // Region of pane associated with this splitter.
24858 // "top", "bottom", "left", "right".
24859 region: null,
24860 =====*/
24861
24862 // live: [const] Boolean
24863 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
24864 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
24865 live: true,
24866
24867 templateString: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
24868
24869 constructor: function(){
24870 this._handlers = [];
24871 },
24872
24873 postMixInProperties: function(){
24874 this.inherited(arguments);
24875
24876 this.horizontal = /top|bottom/.test(this.region);
24877 this._factor = /top|left/.test(this.region) ? 1 : -1;
24878 this._cookieName = this.container.id + "_" + this.region;
24879 },
24880
24881 buildRendering: function(){
24882 this.inherited(arguments);
24883
24884 domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
24885
24886 if(this.container.persist){
24887 // restore old size
24888 var persistSize = cookie(this._cookieName);
24889 if(persistSize){
24890 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
24891 }
24892 }
24893 },
24894
24895 _computeMaxSize: function(){
24896 // summary:
24897 // Return the maximum size that my corresponding pane can be set to
24898
24899 var dim = this.horizontal ? 'h' : 'w',
24900 childSize = domGeometry.getMarginBox(this.child.domNode)[dim],
24901 center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
24902 spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0
24903
24904 return Math.min(this.child.maxSize, childSize + spaceAvailable);
24905 },
24906
24907 _startDrag: function(e){
24908 if(!this.cover){
24909 this.cover = domConstruct.place("<div class=dijitSplitterCover></div>", this.child.domNode, "after");
24910 }
24911 domClass.add(this.cover, "dijitSplitterCoverActive");
24912
24913 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
24914 if(this.fake){ domConstruct.destroy(this.fake); }
24915 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
24916 // create fake splitter to display at old position while we drag
24917 (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
24918 domClass.add(this.domNode, "dijitSplitterShadow");
24919 domConstruct.place(this.fake, this.domNode, "after");
24920 }
24921 domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
24922 if(this.fake){
24923 domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
24924 }
24925
24926 //Performance: load data info local vars for onmousevent function closure
24927 var factor = this._factor,
24928 isHorizontal = this.horizontal,
24929 axis = isHorizontal ? "pageY" : "pageX",
24930 pageStart = e[axis],
24931 splitterStyle = this.domNode.style,
24932 dim = isHorizontal ? 'h' : 'w',
24933 childStart = domGeometry.getMarginBox(this.child.domNode)[dim],
24934 max = this._computeMaxSize(),
24935 min = this.child.minSize || 20,
24936 region = this.region,
24937 splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
24938 splitterStart = parseInt(splitterStyle[splitterAttr], 10),
24939 resize = this._resize,
24940 layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id),
24941 de = this.ownerDocument;
24942
24943 this._handlers = this._handlers.concat([
24944 on(de, touch.move, this._drag = function(e, forceResize){
24945 var delta = e[axis] - pageStart,
24946 childSize = factor * delta + childStart,
24947 boundChildSize = Math.max(Math.min(childSize, max), min);
24948
24949 if(resize || forceResize){
24950 layoutFunc(boundChildSize);
24951 }
24952 // TODO: setting style directly (usually) sets content box size, need to set margin box size
24953 splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
24954 }),
24955 on(de, "dragstart", event.stop),
24956 on(this.ownerDocumentBody, "selectstart", event.stop),
24957 on(de, touch.release, lang.hitch(this, "_stopDrag"))
24958 ]);
24959 event.stop(e);
24960 },
24961
24962 _onMouse: function(e){
24963 // summary:
24964 // Handler for onmouseenter / onmouseleave events
24965 var o = (e.type == "mouseover" || e.type == "mouseenter");
24966 domClass.toggle(this.domNode, "dijitSplitterHover", o);
24967 domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
24968 },
24969
24970 _stopDrag: function(e){
24971 try{
24972 if(this.cover){
24973 domClass.remove(this.cover, "dijitSplitterCoverActive");
24974 }
24975 if(this.fake){ domConstruct.destroy(this.fake); }
24976 domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter"
24977 + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
24978 this._drag(e); //TODO: redundant with onmousemove?
24979 this._drag(e, true);
24980 }finally{
24981 this._cleanupHandlers();
24982 delete this._drag;
24983 }
24984
24985 if(this.container.persist){
24986 cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
24987 }
24988 },
24989
24990 _cleanupHandlers: function(){
24991 var h;
24992 while(h = this._handlers.pop()){ h.remove(); }
24993 },
24994
24995 _onKeyPress: function(/*Event*/ e){
24996 // should we apply typematic to this?
24997 this._resize = true;
24998 var horizontal = this.horizontal;
24999 var tick = 1;
25000 switch(e.charOrCode){
25001 case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW:
25002 tick *= -1;
25003 // break;
25004 case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW:
25005 break;
25006 default:
25007 // this.inherited(arguments);
25008 return;
25009 }
25010 var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
25011 this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
25012 event.stop(e);
25013 },
25014
25015 destroy: function(){
25016 this._cleanupHandlers();
25017 delete this.child;
25018 delete this.container;
25019 delete this.cover;
25020 delete this.fake;
25021 this.inherited(arguments);
25022 }
25023 });
25024
25025 var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin],
25026 {
25027 // summary:
25028 // Just a spacer div to separate side pane from center pane.
25029 // Basically a trick to lookup the gutter/splitter width from the theme.
25030 // description:
25031 // Instantiated by `dijit/layout/BorderContainer`. Users should not
25032 // create directly.
25033 // tags:
25034 // private
25035
25036 templateString: '<div class="dijitGutter" role="presentation"></div>',
25037
25038 postMixInProperties: function(){
25039 this.inherited(arguments);
25040 this.horizontal = /top|bottom/.test(this.region);
25041 },
25042
25043 buildRendering: function(){
25044 this.inherited(arguments);
25045 domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
25046 }
25047 });
25048
25049 var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, {
25050 // summary:
25051 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
25052 // description:
25053 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
25054 // that contains a child widget marked region="center" and optionally children widgets marked
25055 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
25056 // Children along the edges will be laid out according to width or height dimensions and may
25057 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
25058 // space is designated for the center region.
25059 //
25060 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
25061 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
25062 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
25063 // "left" and "right" except that they will be reversed in right-to-left environments.
25064 //
25065 // For complex layouts, multiple children can be specified for a single region. In this case, the
25066 // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
25067 // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
25068 // instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
25069 //
25070 // See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on
25071 // children of a `BorderContainer`.
25072 // example:
25073 // | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
25074 // | style="width: 400px; height: 300px;">
25075 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div>
25076 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
25077 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div>
25078 // | </div>
25079
25080 // design: String
25081 // Which design is used for the layout:
25082 //
25083 // - "headline" (default) where the top and bottom extend the full width of the container
25084 // - "sidebar" where the left and right sides extend from top to bottom.
25085 design: "headline",
25086
25087 // gutters: [const] Boolean
25088 // Give each pane a border and margin.
25089 // Margin determined by domNode.paddingLeft.
25090 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
25091 gutters: true,
25092
25093 // liveSplitters: [const] Boolean
25094 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
25095 liveSplitters: true,
25096
25097 // persist: Boolean
25098 // Save splitter positions in a cookie.
25099 persist: false,
25100
25101 baseClass: "dijitBorderContainer",
25102
25103 // _splitterClass: Function||String
25104 // Optional hook to override the default Splitter widget used by BorderContainer
25105 _splitterClass: _Splitter,
25106
25107 postMixInProperties: function(){
25108 // change class name to indicate that BorderContainer is being used purely for
25109 // layout (like LayoutContainer) rather than for pretty formatting.
25110 if(!this.gutters){
25111 this.baseClass += "NoGutter";
25112 }
25113 this.inherited(arguments);
25114 },
25115
25116 startup: function(){
25117 if(this._started){ return; }
25118 array.forEach(this.getChildren(), this._setupChild, this);
25119 this.inherited(arguments);
25120 },
25121
25122 _setupChild: function(/*dijit/_WidgetBase*/ child){
25123 // Override _LayoutWidget._setupChild().
25124
25125 var region = child.region;
25126 if(region){
25127 this.inherited(arguments);
25128
25129 domClass.add(child.domNode, this.baseClass+"Pane");
25130
25131 var ltr = this.isLeftToRight();
25132 if(region == "leading"){ region = ltr ? "left" : "right"; }
25133 if(region == "trailing"){ region = ltr ? "right" : "left"; }
25134
25135 // Create draggable splitter for resizing pane,
25136 // or alternately if splitter=false but BorderContainer.gutters=true then
25137 // insert dummy div just for spacing
25138 if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
25139 var _Splitter = child.splitter ? this._splitterClass : _Gutter;
25140 if(lang.isString(_Splitter)){
25141 _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
25142 }
25143 var splitter = new _Splitter({
25144 id: child.id + "_splitter",
25145 container: this,
25146 child: child,
25147 region: region,
25148 live: this.liveSplitters
25149 });
25150 splitter.isSplitter = true;
25151 child._splitterWidget = splitter;
25152
25153 domConstruct.place(splitter.domNode, child.domNode, "after");
25154
25155 // Splitters aren't added as Contained children, so we need to call startup explicitly
25156 splitter.startup();
25157 }
25158 child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
25159 }
25160 },
25161
25162 layout: function(){
25163 // Implement _LayoutWidget.layout() virtual method.
25164 this._layoutChildren();
25165 },
25166
25167 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
25168 // Override _LayoutWidget.addChild().
25169 this.inherited(arguments);
25170 if(this._started){
25171 this.layout(); //OPT
25172 }
25173 },
25174
25175 removeChild: function(/*dijit/_WidgetBase*/ child){
25176 // Override _LayoutWidget.removeChild().
25177
25178 var region = child.region;
25179 var splitter = child._splitterWidget;
25180 if(splitter){
25181 splitter.destroy();
25182 delete child._splitterWidget;
25183 }
25184 this.inherited(arguments);
25185
25186 if(this._started){
25187 this._layoutChildren();
25188 }
25189 // Clean up whatever style changes we made to the child pane.
25190 // Unclear how height and width should be handled.
25191 domClass.remove(child.domNode, this.baseClass+"Pane");
25192 domStyle.set(child.domNode, {
25193 top: "auto",
25194 bottom: "auto",
25195 left: "auto",
25196 right: "auto",
25197 position: "static"
25198 });
25199 domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
25200 },
25201
25202 getChildren: function(){
25203 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
25204 return array.filter(this.inherited(arguments), function(widget){
25205 return !widget.isSplitter;
25206 });
25207 },
25208
25209 // TODO: remove in 2.0
25210 getSplitter: function(/*String*/region){
25211 // summary:
25212 // Returns the widget responsible for rendering the splitter associated with region
25213 // tags:
25214 // deprecated
25215 return array.filter(this.getChildren(), function(child){
25216 return child.region == region;
25217 })[0]._splitterWidget;
25218 },
25219
25220 resize: function(newSize, currentSize){
25221 // Overrides _LayoutWidget.resize().
25222
25223 // resetting potential padding to 0px to provide support for 100% width/height + padding
25224 // TODO: this hack doesn't respect the box model and is a temporary fix
25225 if(!this.cs || !this.pe){
25226 var node = this.domNode;
25227 this.cs = domStyle.getComputedStyle(node);
25228 this.pe = domGeometry.getPadExtents(node, this.cs);
25229 this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight);
25230 this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom);
25231
25232 domStyle.set(node, "padding", "0px");
25233 }
25234
25235 this.inherited(arguments);
25236 },
25237
25238 _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
25239 // summary:
25240 // This is the main routine for setting size/position of each child.
25241 // description:
25242 // With no arguments, measures the height of top/bottom panes, the width
25243 // of left/right panes, and then sizes all panes accordingly.
25244 //
25245 // With changedRegion specified (as "left", "top", "bottom", or "right"),
25246 // it changes that region's width/height to changedRegionSize and
25247 // then resizes other regions that were affected.
25248 // changedChildId:
25249 // Id of the child which should be resized because splitter was dragged.
25250 // changedChildSize:
25251 // The new width/height (in pixels) to make specified child
25252
25253 if(!this._borderBox || !this._borderBox.h){
25254 // We are currently hidden, or we haven't been sized by our parent yet.
25255 // Abort. Someone will resize us later.
25256 return;
25257 }
25258
25259 // Generate list of wrappers of my children in the order that I want layoutChildren()
25260 // to process them (i.e. from the outside to the inside)
25261 var wrappers = array.map(this.getChildren(), function(child, idx){
25262 return {
25263 pane: child,
25264 weight: [
25265 child.region == "center" ? Infinity : 0,
25266 child.layoutPriority,
25267 (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
25268 idx
25269 ]
25270 };
25271 }, this);
25272 wrappers.sort(function(a, b){
25273 var aw = a.weight, bw = b.weight;
25274 for(var i=0; i<aw.length; i++){
25275 if(aw[i] != bw[i]){
25276 return aw[i] - bw[i];
25277 }
25278 }
25279 return 0;
25280 });
25281
25282 // Make new list, combining the externally specified children with splitters and gutters
25283 var childrenAndSplitters = [];
25284 array.forEach(wrappers, function(wrapper){
25285 var pane = wrapper.pane;
25286 childrenAndSplitters.push(pane);
25287 if(pane._splitterWidget){
25288 childrenAndSplitters.push(pane._splitterWidget);
25289 }
25290 });
25291
25292 // Compute the box in which to lay out my children
25293 var dim = {
25294 l: this.pe.l,
25295 t: this.pe.t,
25296 w: this._borderBox.w - this.pe.w,
25297 h: this._borderBox.h - this.pe.h
25298 };
25299
25300 // Layout the children, possibly changing size due to a splitter drag
25301 layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
25302 changedChildId, changedChildSize);
25303 },
25304
25305 destroyRecursive: function(){
25306 // Destroy splitters first, while getChildren() still works
25307 array.forEach(this.getChildren(), function(child){
25308 var splitter = child._splitterWidget;
25309 if(splitter){
25310 splitter.destroy();
25311 }
25312 delete child._splitterWidget;
25313 });
25314
25315 // Then destroy the real children, and myself
25316 this.inherited(arguments);
25317 }
25318 });
25319
25320 BorderContainer.ChildWidgetProperties = {
25321 // summary:
25322 // These properties can be specified for the children of a BorderContainer.
25323
25324 // region: [const] String
25325 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
25326 // See the `dijit/layout/BorderContainer` description for details.
25327 region: '',
25328
25329 // layoutPriority: [const] Number
25330 // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
25331 // between children with a lower layoutPriority.
25332 layoutPriority: 0,
25333
25334 // splitter: [const] Boolean
25335 // Parameter for children where region != "center".
25336 // If true, enables user to resize the widget by putting a draggable splitter between
25337 // this widget and the region=center widget.
25338 splitter: false,
25339
25340 // minSize: [const] Number
25341 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
25342 minSize: 0,
25343
25344 // maxSize: [const] Number
25345 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
25346 maxSize: Infinity
25347 };
25348
25349 // Since any widget can be specified as a LayoutContainer child, mix it
25350 // into the base widget class. (This is a hack, but it's effective.)
25351 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
25352 lang.extend(_WidgetBase, /*===== {} || =====*/ BorderContainer.ChildWidgetProperties);
25353
25354 // For monkey patching
25355 BorderContainer._Splitter = _Splitter;
25356 BorderContainer._Gutter = _Gutter;
25357
25358 return BorderContainer;
25359 });
25360
25361 },
25362 'dijit/_base':function(){
25363 define("dijit/_base", [
25364 "./main",
25365 "./a11y", // used to be in dijit/_base/manager
25366 "./WidgetSet", // used to be in dijit/_base/manager
25367 "./_base/focus",
25368 "./_base/manager",
25369 "./_base/place",
25370 "./_base/popup",
25371 "./_base/scroll",
25372 "./_base/sniff",
25373 "./_base/typematic",
25374 "./_base/wai",
25375 "./_base/window"
25376 ], function(dijit){
25377
25378 // module:
25379 // dijit/_base
25380
25381 /*=====
25382 return {
25383 // summary:
25384 // Includes all the modules in dijit/_base
25385 };
25386 =====*/
25387
25388 return dijit._base;
25389 });
25390
25391 },
25392 'dojo/window':function(){
25393 define("dojo/window", ["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
25394 function(lang, has, baseWindow, dom, geom, style){
25395
25396 // module:
25397 // dojo/window
25398
25399 var window = {
25400 // summary:
25401 // TODOC
25402
25403 getBox: function(/*Document?*/ doc){
25404 // summary:
25405 // Returns the dimensions and scroll position of the viewable area of a browser window
25406
25407 doc = doc || baseWindow.doc;
25408
25409 var
25410 scrollRoot = (doc.compatMode == 'BackCompat') ? baseWindow.body(doc) : doc.documentElement,
25411 // get scroll position
25412 scroll = geom.docScroll(doc), // scrollRoot.scrollTop/Left should work
25413 w, h;
25414
25415 if(has("touch")){ // if(scrollbars not supported)
25416 var uiWindow = window.get(doc); // use UI window, not dojo.global window
25417 // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
25418 w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated
25419 h = uiWindow.innerHeight || scrollRoot.clientHeight;
25420 }else{
25421 // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
25422 // uiWindow.innerWidth/Height includes the scrollbar and cannot be used
25423 w = scrollRoot.clientWidth;
25424 h = scrollRoot.clientHeight;
25425 }
25426 return {
25427 l: scroll.x,
25428 t: scroll.y,
25429 w: w,
25430 h: h
25431 };
25432 },
25433
25434 get: function(/*Document*/ doc){
25435 // summary:
25436 // Get window object associated with document doc.
25437 // doc:
25438 // The document to get the associated window for.
25439
25440 // In some IE versions (at least 6.0), document.parentWindow does not return a
25441 // reference to the real window object (maybe a copy), so we must fix it as well
25442 // We use IE specific execScript to attach the real window reference to
25443 // document._parentWindow for later use
25444 if(has("ie") && window !== document.parentWindow){
25445 /*
25446 In IE 6, only the variable "window" can be used to connect events (others
25447 may be only copies).
25448 */
25449 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
25450 //to prevent memory leak, unset it after use
25451 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
25452 var win = doc._parentWindow;
25453 doc._parentWindow = null;
25454 return win; // Window
25455 }
25456
25457 return doc.parentWindow || doc.defaultView; // Window
25458 },
25459
25460 scrollIntoView: function(/*DomNode*/ node, /*Object?*/ pos){
25461 // summary:
25462 // Scroll the passed node into view, if it is not already.
25463
25464 // don't rely on node.scrollIntoView working just because the function is there
25465
25466 try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
25467 node = dom.byId(node);
25468 var doc = node.ownerDocument || baseWindow.doc, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined?
25469 body = baseWindow.body(doc),
25470 html = doc.documentElement || body.parentNode,
25471 isIE = has("ie"), isWK = has("webkit");
25472 // if an untested browser, then use the native method
25473 if((!(has("mozilla") || isIE || isWK || has("opera")) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
25474 node.scrollIntoView(false); // short-circuit to native if possible
25475 return;
25476 }
25477 var backCompat = doc.compatMode == 'BackCompat',
25478 clientAreaRoot = (isIE >= 9 && "frameElement" in node.ownerDocument.parentWindow)
25479 ? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body)
25480 : (backCompat ? body : html),
25481 scrollRoot = isWK ? body : clientAreaRoot,
25482 rootWidth = clientAreaRoot.clientWidth,
25483 rootHeight = clientAreaRoot.clientHeight,
25484 rtl = !geom.isBodyLtr(doc),
25485 nodePos = pos || geom.position(node),
25486 el = node.parentNode,
25487 isFixed = function(el){
25488 return ((isIE <= 6 || (isIE && backCompat))? false : (style.get(el, 'position').toLowerCase() == "fixed"));
25489 };
25490 if(isFixed(node)){ return; } // nothing to do
25491
25492 while(el){
25493 if(el == body){ el = scrollRoot; }
25494 var elPos = geom.position(el),
25495 fixedPos = isFixed(el);
25496
25497 if(el == scrollRoot){
25498 elPos.w = rootWidth; elPos.h = rootHeight;
25499 if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
25500 if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
25501 if(elPos.y < 0 || !isIE){ elPos.y = 0; }
25502 }else{
25503 var pb = geom.getPadBorderExtents(el);
25504 elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
25505 var clientSize = el.clientWidth,
25506 scrollBarSize = elPos.w - clientSize;
25507 if(clientSize > 0 && scrollBarSize > 0){
25508 elPos.w = clientSize;
25509 elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0;
25510 }
25511 clientSize = el.clientHeight;
25512 scrollBarSize = elPos.h - clientSize;
25513 if(clientSize > 0 && scrollBarSize > 0){
25514 elPos.h = clientSize;
25515 }
25516 }
25517 if(fixedPos){ // bounded by viewport, not parents
25518 if(elPos.y < 0){
25519 elPos.h += elPos.y; elPos.y = 0;
25520 }
25521 if(elPos.x < 0){
25522 elPos.w += elPos.x; elPos.x = 0;
25523 }
25524 if(elPos.y + elPos.h > rootHeight){
25525 elPos.h = rootHeight - elPos.y;
25526 }
25527 if(elPos.x + elPos.w > rootWidth){
25528 elPos.w = rootWidth - elPos.x;
25529 }
25530 }
25531 // calculate overflow in all 4 directions
25532 var l = nodePos.x - elPos.x, // beyond left: < 0
25533 t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
25534 r = l + nodePos.w - elPos.w, // beyond right: > 0
25535 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
25536 if(r * l > 0){
25537 var s = Math[l < 0? "max" : "min"](l, r);
25538 if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
25539 nodePos.x += el.scrollLeft;
25540 el.scrollLeft += s;
25541 nodePos.x -= el.scrollLeft;
25542 }
25543 if(bot * t > 0){
25544 nodePos.y += el.scrollTop;
25545 el.scrollTop += Math[t < 0? "max" : "min"](t, bot);
25546 nodePos.y -= el.scrollTop;
25547 }
25548 el = (el != scrollRoot) && !fixedPos && el.parentNode;
25549 }
25550 }catch(error){
25551 console.error('scrollIntoView: ' + error);
25552 node.scrollIntoView(false);
25553 }
25554 }
25555 };
25556
25557 1 && lang.setObject("dojo.window", window);
25558
25559 return window;
25560 });
25561
25562 },
25563 'dojo/number':function(){
25564 define("dojo/number", [/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
25565 function(/*===== declare, =====*/ lang, i18n, nlsNumber, dstring, dregexp){
25566
25567 // module:
25568 // dojo/number
25569
25570 var number = {
25571 // summary:
25572 // localized formatting and parsing routines for Number
25573 };
25574 lang.setObject("dojo.number", number);
25575
25576 /*=====
25577 number.__FormatOptions = declare(null, {
25578 // pattern: String?
25579 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25580 // with this string. Default value is based on locale. Overriding this property will defeat
25581 // localization. Literal characters in patterns are not supported.
25582 // type: String?
25583 // choose a format type based on the locale from the following:
25584 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25585 // places: Number?
25586 // fixed number of decimal places to show. This overrides any
25587 // information in the provided pattern.
25588 // round: Number?
25589 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25590 // means do not round.
25591 // locale: String?
25592 // override the locale used to determine formatting rules
25593 // fractional: Boolean?
25594 // If false, show no decimal places, overriding places and pattern settings.
25595 });
25596 =====*/
25597
25598 number.format = function(/*Number*/ value, /*number.__FormatOptions?*/ options){
25599 // summary:
25600 // Format a Number as a String, using locale-specific settings
25601 // description:
25602 // Create a string from a Number using a known localized pattern.
25603 // Formatting patterns appropriate to the locale are chosen from the
25604 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
25605 // delimiters.
25606 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
25607 // value:
25608 // the number to be formatted
25609
25610 options = lang.mixin({}, options || {});
25611 var locale = i18n.normalizeLocale(options.locale),
25612 bundle = i18n.getLocalization("dojo.cldr", "number", locale);
25613 options.customs = bundle;
25614 var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
25615 if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
25616 return number._applyPattern(value, pattern, options); // String
25617 };
25618
25619 //number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
25620 number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
25621
25622 number._applyPattern = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatOptions?*/ options){
25623 // summary:
25624 // Apply pattern to format value as a string using options. Gives no
25625 // consideration to local customs.
25626 // value:
25627 // the number to be formatted.
25628 // pattern:
25629 // a pattern string as described by
25630 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25631 // options: number.__FormatOptions?
25632 // _applyPattern is usually called via `dojo/number.format()` which
25633 // populates an extra property in the options parameter, "customs".
25634 // The customs object specifies group and decimal parameters if set.
25635
25636 //TODO: support escapes
25637 options = options || {};
25638 var group = options.customs.group,
25639 decimal = options.customs.decimal,
25640 patternList = pattern.split(';'),
25641 positivePattern = patternList[0];
25642 pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
25643
25644 //TODO: only test against unescaped
25645 if(pattern.indexOf('%') != -1){
25646 value *= 100;
25647 }else if(pattern.indexOf('\u2030') != -1){
25648 value *= 1000; // per mille
25649 }else if(pattern.indexOf('\u00a4') != -1){
25650 group = options.customs.currencyGroup || group;//mixins instead?
25651 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
25652 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
25653 var prop = ["symbol", "currency", "displayName"][match.length-1];
25654 return options[prop] || options.currency || "";
25655 });
25656 }else if(pattern.indexOf('E') != -1){
25657 throw new Error("exponential notation not supported");
25658 }
25659
25660 //TODO: support @ sig figs?
25661 var numberPatternRE = number._numberPatternRE;
25662 var numberPattern = positivePattern.match(numberPatternRE);
25663 if(!numberPattern){
25664 throw new Error("unable to find a number expression in pattern: "+pattern);
25665 }
25666 if(options.fractional === false){ options.places = 0; }
25667 return pattern.replace(numberPatternRE,
25668 number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
25669 };
25670
25671 number.round = function(/*Number*/ value, /*Number?*/ places, /*Number?*/ increment){
25672 // summary:
25673 // Rounds to the nearest value with the given number of decimal places, away from zero
25674 // description:
25675 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
25676 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
25677 // fractional increments also, such as the nearest quarter.
25678 // NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround.
25679 // value:
25680 // The number to round
25681 // places:
25682 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
25683 // Must be non-negative.
25684 // increment:
25685 // Rounds next place to nearest value of increment/10. 10 by default.
25686 // example:
25687 // | >>> number.round(-0.5)
25688 // | -1
25689 // | >>> number.round(162.295, 2)
25690 // | 162.29 // note floating point error. Should be 162.3
25691 // | >>> number.round(10.71, 0, 2.5)
25692 // | 10.75
25693 var factor = 10 / (increment || 10);
25694 return (factor * +value).toFixed(places) / factor; // Number
25695 };
25696
25697 if((0.9).toFixed() == 0){
25698 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
25699 // is just after the rounding place and is >=5
25700 var round = number.round;
25701 number.round = function(v, p, m){
25702 var d = Math.pow(10, -p || 0), a = Math.abs(v);
25703 if(!v || a >= d){
25704 d = 0;
25705 }else{
25706 a /= d;
25707 if(a < 0.5 || a >= 0.95){
25708 d = 0;
25709 }
25710 }
25711 return round(v, p, m) + (v > 0 ? d : -d);
25712 };
25713
25714 // Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above.
25715 /*===== number.round = round; =====*/
25716 }
25717
25718 /*=====
25719 number.__FormatAbsoluteOptions = declare(null, {
25720 // decimal: String?
25721 // the decimal separator
25722 // group: String?
25723 // the group separator
25724 // places: Number|String?
25725 // number of decimal places. the range "n,m" will format to m places.
25726 // round: Number?
25727 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25728 // means don't round.
25729 });
25730 =====*/
25731
25732 number._formatAbsolute = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatAbsoluteOptions?*/ options){
25733 // summary:
25734 // Apply numeric pattern to absolute value using options. Gives no
25735 // consideration to local customs.
25736 // value:
25737 // the number to be formatted, ignores sign
25738 // pattern:
25739 // the number portion of a pattern (e.g. `#,##0.00`)
25740 options = options || {};
25741 if(options.places === true){options.places=0;}
25742 if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
25743
25744 var patternParts = pattern.split("."),
25745 comma = typeof options.places == "string" && options.places.indexOf(","),
25746 maxPlaces = options.places;
25747 if(comma){
25748 maxPlaces = options.places.substring(comma + 1);
25749 }else if(!(maxPlaces >= 0)){
25750 maxPlaces = (patternParts[1] || []).length;
25751 }
25752 if(!(options.round < 0)){
25753 value = number.round(value, maxPlaces, options.round);
25754 }
25755
25756 var valueParts = String(Math.abs(value)).split("."),
25757 fractional = valueParts[1] || "";
25758 if(patternParts[1] || options.places){
25759 if(comma){
25760 options.places = options.places.substring(0, comma);
25761 }
25762 // Pad fractional with trailing zeros
25763 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
25764 if(pad > fractional.length){
25765 valueParts[1] = dstring.pad(fractional, pad, '0', true);
25766 }
25767
25768 // Truncate fractional
25769 if(maxPlaces < fractional.length){
25770 valueParts[1] = fractional.substr(0, maxPlaces);
25771 }
25772 }else{
25773 if(valueParts[1]){ valueParts.pop(); }
25774 }
25775
25776 // Pad whole with leading zeros
25777 var patternDigits = patternParts[0].replace(',', '');
25778 pad = patternDigits.indexOf("0");
25779 if(pad != -1){
25780 pad = patternDigits.length - pad;
25781 if(pad > valueParts[0].length){
25782 valueParts[0] = dstring.pad(valueParts[0], pad);
25783 }
25784
25785 // Truncate whole
25786 if(patternDigits.indexOf("#") == -1){
25787 valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
25788 }
25789 }
25790
25791 // Add group separators
25792 var index = patternParts[0].lastIndexOf(','),
25793 groupSize, groupSize2;
25794 if(index != -1){
25795 groupSize = patternParts[0].length - index - 1;
25796 var remainder = patternParts[0].substr(0, index);
25797 index = remainder.lastIndexOf(',');
25798 if(index != -1){
25799 groupSize2 = remainder.length - index - 1;
25800 }
25801 }
25802 var pieces = [];
25803 for(var whole = valueParts[0]; whole;){
25804 var off = whole.length - groupSize;
25805 pieces.push((off > 0) ? whole.substr(off) : whole);
25806 whole = (off > 0) ? whole.slice(0, off) : "";
25807 if(groupSize2){
25808 groupSize = groupSize2;
25809 delete groupSize2;
25810 }
25811 }
25812 valueParts[0] = pieces.reverse().join(options.group || ",");
25813
25814 return valueParts.join(options.decimal || ".");
25815 };
25816
25817 /*=====
25818 number.__RegexpOptions = declare(null, {
25819 // pattern: String?
25820 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25821 // with this string. Default value is based on locale. Overriding this property will defeat
25822 // localization.
25823 // type: String?
25824 // choose a format type based on the locale from the following:
25825 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25826 // locale: String?
25827 // override the locale used to determine formatting rules
25828 // strict: Boolean?
25829 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25830 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25831 // places: Number|String?
25832 // number of decimal places to accept: Infinity, a positive number, or
25833 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
25834 });
25835 =====*/
25836 number.regexp = function(/*number.__RegexpOptions?*/ options){
25837 // summary:
25838 // Builds the regular needed to parse a number
25839 // description:
25840 // Returns regular expression with positive and negative match, group
25841 // and decimal separators
25842 return number._parseInfo(options).regexp; // String
25843 };
25844
25845 number._parseInfo = function(/*Object?*/ options){
25846 options = options || {};
25847 var locale = i18n.normalizeLocale(options.locale),
25848 bundle = i18n.getLocalization("dojo.cldr", "number", locale),
25849 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
25850 //TODO: memoize?
25851 group = bundle.group,
25852 decimal = bundle.decimal,
25853 factor = 1;
25854
25855 if(pattern.indexOf('%') != -1){
25856 factor /= 100;
25857 }else if(pattern.indexOf('\u2030') != -1){
25858 factor /= 1000; // per mille
25859 }else{
25860 var isCurrency = pattern.indexOf('\u00a4') != -1;
25861 if(isCurrency){
25862 group = bundle.currencyGroup || group;
25863 decimal = bundle.currencyDecimal || decimal;
25864 }
25865 }
25866
25867 //TODO: handle quoted escapes
25868 var patternList = pattern.split(';');
25869 if(patternList.length == 1){
25870 patternList.push("-" + patternList[0]);
25871 }
25872
25873 var re = dregexp.buildGroupRE(patternList, function(pattern){
25874 pattern = "(?:"+dregexp.escapeString(pattern, '.')+")";
25875 return pattern.replace(number._numberPatternRE, function(format){
25876 var flags = {
25877 signed: false,
25878 separator: options.strict ? group : [group,""],
25879 fractional: options.fractional,
25880 decimal: decimal,
25881 exponent: false
25882 },
25883
25884 parts = format.split('.'),
25885 places = options.places;
25886
25887 // special condition for percent (factor != 1)
25888 // allow decimal places even if not specified in pattern
25889 if(parts.length == 1 && factor != 1){
25890 parts[1] = "###";
25891 }
25892 if(parts.length == 1 || places === 0){
25893 flags.fractional = false;
25894 }else{
25895 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
25896 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
25897 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
25898 flags.places = places;
25899 }
25900 var groups = parts[0].split(',');
25901 if(groups.length > 1){
25902 flags.groupSize = groups.pop().length;
25903 if(groups.length > 1){
25904 flags.groupSize2 = groups.pop().length;
25905 }
25906 }
25907 return "("+number._realNumberRegexp(flags)+")";
25908 });
25909 }, true);
25910
25911 if(isCurrency){
25912 // substitute the currency symbol for the placeholder in the pattern
25913 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
25914 var prop = ["symbol", "currency", "displayName"][target.length-1],
25915 symbol = dregexp.escapeString(options[prop] || options.currency || "");
25916 before = before ? "[\\s\\xa0]" : "";
25917 after = after ? "[\\s\\xa0]" : "";
25918 if(!options.strict){
25919 if(before){before += "*";}
25920 if(after){after += "*";}
25921 return "(?:"+before+symbol+after+")?";
25922 }
25923 return before+symbol+after;
25924 });
25925 }
25926
25927 //TODO: substitute localized sign/percent/permille/etc.?
25928
25929 // normalize whitespace and return
25930 return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
25931 };
25932
25933 /*=====
25934 number.__ParseOptions = declare(null, {
25935 // pattern: String?
25936 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25937 // with this string. Default value is based on locale. Overriding this property will defeat
25938 // localization. Literal characters in patterns are not supported.
25939 // type: String?
25940 // choose a format type based on the locale from the following:
25941 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25942 // locale: String?
25943 // override the locale used to determine formatting rules
25944 // strict: Boolean?
25945 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25946 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25947 // fractional: Boolean|Array?
25948 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
25949 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
25950 });
25951 =====*/
25952 number.parse = function(/*String*/ expression, /*number.__ParseOptions?*/ options){
25953 // summary:
25954 // Convert a properly formatted string to a primitive Number, using
25955 // locale-specific settings.
25956 // description:
25957 // Create a Number from a string using a known localized pattern.
25958 // Formatting patterns are chosen appropriate to the locale
25959 // and follow the syntax described by
25960 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25961 // Note that literal characters in patterns are not supported.
25962 // expression:
25963 // A string representation of a Number
25964 var info = number._parseInfo(options),
25965 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
25966 if(!results){
25967 return NaN; //NaN
25968 }
25969 var absoluteMatch = results[1]; // match for the positive expression
25970 if(!results[1]){
25971 if(!results[2]){
25972 return NaN; //NaN
25973 }
25974 // matched the negative pattern
25975 absoluteMatch =results[2];
25976 info.factor *= -1;
25977 }
25978
25979 // Transform it to something Javascript can parse as a number. Normalize
25980 // decimal point and strip out group separators or alternate forms of whitespace
25981 absoluteMatch = absoluteMatch.
25982 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
25983 replace(info.decimal, ".");
25984 // Adjust for negative sign, percent, etc. as necessary
25985 return absoluteMatch * info.factor; //Number
25986 };
25987
25988 /*=====
25989 number.__RealNumberRegexpFlags = declare(null, {
25990 // places: Number?
25991 // The integer number of decimal places or a range given as "n,m". If
25992 // not given, the decimal part is optional and the number of places is
25993 // unlimited.
25994 // decimal: String?
25995 // A string for the character used as the decimal point. Default
25996 // is ".".
25997 // fractional: Boolean|Array?
25998 // Whether decimal places are used. Can be true, false, or [true,
25999 // false]. Default is [true, false] which means optional.
26000 // exponent: Boolean|Array?
26001 // Express in exponential notation. Can be true, false, or [true,
26002 // false]. Default is [true, false], (i.e. will match if the
26003 // exponential part is present are not).
26004 // eSigned: Boolean|Array?
26005 // The leading plus-or-minus sign on the exponent. Can be true,
26006 // false, or [true, false]. Default is [true, false], (i.e. will
26007 // match if it is signed or unsigned). flags in regexp.integer can be
26008 // applied.
26009 });
26010 =====*/
26011
26012 number._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags){
26013 // summary:
26014 // Builds a regular expression to match a real number in exponential
26015 // notation
26016
26017 // assign default values to missing parameters
26018 flags = flags || {};
26019 //TODO: use mixin instead?
26020 if(!("places" in flags)){ flags.places = Infinity; }
26021 if(typeof flags.decimal != "string"){ flags.decimal = "."; }
26022 if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
26023 if(!("exponent" in flags)){ flags.exponent = [true, false]; }
26024 if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
26025
26026 var integerRE = number._integerRegexp(flags),
26027 decimalRE = dregexp.buildGroupRE(flags.fractional,
26028 function(q){
26029 var re = "";
26030 if(q && (flags.places!==0)){
26031 re = "\\" + flags.decimal;
26032 if(flags.places == Infinity){
26033 re = "(?:" + re + "\\d+)?";
26034 }else{
26035 re += "\\d{" + flags.places + "}";
26036 }
26037 }
26038 return re;
26039 },
26040 true
26041 );
26042
26043 var exponentRE = dregexp.buildGroupRE(flags.exponent,
26044 function(q){
26045 if(q){ return "([eE]" + number._integerRegexp({ signed: flags.eSigned}) + ")"; }
26046 return "";
26047 }
26048 );
26049
26050 var realRE = integerRE + decimalRE;
26051 // allow for decimals without integers, e.g. .25
26052 if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
26053 return realRE + exponentRE; // String
26054 };
26055
26056 /*=====
26057 number.__IntegerRegexpFlags = declare(null, {
26058 // signed: Boolean?
26059 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
26060 // Default is `[true, false]`, (i.e. will match if it is signed
26061 // or unsigned).
26062 // separator: String?
26063 // The character used as the thousands separator. Default is no
26064 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
26065 // makes ',' optional.
26066 // groupSize: Number?
26067 // group size between separators
26068 // groupSize2: Number?
26069 // second grouping, where separators 2..n have a different interval than the first separator (for India)
26070 });
26071 =====*/
26072
26073 number._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags){
26074 // summary:
26075 // Builds a regular expression that matches an integer
26076
26077 // assign default values to missing parameters
26078 flags = flags || {};
26079 if(!("signed" in flags)){ flags.signed = [true, false]; }
26080 if(!("separator" in flags)){
26081 flags.separator = "";
26082 }else if(!("groupSize" in flags)){
26083 flags.groupSize = 3;
26084 }
26085
26086 var signRE = dregexp.buildGroupRE(flags.signed,
26087 function(q){ return q ? "[-+]" : ""; },
26088 true
26089 );
26090
26091 var numberRE = dregexp.buildGroupRE(flags.separator,
26092 function(sep){
26093 if(!sep){
26094 return "(?:\\d+)";
26095 }
26096
26097 sep = dregexp.escapeString(sep);
26098 if(sep == " "){ sep = "\\s"; }
26099 else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
26100
26101 var grp = flags.groupSize, grp2 = flags.groupSize2;
26102 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
26103 if(grp2){
26104 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
26105 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
26106 }
26107 return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
26108 },
26109 true
26110 );
26111
26112 return signRE + numberRE; // String
26113 };
26114
26115 return number;
26116 });
26117
26118 },
26119 'dijit/_FocusMixin':function(){
26120 define("dijit/_FocusMixin", [
26121 "./focus",
26122 "./_WidgetBase",
26123 "dojo/_base/declare", // declare
26124 "dojo/_base/lang" // lang.extend
26125 ], function(focus, _WidgetBase, declare, lang){
26126
26127 // module:
26128 // dijit/_FocusMixin
26129
26130 // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
26131 // to be last in the inheritance chain, so mixin to _WidgetBase.
26132 lang.extend(_WidgetBase, {
26133 // focused: [readonly] Boolean
26134 // This widget or a widget it contains has focus, or is "active" because
26135 // it was recently clicked.
26136 focused: false,
26137
26138 onFocus: function(){
26139 // summary:
26140 // Called when the widget becomes "active" because
26141 // it or a widget inside of it either has focus, or has recently
26142 // been clicked.
26143 // tags:
26144 // callback
26145 },
26146
26147 onBlur: function(){
26148 // summary:
26149 // Called when the widget stops being "active" because
26150 // focus moved to something outside of it, or the user
26151 // clicked somewhere outside of it, or the widget was
26152 // hidden.
26153 // tags:
26154 // callback
26155 },
26156
26157 _onFocus: function(){
26158 // summary:
26159 // This is where widgets do processing for when they are active,
26160 // such as changing CSS classes. See onFocus() for more details.
26161 // tags:
26162 // protected
26163 this.onFocus();
26164 },
26165
26166 _onBlur: function(){
26167 // summary:
26168 // This is where widgets do processing for when they stop being active,
26169 // such as changing CSS classes. See onBlur() for more details.
26170 // tags:
26171 // protected
26172 this.onBlur();
26173 }
26174 });
26175
26176 return declare("dijit._FocusMixin", null, {
26177 // summary:
26178 // Mixin to widget to provide _onFocus() and _onBlur() methods that
26179 // fire when a widget or its descendants get/lose focus
26180
26181 // flag that I want _onFocus()/_onBlur() notifications from focus manager
26182 _focusManager: focus
26183 });
26184
26185 });
26186
26187 },
26188 'dojo/data/util/filter':function(){
26189 define("dojo/data/util/filter", ["../../_base/lang"], function(lang){
26190 // module:
26191 // dojo/data/util/filter
26192 // summary:
26193 // TODOC
26194
26195 var filter = {};
26196 lang.setObject("dojo.data.util.filter", filter);
26197
26198 filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
26199 // summary:
26200 // Helper function to convert a simple pattern to a regular expression for matching.
26201 // description:
26202 // Returns a regular expression object that conforms to the defined conversion rules.
26203 // For example:
26204 //
26205 // - ca* -> /^ca.*$/
26206 // - *ca* -> /^.*ca.*$/
26207 // - *c\*a* -> /^.*c\*a.*$/
26208 // - *c\*a?* -> /^.*c\*a..*$/
26209 //
26210 // and so on.
26211 // pattern: string
26212 // A simple matching pattern to convert that follows basic rules:
26213 //
26214 // - * Means match anything, so ca* means match anything starting with ca
26215 // - ? Means match single character. So, b?b will match to bob and bab, and so on.
26216 // - \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
26217 //
26218 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
26219 // represented by \\ to be treated as an ordinary \ character instead of an escape.
26220 // ignoreCase:
26221 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
26222 // By default, it is assumed case sensitive.
26223
26224 var rxp = "^";
26225 var c = null;
26226 for(var i = 0; i < pattern.length; i++){
26227 c = pattern.charAt(i);
26228 switch(c){
26229 case '\\':
26230 rxp += c;
26231 i++;
26232 rxp += pattern.charAt(i);
26233 break;
26234 case '*':
26235 rxp += ".*"; break;
26236 case '?':
26237 rxp += "."; break;
26238 case '$':
26239 case '^':
26240 case '/':
26241 case '+':
26242 case '.':
26243 case '|':
26244 case '(':
26245 case ')':
26246 case '{':
26247 case '}':
26248 case '[':
26249 case ']':
26250 rxp += "\\"; //fallthrough
26251 default:
26252 rxp += c;
26253 }
26254 }
26255 rxp += "$";
26256 if(ignoreCase){
26257 return new RegExp(rxp,"mi"); //RegExp
26258 }else{
26259 return new RegExp(rxp,"m"); //RegExp
26260 }
26261
26262 };
26263
26264 return filter;
26265 });
26266
26267 },
26268 'dijit/_WidgetsInTemplateMixin':function(){
26269 define("dijit/_WidgetsInTemplateMixin", [
26270 "dojo/_base/array", // array.forEach
26271 "dojo/_base/declare", // declare
26272 "dojo/parser" // parser.parse
26273 ], function(array, declare, parser){
26274
26275 // module:
26276 // dijit/_WidgetsInTemplateMixin
26277
26278 return declare("dijit._WidgetsInTemplateMixin", null, {
26279 // summary:
26280 // Mixin to supplement _TemplatedMixin when template contains widgets
26281
26282 // _earlyTemplatedStartup: Boolean
26283 // A fallback to preserve the 1.0 - 1.3 behavior of children in
26284 // templates having their startup called before the parent widget
26285 // fires postCreate. Defaults to 'false', causing child widgets to
26286 // have their .startup() called immediately before a parent widget
26287 // .startup(), but always after the parent .postCreate(). Set to
26288 // 'true' to re-enable to previous, arguably broken, behavior.
26289 _earlyTemplatedStartup: false,
26290
26291 // widgetsInTemplate: [protected] Boolean
26292 // Should we parse the template to find widgets that might be
26293 // declared in markup inside it? (Remove for 2.0 and assume true)
26294 widgetsInTemplate: true,
26295
26296 _beforeFillContent: function(){
26297 if(this.widgetsInTemplate){
26298 // Before copying over content, instantiate widgets in template
26299 var node = this.domNode;
26300
26301 var cw = (this._startupWidgets = parser.parse(node, {
26302 noStart: !this._earlyTemplatedStartup,
26303 template: true,
26304 inherited: {dir: this.dir, lang: this.lang, textDir: this.textDir},
26305 propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
26306 scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
26307 }));
26308
26309 if(!cw.isFulfilled()){
26310 throw new Error(this.declaredClass + ": parser returned unfilled promise (probably waiting for module auto-load), " +
26311 "unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.");
26312 }
26313
26314 // _WidgetBase::destroy() will destroy any supporting widgets under this.domNode.
26315 // If we wanted to, we could call this.own() on anything in this._startupWidgets that was moved outside
26316 // of this.domNode (like Dialog, which is moved to <body>).
26317
26318 this._attachTemplateNodes(cw, function(n,p){
26319 return n[p];
26320 });
26321 }
26322 },
26323
26324 startup: function(){
26325 array.forEach(this._startupWidgets, function(w){
26326 if(w && !w._started && w.startup){
26327 w.startup();
26328 }
26329 });
26330 this.inherited(arguments);
26331 }
26332 });
26333 });
26334
26335 },
26336 'dojo/fx/Toggler':function(){
26337 define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
26338 function(lang, declare, baseFx, connectUtil){
26339 // module:
26340 // dojo/fx/Toggler
26341
26342 return declare("dojo.fx.Toggler", null, {
26343 // summary:
26344 // A simple `dojo.Animation` toggler API.
26345 // description:
26346 // class constructor for an animation toggler. It accepts a packed
26347 // set of arguments about what type of animation to use in each
26348 // direction, duration, etc. All available members are mixed into
26349 // these animations from the constructor (for example, `node`,
26350 // `showDuration`, `hideDuration`).
26351 // example:
26352 // | var t = new dojo/fx/Toggler({
26353 // | node: "nodeId",
26354 // | showDuration: 500,
26355 // | // hideDuration will default to "200"
26356 // | showFunc: dojo/fx/wipeIn,
26357 // | // hideFunc will default to "fadeOut"
26358 // | });
26359 // | t.show(100); // delay showing for 100ms
26360 // | // ...time passes...
26361 // | t.hide();
26362
26363 // node: DomNode
26364 // the node to target for the showing and hiding animations
26365 node: null,
26366
26367 // showFunc: Function
26368 // The function that returns the `dojo.Animation` to show the node
26369 showFunc: baseFx.fadeIn,
26370
26371 // hideFunc: Function
26372 // The function that returns the `dojo.Animation` to hide the node
26373 hideFunc: baseFx.fadeOut,
26374
26375 // showDuration:
26376 // Time in milliseconds to run the show Animation
26377 showDuration: 200,
26378
26379 // hideDuration:
26380 // Time in milliseconds to run the hide Animation
26381 hideDuration: 200,
26382
26383 // FIXME: need a policy for where the toggler should "be" the next
26384 // time show/hide are called if we're stopped somewhere in the
26385 // middle.
26386 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
26387 // each animation individually.
26388 // FIXME: also would be nice to have events from the animations exposed/bridged
26389
26390 /*=====
26391 _showArgs: null,
26392 _showAnim: null,
26393
26394 _hideArgs: null,
26395 _hideAnim: null,
26396
26397 _isShowing: false,
26398 _isHiding: false,
26399 =====*/
26400
26401 constructor: function(args){
26402 var _t = this;
26403
26404 lang.mixin(_t, args);
26405 _t.node = args.node;
26406 _t._showArgs = lang.mixin({}, args);
26407 _t._showArgs.node = _t.node;
26408 _t._showArgs.duration = _t.showDuration;
26409 _t.showAnim = _t.showFunc(_t._showArgs);
26410
26411 _t._hideArgs = lang.mixin({}, args);
26412 _t._hideArgs.node = _t.node;
26413 _t._hideArgs.duration = _t.hideDuration;
26414 _t.hideAnim = _t.hideFunc(_t._hideArgs);
26415
26416 connectUtil.connect(_t.showAnim, "beforeBegin", lang.hitch(_t.hideAnim, "stop", true));
26417 connectUtil.connect(_t.hideAnim, "beforeBegin", lang.hitch(_t.showAnim, "stop", true));
26418 },
26419
26420 show: function(delay){
26421 // summary:
26422 // Toggle the node to showing
26423 // delay: Integer?
26424 // Amount of time to stall playing the show animation
26425 return this.showAnim.play(delay || 0);
26426 },
26427
26428 hide: function(delay){
26429 // summary:
26430 // Toggle the node to hidden
26431 // delay: Integer?
26432 // Amount of time to stall playing the hide animation
26433 return this.hideAnim.play(delay || 0);
26434 }
26435 });
26436
26437 });
26438
26439 },
26440 'dijit/form/FilteringSelect':function(){
26441 define("dijit/form/FilteringSelect", [
26442 "dojo/data/util/filter", // filter.patternToRegExp
26443 "dojo/_base/declare", // declare
26444 "dojo/_base/lang", // lang.mixin
26445 "dojo/when",
26446 "./MappedTextBox",
26447 "./ComboBoxMixin"
26448 ], function(filter, declare, lang, when, MappedTextBox, ComboBoxMixin){
26449
26450 // module:
26451 // dijit/form/FilteringSelect
26452
26453 return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
26454 // summary:
26455 // An enhanced version of the HTML SELECT tag, populated dynamically
26456 //
26457 // description:
26458 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
26459 // very nicely with very large data sets because it can load and page data as needed.
26460 // It also resembles ComboBox, but does not allow values outside of the provided ones.
26461 // If OPTION tags are used as the data provider via markup, then the
26462 // OPTION tag's child text node is used as the displayed value when selected
26463 // while the OPTION tag's value attribute is used as the widget value on form submit.
26464 // To set the default value when using OPTION tags, specify the selected
26465 // attribute on 1 of the child OPTION tags.
26466 //
26467 // Similar features:
26468 //
26469 // - There is a drop down list of possible values.
26470 // - You can only enter a value from the drop down list. (You can't
26471 // enter an arbitrary value.)
26472 // - The value submitted with the form is the hidden value (ex: CA),
26473 // not the displayed value a.k.a. label (ex: California)
26474 //
26475 // Enhancements over plain HTML version:
26476 //
26477 // - If you type in some text then it will filter down the list of
26478 // possible values in the drop down list.
26479 // - List can be specified either as a static list or via a javascript
26480 // function (that can get the list from a server)
26481
26482 // required: Boolean
26483 // True (default) if user is required to enter a value into this field.
26484 required: true,
26485
26486 _lastDisplayedValue: "",
26487
26488 _isValidSubset: function(){
26489 return this._opened;
26490 },
26491
26492 isValid: function(){
26493 // Overrides ValidationTextBox.isValid()
26494 return !!this.item || (!this.required && this.get('displayedValue') == ""); // #5974
26495 },
26496
26497 _refreshState: function(){
26498 if(!this.searchTimer){ // state will be refreshed after results are returned
26499 this.inherited(arguments);
26500 }
26501 },
26502
26503 _callbackSetLabel: function(
26504 /*Array*/ result,
26505 /*Object*/ query,
26506 /*Object*/ options,
26507 /*Boolean?*/ priorityChange){
26508 // summary:
26509 // Callback from dojo.store after lookup of user entered value finishes
26510
26511 // setValue does a synchronous lookup,
26512 // so it calls _callbackSetLabel directly,
26513 // and so does not pass dataObject
26514 // still need to test against _lastQuery in case it came too late
26515 if((query && query[this.searchAttr] !== this._lastQuery) || (!query && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
26516 return;
26517 }
26518 if(!result.length){
26519 //#3268: don't modify display value on bad input
26520 //#3285: change CSS to indicate error
26521 this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
26522 }else{
26523 this.set('item', result[0], priorityChange);
26524 }
26525 },
26526
26527 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
26528 // Callback when a data store query completes.
26529 // Overrides ComboBox._openResultList()
26530
26531 // #3285: tap into search callback to see if user's query resembles a match
26532 if(query[this.searchAttr] !== this._lastQuery){
26533 return;
26534 }
26535 this.inherited(arguments);
26536
26537 if(this.item === undefined){ // item == undefined for keyboard search
26538 // If the search returned no items that means that the user typed
26539 // in something invalid (and they can't make it valid by typing more characters),
26540 // so flag the FilteringSelect as being in an invalid state
26541 this.validate(true);
26542 }
26543 },
26544
26545 _getValueAttr: function(){
26546 // summary:
26547 // Hook for get('value') to work.
26548
26549 // don't get the textbox value but rather the previously set hidden value.
26550 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
26551 return this.valueNode.value;
26552 },
26553
26554 _getValueField: function(){
26555 // Overrides ComboBox._getValueField()
26556 return "value";
26557 },
26558
26559 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
26560 // summary:
26561 // Hook so set('value', value) works.
26562 // description:
26563 // Sets the value of the select.
26564 // Also sets the label to the corresponding value by reverse lookup.
26565 if(!this._onChangeActive){ priorityChange = null; }
26566
26567 if(item === undefined){
26568 if(value === null || value === ''){
26569 value = '';
26570 if(!lang.isString(displayedValue)){
26571 this._setDisplayedValueAttr(displayedValue||'', priorityChange);
26572 return;
26573 }
26574 }
26575
26576 var self = this;
26577 this._lastQuery = value;
26578 when(this.store.get(value), function(item){
26579 self._callbackSetLabel(item? [item] : [], undefined, undefined, priorityChange);
26580 });
26581 }else{
26582 this.valueNode.value = value;
26583 this.inherited(arguments);
26584 }
26585 },
26586
26587 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
26588 // summary:
26589 // Set the displayed valued in the input box, and the hidden value
26590 // that gets submitted, based on a dojo.data store item.
26591 // description:
26592 // Users shouldn't call this function; they should be calling
26593 // set('item', value)
26594 // tags:
26595 // private
26596 this.inherited(arguments);
26597 this._lastDisplayedValue = this.textbox.value;
26598 },
26599
26600 _getDisplayQueryString: function(/*String*/ text){
26601 return text.replace(/([\\\*\?])/g, "\\$1");
26602 },
26603
26604 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
26605 // summary:
26606 // Hook so set('displayedValue', label) works.
26607 // description:
26608 // Sets textbox to display label. Also performs reverse lookup
26609 // to set the hidden value. label should corresponding to item.searchAttr.
26610
26611 if(label == null){ label = ''; }
26612
26613 // This is called at initialization along with every custom setter.
26614 // Usually (or always?) the call can be ignored. If it needs to be
26615 // processed then at least make sure that the XHR request doesn't trigger an onChange()
26616 // event, even if it returns after creation has finished
26617 if(!this._created){
26618 if(!("displayedValue" in this.params)){
26619 return;
26620 }
26621 priorityChange = false;
26622 }
26623
26624 // Do a reverse lookup to map the specified displayedValue to the hidden value.
26625 // Note that if there's a custom labelFunc() this code
26626 if(this.store){
26627 this.closeDropDown();
26628 var query = lang.clone(this.query); // #6196: populate query with user-specifics
26629
26630 // Generate query
26631 var qs = this._getDisplayQueryString(label), q;
26632 if(this.store._oldAPI){
26633 // remove this branch for 2.0
26634 q = qs;
26635 }else{
26636 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
26637 // but with a toString() method to help dojo/store/JsonRest.
26638 // Search string like "Co*" converted to regex like /^Co.*$/i.
26639 q = filter.patternToRegExp(qs, this.ignoreCase);
26640 q.toString = function(){ return qs; };
26641 }
26642 this._lastQuery = query[this.searchAttr] = q;
26643
26644 // If the label is not valid, the callback will never set it,
26645 // so the last valid value will get the warning textbox. Set the
26646 // textbox value now so that the impending warning will make
26647 // sense to the user
26648 this.textbox.value = label;
26649 this._lastDisplayedValue = label;
26650 this._set("displayedValue", label); // for watch("displayedValue") notification
26651 var _this = this;
26652 var options = {
26653 ignoreCase: this.ignoreCase,
26654 deep: true
26655 };
26656 lang.mixin(options, this.fetchProperties);
26657 this._fetchHandle = this.store.query(query, options);
26658 when(this._fetchHandle, function(result){
26659 _this._fetchHandle = null;
26660 _this._callbackSetLabel(result || [], query, options, priorityChange);
26661 }, function(err){
26662 _this._fetchHandle = null;
26663 if(!_this._cancelingQuery){ // don't treat canceled query as an error
26664 console.error('dijit.form.FilteringSelect: ' + err.toString());
26665 }
26666 });
26667 }
26668 },
26669
26670 undo: function(){
26671 this.set('displayedValue', this._lastDisplayedValue);
26672 }
26673 });
26674 });
26675
26676 },
26677 'dojo/data/util/sorter':function(){
26678 define("dojo/data/util/sorter", ["../../_base/lang"], function(lang){
26679 // module:
26680 // dojo/data/util/sorter
26681 // summary:
26682 // TODOC
26683
26684 var sorter = {};
26685 lang.setObject("dojo.data.util.sorter", sorter);
26686
26687 sorter.basicComparator = function( /*anything*/ a,
26688 /*anything*/ b){
26689 // summary:
26690 // Basic comparison function that compares if an item is greater or less than another item
26691 // description:
26692 // returns 1 if a > b, -1 if a < b, 0 if equal.
26693 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
26694 // And compared to each other, null is equivalent to undefined.
26695
26696 //null is a problematic compare, so if null, we set to undefined.
26697 //Makes the check logic simple, compact, and consistent
26698 //And (null == undefined) === true, so the check later against null
26699 //works for undefined and is less bytes.
26700 var r = -1;
26701 if(a === null){
26702 a = undefined;
26703 }
26704 if(b === null){
26705 b = undefined;
26706 }
26707 if(a == b){
26708 r = 0;
26709 }else if(a > b || a == null){
26710 r = 1;
26711 }
26712 return r; //int {-1,0,1}
26713 };
26714
26715 sorter.createSortFunction = function( /* attributes[] */sortSpec, /*dojo/data/api/Read*/ store){
26716 // summary:
26717 // Helper function to generate the sorting function based off the list of sort attributes.
26718 // description:
26719 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
26720 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
26721 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
26722 // Returns the sorting function for this particular list of attributes and sorting directions.
26723 // sortSpec:
26724 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
26725 // The objects should be formatted as follows:
26726 // | {
26727 // | attribute: "attributeName-string" || attribute,
26728 // | descending: true|false; // Default is false.
26729 // | }
26730 // store:
26731 // The datastore object to look up item values from.
26732
26733 var sortFunctions=[];
26734
26735 function createSortFunction(attr, dir, comp, s){
26736 //Passing in comp and s (comparator and store), makes this
26737 //function much faster.
26738 return function(itemA, itemB){
26739 var a = s.getValue(itemA, attr);
26740 var b = s.getValue(itemB, attr);
26741 return dir * comp(a,b); //int
26742 };
26743 }
26744 var sortAttribute;
26745 var map = store.comparatorMap;
26746 var bc = sorter.basicComparator;
26747 for(var i = 0; i < sortSpec.length; i++){
26748 sortAttribute = sortSpec[i];
26749 var attr = sortAttribute.attribute;
26750 if(attr){
26751 var dir = (sortAttribute.descending) ? -1 : 1;
26752 var comp = bc;
26753 if(map){
26754 if(typeof attr !== "string" && ("toString" in attr)){
26755 attr = attr.toString();
26756 }
26757 comp = map[attr] || bc;
26758 }
26759 sortFunctions.push(createSortFunction(attr,
26760 dir, comp, store));
26761 }
26762 }
26763 return function(rowA, rowB){
26764 var i=0;
26765 while(i < sortFunctions.length){
26766 var ret = sortFunctions[i++](rowA, rowB);
26767 if(ret !== 0){
26768 return ret;//int
26769 }
26770 }
26771 return 0; //int
26772 }; // Function
26773 };
26774
26775 return sorter;
26776 });
26777
26778 },
26779 'dijit/form/_ButtonMixin':function(){
26780 define("dijit/form/_ButtonMixin", [
26781 "dojo/_base/declare", // declare
26782 "dojo/dom", // dom.setSelectable
26783 "dojo/_base/event", // event.stop
26784 "../registry" // registry.byNode
26785 ], function(declare, dom, event, registry){
26786
26787 // module:
26788 // dijit/form/_ButtonMixin
26789
26790 return declare("dijit.form._ButtonMixin", null, {
26791 // summary:
26792 // A mixin to add a thin standard API wrapper to a normal HTML button
26793 // description:
26794 // A label should always be specified (through innerHTML) or the label attribute.
26795 //
26796 // Attach points:
26797 //
26798 // - focusNode (required): this node receives focus
26799 // - valueNode (optional): this node's value gets submitted with FORM elements
26800 // - containerNode (optional): this node gets the innerHTML assignment for label
26801 // example:
26802 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
26803 // example:
26804 // | var button1 = new Button({label: "hello world", onClick: foo});
26805 // | dojo.body().appendChild(button1.domNode);
26806
26807 // label: HTML String
26808 // Content to display in button.
26809 label: "",
26810
26811 // type: [const] String
26812 // Type of button (submit, reset, button, checkbox, radio)
26813 type: "button",
26814
26815 _onClick: function(/*Event*/ e){
26816 // summary:
26817 // Internal function to handle click actions
26818 if(this.disabled){
26819 event.stop(e);
26820 return false;
26821 }
26822 var preventDefault = this.onClick(e) === false; // user click actions
26823 if(!preventDefault && this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a non-form widget needs to be signalled
26824 for(var node=this.domNode; node.parentNode; node=node.parentNode){
26825 var widget=registry.byNode(node);
26826 if(widget && typeof widget._onSubmit == "function"){
26827 widget._onSubmit(e);
26828 preventDefault = true;
26829 break;
26830 }
26831 }
26832 }
26833 if(preventDefault){
26834 e.preventDefault();
26835 }
26836 return !preventDefault;
26837 },
26838
26839 postCreate: function(){
26840 this.inherited(arguments);
26841 dom.setSelectable(this.focusNode, false);
26842 },
26843
26844 onClick: function(/*Event*/ /*===== e =====*/){
26845 // summary:
26846 // Callback for when button is clicked.
26847 // If type="submit", return true to perform submit, or false to cancel it.
26848 // type:
26849 // callback
26850 return true; // Boolean
26851 },
26852
26853 _setLabelAttr: function(/*String*/ content){
26854 // summary:
26855 // Hook for set('label', ...) to work.
26856 // description:
26857 // Set the label (text) of the button; takes an HTML string.
26858 this._set("label", content);
26859 (this.containerNode||this.focusNode).innerHTML = content;
26860 }
26861 });
26862
26863 });
26864
26865 },
26866 'dojo/colors':function(){
26867 define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil){
26868 // module:
26869 // dojo/colors
26870
26871 /*=====
26872 return {
26873 // summary:
26874 // Color utilities, extending Base dojo.Color
26875 };
26876 =====*/
26877
26878 var ColorExt = {};
26879 lang.setObject("dojo.colors", ColorExt);
26880
26881 //TODO: this module appears to break naming conventions
26882
26883 // this is a standard conversion prescribed by the CSS3 Color Module
26884 var hue2rgb = function(m1, m2, h){
26885 if(h < 0){ ++h; }
26886 if(h > 1){ --h; }
26887 var h6 = 6 * h;
26888 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
26889 if(2 * h < 1){ return m2; }
26890 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
26891 return m1;
26892 };
26893 // Override base Color.fromRgb with the impl in this module
26894 dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo/_base/Color?*/ obj){
26895 // summary:
26896 // get rgb(a) array from css-style color declarations
26897 // description:
26898 // this function can handle all 4 CSS3 Color Module formats: rgb,
26899 // rgba, hsl, hsla, including rgb(a) with percentage values.
26900 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
26901 if(m){
26902 var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
26903 if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
26904 var r = c[0];
26905 if(r.charAt(r.length - 1) == "%"){
26906 // 3 rgb percentage values
26907 a = ArrayUtil.map(c, function(x){
26908 return parseFloat(x) * 2.56;
26909 });
26910 if(l == 4){ a[3] = c[3]; }
26911 return Color.fromArray(a, obj); // dojo/_base/Color
26912 }
26913 return Color.fromArray(c, obj); // dojo/_base/Color
26914 }
26915 if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
26916 // normalize hsl values
26917 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
26918 S = parseFloat(c[1]) / 100,
26919 L = parseFloat(c[2]) / 100,
26920 // calculate rgb according to the algorithm
26921 // recommended by the CSS3 Color Module
26922 m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
26923 m1 = 2 * L - m2;
26924 a = [
26925 hue2rgb(m1, m2, H + 1 / 3) * 256,
26926 hue2rgb(m1, m2, H) * 256,
26927 hue2rgb(m1, m2, H - 1 / 3) * 256,
26928 1
26929 ];
26930 if(l == 4){ a[3] = c[3]; }
26931 return Color.fromArray(a, obj); // dojo/_base/Color
26932 }
26933 }
26934 return null; // dojo/_base/Color
26935 };
26936
26937 var confine = function(c, low, high){
26938 // summary:
26939 // sanitize a color component by making sure it is a number,
26940 // and clamping it to valid values
26941 c = Number(c);
26942 return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
26943 };
26944
26945 Color.prototype.sanitize = function(){
26946 // summary:
26947 // makes sure that the object has correct attributes
26948 var t = this;
26949 t.r = Math.round(confine(t.r, 0, 255));
26950 t.g = Math.round(confine(t.g, 0, 255));
26951 t.b = Math.round(confine(t.b, 0, 255));
26952 t.a = confine(t.a, 0, 1);
26953 return this; // dojo/_base/Color
26954 };
26955
26956 ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){
26957 // summary:
26958 // creates a greyscale color with an optional alpha
26959 return Color.fromArray([g, g, g, a]); // dojo/_base/Color
26960 };
26961
26962 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
26963 lang.mixin(Color.named, {
26964 "aliceblue": [240,248,255],
26965 "antiquewhite": [250,235,215],
26966 "aquamarine": [127,255,212],
26967 "azure": [240,255,255],
26968 "beige": [245,245,220],
26969 "bisque": [255,228,196],
26970 "blanchedalmond": [255,235,205],
26971 "blueviolet": [138,43,226],
26972 "brown": [165,42,42],
26973 "burlywood": [222,184,135],
26974 "cadetblue": [95,158,160],
26975 "chartreuse": [127,255,0],
26976 "chocolate": [210,105,30],
26977 "coral": [255,127,80],
26978 "cornflowerblue": [100,149,237],
26979 "cornsilk": [255,248,220],
26980 "crimson": [220,20,60],
26981 "cyan": [0,255,255],
26982 "darkblue": [0,0,139],
26983 "darkcyan": [0,139,139],
26984 "darkgoldenrod": [184,134,11],
26985 "darkgray": [169,169,169],
26986 "darkgreen": [0,100,0],
26987 "darkgrey": [169,169,169],
26988 "darkkhaki": [189,183,107],
26989 "darkmagenta": [139,0,139],
26990 "darkolivegreen": [85,107,47],
26991 "darkorange": [255,140,0],
26992 "darkorchid": [153,50,204],
26993 "darkred": [139,0,0],
26994 "darksalmon": [233,150,122],
26995 "darkseagreen": [143,188,143],
26996 "darkslateblue": [72,61,139],
26997 "darkslategray": [47,79,79],
26998 "darkslategrey": [47,79,79],
26999 "darkturquoise": [0,206,209],
27000 "darkviolet": [148,0,211],
27001 "deeppink": [255,20,147],
27002 "deepskyblue": [0,191,255],
27003 "dimgray": [105,105,105],
27004 "dimgrey": [105,105,105],
27005 "dodgerblue": [30,144,255],
27006 "firebrick": [178,34,34],
27007 "floralwhite": [255,250,240],
27008 "forestgreen": [34,139,34],
27009 "gainsboro": [220,220,220],
27010 "ghostwhite": [248,248,255],
27011 "gold": [255,215,0],
27012 "goldenrod": [218,165,32],
27013 "greenyellow": [173,255,47],
27014 "grey": [128,128,128],
27015 "honeydew": [240,255,240],
27016 "hotpink": [255,105,180],
27017 "indianred": [205,92,92],
27018 "indigo": [75,0,130],
27019 "ivory": [255,255,240],
27020 "khaki": [240,230,140],
27021 "lavender": [230,230,250],
27022 "lavenderblush": [255,240,245],
27023 "lawngreen": [124,252,0],
27024 "lemonchiffon": [255,250,205],
27025 "lightblue": [173,216,230],
27026 "lightcoral": [240,128,128],
27027 "lightcyan": [224,255,255],
27028 "lightgoldenrodyellow": [250,250,210],
27029 "lightgray": [211,211,211],
27030 "lightgreen": [144,238,144],
27031 "lightgrey": [211,211,211],
27032 "lightpink": [255,182,193],
27033 "lightsalmon": [255,160,122],
27034 "lightseagreen": [32,178,170],
27035 "lightskyblue": [135,206,250],
27036 "lightslategray": [119,136,153],
27037 "lightslategrey": [119,136,153],
27038 "lightsteelblue": [176,196,222],
27039 "lightyellow": [255,255,224],
27040 "limegreen": [50,205,50],
27041 "linen": [250,240,230],
27042 "magenta": [255,0,255],
27043 "mediumaquamarine": [102,205,170],
27044 "mediumblue": [0,0,205],
27045 "mediumorchid": [186,85,211],
27046 "mediumpurple": [147,112,219],
27047 "mediumseagreen": [60,179,113],
27048 "mediumslateblue": [123,104,238],
27049 "mediumspringgreen": [0,250,154],
27050 "mediumturquoise": [72,209,204],
27051 "mediumvioletred": [199,21,133],
27052 "midnightblue": [25,25,112],
27053 "mintcream": [245,255,250],
27054 "mistyrose": [255,228,225],
27055 "moccasin": [255,228,181],
27056 "navajowhite": [255,222,173],
27057 "oldlace": [253,245,230],
27058 "olivedrab": [107,142,35],
27059 "orange": [255,165,0],
27060 "orangered": [255,69,0],
27061 "orchid": [218,112,214],
27062 "palegoldenrod": [238,232,170],
27063 "palegreen": [152,251,152],
27064 "paleturquoise": [175,238,238],
27065 "palevioletred": [219,112,147],
27066 "papayawhip": [255,239,213],
27067 "peachpuff": [255,218,185],
27068 "peru": [205,133,63],
27069 "pink": [255,192,203],
27070 "plum": [221,160,221],
27071 "powderblue": [176,224,230],
27072 "rosybrown": [188,143,143],
27073 "royalblue": [65,105,225],
27074 "saddlebrown": [139,69,19],
27075 "salmon": [250,128,114],
27076 "sandybrown": [244,164,96],
27077 "seagreen": [46,139,87],
27078 "seashell": [255,245,238],
27079 "sienna": [160,82,45],
27080 "skyblue": [135,206,235],
27081 "slateblue": [106,90,205],
27082 "slategray": [112,128,144],
27083 "slategrey": [112,128,144],
27084 "snow": [255,250,250],
27085 "springgreen": [0,255,127],
27086 "steelblue": [70,130,180],
27087 "tan": [210,180,140],
27088 "thistle": [216,191,216],
27089 "tomato": [255,99,71],
27090 "turquoise": [64,224,208],
27091 "violet": [238,130,238],
27092 "wheat": [245,222,179],
27093 "whitesmoke": [245,245,245],
27094 "yellowgreen": [154,205,50]
27095 });
27096
27097 return Color; // TODO: return ColorExt, not Color
27098 });
27099
27100 },
27101 'dijit/registry':function(){
27102 define("dijit/registry", [
27103 "dojo/_base/array", // array.forEach array.map
27104 "dojo/sniff", // has("ie")
27105 "dojo/_base/unload", // unload.addOnWindowUnload
27106 "dojo/_base/window", // win.body
27107 "./main" // dijit._scopeName
27108 ], function(array, has, unload, win, dijit){
27109
27110 // module:
27111 // dijit/registry
27112
27113 var _widgetTypeCtr = {}, hash = {};
27114
27115 var registry = {
27116 // summary:
27117 // Registry of existing widget on page, plus some utility methods.
27118
27119 // length: Number
27120 // Number of registered widgets
27121 length: 0,
27122
27123 add: function(widget){
27124 // summary:
27125 // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
27126 // widget: dijit/_WidgetBase
27127 // Any dijit/_WidgetBase subclass.
27128 if(hash[widget.id]){
27129 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
27130 }
27131 hash[widget.id] = widget;
27132 this.length++;
27133 },
27134
27135 remove: function(/*String*/ id){
27136 // summary:
27137 // Remove a widget from the registry. Does not destroy the widget; simply
27138 // removes the reference.
27139 if(hash[id]){
27140 delete hash[id];
27141 this.length--;
27142 }
27143 },
27144
27145 byId: function(/*String|Widget*/ id){
27146 // summary:
27147 // Find a widget by it's id.
27148 // If passed a widget then just returns the widget.
27149 return typeof id == "string" ? hash[id] : id; // dijit/_WidgetBase
27150 },
27151
27152 byNode: function(/*DOMNode*/ node){
27153 // summary:
27154 // Returns the widget corresponding to the given DOMNode
27155 return hash[node.getAttribute("widgetId")]; // dijit/_WidgetBase
27156 },
27157
27158 toArray: function(){
27159 // summary:
27160 // Convert registry into a true Array
27161 //
27162 // example:
27163 // Work with the widget .domNodes in a real Array
27164 // | array.map(registry.toArray(), function(w){ return w.domNode; });
27165
27166 var ar = [];
27167 for(var id in hash){
27168 ar.push(hash[id]);
27169 }
27170 return ar; // dijit/_WidgetBase[]
27171 },
27172
27173 getUniqueId: function(/*String*/widgetType){
27174 // summary:
27175 // Generates a unique id for a given widgetType
27176
27177 var id;
27178 do{
27179 id = widgetType + "_" +
27180 (widgetType in _widgetTypeCtr ?
27181 ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
27182 }while(hash[id]);
27183 return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
27184 },
27185
27186 findWidgets: function(root, skipNode){
27187 // summary:
27188 // Search subtree under root returning widgets found.
27189 // Doesn't search for nested widgets (ie, widgets inside other widgets).
27190 // root: DOMNode
27191 // Node to search under.
27192 // skipNode: DOMNode
27193 // If specified, don't search beneath this node (usually containerNode).
27194
27195 var outAry = [];
27196
27197 function getChildrenHelper(root){
27198 for(var node = root.firstChild; node; node = node.nextSibling){
27199 if(node.nodeType == 1){
27200 var widgetId = node.getAttribute("widgetId");
27201 if(widgetId){
27202 var widget = hash[widgetId];
27203 if(widget){ // may be null on page w/multiple dojo's loaded
27204 outAry.push(widget);
27205 }
27206 }else if(node !== skipNode){
27207 getChildrenHelper(node);
27208 }
27209 }
27210 }
27211 }
27212
27213 getChildrenHelper(root);
27214 return outAry;
27215 },
27216
27217 _destroyAll: function(){
27218 // summary:
27219 // Code to destroy all widgets and do other cleanup on page unload
27220
27221 // Clean up focus manager lingering references to widgets and nodes
27222 dijit._curFocus = null;
27223 dijit._prevFocus = null;
27224 dijit._activeStack = [];
27225
27226 // Destroy all the widgets, top down
27227 array.forEach(registry.findWidgets(win.body()), function(widget){
27228 // Avoid double destroy of widgets like Menu that are attached to <body>
27229 // even though they are logically children of other widgets.
27230 if(!widget._destroyed){
27231 if(widget.destroyRecursive){
27232 widget.destroyRecursive();
27233 }else if(widget.destroy){
27234 widget.destroy();
27235 }
27236 }
27237 });
27238 },
27239
27240 getEnclosingWidget: function(/*DOMNode*/ node){
27241 // summary:
27242 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
27243 // the node is not contained within the DOM tree of any widget
27244 while(node){
27245 var id = node.nodeType == 1 && node.getAttribute("widgetId");
27246 if(id){
27247 return hash[id];
27248 }
27249 node = node.parentNode;
27250 }
27251 return null;
27252 },
27253
27254 // In case someone needs to access hash.
27255 // Actually, this is accessed from WidgetSet back-compatibility code
27256 _hash: hash
27257 };
27258
27259 dijit.registry = registry;
27260
27261 return registry;
27262 });
27263
27264 },
27265 'dijit/tree/_dndContainer':function(){
27266 define("dijit/tree/_dndContainer", [
27267 "dojo/aspect", // aspect.after
27268 "dojo/_base/declare", // declare
27269 "dojo/dom-class", // domClass.add domClass.remove domClass.replace
27270 "dojo/_base/event", // event.stop
27271 "dojo/_base/lang", // lang.mixin lang.hitch
27272 "dojo/on",
27273 "dojo/touch"
27274 ], function(aspect, declare,domClass, event, lang, on, touch){
27275
27276 // module:
27277 // dijit/tree/_dndContainer
27278
27279 /*=====
27280 var __Args = {
27281 // summary:
27282 // A dict of parameters for Tree source configuration.
27283 // isSource: Boolean?
27284 // Can be used as a DnD source. Defaults to true.
27285 // accept: String[]
27286 // List of accepted types (text strings) for a target; defaults to
27287 // ["text", "treeNode"]
27288 // copyOnly: Boolean?
27289 // Copy items, if true, use a state of Ctrl key otherwise,
27290 // dragThreshold: Number
27291 // The move delay in pixels before detecting a drag; 0 by default
27292 // betweenThreshold: Integer
27293 // Distance from upper/lower edge of node to allow drop to reorder nodes
27294 };
27295 =====*/
27296
27297 return declare("dijit.tree._dndContainer", null, {
27298
27299 // summary:
27300 // This is a base class for `dijit/tree/_dndSelector`, and isn't meant to be used directly.
27301 // It's modeled after `dojo/dnd/Container`.
27302 // tags:
27303 // protected
27304
27305 /*=====
27306 // current: DomNode
27307 // The currently hovered TreeNode.rowNode (which is the DOM node
27308 // associated w/a given node in the tree, excluding it's descendants)
27309 current: null,
27310 =====*/
27311
27312 constructor: function(tree, params){
27313 // summary:
27314 // A constructor of the Container
27315 // tree: Node
27316 // Node or node's id to build the container on
27317 // params: __Args
27318 // A dict of parameters, which gets mixed into the object
27319 // tags:
27320 // private
27321 this.tree = tree;
27322 this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
27323 lang.mixin(this, params);
27324
27325 // class-specific variables
27326 this.current = null; // current TreeNode's DOM node
27327
27328 // states
27329 this.containerState = "";
27330 domClass.add(this.node, "dojoDndContainer");
27331
27332 // set up events
27333 this.events = [
27334 // Mouse (or touch) enter/leave on Tree itself
27335 on(this.node, touch.enter, lang.hitch(this, "onOverEvent")),
27336 on(this.node, touch.leave, lang.hitch(this, "onOutEvent")),
27337
27338 // switching between TreeNodes
27339 aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true),
27340 aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true),
27341
27342 // cancel text selection and text dragging
27343 on(this.node, "dragstart", lang.hitch(event, "stop")),
27344 on(this.node, "selectstart", lang.hitch(event, "stop"))
27345 ];
27346 },
27347
27348 destroy: function(){
27349 // summary:
27350 // Prepares this object to be garbage-collected
27351
27352 var h;
27353 while(h = this.events.pop()){ h.remove(); }
27354
27355 // this.clearItems();
27356 this.node = this.parent = null;
27357 },
27358
27359 // mouse events
27360 onMouseOver: function(widget /*===== , evt =====*/){
27361 // summary:
27362 // Called when mouse is moved over a TreeNode
27363 // widget: TreeNode
27364 // evt: Event
27365 // tags:
27366 // protected
27367 this.current = widget;
27368 },
27369
27370 onMouseOut: function(/*===== widget, evt =====*/){
27371 // summary:
27372 // Called when mouse is moved away from a TreeNode
27373 // widget: TreeNode
27374 // evt: Event
27375 // tags:
27376 // protected
27377 this.current = null;
27378 },
27379
27380 _changeState: function(type, newState){
27381 // summary:
27382 // Changes a named state to new state value
27383 // type: String
27384 // A name of the state to change
27385 // newState: String
27386 // new state
27387 var prefix = "dojoDnd" + type;
27388 var state = type.toLowerCase() + "State";
27389 //domClass.replace(this.node, prefix + newState, prefix + this[state]);
27390 domClass.replace(this.node, prefix + newState, prefix + this[state]);
27391 this[state] = newState;
27392 },
27393
27394 _addItemClass: function(node, type){
27395 // summary:
27396 // Adds a class with prefix "dojoDndItem"
27397 // node: Node
27398 // A node
27399 // type: String
27400 // A variable suffix for a class name
27401 domClass.add(node, "dojoDndItem" + type);
27402 },
27403
27404 _removeItemClass: function(node, type){
27405 // summary:
27406 // Removes a class with prefix "dojoDndItem"
27407 // node: Node
27408 // A node
27409 // type: String
27410 // A variable suffix for a class name
27411 domClass.remove(node, "dojoDndItem" + type);
27412 },
27413
27414 onOverEvent: function(){
27415 // summary:
27416 // This function is called once, when mouse is over our container
27417 // tags:
27418 // protected
27419 this._changeState("Container", "Over");
27420 },
27421
27422 onOutEvent: function(){
27423 // summary:
27424 // This function is called once, when mouse is out of our container
27425 // tags:
27426 // protected
27427 this._changeState("Container", "");
27428 }
27429 });
27430 });
27431
27432 },
27433 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\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",
27434 'dijit/_base/wai':function(){
27435 define("dijit/_base/wai", [
27436 "dojo/dom-attr", // domAttr.attr
27437 "dojo/_base/lang", // lang.mixin
27438 "../main", // export symbols to dijit
27439 "../hccss" // not using this module directly, but loading it sets CSS flag on <html>
27440 ], function(domAttr, lang, dijit){
27441
27442 // module:
27443 // dijit/_base/wai
27444
27445 var exports = {
27446 // summary:
27447 // Deprecated methods for setting/getting wai roles and states.
27448 // New code should call setAttribute()/getAttribute() directly.
27449 //
27450 // Also loads hccss to apply dj_a11y class to root node if machine is in high-contrast mode.
27451
27452 hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
27453 // summary:
27454 // Determines if an element has a particular role.
27455 // returns:
27456 // True if elem has the specific role attribute and false if not.
27457 // For backwards compatibility if role parameter not provided,
27458 // returns true if has a role
27459 var waiRole = this.getWaiRole(elem);
27460 return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
27461 },
27462
27463 getWaiRole: function(/*Element*/ elem){
27464 // summary:
27465 // Gets the role for an element (which should be a wai role).
27466 // returns:
27467 // The role of elem or an empty string if elem
27468 // does not have a role.
27469 return lang.trim((domAttr.get(elem, "role") || "").replace("wairole:",""));
27470 },
27471
27472 setWaiRole: function(/*Element*/ elem, /*String*/ role){
27473 // summary:
27474 // Sets the role on an element.
27475 // description:
27476 // Replace existing role attribute with new role.
27477
27478 domAttr.set(elem, "role", role);
27479 },
27480
27481 removeWaiRole: function(/*Element*/ elem, /*String*/ role){
27482 // summary:
27483 // Removes the specified role from an element.
27484 // Removes role attribute if no specific role provided (for backwards compat.)
27485
27486 var roleValue = domAttr.get(elem, "role");
27487 if(!roleValue){ return; }
27488 if(role){
27489 var t = lang.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
27490 domAttr.set(elem, "role", t);
27491 }else{
27492 elem.removeAttribute("role");
27493 }
27494 },
27495
27496 hasWaiState: function(/*Element*/ elem, /*String*/ state){
27497 // summary:
27498 // Determines if an element has a given state.
27499 // description:
27500 // Checks for an attribute called "aria-"+state.
27501 // returns:
27502 // true if elem has a value for the given state and
27503 // false if it does not.
27504
27505 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
27506 },
27507
27508 getWaiState: function(/*Element*/ elem, /*String*/ state){
27509 // summary:
27510 // Gets the value of a state on an element.
27511 // description:
27512 // Checks for an attribute called "aria-"+state.
27513 // returns:
27514 // The value of the requested state on elem
27515 // or an empty string if elem has no value for state.
27516
27517 return elem.getAttribute("aria-"+state) || "";
27518 },
27519
27520 setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
27521 // summary:
27522 // Sets a state on an element.
27523 // description:
27524 // Sets an attribute called "aria-"+state.
27525
27526 elem.setAttribute("aria-"+state, value);
27527 },
27528
27529 removeWaiState: function(/*Element*/ elem, /*String*/ state){
27530 // summary:
27531 // Removes a state from an element.
27532 // description:
27533 // Sets an attribute called "aria-"+state.
27534
27535 elem.removeAttribute("aria-"+state);
27536 }
27537 };
27538
27539 lang.mixin(dijit, exports);
27540
27541 /*===== return exports; =====*/
27542 return dijit; // for back compat :-(
27543 });
27544
27545 },
27546 'dijit/form/_FormSelectWidget':function(){
27547 define("dijit/form/_FormSelectWidget", [
27548 "dojo/_base/array", // array.filter array.forEach array.map array.some
27549 "dojo/_base/Deferred",
27550 "dojo/aspect", // aspect.after
27551 "dojo/data/util/sorter", // util.sorter.createSortFunction
27552 "dojo/_base/declare", // declare
27553 "dojo/dom", // dom.setSelectable
27554 "dojo/dom-class", // domClass.toggle
27555 "dojo/_base/kernel", // _scopeName
27556 "dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
27557 "dojo/query", // query
27558 "dojo/when",
27559 "dojo/store/util/QueryResults",
27560 "./_FormValueWidget"
27561 ], function(array, Deferred, aspect, sorter, declare, dom, domClass, kernel, lang, query, when,
27562 QueryResults, _FormValueWidget){
27563
27564 // module:
27565 // dijit/form/_FormSelectWidget
27566
27567 /*=====
27568 var __SelectOption = {
27569 // value: String
27570 // The value of the option. Setting to empty (or missing) will
27571 // place a separator at that location
27572 // label: String
27573 // The label for our option. It can contain html tags.
27574 // selected: Boolean
27575 // Whether or not we are a selected option
27576 // disabled: Boolean
27577 // Whether or not this specific option is disabled
27578 };
27579 =====*/
27580
27581 var _FormSelectWidget = declare("dijit.form._FormSelectWidget", _FormValueWidget, {
27582 // summary:
27583 // Extends _FormValueWidget in order to provide "select-specific"
27584 // values - i.e., those values that are unique to `<select>` elements.
27585 // This also provides the mechanism for reading the elements from
27586 // a store, if desired.
27587
27588 // multiple: [const] Boolean
27589 // Whether or not we are multi-valued
27590 multiple: false,
27591
27592 // options: __SelectOption[]
27593 // The set of options for our select item. Roughly corresponds to
27594 // the html `<option>` tag.
27595 options: null,
27596
27597 // store: dojo/store/api/Store
27598 // A store to use for getting our list of options - rather than reading them
27599 // from the `<option>` html tags. Should support getIdentity().
27600 // For back-compat store can also be a dojo/data/api/Identity.
27601 store: null,
27602
27603 // query: object
27604 // A query to use when fetching items from our store
27605 query: null,
27606
27607 // queryOptions: object
27608 // Query options to use when fetching from the store
27609 queryOptions: null,
27610
27611 // labelAttr: String?
27612 // The entries in the drop down list come from this attribute in the dojo.store items.
27613 // If ``store`` is set, labelAttr must be set too, unless store is an old-style
27614 // dojo.data store rather than a new dojo/store.
27615 labelAttr: "",
27616
27617 // onFetch: Function
27618 // A callback to do with an onFetch - but before any items are actually
27619 // iterated over (i.e. to filter even further what you want to add)
27620 onFetch: null,
27621
27622 // sortByLabel: Boolean
27623 // Flag to sort the options returned from a store by the label of
27624 // the store.
27625 sortByLabel: true,
27626
27627
27628 // loadChildrenOnOpen: Boolean
27629 // By default loadChildren is called when the items are fetched from the
27630 // store. This property allows delaying loadChildren (and the creation
27631 // of the options/menuitems) until the user clicks the button to open the
27632 // dropdown.
27633 loadChildrenOnOpen: false,
27634
27635 // onLoadDeferred: [readonly] dojo.Deferred
27636 // This is the `dojo.Deferred` returned by setStore().
27637 // Calling onLoadDeferred.then() registers your
27638 // callback to be called only once, when the prior setStore completes.
27639 onLoadDeferred: null,
27640
27641 getOptions: function(/*anything*/ valueOrIdx){
27642 // summary:
27643 // Returns a given option (or options).
27644 // valueOrIdx:
27645 // If passed in as a string, that string is used to look up the option
27646 // in the array of options - based on the value property.
27647 // (See dijit/form/_FormSelectWidget.__SelectOption).
27648 //
27649 // If passed in a number, then the option with the given index (0-based)
27650 // within this select will be returned.
27651 //
27652 // If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
27653 // returned if and only if it exists within this select.
27654 //
27655 // If passed an array, then an array will be returned with each element
27656 // in the array being looked up.
27657 //
27658 // If not passed a value, then all options will be returned
27659 //
27660 // returns:
27661 // The option corresponding with the given value or index. null
27662 // is returned if any of the following are true:
27663 //
27664 // - A string value is passed in which doesn't exist
27665 // - An index is passed in which is outside the bounds of the array of options
27666 // - A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
27667
27668 // NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
27669 // if the value property matches - NOT if the exact option exists
27670 // NOTE: if passing in an array, null elements will be placed in the returned
27671 // array when a value is not found.
27672 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
27673
27674 if(lookupValue === undefined){
27675 return opts; // __SelectOption[]
27676 }
27677 if(lang.isArray(lookupValue)){
27678 return array.map(lookupValue, "return this.getOptions(item);", this); // __SelectOption[]
27679 }
27680 if(lang.isObject(valueOrIdx)){
27681 // We were passed an option - so see if it's in our array (directly),
27682 // and if it's not, try and find it by value.
27683 if(!array.some(this.options, function(o, idx){
27684 if(o === lookupValue ||
27685 (o.value && o.value === lookupValue.value)){
27686 lookupValue = idx;
27687 return true;
27688 }
27689 return false;
27690 })){
27691 lookupValue = -1;
27692 }
27693 }
27694 if(typeof lookupValue == "string"){
27695 for(var i=0; i<l; i++){
27696 if(opts[i].value === lookupValue){
27697 lookupValue = i;
27698 break;
27699 }
27700 }
27701 }
27702 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
27703 return this.options[lookupValue]; // __SelectOption
27704 }
27705 return null; // null
27706 },
27707
27708 addOption: function(/*__SelectOption|__SelectOption[]*/ option){
27709 // summary:
27710 // Adds an option or options to the end of the select. If value
27711 // of the option is empty or missing, a separator is created instead.
27712 // Passing in an array of options will yield slightly better performance
27713 // since the children are only loaded once.
27714 if(!lang.isArray(option)){ option = [option]; }
27715 array.forEach(option, function(i){
27716 if(i && lang.isObject(i)){
27717 this.options.push(i);
27718 }
27719 }, this);
27720 this._loadChildren();
27721 },
27722
27723 removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx){
27724 // summary:
27725 // Removes the given option or options. You can remove by string
27726 // (in which case the value is removed), number (in which case the
27727 // index in the options array is removed), or select option (in
27728 // which case, the select option with a matching value is removed).
27729 // You can also pass in an array of those values for a slightly
27730 // better performance since the children are only loaded once.
27731 if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
27732 var oldOpts = this.getOptions(valueOrIdx);
27733 array.forEach(oldOpts, function(i){
27734 // We can get null back in our array - if our option was not found. In
27735 // that case, we don't want to blow up...
27736 if(i){
27737 this.options = array.filter(this.options, function(node){
27738 return (node.value !== i.value || node.label !== i.label);
27739 });
27740 this._removeOptionItem(i);
27741 }
27742 }, this);
27743 this._loadChildren();
27744 },
27745
27746 updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption){
27747 // summary:
27748 // Updates the values of the given option. The option to update
27749 // is matched based on the value of the entered option. Passing
27750 // in an array of new options will yield better performance since
27751 // the children will only be loaded once.
27752 if(!lang.isArray(newOption)){ newOption = [newOption]; }
27753 array.forEach(newOption, function(i){
27754 var oldOpt = this.getOptions(i), k;
27755 if(oldOpt){
27756 for(k in i){ oldOpt[k] = i[k]; }
27757 }
27758 }, this);
27759 this._loadChildren();
27760 },
27761
27762 setStore: function(store,
27763 selectedValue,
27764 fetchArgs){
27765 // summary:
27766 // Sets the store you would like to use with this select widget.
27767 // The selected value is the value of the new store to set. This
27768 // function returns the original store, in case you want to reuse
27769 // it or something.
27770 // store: dojo/store/api/Store
27771 // The dojo.store you would like to use - it MUST implement getIdentity()
27772 // and MAY implement observe().
27773 // For backwards-compatibility this can also be a data.data store, in which case
27774 // it MUST implement dojo/data/api/Identity,
27775 // and MAY implement dojo/data/api/Notification.
27776 // selectedValue: anything?
27777 // The value that this widget should set itself to *after* the store
27778 // has been loaded
27779 // fetchArgs: Object?
27780 // Hash of parameters to set filter on store, etc.
27781 //
27782 // - query: new value for Select.query,
27783 // - queryOptions: new value for Select.queryOptions,
27784 // - onFetch: callback function for each item in data (Deprecated)
27785 var oStore = this.store;
27786 fetchArgs = fetchArgs || {};
27787
27788 if(oStore !== store){
27789 // Our store has changed, so cancel any listeners on old store (remove for 2.0)
27790 var h;
27791 while((h = this._notifyConnections.pop())){ h.remove(); }
27792
27793 // For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
27794 if(!store.get){
27795 lang.mixin(store, {
27796 _oldAPI: true,
27797 get: function(id){
27798 // summary:
27799 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
27800 // Like dojo.store.DataStore.get() except returns native item.
27801 var deferred = new Deferred();
27802 this.fetchItemByIdentity({
27803 identity: id,
27804 onItem: function(object){
27805 deferred.resolve(object);
27806 },
27807 onError: function(error){
27808 deferred.reject(error);
27809 }
27810 });
27811 return deferred.promise;
27812 },
27813 query: function(query, options){
27814 // summary:
27815 // Queries the store for objects. Like dojo/store/DataStore.query()
27816 // except returned Deferred contains array of native items.
27817 var deferred = new Deferred(function(){ if(fetchHandle.abort){ fetchHandle.abort(); } } );
27818 deferred.total = new Deferred();
27819 var fetchHandle = this.fetch(lang.mixin({
27820 query: query,
27821 onBegin: function(count){
27822 deferred.total.resolve(count);
27823 },
27824 onComplete: function(results){
27825 deferred.resolve(results);
27826 },
27827 onError: function(error){
27828 deferred.reject(error);
27829 }
27830 }, options));
27831 return new QueryResults(deferred);
27832 }
27833 });
27834
27835 if(store.getFeatures()["dojo.data.api.Notification"]){
27836 this._notifyConnections = [
27837 aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
27838 aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
27839 aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
27840 ];
27841 }
27842 }
27843 this._set("store", store); // Our store has changed, so update our notifications
27844 }
27845
27846 // Remove existing options (if there are any)
27847 if(this.options && this.options.length){
27848 this.removeOption(this.options);
27849 }
27850
27851 // Cancel listener for updates to old store
27852 if(this._queryRes && this._queryRes.close){
27853 this._queryRes.close();
27854 }
27855
27856 // If user has specified new query and query options along with this new store, then use them.
27857 if(fetchArgs.query){
27858 this._set("query", fetchArgs.query);
27859 this._set("queryOptions", fetchArgs.queryOptions);
27860 }
27861
27862 // Add our new options
27863 if(store){
27864 this._loadingStore = true;
27865 this.onLoadDeferred = new Deferred();
27866
27867 // Run query
27868 // Save result in this._queryRes so we can cancel the listeners we register below
27869 this._queryRes = store.query(this.query, this.queryOptions);
27870 when(this._queryRes, lang.hitch(this, function(items){
27871
27872 if(this.sortByLabel && !fetchArgs.sort && items.length){
27873 if(items[0].getValue){
27874 // Old dojo.data API to access items, remove for 2.0
27875 items.sort(sorter.createSortFunction([{
27876 attribute: store.getLabelAttributes(items[0])[0]
27877 }], store));
27878 }else{
27879 var labelAttr = this.labelAttr;
27880 items.sort(function(a, b){
27881 return a[labelAttr] > b[labelAttr] ? 1 : b[labelAttr] > a[labelAttr] ? -1 : 0;
27882 });
27883 }
27884 }
27885
27886 if(fetchArgs.onFetch){
27887 items = fetchArgs.onFetch.call(this, items, fetchArgs);
27888 }
27889
27890 // TODO: Add these guys as a batch, instead of separately
27891 array.forEach(items, function(i){
27892 this._addOptionForItem(i);
27893 }, this);
27894
27895 // Register listener for store updates
27896 if(this._queryRes.observe){
27897 this._queryRes.observe(lang.hitch(this, function(object, deletedFrom, insertedInto){
27898 if(deletedFrom == insertedInto){
27899 this._onSetItem(object);
27900 }else{
27901 if(deletedFrom != -1){
27902 this._onDeleteItem(object);
27903 }
27904 if(insertedInto != -1){
27905 this._onNewItem(object);
27906 }
27907 }
27908 }), true);
27909 }
27910
27911 // Set our value (which might be undefined), and then tweak
27912 // it to send a change event with the real value
27913 this._loadingStore = false;
27914 this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
27915 delete this._pendingValue;
27916
27917 if(!this.loadChildrenOnOpen){
27918 this._loadChildren();
27919 }else{
27920 this._pseudoLoadChildren(items);
27921 }
27922 this.onLoadDeferred.resolve(true);
27923 this.onSetStore();
27924 }), function(err){
27925 console.error('dijit.form.Select: ' + err.toString());
27926 this.onLoadDeferred.reject(err);
27927 });
27928 }
27929 return oStore; // dojo/data/api/Identity
27930 },
27931
27932 // TODO: implement set() and watch() for store and query, although not sure how to handle
27933 // setting them individually rather than together (as in setStore() above)
27934
27935 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
27936 // summary:
27937 // set the value of the widget.
27938 // If a string is passed, then we set our value from looking it up.
27939 if(!this._onChangeActive){ priorityChange = null; }
27940 if(this._loadingStore){
27941 // Our store is loading - so save our value, and we'll set it when
27942 // we're done
27943 this._pendingValue = newValue;
27944 return;
27945 }
27946 var opts = this.getOptions() || [];
27947 if(!lang.isArray(newValue)){
27948 newValue = [newValue];
27949 }
27950 array.forEach(newValue, function(i, idx){
27951 if(!lang.isObject(i)){
27952 i = i + "";
27953 }
27954 if(typeof i === "string"){
27955 newValue[idx] = array.filter(opts, function(node){
27956 return node.value === i;
27957 })[0] || {value: "", label: ""};
27958 }
27959 }, this);
27960
27961 // Make sure some sane default is set
27962 newValue = array.filter(newValue, function(i){ return i && i.value; });
27963 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
27964 newValue[0] = opts[0];
27965 }
27966 array.forEach(opts, function(i){
27967 i.selected = array.some(newValue, function(v){ return v.value === i.value; });
27968 });
27969 var val = array.map(newValue, function(i){ return i.value; }),
27970 disp = array.map(newValue, function(i){ return i.label; });
27971
27972 if(typeof val == "undefined" || typeof val[0] == "undefined"){ return; } // not fully initialized yet or a failed value lookup
27973 this._setDisplay(this.multiple ? disp : disp[0]);
27974 this.inherited(arguments, [ this.multiple ? val : val[0], priorityChange ]);
27975 this._updateSelection();
27976 },
27977
27978 _getDisplayedValueAttr: function(){
27979 // summary:
27980 // returns the displayed value of the widget
27981 var val = this.get("value");
27982 if(!lang.isArray(val)){
27983 val = [val];
27984 }
27985 var ret = array.map(this.getOptions(val), function(v){
27986 if(v && "label" in v){
27987 return v.label;
27988 }else if(v){
27989 return v.value;
27990 }
27991 return null;
27992 }, this);
27993 return this.multiple ? ret : ret[0];
27994 },
27995
27996 _loadChildren: function(){
27997 // summary:
27998 // Loads the children represented by this widget's options.
27999 // reset the menu to make it populatable on the next click
28000 if(this._loadingStore){ return; }
28001 array.forEach(this._getChildren(), function(child){
28002 child.destroyRecursive();
28003 });
28004 // Add each menu item
28005 array.forEach(this.options, this._addOptionItem, this);
28006
28007 // Update states
28008 this._updateSelection();
28009 },
28010
28011 _updateSelection: function(){
28012 // summary:
28013 // Sets the "selected" class on the item for styling purposes
28014 this._set("value", this._getValueFromOpts());
28015 var val = this.value;
28016 if(!lang.isArray(val)){
28017 val = [val];
28018 }
28019 if(val && val[0]){
28020 array.forEach(this._getChildren(), function(child){
28021 var isSelected = array.some(val, function(v){
28022 return child.option && (v === child.option.value);
28023 });
28024 domClass.toggle(child.domNode, this.baseClass.replace(/\s+|$/g, "SelectedOption "), isSelected);
28025 child.domNode.setAttribute("aria-selected", isSelected ? "true" : "false");
28026 }, this);
28027 }
28028 },
28029
28030 _getValueFromOpts: function(){
28031 // summary:
28032 // Returns the value of the widget by reading the options for
28033 // the selected flag
28034 var opts = this.getOptions() || [];
28035 if(!this.multiple && opts.length){
28036 // Mirror what a select does - choose the first one
28037 var opt = array.filter(opts, function(i){
28038 return i.selected;
28039 })[0];
28040 if(opt && opt.value){
28041 return opt.value;
28042 }else{
28043 opts[0].selected = true;
28044 return opts[0].value;
28045 }
28046 }else if(this.multiple){
28047 // Set value to be the sum of all selected
28048 return array.map(array.filter(opts, function(i){
28049 return i.selected;
28050 }), function(i){
28051 return i.value;
28052 }) || [];
28053 }
28054 return "";
28055 },
28056
28057 // Internal functions to call when we have store notifications come in
28058 _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
28059 if(!parentInfo || !parentInfo.parent){
28060 // Only add it if we are top-level
28061 this._addOptionForItem(item);
28062 }
28063 },
28064 _onDeleteItem: function(/*item*/ item){
28065 var store = this.store;
28066 this.removeOption(store.getIdentity(item));
28067 },
28068 _onSetItem: function(/*item*/ item){
28069 this.updateOption(this._getOptionObjForItem(item));
28070 },
28071
28072 _getOptionObjForItem: function(item){
28073 // summary:
28074 // Returns an option object based off the given item. The "value"
28075 // of the option item will be the identity of the item, the "label"
28076 // of the option will be the label of the item.
28077
28078 // remove getLabel() call for 2.0 (it's to support the old dojo.data API)
28079 var store = this.store,
28080 label = (this.labelAttr && this.labelAttr in item) ? item[this.labelAttr] : store.getLabel(item),
28081 value = (label ? store.getIdentity(item) : null);
28082 return {value: value, label: label, item: item}; // __SelectOption
28083 },
28084
28085 _addOptionForItem: function(/*item*/ item){
28086 // summary:
28087 // Creates (and adds) the option for the given item
28088 var store = this.store;
28089 if(store.isItemLoaded && !store.isItemLoaded(item)){
28090 // We are not loaded - so let's load it and add later.
28091 // Remove for 2.0 (it's the old dojo.data API)
28092 store.loadItem({item: item, onItem: function(i){
28093 this._addOptionForItem(i);
28094 },
28095 scope: this});
28096 return;
28097 }
28098 var newOpt = this._getOptionObjForItem(item);
28099 this.addOption(newOpt);
28100 },
28101
28102 constructor: function(params /*===== , srcNodeRef =====*/){
28103 // summary:
28104 // Create the widget.
28105 // params: Object|null
28106 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
28107 // and functions, typically callbacks like onClick.
28108 // The hash can contain any of the widget's properties, excluding read-only properties.
28109 // srcNodeRef: DOMNode|String?
28110 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
28111
28112 // Saves off our value, if we have an initial one set so we
28113 // can use it if we have a store as well (see startup())
28114 this._oValue = (params || {}).value || null;
28115 this._notifyConnections = []; // remove for 2.0
28116 },
28117
28118 buildRendering: function(){
28119 this.inherited(arguments);
28120 dom.setSelectable(this.focusNode, false);
28121 },
28122
28123 _fillContent: function(){
28124 // summary:
28125 // Loads our options and sets up our dropdown correctly. We
28126 // don't want any content, so we don't call any inherit chain
28127 // function.
28128 if(!this.options){
28129 this.options =
28130 this.srcNodeRef
28131 ? query("> *", this.srcNodeRef).map(
28132 function(node){
28133 if(node.getAttribute("type") === "separator"){
28134 return { value: "", label: "", selected: false, disabled: false };
28135 }
28136 return {
28137 value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
28138 label: String(node.innerHTML),
28139 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
28140 // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
28141 // decide before 1.6
28142 selected: node.getAttribute("selected") || false,
28143 disabled: node.getAttribute("disabled") || false
28144 };
28145 },
28146 this)
28147 : [];
28148 }
28149 if(!this.value){
28150 this._set("value", this._getValueFromOpts());
28151 }else if(this.multiple && typeof this.value == "string"){
28152 this._set("value", this.value.split(","));
28153 }
28154 },
28155
28156 postCreate: function(){
28157 // summary:
28158 // sets up our event handling that we need for functioning
28159 // as a select
28160 this.inherited(arguments);
28161
28162 // Make our event connections for updating state
28163 this.connect(this, "onChange", "_updateSelection");
28164
28165 // moved from startup
28166 // Connects in our store, if we have one defined
28167 var store = this.store;
28168 if(store && (store.getIdentity || store.getFeatures()["dojo.data.api.Identity"])){
28169 // Temporarily set our store to null so that it will get set
28170 // and connected appropriately
28171 this.store = null;
28172 this.setStore(store, this._oValue);
28173 }
28174 },
28175
28176 startup: function(){
28177 // summary:
28178 this._loadChildren();
28179 this.inherited(arguments);
28180 },
28181
28182 destroy: function(){
28183 // summary:
28184 // Clean up our connections
28185
28186 var h;
28187 while((h = this._notifyConnections.pop())){ h.remove(); }
28188
28189 // Cancel listener for store updates
28190 if(this._queryRes && this._queryRes.close){
28191 this._queryRes.close();
28192 }
28193
28194 this.inherited(arguments);
28195 },
28196
28197 _addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
28198 // summary:
28199 // User-overridable function which, for the given option, adds an
28200 // item to the select. If the option doesn't have a value, then a
28201 // separator is added in that place. Make sure to store the option
28202 // in the created option widget.
28203 },
28204
28205 _removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
28206 // summary:
28207 // User-overridable function which, for the given option, removes
28208 // its item from the select.
28209 },
28210
28211 _setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
28212 // summary:
28213 // Overridable function which will set the display for the
28214 // widget. newDisplay is either a string (in the case of
28215 // single selects) or array of strings (in the case of multi-selects)
28216 },
28217
28218 _getChildren: function(){
28219 // summary:
28220 // Overridable function to return the children that this widget contains.
28221 return [];
28222 },
28223
28224 _getSelectedOptionsAttr: function(){
28225 // summary:
28226 // hooks into this.attr to provide a mechanism for getting the
28227 // option items for the current value of the widget.
28228 return this.getOptions(this.get("value"));
28229 },
28230
28231 _pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
28232 // summary:
28233 // a function that will "fake" loading children, if needed, and
28234 // if we have set to not load children until the widget opens.
28235 // items:
28236 // An array of items that will be loaded, when needed
28237 },
28238
28239 onSetStore: function(){
28240 // summary:
28241 // a function that can be connected to in order to receive a
28242 // notification that the store has finished loading and all options
28243 // from that store are available
28244 }
28245 });
28246
28247 /*=====
28248 _FormSelectWidget.__SelectOption = __SelectOption;
28249 =====*/
28250
28251 return _FormSelectWidget;
28252
28253 });
28254
28255 },
28256 'dijit/form/Select':function(){
28257 require({cache:{
28258 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t/></div\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer\"\n\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n"}});
28259 define("dijit/form/Select", [
28260 "dojo/_base/array", // array.forEach
28261 "dojo/_base/declare", // declare
28262 "dojo/dom-attr", // domAttr.set
28263 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
28264 "dojo/dom-geometry", // domGeometry.setMarginBox
28265 "dojo/_base/event", // event.stop
28266 "dojo/i18n", // i18n.getLocalization
28267 "dojo/_base/lang", // lang.hitch
28268 "dojo/sniff", // has("ie")
28269 "./_FormSelectWidget",
28270 "../_HasDropDown",
28271 "../Menu",
28272 "../MenuItem",
28273 "../MenuSeparator",
28274 "../Tooltip",
28275 "dojo/text!./templates/Select.html",
28276 "dojo/i18n!./nls/validate"
28277 ], function(array, declare, domAttr, domClass, domGeometry, event, i18n, lang, has,
28278 _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
28279
28280 // module:
28281 // dijit/form/Select
28282
28283
28284 var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
28285 // summary:
28286 // An internally-used menu for dropdown that allows us a vertical scrollbar
28287
28288 // Override Menu.autoFocus setting so that opening a Select highlights the current value.
28289 autoFocus: true,
28290
28291 buildRendering: function(){
28292 // summary:
28293 // Stub in our own changes, so that our domNode is not a table
28294 // otherwise, we won't respond correctly to heights/overflows
28295 this.inherited(arguments);
28296 var o = (this.menuTableNode = this.domNode);
28297 var n = (this.domNode = this.ownerDocument.createElement("div"));
28298 n.style.cssText = "overflow-x: hidden; overflow-y: scroll";
28299 if(o.parentNode){
28300 o.parentNode.replaceChild(n, o);
28301 }
28302 domClass.remove(o, "dijitMenuTable");
28303 n.className = o.className + " dijitSelectMenu";
28304 o.className = "dijitReset dijitMenuTable";
28305 o.setAttribute("role", "listbox");
28306 n.setAttribute("role", "presentation");
28307 n.appendChild(o);
28308 },
28309
28310 postCreate: function(){
28311 // summary:
28312 // stop mousemove from selecting text on IE to be consistent with other browsers
28313
28314 this.inherited(arguments);
28315
28316 this.connect(this.domNode, "onselectstart", event.stop);
28317 },
28318
28319
28320 focus: function(){
28321 // summary:
28322 // Overridden so that the previously selected value will be focused instead of only the first item
28323 var found = false,
28324 val = this.parentWidget.value;
28325 if(lang.isArray(val)){
28326 val = val[val.length-1];
28327 }
28328 if(val){ // if focus selected
28329 array.forEach(this.parentWidget._getChildren(), function(child){
28330 if(child.option && (val === child.option.value)){ // find menu item widget with this value
28331 found = true;
28332 this.focusChild(child, false); // focus previous selection
28333 }
28334 }, this);
28335 }
28336 if(!found){
28337 this.inherited(arguments); // focus first item by default
28338 }
28339 },
28340
28341 resize: function(/*Object*/ mb){
28342 // summary:
28343 // Overridden so that we are able to handle resizing our
28344 // internal widget. Note that this is not a "full" resize
28345 // implementation - it only works correctly if you pass it a
28346 // marginBox.
28347 //
28348 // mb: Object
28349 // The margin box to set this dropdown to.
28350 if(mb){
28351 domGeometry.setMarginBox(this.domNode, mb);
28352 if("w" in mb){
28353 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
28354 // 100% is safer than a pixel value because there may be a scroll bar with
28355 // browser/OS specific width.
28356 this.menuTableNode.style.width = "100%";
28357 }
28358 }
28359 }
28360 });
28361
28362 var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
28363 // summary:
28364 // This is a "styleable" select box - it is basically a DropDownButton which
28365 // can take a `<select>` as its input.
28366
28367 baseClass: "dijitSelect dijitValidationTextBox",
28368
28369 templateString: template,
28370
28371 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
28372
28373 // required: Boolean
28374 // Can be true or false, default is false.
28375 required: false,
28376
28377 // state: [readonly] String
28378 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
28379 state: "",
28380
28381 // message: String
28382 // Currently displayed error/prompt message
28383 message: "",
28384
28385 // tooltipPosition: String[]
28386 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
28387 tooltipPosition: [],
28388
28389 // emptyLabel: string
28390 // What to display in an "empty" dropdown
28391 emptyLabel: "&#160;", // &nbsp;
28392
28393 // _isLoaded: Boolean
28394 // Whether or not we have been loaded
28395 _isLoaded: false,
28396
28397 // _childrenLoaded: Boolean
28398 // Whether or not our children have been loaded
28399 _childrenLoaded: false,
28400
28401 _fillContent: function(){
28402 // summary:
28403 // Set the value to be the first, or the selected index
28404 this.inherited(arguments);
28405 // set value from selected option
28406 if(this.options.length && !this.value && this.srcNodeRef){
28407 var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
28408 this.value = this.options[si >= 0 ? si : 0].value;
28409 }
28410 // Create the dropDown widget
28411 this.dropDown = new _SelectMenu({ id: this.id + "_menu", parentWidget: this });
28412 domClass.add(this.dropDown.domNode, this.baseClass.replace(/\s+|$/g, "Menu "));
28413 },
28414
28415 _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){
28416 // summary:
28417 // For the given option, return the menu item that should be
28418 // used to display it. This can be overridden as needed
28419 if(!option.value && !option.label){
28420 // We are a separator (no label set for it)
28421 return new MenuSeparator({ownerDocument: this.ownerDocument});
28422 }else{
28423 // Just a regular menu option
28424 var click = lang.hitch(this, "_setValueAttr", option);
28425 var item = new MenuItem({
28426 option: option,
28427 label: option.label || this.emptyLabel,
28428 onClick: click,
28429 ownerDocument: this.ownerDocument,
28430 dir: this.dir,
28431 disabled: option.disabled || false
28432 });
28433 item.focusNode.setAttribute("role", "option");
28434 return item;
28435 }
28436 },
28437
28438 _addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option){
28439 // summary:
28440 // For the given option, add an option to our dropdown.
28441 // If the option doesn't have a value, then a separator is added
28442 // in that place.
28443 if(this.dropDown){
28444 this.dropDown.addChild(this._getMenuItemForOption(option));
28445 }
28446 },
28447
28448 _getChildren: function(){
28449 if(!this.dropDown){
28450 return [];
28451 }
28452 return this.dropDown.getChildren();
28453 },
28454
28455 _loadChildren: function(/*Boolean*/ loadMenuItems){
28456 // summary:
28457 // Resets the menu and the length attribute of the button - and
28458 // ensures that the label is appropriately set.
28459 // loadMenuItems: Boolean
28460 // actually loads the child menu items - we only do this when we are
28461 // populating for showing the dropdown.
28462
28463 if(loadMenuItems === true){
28464 // this.inherited destroys this.dropDown's child widgets (MenuItems).
28465 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
28466 // issues later in _setSelected). (see #10296)
28467 if(this.dropDown){
28468 delete this.dropDown.focusedChild;
28469 }
28470 if(this.options.length){
28471 this.inherited(arguments);
28472 }else{
28473 // Drop down menu is blank but add one blank entry just so something appears on the screen
28474 // to let users know that they are no choices (mimicing native select behavior)
28475 array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
28476 var item = new MenuItem({
28477 ownerDocument: this.ownerDocument,
28478 label: this.emptyLabel
28479 });
28480 this.dropDown.addChild(item);
28481 }
28482 }else{
28483 this._updateSelection();
28484 }
28485
28486 this._isLoaded = false;
28487 this._childrenLoaded = true;
28488
28489 if(!this._loadingStore){
28490 // Don't call this if we are loading - since we will handle it later
28491 this._setValueAttr(this.value, false);
28492 }
28493 },
28494
28495 _refreshState: function(){
28496 if(this._started){
28497 this.validate(this.focused);
28498 }
28499 },
28500
28501 startup: function(){
28502 this.inherited(arguments);
28503 this._refreshState(); // after all _set* methods have run
28504 },
28505
28506 _setValueAttr: function(value){
28507 this.inherited(arguments);
28508 domAttr.set(this.valueNode, "value", this.get("value"));
28509 this._refreshState(); // to update this.state
28510 },
28511
28512 _setDisabledAttr: function(/*Boolean*/ value){
28513 this.inherited(arguments);
28514 this._refreshState(); // to update this.state
28515 },
28516
28517 _setRequiredAttr: function(/*Boolean*/ value){
28518 this._set("required", value);
28519 this.focusNode.setAttribute("aria-required", value);
28520 this._refreshState(); // to update this.state
28521 },
28522
28523 _setOptionsAttr: function(/*Array*/ options){
28524 this._isLoaded = false;
28525 this._set('options', options);
28526 },
28527
28528 _setDisplay: function(/*String*/ newDisplay){
28529 // summary:
28530 // sets the display for the given value (or values)
28531 var lbl = newDisplay || this.emptyLabel;
28532 this.containerNode.innerHTML = '<span role="option" class="dijitReset dijitInline ' + this.baseClass.replace(/\s+|$/g, "Label ")+'">' + lbl + '</span>';
28533 },
28534
28535 validate: function(/*Boolean*/ isFocused){
28536 // summary:
28537 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
28538 // description:
28539 // Show missing or invalid messages if appropriate, and highlight textbox field.
28540 // Used when a select is initially set to no value and the user is required to
28541 // set the value.
28542
28543 var isValid = this.disabled || this.isValid(isFocused);
28544 this._set("state", isValid ? "" : (this._hasBeenBlurred ? "Error" : "Incomplete"));
28545 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
28546 var message = isValid ? "" : this._missingMsg;
28547 if(message && this.focused && this._hasBeenBlurred){
28548 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
28549 }else{
28550 Tooltip.hide(this.domNode);
28551 }
28552 this._set("message", message);
28553 return isValid;
28554 },
28555
28556 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
28557 // summary:
28558 // Whether or not this is a valid value. The only way a Select
28559 // can be invalid is when it's required but nothing is selected.
28560 return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
28561 },
28562
28563 reset: function(){
28564 // summary:
28565 // Overridden so that the state will be cleared.
28566 this.inherited(arguments);
28567 Tooltip.hide(this.domNode);
28568 this._refreshState(); // to update this.state
28569 },
28570
28571 postMixInProperties: function(){
28572 // summary:
28573 // set the missing message
28574 this.inherited(arguments);
28575 this._missingMsg = i18n.getLocalization("dijit.form", "validate", this.lang).missingMessage;
28576 },
28577
28578 postCreate: function(){
28579 // summary:
28580 // stop mousemove from selecting text on IE to be consistent with other browsers
28581
28582 this.inherited(arguments);
28583
28584 this.connect(this.domNode, "onselectstart", event.stop);
28585 this.domNode.setAttribute("aria-expanded", "false");
28586
28587 if(has("ie") < 9){
28588 // IE INPUT tag fontFamily has to be set directly using STYLE
28589 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
28590 this.defer(function(){
28591 try{
28592 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
28593 if(s){
28594 var ff = s.fontFamily;
28595 if(ff){
28596 var inputs = this.domNode.getElementsByTagName("INPUT");
28597 if(inputs){
28598 for(var i=0; i < inputs.length; i++){
28599 inputs[i].style.fontFamily = ff;
28600 }
28601 }
28602 }
28603 }
28604 }catch(e){/*when used in a Dialog, and this is called before the dialog is
28605 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
28606 });
28607 }
28608 },
28609
28610 _setStyleAttr: function(/*String||Object*/ value){
28611 this.inherited(arguments);
28612 domClass.toggle(this.domNode, this.baseClass.replace(/\s+|$/g, "FixedWidth "), !!this.domNode.style.width);
28613 },
28614
28615 isLoaded: function(){
28616 return this._isLoaded;
28617 },
28618
28619 loadDropDown: function(/*Function*/ loadCallback){
28620 // summary:
28621 // populates the menu
28622 this._loadChildren(true);
28623 this._isLoaded = true;
28624 loadCallback();
28625 },
28626
28627 closeDropDown: function(){
28628 // overriding _HasDropDown.closeDropDown()
28629 this.inherited(arguments);
28630
28631 if(this.dropDown && this.dropDown.menuTableNode){
28632 // Erase possible width: 100% setting from _SelectMenu.resize().
28633 // Leaving it would interfere with the next openDropDown() call, which
28634 // queries the natural size of the drop down.
28635 this.dropDown.menuTableNode.style.width = "";
28636 }
28637 },
28638
28639 destroy: function(preserveDom){
28640 if(this.dropDown && !this.dropDown._destroyed){
28641 this.dropDown.destroyRecursive(preserveDom);
28642 delete this.dropDown;
28643 }
28644 this.inherited(arguments);
28645 },
28646
28647 _onFocus: function(){
28648 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
28649 this.inherited(arguments);
28650 },
28651
28652 _onBlur: function(){
28653 Tooltip.hide(this.domNode);
28654 this.inherited(arguments);
28655 this.validate(false);
28656 }
28657 });
28658
28659 Select._Menu = _SelectMenu; // for monkey patching
28660
28661 return Select;
28662 });
28663
28664 },
28665 'dojo/store/util/QueryResults':function(){
28666 define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
28667 ], function(array, lang, Deferred){
28668
28669 // module:
28670 // dojo/store/util/QueryResults
28671
28672 var QueryResults = function(results){
28673 // summary:
28674 // A function that wraps the results of a store query with additional
28675 // methods.
28676 // description:
28677 // QueryResults is a basic wrapper that allows for array-like iteration
28678 // over any kind of returned data from a query. While the simplest store
28679 // will return a plain array of data, other stores may return deferreds or
28680 // promises; this wrapper makes sure that *all* results can be treated
28681 // the same.
28682 //
28683 // Additional methods include `forEach`, `filter` and `map`.
28684 // results: Array|dojo/promise/Promise
28685 // The result set as an array, or a promise for an array.
28686 // returns:
28687 // An array-like object that can be used for iterating over.
28688 // example:
28689 // Query a store and iterate over the results.
28690 //
28691 // | store.query({ prime: true }).forEach(function(item){
28692 // | // do something
28693 // | });
28694
28695 if(!results){
28696 return results;
28697 }
28698 // if it is a promise it may be frozen
28699 if(results.then){
28700 results = lang.delegate(results);
28701 }
28702 function addIterativeMethod(method){
28703 if(!results[method]){
28704 results[method] = function(){
28705 var args = arguments;
28706 return Deferred.when(results, function(results){
28707 Array.prototype.unshift.call(args, results);
28708 return QueryResults(array[method].apply(array, args));
28709 });
28710 };
28711 }
28712 }
28713 addIterativeMethod("forEach");
28714 addIterativeMethod("filter");
28715 addIterativeMethod("map");
28716 if(!results.total){
28717 results.total = Deferred.when(results, function(results){
28718 return results.length;
28719 });
28720 }
28721 return results; // Object
28722 };
28723
28724 lang.setObject("dojo.store.util.QueryResults", QueryResults);
28725
28726 return QueryResults;
28727
28728 });
28729
28730 },
28731 'dijit/form/_ListBase':function(){
28732 define("dijit/form/_ListBase", [
28733 "dojo/_base/declare", // declare
28734 "dojo/on",
28735 "dojo/window" // winUtils.scrollIntoView
28736 ], function(declare, on, winUtils){
28737
28738 // module:
28739 // dijit/form/_ListBase
28740
28741 return declare( "dijit.form._ListBase", null, {
28742 // summary:
28743 // Focus-less menu to handle UI events consistently
28744 // Abstract methods that must be defined externally:
28745 //
28746 // - onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
28747 // - onDeselect: cancels onSelect
28748 // tags:
28749 // private
28750
28751 // selected: DOMNode
28752 // currently selected node
28753 selected: null,
28754
28755 _listConnect: function(/*String|Function*/ eventType, /*String*/ callbackFuncName){
28756 // summary:
28757 // Connects 'containerNode' to specified method of this object
28758 // and automatically registers for 'disconnect' on widget destroy.
28759 // description:
28760 // Provide widget-specific analog to 'connect'.
28761 // The callback function is called with the normal event object,
28762 // but also a second parameter is passed that indicates which list item
28763 // actually received the event.
28764 // returns:
28765 // A handle that can be passed to `disconnect` in order to disconnect
28766 // before the widget is destroyed.
28767 // tags:
28768 // private
28769
28770 var self = this;
28771 return self.own(on(self.containerNode,
28772 on.selector(
28773 function(eventTarget, selector, target){
28774 return eventTarget.parentNode == target;
28775 },
28776 eventType
28777 ),
28778 function(evt){
28779 evt.preventDefault();
28780 self[callbackFuncName](evt, this);
28781 }
28782 ));
28783 },
28784
28785 selectFirstNode: function(){
28786 // summary:
28787 // Select the first displayed item in the list.
28788 var first = this.containerNode.firstChild;
28789 while(first && first.style.display == "none"){
28790 first = first.nextSibling;
28791 }
28792 this._setSelectedAttr(first);
28793 },
28794
28795 selectLastNode: function(){
28796 // summary:
28797 // Select the last displayed item in the list
28798 var last = this.containerNode.lastChild;
28799 while(last && last.style.display == "none"){
28800 last = last.previousSibling;
28801 }
28802 this._setSelectedAttr(last);
28803 },
28804
28805 selectNextNode: function(){
28806 // summary:
28807 // Select the item just below the current selection.
28808 // If nothing selected, select first node.
28809 var selectedNode = this.selected;
28810 if(!selectedNode){
28811 this.selectFirstNode();
28812 }else{
28813 var next = selectedNode.nextSibling;
28814 while(next && next.style.display == "none"){
28815 next = next.nextSibling;
28816 }
28817 if(!next){
28818 this.selectFirstNode();
28819 }else{
28820 this._setSelectedAttr(next);
28821 }
28822 }
28823 },
28824
28825 selectPreviousNode: function(){
28826 // summary:
28827 // Select the item just above the current selection.
28828 // If nothing selected, select last node (if
28829 // you select Previous and try to keep scrolling up the list).
28830 var selectedNode = this.selected;
28831 if(!selectedNode){
28832 this.selectLastNode();
28833 }else{
28834 var prev = selectedNode.previousSibling;
28835 while(prev && prev.style.display == "none"){
28836 prev = prev.previousSibling;
28837 }
28838 if(!prev){
28839 this.selectLastNode();
28840 }else{
28841 this._setSelectedAttr(prev);
28842 }
28843 }
28844 },
28845
28846 _setSelectedAttr: function(/*DomNode*/ node){
28847 // summary:
28848 // Does the actual select.
28849 if(this.selected != node){
28850 var selectedNode = this.selected;
28851 if(selectedNode){
28852 this.onDeselect(selectedNode);
28853 this.selected = null;
28854 }
28855 if(node){
28856 this.selected = node;
28857 winUtils.scrollIntoView(node);
28858 this.onSelect(node);
28859 }
28860 }else if(node){
28861 this.onSelect(node);
28862 }
28863 }
28864 });
28865
28866 });
28867
28868 },
28869 'dijit/form/_FormWidget':function(){
28870 define("dijit/form/_FormWidget", [
28871 "dojo/_base/declare", // declare
28872 "dojo/has", // has("dijit-legacy-requires")
28873 "dojo/_base/kernel", // kernel.deprecated
28874 "dojo/ready",
28875 "../_Widget",
28876 "../_CssStateMixin",
28877 "../_TemplatedMixin",
28878 "./_FormWidgetMixin"
28879 ], function(declare, has, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){
28880
28881
28882 // module:
28883 // dijit/form/_FormWidget
28884
28885 // Back compat w/1.6, remove for 2.0
28886 if(has("dijit-legacy-requires")){
28887 ready(0, function(){
28888 var requires = ["dijit/form/_FormValueWidget"];
28889 require(requires); // use indirection so modules not rolled into a build
28890 });
28891 }
28892
28893 return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
28894 // summary:
28895 // Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
28896 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
28897 //
28898 // description:
28899 // Represents a single HTML element.
28900 // All these widgets should have these attributes just like native HTML input elements.
28901 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
28902 //
28903 // They also share some common methods.
28904
28905 setDisabled: function(/*Boolean*/ disabled){
28906 // summary:
28907 // Deprecated. Use set('disabled', ...) instead.
28908 kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
28909 this.set('disabled', disabled);
28910 },
28911
28912 setValue: function(/*String*/ value){
28913 // summary:
28914 // Deprecated. Use set('value', ...) instead.
28915 kernel.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
28916 this.set('value', value);
28917 },
28918
28919 getValue: function(){
28920 // summary:
28921 // Deprecated. Use get('value') instead.
28922 kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
28923 return this.get('value');
28924 },
28925
28926 postMixInProperties: function(){
28927 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
28928 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
28929 // Regarding escaping, see heading "Attribute values" in
28930 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
28931 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/"/g, "&quot;") + '"') : '';
28932 this.inherited(arguments);
28933 },
28934
28935 // Override automatic assigning type --> focusNode, it causes exception on IE.
28936 // Instead, type must be specified as ${type} in the template, as part of the original DOM
28937 _setTypeAttr: null
28938 });
28939
28940 });
28941
28942 },
28943 'dojo/DeferredList':function(){
28944 define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
28945 // module:
28946 // dojo/DeferredList
28947
28948
28949 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
28950 // summary:
28951 // Deprecated, use dojo/promise/all instead.
28952 // Provides event handling for a group of Deferred objects.
28953 // description:
28954 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
28955 // this new deferred will typically have its callback fired when all of the deferreds in
28956 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
28957 // fireOnOneErrback, will fire before all the deferreds as appropriate
28958 // list:
28959 // The list of deferreds to be synchronizied with this DeferredList
28960 // fireOnOneCallback:
28961 // Will cause the DeferredLists callback to be fired as soon as any
28962 // of the deferreds in its list have been fired instead of waiting until
28963 // the entire list has finished
28964 // fireonOneErrback:
28965 // Will cause the errback to fire upon any of the deferreds errback
28966 // canceller:
28967 // A deferred canceller function, see dojo.Deferred
28968 var resultList = [];
28969 Deferred.call(this);
28970 var self = this;
28971 if(list.length === 0 && !fireOnOneCallback){
28972 this.resolve([0, []]);
28973 }
28974 var finished = 0;
28975 darray.forEach(list, function(item, i){
28976 item.then(function(result){
28977 if(fireOnOneCallback){
28978 self.resolve([i, result]);
28979 }else{
28980 addResult(true, result);
28981 }
28982 },function(error){
28983 if(fireOnOneErrback){
28984 self.reject(error);
28985 }else{
28986 addResult(false, error);
28987 }
28988 if(consumeErrors){
28989 return null;
28990 }
28991 throw error;
28992 });
28993 function addResult(succeeded, result){
28994 resultList[i] = [succeeded, result];
28995 finished++;
28996 if(finished === list.length){
28997 self.resolve(resultList);
28998 }
28999
29000 }
29001 });
29002 };
29003 dojo.DeferredList.prototype = new Deferred();
29004
29005 dojo.DeferredList.prototype.gatherResults = function(deferredList){
29006 // summary:
29007 // Gathers the results of the deferreds for packaging
29008 // as the parameters to the Deferred Lists' callback
29009 // deferredList: dojo/DeferredList
29010 // The deferred list from which this function gathers results.
29011 // returns: dojo/DeferredList
29012 // The newly created deferred list which packs results as
29013 // parameters to its callback.
29014
29015 var d = new dojo.DeferredList(deferredList, false, true, false);
29016 d.addCallback(function(results){
29017 var ret = [];
29018 darray.forEach(results, function(result){
29019 ret.push(result[1]);
29020 });
29021 return ret;
29022 });
29023 return d;
29024 };
29025
29026 return dojo.DeferredList;
29027 });
29028
29029 },
29030 'dojo/dnd/common':function(){
29031 define("dojo/dnd/common", ["../_base/connect", "../_base/kernel", "../_base/lang", "../dom"],
29032 function(connect, kernel, lang, dom){
29033
29034 // module:
29035 // dojo/dnd/common
29036
29037 var exports = lang.getObject("dojo.dnd", true);
29038 /*=====
29039 // TODO: for 2.0, replace line above with this code.
29040 var exports = {
29041 // summary:
29042 // TODOC
29043 };
29044 =====*/
29045
29046 exports.getCopyKeyState = connect.isCopyKey;
29047
29048 exports._uniqueId = 0;
29049 exports.getUniqueId = function(){
29050 // summary:
29051 // returns a unique string for use with any DOM element
29052 var id;
29053 do{
29054 id = kernel._scopeName + "Unique" + (++exports._uniqueId);
29055 }while(dom.byId(id));
29056 return id;
29057 };
29058
29059 exports._empty = {};
29060
29061 exports.isFormElement = function(/*Event*/ e){
29062 // summary:
29063 // returns true if user clicked on a form element
29064 var t = e.target;
29065 if(t.nodeType == 3 /*TEXT_NODE*/){
29066 t = t.parentNode;
29067 }
29068 return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
29069 };
29070
29071 return exports;
29072 });
29073
29074 },
29075 'dijit/CheckedMenuItem':function(){
29076 require({cache:{
29077 'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">&#10003;</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">&#160;</td>\n</tr>\n"}});
29078 define("dijit/CheckedMenuItem", [
29079 "dojo/_base/declare", // declare
29080 "dojo/dom-class", // domClass.toggle
29081 "./MenuItem",
29082 "dojo/text!./templates/CheckedMenuItem.html",
29083 "./hccss"
29084 ], function(declare, domClass, MenuItem, template){
29085
29086 // module:
29087 // dijit/CheckedMenuItem
29088
29089 return declare("dijit.CheckedMenuItem", MenuItem, {
29090 // summary:
29091 // A checkbox-like menu item for toggling on and off
29092
29093 templateString: template,
29094
29095 // checked: Boolean
29096 // Our checked state
29097 checked: false,
29098 _setCheckedAttr: function(/*Boolean*/ checked){
29099 // summary:
29100 // Hook so attr('checked', bool) works.
29101 // Sets the class and state for the check box.
29102 domClass.toggle(this.domNode, "dijitCheckedMenuItemChecked", checked);
29103 this.domNode.setAttribute("aria-checked", checked ? "true" : "false");
29104 this._set("checked", checked);
29105 },
29106
29107 iconClass: "", // override dijitNoIcon
29108
29109 onChange: function(/*Boolean*/ /*===== checked =====*/){
29110 // summary:
29111 // User defined function to handle check/uncheck events
29112 // tags:
29113 // callback
29114 },
29115
29116 _onClick: function(evt){
29117 // summary:
29118 // Clicking this item just toggles its state
29119 // tags:
29120 // private
29121 if(!this.disabled){
29122 this.set("checked", !this.checked);
29123 this.onChange(this.checked);
29124 }
29125 this.onClick(evt);
29126 }
29127 });
29128 });
29129
29130 },
29131 'dijit/Viewport':function(){
29132 define("dijit/Viewport", [
29133 "dojo/Evented",
29134 "dojo/on",
29135 "dojo/ready",
29136 "dojo/sniff",
29137 "dojo/_base/window", // global
29138 "dojo/window" // getBox()
29139 ], function(Evented, on, ready, has, win, winUtils){
29140
29141 // module:
29142 // dijit/Viewport
29143
29144 /*=====
29145 return {
29146 // summary:
29147 // Utility singleton to watch for viewport resizes, avoiding duplicate notifications
29148 // which can lead to infinite loops.
29149 // description:
29150 // Usage: Viewport.on("resize", myCallback).
29151 //
29152 // myCallback() is called without arguments in case it's _WidgetBase.resize(),
29153 // which would interpret the argument as the size to make the widget.
29154 };
29155 =====*/
29156
29157 var Viewport = new Evented();
29158
29159 ready(200, function(){
29160 var oldBox = winUtils.getBox();
29161 Viewport._rlh = on(win.global, "resize", function(){
29162 var newBox = winUtils.getBox();
29163 if(oldBox.h == newBox.h && oldBox.w == newBox.w){ return; }
29164 oldBox = newBox;
29165 Viewport.emit("resize");
29166 });
29167
29168 // Also catch zoom changes on IE8, since they don't naturally generate resize events
29169 if(has("ie") == 8){
29170 var deviceXDPI = screen.deviceXDPI;
29171 setInterval(function(){
29172 if(screen.deviceXDPI != deviceXDPI){
29173 deviceXDPI = screen.deviceXDPI;
29174 Viewport.emit("resize");
29175 }
29176 }, 500);
29177 }
29178 });
29179
29180 return Viewport;
29181 });
29182
29183 },
29184 'dijit/_base/place':function(){
29185 define("dijit/_base/place", [
29186 "dojo/_base/array", // array.forEach
29187 "dojo/_base/lang", // lang.isArray, lang.mixin
29188 "dojo/window", // windowUtils.getBox
29189 "../place",
29190 "../main" // export to dijit namespace
29191 ], function(array, lang, windowUtils, place, dijit){
29192
29193 // module:
29194 // dijit/_base/place
29195
29196
29197 var exports = {
29198 // summary:
29199 // Deprecated back compatibility module, new code should use dijit/place directly instead of using this module.
29200 };
29201
29202 exports.getViewport = function(){
29203 // summary:
29204 // Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
29205 // New code should use windowUtils.getBox()
29206
29207 return windowUtils.getBox();
29208 };
29209
29210 exports.placeOnScreen = place.at;
29211
29212 exports.placeOnScreenAroundElement = function(node, aroundNode, aroundCorners, layoutNode){
29213 // summary:
29214 // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
29215 // for the "around" argument and finds a proper processor to place a node.
29216 // Deprecated, new code should use dijit/place.around() instead.
29217
29218 // Convert old style {"BL": "TL", "BR": "TR"} type argument
29219 // to style needed by dijit.place code:
29220 // [
29221 // {aroundCorner: "BL", corner: "TL" },
29222 // {aroundCorner: "BR", corner: "TR" }
29223 // ]
29224 var positions;
29225 if(lang.isArray(aroundCorners)){
29226 positions = aroundCorners;
29227 }else{
29228 positions = [];
29229 for(var key in aroundCorners){
29230 positions.push({aroundCorner: key, corner: aroundCorners[key]});
29231 }
29232 }
29233
29234 return place.around(node, aroundNode, positions, true, layoutNode);
29235 };
29236
29237 exports.placeOnScreenAroundNode = exports.placeOnScreenAroundElement;
29238 /*=====
29239 exports.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
29240 // summary:
29241 // Position node adjacent or kitty-corner to aroundNode
29242 // such that it's fully visible in viewport.
29243 // Deprecated, new code should use dijit/place.around() instead.
29244 };
29245 =====*/
29246
29247 exports.placeOnScreenAroundRectangle = exports.placeOnScreenAroundElement;
29248 /*=====
29249 exports.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, layoutNode){
29250 // summary:
29251 // Like dijit.placeOnScreenAroundNode(), except that the "around"
29252 // parameter is an arbitrary rectangle on the screen (x, y, width, height)
29253 // instead of a dom node.
29254 // Deprecated, new code should use dijit/place.around() instead.
29255 };
29256 =====*/
29257
29258 exports.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
29259 // summary:
29260 // Deprecated method, unneeded when using dijit/place directly.
29261 // Transforms the passed array of preferred positions into a format suitable for
29262 // passing as the aroundCorners argument to dijit/place.placeOnScreenAroundElement.
29263 // position: String[]
29264 // This variable controls the position of the drop down.
29265 // It's an array of strings with the following values:
29266 //
29267 // - before: places drop down to the left of the target node/widget, or to the right in
29268 // the case of RTL scripts like Hebrew and Arabic
29269 // - after: places drop down to the right of the target node/widget, or to the left in
29270 // the case of RTL scripts like Hebrew and Arabic
29271 // - above: drop down goes above target node
29272 // - below: drop down goes below target node
29273 //
29274 // The list is positions is tried, in order, until a position is found where the drop down fits
29275 // within the viewport.
29276 // leftToRight: Boolean
29277 // Whether the popup will be displaying in leftToRight mode.
29278
29279 var align = {};
29280 array.forEach(position, function(pos){
29281 var ltr = leftToRight;
29282 switch(pos){
29283 case "after":
29284 align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
29285 break;
29286 case "before":
29287 align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
29288 break;
29289 case "below-alt":
29290 ltr = !ltr;
29291 // fall through
29292 case "below":
29293 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
29294 align[ltr ? "BL" : "BR"] = ltr ? "TL" : "TR";
29295 align[ltr ? "BR" : "BL"] = ltr ? "TR" : "TL";
29296 break;
29297 case "above-alt":
29298 ltr = !ltr;
29299 // fall through
29300 case "above":
29301 default:
29302 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
29303 align[ltr ? "TL" : "TR"] = ltr ? "BL" : "BR";
29304 align[ltr ? "TR" : "TL"] = ltr ? "BR" : "BL";
29305 break;
29306 }
29307 });
29308 return align;
29309 };
29310
29311 lang.mixin(dijit, exports);
29312
29313 /*===== return exports; =====*/
29314 return dijit; // for back compat :-(
29315 });
29316
29317 },
29318 'dijit/MenuSeparator':function(){
29319 require({cache:{
29320 'url: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>"}});
29321 define("dijit/MenuSeparator", [
29322 "dojo/_base/declare", // declare
29323 "dojo/dom", // dom.setSelectable
29324 "./_WidgetBase",
29325 "./_TemplatedMixin",
29326 "./_Contained",
29327 "dojo/text!./templates/MenuSeparator.html"
29328 ], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
29329
29330 // module:
29331 // dijit/MenuSeparator
29332
29333 return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
29334 // summary:
29335 // A line between two menu items
29336
29337 templateString: template,
29338
29339 buildRendering: function(){
29340 this.inherited(arguments);
29341 dom.setSelectable(this.domNode, false);
29342 },
29343
29344 isFocusable: function(){
29345 // summary:
29346 // Override to always return false
29347 // tags:
29348 // protected
29349
29350 return false; // Boolean
29351 }
29352 });
29353 });
29354
29355 },
29356 'dijit/form/_ComboBoxMenu':function(){
29357 define("dijit/form/_ComboBoxMenu", [
29358 "dojo/_base/declare", // declare
29359 "dojo/dom-class", // domClass.add domClass.remove
29360 "dojo/dom-style", // domStyle.get
29361 "dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
29362 "../_WidgetBase",
29363 "../_TemplatedMixin",
29364 "./_ComboBoxMenuMixin",
29365 "./_ListMouseMixin"
29366 ], function(declare, domClass, domStyle, keys,
29367 _WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
29368
29369
29370 // module:
29371 // dijit/form/_ComboBoxMenu
29372
29373 return declare("dijit.form._ComboBoxMenu",[_WidgetBase, _TemplatedMixin, _ListMouseMixin, _ComboBoxMenuMixin], {
29374 // summary:
29375 // Focus-less menu for internal use in `dijit/form/ComboBox`
29376 // Abstract methods that must be defined externally:
29377 //
29378 // - onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
29379 // - onPage: next(1) or previous(-1) button pressed
29380 // tags:
29381 // private
29382
29383 templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;' role='listbox'>"
29384 +"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
29385 +"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
29386 +"</div>",
29387
29388 baseClass: "dijitComboBoxMenu",
29389
29390 postCreate: function(){
29391 this.inherited(arguments);
29392 if(!this.isLeftToRight()){
29393 domClass.add(this.previousButton, "dijitMenuItemRtl");
29394 domClass.add(this.nextButton, "dijitMenuItemRtl");
29395 }
29396 },
29397
29398 _createMenuItem: function(){
29399 // note: not using domConstruct.create() because need to specify document
29400 var item = this.ownerDocument.createElement("div");
29401 item.className = "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl");
29402 item.setAttribute("role", "option");
29403 return item;
29404 },
29405
29406 onHover: function(/*DomNode*/ node){
29407 // summary:
29408 // Add hover CSS
29409 domClass.add(node, "dijitMenuItemHover");
29410 },
29411
29412 onUnhover: function(/*DomNode*/ node){
29413 // summary:
29414 // Remove hover CSS
29415 domClass.remove(node, "dijitMenuItemHover");
29416 },
29417
29418 onSelect: function(/*DomNode*/ node){
29419 // summary:
29420 // Add selected CSS
29421 domClass.add(node, "dijitMenuItemSelected");
29422 },
29423
29424 onDeselect: function(/*DomNode*/ node){
29425 // summary:
29426 // Remove selected CSS
29427 domClass.remove(node, "dijitMenuItemSelected");
29428 },
29429
29430 _page: function(/*Boolean*/ up){
29431 // summary:
29432 // Handles page-up and page-down keypresses
29433
29434 var scrollamount = 0;
29435 var oldscroll = this.domNode.scrollTop;
29436 var height = domStyle.get(this.domNode, "height");
29437 // if no item is highlighted, highlight the first option
29438 if(!this.getHighlightedOption()){
29439 this.selectNextNode();
29440 }
29441 while(scrollamount<height){
29442 var highlighted_option = this.getHighlightedOption();
29443 if(up){
29444 // stop at option 1
29445 if(!highlighted_option.previousSibling ||
29446 highlighted_option.previousSibling.style.display == "none"){
29447 break;
29448 }
29449 this.selectPreviousNode();
29450 }else{
29451 // stop at last option
29452 if(!highlighted_option.nextSibling ||
29453 highlighted_option.nextSibling.style.display == "none"){
29454 break;
29455 }
29456 this.selectNextNode();
29457 }
29458 // going backwards
29459 var newscroll = this.domNode.scrollTop;
29460 scrollamount += (newscroll-oldscroll)*(up ? -1:1);
29461 oldscroll = newscroll;
29462 }
29463 },
29464
29465 handleKey: function(evt){
29466 // summary:
29467 // Handle keystroke event forwarded from ComboBox, returning false if it's
29468 // a keystroke I recognize and process, true otherwise.
29469 switch(evt.keyCode){
29470 case keys.DOWN_ARROW:
29471 this.selectNextNode();
29472 return false;
29473 case keys.PAGE_DOWN:
29474 this._page(false);
29475 return false;
29476 case keys.UP_ARROW:
29477 this.selectPreviousNode();
29478 return false;
29479 case keys.PAGE_UP:
29480 this._page(true);
29481 return false;
29482 default:
29483 return true;
29484 }
29485 }
29486 });
29487 });
29488
29489 },
29490 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
29491 'dijit/Dialog':function(){
29492 require({cache:{
29493 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
29494 define("dijit/Dialog", [
29495 "require",
29496 "dojo/_base/array", // array.forEach array.indexOf array.map
29497 "dojo/_base/connect", // connect._keypress
29498 "dojo/_base/declare", // declare
29499 "dojo/_base/Deferred", // Deferred
29500 "dojo/dom", // dom.isDescendant
29501 "dojo/dom-class", // domClass.add domClass.contains
29502 "dojo/dom-geometry", // domGeometry.position
29503 "dojo/dom-style", // domStyle.set
29504 "dojo/_base/event", // event.stop
29505 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
29506 "dojo/i18n", // i18n.getLocalization
29507 "dojo/keys",
29508 "dojo/_base/lang", // lang.mixin lang.hitch
29509 "dojo/on",
29510 "dojo/ready",
29511 "dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
29512 "dojo/window", // winUtils.getBox, winUtils.get
29513 "dojo/dnd/Moveable", // Moveable
29514 "dojo/dnd/TimedMoveable", // TimedMoveable
29515 "./focus",
29516 "./_base/manager", // manager.defaultDuration
29517 "./_Widget",
29518 "./_TemplatedMixin",
29519 "./_CssStateMixin",
29520 "./form/_FormMixin",
29521 "./_DialogMixin",
29522 "./DialogUnderlay",
29523 "./layout/ContentPane",
29524 "dojo/text!./templates/Dialog.html",
29525 "./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
29526 "dojo/i18n!./nls/common"
29527 ], function(require, array, connect, declare, Deferred,
29528 dom, domClass, domGeometry, domStyle, event, fx, i18n, keys, lang, on, ready, has, winUtils,
29529 Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
29530 DialogUnderlay, ContentPane, template, dijit){
29531
29532 // module:
29533 // dijit/Dialog
29534
29535 /*=====
29536 dijit._underlay = function(kwArgs){
29537 // summary:
29538 // A shared instance of a `dijit.DialogUnderlay`
29539 //
29540 // description:
29541 // A shared instance of a `dijit.DialogUnderlay` created and
29542 // used by `dijit.Dialog`, though never created until some Dialog
29543 // or subclass thereof is shown.
29544 };
29545 =====*/
29546
29547 var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
29548 templateString: template,
29549
29550 baseClass: "dijitDialog",
29551
29552 cssStateNodes: {
29553 closeButtonNode: "dijitDialogCloseIcon"
29554 },
29555
29556 // Map widget attributes to DOMNode attributes.
29557 _setTitleAttr: [
29558 { node: "titleNode", type: "innerHTML" },
29559 { node: "titleBar", type: "attribute" }
29560 ],
29561
29562 // open: [readonly] Boolean
29563 // True if Dialog is currently displayed on screen.
29564 open: false,
29565
29566 // duration: Integer
29567 // The time in milliseconds it takes the dialog to fade in and out
29568 duration: manager.defaultDuration,
29569
29570 // refocus: Boolean
29571 // A Toggle to modify the default focus behavior of a Dialog, which
29572 // is to re-focus the element which had focus before being opened.
29573 // False will disable refocusing. Default: true
29574 refocus: true,
29575
29576 // autofocus: Boolean
29577 // A Toggle to modify the default focus behavior of a Dialog, which
29578 // is to focus on the first dialog element after opening the dialog.
29579 // False will disable autofocusing. Default: true
29580 autofocus: true,
29581
29582 // _firstFocusItem: [private readonly] DomNode
29583 // The pointer to the first focusable node in the dialog.
29584 // Set by `dijit/_DialogMixin._getFocusItems()`.
29585 _firstFocusItem: null,
29586
29587 // _lastFocusItem: [private readonly] DomNode
29588 // The pointer to which node has focus prior to our dialog.
29589 // Set by `dijit/_DialogMixin._getFocusItems()`.
29590 _lastFocusItem: null,
29591
29592 // doLayout: [protected] Boolean
29593 // Don't change this parameter from the default value.
29594 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
29595 // is never a child of a layout container, nor can you specify the size of
29596 // Dialog in order to control the size of an inner widget.
29597 doLayout: false,
29598
29599 // draggable: Boolean
29600 // Toggles the moveable aspect of the Dialog. If true, Dialog
29601 // can be dragged by it's title. If false it will remain centered
29602 // in the viewport.
29603 draggable: true,
29604
29605 _setDraggableAttr: function(/*Boolean*/ val){
29606 // Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
29607 // as that prevents text select on modern browsers (#14452)
29608 this._set("draggable", val);
29609 },
29610
29611 // aria-describedby: String
29612 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
29613 // be the id of the container element of text that describes the dialog purpose (usually
29614 // the first text in the dialog).
29615 // | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
29616 // | <div id="intro">Introductory text</div>
29617 // | <div>rest of dialog contents</div>
29618 // | </div>
29619 "aria-describedby": "",
29620
29621 // maxRatio: Number
29622 // Maximum size to allow the dialog to expand to, relative to viewport size
29623 maxRatio: 0.9,
29624
29625 postMixInProperties: function(){
29626 var _nlsResources = i18n.getLocalization("dijit", "common");
29627 lang.mixin(this, _nlsResources);
29628 this.inherited(arguments);
29629 },
29630
29631 postCreate: function(){
29632 domStyle.set(this.domNode, {
29633 display: "none",
29634 position:"absolute"
29635 });
29636 this.ownerDocumentBody.appendChild(this.domNode);
29637
29638 this.inherited(arguments);
29639
29640 this.connect(this, "onExecute", "hide");
29641 this.connect(this, "onCancel", "hide");
29642 this._modalconnects = [];
29643 },
29644
29645 onLoad: function(){
29646 // summary:
29647 // Called when data has been loaded from an href.
29648 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
29649 // but should *not* be overridden.
29650 // tags:
29651 // callback
29652
29653 // when href is specified we need to reposition the dialog after the data is loaded
29654 // and find the focusable elements
29655 this._position();
29656 if(this.autofocus && DialogLevelManager.isTop(this)){
29657 this._getFocusItems(this.domNode);
29658 focus.focus(this._firstFocusItem);
29659 }
29660 this.inherited(arguments);
29661 },
29662
29663 _onBlur: function(by){
29664 this.inherited(arguments);
29665
29666 // If focus was accidentally removed from the dialog, such as if the user clicked a blank
29667 // area of the screen, or clicked the browser's address bar and then tabbed into the page,
29668 // then refocus. Won't do anything if focus was removed because the Dialog was closed, or
29669 // because a new Dialog popped up on top of the old one.
29670 var refocus = lang.hitch(this, function(){
29671 if(this.open && !this._destroyed && DialogLevelManager.isTop(this)){
29672 this._getFocusItems(this.domNode);
29673 focus.focus(this._firstFocusItem);
29674 }
29675 });
29676 if(by == "mouse"){
29677 // wait for mouse up, and then refocus dialog; otherwise doesn't work
29678 on.once(this.ownerDocument, "mouseup", refocus);
29679 }else{
29680 refocus();
29681 }
29682 },
29683
29684 _endDrag: function(){
29685 // summary:
29686 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
29687 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
29688 var nodePosition = domGeometry.position(this.domNode),
29689 viewport = winUtils.getBox(this.ownerDocument);
29690 nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
29691 nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
29692 this._relativePosition = nodePosition;
29693 this._position();
29694 },
29695
29696 _setup: function(){
29697 // summary:
29698 // Stuff we need to do before showing the Dialog for the first
29699 // time (but we defer it until right beforehand, for
29700 // performance reasons).
29701 // tags:
29702 // private
29703
29704 var node = this.domNode;
29705
29706 if(this.titleBar && this.draggable){
29707 this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
29708 : Moveable)(node, { handle: this.titleBar });
29709 this.connect(this._moveable, "onMoveStop", "_endDrag");
29710 }else{
29711 domClass.add(node,"dijitDialogFixed");
29712 }
29713
29714 this.underlayAttrs = {
29715 dialogId: this.id,
29716 "class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" "),
29717 ownerDocument: this.ownerDocument
29718 };
29719 },
29720
29721 _size: function(){
29722 // summary:
29723 // If necessary, shrink dialog contents so dialog fits in viewport
29724 // tags:
29725 // private
29726
29727 this._checkIfSingleChild();
29728
29729 // If we resized the dialog contents earlier, reset them back to original size, so
29730 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
29731 // Need to do this before the domGeometry.position(this.domNode) call below.
29732 if(this._singleChild){
29733 if(typeof this._singleChildOriginalStyle != "undefined"){
29734 this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
29735 delete this._singleChildOriginalStyle;
29736 }
29737 }else{
29738 domStyle.set(this.containerNode, {
29739 width:"auto",
29740 height:"auto"
29741 });
29742 }
29743
29744 var bb = domGeometry.position(this.domNode);
29745
29746 // Get viewport size but then reduce it by a bit; Dialog should always have some space around it
29747 // to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
29748 var viewport = winUtils.getBox(this.ownerDocument);
29749 viewport.w *= this.maxRatio;
29750 viewport.h *= this.maxRatio;
29751
29752 if(bb.w >= viewport.w || bb.h >= viewport.h){
29753 // Reduce size of dialog contents so that dialog fits in viewport
29754
29755 var containerSize = domGeometry.position(this.containerNode),
29756 w = Math.min(bb.w, viewport.w) - (bb.w - containerSize.w),
29757 h = Math.min(bb.h, viewport.h) - (bb.h - containerSize.h);
29758
29759 if(this._singleChild && this._singleChild.resize){
29760 if(typeof this._singleChildOriginalStyle == "undefined"){
29761 this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
29762 }
29763 this._singleChild.resize({w: w, h: h});
29764 }else{
29765 domStyle.set(this.containerNode, {
29766 width: w + "px",
29767 height: h + "px",
29768 overflow: "auto",
29769 position: "relative" // workaround IE bug moving scrollbar or dragging dialog
29770 });
29771 }
29772 }else{
29773 if(this._singleChild && this._singleChild.resize){
29774 this._singleChild.resize();
29775 }
29776 }
29777 },
29778
29779 _position: function(){
29780 // summary:
29781 // Position modal dialog in the viewport. If no relative offset
29782 // in the viewport has been determined (by dragging, for instance),
29783 // center the node. Otherwise, use the Dialog's stored relative offset,
29784 // and position the node to top: left: values based on the viewport.
29785 if(!domClass.contains(this.ownerDocumentBody, "dojoMove")){ // don't do anything if called during auto-scroll
29786 var node = this.domNode,
29787 viewport = winUtils.getBox(this.ownerDocument),
29788 p = this._relativePosition,
29789 bb = p ? null : domGeometry.position(node),
29790 l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
29791 t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
29792 ;
29793 domStyle.set(node,{
29794 left: l + "px",
29795 top: t + "px"
29796 });
29797 }
29798 },
29799
29800 _onKey: function(/*Event*/ evt){
29801 // summary:
29802 // Handles the keyboard events for accessibility reasons
29803 // tags:
29804 // private
29805
29806 if(evt.charOrCode){
29807 var node = evt.target;
29808 if(evt.charOrCode === keys.TAB){
29809 this._getFocusItems(this.domNode);
29810 }
29811 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
29812 // see if we are shift-tabbing from first focusable item on dialog
29813 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
29814 if(!singleFocusItem){
29815 focus.focus(this._lastFocusItem); // send focus to last item in dialog
29816 }
29817 event.stop(evt);
29818 }else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
29819 if(!singleFocusItem){
29820 focus.focus(this._firstFocusItem); // send focus to first item in dialog
29821 }
29822 event.stop(evt);
29823 }else{
29824 // see if the key is for the dialog
29825 while(node){
29826 if(node == this.domNode || domClass.contains(node, "dijitPopup")){
29827 if(evt.charOrCode == keys.ESCAPE){
29828 this.onCancel();
29829 }else{
29830 return; // just let it go
29831 }
29832 }
29833 node = node.parentNode;
29834 }
29835 // this key is for the disabled document window
29836 if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y
29837 event.stop(evt);
29838 // opera won't tab to a div
29839 }else if(!has("opera")){
29840 try{
29841 this._firstFocusItem.focus();
29842 }catch(e){ /*squelch*/ }
29843 }
29844 }
29845 }
29846 },
29847
29848 show: function(){
29849 // summary:
29850 // Display the dialog
29851 // returns: dojo/_base/Deferred
29852 // Deferred object that resolves when the display animation is complete
29853
29854 if(this.open){ return; }
29855
29856 if(!this._started){
29857 this.startup();
29858 }
29859
29860 // first time we show the dialog, there's some initialization stuff to do
29861 if(!this._alreadyInitialized){
29862 this._setup();
29863 this._alreadyInitialized=true;
29864 }
29865
29866 if(this._fadeOutDeferred){
29867 this._fadeOutDeferred.cancel();
29868 }
29869
29870 // Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
29871 var win = winUtils.get(this.ownerDocument);
29872 this._modalconnects.push(on(win, "scroll", lang.hitch(this, "resize")));
29873
29874 this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
29875
29876 domStyle.set(this.domNode, {
29877 opacity:0,
29878 display:""
29879 });
29880
29881 this._set("open", true);
29882 this._onShow(); // lazy load trigger
29883
29884 this._size();
29885 this._position();
29886
29887 // fade-in Animation object, setup below
29888 var fadeIn;
29889
29890 this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
29891 fadeIn.stop();
29892 delete this._fadeInDeferred;
29893 }));
29894
29895 fadeIn = fx.fadeIn({
29896 node: this.domNode,
29897 duration: this.duration,
29898 beforeBegin: lang.hitch(this, function(){
29899 DialogLevelManager.show(this, this.underlayAttrs);
29900 }),
29901 onEnd: lang.hitch(this, function(){
29902 if(this.autofocus && DialogLevelManager.isTop(this)){
29903 // find focusable items each time dialog is shown since if dialog contains a widget the
29904 // first focusable items can change
29905 this._getFocusItems(this.domNode);
29906 focus.focus(this._firstFocusItem);
29907 }
29908 this._fadeInDeferred.resolve(true);
29909 delete this._fadeInDeferred;
29910 })
29911 }).play();
29912
29913 return this._fadeInDeferred;
29914 },
29915
29916 hide: function(){
29917 // summary:
29918 // Hide the dialog
29919 // returns: dojo/_base/Deferred
29920 // Deferred object that resolves when the hide animation is complete
29921
29922 // If we haven't been initialized yet then we aren't showing and we can just return.
29923 // Likewise if we are already hidden, or are currently fading out.
29924 if(!this._alreadyInitialized || !this.open){
29925 return;
29926 }
29927 if(this._fadeInDeferred){
29928 this._fadeInDeferred.cancel();
29929 }
29930
29931 // fade-in Animation object, setup below
29932 var fadeOut;
29933
29934 this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
29935 fadeOut.stop();
29936 delete this._fadeOutDeferred;
29937 }));
29938 // fire onHide when the promise resolves.
29939 this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
29940
29941 fadeOut = fx.fadeOut({
29942 node: this.domNode,
29943 duration: this.duration,
29944 onEnd: lang.hitch(this, function(){
29945 this.domNode.style.display = "none";
29946 DialogLevelManager.hide(this);
29947 this._fadeOutDeferred.resolve(true);
29948 delete this._fadeOutDeferred;
29949 })
29950 }).play();
29951
29952 if(this._scrollConnected){
29953 this._scrollConnected = false;
29954 }
29955 var h;
29956 while(h = this._modalconnects.pop()){
29957 h.remove();
29958 }
29959
29960 if(this._relativePosition){
29961 delete this._relativePosition;
29962 }
29963 this._set("open", false);
29964
29965 return this._fadeOutDeferred;
29966 },
29967
29968 resize: function(){
29969 // summary:
29970 // Called when viewport scrolled or size changed. Position the Dialog and the underlay.
29971 // tags:
29972 // private
29973 if(this.domNode.style.display != "none"){
29974 if(DialogUnderlay._singleton){ // avoid race condition during show()
29975 DialogUnderlay._singleton.layout();
29976 }
29977 this._position();
29978 this._size();
29979 }
29980 },
29981
29982 destroy: function(){
29983 if(this._fadeInDeferred){
29984 this._fadeInDeferred.cancel();
29985 }
29986 if(this._fadeOutDeferred){
29987 this._fadeOutDeferred.cancel();
29988 }
29989 if(this._moveable){
29990 this._moveable.destroy();
29991 }
29992 var h;
29993 while(h = this._modalconnects.pop()){
29994 h.remove();
29995 }
29996
29997 DialogLevelManager.hide(this);
29998
29999 this.inherited(arguments);
30000 }
30001 });
30002
30003 var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {
30004 // summary:
30005 // A modal dialog Widget.
30006 // description:
30007 // Pops up a modal dialog window, blocking access to the screen
30008 // and also graying out the screen Dialog is extended from
30009 // ContentPane so it supports all the same parameters (href, etc.).
30010 // example:
30011 // | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
30012 // example:
30013 // | var foo = new Dialog({ title: "test dialog", content: "test content" };
30014 // | foo.placeAt(win.body());
30015 // | foo.startup();
30016 });
30017 Dialog._DialogBase = _DialogBase; // for monkey patching and dojox/widget/DialogSimple
30018
30019 var DialogLevelManager = Dialog._DialogLevelManager = {
30020 // summary:
30021 // Controls the various active "levels" on the page, starting with the
30022 // stuff initially visible on the page (at z-index 0), and then having an entry for
30023 // each Dialog shown.
30024
30025 _beginZIndex: 950,
30026
30027 show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs){
30028 // summary:
30029 // Call right before fade-in animation for new dialog.
30030 // Saves current focus, displays/adjusts underlay for new dialog,
30031 // and sets the z-index of the dialog itself.
30032 //
30033 // New dialog will be displayed on top of all currently displayed dialogs.
30034 //
30035 // Caller is responsible for setting focus in new dialog after the fade-in
30036 // animation completes.
30037
30038 // Save current focus
30039 ds[ds.length-1].focus = focus.curNode;
30040
30041 // Display the underlay, or if already displayed then adjust for this new dialog
30042 // TODO: one underlay per document (based on dialog.ownerDocument)
30043 var underlay = DialogUnderlay._singleton;
30044 if(!underlay || underlay._destroyed){
30045 underlay = dijit._underlay = DialogUnderlay._singleton = new DialogUnderlay(underlayAttrs);
30046 }else{
30047 underlay.set(dialog.underlayAttrs);
30048 }
30049
30050 // Set z-index a bit above previous dialog
30051 var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
30052 if(ds.length == 1){ // first dialog
30053 underlay.show();
30054 }
30055 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', zIndex - 1);
30056
30057 // Dialog
30058 domStyle.set(dialog.domNode, 'zIndex', zIndex);
30059
30060 ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
30061 },
30062
30063 hide: function(/*dijit/_WidgetBase*/ dialog){
30064 // summary:
30065 // Called when the specified dialog is hidden/destroyed, after the fade-out
30066 // animation ends, in order to reset page focus, fix the underlay, etc.
30067 // If the specified dialog isn't open then does nothing.
30068 //
30069 // Caller is responsible for either setting display:none on the dialog domNode,
30070 // or calling dijit/popup.hide(), or removing it from the page DOM.
30071
30072 if(ds[ds.length-1].dialog == dialog){
30073 // Removing the top (or only) dialog in the stack, return focus
30074 // to previous dialog
30075
30076 ds.pop();
30077
30078 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
30079
30080 // Adjust underlay, unless the underlay widget has already been destroyed
30081 // because we are being called during page unload (when all widgets are destroyed)
30082 if(!DialogUnderlay._singleton._destroyed){
30083 if(ds.length == 1){
30084 // Returning to original page. Hide the underlay.
30085 DialogUnderlay._singleton.hide();
30086 }else{
30087 // Popping back to previous dialog, adjust underlay.
30088 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
30089 DialogUnderlay._singleton.set(pd.underlayAttrs);
30090 }
30091 }
30092
30093 // Adjust focus
30094 if(dialog.refocus){
30095 // If we are returning control to a previous dialog but for some reason
30096 // that dialog didn't have a focused field, set focus to first focusable item.
30097 // This situation could happen if two dialogs appeared at nearly the same time,
30098 // since a dialog doesn't set it's focus until the fade-in is finished.
30099 var focus = pd.focus;
30100 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
30101 pd.dialog._getFocusItems(pd.dialog.domNode);
30102 focus = pd.dialog._firstFocusItem;
30103 }
30104
30105 if(focus){
30106 // Refocus the button that spawned the Dialog. This will fail in corner cases including
30107 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
30108 // before this code runs. (#15058)
30109 try{
30110 focus.focus();
30111 }catch(e){}
30112 }
30113 }
30114 }else{
30115 // Removing a dialog out of order (#9944, #10705).
30116 // Don't need to mess with underlay or z-index or anything.
30117 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
30118 if(idx != -1){
30119 ds.splice(idx, 1);
30120 }
30121 }
30122 },
30123
30124 isTop: function(/*dijit/_WidgetBase*/ dialog){
30125 // summary:
30126 // Returns true if specified Dialog is the top in the task
30127 return ds[ds.length-1].dialog == dialog;
30128 }
30129 };
30130
30131 // Stack representing the various active "levels" on the page, starting with the
30132 // stuff initially visible on the page (at z-index 0), and then having an entry for
30133 // each Dialog shown.
30134 // Each element in stack has form {
30135 // dialog: dialogWidget,
30136 // focus: returnFromGetFocus(),
30137 // underlayAttrs: attributes to set on underlay (when this widget is active)
30138 // }
30139 var ds = Dialog._dialogStack = [
30140 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
30141 ];
30142
30143 // Back compat w/1.6, remove for 2.0
30144 if(has("dijit-legacy-requires")){
30145 ready(0, function(){
30146 var requires = ["dijit/TooltipDialog"];
30147 require(requires); // use indirection so modules not rolled into a build
30148 });
30149 }
30150
30151 return Dialog;
30152 });
30153
30154 },
30155 'dijit/_base/focus':function(){
30156 define("dijit/_base/focus", [
30157 "dojo/_base/array", // array.forEach
30158 "dojo/dom", // dom.isDescendant
30159 "dojo/_base/lang", // lang.isArray
30160 "dojo/topic", // publish
30161 "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
30162 "../focus",
30163 "../main" // for exporting symbols to dijit
30164 ], function(array, dom, lang, topic, win, focus, dijit){
30165
30166 // module:
30167 // dijit/_base/focus
30168
30169 var exports = {
30170 // summary:
30171 // Deprecated module to monitor currently focused node and stack of currently focused widgets.
30172 // New code should access dijit/focus directly.
30173
30174 // _curFocus: DomNode
30175 // Currently focused item on screen
30176 _curFocus: null,
30177
30178 // _prevFocus: DomNode
30179 // Previously focused item on screen
30180 _prevFocus: null,
30181
30182 isCollapsed: function(){
30183 // summary:
30184 // Returns true if there is no text selected
30185 return dijit.getBookmark().isCollapsed;
30186 },
30187
30188 getBookmark: function(){
30189 // summary:
30190 // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
30191 var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
30192
30193 if(win.global.getSelection){
30194 //W3C Range API for selections.
30195 sel = win.global.getSelection();
30196 if(sel){
30197 if(sel.isCollapsed){
30198 tg = cf? cf.tagName : "";
30199 if(tg){
30200 //Create a fake rangelike item to restore selections.
30201 tg = tg.toLowerCase();
30202 if(tg == "textarea" ||
30203 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
30204 sel = {
30205 start: cf.selectionStart,
30206 end: cf.selectionEnd,
30207 node: cf,
30208 pRange: true
30209 };
30210 return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
30211 }
30212 }
30213 bm = {isCollapsed:true};
30214 if(sel.rangeCount){
30215 bm.mark = sel.getRangeAt(0).cloneRange();
30216 }
30217 }else{
30218 rg = sel.getRangeAt(0);
30219 bm = {isCollapsed: false, mark: rg.cloneRange()};
30220 }
30221 }
30222 }else if(sel){
30223 // If the current focus was a input of some sort and no selection, don't bother saving
30224 // a native bookmark. This is because it causes issues with dialog/page selection restore.
30225 // So, we need to create psuedo bookmarks to work with.
30226 tg = cf ? cf.tagName : "";
30227 tg = tg.toLowerCase();
30228 if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
30229 if(sel.type && sel.type.toLowerCase() == "none"){
30230 return {
30231 isCollapsed: true,
30232 mark: null
30233 }
30234 }else{
30235 rg = sel.createRange();
30236 return {
30237 isCollapsed: rg.text && rg.text.length?false:true,
30238 mark: {
30239 range: rg,
30240 pRange: true
30241 }
30242 };
30243 }
30244 }
30245 bm = {};
30246
30247 //'IE' way for selections.
30248 try{
30249 // createRange() throws exception when dojo in iframe
30250 //and nothing selected, see #9632
30251 rg = sel.createRange();
30252 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
30253 }catch(e){
30254 bm.isCollapsed = true;
30255 return bm;
30256 }
30257 if(sel.type.toUpperCase() == 'CONTROL'){
30258 if(rg.length){
30259 bm.mark=[];
30260 var i=0,len=rg.length;
30261 while(i<len){
30262 bm.mark.push(rg.item(i++));
30263 }
30264 }else{
30265 bm.isCollapsed = true;
30266 bm.mark = null;
30267 }
30268 }else{
30269 bm.mark = rg.getBookmark();
30270 }
30271 }else{
30272 console.warn("No idea how to store the current selection for this browser!");
30273 }
30274 return bm; // Object
30275 },
30276
30277 moveToBookmark: function(/*Object*/ bookmark){
30278 // summary:
30279 // Moves current selection to a bookmark
30280 // bookmark:
30281 // This should be a returned object from dijit.getBookmark()
30282
30283 var _doc = win.doc,
30284 mark = bookmark.mark;
30285 if(mark){
30286 if(win.global.getSelection){
30287 //W3C Rangi API (FF, WebKit, Opera, etc)
30288 var sel = win.global.getSelection();
30289 if(sel && sel.removeAllRanges){
30290 if(mark.pRange){
30291 var n = mark.node;
30292 n.selectionStart = mark.start;
30293 n.selectionEnd = mark.end;
30294 }else{
30295 sel.removeAllRanges();
30296 sel.addRange(mark);
30297 }
30298 }else{
30299 console.warn("No idea how to restore selection for this browser!");
30300 }
30301 }else if(_doc.selection && mark){
30302 //'IE' way.
30303 var rg;
30304 if(mark.pRange){
30305 rg = mark.range;
30306 }else if(lang.isArray(mark)){
30307 rg = _doc.body.createControlRange();
30308 //rg.addElement does not have call/apply method, so can not call it directly
30309 //rg is not available in "range.addElement(item)", so can't use that either
30310 array.forEach(mark, function(n){
30311 rg.addElement(n);
30312 });
30313 }else{
30314 rg = _doc.body.createTextRange();
30315 rg.moveToBookmark(mark);
30316 }
30317 rg.select();
30318 }
30319 }
30320 },
30321
30322 getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
30323 // summary:
30324 // Called as getFocus(), this returns an Object showing the current focus
30325 // and selected text.
30326 //
30327 // Called as getFocus(widget), where widget is a (widget representing) a button
30328 // that was just pressed, it returns where focus was before that button
30329 // was pressed. (Pressing the button may have either shifted focus to the button,
30330 // or removed focus altogether.) In this case the selected text is not returned,
30331 // since it can't be accurately determined.
30332 //
30333 // menu: dijit/_WidgetBase|{domNode: DomNode} structure
30334 // The button that was just pressed. If focus has disappeared or moved
30335 // to this button, returns the previous focus. In this case the bookmark
30336 // information is already lost, and null is returned.
30337 //
30338 // openedForWindow:
30339 // iframe in which menu was opened
30340 //
30341 // returns:
30342 // A handle to restore focus/selection, to be passed to `dijit.focus`
30343 var node = !focus.curNode || (menu && dom.isDescendant(focus.curNode, menu.domNode)) ? dijit._prevFocus : focus.curNode;
30344 return {
30345 node: node,
30346 bookmark: node && (node == focus.curNode) && win.withGlobal(openedForWindow || win.global, dijit.getBookmark),
30347 openedForWindow: openedForWindow
30348 }; // Object
30349 },
30350
30351 // _activeStack: dijit/_WidgetBase[]
30352 // List of currently active widgets (focused widget and it's ancestors)
30353 _activeStack: [],
30354
30355 registerIframe: function(/*DomNode*/ iframe){
30356 // summary:
30357 // Registers listeners on the specified iframe so that any click
30358 // or focus event on that iframe (or anything in it) is reported
30359 // as a focus/click event on the `<iframe>` itself.
30360 // description:
30361 // Currently only used by editor.
30362 // returns:
30363 // Handle to pass to unregisterIframe()
30364 return focus.registerIframe(iframe);
30365 },
30366
30367 unregisterIframe: function(/*Object*/ handle){
30368 // summary:
30369 // Unregisters listeners on the specified iframe created by registerIframe.
30370 // After calling be sure to delete or null out the handle itself.
30371 // handle:
30372 // Handle returned by registerIframe()
30373
30374 handle && handle.remove();
30375 },
30376
30377 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
30378 // summary:
30379 // Registers listeners on the specified window (either the main
30380 // window or an iframe's window) to detect when the user has clicked somewhere
30381 // or focused somewhere.
30382 // description:
30383 // Users should call registerIframe() instead of this method.
30384 // targetWindow:
30385 // If specified this is the window associated with the iframe,
30386 // i.e. iframe.contentWindow.
30387 // effectiveNode:
30388 // If specified, report any focus events inside targetWindow as
30389 // an event on effectiveNode, rather than on evt.target.
30390 // returns:
30391 // Handle to pass to unregisterWin()
30392
30393 return focus.registerWin(targetWindow, effectiveNode);
30394 },
30395
30396 unregisterWin: function(/*Handle*/ handle){
30397 // summary:
30398 // Unregisters listeners on the specified window (either the main
30399 // window or an iframe's window) according to handle returned from registerWin().
30400 // After calling be sure to delete or null out the handle itself.
30401
30402 handle && handle.remove();
30403 }
30404 };
30405
30406 // Override focus singleton's focus function so that dijit.focus()
30407 // has backwards compatible behavior of restoring selection (although
30408 // probably no one is using that).
30409 focus.focus = function(/*Object|DomNode */ handle){
30410 // summary:
30411 // Sets the focused node and the selection according to argument.
30412 // To set focus to an iframe's content, pass in the iframe itself.
30413 // handle:
30414 // object returned by get(), or a DomNode
30415
30416 if(!handle){ return; }
30417
30418 var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
30419 bookmark = handle.bookmark,
30420 openedForWindow = handle.openedForWindow,
30421 collapsed = bookmark ? bookmark.isCollapsed : false;
30422
30423 // Set the focus
30424 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
30425 // but we need to set focus to iframe.contentWindow
30426 if(node){
30427 var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
30428 if(focusNode && focusNode.focus){
30429 try{
30430 // Gecko throws sometimes if setting focus is impossible,
30431 // node not displayed or something like that
30432 focusNode.focus();
30433 }catch(e){/*quiet*/}
30434 }
30435 focus._onFocusNode(node);
30436 }
30437
30438 // set the selection
30439 // do not need to restore if current selection is not empty
30440 // (use keyboard to select a menu item) or if previous selection was collapsed
30441 // as it may cause focus shift (Esp in IE).
30442 if(bookmark && win.withGlobal(openedForWindow || win.global, dijit.isCollapsed) && !collapsed){
30443 if(openedForWindow){
30444 openedForWindow.focus();
30445 }
30446 try{
30447 win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
30448 }catch(e2){
30449 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
30450 }
30451 }
30452 };
30453
30454 // For back compatibility, monitor changes to focused node and active widget stack,
30455 // publishing events and copying changes from focus manager variables into dijit (top level) variables
30456 focus.watch("curNode", function(name, oldVal, newVal){
30457 dijit._curFocus = newVal;
30458 dijit._prevFocus = oldVal;
30459 if(newVal){
30460 topic.publish("focusNode", newVal); // publish
30461 }
30462 });
30463 focus.watch("activeStack", function(name, oldVal, newVal){
30464 dijit._activeStack = newVal;
30465 });
30466
30467 focus.on("widget-blur", function(widget, by){
30468 topic.publish("widgetBlur", widget, by); // publish
30469 });
30470 focus.on("widget-focus", function(widget, by){
30471 topic.publish("widgetFocus", widget, by); // publish
30472 });
30473
30474 lang.mixin(dijit, exports);
30475
30476 /*===== return exports; =====*/
30477 return dijit; // for back compat :-(
30478 });
30479
30480 },
30481 'dijit/tree/dndSource':function(){
30482 define("dijit/tree/dndSource", [
30483 "dojo/_base/array", // array.forEach array.indexOf array.map
30484 "dojo/_base/connect", // isCopyKey
30485 "dojo/_base/declare", // declare
30486 "dojo/dom-class", // domClass.add
30487 "dojo/dom-geometry", // domGeometry.position
30488 "dojo/_base/lang", // lang.mixin lang.hitch
30489 "dojo/on", // subscribe
30490 "dojo/touch",
30491 "dojo/topic",
30492 "dojo/dnd/Manager", // DNDManager.manager
30493 "./_dndSelector"
30494 ], function(array, connect, declare, domClass, domGeometry, lang, on, touch, topic, DNDManager, _dndSelector){
30495
30496 // module:
30497 // dijit/tree/dndSource
30498 // summary:
30499 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30500
30501 /*=====
30502 var __Item = {
30503 // summary:
30504 // New item to be added to the Tree, like:
30505 // id: Anything
30506 id: "",
30507 // name: String
30508 name: ""
30509 };
30510 =====*/
30511
30512 var dndSource = declare("dijit.tree.dndSource", _dndSelector, {
30513 // summary:
30514 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30515
30516 // isSource: Boolean
30517 // Can be used as a DnD source.
30518 isSource: true,
30519
30520 // accept: String[]
30521 // List of accepted types (text strings) for the Tree; defaults to
30522 // ["text"]
30523 accept: ["text", "treeNode"],
30524
30525 // copyOnly: [private] Boolean
30526 // Copy items, if true, use a state of Ctrl key otherwise
30527 copyOnly: false,
30528
30529 // dragThreshold: Number
30530 // The move delay in pixels before detecting a drag; 5 by default
30531 dragThreshold: 5,
30532
30533 // betweenThreshold: Integer
30534 // Distance from upper/lower edge of node to allow drop to reorder nodes
30535 betweenThreshold: 0,
30536
30537 // Flag used by Avatar.js to signal to generate text node when dragging
30538 generateText: true,
30539
30540 constructor: function(/*dijit/Tree*/ tree, /*dijit/tree/dndSource*/ params){
30541 // summary:
30542 // a constructor of the Tree DnD Source
30543 // tags:
30544 // private
30545 if(!params){ params = {}; }
30546 lang.mixin(this, params);
30547 var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
30548 this.accept = null;
30549 if(type.length){
30550 this.accept = {};
30551 for(var i = 0; i < type.length; ++i){
30552 this.accept[type[i]] = 1;
30553 }
30554 }
30555
30556 // class-specific variables
30557 this.isDragging = false;
30558 this.mouseDown = false;
30559 this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode
30560 this.targetBox = null; // coordinates of this.targetAnchor
30561 this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
30562 this._lastX = 0;
30563 this._lastY = 0;
30564
30565 // states
30566 this.sourceState = "";
30567 if(this.isSource){
30568 domClass.add(this.node, "dojoDndSource");
30569 }
30570 this.targetState = "";
30571 if(this.accept){
30572 domClass.add(this.node, "dojoDndTarget");
30573 }
30574
30575 // set up events
30576 this.topics = [
30577 topic.subscribe("/dnd/source/over", lang.hitch(this, "onDndSourceOver")),
30578 topic.subscribe("/dnd/start", lang.hitch(this, "onDndStart")),
30579 topic.subscribe("/dnd/drop", lang.hitch(this, "onDndDrop")),
30580 topic.subscribe("/dnd/cancel", lang.hitch(this, "onDndCancel"))
30581 ];
30582 },
30583
30584 // methods
30585 checkAcceptance: function(/*===== source, nodes =====*/){
30586 // summary:
30587 // Checks if the target can accept nodes from this source
30588 // source: dijit/tree/dndSource
30589 // The source which provides items
30590 // nodes: DOMNode[]
30591 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
30592 // source is a dijit/Tree.
30593 // tags:
30594 // extension
30595 return true; // Boolean
30596 },
30597
30598 copyState: function(keyPressed){
30599 // summary:
30600 // Returns true, if we need to copy items, false to move.
30601 // It is separated to be overwritten dynamically, if needed.
30602 // keyPressed: Boolean
30603 // The "copy" control key was pressed
30604 // tags:
30605 // protected
30606 return this.copyOnly || keyPressed; // Boolean
30607 },
30608 destroy: function(){
30609 // summary:
30610 // Prepares the object to be garbage-collected.
30611 this.inherited(arguments);
30612 var h;
30613 while(h = this.topics.pop()){ h.remove(); }
30614 this.targetAnchor = null;
30615 },
30616
30617 _onDragMouse: function(e, firstTime){
30618 // summary:
30619 // Helper method for processing onmousemove/onmouseover events while drag is in progress.
30620 // Keeps track of current drop target.
30621 // e: Event
30622 // The mousemove event.
30623 // firstTime: Boolean?
30624 // If this flag is set, this is the first mouse move event of the drag, so call m.canDrop() etc.
30625 // even if newTarget == null because the user quickly dragged a node in the Tree to a position
30626 // over Tree.containerNode but not over any TreeNode (#7971)
30627
30628 var m = DNDManager.manager(),
30629 oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over
30630 newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over
30631 oldDropPosition = this.dropPosition; // the previous drop position (over/before/after)
30632
30633 // calculate if user is indicating to drop the dragged node before, after, or over
30634 // (i.e., to become a child of) the target node
30635 var newDropPosition = "Over";
30636 if(newTarget && this.betweenThreshold > 0){
30637 // If mouse is over a new TreeNode, then get new TreeNode's position and size
30638 if(!this.targetBox || oldTarget != newTarget){
30639 this.targetBox = domGeometry.position(newTarget.rowNode, true);
30640 }
30641 if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
30642 newDropPosition = "Before";
30643 }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
30644 newDropPosition = "After";
30645 }
30646 }
30647
30648 if(firstTime || newTarget != oldTarget || newDropPosition != oldDropPosition){
30649 if(oldTarget){
30650 this._removeItemClass(oldTarget.rowNode, oldDropPosition);
30651 }
30652 if(newTarget){
30653 this._addItemClass(newTarget.rowNode, newDropPosition);
30654 }
30655
30656 // Check if it's ok to drop the dragged node on/before/after the target node.
30657 if(!newTarget){
30658 m.canDrop(false);
30659 }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
30660 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
30661 m.canDrop(false);
30662 }else{
30663 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
30664 var sameId = false;
30665 if(m.source == this){
30666 for(var dragId in this.selection){
30667 var dragNode = this.selection[dragId];
30668 if(dragNode.item === newTarget.item){
30669 sameId = true;
30670 break;
30671 }
30672 }
30673 }
30674 if(sameId){
30675 m.canDrop(false);
30676 }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
30677 && !this._isParentChildDrop(m.source, newTarget.rowNode)){
30678 m.canDrop(true);
30679 }else{
30680 m.canDrop(false);
30681 }
30682 }
30683
30684 this.targetAnchor = newTarget;
30685 this.dropPosition = newDropPosition;
30686 }
30687 },
30688
30689 onMouseMove: function(e){
30690 // summary:
30691 // Called for any onmousemove/ontouchmove events over the Tree
30692 // e: Event
30693 // onmousemouse/ontouchmove event
30694 // tags:
30695 // private
30696 if(this.isDragging && this.targetState == "Disabled"){ return; }
30697 this.inherited(arguments);
30698 var m = DNDManager.manager();
30699 if(this.isDragging){
30700 this._onDragMouse(e);
30701 }else{
30702 if(this.mouseDown && this.isSource &&
30703 (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
30704 var nodes = this.getSelectedTreeNodes();
30705 if(nodes.length){
30706 if(nodes.length > 1){
30707 //filter out all selected items which has one of their ancestor selected as well
30708 var seen = this.selection, i = 0, r = [], n, p;
30709 nextitem: while((n = nodes[i++])){
30710 for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
30711 if(seen[p.id]){ //parent is already selected, skip this node
30712 continue nextitem;
30713 }
30714 }
30715 //this node does not have any ancestors selected, add it
30716 r.push(n);
30717 }
30718 nodes = r;
30719 }
30720 nodes = array.map(nodes, function(n){return n.domNode});
30721 m.startDrag(this, nodes, this.copyState(connect.isCopyKey(e)));
30722 this._onDragMouse(e, true); // because this may be the only mousemove event we get before the drop
30723 }
30724 }
30725 }
30726 },
30727
30728 onMouseDown: function(e){
30729 // summary:
30730 // Event processor for onmousedown/ontouchstart
30731 // e: Event
30732 // onmousedown/ontouchend event
30733 // tags:
30734 // private
30735 this.mouseDown = true;
30736 this.mouseButton = e.button;
30737 this._lastX = e.pageX;
30738 this._lastY = e.pageY;
30739 this.inherited(arguments);
30740 },
30741
30742 onMouseUp: function(e){
30743 // summary:
30744 // Event processor for onmouseup/ontouchend
30745 // e: Event
30746 // onmouseup/ontouchend event
30747 // tags:
30748 // private
30749 if(this.mouseDown){
30750 this.mouseDown = false;
30751 this.inherited(arguments);
30752 }
30753 },
30754
30755 onMouseOut: function(){
30756 // summary:
30757 // Event processor for when mouse is moved away from a TreeNode
30758 // tags:
30759 // private
30760 this.inherited(arguments);
30761 this._unmarkTargetAnchor();
30762 },
30763
30764 checkItemAcceptance: function(/*===== target, source, position =====*/){
30765 // summary:
30766 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
30767 // description:
30768 // In the base case, this is called to check if target can become a child of source.
30769 // When betweenThreshold is set, position="before" or "after" means that we
30770 // are asking if the source node can be dropped before/after the target node.
30771 // target: DOMNode
30772 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
30773 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
30774 // source: dijit/tree/dndSource
30775 // The (set of) nodes we are dropping
30776 // position: String
30777 // "over", "before", or "after"
30778 // tags:
30779 // extension
30780 return true;
30781 },
30782
30783 // topic event processors
30784 onDndSourceOver: function(source){
30785 // summary:
30786 // Topic event processor for /dnd/source/over, called when detected a current source.
30787 // source: Object
30788 // The dijit/tree/dndSource / dojo/dnd/Source which has the mouse over it
30789 // tags:
30790 // private
30791 if(this != source){
30792 this.mouseDown = false;
30793 this._unmarkTargetAnchor();
30794 }else if(this.isDragging){
30795 var m = DNDManager.manager();
30796 m.canDrop(false);
30797 }
30798 },
30799 onDndStart: function(source, nodes, copy){
30800 // summary:
30801 // Topic event processor for /dnd/start, called to initiate the DnD operation
30802 // source: Object
30803 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30804 // nodes: DomNode[]
30805 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30806 // copy: Boolean
30807 // Copy items, if true, move items otherwise
30808 // tags:
30809 // private
30810
30811 if(this.isSource){
30812 this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
30813 }
30814 var accepted = this.checkAcceptance(source, nodes);
30815
30816 this._changeState("Target", accepted ? "" : "Disabled");
30817
30818 if(this == source){
30819 DNDManager.manager().overSource(this);
30820 }
30821
30822 this.isDragging = true;
30823 },
30824
30825 itemCreator: function(nodes /*===== , target, source =====*/){
30826 // summary:
30827 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
30828 // dropped onto the tree. Developer must override this method to enable
30829 // dropping from external sources onto this Tree, unless the Tree.model's items
30830 // happen to look like {id: 123, name: "Apple" } with no other attributes.
30831 // description:
30832 // For each node in nodes[], which came from source, create a hash of name/value
30833 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
30834 // nodes: DomNode[]
30835 // target: DomNode
30836 // source: dojo/dnd/Source
30837 // returns: __Item[]
30838 // Array of name/value hashes for each new item to be added to the Tree
30839 // tags:
30840 // extension
30841
30842 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
30843 // make signature itemCreator(sourceItem, node, target) (or similar).
30844
30845 return array.map(nodes, function(node){
30846 return {
30847 "id": node.id,
30848 "name": node.textContent || node.innerText || ""
30849 };
30850 }); // Object[]
30851 },
30852
30853 onDndDrop: function(source, nodes, copy){
30854 // summary:
30855 // Topic event processor for /dnd/drop, called to finish the DnD operation.
30856 // description:
30857 // Updates data store items according to where node was dragged from and dropped
30858 // to. The tree will then respond to those data store updates and redraw itself.
30859 // source: Object
30860 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30861 // nodes: DomNode[]
30862 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30863 // copy: Boolean
30864 // Copy items, if true, move items otherwise
30865 // tags:
30866 // protected
30867 if(this.containerState == "Over"){
30868 var tree = this.tree,
30869 model = tree.model,
30870 target = this.targetAnchor;
30871
30872 this.isDragging = false;
30873
30874 // Compute the new parent item
30875 var newParentItem;
30876 var insertIndex;
30877 var before; // drop source before (aka previous sibling) of target
30878 newParentItem = (target && target.item) || tree.item;
30879 if(this.dropPosition == "Before" || this.dropPosition == "After"){
30880 // TODO: if there is no parent item then disallow the drop.
30881 // Actually this should be checked during onMouseMove too, to make the drag icon red.
30882 newParentItem = (target.getParent() && target.getParent().item) || tree.item;
30883 // Compute the insert index for reordering
30884 insertIndex = target.getIndexInParent();
30885 if(this.dropPosition == "After"){
30886 insertIndex = target.getIndexInParent() + 1;
30887 before = target.getNextSibling() && target.getNextSibling().item;
30888 }else{
30889 before = target.item;
30890 }
30891 }else{
30892 newParentItem = (target && target.item) || tree.item;
30893 }
30894
30895 // If necessary, use this variable to hold array of hashes to pass to model.newItem()
30896 // (one entry in the array for each dragged node).
30897 var newItemsParams;
30898
30899 array.forEach(nodes, function(node, idx){
30900 // dojo/dnd/Item representing the thing being dropped.
30901 // Don't confuse the use of item here (meaning a DnD item) with the
30902 // uses below where item means dojo.data item.
30903 var sourceItem = source.getItem(node.id);
30904
30905 // Information that's available if the source is another Tree
30906 // (possibly but not necessarily this tree, possibly but not
30907 // necessarily the same model as this Tree)
30908 if(array.indexOf(sourceItem.type, "treeNode") != -1){
30909 var childTreeNode = sourceItem.data,
30910 childItem = childTreeNode.item,
30911 oldParentItem = childTreeNode.getParent().item;
30912 }
30913
30914 if(source == this){
30915 // This is a node from my own tree, and we are moving it, not copying.
30916 // Remove item from old parent's children attribute.
30917 // TODO: dijit/tree/dndSelector should implement deleteSelectedNodes()
30918 // and this code should go there.
30919
30920 if(typeof insertIndex == "number"){
30921 if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
30922 insertIndex -= 1;
30923 }
30924 }
30925 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
30926 }else if(model.isItem(childItem)){
30927 // Item from same model
30928 // (maybe we should only do this branch if the source is a tree?)
30929 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
30930 }else{
30931 // Get the hash to pass to model.newItem(). A single call to
30932 // itemCreator() returns an array of hashes, one for each drag source node.
30933 if(!newItemsParams){
30934 newItemsParams = this.itemCreator(nodes, target.rowNode, source);
30935 }
30936
30937 // Create new item in the tree, based on the drag source.
30938 model.newItem(newItemsParams[idx], newParentItem, insertIndex, before);
30939 }
30940 }, this);
30941
30942 // Expand the target node (if it's currently collapsed) so the user can see
30943 // where their node was dropped. In particular since that node is still selected.
30944 this.tree._expandNode(target);
30945 }
30946 this.onDndCancel();
30947 },
30948
30949 onDndCancel: function(){
30950 // summary:
30951 // Topic event processor for /dnd/cancel, called to cancel the DnD operation
30952 // tags:
30953 // private
30954 this._unmarkTargetAnchor();
30955 this.isDragging = false;
30956 this.mouseDown = false;
30957 delete this.mouseButton;
30958 this._changeState("Source", "");
30959 this._changeState("Target", "");
30960 },
30961
30962 // When focus moves in/out of the entire Tree
30963 onOverEvent: function(){
30964 // summary:
30965 // This method is called when mouse is moved over our container (like onmouseenter)
30966 // tags:
30967 // private
30968 this.inherited(arguments);
30969 DNDManager.manager().overSource(this);
30970 },
30971 onOutEvent: function(){
30972 // summary:
30973 // This method is called when mouse is moved out of our container (like onmouseleave)
30974 // tags:
30975 // private
30976 this._unmarkTargetAnchor();
30977 var m = DNDManager.manager();
30978 if(this.isDragging){
30979 m.canDrop(false);
30980 }
30981 m.outSource(this);
30982
30983 this.inherited(arguments);
30984 },
30985
30986 _isParentChildDrop: function(source, targetRow){
30987 // summary:
30988 // Checks whether the dragged items are parent rows in the tree which are being
30989 // dragged into their own children.
30990 //
30991 // source:
30992 // The DragSource object.
30993 //
30994 // targetRow:
30995 // The tree row onto which the dragged nodes are being dropped.
30996 //
30997 // tags:
30998 // private
30999
31000 // If the dragged object is not coming from the tree this widget belongs to,
31001 // it cannot be invalid.
31002 if(!source.tree || source.tree != this.tree){
31003 return false;
31004 }
31005
31006
31007 var root = source.tree.domNode;
31008 var ids = source.selection;
31009
31010 var node = targetRow.parentNode;
31011
31012 // Iterate up the DOM hierarchy from the target drop row,
31013 // checking of any of the dragged nodes have the same ID.
31014 while(node != root && !ids[node.id]){
31015 node = node.parentNode;
31016 }
31017
31018 return node.id && ids[node.id];
31019 },
31020
31021 _unmarkTargetAnchor: function(){
31022 // summary:
31023 // Removes hover class of the current target anchor
31024 // tags:
31025 // private
31026 if(!this.targetAnchor){ return; }
31027 this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
31028 this.targetAnchor = null;
31029 this.targetBox = null;
31030 this.dropPosition = null;
31031 },
31032
31033 _markDndStatus: function(copy){
31034 // summary:
31035 // Changes source's state based on "copy" status
31036 this._changeState("Source", copy ? "Copied" : "Moved");
31037 }
31038 });
31039
31040 /*=====
31041 dndSource.__Item = __Item;
31042 =====*/
31043
31044 return dndSource;
31045 });
31046
31047 },
31048 'dijit/a11y':function(){
31049 define("dijit/a11y", [
31050 "dojo/_base/array", // array.forEach array.map
31051 "dojo/_base/config", // defaultDuration
31052 "dojo/_base/declare", // declare
31053 "dojo/dom", // dom.byId
31054 "dojo/dom-attr", // domAttr.attr domAttr.has
31055 "dojo/dom-style", // style.style
31056 "dojo/sniff", // has("ie")
31057 "./main" // for exporting methods to dijit namespace
31058 ], function(array, config, declare, dom, domAttr, domStyle, has, dijit){
31059
31060 // module:
31061 // dijit/a11y
31062
31063 var shown = (dijit._isElementShown = function(/*Element*/ elem){
31064 var s = domStyle.get(elem);
31065 return (s.visibility != "hidden")
31066 && (s.visibility != "collapsed")
31067 && (s.display != "none")
31068 && (domAttr.get(elem, "type") != "hidden");
31069 });
31070
31071 dijit.hasDefaultTabStop = function(/*Element*/ elem){
31072 // summary:
31073 // Tests if element is tab-navigable even without an explicit tabIndex setting
31074
31075 // No explicit tabIndex setting, need to investigate node type
31076 switch(elem.nodeName.toLowerCase()){
31077 case "a":
31078 // An <a> w/out a tabindex is only navigable if it has an href
31079 return domAttr.has(elem, "href");
31080 case "area":
31081 case "button":
31082 case "input":
31083 case "object":
31084 case "select":
31085 case "textarea":
31086 // These are navigable by default
31087 return true;
31088 case "iframe":
31089 // If it's an editor <iframe> then it's tab navigable.
31090 var body;
31091 try{
31092 // non-IE
31093 var contentDocument = elem.contentDocument;
31094 if("designMode" in contentDocument && contentDocument.designMode == "on"){
31095 return true;
31096 }
31097 body = contentDocument.body;
31098 }catch(e1){
31099 // contentWindow.document isn't accessible within IE7/8
31100 // if the iframe.src points to a foreign url and this
31101 // page contains an element, that could get focus
31102 try{
31103 body = elem.contentWindow.document.body;
31104 }catch(e2){
31105 return false;
31106 }
31107 }
31108 return body && (body.contentEditable == 'true' ||
31109 (body.firstChild && body.firstChild.contentEditable == 'true'));
31110 default:
31111 return elem.contentEditable == 'true';
31112 }
31113 };
31114
31115 var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
31116 // summary:
31117 // Tests if an element is tab-navigable
31118
31119 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
31120 if(domAttr.get(elem, "disabled")){
31121 return false;
31122 }else if(domAttr.has(elem, "tabIndex")){
31123 // Explicit tab index setting
31124 return domAttr.get(elem, "tabIndex") >= 0; // boolean
31125 }else{
31126 // No explicit tabIndex setting, so depends on node type
31127 return dijit.hasDefaultTabStop(elem);
31128 }
31129 });
31130
31131 dijit._getTabNavigable = function(/*DOMNode*/ root){
31132 // summary:
31133 // Finds descendants of the specified root node.
31134 // description:
31135 // Finds the following descendants of the specified root node:
31136 //
31137 // - the first tab-navigable element in document order
31138 // without a tabIndex or with tabIndex="0"
31139 // - the last tab-navigable element in document order
31140 // without a tabIndex or with tabIndex="0"
31141 // - the first element in document order with the lowest
31142 // positive tabIndex value
31143 // - the last element in document order with the highest
31144 // positive tabIndex value
31145 var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
31146
31147 function radioName(node){
31148 // If this element is part of a radio button group, return the name for that group.
31149 return node && node.tagName.toLowerCase() == "input" &&
31150 node.type && node.type.toLowerCase() == "radio" &&
31151 node.name && node.name.toLowerCase();
31152 }
31153
31154 var walkTree = function(/*DOMNode*/ parent){
31155 for(var child = parent.firstChild; child; child = child.nextSibling){
31156 // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
31157 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
31158 if(child.nodeType != 1 || (has("ie") <= 9 && child.scopeName !== "HTML") || !shown(child)){
31159 continue;
31160 }
31161
31162 if(isTabNavigable(child)){
31163 var tabindex = +domAttr.get(child, "tabIndex"); // + to convert string --> number
31164 if(!domAttr.has(child, "tabIndex") || tabindex == 0){
31165 if(!first){
31166 first = child;
31167 }
31168 last = child;
31169 }else if(tabindex > 0){
31170 if(!lowest || tabindex < lowestTabindex){
31171 lowestTabindex = tabindex;
31172 lowest = child;
31173 }
31174 if(!highest || tabindex >= highestTabindex){
31175 highestTabindex = tabindex;
31176 highest = child;
31177 }
31178 }
31179 var rn = radioName(child);
31180 if(domAttr.get(child, "checked") && rn){
31181 radioSelected[rn] = child;
31182 }
31183 }
31184 if(child.nodeName.toUpperCase() != 'SELECT'){
31185 walkTree(child);
31186 }
31187 }
31188 };
31189 if(shown(root)){
31190 walkTree(root);
31191 }
31192 function rs(node){
31193 // substitute checked radio button for unchecked one, if there is a checked one with the same name.
31194 return radioSelected[radioName(node)] || node;
31195 }
31196
31197 return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
31198 };
31199 dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
31200 // summary:
31201 // Finds the descendant of the specified root node
31202 // that is first in the tabbing order
31203 var elems = dijit._getTabNavigable(dom.byId(root, doc));
31204 return elems.lowest ? elems.lowest : elems.first; // DomNode
31205 };
31206
31207 dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
31208 // summary:
31209 // Finds the descendant of the specified root node
31210 // that is last in the tabbing order
31211 var elems = dijit._getTabNavigable(dom.byId(root, doc));
31212 return elems.last ? elems.last : elems.highest; // DomNode
31213 };
31214
31215 return {
31216 // summary:
31217 // Accessibility utility functions (keyboard, tab stops, etc.)
31218
31219 hasDefaultTabStop: dijit.hasDefaultTabStop,
31220 isTabNavigable: dijit.isTabNavigable,
31221 _getTabNavigable: dijit._getTabNavigable,
31222 getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
31223 getLastInTabbingOrder: dijit.getLastInTabbingOrder
31224 };
31225 });
31226
31227 },
31228 'dijit/form/_ToggleButtonMixin':function(){
31229 define("dijit/form/_ToggleButtonMixin", [
31230 "dojo/_base/declare", // declare
31231 "dojo/dom-attr" // domAttr.set
31232 ], function(declare, domAttr){
31233
31234 // module:
31235 // dijit/form/_ToggleButtonMixin
31236
31237 return declare("dijit.form._ToggleButtonMixin", null, {
31238 // summary:
31239 // A mixin to provide functionality to allow a button that can be in two states (checked or not).
31240
31241 // checked: Boolean
31242 // Corresponds to the native HTML `<input>` element's attribute.
31243 // In markup, specified as "checked='checked'" or just "checked".
31244 // True if the button is depressed, or the checkbox is checked,
31245 // or the radio button is selected, etc.
31246 checked: false,
31247
31248 // aria-pressed for toggle buttons, and aria-checked for checkboxes
31249 _aria_attr: "aria-pressed",
31250
31251 _onClick: function(/*Event*/ evt){
31252 var original = this.checked;
31253 this._set('checked', !original); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
31254 var ret = this.inherited(arguments); // the user could reset the value here
31255 this.set('checked', ret ? this.checked : original); // officially set the toggled or user value, or reset it back
31256 return ret;
31257 },
31258
31259 _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
31260 this._set("checked", value);
31261 var node = this.focusNode || this.domNode;
31262 domAttr.set(node, "checked", !!value); // "mixed" -> true
31263 if(value){
31264 node.setAttribute("checked", "");
31265 }else{
31266 node.removeAttribute("checked");
31267 }
31268 node.setAttribute(this._aria_attr, String(value)); // aria values should be strings
31269 this._handleOnChange(value, priorityChange);
31270 },
31271
31272 reset: function(){
31273 // summary:
31274 // Reset the widget's value to what it was at initialization time
31275
31276 this._hasBeenBlurred = false;
31277
31278 // set checked state to original setting
31279 this.set('checked', this.params.checked || false);
31280 }
31281 });
31282
31283 });
31284
31285 },
31286 'dijit/_Widget':function(){
31287 define("dijit/_Widget", [
31288 "dojo/aspect", // aspect.around
31289 "dojo/_base/config", // config.isDebug
31290 "dojo/_base/connect", // connect.connect
31291 "dojo/_base/declare", // declare
31292 "dojo/has",
31293 "dojo/_base/kernel", // kernel.deprecated
31294 "dojo/_base/lang", // lang.hitch
31295 "dojo/query",
31296 "dojo/ready",
31297 "./registry", // registry.byNode
31298 "./_WidgetBase",
31299 "./_OnDijitClickMixin",
31300 "./_FocusMixin",
31301 "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
31302 "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
31303 ], function(aspect, config, connect, declare, has, kernel, lang, query, ready,
31304 registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
31305
31306
31307 // module:
31308 // dijit/_Widget
31309
31310
31311 function connectToDomNode(){
31312 // summary:
31313 // If user connects to a widget method === this function, then they will
31314 // instead actually be connecting the equivalent event on this.domNode
31315 }
31316
31317 // Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
31318 function aroundAdvice(originalConnect){
31319 return function(obj, event, scope, method){
31320 if(obj && typeof event == "string" && obj[event] == connectToDomNode){
31321 return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
31322 }
31323 return originalConnect.apply(connect, arguments);
31324 };
31325 }
31326 aspect.around(connect, "connect", aroundAdvice);
31327 if(kernel.connect){
31328 aspect.around(kernel, "connect", aroundAdvice);
31329 }
31330
31331 var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
31332 // summary:
31333 // Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
31334 // description:
31335 // Old Base class for Dijit widgets.
31336 //
31337 // Extends _WidgetBase, adding support for:
31338 //
31339 // - declaratively/programatically specifying widget initialization parameters like
31340 // onMouseMove="foo" that call foo when this.domNode gets a mousemove event
31341 // - ondijitclick:
31342 // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
31343 // - focus related functions:
31344 // In particular, the onFocus()/onBlur() callbacks. Driven internally by
31345 // dijit/_base/focus.js.
31346 // - deprecated methods
31347 // - onShow(), onHide(), onClose()
31348 //
31349 // Also, by loading code in dijit/_base, turns on:
31350 //
31351 // - browser sniffing (putting browser class like `dj_ie` on `<html>` node)
31352 // - high contrast mode sniffing (add `dijit_a11y` class to `<body>` if machine is in high contrast mode)
31353
31354
31355 ////////////////// DEFERRED CONNECTS ///////////////////
31356
31357 onClick: connectToDomNode,
31358 /*=====
31359 onClick: function(event){
31360 // summary:
31361 // Connect to this function to receive notifications of mouse click events.
31362 // event:
31363 // mouse Event
31364 // tags:
31365 // callback
31366 },
31367 =====*/
31368 onDblClick: connectToDomNode,
31369 /*=====
31370 onDblClick: function(event){
31371 // summary:
31372 // Connect to this function to receive notifications of mouse double click events.
31373 // event:
31374 // mouse Event
31375 // tags:
31376 // callback
31377 },
31378 =====*/
31379 onKeyDown: connectToDomNode,
31380 /*=====
31381 onKeyDown: function(event){
31382 // summary:
31383 // Connect to this function to receive notifications of keys being pressed down.
31384 // event:
31385 // key Event
31386 // tags:
31387 // callback
31388 },
31389 =====*/
31390 onKeyPress: connectToDomNode,
31391 /*=====
31392 onKeyPress: function(event){
31393 // summary:
31394 // Connect to this function to receive notifications of printable keys being typed.
31395 // event:
31396 // key Event
31397 // tags:
31398 // callback
31399 },
31400 =====*/
31401 onKeyUp: connectToDomNode,
31402 /*=====
31403 onKeyUp: function(event){
31404 // summary:
31405 // Connect to this function to receive notifications of keys being released.
31406 // event:
31407 // key Event
31408 // tags:
31409 // callback
31410 },
31411 =====*/
31412 onMouseDown: connectToDomNode,
31413 /*=====
31414 onMouseDown: function(event){
31415 // summary:
31416 // Connect to this function to receive notifications of when the mouse button is pressed down.
31417 // event:
31418 // mouse Event
31419 // tags:
31420 // callback
31421 },
31422 =====*/
31423 onMouseMove: connectToDomNode,
31424 /*=====
31425 onMouseMove: function(event){
31426 // summary:
31427 // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
31428 // event:
31429 // mouse Event
31430 // tags:
31431 // callback
31432 },
31433 =====*/
31434 onMouseOut: connectToDomNode,
31435 /*=====
31436 onMouseOut: function(event){
31437 // summary:
31438 // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
31439 // event:
31440 // mouse Event
31441 // tags:
31442 // callback
31443 },
31444 =====*/
31445 onMouseOver: connectToDomNode,
31446 /*=====
31447 onMouseOver: function(event){
31448 // summary:
31449 // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
31450 // event:
31451 // mouse Event
31452 // tags:
31453 // callback
31454 },
31455 =====*/
31456 onMouseLeave: connectToDomNode,
31457 /*=====
31458 onMouseLeave: function(event){
31459 // summary:
31460 // Connect to this function to receive notifications of when the mouse moves off of this widget.
31461 // event:
31462 // mouse Event
31463 // tags:
31464 // callback
31465 },
31466 =====*/
31467 onMouseEnter: connectToDomNode,
31468 /*=====
31469 onMouseEnter: function(event){
31470 // summary:
31471 // Connect to this function to receive notifications of when the mouse moves onto this widget.
31472 // event:
31473 // mouse Event
31474 // tags:
31475 // callback
31476 },
31477 =====*/
31478 onMouseUp: connectToDomNode,
31479 /*=====
31480 onMouseUp: function(event){
31481 // summary:
31482 // Connect to this function to receive notifications of when the mouse button is released.
31483 // event:
31484 // mouse Event
31485 // tags:
31486 // callback
31487 },
31488 =====*/
31489
31490 constructor: function(params /*===== ,srcNodeRef =====*/){
31491 // summary:
31492 // Create the widget.
31493 // params: Object|null
31494 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
31495 // and functions, typically callbacks like onClick.
31496 // The hash can contain any of the widget's properties, excluding read-only properties.
31497 // srcNodeRef: DOMNode|String?
31498 // If a srcNodeRef (DOM node) is specified:
31499 //
31500 // - use srcNodeRef.innerHTML as my contents
31501 // - if this is a behavioral widget then apply behavior to that srcNodeRef
31502 // - otherwise, replace srcNodeRef with my generated DOM tree
31503
31504 // extract parameters like onMouseMove that should connect directly to this.domNode
31505 this._toConnect = {};
31506 for(var name in params){
31507 if(this[name] === connectToDomNode){
31508 this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
31509 delete params[name];
31510 }
31511 }
31512 },
31513
31514 postCreate: function(){
31515 this.inherited(arguments);
31516
31517 // perform connection from this.domNode to user specified handlers (ex: onMouseMove)
31518 for(var name in this._toConnect){
31519 this.on(name, this._toConnect[name]);
31520 }
31521 delete this._toConnect;
31522 },
31523
31524 on: function(/*String|Function*/ type, /*Function*/ func){
31525 if(this[this._onMap(type)] === connectToDomNode){
31526 // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
31527 // normalization of onkeypress/onkeydown to behave like firefox, etc.
31528 // Also, need to specify context as "this" rather than the default context of the DOMNode
31529 // Remove in 2.0.
31530 return connect.connect(this.domNode, type.toLowerCase(), this, func);
31531 }
31532 return this.inherited(arguments);
31533 },
31534
31535 _setFocusedAttr: function(val){
31536 // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
31537 // (but since it's a private variable we aren't required to keep supporting it).
31538 this._focused = val;
31539 this._set("focused", val);
31540 },
31541
31542 ////////////////// DEPRECATED METHODS ///////////////////
31543
31544 setAttribute: function(/*String*/ attr, /*anything*/ value){
31545 // summary:
31546 // Deprecated. Use set() instead.
31547 // tags:
31548 // deprecated
31549 kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
31550 this.set(attr, value);
31551 },
31552
31553 attr: function(/*String|Object*/name, /*Object?*/value){
31554 // summary:
31555 // Set or get properties on a widget instance.
31556 // name:
31557 // The property to get or set. If an object is passed here and not
31558 // a string, its keys are used as names of attributes to be set
31559 // and the value of the object as values to set in the widget.
31560 // value:
31561 // Optional. If provided, attr() operates as a setter. If omitted,
31562 // the current value of the named property is returned.
31563 // description:
31564 // This method is deprecated, use get() or set() directly.
31565
31566 // Print deprecation warning but only once per calling function
31567 if(config.isDebug){
31568 var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
31569 caller = (arguments.callee.caller || "unknown caller").toString();
31570 if(!alreadyCalledHash[caller]){
31571 kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
31572 caller, "", "2.0");
31573 alreadyCalledHash[caller] = true;
31574 }
31575 }
31576
31577 var args = arguments.length;
31578 if(args >= 2 || typeof name === "object"){ // setter
31579 return this.set.apply(this, arguments);
31580 }else{ // getter
31581 return this.get(name);
31582 }
31583 },
31584
31585 getDescendants: function(){
31586 // summary:
31587 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
31588 // This method should generally be avoided as it returns widgets declared in templates, which are
31589 // supposed to be internal/hidden, but it's left here for back-compat reasons.
31590
31591 kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
31592 return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit/_WidgetBase[]
31593 },
31594
31595 ////////////////// MISCELLANEOUS METHODS ///////////////////
31596
31597 _onShow: function(){
31598 // summary:
31599 // Internal method called when this widget is made visible.
31600 // See `onShow` for details.
31601 this.onShow();
31602 },
31603
31604 onShow: function(){
31605 // summary:
31606 // Called when this widget becomes the selected pane in a
31607 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31608 // `dijit/layout/AccordionContainer`, etc.
31609 //
31610 // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31611 // tags:
31612 // callback
31613 },
31614
31615 onHide: function(){
31616 // summary:
31617 // Called when another widget becomes the selected pane in a
31618 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31619 // `dijit/layout/AccordionContainer`, etc.
31620 //
31621 // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31622 // tags:
31623 // callback
31624 },
31625
31626 onClose: function(){
31627 // summary:
31628 // Called when this widget is being displayed as a popup (ex: a Calendar popped
31629 // up from a DateTextBox), and it is hidden.
31630 // This is called from the dijit.popup code, and should not be called directly.
31631 //
31632 // Also used as a parameter for children of `dijit/layout/StackContainer` or subclasses.
31633 // Callback if a user tries to close the child. Child will be closed if this function returns true.
31634 // tags:
31635 // extension
31636
31637 return true; // Boolean
31638 }
31639 });
31640
31641 // For back-compat, remove in 2.0.
31642 if(has("dijit-legacy-requires")){
31643 ready(0, function(){
31644 var requires = ["dijit/_base"];
31645 require(requires); // use indirection so modules not rolled into a build
31646 });
31647 }
31648 return _Widget;
31649 });
31650
31651 },
31652 'dojo/touch':function(){
31653 define("dojo/touch", ["./_base/kernel", "./aspect", "./dom", "./on", "./has", "./mouse", "./ready", "./_base/window"],
31654 function(dojo, aspect, dom, on, has, mouse, ready, win){
31655
31656 // module:
31657 // dojo/touch
31658
31659 var hasTouch = has("touch");
31660
31661 // TODO: get iOS version from dojo/sniff after #15827 is fixed
31662 var ios4 = false;
31663 if(has("ios")){
31664 var ua = navigator.userAgent;
31665 var v = ua.match(/OS ([\d_]+)/) ? RegExp.$1 : "1";
31666 var os = parseFloat(v.replace(/_/, '.').replace(/_/g, ''));
31667 ios4 = os < 5;
31668 }
31669
31670 var touchmove, hoveredNode;
31671
31672 if(hasTouch){
31673 ready(function(){
31674 // Keep track of currently hovered node
31675 hoveredNode = win.body(); // currently hovered node
31676
31677 win.doc.addEventListener("touchstart", function(evt){
31678 // Precede touchstart event with touch.over event. DnD depends on this.
31679 // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
31680 // and to ensure this code runs even if the listener on the node does event.stop().
31681 var oldNode = hoveredNode;
31682 hoveredNode = evt.target;
31683 on.emit(oldNode, "dojotouchout", {
31684 target: oldNode,
31685 relatedTarget: hoveredNode,
31686 bubbles: true
31687 });
31688 on.emit(hoveredNode, "dojotouchover", {
31689 target: hoveredNode,
31690 relatedTarget: oldNode,
31691 bubbles: true
31692 });
31693 }, true);
31694
31695 // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
31696 on(win.doc, "touchmove", function(evt){
31697 var newNode = win.doc.elementFromPoint(
31698 evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
31699 evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
31700 );
31701 if(newNode && hoveredNode !== newNode){
31702 // touch out on the old node
31703 on.emit(hoveredNode, "dojotouchout", {
31704 target: hoveredNode,
31705 relatedTarget: newNode,
31706 bubbles: true
31707 });
31708
31709 // touchover on the new node
31710 on.emit(newNode, "dojotouchover", {
31711 target: newNode,
31712 relatedTarget: hoveredNode,
31713 bubbles: true
31714 });
31715
31716 hoveredNode = newNode;
31717 }
31718 });
31719 });
31720
31721 // Define synthetic touch.move event that unlike the native touchmove, fires for the node the finger is
31722 // currently dragging over rather than the node where the touch started.
31723 touchmove = function(node, listener){
31724 return on(win.doc, "touchmove", function(evt){
31725 if(node === win.doc || dom.isDescendant(hoveredNode, node)){
31726 evt.target = hoveredNode;
31727 listener.call(this, evt);
31728 }
31729 });
31730 };
31731 }
31732
31733
31734 function _handle(type){
31735 // type: String
31736 // press | move | release | cancel
31737
31738 return function(node, listener){//called by on(), see dojo.on
31739 return on(node, type, listener);
31740 };
31741 }
31742
31743 //device neutral events - touch.press|move|release|cancel/over/out
31744 var touch = {
31745 press: _handle(hasTouch ? "touchstart": "mousedown"),
31746 move: hasTouch ? touchmove :_handle("mousemove"),
31747 release: _handle(hasTouch ? "touchend": "mouseup"),
31748 cancel: hasTouch ? _handle("touchcancel") : mouse.leave,
31749 over: _handle(hasTouch ? "dojotouchover": "mouseover"),
31750 out: _handle(hasTouch ? "dojotouchout": "mouseout"),
31751 enter: mouse._eventHandler(hasTouch ? "dojotouchover" : "mouseover"),
31752 leave: mouse._eventHandler(hasTouch ? "dojotouchout" : "mouseout")
31753 };
31754 /*=====
31755 touch = {
31756 // summary:
31757 // This module provides unified touch event handlers by exporting
31758 // press, move, release and cancel which can also run well on desktop.
31759 // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
31760 //
31761 // example:
31762 // Used with dojo.on
31763 // | define(["dojo/on", "dojo/touch"], function(on, touch){
31764 // | on(node, touch.press, function(e){});
31765 // | on(node, touch.move, function(e){});
31766 // | on(node, touch.release, function(e){});
31767 // | on(node, touch.cancel, function(e){});
31768 // example:
31769 // Used with touch.* directly
31770 // | touch.press(node, function(e){});
31771 // | touch.move(node, function(e){});
31772 // | touch.release(node, function(e){});
31773 // | touch.cancel(node, function(e){});
31774
31775 press: function(node, listener){
31776 // summary:
31777 // Register a listener to 'touchstart'|'mousedown' for the given node
31778 // node: Dom
31779 // Target node to listen to
31780 // listener: Function
31781 // Callback function
31782 // returns:
31783 // A handle which will be used to remove the listener by handle.remove()
31784 },
31785 move: function(node, listener){
31786 // summary:
31787 // Register a listener to 'touchmove'|'mousemove' for the given node
31788 // node: Dom
31789 // Target node to listen to
31790 // listener: Function
31791 // Callback function
31792 // returns:
31793 // A handle which will be used to remove the listener by handle.remove()
31794 },
31795 release: function(node, listener){
31796 // summary:
31797 // Register a listener to 'touchend'|'mouseup' for the given node
31798 // node: Dom
31799 // Target node to listen to
31800 // listener: Function
31801 // Callback function
31802 // returns:
31803 // A handle which will be used to remove the listener by handle.remove()
31804 },
31805 cancel: function(node, listener){
31806 // summary:
31807 // Register a listener to 'touchcancel'|'mouseleave' for the given node
31808 // node: Dom
31809 // Target node to listen to
31810 // listener: Function
31811 // Callback function
31812 // returns:
31813 // A handle which will be used to remove the listener by handle.remove()
31814 },
31815 over: function(node, listener){
31816 // summary:
31817 // Register a listener to 'mouseover' or touch equivalent for the given node
31818 // node: Dom
31819 // Target node to listen to
31820 // listener: Function
31821 // Callback function
31822 // returns:
31823 // A handle which will be used to remove the listener by handle.remove()
31824 },
31825 out: function(node, listener){
31826 // summary:
31827 // Register a listener to 'mouseout' or touch equivalent for the given node
31828 // node: Dom
31829 // Target node to listen to
31830 // listener: Function
31831 // Callback function
31832 // returns:
31833 // A handle which will be used to remove the listener by handle.remove()
31834 },
31835 enter: function(node, listener){
31836 // summary:
31837 // Register a listener to mouse.enter or touch equivalent for the given node
31838 // node: Dom
31839 // Target node to listen to
31840 // listener: Function
31841 // Callback function
31842 // returns:
31843 // A handle which will be used to remove the listener by handle.remove()
31844 },
31845 leave: function(node, listener){
31846 // summary:
31847 // Register a listener to mouse.leave or touch equivalent for the given node
31848 // node: Dom
31849 // Target node to listen to
31850 // listener: Function
31851 // Callback function
31852 // returns:
31853 // A handle which will be used to remove the listener by handle.remove()
31854 }
31855 };
31856 =====*/
31857
31858 1 && (dojo.touch = touch);
31859
31860 return touch;
31861 });
31862
31863 },
31864 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t/></div\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer\"\n\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n",
31865 'dojo/fx':function(){
31866 define("dojo/fx", [
31867 "./_base/lang",
31868 "./Evented",
31869 "./_base/kernel",
31870 "./_base/array",
31871 "./_base/connect",
31872 "./_base/fx",
31873 "./dom",
31874 "./dom-style",
31875 "./dom-geometry",
31876 "./ready",
31877 "require" // for context sensitive loading of Toggler
31878 ], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require){
31879
31880 // module:
31881 // dojo/fx
31882
31883 // For back-compat, remove in 2.0.
31884 if(!dojo.isAsync){
31885 ready(0, function(){
31886 var requires = ["./fx/Toggler"];
31887 require(requires); // use indirection so modules not rolled into a build
31888 });
31889 }
31890
31891 var coreFx = dojo.fx = {
31892 // summary:
31893 // Effects library on top of Base animations
31894 };
31895
31896 var _baseObj = {
31897 _fire: function(evt, args){
31898 if(this[evt]){
31899 this[evt].apply(this, args||[]);
31900 }
31901 return this;
31902 }
31903 };
31904
31905 var _chain = function(animations){
31906 this._index = -1;
31907 this._animations = animations||[];
31908 this._current = this._onAnimateCtx = this._onEndCtx = null;
31909
31910 this.duration = 0;
31911 arrayUtil.forEach(this._animations, function(a){
31912 this.duration += a.duration;
31913 if(a.delay){ this.duration += a.delay; }
31914 }, this);
31915 };
31916 _chain.prototype = new Evented();
31917 lang.extend(_chain, {
31918 _onAnimate: function(){
31919 this._fire("onAnimate", arguments);
31920 },
31921 _onEnd: function(){
31922 connect.disconnect(this._onAnimateCtx);
31923 connect.disconnect(this._onEndCtx);
31924 this._onAnimateCtx = this._onEndCtx = null;
31925 if(this._index + 1 == this._animations.length){
31926 this._fire("onEnd");
31927 }else{
31928 // switch animations
31929 this._current = this._animations[++this._index];
31930 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
31931 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
31932 this._current.play(0, true);
31933 }
31934 },
31935 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
31936 if(!this._current){ this._current = this._animations[this._index = 0]; }
31937 if(!gotoStart && this._current.status() == "playing"){ return this; }
31938 var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
31939 this._fire("beforeBegin");
31940 }),
31941 onBegin = connect.connect(this._current, "onBegin", this, function(arg){
31942 this._fire("onBegin", arguments);
31943 }),
31944 onPlay = connect.connect(this._current, "onPlay", this, function(arg){
31945 this._fire("onPlay", arguments);
31946 connect.disconnect(beforeBegin);
31947 connect.disconnect(onBegin);
31948 connect.disconnect(onPlay);
31949 });
31950 if(this._onAnimateCtx){
31951 connect.disconnect(this._onAnimateCtx);
31952 }
31953 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
31954 if(this._onEndCtx){
31955 connect.disconnect(this._onEndCtx);
31956 }
31957 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
31958 this._current.play.apply(this._current, arguments);
31959 return this;
31960 },
31961 pause: function(){
31962 if(this._current){
31963 var e = connect.connect(this._current, "onPause", this, function(arg){
31964 this._fire("onPause", arguments);
31965 connect.disconnect(e);
31966 });
31967 this._current.pause();
31968 }
31969 return this;
31970 },
31971 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
31972 this.pause();
31973 var offset = this.duration * percent;
31974 this._current = null;
31975 arrayUtil.some(this._animations, function(a){
31976 if(a.duration <= offset){
31977 this._current = a;
31978 return true;
31979 }
31980 offset -= a.duration;
31981 return false;
31982 });
31983 if(this._current){
31984 this._current.gotoPercent(offset / this._current.duration, andPlay);
31985 }
31986 return this;
31987 },
31988 stop: function(/*boolean?*/ gotoEnd){
31989 if(this._current){
31990 if(gotoEnd){
31991 for(; this._index + 1 < this._animations.length; ++this._index){
31992 this._animations[this._index].stop(true);
31993 }
31994 this._current = this._animations[this._index];
31995 }
31996 var e = connect.connect(this._current, "onStop", this, function(arg){
31997 this._fire("onStop", arguments);
31998 connect.disconnect(e);
31999 });
32000 this._current.stop();
32001 }
32002 return this;
32003 },
32004 status: function(){
32005 return this._current ? this._current.status() : "stopped";
32006 },
32007 destroy: function(){
32008 if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
32009 if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
32010 }
32011 });
32012 lang.extend(_chain, _baseObj);
32013
32014 coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){
32015 // summary:
32016 // Chain a list of `dojo.Animation`s to run in sequence
32017 //
32018 // description:
32019 // Return a `dojo.Animation` which will play all passed
32020 // `dojo.Animation` instances in sequence, firing its own
32021 // synthesized events simulating a single animation. (eg:
32022 // onEnd of this animation means the end of the chain,
32023 // not the individual animations within)
32024 //
32025 // example:
32026 // Once `node` is faded out, fade in `otherNode`
32027 // | fx.chain([
32028 // | dojo.fadeIn({ node:node }),
32029 // | dojo.fadeOut({ node:otherNode })
32030 // | ]).play();
32031 //
32032 return new _chain(animations); // dojo/_base/fx.Animation
32033 };
32034
32035 var _combine = function(animations){
32036 this._animations = animations||[];
32037 this._connects = [];
32038 this._finished = 0;
32039
32040 this.duration = 0;
32041 arrayUtil.forEach(animations, function(a){
32042 var duration = a.duration;
32043 if(a.delay){ duration += a.delay; }
32044 if(this.duration < duration){ this.duration = duration; }
32045 this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
32046 }, this);
32047
32048 this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
32049 var self = this;
32050 arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
32051 function(evt){
32052 self._connects.push(connect.connect(self._pseudoAnimation, evt,
32053 function(){ self._fire(evt, arguments); }
32054 ));
32055 }
32056 );
32057 };
32058 lang.extend(_combine, {
32059 _doAction: function(action, args){
32060 arrayUtil.forEach(this._animations, function(a){
32061 a[action].apply(a, args);
32062 });
32063 return this;
32064 },
32065 _onEnd: function(){
32066 if(++this._finished > this._animations.length){
32067 this._fire("onEnd");
32068 }
32069 },
32070 _call: function(action, args){
32071 var t = this._pseudoAnimation;
32072 t[action].apply(t, args);
32073 },
32074 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
32075 this._finished = 0;
32076 this._doAction("play", arguments);
32077 this._call("play", arguments);
32078 return this;
32079 },
32080 pause: function(){
32081 this._doAction("pause", arguments);
32082 this._call("pause", arguments);
32083 return this;
32084 },
32085 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
32086 var ms = this.duration * percent;
32087 arrayUtil.forEach(this._animations, function(a){
32088 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
32089 });
32090 this._call("gotoPercent", arguments);
32091 return this;
32092 },
32093 stop: function(/*boolean?*/ gotoEnd){
32094 this._doAction("stop", arguments);
32095 this._call("stop", arguments);
32096 return this;
32097 },
32098 status: function(){
32099 return this._pseudoAnimation.status();
32100 },
32101 destroy: function(){
32102 arrayUtil.forEach(this._connects, connect.disconnect);
32103 }
32104 });
32105 lang.extend(_combine, _baseObj);
32106
32107 coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){
32108 // summary:
32109 // Combine a list of `dojo.Animation`s to run in parallel
32110 //
32111 // description:
32112 // Combine an array of `dojo.Animation`s to run in parallel,
32113 // providing a new `dojo.Animation` instance encompasing each
32114 // animation, firing standard animation events.
32115 //
32116 // example:
32117 // Fade out `node` while fading in `otherNode` simultaneously
32118 // | fx.combine([
32119 // | dojo.fadeIn({ node:node }),
32120 // | dojo.fadeOut({ node:otherNode })
32121 // | ]).play();
32122 //
32123 // example:
32124 // When the longest animation ends, execute a function:
32125 // | var anim = fx.combine([
32126 // | dojo.fadeIn({ node: n, duration:700 }),
32127 // | dojo.fadeOut({ node: otherNode, duration: 300 })
32128 // | ]);
32129 // | dojo.connect(anim, "onEnd", function(){
32130 // | // overall animation is done.
32131 // | });
32132 // | anim.play(); // play the animation
32133 //
32134 return new _combine(animations); // dojo/_base/fx.Animation
32135 };
32136
32137 coreFx.wipeIn = function(/*Object*/ args){
32138 // summary:
32139 // Expand a node to it's natural height.
32140 //
32141 // description:
32142 // Returns an animation that will expand the
32143 // node defined in 'args' object from it's current height to
32144 // it's natural height (with no scrollbar).
32145 // Node must have no margin/border/padding.
32146 //
32147 // args: Object
32148 // A hash-map of standard `dojo.Animation` constructor properties
32149 // (such as easing: node: duration: and so on)
32150 //
32151 // example:
32152 // | fx.wipeIn({
32153 // | node:"someId"
32154 // | }).play()
32155 var node = args.node = dom.byId(args.node), s = node.style, o;
32156
32157 var anim = baseFx.animateProperty(lang.mixin({
32158 properties: {
32159 height: {
32160 // wrapped in functions so we wait till the last second to query (in case value has changed)
32161 start: function(){
32162 // start at current [computed] height, but use 1px rather than 0
32163 // because 0 causes IE to display the whole panel
32164 o = s.overflow;
32165 s.overflow = "hidden";
32166 if(s.visibility == "hidden" || s.display == "none"){
32167 s.height = "1px";
32168 s.display = "";
32169 s.visibility = "";
32170 return 1;
32171 }else{
32172 var height = domStyle.get(node, "height");
32173 return Math.max(height, 1);
32174 }
32175 },
32176 end: function(){
32177 return node.scrollHeight;
32178 }
32179 }
32180 }
32181 }, args));
32182
32183 var fini = function(){
32184 s.height = "auto";
32185 s.overflow = o;
32186 };
32187 connect.connect(anim, "onStop", fini);
32188 connect.connect(anim, "onEnd", fini);
32189
32190 return anim; // dojo/_base/fx.Animation
32191 };
32192
32193 coreFx.wipeOut = function(/*Object*/ args){
32194 // summary:
32195 // Shrink a node to nothing and hide it.
32196 //
32197 // description:
32198 // Returns an animation that will shrink node defined in "args"
32199 // from it's current height to 1px, and then hide it.
32200 //
32201 // args: Object
32202 // A hash-map of standard `dojo.Animation` constructor properties
32203 // (such as easing: node: duration: and so on)
32204 //
32205 // example:
32206 // | fx.wipeOut({ node:"someId" }).play()
32207
32208 var node = args.node = dom.byId(args.node), s = node.style, o;
32209
32210 var anim = baseFx.animateProperty(lang.mixin({
32211 properties: {
32212 height: {
32213 end: 1 // 0 causes IE to display the whole panel
32214 }
32215 }
32216 }, args));
32217
32218 connect.connect(anim, "beforeBegin", function(){
32219 o = s.overflow;
32220 s.overflow = "hidden";
32221 s.display = "";
32222 });
32223 var fini = function(){
32224 s.overflow = o;
32225 s.height = "auto";
32226 s.display = "none";
32227 };
32228 connect.connect(anim, "onStop", fini);
32229 connect.connect(anim, "onEnd", fini);
32230
32231 return anim; // dojo/_base/fx.Animation
32232 };
32233
32234 coreFx.slideTo = function(/*Object*/ args){
32235 // summary:
32236 // Slide a node to a new top/left position
32237 //
32238 // description:
32239 // Returns an animation that will slide "node"
32240 // defined in args Object from its current position to
32241 // the position defined by (args.left, args.top).
32242 //
32243 // args: Object
32244 // A hash-map of standard `dojo.Animation` constructor properties
32245 // (such as easing: node: duration: and so on). Special args members
32246 // are `top` and `left`, which indicate the new position to slide to.
32247 //
32248 // example:
32249 // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
32250
32251 var node = args.node = dom.byId(args.node),
32252 top = null, left = null;
32253
32254 var init = (function(n){
32255 return function(){
32256 var cs = domStyle.getComputedStyle(n);
32257 var pos = cs.position;
32258 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
32259 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
32260 if(pos != 'absolute' && pos != 'relative'){
32261 var ret = geom.position(n, true);
32262 top = ret.y;
32263 left = ret.x;
32264 n.style.position="absolute";
32265 n.style.top=top+"px";
32266 n.style.left=left+"px";
32267 }
32268 };
32269 })(node);
32270 init();
32271
32272 var anim = baseFx.animateProperty(lang.mixin({
32273 properties: {
32274 top: args.top || 0,
32275 left: args.left || 0
32276 }
32277 }, args));
32278 connect.connect(anim, "beforeBegin", anim, init);
32279
32280 return anim; // dojo/_base/fx.Animation
32281 };
32282
32283 return coreFx;
32284 });
32285
32286 },
32287 'dijit/_DialogMixin':function(){
32288 define("dijit/_DialogMixin", [
32289 "dojo/_base/declare", // declare
32290 "./a11y" // _getTabNavigable
32291 ], function(declare, a11y){
32292
32293 // module:
32294 // dijit/_DialogMixin
32295
32296 return declare("dijit._DialogMixin", null, {
32297 // summary:
32298 // This provides functions useful to Dialog and TooltipDialog
32299
32300 execute: function(/*Object*/ /*===== formContents =====*/){
32301 // summary:
32302 // Callback when the user hits the submit button.
32303 // Override this method to handle Dialog execution.
32304 // description:
32305 // After the user has pressed the submit button, the Dialog
32306 // first calls onExecute() to notify the container to hide the
32307 // dialog and restore focus to wherever it used to be.
32308 //
32309 // *Then* this method is called.
32310 // type:
32311 // callback
32312 },
32313
32314 onCancel: function(){
32315 // summary:
32316 // Called when user has pressed the Dialog's cancel button, to notify container.
32317 // description:
32318 // Developer shouldn't override or connect to this method;
32319 // it's a private communication device between the TooltipDialog
32320 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
32321 // type:
32322 // protected
32323 },
32324
32325 onExecute: function(){
32326 // summary:
32327 // Called when user has pressed the dialog's OK button, to notify container.
32328 // description:
32329 // Developer shouldn't override or connect to this method;
32330 // it's a private communication device between the TooltipDialog
32331 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
32332 // type:
32333 // protected
32334 },
32335
32336 _onSubmit: function(){
32337 // summary:
32338 // Callback when user hits submit button
32339 // type:
32340 // protected
32341 this.onExecute(); // notify container that we are about to execute
32342 this.execute(this.get('value'));
32343 },
32344
32345 _getFocusItems: function(){
32346 // summary:
32347 // Finds focusable items in dialog,
32348 // and sets this._firstFocusItem and this._lastFocusItem
32349 // tags:
32350 // protected
32351
32352 var elems = a11y._getTabNavigable(this.containerNode);
32353 this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
32354 this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
32355 }
32356 });
32357 });
32358
32359 },
32360 'dijit/Tree':function(){
32361 require({cache:{
32362 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
32363 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});
32364 define("dijit/Tree", [
32365 "dojo/_base/array", // array.filter array.forEach array.map
32366 "dojo/_base/connect", // connect.isCopyKey()
32367 "dojo/cookie", // cookie
32368 "dojo/_base/declare", // declare
32369 "dojo/Deferred", // Deferred
32370 "dojo/DeferredList", // DeferredList
32371 "dojo/dom", // dom.isDescendant
32372 "dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
32373 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
32374 "dojo/dom-style",// domStyle.set
32375 "dojo/_base/event", // event.stop
32376 "dojo/errors/create", // createError
32377 "dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
32378 "dojo/_base/kernel", // kernel.deprecated
32379 "dojo/keys", // arrows etc.
32380 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
32381 "dojo/on", // on(), on.selector()
32382 "dojo/topic",
32383 "dojo/touch",
32384 "dojo/when",
32385 "./focus",
32386 "./registry", // registry.byNode(), registry.getEnclosingWidget()
32387 "./_base/manager", // manager.defaultDuration
32388 "./_Widget",
32389 "./_TemplatedMixin",
32390 "./_Container",
32391 "./_Contained",
32392 "./_CssStateMixin",
32393 "dojo/text!./templates/TreeNode.html",
32394 "dojo/text!./templates/Tree.html",
32395 "./tree/TreeStoreModel",
32396 "./tree/ForestStoreModel",
32397 "./tree/_dndSelector"
32398 ], function(array, connect, cookie, declare, Deferred, DeferredList,
32399 dom, domClass, domGeometry, domStyle, event, createError, fxUtils, kernel, keys, lang, on, topic, touch, when,
32400 focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
32401 treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
32402
32403 // module:
32404 // dijit/Tree
32405
32406 // Back-compat shim
32407 Deferred = declare(Deferred, {
32408 addCallback: function(callback){ this.then(callback); },
32409 addErrback: function(errback){ this.then(null, errback); }
32410 });
32411
32412 var TreeNode = declare(
32413 "dijit._TreeNode",
32414 [_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
32415 {
32416 // summary:
32417 // Single node within a tree. This class is used internally
32418 // by Tree and should not be accessed directly.
32419 // tags:
32420 // private
32421
32422 // item: [const] Item
32423 // the dojo.data entry this tree represents
32424 item: null,
32425
32426 // isTreeNode: [protected] Boolean
32427 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
32428 // should not be accessed directly.
32429 isTreeNode: true,
32430
32431 // label: String
32432 // Text of this tree node
32433 label: "",
32434 _setLabelAttr: {node: "labelNode", type: "innerText"},
32435
32436 // isExpandable: [private] Boolean
32437 // This node has children, so show the expando node (+ sign)
32438 isExpandable: null,
32439
32440 // isExpanded: [readonly] Boolean
32441 // This node is currently expanded (ie, opened)
32442 isExpanded: false,
32443
32444 // state: [private] String
32445 // Dynamic loading-related stuff.
32446 // When an empty folder node appears, it is "UNCHECKED" first,
32447 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
32448 state: "UNCHECKED",
32449
32450 templateString: treeNodeTemplate,
32451
32452 baseClass: "dijitTreeNode",
32453
32454 // For hover effect for tree node, and focus effect for label
32455 cssStateNodes: {
32456 rowNode: "dijitTreeRow"
32457 },
32458
32459 // Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
32460 _setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
32461
32462 buildRendering: function(){
32463 this.inherited(arguments);
32464
32465 // set expand icon for leaf
32466 this._setExpando();
32467
32468 // set icon and label class based on item
32469 this._updateItemClasses(this.item);
32470
32471 if(this.isExpandable){
32472 this.labelNode.setAttribute("aria-expanded", this.isExpanded);
32473 }
32474
32475 //aria-selected should be false on all selectable elements.
32476 this.setSelected(false);
32477 },
32478
32479 _setIndentAttr: function(indent){
32480 // summary:
32481 // Tell this node how many levels it should be indented
32482 // description:
32483 // 0 for top level nodes, 1 for their children, 2 for their
32484 // grandchildren, etc.
32485
32486 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
32487 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
32488
32489 domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px"); // TODOC: what is this for???
32490 domStyle.set(this.indentNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
32491
32492 array.forEach(this.getChildren(), function(child){
32493 child.set("indent", indent+1);
32494 });
32495
32496 this._set("indent", indent);
32497 },
32498
32499 markProcessing: function(){
32500 // summary:
32501 // Visually denote that tree is loading data, etc.
32502 // tags:
32503 // private
32504 this.state = "LOADING";
32505 this._setExpando(true);
32506 },
32507
32508 unmarkProcessing: function(){
32509 // summary:
32510 // Clear markup from markProcessing() call
32511 // tags:
32512 // private
32513 this._setExpando(false);
32514 },
32515
32516 _updateItemClasses: function(item){
32517 // summary:
32518 // Set appropriate CSS classes for icon and label dom node
32519 // (used to allow for item updates to change respective CSS)
32520 // tags:
32521 // private
32522 var tree = this.tree, model = tree.model;
32523 if(tree._v10Compat && item === model.root){
32524 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
32525 item = null;
32526 }
32527 this._applyClassAndStyle(item, "icon", "Icon");
32528 this._applyClassAndStyle(item, "label", "Label");
32529 this._applyClassAndStyle(item, "row", "Row");
32530
32531 this.tree._startPaint(true); // signifies paint started and finished (synchronously)
32532 },
32533
32534 _applyClassAndStyle: function(item, lower, upper){
32535 // summary:
32536 // Set the appropriate CSS classes and styles for labels, icons and rows.
32537 //
32538 // item:
32539 // The data item.
32540 //
32541 // lower:
32542 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
32543 //
32544 // upper:
32545 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
32546 //
32547 // tags:
32548 // private
32549
32550 var clsName = "_" + lower + "Class";
32551 var nodeName = lower + "Node";
32552 var oldCls = this[clsName];
32553
32554 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
32555 domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
32556
32557 domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
32558 },
32559
32560 _updateLayout: function(){
32561 // summary:
32562 // Set appropriate CSS classes for this.domNode
32563 // tags:
32564 // private
32565 var parent = this.getParent();
32566 if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
32567 /* if we are hiding the root node then make every first level child look like a root node */
32568 domClass.add(this.domNode, "dijitTreeIsRoot");
32569 }else{
32570 domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
32571 }
32572 },
32573
32574 _setExpando: function(/*Boolean*/ processing){
32575 // summary:
32576 // Set the right image for the expando node
32577 // tags:
32578 // private
32579
32580 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
32581 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
32582 _a11yStates = ["*","-","+","*"],
32583 idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
32584
32585 // apply the appropriate class to the expando node
32586 domClass.replace(this.expandoNode, styles[idx], styles);
32587
32588 // provide a non-image based indicator for images-off mode
32589 this.expandoNodeText.innerHTML = _a11yStates[idx];
32590
32591 },
32592
32593 expand: function(){
32594 // summary:
32595 // Show my children
32596 // returns:
32597 // Deferred that fires when expansion is complete
32598
32599 // If there's already an expand in progress or we are already expanded, just return
32600 if(this._expandDeferred){
32601 return this._expandDeferred; // dojo/_base/Deferred
32602 }
32603
32604 // cancel in progress collapse operation
32605 if(this._collapseDeferred){
32606 this._collapseDeferred.cancel();
32607 delete this._collapseDeferred;
32608 }
32609
32610 // All the state information for when a node is expanded, maybe this should be
32611 // set when the animation completes instead
32612 this.isExpanded = true;
32613 this.labelNode.setAttribute("aria-expanded", "true");
32614 if(this.tree.showRoot || this !== this.tree.rootNode){
32615 this.containerNode.setAttribute("role", "group");
32616 }
32617 domClass.add(this.contentNode,'dijitTreeContentExpanded');
32618 this._setExpando();
32619 this._updateItemClasses(this.item);
32620
32621 if(this == this.tree.rootNode && this.tree.showRoot){
32622 this.tree.domNode.setAttribute("aria-expanded", "true");
32623 }
32624
32625 var def,
32626 wipeIn = fxUtils.wipeIn({
32627 node: this.containerNode,
32628 duration: manager.defaultDuration,
32629 onEnd: function(){
32630 def.resolve(true);
32631 }
32632 });
32633
32634 // Deferred that fires when expand is complete
32635 def = (this._expandDeferred = new Deferred(function(){
32636 // Canceller
32637 wipeIn.stop();
32638 }));
32639
32640 wipeIn.play();
32641
32642 return def; // dojo/_base/Deferred
32643 },
32644
32645 collapse: function(){
32646 // summary:
32647 // Collapse this node (if it's expanded)
32648
32649 if(this._collapseDeferred){
32650 // Node is already collapsed, or there's a collapse in progress, just return that Deferred
32651 return this._collapseDeferred;
32652 }
32653
32654 // cancel in progress expand operation
32655 if(this._expandDeferred){
32656 this._expandDeferred.cancel();
32657 delete this._expandDeferred;
32658 }
32659
32660 this.isExpanded = false;
32661 this.labelNode.setAttribute("aria-expanded", "false");
32662 if(this == this.tree.rootNode && this.tree.showRoot){
32663 this.tree.domNode.setAttribute("aria-expanded", "false");
32664 }
32665 domClass.remove(this.contentNode,'dijitTreeContentExpanded');
32666 this._setExpando();
32667 this._updateItemClasses(this.item);
32668
32669 var def,
32670 wipeOut = fxUtils.wipeOut({
32671 node: this.containerNode,
32672 duration: manager.defaultDuration,
32673 onEnd: function(){
32674 def.resolve(true);
32675 }
32676 });
32677
32678 // Deferred that fires when expand is complete
32679 def = (this._collapseDeferred = new Deferred(function(){
32680 // Canceller
32681 wipeOut.stop();
32682 }));
32683
32684 wipeOut.play();
32685
32686 return def; // dojo/_base/Deferred
32687 },
32688
32689 // indent: Integer
32690 // Levels from this node to the root node
32691 indent: 0,
32692
32693 setChildItems: function(/* Object[] */ items){
32694 // summary:
32695 // Sets the child items of this node, removing/adding nodes
32696 // from current children to match specified items[] array.
32697 // Also, if this.persist == true, expands any children that were previously
32698 // opened.
32699 // returns:
32700 // Deferred object that fires after all previously opened children
32701 // have been expanded again (or fires instantly if there are no such children).
32702
32703 var tree = this.tree,
32704 model = tree.model,
32705 defs = []; // list of deferreds that need to fire before I am complete
32706
32707
32708 // Orphan all my existing children.
32709 // If items contains some of the same items as before then we will reattach them.
32710 // Don't call this.removeChild() because that will collapse the tree etc.
32711 var oldChildren = this.getChildren();
32712 array.forEach(oldChildren, function(child){
32713 _Container.prototype.removeChild.call(this, child);
32714 }, this);
32715
32716 // All the old children of this TreeNode are subject for destruction if
32717 // 1) they aren't listed in the new children array (items)
32718 // 2) they aren't immediately adopted by another node (DnD)
32719 this.defer(function(){
32720 array.forEach(oldChildren, function(node){
32721 if(!node._destroyed && !node.getParent()){
32722 // If node is in selection then remove it.
32723 tree.dndController.removeTreeNode(node);
32724
32725 // Deregister mapping from item id --> this node
32726 var id = model.getIdentity(node.item),
32727 ary = tree._itemNodesMap[id];
32728 if(ary.length == 1){
32729 delete tree._itemNodesMap[id];
32730 }else{
32731 var index = array.indexOf(ary, node);
32732 if(index != -1){
32733 ary.splice(index, 1);
32734 }
32735 }
32736
32737 // And finally we can destroy the node
32738 node.destroyRecursive();
32739 }
32740 });
32741 });
32742
32743 this.state = "LOADED";
32744
32745 if(items && items.length > 0){
32746 this.isExpandable = true;
32747
32748 // Create _TreeNode widget for each specified tree node, unless one already
32749 // exists and isn't being used (presumably it's from a DnD move and was recently
32750 // released
32751 array.forEach(items, function(item){ // MARKER: REUSE NODE
32752 var id = model.getIdentity(item),
32753 existingNodes = tree._itemNodesMap[id],
32754 node;
32755 if(existingNodes){
32756 for(var i=0;i<existingNodes.length;i++){
32757 if(existingNodes[i] && !existingNodes[i].getParent()){
32758 node = existingNodes[i];
32759 node.set('indent', this.indent+1);
32760 break;
32761 }
32762 }
32763 }
32764 if(!node){
32765 node = this.tree._createTreeNode({
32766 item: item,
32767 tree: tree,
32768 isExpandable: model.mayHaveChildren(item),
32769 label: tree.getLabel(item),
32770 tooltip: tree.getTooltip(item),
32771 ownerDocument: tree.ownerDocument,
32772 dir: tree.dir,
32773 lang: tree.lang,
32774 textDir: tree.textDir,
32775 indent: this.indent + 1
32776 });
32777 if(existingNodes){
32778 existingNodes.push(node);
32779 }else{
32780 tree._itemNodesMap[id] = [node];
32781 }
32782 }
32783 this.addChild(node);
32784
32785 // If node was previously opened then open it again now (this may trigger
32786 // more data store accesses, recursively)
32787 if(this.tree.autoExpand || this.tree._state(node)){
32788 defs.push(tree._expandNode(node));
32789 }
32790 }, this);
32791
32792 // note that updateLayout() needs to be called on each child after
32793 // _all_ the children exist
32794 array.forEach(this.getChildren(), function(child){
32795 child._updateLayout();
32796 });
32797 }else{
32798 this.isExpandable=false;
32799 }
32800
32801 if(this._setExpando){
32802 // change expando to/from dot or + icon, as appropriate
32803 this._setExpando(false);
32804 }
32805
32806 // Set leaf icon or folder icon, as appropriate
32807 this._updateItemClasses(this.item);
32808
32809 // On initial tree show, make the selected TreeNode as either the root node of the tree,
32810 // or the first child, if the root node is hidden
32811 if(this == tree.rootNode){
32812 var fc = this.tree.showRoot ? this : this.getChildren()[0];
32813 if(fc){
32814 fc.setFocusable(true);
32815 tree.lastFocused = fc;
32816 }else{
32817 // fallback: no nodes in tree so focus on Tree <div> itself
32818 tree.domNode.setAttribute("tabIndex", "0");
32819 }
32820 }
32821
32822 var def = new DeferredList(defs);
32823 this.tree._startPaint(def); // to reset TreeNode widths after an item is added/removed from the Tree
32824 return def; // dojo/_base/Deferred
32825 },
32826
32827 getTreePath: function(){
32828 var node = this;
32829 var path = [];
32830 while(node && node !== this.tree.rootNode){
32831 path.unshift(node.item);
32832 node = node.getParent();
32833 }
32834 path.unshift(this.tree.rootNode.item);
32835
32836 return path;
32837 },
32838
32839 getIdentity: function(){
32840 return this.tree.model.getIdentity(this.item);
32841 },
32842
32843 removeChild: function(/* treeNode */ node){
32844 this.inherited(arguments);
32845
32846 var children = this.getChildren();
32847 if(children.length == 0){
32848 this.isExpandable = false;
32849 this.collapse();
32850 }
32851
32852 array.forEach(children, function(child){
32853 child._updateLayout();
32854 });
32855 },
32856
32857 makeExpandable: function(){
32858 // summary:
32859 // if this node wasn't already showing the expando node,
32860 // turn it into one and call _setExpando()
32861
32862 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
32863
32864 this.isExpandable = true;
32865 this._setExpando(false);
32866 },
32867
32868 setSelected: function(/*Boolean*/ selected){
32869 // summary:
32870 // A Tree has a (single) currently selected node.
32871 // Mark that this node is/isn't that currently selected node.
32872 // description:
32873 // In particular, setting a node as selected involves setting tabIndex
32874 // so that when user tabs to the tree, focus will go to that node (only).
32875 this.labelNode.setAttribute("aria-selected", selected ? "true" : "false");
32876 domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
32877 },
32878
32879 setFocusable: function(/*Boolean*/ selected){
32880 // summary:
32881 // A Tree has a (single) node that's focusable.
32882 // Mark that this node is/isn't that currently focsuable node.
32883 // description:
32884 // In particular, setting a node as selected involves setting tabIndex
32885 // so that when user tabs to the tree, focus will go to that node (only).
32886
32887 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
32888 },
32889
32890
32891 _setTextDirAttr: function(textDir){
32892 if(textDir &&((this.textDir != textDir) || !this._created)){
32893 this._set("textDir", textDir);
32894 this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
32895 array.forEach(this.getChildren(), function(childNode){
32896 childNode.set("textDir", textDir);
32897 }, this);
32898 }
32899 }
32900 });
32901
32902 var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
32903 // summary:
32904 // This widget displays hierarchical data from a store.
32905
32906 // store: [deprecated] String|dojo/data/Store
32907 // Deprecated. Use "model" parameter instead.
32908 // The store to get data to display in the tree.
32909 store: null,
32910
32911 // model: dijit/tree/model
32912 // Interface to read tree data, get notifications of changes to tree data,
32913 // and for handling drop operations (i.e drag and drop onto the tree)
32914 model: null,
32915
32916 // query: [deprecated] anything
32917 // Deprecated. User should specify query to the model directly instead.
32918 // Specifies datastore query to return the root item or top items for the tree.
32919 query: null,
32920
32921 // label: [deprecated] String
32922 // Deprecated. Use dijit/tree/ForestStoreModel directly instead.
32923 // Used in conjunction with query parameter.
32924 // If a query is specified (rather than a root node id), and a label is also specified,
32925 // then a fake root node is created and displayed, with this label.
32926 label: "",
32927
32928 // showRoot: [const] Boolean
32929 // Should the root node be displayed, or hidden?
32930 showRoot: true,
32931
32932 // childrenAttr: [deprecated] String[]
32933 // Deprecated. This information should be specified in the model.
32934 // One ore more attributes that holds children of a tree node
32935 childrenAttr: ["children"],
32936
32937 // paths: String[][] or Item[][]
32938 // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
32939 // Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
32940 // returns a Deferred to indicate when the set is complete.
32941 paths: [],
32942
32943 // path: String[] or Item[]
32944 // Backward compatible singular variant of paths.
32945 path: [],
32946
32947 // selectedItems: [readonly] Item[]
32948 // The currently selected items in this tree.
32949 // This property can only be set (via set('selectedItems', ...)) when that item is already
32950 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
32951 // Should generally use `paths` attribute to set the selected items instead.
32952 selectedItems: null,
32953
32954 // selectedItem: [readonly] Item
32955 // Backward compatible singular variant of selectedItems.
32956 selectedItem: null,
32957
32958 // openOnClick: Boolean
32959 // If true, clicking a folder node's label will open it, rather than calling onClick()
32960 openOnClick: false,
32961
32962 // openOnDblClick: Boolean
32963 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
32964 openOnDblClick: false,
32965
32966 templateString: treeTemplate,
32967
32968 // persist: Boolean
32969 // Enables/disables use of cookies for state saving.
32970 persist: true,
32971
32972 // autoExpand: Boolean
32973 // Fully expand the tree on load. Overrides `persist`.
32974 autoExpand: false,
32975
32976 // dndController: [protected] Function|String
32977 // Class to use as as the dnd controller. Specifying this class enables DnD.
32978 // Generally you should specify this as dijit/tree/dndSource.
32979 // Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
32980 dndController: _dndSelector,
32981
32982 // parameters to pull off of the tree and pass on to the dndController as its params
32983 dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
32984
32985 //declare the above items so they can be pulled from the tree's markup
32986
32987 // onDndDrop: [protected] Function
32988 // Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
32989 // Generally this doesn't need to be set.
32990 onDndDrop: null,
32991
32992 itemCreator: null,
32993 /*=====
32994 itemCreator: function(nodes, target, source){
32995 // summary:
32996 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
32997 // dropped onto the tree. Developer must override this method to enable
32998 // dropping from external sources onto this Tree, unless the Tree.model's items
32999 // happen to look like {id: 123, name: "Apple" } with no other attributes.
33000 //
33001 // For each node in nodes[], which came from source, create a hash of name/value
33002 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
33003 // nodes: DomNode[]
33004 // The DOMNodes dragged from the source container
33005 // target: DomNode
33006 // The target TreeNode.rowNode
33007 // source: dojo/dnd/Source
33008 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
33009 // returns: Object[]
33010 // Array of name/value hashes for each new item to be added to the Tree, like:
33011 // | [
33012 // | { id: 123, label: "apple", foo: "bar" },
33013 // | { id: 456, label: "pear", zaz: "bam" }
33014 // | ]
33015 // tags:
33016 // extension
33017 return [{}];
33018 },
33019 =====*/
33020
33021 // onDndCancel: [protected] Function
33022 // Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
33023 // Generally this doesn't need to be set.
33024 onDndCancel: null,
33025
33026 /*=====
33027 checkAcceptance: function(source, nodes){
33028 // summary:
33029 // Checks if the Tree itself can accept nodes from this source
33030 // source: dijit/tree/dndSource
33031 // The source which provides items
33032 // nodes: DOMNode[]
33033 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
33034 // source is a dijit/Tree.
33035 // tags:
33036 // extension
33037 return true; // Boolean
33038 },
33039 =====*/
33040 checkAcceptance: null,
33041
33042 /*=====
33043 checkItemAcceptance: function(target, source, position){
33044 // summary:
33045 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
33046 // description:
33047 // In the base case, this is called to check if target can become a child of source.
33048 // When betweenThreshold is set, position="before" or "after" means that we
33049 // are asking if the source node can be dropped before/after the target node.
33050 // target: DOMNode
33051 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
33052 // Use registry.getEnclosingWidget(target) to get the TreeNode.
33053 // source: dijit/tree/dndSource
33054 // The (set of) nodes we are dropping
33055 // position: String
33056 // "over", "before", or "after"
33057 // tags:
33058 // extension
33059 return true; // Boolean
33060 },
33061 =====*/
33062 checkItemAcceptance: null,
33063
33064 // dragThreshold: Integer
33065 // Number of pixels mouse moves before it's considered the start of a drag operation
33066 dragThreshold: 5,
33067
33068 // betweenThreshold: Integer
33069 // Set to a positive value to allow drag and drop "between" nodes.
33070 //
33071 // If during DnD mouse is over a (target) node but less than betweenThreshold
33072 // pixels from the bottom edge, dropping the the dragged node will make it
33073 // the next sibling of the target node, rather than the child.
33074 //
33075 // Similarly, if mouse is over a target node but less that betweenThreshold
33076 // pixels from the top edge, dropping the dragged node will make it
33077 // the target node's previous sibling rather than the target node's child.
33078 betweenThreshold: 0,
33079
33080 // _nodePixelIndent: Integer
33081 // Number of pixels to indent tree nodes (relative to parent node).
33082 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
33083 // and calling resize() or startup() on tree after it's in the DOM.
33084 _nodePixelIndent: 19,
33085
33086 _publish: function(/*String*/ topicName, /*Object*/ message){
33087 // summary:
33088 // Publish a message for this widget/topic
33089 topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
33090 },
33091
33092 postMixInProperties: function(){
33093 this.tree = this;
33094
33095 if(this.autoExpand){
33096 // There's little point in saving opened/closed state of nodes for a Tree
33097 // that initially opens all it's nodes.
33098 this.persist = false;
33099 }
33100
33101 this._itemNodesMap = {};
33102
33103 if(!this.cookieName && this.id){
33104 this.cookieName = this.id + "SaveStateCookie";
33105 }
33106
33107 // Deferred that fires when all the children have loaded.
33108 this.expandChildrenDeferred = new Deferred();
33109
33110 // Deferred that fires when all pending operations complete.
33111 this.pendingCommandsDeferred = this.expandChildrenDeferred;
33112
33113 this.inherited(arguments);
33114 },
33115
33116 postCreate: function(){
33117 this._initState();
33118
33119 // Catch events on TreeNodes
33120 var self = this;
33121 this.own(
33122 on(this.domNode, on.selector(".dijitTreeNode", touch.enter), function(evt){
33123 self._onNodeMouseEnter(registry.byNode(this), evt);
33124 }),
33125 on(this.domNode, on.selector(".dijitTreeNode", touch.leave), function(evt){
33126 self._onNodeMouseLeave(registry.byNode(this), evt);
33127 }),
33128 on(this.domNode, on.selector(".dijitTreeNode", "click"), function(evt){
33129 self._onClick(registry.byNode(this), evt);
33130 }),
33131 on(this.domNode, on.selector(".dijitTreeNode", "dblclick"), function(evt){
33132 self._onDblClick(registry.byNode(this), evt);
33133 }),
33134 on(this.domNode, on.selector(".dijitTreeNode", "keypress"), function(evt){
33135 self._onKeyPress(registry.byNode(this), evt);
33136 }),
33137 on(this.domNode, on.selector(".dijitTreeNode", "keydown"), function(evt){
33138 self._onKeyDown(registry.byNode(this), evt);
33139 }),
33140 on(this.domNode, on.selector(".dijitTreeRow", "focusin"), function(evt){
33141 self._onNodeFocus(registry.getEnclosingWidget(this), evt);
33142 })
33143 );
33144
33145 // Create glue between store and Tree, if not specified directly by user
33146 if(!this.model){
33147 this._store2model();
33148 }
33149
33150 // monitor changes to items
33151 this.connect(this.model, "onChange", "_onItemChange");
33152 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
33153 this.connect(this.model, "onDelete", "_onItemDelete");
33154
33155 this.inherited(arguments);
33156
33157 if(this.dndController){
33158 if(lang.isString(this.dndController)){
33159 this.dndController = lang.getObject(this.dndController);
33160 }
33161 var params={};
33162 for(var i=0; i<this.dndParams.length;i++){
33163 if(this[this.dndParams[i]]){
33164 params[this.dndParams[i]] = this[this.dndParams[i]];
33165 }
33166 }
33167 this.dndController = new this.dndController(this, params);
33168 }
33169
33170 this._load();
33171
33172 // If no path was specified to the constructor, use path saved in cookie
33173 if(!this.params.path && !this.params.paths && this.persist){
33174 this.set("paths", this.dndController._getSavedPaths());
33175 }
33176
33177 // onLoadDeferred should fire when all commands that are part of initialization have completed.
33178 // It will include all the set("paths", ...) commands that happen during initialization.
33179 this.onLoadDeferred = this.pendingCommandsDeferred;
33180
33181 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
33182 },
33183
33184 _store2model: function(){
33185 // summary:
33186 // User specified a store&query rather than model, so create model from store/query
33187 this._v10Compat = true;
33188 kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
33189
33190 var modelParams = {
33191 id: this.id + "_ForestStoreModel",
33192 store: this.store,
33193 query: this.query,
33194 childrenAttrs: this.childrenAttr
33195 };
33196
33197 // Only override the model's mayHaveChildren() method if the user has specified an override
33198 if(this.params.mayHaveChildren){
33199 modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
33200 }
33201
33202 if(this.params.getItemChildren){
33203 modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
33204 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
33205 });
33206 }
33207 this.model = new ForestStoreModel(modelParams);
33208
33209 // For backwards compatibility, the visibility of the root node is controlled by
33210 // whether or not the user has specified a label
33211 this.showRoot = Boolean(this.label);
33212 },
33213
33214 onLoad: function(){
33215 // summary:
33216 // Called when tree finishes loading and expanding.
33217 // description:
33218 // If persist == true the loading may encompass many levels of fetches
33219 // from the data store, each asynchronous. Waits for all to finish.
33220 // tags:
33221 // callback
33222 },
33223
33224 _load: function(){
33225 // summary:
33226 // Initial load of the tree.
33227 // Load root node (possibly hidden) and it's children.
33228 this.model.getRoot(
33229 lang.hitch(this, function(item){
33230 var rn = (this.rootNode = this.tree._createTreeNode({
33231 item: item,
33232 tree: this,
33233 isExpandable: true,
33234 label: this.label || this.getLabel(item),
33235 textDir: this.textDir,
33236 indent: this.showRoot ? 0 : -1
33237 }));
33238
33239 if(!this.showRoot){
33240 rn.rowNode.style.display="none";
33241 // if root is not visible, move tree role to the invisible
33242 // root node's containerNode, see #12135
33243 this.domNode.setAttribute("role", "presentation");
33244 this.domNode.removeAttribute("aria-expanded");
33245 this.domNode.removeAttribute("aria-multiselectable");
33246
33247 rn.labelNode.setAttribute("role", "presentation");
33248 rn.containerNode.setAttribute("role", "tree");
33249 rn.containerNode.setAttribute("aria-expanded","true");
33250 rn.containerNode.setAttribute("aria-multiselectable", !this.dndController.singular);
33251 }else{
33252 this.domNode.setAttribute("aria-multiselectable", !this.dndController.singular);
33253 }
33254
33255 this.domNode.appendChild(rn.domNode);
33256 var identity = this.model.getIdentity(item);
33257 if(this._itemNodesMap[identity]){
33258 this._itemNodesMap[identity].push(rn);
33259 }else{
33260 this._itemNodesMap[identity] = [rn];
33261 }
33262
33263 rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
33264
33265 // Load top level children, and if persist==true, all nodes that were previously opened
33266 this._expandNode(rn).then(lang.hitch(this, function(){
33267 // Then, select the nodes that were selected last time, or
33268 // the ones specified by params.paths[].
33269
33270 this.expandChildrenDeferred.resolve(true);
33271 }));
33272 }),
33273 lang.hitch(this, function(err){
33274 console.error(this, ": error loading root: ", err);
33275 })
33276 );
33277 },
33278
33279 getNodesByItem: function(/*Item or id*/ item){
33280 // summary:
33281 // Returns all tree nodes that refer to an item
33282 // returns:
33283 // Array of tree nodes that refer to passed item
33284
33285 if(!item){ return []; }
33286 var identity = lang.isString(item) ? item : this.model.getIdentity(item);
33287 // return a copy so widget don't get messed up by changes to returned array
33288 return [].concat(this._itemNodesMap[identity]);
33289 },
33290
33291 _setSelectedItemAttr: function(/*Item or id*/ item){
33292 this.set('selectedItems', [item]);
33293 },
33294
33295 _setSelectedItemsAttr: function(/*Items or ids*/ items){
33296 // summary:
33297 // Select tree nodes related to passed items.
33298 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
33299 // behavior is undefined. Use set('paths', ...) instead.
33300 var tree = this;
33301 return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then( lang.hitch(this, function(){
33302 var identities = array.map(items, function(item){
33303 return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
33304 });
33305 var nodes = [];
33306 array.forEach(identities, function(id){
33307 nodes = nodes.concat(tree._itemNodesMap[id] || []);
33308 });
33309 this.set('selectedNodes', nodes);
33310 }));
33311 },
33312
33313 _setPathAttr: function(/*Item[]|String[]*/ path){
33314 // summary:
33315 // Singular variant of _setPathsAttr
33316 if(path.length){
33317 return this.set("paths", [path]);
33318 }else{
33319 // Empty list is interpreted as "select nothing"
33320 return this.set("paths", []);
33321 }
33322 },
33323
33324 _setPathsAttr: function(/*Item[][]|String[][]*/ paths){
33325 // summary:
33326 // Select the tree nodes identified by passed paths.
33327 // paths:
33328 // Array of arrays of items or item id's
33329 // returns:
33330 // Deferred to indicate when the set is complete
33331
33332 var tree = this;
33333
33334 // Let any previous set("path", ...) commands complete before this one starts.
33335 return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then(function(){
33336 // We may need to wait for some nodes to expand, so setting
33337 // each path will involve a Deferred. We bring those deferreds
33338 // together with a DeferredList.
33339 return new DeferredList(array.map(paths, function(path){
33340 var d = new Deferred();
33341
33342 // normalize path to use identity
33343 path = array.map(path, function(item){
33344 return lang.isString(item) ? item : tree.model.getIdentity(item);
33345 });
33346
33347 if(path.length){
33348 // Wait for the tree to load, if it hasn't already.
33349 selectPath(path, [tree.rootNode], d);
33350 }else{
33351 d.reject(new Tree.PathError("Empty path"));
33352 }
33353 return d;
33354 }));
33355 }).then(setNodes);
33356
33357 function selectPath(path, nodes, def){
33358 // Traverse path; the next path component should be among "nodes".
33359 var nextPath = path.shift();
33360 var nextNode = array.filter(nodes, function(node){
33361 return node.getIdentity() == nextPath;
33362 })[0];
33363 if(!!nextNode){
33364 if(path.length){
33365 tree._expandNode(nextNode).then(function(){ selectPath(path, nextNode.getChildren(), def); });
33366 }else{
33367 // Successfully reached the end of this path
33368 def.resolve(nextNode);
33369 }
33370 }else{
33371 def.reject(new Tree.PathError("Could not expand path at " + nextPath));
33372 }
33373 }
33374
33375 function setNodes(newNodes){
33376 // After all expansion is finished, set the selection to
33377 // the set of nodes successfully found.
33378 tree.set("selectedNodes", array.map(
33379 array.filter(newNodes,function(x){return x[0];}),
33380 function(x){return x[1];}));
33381 }
33382 },
33383
33384 _setSelectedNodeAttr: function(node){
33385 this.set('selectedNodes', [node]);
33386 },
33387 _setSelectedNodesAttr: function(nodes){
33388 // summary:
33389 // Marks the specified TreeNodes as selected.
33390 // nodes: TreeNode[]
33391 // TreeNodes to mark.
33392 this.dndController.setSelection(nodes);
33393 },
33394
33395
33396 expandAll: function(){
33397 // summary:
33398 // Expand all nodes in the tree
33399 // returns:
33400 // Deferred that fires when all nodes have expanded
33401
33402 var _this = this;
33403
33404 function expand(node){
33405 var def = new dojo.Deferred();
33406
33407 // Expand the node
33408 _this._expandNode(node).then(function(){
33409 // When node has expanded, call expand() recursively on each non-leaf child
33410 var childBranches = array.filter(node.getChildren() || [], function(node){
33411 return node.isExpandable;
33412 }),
33413 defs = array.map(childBranches, expand);
33414
33415 // And when all those recursive calls finish, signal that I'm finished
33416 new dojo.DeferredList(defs).then(function(){
33417 def.resolve(true);
33418 });
33419 });
33420
33421 return def;
33422 }
33423
33424 return expand(this.rootNode);
33425 },
33426
33427 collapseAll: function(){
33428 // summary:
33429 // Collapse all nodes in the tree
33430 // returns:
33431 // Deferred that fires when all nodes have collapsed
33432
33433 var _this = this;
33434
33435 function collapse(node){
33436 var def = new dojo.Deferred();
33437 def.label = "collapseAllDeferred";
33438
33439 // Collapse children first
33440 var childBranches = array.filter(node.getChildren() || [], function(node){
33441 return node.isExpandable;
33442 }),
33443 defs = array.map(childBranches, collapse);
33444
33445 // And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
33446 // in which case collapseAll() is finished
33447 new dojo.DeferredList(defs).then(function(){
33448 if(!node.isExpanded || (node == _this.rootNode && !_this.showRoot)){
33449 def.resolve(true);
33450 }else{
33451 _this._collapseNode(node).then(function(){
33452 // When node has collapsed, signal that call is finished
33453 def.resolve(true);
33454 });
33455 }
33456 });
33457
33458
33459 return def;
33460 }
33461
33462 return collapse(this.rootNode);
33463 },
33464
33465 ////////////// Data store related functions //////////////////////
33466 // These just get passed to the model; they are here for back-compat
33467
33468 mayHaveChildren: function(/*dojo/data/Item*/ /*===== item =====*/){
33469 // summary:
33470 // Deprecated. This should be specified on the model itself.
33471 //
33472 // Overridable function to tell if an item has or may have children.
33473 // Controls whether or not +/- expando icon is shown.
33474 // (For efficiency reasons we may not want to check if an element actually
33475 // has children until user clicks the expando node)
33476 // tags:
33477 // deprecated
33478 },
33479
33480 getItemChildren: function(/*===== parentItem, onComplete =====*/){
33481 // summary:
33482 // Deprecated. This should be specified on the model itself.
33483 //
33484 // Overridable function that return array of child items of given parent item,
33485 // or if parentItem==null then return top items in tree
33486 // tags:
33487 // deprecated
33488 },
33489
33490 ///////////////////////////////////////////////////////
33491 // Functions for converting an item to a TreeNode
33492 getLabel: function(/*dojo/data/Item*/ item){
33493 // summary:
33494 // Overridable function to get the label for a tree node (given the item)
33495 // tags:
33496 // extension
33497 return this.model.getLabel(item); // String
33498 },
33499
33500 getIconClass: function(/*dojo/data/Item*/ item, /*Boolean*/ opened){
33501 // summary:
33502 // Overridable function to return CSS class name to display icon
33503 // tags:
33504 // extension
33505 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
33506 },
33507
33508 getLabelClass: function(/*===== item, opened =====*/){
33509 // summary:
33510 // Overridable function to return CSS class name to display label
33511 // item: dojo/data/Item
33512 // opened: Boolean
33513 // returns: String
33514 // CSS class name
33515 // tags:
33516 // extension
33517 },
33518
33519 getRowClass: function(/*===== item, opened =====*/){
33520 // summary:
33521 // Overridable function to return CSS class name to display row
33522 // item: dojo/data/Item
33523 // opened: Boolean
33524 // returns: String
33525 // CSS class name
33526 // tags:
33527 // extension
33528 },
33529
33530 getIconStyle: function(/*===== item, opened =====*/){
33531 // summary:
33532 // Overridable function to return CSS styles to display icon
33533 // item: dojo/data/Item
33534 // opened: Boolean
33535 // returns: Object
33536 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
33537 // tags:
33538 // extension
33539 },
33540
33541 getLabelStyle: function(/*===== item, opened =====*/){
33542 // summary:
33543 // Overridable function to return CSS styles to display label
33544 // item: dojo/data/Item
33545 // opened: Boolean
33546 // returns:
33547 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
33548 // tags:
33549 // extension
33550 },
33551
33552 getRowStyle: function(/*===== item, opened =====*/){
33553 // summary:
33554 // Overridable function to return CSS styles to display row
33555 // item: dojo/data/Item
33556 // opened: Boolean
33557 // returns:
33558 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
33559 // tags:
33560 // extension
33561 },
33562
33563 getTooltip: function(/*dojo/data/Item*/ /*===== item =====*/){
33564 // summary:
33565 // Overridable function to get the tooltip for a tree node (given the item)
33566 // tags:
33567 // extension
33568 return ""; // String
33569 },
33570
33571 /////////// Keyboard and Mouse handlers ////////////////////
33572
33573 _onKeyPress: function(/*TreeNode*/ treeNode, /*Event*/ e){
33574 // summary:
33575 // Handles keystrokes for printable keys, doing search navigation
33576
33577 if(e.charCode <= 32){
33578 // Avoid duplicate events on firefox (this is an arrow key that will be handled by keydown handler)
33579 return;
33580 }
33581
33582 if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
33583 var c = String.fromCharCode(e.charCode);
33584 this._onLetterKeyNav( { node: treeNode, key: c.toLowerCase() } );
33585 event.stop(e);
33586 }
33587 },
33588
33589 _onKeyDown: function(/*TreeNode*/ treeNode, /*Event*/ e){
33590 // summary:
33591 // Handles arrow, space, and enter keys
33592
33593 var key = e.keyCode;
33594
33595 var map = this._keyHandlerMap;
33596 if(!map){
33597 // Setup table mapping keys to events.
33598 // On WebKit based browsers, the combination ctrl-enter does not get passed through. To allow accessible
33599 // multi-select on those browsers, the space key is also used for selection.
33600 // Therefore, also allow space key for keyboard "click" operation.
33601 map = {};
33602 map[keys.ENTER] = map[keys.SPACE] = map[" "] = "_onEnterKey";
33603 map[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW] = "_onLeftArrow";
33604 map[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW] = "_onRightArrow";
33605 map[keys.UP_ARROW] = "_onUpArrow";
33606 map[keys.DOWN_ARROW] = "_onDownArrow";
33607 map[keys.HOME] = "_onHomeKey";
33608 map[keys.END] = "_onEndKey";
33609 this._keyHandlerMap = map;
33610 }
33611
33612 if(this._keyHandlerMap[key]){
33613 // clear record of recent printables (being saved for multi-char letter navigation),
33614 // because "a", down-arrow, "b" shouldn't search for "ab"
33615 if(this._curSearch){
33616 this._curSearch.timer.remove();
33617 delete this._curSearch;
33618 }
33619
33620 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
33621 event.stop(e);
33622 }
33623 },
33624
33625 _onEnterKey: function(/*Object*/ message){
33626 this._publish("execute", { item: message.item, node: message.node } );
33627 this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
33628 this.onClick(message.item, message.node, message.evt);
33629 },
33630
33631 _onDownArrow: function(/*Object*/ message){
33632 // summary:
33633 // down arrow pressed; get next visible node, set focus there
33634 var node = this._getNextNode(message.node);
33635 if(node && node.isTreeNode){
33636 this.focusNode(node);
33637 }
33638 },
33639
33640 _onUpArrow: function(/*Object*/ message){
33641 // summary:
33642 // Up arrow pressed; move to previous visible node
33643
33644 var node = message.node;
33645
33646 // if younger siblings
33647 var previousSibling = node.getPreviousSibling();
33648 if(previousSibling){
33649 node = previousSibling;
33650 // if the previous node is expanded, dive in deep
33651 while(node.isExpandable && node.isExpanded && node.hasChildren()){
33652 // move to the last child
33653 var children = node.getChildren();
33654 node = children[children.length-1];
33655 }
33656 }else{
33657 // if this is the first child, return the parent
33658 // unless the parent is the root of a tree with a hidden root
33659 var parent = node.getParent();
33660 if(!(!this.showRoot && parent === this.rootNode)){
33661 node = parent;
33662 }
33663 }
33664
33665 if(node && node.isTreeNode){
33666 this.focusNode(node);
33667 }
33668 },
33669
33670 _onRightArrow: function(/*Object*/ message){
33671 // summary:
33672 // Right arrow pressed; go to child node
33673 var node = message.node;
33674
33675 // if not expanded, expand, else move to 1st child
33676 if(node.isExpandable && !node.isExpanded){
33677 this._expandNode(node);
33678 }else if(node.hasChildren()){
33679 node = node.getChildren()[0];
33680 if(node && node.isTreeNode){
33681 this.focusNode(node);
33682 }
33683 }
33684 },
33685
33686 _onLeftArrow: function(/*Object*/ message){
33687 // summary:
33688 // Left arrow pressed.
33689 // If not collapsed, collapse, else move to parent.
33690
33691 var node = message.node;
33692
33693 if(node.isExpandable && node.isExpanded){
33694 this._collapseNode(node);
33695 }else{
33696 var parent = node.getParent();
33697 if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
33698 this.focusNode(parent);
33699 }
33700 }
33701 },
33702
33703 _onHomeKey: function(){
33704 // summary:
33705 // Home key pressed; get first visible node, and set focus there
33706 var node = this._getRootOrFirstNode();
33707 if(node){
33708 this.focusNode(node);
33709 }
33710 },
33711
33712 _onEndKey: function(){
33713 // summary:
33714 // End key pressed; go to last visible node.
33715
33716 var node = this.rootNode;
33717 while(node.isExpanded){
33718 var c = node.getChildren();
33719 node = c[c.length - 1];
33720 }
33721
33722 if(node && node.isTreeNode){
33723 this.focusNode(node);
33724 }
33725 },
33726
33727 // multiCharSearchDuration: Number
33728 // If multiple characters are typed where each keystroke happens within
33729 // multiCharSearchDuration of the previous keystroke,
33730 // search for nodes matching all the keystrokes.
33731 //
33732 // For example, typing "ab" will search for entries starting with
33733 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
33734 multiCharSearchDuration: 250,
33735
33736 _onLetterKeyNav: function(message){
33737 // summary:
33738 // Called when user presses a prinatable key; search for node starting with recently typed letters.
33739 // message: Object
33740 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
33741
33742 // Branch depending on whether this key starts a new search, or modifies an existing search
33743 var cs = this._curSearch;
33744 if(cs){
33745 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
33746 // 'b', so we want to search for nodes starting w/"ab".
33747 cs.pattern = cs.pattern + message.key;
33748 cs.timer.remove();
33749 }else{
33750 // We are starting a new search
33751 cs = this._curSearch = {
33752 pattern: message.key,
33753 startNode: message.node
33754 };
33755 }
33756
33757 // set/reset timer to forget recent keystrokes
33758 cs.timer = this.defer(function(){
33759 delete this._curSearch;
33760 }, this.multiCharSearchDuration);
33761
33762 // Navigate to TreeNode matching keystrokes [entered so far].
33763 var node = cs.startNode;
33764 do{
33765 node = this._getNextNode(node);
33766 //check for last node, jump to first node if necessary
33767 if(!node){
33768 node = this._getRootOrFirstNode();
33769 }
33770 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
33771 if(node && node.isTreeNode){
33772 // no need to set focus if back where we started
33773 if(node !== cs.startNode){
33774 this.focusNode(node);
33775 }
33776 }
33777 },
33778
33779 isExpandoNode: function(node, widget){
33780 // summary:
33781 // check whether a dom node is the expandoNode for a particular TreeNode widget
33782 return dom.isDescendant(node, widget.expandoNode) || dom.isDescendant(node, widget.expandoNodeText);
33783 },
33784
33785 _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
33786 // summary:
33787 // Translates click events into commands for the controller to process
33788
33789 var domElement = e.target,
33790 isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
33791
33792 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
33793 // expando node was clicked, or label of a folder node was clicked; open it
33794 if(nodeWidget.isExpandable){
33795 this._onExpandoClick({node:nodeWidget});
33796 }
33797 }else{
33798 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
33799 this.onClick(nodeWidget.item, nodeWidget, e);
33800 this.focusNode(nodeWidget);
33801 }
33802 event.stop(e);
33803 },
33804 _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
33805 // summary:
33806 // Translates double-click events into commands for the controller to process
33807
33808 var domElement = e.target,
33809 isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
33810
33811 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
33812 // expando node was clicked, or label of a folder node was clicked; open it
33813 if(nodeWidget.isExpandable){
33814 this._onExpandoClick({node:nodeWidget});
33815 }
33816 }else{
33817 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
33818 this.onDblClick(nodeWidget.item, nodeWidget, e);
33819 this.focusNode(nodeWidget);
33820 }
33821 event.stop(e);
33822 },
33823
33824 _onExpandoClick: function(/*Object*/ message){
33825 // summary:
33826 // User clicked the +/- icon; expand or collapse my children.
33827 var node = message.node;
33828
33829 // If we are collapsing, we might be hiding the currently focused node.
33830 // Also, clicking the expando node might have erased focus from the current node.
33831 // For simplicity's sake just focus on the node with the expando.
33832 this.focusNode(node);
33833
33834 if(node.isExpanded){
33835 this._collapseNode(node);
33836 }else{
33837 this._expandNode(node);
33838 }
33839 },
33840
33841 onClick: function(/*===== item, node, evt =====*/){
33842 // summary:
33843 // Callback when a tree node is clicked
33844 // item: Object
33845 // Object from the dojo/store corresponding to this TreeNode
33846 // node: TreeNode
33847 // The TreeNode itself
33848 // evt: Event
33849 // The event
33850 // tags:
33851 // callback
33852 },
33853 onDblClick: function(/*===== item, node, evt =====*/){
33854 // summary:
33855 // Callback when a tree node is double-clicked
33856 // item: Object
33857 // Object from the dojo/store corresponding to this TreeNode
33858 // node: TreeNode
33859 // The TreeNode itself
33860 // evt: Event
33861 // The event
33862 // tags:
33863 // callback
33864 },
33865 onOpen: function(/*===== item, node =====*/){
33866 // summary:
33867 // Callback when a node is opened
33868 // item: dojo/data/Item
33869 // node: TreeNode
33870 // tags:
33871 // callback
33872 },
33873 onClose: function(/*===== item, node =====*/){
33874 // summary:
33875 // Callback when a node is closed
33876 // item: Object
33877 // Object from the dojo/store corresponding to this TreeNode
33878 // node: TreeNode
33879 // The TreeNode itself
33880 // tags:
33881 // callback
33882 },
33883
33884 _getNextNode: function(node){
33885 // summary:
33886 // Get next visible node
33887
33888 if(node.isExpandable && node.isExpanded && node.hasChildren()){
33889 // if this is an expanded node, get the first child
33890 return node.getChildren()[0]; // TreeNode
33891 }else{
33892 // find a parent node with a sibling
33893 while(node && node.isTreeNode){
33894 var returnNode = node.getNextSibling();
33895 if(returnNode){
33896 return returnNode; // TreeNode
33897 }
33898 node = node.getParent();
33899 }
33900 return null;
33901 }
33902 },
33903
33904 _getRootOrFirstNode: function(){
33905 // summary:
33906 // Get first visible node
33907 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
33908 },
33909
33910 _collapseNode: function(/*TreeNode*/ node){
33911 // summary:
33912 // Called when the user has requested to collapse the node
33913 // returns:
33914 // Deferred that fires when the node is closed
33915
33916 if(node._expandNodeDeferred){
33917 delete node._expandNodeDeferred;
33918 }
33919
33920 if(node.state == "LOADING"){
33921 // ignore clicks while we are in the process of loading data
33922 return;
33923 }
33924
33925 if(node.isExpanded){
33926 var ret = node.collapse();
33927
33928 this.onClose(node.item, node);
33929 this._state(node, false);
33930
33931 this._startPaint(ret); // after this finishes, need to reset widths of TreeNodes
33932
33933 return ret;
33934 }
33935 },
33936
33937 _expandNode: function(/*TreeNode*/ node){
33938 // summary:
33939 // Called when the user has requested to expand the node
33940 // returns:
33941 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
33942 // that were previously opened too
33943
33944 // Signal that this call is complete
33945 var def = new Deferred();
33946
33947 if(node._expandNodeDeferred){
33948 // there's already an expand in progress, or completed, so just return
33949 return node._expandNodeDeferred; // dojo/_base/Deferred
33950 }
33951
33952 var model = this.model,
33953 item = node.item,
33954 _this = this;
33955
33956 // Load data if it's not already loaded
33957 if(!node._loadDeferred){
33958 // need to load all the children before expanding
33959 node.markProcessing();
33960
33961 // Setup deferred to signal when the load and expand are finished.
33962 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
33963 node._loadDeferred = new Deferred();
33964
33965 // Get the children
33966 model.getChildren(
33967 item,
33968 function(items){
33969 node.unmarkProcessing();
33970
33971 // Display the children and also start expanding any children that were previously expanded
33972 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
33973 node.setChildItems(items).then(function(){
33974 node._loadDeferred.resolve(items);
33975 });
33976 },
33977 function(err){
33978 console.error(_this, ": error loading " + node.label + " children: ", err);
33979 node._loadDeferred.reject(err);
33980 }
33981 );
33982 }
33983
33984 // Expand the node after data has loaded
33985 node._loadDeferred.then(lang.hitch(this, function(){
33986 node.expand().then(function(){
33987 def.resolve(true); // signal that this _expandNode() call is complete
33988 });
33989
33990 // seems like these should be inside of then(), but left here for back-compat about
33991 // when this.isOpen flag gets set (ie, at the beginning of the animation)
33992 this.onOpen(node.item, node);
33993 this._state(node, true);
33994 }));
33995
33996 this._startPaint(def); // after this finishes, need to reset widths of TreeNodes
33997
33998 return def; // dojo/_base/Deferred
33999 },
34000
34001 ////////////////// Miscellaneous functions ////////////////
34002
34003 focusNode: function(/* _tree.Node */ node){
34004 // summary:
34005 // Focus on the specified node (which must be visible)
34006 // tags:
34007 // protected
34008
34009 // set focus so that the label will be voiced using screen readers
34010 focus.focus(node.labelNode);
34011 },
34012
34013 _onNodeFocus: function(/*dijit/_WidgetBase*/ node){
34014 // summary:
34015 // Called when a TreeNode gets focus, either by user clicking
34016 // it, or programatically by arrow key handling code.
34017 // description:
34018 // It marks that the current node is the selected one, and the previously
34019 // selected node no longer is.
34020
34021 if(node && node != this.lastFocused){
34022 if(this.lastFocused && !this.lastFocused._destroyed){
34023 // mark that the previously focsable node is no longer focusable
34024 this.lastFocused.setFocusable(false);
34025 }
34026
34027 // mark that the new node is the currently selected one
34028 node.setFocusable(true);
34029 this.lastFocused = node;
34030 }
34031 },
34032
34033 _onNodeMouseEnter: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
34034 // summary:
34035 // Called when mouse is over a node (onmouseenter event),
34036 // this is monitored by the DND code
34037 },
34038
34039 _onNodeMouseLeave: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
34040 // summary:
34041 // Called when mouse leaves a node (onmouseleave event),
34042 // this is monitored by the DND code
34043 },
34044
34045 //////////////// Events from the model //////////////////////////
34046
34047 _onItemChange: function(/*Item*/ item){
34048 // summary:
34049 // Processes notification of a change to an item's scalar values like label
34050 var model = this.model,
34051 identity = model.getIdentity(item),
34052 nodes = this._itemNodesMap[identity];
34053
34054 if(nodes){
34055 var label = this.getLabel(item),
34056 tooltip = this.getTooltip(item);
34057 array.forEach(nodes, function(node){
34058 node.set({
34059 item: item, // theoretically could be new JS Object representing same item
34060 label: label,
34061 tooltip: tooltip
34062 });
34063 node._updateItemClasses(item);
34064 });
34065 }
34066 },
34067
34068 _onItemChildrenChange: function(/*dojo/data/Item*/ parent, /*dojo/data/Item[]*/ newChildrenList){
34069 // summary:
34070 // Processes notification of a change to an item's children
34071 var model = this.model,
34072 identity = model.getIdentity(parent),
34073 parentNodes = this._itemNodesMap[identity];
34074
34075 if(parentNodes){
34076 array.forEach(parentNodes,function(parentNode){
34077 parentNode.setChildItems(newChildrenList);
34078 });
34079 }
34080 },
34081
34082 _onItemDelete: function(/*Item*/ item){
34083 // summary:
34084 // Processes notification of a deletion of an item.
34085 // Not called from new dojo.store interface but there's cleanup code in setChildItems() instead.
34086
34087 var model = this.model,
34088 identity = model.getIdentity(item),
34089 nodes = this._itemNodesMap[identity];
34090
34091 if(nodes){
34092 array.forEach(nodes,function(node){
34093 // Remove node from set of selected nodes (if it's selected)
34094 this.dndController.removeTreeNode(node);
34095
34096 var parent = node.getParent();
34097 if(parent){
34098 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
34099 parent.removeChild(node);
34100 }
34101 node.destroyRecursive();
34102 }, this);
34103 delete this._itemNodesMap[identity];
34104 }
34105 },
34106
34107 /////////////// Miscellaneous funcs
34108
34109 _initState: function(){
34110 // summary:
34111 // Load in which nodes should be opened automatically
34112 this._openedNodes = {};
34113 if(this.persist && this.cookieName){
34114 var oreo = cookie(this.cookieName);
34115 if(oreo){
34116 array.forEach(oreo.split(','), function(item){
34117 this._openedNodes[item] = true;
34118 }, this);
34119 }
34120 }
34121 },
34122 _state: function(node, expanded){
34123 // summary:
34124 // Query or set expanded state for an node
34125 if(!this.persist){
34126 return false;
34127 }
34128 var path = array.map(node.getTreePath(), function(item){
34129 return this.model.getIdentity(item);
34130 }, this).join("/");
34131 if(arguments.length === 1){
34132 return this._openedNodes[path];
34133 }else{
34134 if(expanded){
34135 this._openedNodes[path] = true;
34136 }else{
34137 delete this._openedNodes[path];
34138 }
34139 if(this.persist && this.cookieName){
34140 var ary = [];
34141 for(var id in this._openedNodes){
34142 ary.push(id);
34143 }
34144 cookie(this.cookieName, ary.join(","), {expires:365});
34145 }
34146 }
34147 },
34148
34149 destroy: function(){
34150 if(this._curSearch){
34151 this._curSearch.timer.remove();
34152 delete this._curSearch;
34153 }
34154 if(this.rootNode){
34155 this.rootNode.destroyRecursive();
34156 }
34157 if(this.dndController && !lang.isString(this.dndController)){
34158 this.dndController.destroy();
34159 }
34160 this.rootNode = null;
34161 this.inherited(arguments);
34162 },
34163
34164 destroyRecursive: function(){
34165 // A tree is treated as a leaf, not as a node with children (like a grid),
34166 // but defining destroyRecursive for back-compat.
34167 this.destroy();
34168 },
34169
34170 resize: function(changeSize){
34171 if(changeSize){
34172 domGeometry.setMarginBox(this.domNode, changeSize);
34173 }
34174
34175 // The main JS sizing involved w/tree is the indentation, which is specified
34176 // in CSS and read in through this dummy indentDetector node (tree must be
34177 // visible and attached to the DOM to read this).
34178 // If the Tree is hidden domGeometry.position(this.tree.indentDetector).w will return 0, in which case just
34179 // keep the default value.
34180 this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w || this._nodePixelIndent;
34181
34182 // resize() may be called before this.rootNode is created, so wait until it's available
34183 this.expandChildrenDeferred.then(lang.hitch(this, function(){
34184 // If tree has already loaded, then reset indent for all the nodes
34185 this.rootNode.set('indent', this.showRoot ? 0 : -1);
34186
34187 // Also, adjust widths of all rows to match width of Tree
34188 this._adjustWidths();
34189 }));
34190 },
34191
34192 _outstandingPaintOperations: 0,
34193 _startPaint: function(/*Promise|Boolean*/ p){
34194 // summary:
34195 // Called at the start of an operation that will change what's displayed.
34196 // p:
34197 // Promise that tells when the operation will complete. Alternately, if it's just a Boolean, it signifies
34198 // that the operation was synchronous, and already completed.
34199
34200 this._outstandingPaintOperations++;
34201 if(this._adjustWidthsTimer){
34202 this._adjustWidthsTimer.remove();
34203 delete this._adjustWidthsTimer;
34204 }
34205
34206 var oc = lang.hitch(this, function(){
34207 this._outstandingPaintOperations--;
34208
34209 if(this._outstandingPaintOperations <= 0 && !this._adjustWidthsTimer && this._started){
34210 // Use defer() to avoid a width adjustment when another operation will immediately follow,
34211 // such as a sequence of opening a node, then it's children, then it's grandchildren, etc.
34212 this._adjustWidthsTimer = this.defer("_adjustWidths");
34213 }
34214 });
34215 when(p, oc, oc);
34216 },
34217
34218 _adjustWidths: function(){
34219 // summary:
34220 // Get width of widest TreeNode, or the width of the Tree itself, whichever is greater,
34221 // and then set all TreeNodes to that width, so that selection/hover highlighting
34222 // extends to the edge of the Tree (#13141)
34223
34224 if(this._adjustWidthsTimer){
34225 this._adjustWidthsTimer.remove();
34226 delete this._adjustWidthsTimer;
34227 }
34228
34229 var maxWidth = 0,
34230 nodes = [];
34231 function collect(/*TreeNode*/ parent){
34232 var node = parent.rowNode;
34233 node.style.width = "auto"; // erase setting from previous run
34234 maxWidth = Math.max(maxWidth, node.clientWidth);
34235 nodes.push(node);
34236 if(parent.isExpanded){
34237 array.forEach(parent.getChildren(), collect);
34238 }
34239 }
34240 collect(this.rootNode);
34241 maxWidth = Math.max(maxWidth, domGeometry.getContentBox(this.domNode).w); // do after node.style.width="auto"
34242 array.forEach(nodes, function(node){
34243 node.style.width = maxWidth + "px"; // assumes no horizontal padding, border, or margin on rowNode
34244 });
34245 },
34246
34247 _createTreeNode: function(/*Object*/ args){
34248 // summary:
34249 // creates a TreeNode
34250 // description:
34251 // Developers can override this method to define their own TreeNode class;
34252 // However it will probably be removed in a future release in favor of a way
34253 // of just specifying a widget for the label, rather than one that contains
34254 // the children too.
34255 return new TreeNode(args);
34256 },
34257
34258 _setTextDirAttr: function(textDir){
34259 if(textDir && this.textDir!= textDir){
34260 this._set("textDir",textDir);
34261 this.rootNode.set("textDir", textDir);
34262 }
34263 }
34264 });
34265
34266 Tree.PathError = createError("TreePathError");
34267 Tree._TreeNode = TreeNode; // for monkey patching or creating subclasses of TreeNode
34268
34269 return Tree;
34270 });
34271
34272 },
34273 'dijit/form/_FormValueWidget':function(){
34274 define("dijit/form/_FormValueWidget", [
34275 "dojo/_base/declare", // declare
34276 "dojo/sniff", // has("ie")
34277 "./_FormWidget",
34278 "./_FormValueMixin"
34279 ], function(declare, has, _FormWidget, _FormValueMixin){
34280
34281 // module:
34282 // dijit/form/_FormValueWidget
34283
34284 return declare("dijit.form._FormValueWidget", [_FormWidget, _FormValueMixin],
34285 {
34286 // summary:
34287 // Base class for widgets corresponding to native HTML elements such as `<input>` or `<select>`
34288 // that have user changeable values.
34289 // description:
34290 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) `<input>` element,
34291 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
34292 // works as expected.
34293
34294 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
34295 // directly in the template as read by the parser in order to function. IE is known to specifically
34296 // require the 'name' attribute at element creation time. See #8484, #8660.
34297
34298 _layoutHackIE7: function(){
34299 // summary:
34300 // Work around table sizing bugs on IE7 by forcing redraw
34301
34302 if(has("ie") == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
34303 var domNode = this.domNode;
34304 var parent = domNode.parentNode;
34305 var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
34306 var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
34307 var _this = this;
34308 while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
34309 (function ping(){
34310 var disconnectHandle = _this.connect(parent, "onscroll",
34311 function(){
34312 _this.disconnect(disconnectHandle); // only call once
34313 pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
34314 _this.defer(function(){ pingNode.style.filter = origFilter; }); // restore custom filter, if any
34315 }
34316 );
34317 })();
34318 parent = parent.parentNode;
34319 }
34320 }
34321 }
34322 });
34323
34324 });
34325
34326 },
34327 '*now':function(r){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de","el","en-gb","en-us","es-es","fi-fi","fr-fr","he-il","hu","it-it","ja-jp","ko-kr","nl-nl","nb","pl","pt-br","pt-pt","ru","sk","sl","sv","th","tr","zh-tw","zh-cn","ROOT"]']);}
34328 }});
34329 define("dojo/tt-rss-layer", [], 1);