]> git.wh0rd.org - tt-rss.git/blob - lib/dojo/tt-rss-layer.js.uncompressed.js
upgrade dojo to 1.8.3 (refs #570)
[tt-rss.git] / lib / dojo / tt-rss-layer.js.uncompressed.js
1 require({cache:{
2 'dijit/form/TextBox':function(){
3 require({cache:{
4 '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"}});
5 define("dijit/form/TextBox", [
6 "dojo/_base/declare", // declare
7 "dojo/dom-construct", // domConstruct.create
8 "dojo/dom-style", // domStyle.getComputedStyle
9 "dojo/_base/kernel", // kernel.deprecated
10 "dojo/_base/lang", // lang.hitch
11 "dojo/sniff", // has("ie") has("mozilla")
12 "./_FormValueWidget",
13 "./_TextBoxMixin",
14 "dojo/text!./templates/TextBox.html",
15 "../main" // to export dijit._setSelectionRange, remove in 2.0
16 ], function(declare, domConstruct, domStyle, kernel, lang, has,
17 _FormValueWidget, _TextBoxMixin, template, dijit){
18
19 // module:
20 // dijit/form/TextBox
21
22 var TextBox = declare("dijit.form.TextBox", [_FormValueWidget, _TextBoxMixin], {
23 // summary:
24 // A base class for textbox form inputs
25
26 templateString: template,
27 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
28
29 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
30
31 baseClass: "dijitTextBox",
32
33 postMixInProperties: function(){
34 var type = this.type.toLowerCase();
35 if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
36 this.templateString = this._singleNodeTemplate;
37 }
38 this.inherited(arguments);
39 },
40
41 postCreate: function(){
42 this.inherited(arguments);
43
44 if(has("ie") < 9){
45 // IE INPUT tag fontFamily has to be set directly using STYLE
46 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
47 this.defer(function(){
48 try{
49 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
50 if(s){
51 var ff = s.fontFamily;
52 if(ff){
53 var inputs = this.domNode.getElementsByTagName("INPUT");
54 if(inputs){
55 for(var i=0; i < inputs.length; i++){
56 inputs[i].style.fontFamily = ff;
57 }
58 }
59 }
60 }
61 }catch(e){/*when used in a Dialog, and this is called before the dialog is
62 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
63 });
64 }
65 },
66
67 _onInput: function(e){
68 this.inherited(arguments);
69 if(this.intermediateChanges){ // _TextBoxMixin uses onInput
70 // allow the key to post to the widget input box
71 this.defer(function(){ this._handleOnChange(this.get('value'), false); });
72 }
73 },
74
75 _setPlaceHolderAttr: function(v){
76 this._set("placeHolder", v);
77 if(!this._phspan){
78 this._attachPoints.push('_phspan');
79 // dijitInputField class gives placeHolder same padding as the input field
80 // parent node already has dijitInputField class but it doesn't affect this <span>
81 // since it's position: absolute.
82 this._phspan = domConstruct.create('span',{ onmousedown:function(e){ e.preventDefault(); }, className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
83 }
84 this._phspan.innerHTML="";
85 this._phspan.appendChild(this._phspan.ownerDocument.createTextNode(v));
86 this._updatePlaceHolder();
87 },
88
89 _updatePlaceHolder: function(){
90 if(this._phspan){
91 this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
92 }
93 },
94
95 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
96 this.inherited(arguments);
97 this._updatePlaceHolder();
98 },
99
100 getDisplayedValue: function(){
101 // summary:
102 // Deprecated. Use get('displayedValue') instead.
103 // tags:
104 // deprecated
105 kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
106 return this.get('displayedValue');
107 },
108
109 setDisplayedValue: function(/*String*/ value){
110 // summary:
111 // Deprecated. Use set('displayedValue', ...) instead.
112 // tags:
113 // deprecated
114 kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
115 this.set('displayedValue', value);
116 },
117
118 _onBlur: function(e){
119 if(this.disabled){ return; }
120 this.inherited(arguments);
121 this._updatePlaceHolder();
122
123 if(has("mozilla")){
124 if(this.selectOnClick){
125 // clear selection so that the next mouse click doesn't reselect
126 this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
127 }
128 }
129 },
130
131 _onFocus: function(/*String*/ by){
132 if(this.disabled || this.readOnly){ return; }
133 this.inherited(arguments);
134 this._updatePlaceHolder();
135 }
136 });
137
138 if(has("ie")){
139 TextBox.prototype._isTextSelected = function(){
140 var range = this.ownerDocument.selection.createRange();
141 var parent = range.parentElement();
142 return parent == this.textbox && range.text.length > 0;
143 };
144
145 // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
146 dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
147 if(element.createTextRange){
148 var r = element.createTextRange();
149 r.collapse(true);
150 r.moveStart("character", -99999); // move to 0
151 r.moveStart("character", start); // delta from 0 is the correct position
152 r.moveEnd("character", stop-start);
153 r.select();
154 }
155 }
156 }
157
158 return TextBox;
159 });
160
161 },
162 'dijit/_base/scroll':function(){
163 define("dijit/_base/scroll", [
164 "dojo/window", // windowUtils.scrollIntoView
165 "../main" // export symbol to dijit
166 ], function(windowUtils, dijit){
167 // module:
168 // dijit/_base/scroll
169
170 /*=====
171 return {
172 // summary:
173 // Back compatibility module, new code should use windowUtils directly instead of using this module.
174 };
175 =====*/
176
177 dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
178 // summary:
179 // Scroll the passed node into view, if it is not already.
180 // Deprecated, use `windowUtils.scrollIntoView` instead.
181
182 windowUtils.scrollIntoView(node, pos);
183 };
184 });
185
186 },
187 'dijit/_TemplatedMixin':function(){
188 define("dijit/_TemplatedMixin", [
189 "dojo/_base/lang", // lang.getObject
190 "dojo/touch",
191 "./_WidgetBase",
192 "dojo/string", // string.substitute string.trim
193 "dojo/cache", // dojo.cache
194 "dojo/_base/array", // array.forEach
195 "dojo/_base/declare", // declare
196 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
197 "dojo/sniff", // has("ie")
198 "dojo/_base/unload" // unload.addOnWindowUnload
199 ], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload) {
200
201 // module:
202 // dijit/_TemplatedMixin
203
204 var _TemplatedMixin = declare("dijit._TemplatedMixin", null, {
205 // summary:
206 // Mixin for widgets that are instantiated from a template
207
208 // templateString: [protected] String
209 // A string that represents the widget template.
210 // Use in conjunction with dojo.cache() to load from a file.
211 templateString: null,
212
213 // templatePath: [protected deprecated] String
214 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
215 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
216 templatePath: null,
217
218 // skipNodeCache: [protected] Boolean
219 // If using a cached widget template nodes poses issues for a
220 // particular widget class, it can set this property to ensure
221 // that its template is always re-built from a string
222 _skipNodeCache: false,
223
224 // _earlyTemplatedStartup: Boolean
225 // A fallback to preserve the 1.0 - 1.3 behavior of children in
226 // templates having their startup called before the parent widget
227 // fires postCreate. Defaults to 'false', causing child widgets to
228 // have their .startup() called immediately before a parent widget
229 // .startup(), but always after the parent .postCreate(). Set to
230 // 'true' to re-enable to previous, arguably broken, behavior.
231 _earlyTemplatedStartup: false,
232
233 /*=====
234 // _attachPoints: [private] String[]
235 // List of widget attribute names associated with data-dojo-attach-point=... in the
236 // template, ex: ["containerNode", "labelNode"]
237 _attachPoints: [],
238
239 // _attachEvents: [private] Handle[]
240 // List of connections associated with data-dojo-attach-event=... in the
241 // template
242 _attachEvents: [],
243 =====*/
244
245 constructor: function(/*===== params, srcNodeRef =====*/){
246 // summary:
247 // Create the widget.
248 // params: Object|null
249 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
250 // and functions, typically callbacks like onClick.
251 // The hash can contain any of the widget's properties, excluding read-only properties.
252 // srcNodeRef: DOMNode|String?
253 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
254
255 this._attachPoints = [];
256 this._attachEvents = [];
257 },
258
259 _stringRepl: function(tmpl){
260 // summary:
261 // Does substitution of ${foo} type properties in template string
262 // tags:
263 // private
264 var className = this.declaredClass, _this = this;
265 // Cache contains a string because we need to do property replacement
266 // do the property replacement
267 return string.substitute(tmpl, this, function(value, key){
268 if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); }
269 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
270 if(value == null){ return ""; }
271
272 // Substitution keys beginning with ! will skip the transform step,
273 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
274 return key.charAt(0) == "!" ? value :
275 // Safer substitution, see heading "Attribute values" in
276 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
277 value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
278 }, this);
279 },
280
281 buildRendering: function(){
282 // summary:
283 // Construct the UI for this widget from a template, setting this.domNode.
284 // tags:
285 // protected
286
287 if(!this.templateString){
288 this.templateString = cache(this.templatePath, {sanitize: true});
289 }
290
291 // Lookup cached version of template, and download to cache if it
292 // isn't there already. Returns either a DomNode or a string, depending on
293 // whether or not the template contains ${foo} replacement parameters.
294 var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache, this.ownerDocument);
295
296 var node;
297 if(lang.isString(cached)){
298 node = domConstruct.toDom(this._stringRepl(cached), this.ownerDocument);
299 if(node.nodeType != 1){
300 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
301 throw new Error("Invalid template: " + cached);
302 }
303 }else{
304 // if it's a node, all we have to do is clone it
305 node = cached.cloneNode(true);
306 }
307
308 this.domNode = node;
309
310 // Call down to _Widget.buildRendering() to get base classes assigned
311 // TODO: change the baseClass assignment to _setBaseClassAttr
312 this.inherited(arguments);
313
314 // recurse through the node, looking for, and attaching to, our
315 // attachment points and events, which should be defined on the template node.
316 this._attachTemplateNodes(node, function(n,p){ return n.getAttribute(p); });
317
318 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
319
320 this._fillContent(this.srcNodeRef);
321 },
322
323 _beforeFillContent: function(){
324 },
325
326 _fillContent: function(/*DomNode*/ source){
327 // summary:
328 // Relocate source contents to templated container node.
329 // this.containerNode must be able to receive children, or exceptions will be thrown.
330 // tags:
331 // protected
332 var dest = this.containerNode;
333 if(source && dest){
334 while(source.hasChildNodes()){
335 dest.appendChild(source.firstChild);
336 }
337 }
338 },
339
340 _attachTemplateNodes: function(rootNode, getAttrFunc){
341 // summary:
342 // Iterate through the template and attach functions and nodes accordingly.
343 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
344 // etc. for those widgets.
345 // description:
346 // Map widget properties and functions to the handlers specified in
347 // the dom node and it's descendants. This function iterates over all
348 // nodes and looks for these properties:
349 //
350 // - dojoAttachPoint/data-dojo-attach-point
351 // - dojoAttachEvent/data-dojo-attach-event
352 // rootNode: DomNode|Widget[]
353 // the node to search for properties. All children will be searched.
354 // getAttrFunc: Function
355 // a function which will be used to obtain property for a given
356 // DomNode/Widget
357 // tags:
358 // private
359
360 var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
361 var x = lang.isArray(rootNode) ? 0 : -1;
362 for(; x < 0 || nodes[x]; x++){ // don't access nodes.length on IE, see #14346
363 var baseNode = (x == -1) ? rootNode : nodes[x];
364 if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
365 continue;
366 }
367 // Process data-dojo-attach-point
368 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
369 if(attachPoint){
370 var point, points = attachPoint.split(/\s*,\s*/);
371 while((point = points.shift())){
372 if(lang.isArray(this[point])){
373 this[point].push(baseNode);
374 }else{
375 this[point]=baseNode;
376 }
377 this._attachPoints.push(point);
378 }
379 }
380
381 // Process data-dojo-attach-event
382 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
383 if(attachEvent){
384 // NOTE: we want to support attributes that have the form
385 // "domEvent: nativeEvent; ..."
386 var event, events = attachEvent.split(/\s*,\s*/);
387 var trim = lang.trim;
388 while((event = events.shift())){
389 if(event){
390 var thisFunc = null;
391 if(event.indexOf(":") != -1){
392 // oh, if only JS had tuple assignment
393 var funcNameArr = event.split(":");
394 event = trim(funcNameArr[0]);
395 thisFunc = trim(funcNameArr[1]);
396 }else{
397 event = trim(event);
398 }
399 if(!thisFunc){
400 thisFunc = event;
401 }
402 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
403 this._attachEvents.push(this.connect(baseNode, touch[event] || event, thisFunc));
404 }
405 }
406 }
407 }
408 },
409
410 destroyRendering: function(){
411 // Delete all attach points to prevent IE6 memory leaks.
412 array.forEach(this._attachPoints, function(point){
413 delete this[point];
414 }, this);
415 this._attachPoints = [];
416
417 // And same for event handlers
418 array.forEach(this._attachEvents, this.disconnect, this);
419 this._attachEvents = [];
420
421 this.inherited(arguments);
422 }
423 });
424
425 // key is templateString; object is either string or DOM tree
426 _TemplatedMixin._templateCache = {};
427
428 _TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString, doc){
429 // summary:
430 // Static method to get a template based on the templatePath or
431 // templateString key
432 // templateString: String
433 // The template
434 // alwaysUseString: Boolean
435 // Don't cache the DOM tree for this template, even if it doesn't have any variables
436 // doc: Document?
437 // The target document. Defaults to document global if unspecified.
438 // returns: Mixed
439 // Either string (if there are ${} variables that need to be replaced) or just
440 // a DOM tree (if the node can be cloned directly)
441
442 // is it already cached?
443 var tmplts = _TemplatedMixin._templateCache;
444 var key = templateString;
445 var cached = tmplts[key];
446 if(cached){
447 try{
448 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
449 // current document, then use the current cached value
450 if(!cached.ownerDocument || cached.ownerDocument == (doc || document)){
451 // string or node of the same document
452 return cached;
453 }
454 }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
455 domConstruct.destroy(cached);
456 }
457
458 templateString = string.trim(templateString);
459
460 if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
461 // there are variables in the template so all we can do is cache the string
462 return (tmplts[key] = templateString); //String
463 }else{
464 // there are no variables in the template so we can cache the DOM tree
465 var node = domConstruct.toDom(templateString, doc);
466 if(node.nodeType != 1){
467 throw new Error("Invalid template: " + templateString);
468 }
469 return (tmplts[key] = node); //Node
470 }
471 };
472
473 if(has("ie")){
474 unload.addOnWindowUnload(function(){
475 var cache = _TemplatedMixin._templateCache;
476 for(var key in cache){
477 var value = cache[key];
478 if(typeof value == "object"){ // value is either a string or a DOM node template
479 domConstruct.destroy(value);
480 }
481 delete cache[key];
482 }
483 });
484 }
485
486 // These arguments can be specified for widgets which are used in templates.
487 // Since any widget can be specified as sub widgets in template, mix it
488 // into the base widget class. (This is a hack, but it's effective.).
489 // Remove for 2.0. Also, hide from API doc parser.
490 lang.extend(_WidgetBase, /*===== {} || =====*/ {
491 dojoAttachEvent: "",
492 dojoAttachPoint: ""
493 });
494
495 return _TemplatedMixin;
496 });
497
498 },
499 'dijit/_CssStateMixin':function(){
500 define("dijit/_CssStateMixin", [
501 "dojo/_base/array", // array.forEach array.map
502 "dojo/_base/declare", // declare
503 "dojo/dom", // dom.isDescendant()
504 "dojo/dom-class", // domClass.toggle
505 "dojo/has",
506 "dojo/_base/lang", // lang.hitch
507 "dojo/on",
508 "dojo/ready",
509 "dojo/_base/window", // win.body
510 "./registry"
511 ], function(array, declare, dom, domClass, has, lang, on, ready, win, registry){
512
513 // module:
514 // dijit/_CssStateMixin
515
516 var CssStateMixin = declare("dijit._CssStateMixin", [], {
517 // summary:
518 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
519 // state changes, and also higher-level state changes such becoming disabled or selected.
520 //
521 // description:
522 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
523 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
524 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
525 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
526 //
527 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
528 //
529 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
530 // within the widget).
531
532 // cssStateNodes: [protected] Object
533 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
534 //
535 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
536 // (like "dijitUpArrowButton"). Example:
537 // | {
538 // | "upArrowButton": "dijitUpArrowButton",
539 // | "downArrowButton": "dijitDownArrowButton"
540 // | }
541 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
542 // is hovered, etc.
543 cssStateNodes: {},
544
545 // hovering: [readonly] Boolean
546 // True if cursor is over this widget
547 hovering: false,
548
549 // active: [readonly] Boolean
550 // True if mouse was pressed while over this widget, and hasn't been released yet
551 active: false,
552
553 _applyAttributes: function(){
554 // This code would typically be in postCreate(), but putting in _applyAttributes() for
555 // performance: so the class changes happen before DOM is inserted into the document.
556 // Change back to postCreate() in 2.0. See #11635.
557
558 this.inherited(arguments);
559
560 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
561 array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr){
562 this.watch(attr, lang.hitch(this, "_setStateClass"));
563 }, this);
564
565 // Track hover and active mouse events on widget root node, plus possibly on subnodes
566 for(var ap in this.cssStateNodes){
567 this._trackMouseState(this[ap], this.cssStateNodes[ap]);
568 }
569 this._trackMouseState(this.domNode, this.baseClass);
570
571 // Set state initially; there's probably no hover/active/focus state but widget might be
572 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
573 this._setStateClass();
574 },
575
576 _cssMouseEvent: function(/*Event*/ event){
577 // summary:
578 // Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
579 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
580
581 if(!this.disabled){
582 switch(event.type){
583 case "mouseover":
584 this._set("hovering", true);
585 this._set("active", this._mouseDown);
586 break;
587 case "mouseout":
588 this._set("hovering", false);
589 this._set("active", false);
590 break;
591 case "mousedown":
592 case "touchstart":
593 this._set("active", true);
594 break;
595 case "mouseup":
596 case "touchend":
597 this._set("active", false);
598 break;
599 }
600 }
601 },
602
603 _setStateClass: function(){
604 // summary:
605 // Update the visual state of the widget by setting the css classes on this.domNode
606 // (or this.stateNode if defined) by combining this.baseClass with
607 // various suffixes that represent the current widget state(s).
608 //
609 // description:
610 // In the case where a widget has multiple
611 // states, it sets the class based on all possible
612 // combinations. For example, an invalid form widget that is being hovered
613 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
614 //
615 // The widget may have one or more of the following states, determined
616 // by this.state, this.checked, this.valid, and this.selected:
617 //
618 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
619 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
620 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
621 // - Selected - ex: currently selected tab will have this.selected==true
622 //
623 // In addition, it may have one or more of the following states,
624 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
625 //
626 // - Disabled - if the widget is disabled
627 // - Active - if the mouse (or space/enter key?) is being pressed down
628 // - Focused - if the widget has focus
629 // - Hover - if the mouse is over the widget
630
631 // Compute new set of classes
632 var newStateClasses = this.baseClass.split(" ");
633
634 function multiply(modifier){
635 newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
636 }
637
638 if(!this.isLeftToRight()){
639 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
640 multiply("Rtl");
641 }
642
643 var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
644 if(this.checked){
645 multiply(checkedState);
646 }
647 if(this.state){
648 multiply(this.state);
649 }
650 if(this.selected){
651 multiply("Selected");
652 }
653 if(this._opened){
654 multiply("Opened");
655 }
656
657 if(this.disabled){
658 multiply("Disabled");
659 }else if(this.readOnly){
660 multiply("ReadOnly");
661 }else{
662 if(this.active){
663 multiply("Active");
664 }else if(this.hovering){
665 multiply("Hover");
666 }
667 }
668
669 if(this.focused){
670 multiply("Focused");
671 }
672
673 // Remove old state classes and add new ones.
674 // For performance concerns we only write into domNode.className once.
675 var tn = this.stateNode || this.domNode,
676 classHash = {}; // set of all classes (state and otherwise) for node
677
678 array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
679
680 if("_stateClasses" in this){
681 array.forEach(this._stateClasses, function(c){ delete classHash[c]; });
682 }
683
684 array.forEach(newStateClasses, function(c){ classHash[c] = true; });
685
686 var newClasses = [];
687 for(var c in classHash){
688 newClasses.push(c);
689 }
690 tn.className = newClasses.join(" ");
691
692 this._stateClasses = newStateClasses;
693 },
694
695 _subnodeCssMouseEvent: function(node, clazz, evt){
696 // summary:
697 // Handler for hover/active mouse event on widget's subnode
698 if(this.disabled || this.readOnly){
699 return;
700 }
701 function hover(isHovering){
702 domClass.toggle(node, clazz+"Hover", isHovering);
703 }
704 function active(isActive){
705 domClass.toggle(node, clazz+"Active", isActive);
706 }
707 function focused(isFocused){
708 domClass.toggle(node, clazz+"Focused", isFocused);
709 }
710 switch(evt.type){
711 case "mouseover":
712 hover(true);
713 break;
714 case "mouseout":
715 hover(false);
716 active(false);
717 break;
718 case "mousedown":
719 case "touchstart":
720 active(true);
721 break;
722 case "mouseup":
723 case "touchend":
724 active(false);
725 break;
726 case "focus":
727 case "focusin":
728 focused(true);
729 break;
730 case "blur":
731 case "focusout":
732 focused(false);
733 break;
734 }
735 },
736
737 _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
738 // summary:
739 // Track mouse/focus events on specified node and set CSS class on that node to indicate
740 // current state. Usually not called directly, but via cssStateNodes attribute.
741 // description:
742 // Given class=foo, will set the following CSS class on the node
743 //
744 // - fooActive: if the user is currently pressing down the mouse button while over the node
745 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
746 // - fooFocus: if the node is focused
747 //
748 // Note that it won't set any classes if the widget is disabled.
749 // node: DomNode
750 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
751 // is handled specially and automatically just by mixing in this class.
752 // clazz: String
753 // CSS class name (ex: dijitSliderUpArrow)
754
755 // Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
756 // when node is hovered/active
757 node._cssState = clazz;
758 }
759 });
760
761 ready(function(){
762 // Document level listener to catch hover etc. events on widget root nodes and subnodes.
763 // Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
764 // have been hovered or unhovered (try test_Accordion.html)
765 function handler(evt){
766 // Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
767 // to avoid processing mouseout events moving from a widget's domNode to a descendant node;
768 // such events shouldn't be interpreted as a mouseleave on the widget.
769 if(!dom.isDescendant(evt.relatedTarget, evt.target)){
770 for(var node = evt.target; node && node != evt.relatedTarget; node = node.parentNode){
771 // Process any nodes with _cssState property. They are generally widget root nodes,
772 // but could also be sub-nodes within a widget
773 if(node._cssState){
774 var widget = registry.getEnclosingWidget(node);
775 if(widget){
776 if(node == widget.domNode){
777 // event on the widget's root node
778 widget._cssMouseEvent(evt);
779 }else{
780 // event on widget's sub-node
781 widget._subnodeCssMouseEvent(node, node._cssState, evt);
782 }
783 }
784 }
785 }
786 }
787 }
788 function ieHandler(evt){
789 evt.target = evt.srcElement;
790 handler(evt);
791 }
792
793 // Use addEventListener() (and attachEvent() on IE) to catch the relevant events even if other handlers
794 // (on individual nodes) call evt.stopPropagation() or event.stopEvent().
795 // Currently typematic.js is doing that, not sure why.
796 // Don't monitor mouseover/mouseout on mobile because iOS generates "phantom" mouseover/mouseout events when
797 // drag-scrolling, at the point in the viewport where the drag originated. Test the Tree in api viewer.
798 var body = win.body(),
799 types = (has("touch") ? [] : ["mouseover", "mouseout"]).concat(["mousedown", "touchstart", "mouseup", "touchend"]);
800 array.forEach(types, function(type){
801 if(body.addEventListener){
802 body.addEventListener(type, handler, true); // W3C
803 }else{
804 body.attachEvent("on"+type, ieHandler); // IE
805 }
806 });
807
808 // Track focus events on widget sub-nodes that have been registered via _trackMouseState().
809 // However, don't track focus events on the widget root nodes, because focus is tracked via the
810 // focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
811 // nodes or a subwidget's node or a popup node, etc.)
812 // Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
813 on(body, "focusin, focusout", function(evt){
814 var node = evt.target;
815 if(node._cssState && !node.getAttribute("widgetId")){
816 var widget = registry.getEnclosingWidget(node);
817 widget._subnodeCssMouseEvent(node, node._cssState, evt);
818 }
819 });
820 });
821
822 return CssStateMixin;
823 });
824
825 },
826 'dijit/layout/ScrollingTabController':function(){
827 require({cache:{
828 '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>",
829 '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>"}});
830 define("dijit/layout/ScrollingTabController", [
831 "dojo/_base/array", // array.forEach
832 "dojo/_base/declare", // declare
833 "dojo/dom-class", // domClass.add domClass.contains
834 "dojo/dom-geometry", // domGeometry.contentBox
835 "dojo/dom-style", // domStyle.style
836 "dojo/_base/fx", // Animation
837 "dojo/_base/lang", // lang.hitch
838 "dojo/on",
839 "dojo/query", // query
840 "dojo/sniff", // has("ie"), has("webkit"), has("quirks")
841 "../registry", // registry.byId()
842 "dojo/text!./templates/ScrollingTabController.html",
843 "dojo/text!./templates/_ScrollingTabControllerButton.html",
844 "./TabController",
845 "./utils", // marginBox2contextBox, layoutChildren
846 "../_WidgetsInTemplateMixin",
847 "../Menu",
848 "../MenuItem",
849 "../form/Button",
850 "../_HasDropDown",
851 "dojo/NodeList-dom" // NodeList.style
852 ], function(array, declare, domClass, domGeometry, domStyle, fx, lang, on, query, has,
853 registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
854 Menu, MenuItem, Button, _HasDropDown){
855
856 // module:
857 // dijit/layout/ScrollingTabController
858
859
860 var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
861 // summary:
862 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
863 // all fitting on a single row.
864 // Works only for horizontal tabs (either above or below the content, not to the left
865 // or right).
866 // tags:
867 // private
868
869 baseClass: "dijitTabController dijitScrollingTabController",
870
871 templateString: tabControllerTemplate,
872
873 // useMenu: [const] Boolean
874 // True if a menu should be used to select tabs when they are too
875 // wide to fit the TabContainer, false otherwise.
876 useMenu: true,
877
878 // useSlider: [const] Boolean
879 // True if a slider should be used to select tabs when they are too
880 // wide to fit the TabContainer, false otherwise.
881 useSlider: true,
882
883 // tabStripClass: [const] String
884 // The css class to apply to the tab strip, if it is visible.
885 tabStripClass: "",
886
887 widgetsInTemplate: true,
888
889 // _minScroll: Number
890 // The distance in pixels from the edge of the tab strip which,
891 // if a scroll animation is less than, forces the scroll to
892 // go all the way to the left/right.
893 _minScroll: 5,
894
895 // Override default behavior mapping class to DOMNode
896 _setClassAttr: { node: "containerNode", type: "class" },
897
898 buildRendering: function(){
899 this.inherited(arguments);
900 var n = this.domNode;
901
902 this.scrollNode = this.tablistWrapper;
903 this._initButtons();
904
905 if(!this.tabStripClass){
906 this.tabStripClass = "dijitTabContainer" +
907 this.tabPosition.charAt(0).toUpperCase() +
908 this.tabPosition.substr(1).replace(/-.*/, "") +
909 "None";
910 domClass.add(n, "tabStrip-disabled")
911 }
912
913 domClass.add(this.tablistWrapper, this.tabStripClass);
914 },
915
916 onStartup: function(){
917 this.inherited(arguments);
918
919 // TabController is hidden until it finishes drawing, to give
920 // a less visually jumpy instantiation. When it's finished, set visibility to ""
921 // to that the tabs are hidden/shown depending on the container's visibility setting.
922 domStyle.set(this.domNode, "visibility", "");
923 this._postStartup = true;
924
925 // changes to the tab button label or iconClass will have changed the width of the
926 // buttons, so do a resize
927 this.own(on(this.containerNode, "attrmodified-label, attrmodified-iconclass", lang.hitch(this, function(evt){
928 if(this._dim){
929 this.resize(this._dim);
930 }
931 })));
932 },
933
934 onAddChild: function(page, insertIndex){
935 this.inherited(arguments);
936
937 // Increment the width of the wrapper when a tab is added
938 // This makes sure that the buttons never wrap.
939 // The value 200 is chosen as it should be bigger than most
940 // Tab button widths.
941 domStyle.set(this.containerNode, "width",
942 (domStyle.get(this.containerNode, "width") + 200) + "px");
943 },
944
945 onRemoveChild: function(page, insertIndex){
946 // null out _selectedTab because we are about to delete that dom node
947 var button = this.pane2button[page.id];
948 if(this._selectedTab === button.domNode){
949 this._selectedTab = null;
950 }
951
952 this.inherited(arguments);
953 },
954
955 _initButtons: function(){
956 // summary:
957 // Creates the buttons used to scroll to view tabs that
958 // may not be visible if the TabContainer is too narrow.
959
960 // Make a list of the buttons to display when the tab labels become
961 // wider than the TabContainer, and hide the other buttons.
962 // Also gets the total width of the displayed buttons.
963 this._btnWidth = 0;
964 this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){
965 if((this.useMenu && btn == this._menuBtn.domNode) ||
966 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
967 this._btnWidth += domGeometry.getMarginSize(btn).w;
968 return true;
969 }else{
970 domStyle.set(btn, "display", "none");
971 return false;
972 }
973 }, this);
974 },
975
976 _getTabsWidth: function(){
977 var children = this.getChildren();
978 if(children.length){
979 var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
980 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
981 return rightTab.offsetLeft + rightTab.offsetWidth - leftTab.offsetLeft;
982 }else{
983 return 0;
984 }
985 },
986
987 _enableBtn: function(width){
988 // summary:
989 // Determines if the tabs are wider than the width of the TabContainer, and
990 // thus that we need to display left/right/menu navigation buttons.
991 var tabsWidth = this._getTabsWidth();
992 width = width || domStyle.get(this.scrollNode, "width");
993 return tabsWidth > 0 && width < tabsWidth;
994 },
995
996 resize: function(dim){
997 // summary:
998 // Hides or displays the buttons used to scroll the tab list and launch the menu
999 // that selects tabs.
1000
1001 // Save the dimensions to be used when a child is renamed.
1002 this._dim = dim;
1003
1004 // Set my height to be my natural height (tall enough for one row of tab labels),
1005 // and my content-box width based on margin-box width specified in dim parameter.
1006 // But first reset scrollNode.height in case it was set by layoutChildren() call
1007 // in a previous run of this method.
1008 this.scrollNode.style.height = "auto";
1009 var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
1010 cb.h = this.scrollNode.offsetHeight;
1011 domGeometry.setContentSize(this.domNode, cb);
1012
1013 // Show/hide the left/right/menu navigation buttons depending on whether or not they
1014 // are needed.
1015 var enable = this._enableBtn(this._contentBox.w);
1016 this._buttons.style("display", enable ? "" : "none");
1017
1018 // Position and size the navigation buttons and the tablist
1019 this._leftBtn.layoutAlign = "left";
1020 this._rightBtn.layoutAlign = "right";
1021 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
1022 layoutUtils.layoutChildren(this.domNode, this._contentBox,
1023 [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
1024
1025 // set proper scroll so that selected tab is visible
1026 if(this._selectedTab){
1027 if(this._anim && this._anim.status() == "playing"){
1028 this._anim.stop();
1029 }
1030 this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab());
1031 }
1032
1033 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
1034 this._setButtonClass(this._getScroll());
1035
1036 this._postResize = true;
1037
1038 // Return my size so layoutChildren() can use it.
1039 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
1040 return {h: this._contentBox.h, w: dim.w};
1041 },
1042
1043 _getScroll: function(){
1044 // summary:
1045 // Returns the current scroll of the tabs where 0 means
1046 // "scrolled all the way to the left" and some positive number, based on #
1047 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
1048 return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft :
1049 domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width")
1050 + (has("ie") >= 8 ? -1 : 1) * this.scrollNode.scrollLeft;
1051 },
1052
1053 _convertToScrollLeft: function(val){
1054 // summary:
1055 // Given a scroll value where 0 means "scrolled all the way to the left"
1056 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
1057 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
1058 // to achieve that scroll.
1059 //
1060 // This method is to adjust for RTL funniness in various browsers and versions.
1061 if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
1062 return val;
1063 }else{
1064 var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width");
1065 return (has("ie") >= 8 ? -1 : 1) * (val - maxScroll);
1066 }
1067 },
1068
1069 onSelectChild: function(/*dijit/_WidgetBase*/ page){
1070 // summary:
1071 // Smoothly scrolls to a tab when it is selected.
1072
1073 var tab = this.pane2button[page.id];
1074 if(!tab || !page){return;}
1075
1076 var node = tab.domNode;
1077
1078 // Save the selection
1079 if(node != this._selectedTab){
1080 this._selectedTab = node;
1081
1082 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
1083 if(this._postResize){
1084 var sl = this._getScroll();
1085
1086 if(sl > node.offsetLeft ||
1087 sl + domStyle.get(this.scrollNode, "width") <
1088 node.offsetLeft + domStyle.get(node, "width")){
1089 this.createSmoothScroll().play();
1090 }
1091 }
1092 }
1093
1094 this.inherited(arguments);
1095 },
1096
1097 _getScrollBounds: function(){
1098 // summary:
1099 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
1100 // tabs (respectively)
1101 var children = this.getChildren(),
1102 scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
1103 containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
1104 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
1105 tabsWidth = this._getTabsWidth();
1106
1107 if(children.length && tabsWidth > scrollNodeWidth){
1108 // Scrolling should happen
1109 return {
1110 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
1111 max: this.isLeftToRight() ?
1112 (children[children.length-1].domNode.offsetLeft + children[children.length-1].domNode.offsetWidth) - scrollNodeWidth :
1113 maxPossibleScroll
1114 };
1115 }else{
1116 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
1117 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
1118 return {
1119 min: onlyScrollPosition,
1120 max: onlyScrollPosition
1121 };
1122 }
1123 },
1124
1125 _getScrollForSelectedTab: function(){
1126 // summary:
1127 // Returns the scroll value setting so that the selected tab
1128 // will appear in the center
1129 var w = this.scrollNode,
1130 n = this._selectedTab,
1131 scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
1132 scrollBounds = this._getScrollBounds();
1133
1134 // TODO: scroll minimal amount (to either right or left) so that
1135 // selected tab is fully visible, and just return if it's already visible?
1136 var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
1137 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
1138
1139 // TODO:
1140 // If scrolling close to the left side or right side, scroll
1141 // all the way to the left or right. See this._minScroll.
1142 // (But need to make sure that doesn't scroll the tab out of view...)
1143 return pos;
1144 },
1145
1146 createSmoothScroll: function(x){
1147 // summary:
1148 // Creates a dojo._Animation object that smoothly scrolls the tab list
1149 // either to a fixed horizontal pixel value, or to the selected tab.
1150 // description:
1151 // If an number argument is passed to the function, that horizontal
1152 // pixel position is scrolled to. Otherwise the currently selected
1153 // tab is scrolled to.
1154 // x: Integer?
1155 // An optional pixel value to scroll to, indicating distance from left.
1156
1157 // Calculate position to scroll to
1158 if(arguments.length > 0){
1159 // position specified by caller, just make sure it's within bounds
1160 var scrollBounds = this._getScrollBounds();
1161 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
1162 }else{
1163 // scroll to center the current tab
1164 x = this._getScrollForSelectedTab();
1165 }
1166
1167 if(this._anim && this._anim.status() == "playing"){
1168 this._anim.stop();
1169 }
1170
1171 var self = this,
1172 w = this.scrollNode,
1173 anim = new fx.Animation({
1174 beforeBegin: function(){
1175 if(this.curve){ delete this.curve; }
1176 var oldS = w.scrollLeft,
1177 newS = self._convertToScrollLeft(x);
1178 anim.curve = new fx._Line(oldS, newS);
1179 },
1180 onAnimate: function(val){
1181 w.scrollLeft = val;
1182 }
1183 });
1184 this._anim = anim;
1185
1186 // Disable/enable left/right buttons according to new scroll position
1187 this._setButtonClass(x);
1188
1189 return anim; // dojo/_base/fx/Animation
1190 },
1191
1192 _getBtnNode: function(/*Event*/ e){
1193 // summary:
1194 // Gets a button DOM node from a mouse click event.
1195 // e:
1196 // The mouse click event.
1197 var n = e.target;
1198 while(n && !domClass.contains(n, "tabStripButton")){
1199 n = n.parentNode;
1200 }
1201 return n;
1202 },
1203
1204 doSlideRight: function(/*Event*/ e){
1205 // summary:
1206 // Scrolls the menu to the right.
1207 // e:
1208 // The mouse click event.
1209 this.doSlide(1, this._getBtnNode(e));
1210 },
1211
1212 doSlideLeft: function(/*Event*/ e){
1213 // summary:
1214 // Scrolls the menu to the left.
1215 // e:
1216 // The mouse click event.
1217 this.doSlide(-1,this._getBtnNode(e));
1218 },
1219
1220 doSlide: function(/*Number*/ direction, /*DomNode*/ node){
1221 // summary:
1222 // Scrolls the tab list to the left or right by 75% of the widget width.
1223 // direction:
1224 // If the direction is 1, the widget scrolls to the right, if it is -1,
1225 // it scrolls to the left.
1226
1227 if(node && domClass.contains(node, "dijitTabDisabled")){return;}
1228
1229 var sWidth = domStyle.get(this.scrollNode, "width");
1230 var d = (sWidth * 0.75) * direction;
1231
1232 var to = this._getScroll() + d;
1233
1234 this._setButtonClass(to);
1235
1236 this.createSmoothScroll(to).play();
1237 },
1238
1239 _setButtonClass: function(/*Number*/ scroll){
1240 // summary:
1241 // Disables the left scroll button if the tabs are scrolled all the way to the left,
1242 // or the right scroll button in the opposite case.
1243 // scroll: Integer
1244 // amount of horizontal scroll
1245
1246 var scrollBounds = this._getScrollBounds();
1247 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
1248 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
1249 }
1250 });
1251
1252
1253 var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
1254 baseClass: "dijitTab tabStripButton",
1255
1256 templateString: buttonTemplate,
1257
1258 // Override inherited tabIndex: 0 from dijit/form/Button, because user shouldn't be
1259 // able to tab to the left/right/menu buttons
1260 tabIndex: "",
1261
1262 // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
1263 // either (this override avoids focus() call in FormWidget.js)
1264 isFocusable: function(){ return false; }
1265 });
1266
1267 // Class used in template
1268 declare("dijit.layout._ScrollingTabControllerButton",
1269 [Button, ScrollingTabControllerButtonMixin]);
1270
1271 // Class used in template
1272 declare(
1273 "dijit.layout._ScrollingTabControllerMenuButton",
1274 [Button, _HasDropDown, ScrollingTabControllerButtonMixin],
1275 {
1276 // id of the TabContainer itself
1277 containerId: "",
1278
1279 // -1 so user can't tab into the button, but so that button can still be focused programatically.
1280 // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
1281 tabIndex: "-1",
1282
1283 isLoaded: function(){
1284 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
1285 return false;
1286 },
1287
1288 loadDropDown: function(callback){
1289 this.dropDown = new Menu({
1290 id: this.containerId + "_menu",
1291 ownerDocument: this.ownerDocument,
1292 dir: this.dir,
1293 lang: this.lang,
1294 textDir: this.textDir
1295 });
1296 var container = registry.byId(this.containerId);
1297 array.forEach(container.getChildren(), function(page){
1298 var menuItem = new MenuItem({
1299 id: page.id + "_stcMi",
1300 label: page.title,
1301 iconClass: page.iconClass,
1302 disabled: page.disabled,
1303 ownerDocument: this.ownerDocument,
1304 dir: page.dir,
1305 lang: page.lang,
1306 textDir: page.textDir,
1307 onClick: function(){
1308 container.selectChild(page);
1309 }
1310 });
1311 this.dropDown.addChild(menuItem);
1312 }, this);
1313 callback();
1314 },
1315
1316 closeDropDown: function(/*Boolean*/ focus){
1317 this.inherited(arguments);
1318 if(this.dropDown){
1319 this.dropDown.destroyRecursive();
1320 delete this.dropDown;
1321 }
1322 }
1323 });
1324
1325 return ScrollingTabController;
1326 });
1327
1328 },
1329 'dijit/DialogUnderlay':function(){
1330 define("dijit/DialogUnderlay", [
1331 "dojo/_base/declare", // declare
1332 "dojo/dom-attr", // domAttr.set
1333 "dojo/window", // winUtils.getBox
1334 "./_Widget",
1335 "./_TemplatedMixin",
1336 "./BackgroundIframe"
1337 ], function(declare, domAttr, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
1338
1339 // module:
1340 // dijit/DialogUnderlay
1341
1342 return declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
1343 // summary:
1344 // The component that blocks the screen behind a `dijit.Dialog`
1345 //
1346 // description:
1347 // A component used to block input behind a `dijit.Dialog`. Only a single
1348 // instance of this widget is created by `dijit.Dialog`, and saved as
1349 // a reference to be shared between all Dialogs as `dijit._underlay`
1350 //
1351 // The underlay itself can be styled based on and id:
1352 // | #myDialog_underlay { background-color:red; }
1353 //
1354 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
1355 // suffixed with _underlay.
1356
1357 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
1358 // Inner div has opacity specified in CSS file.
1359 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
1360
1361 // Parameters on creation or updatable later
1362
1363 // dialogId: String
1364 // Id of the dialog.... DialogUnderlay's id is based on this id
1365 dialogId: "",
1366
1367 // class: String
1368 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
1369 "class": "",
1370
1371 _setDialogIdAttr: function(id){
1372 domAttr.set(this.node, "id", id + "_underlay");
1373 this._set("dialogId", id);
1374 },
1375
1376 _setClassAttr: function(clazz){
1377 this.node.className = "dijitDialogUnderlay " + clazz;
1378 this._set("class", clazz);
1379 },
1380
1381 postCreate: function(){
1382 // summary:
1383 // Append the underlay to the body
1384 this.ownerDocumentBody.appendChild(this.domNode);
1385 },
1386
1387 layout: function(){
1388 // summary:
1389 // Sets the background to the size of the viewport
1390 //
1391 // description:
1392 // Sets the background to the size of the viewport (rather than the size
1393 // of the document) since we need to cover the whole browser window, even
1394 // if the document is only a few lines long.
1395 // tags:
1396 // private
1397
1398 var is = this.node.style,
1399 os = this.domNode.style;
1400
1401 // hide the background temporarily, so that the background itself isn't
1402 // causing scrollbars to appear (might happen when user shrinks browser
1403 // window and then we are called to resize)
1404 os.display = "none";
1405
1406 // then resize and show
1407 var viewport = winUtils.getBox(this.ownerDocument);
1408 os.top = viewport.t + "px";
1409 os.left = viewport.l + "px";
1410 is.width = viewport.w + "px";
1411 is.height = viewport.h + "px";
1412 os.display = "block";
1413 },
1414
1415 show: function(){
1416 // summary:
1417 // Show the dialog underlay
1418 this.domNode.style.display = "block";
1419 this.layout();
1420 this.bgIframe = new BackgroundIframe(this.domNode);
1421 },
1422
1423 hide: function(){
1424 // summary:
1425 // Hides the dialog underlay
1426 this.bgIframe.destroy();
1427 delete this.bgIframe;
1428 this.domNode.style.display = "none";
1429 }
1430 });
1431 });
1432
1433 },
1434 'dijit/place':function(){
1435 define("dijit/place", [
1436 "dojo/_base/array", // array.forEach array.map array.some
1437 "dojo/dom-geometry", // domGeometry.position
1438 "dojo/dom-style", // domStyle.getComputedStyle
1439 "dojo/_base/kernel", // kernel.deprecated
1440 "dojo/_base/window", // win.body
1441 "dojo/window", // winUtils.getBox
1442 "./main" // dijit (defining dijit.place to match API doc)
1443 ], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
1444
1445 // module:
1446 // dijit/place
1447
1448
1449 function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){
1450 // summary:
1451 // Given a list of spots to put node, put it at the first spot where it fits,
1452 // of if it doesn't fit anywhere then the place with the least overflow
1453 // choices: Array
1454 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1455 // Above example says to put the top-left corner of the node at (10,20)
1456 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
1457 // for things like tooltip, they are displayed differently (and have different dimensions)
1458 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1459 // It also passes in the available size for the popup, which is useful for tooltips to
1460 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
1461 // how much the popup had to be modified to fit into the available space. This is used to determine
1462 // what the best placement is.
1463 // aroundNodeCoords: Object
1464 // Size of aroundNode, ex: {w: 200, h: 50}
1465
1466 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1467 // viewport over document
1468 var view = winUtils.getBox(node.ownerDocument);
1469
1470 // This won't work if the node is inside a <div style="position: relative">,
1471 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
1472 // and also it might get cutoff)
1473 if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
1474 win.body(node.ownerDocument).appendChild(node);
1475 }
1476
1477 var best = null;
1478 array.some(choices, function(choice){
1479 var corner = choice.corner;
1480 var pos = choice.pos;
1481 var overflow = 0;
1482
1483 // calculate amount of space available given specified position of node
1484 var spaceAvailable = {
1485 w: {
1486 'L': view.l + view.w - pos.x,
1487 'R': pos.x - view.l,
1488 'M': view.w
1489 }[corner.charAt(1)],
1490 h: {
1491 'T': view.t + view.h - pos.y,
1492 'B': pos.y - view.t,
1493 'M': view.h
1494 }[corner.charAt(0)]
1495 };
1496
1497 // Clear left/right position settings set earlier so they don't interfere with calculations,
1498 // specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
1499 var s = node.style;
1500 s.left = s.right = "auto";
1501
1502 // configure node to be displayed in given position relative to button
1503 // (need to do this in order to get an accurate size for the node, because
1504 // a tooltip's size changes based on position, due to triangle)
1505 if(layoutNode){
1506 var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
1507 overflow = typeof res == "undefined" ? 0 : res;
1508 }
1509
1510 // get node's size
1511 var style = node.style;
1512 var oldDisplay = style.display;
1513 var oldVis = style.visibility;
1514 if(style.display == "none"){
1515 style.visibility = "hidden";
1516 style.display = "";
1517 }
1518 var bb = domGeometry.position(node);
1519 style.display = oldDisplay;
1520 style.visibility = oldVis;
1521
1522 // coordinates and size of node with specified corner placed at pos,
1523 // and clipped by viewport
1524 var
1525 startXpos = {
1526 'L': pos.x,
1527 'R': pos.x - bb.w,
1528 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (bb.w >> 1)) - bb.w) // M orientation is more flexible
1529 }[corner.charAt(1)],
1530 startYpos = {
1531 'T': pos.y,
1532 'B': pos.y - bb.h,
1533 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (bb.h >> 1)) - bb.h)
1534 }[corner.charAt(0)],
1535 startX = Math.max(view.l, startXpos),
1536 startY = Math.max(view.t, startYpos),
1537 endX = Math.min(view.l + view.w, startXpos + bb.w),
1538 endY = Math.min(view.t + view.h, startYpos + bb.h),
1539 width = endX - startX,
1540 height = endY - startY;
1541
1542 overflow += (bb.w - width) + (bb.h - height);
1543
1544 if(best == null || overflow < best.overflow){
1545 best = {
1546 corner: corner,
1547 aroundCorner: choice.aroundCorner,
1548 x: startX,
1549 y: startY,
1550 w: width,
1551 h: height,
1552 overflow: overflow,
1553 spaceAvailable: spaceAvailable
1554 };
1555 }
1556
1557 return !overflow;
1558 });
1559
1560 // In case the best position is not the last one we checked, need to call
1561 // layoutNode() again.
1562 if(best.overflow && layoutNode){
1563 layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
1564 }
1565
1566 // And then position the node. Do this last, after the layoutNode() above
1567 // has sized the node, due to browser quirks when the viewport is scrolled
1568 // (specifically that a Tooltip will shrink to fit as though the window was
1569 // scrolled to the left).
1570 //
1571 // In RTL mode, set style.right rather than style.left so in the common case,
1572 // window resizes move the popup along with the aroundNode.
1573 var l = domGeometry.isBodyLtr(node.ownerDocument),
1574 s = node.style;
1575 s.top = best.y + "px";
1576 s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
1577 s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
1578
1579 return best;
1580 }
1581
1582 var place = {
1583 // summary:
1584 // Code to place a DOMNode relative to another DOMNode.
1585 // Load using require(["dijit/place"], function(place){ ... }).
1586
1587 at: function(node, pos, corners, padding){
1588 // summary:
1589 // Positions one of the node's corners at specified position
1590 // such that node is fully visible in viewport.
1591 // description:
1592 // NOTE: node is assumed to be absolutely or relatively positioned.
1593 // node: DOMNode
1594 // The node to position
1595 // pos: dijit/place.__Position
1596 // Object like {x: 10, y: 20}
1597 // corners: String[]
1598 // Array of Strings representing order to try corners in, like ["TR", "BL"].
1599 // Possible values are:
1600 //
1601 // - "BL" - bottom left
1602 // - "BR" - bottom right
1603 // - "TL" - top left
1604 // - "TR" - top right
1605 // padding: dijit/place.__Position?
1606 // optional param to set padding, to put some buffer around the element you want to position.
1607 // example:
1608 // Try to place node's top right corner at (10,20).
1609 // If that makes node go (partially) off screen, then try placing
1610 // bottom left corner at (10,20).
1611 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
1612 var choices = array.map(corners, function(corner){
1613 var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
1614 if(padding){
1615 c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
1616 c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
1617 }
1618 return c;
1619 });
1620
1621 return _place(node, choices);
1622 },
1623
1624 around: function(
1625 /*DomNode*/ node,
1626 /*DomNode|dijit/place.__Rectangle*/ anchor,
1627 /*String[]*/ positions,
1628 /*Boolean*/ leftToRight,
1629 /*Function?*/ layoutNode){
1630
1631 // summary:
1632 // Position node adjacent or kitty-corner to anchor
1633 // such that it's fully visible in viewport.
1634 // description:
1635 // Place node such that corner of node touches a corner of
1636 // aroundNode, and that node is fully visible.
1637 // anchor:
1638 // Either a DOMNode or a rectangle (object with x, y, width, height).
1639 // positions:
1640 // Ordered list of positions to try matching up.
1641 //
1642 // - before: places drop down to the left of the anchor node/widget, or to the right in the case
1643 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1644 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1645 // - after: places drop down to the right of the anchor node/widget, or to the left in the case
1646 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1647 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1648 // - before-centered: centers drop down to the left of the anchor node/widget, or to the right
1649 // in the case of RTL scripts like Hebrew and Arabic
1650 // - after-centered: centers drop down to the right of the anchor node/widget, or to the left
1651 // in the case of RTL scripts like Hebrew and Arabic
1652 // - above-centered: drop down is centered above anchor node
1653 // - above: drop down goes above anchor node, left sides aligned
1654 // - above-alt: drop down goes above anchor node, right sides aligned
1655 // - below-centered: drop down is centered above anchor node
1656 // - below: drop down goes below anchor node
1657 // - below-alt: drop down goes below anchor node, right sides aligned
1658 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1659 // For things like tooltip, they are displayed differently (and have different dimensions)
1660 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1661 // leftToRight:
1662 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
1663 // positions slightly.
1664 // example:
1665 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
1666 // This will try to position node such that node's top-left corner is at the same position
1667 // as the bottom left corner of the aroundNode (ie, put node below
1668 // aroundNode, with left edges aligned). If that fails it will try to put
1669 // the bottom-right corner of node where the top right corner of aroundNode is
1670 // (ie, put node above aroundNode, with right edges aligned)
1671 //
1672
1673 // if around is a DOMNode (or DOMNode id), convert to coordinates
1674 var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor)
1675 ? domGeometry.position(anchor, true)
1676 : anchor;
1677
1678 // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
1679 if(anchor.parentNode){
1680 // ignore nodes between position:relative and position:absolute
1681 var sawPosAbsolute = domStyle.getComputedStyle(anchor).position == "absolute";
1682 var parent = anchor.parentNode;
1683 while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
1684 var parentPos = domGeometry.position(parent, true),
1685 pcs = domStyle.getComputedStyle(parent);
1686 if(/relative|absolute/.test(pcs.position)){
1687 sawPosAbsolute = false;
1688 }
1689 if(!sawPosAbsolute && /hidden|auto|scroll/.test(pcs.overflow)){
1690 var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h);
1691 var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w);
1692 aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x);
1693 aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y);
1694 aroundNodePos.h = bottomYCoord - aroundNodePos.y;
1695 aroundNodePos.w = rightXCoord - aroundNodePos.x;
1696 }
1697 if(pcs.position == "absolute"){
1698 sawPosAbsolute = true;
1699 }
1700 parent = parent.parentNode;
1701 }
1702 }
1703
1704 var x = aroundNodePos.x,
1705 y = aroundNodePos.y,
1706 width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width),
1707 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);
1708
1709 // Convert positions arguments into choices argument for _place()
1710 var choices = [];
1711 function push(aroundCorner, corner){
1712 choices.push({
1713 aroundCorner: aroundCorner,
1714 corner: corner,
1715 pos: {
1716 x: {
1717 'L': x,
1718 'R': x + width,
1719 'M': x + (width >> 1)
1720 }[aroundCorner.charAt(1)],
1721 y: {
1722 'T': y,
1723 'B': y + height,
1724 'M': y + (height >> 1)
1725 }[aroundCorner.charAt(0)]
1726 }
1727 })
1728 }
1729 array.forEach(positions, function(pos){
1730 var ltr = leftToRight;
1731 switch(pos){
1732 case "above-centered":
1733 push("TM", "BM");
1734 break;
1735 case "below-centered":
1736 push("BM", "TM");
1737 break;
1738 case "after-centered":
1739 ltr = !ltr;
1740 // fall through
1741 case "before-centered":
1742 push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
1743 break;
1744 case "after":
1745 ltr = !ltr;
1746 // fall through
1747 case "before":
1748 push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
1749 push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
1750 break;
1751 case "below-alt":
1752 ltr = !ltr;
1753 // fall through
1754 case "below":
1755 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1756 push(ltr ? "BL" : "BR", ltr ? "TL" : "TR");
1757 push(ltr ? "BR" : "BL", ltr ? "TR" : "TL");
1758 break;
1759 case "above-alt":
1760 ltr = !ltr;
1761 // fall through
1762 case "above":
1763 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1764 push(ltr ? "TL" : "TR", ltr ? "BL" : "BR");
1765 push(ltr ? "TR" : "TL", ltr ? "BR" : "BL");
1766 break;
1767 default:
1768 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
1769 // Not meant to be used directly.
1770 push(pos.aroundCorner, pos.corner);
1771 }
1772 });
1773
1774 var position = _place(node, choices, layoutNode, {w: width, h: height});
1775 position.aroundNodePos = aroundNodePos;
1776
1777 return position;
1778 }
1779 };
1780
1781 /*=====
1782 place.__Position = {
1783 // x: Integer
1784 // horizontal coordinate in pixels, relative to document body
1785 // y: Integer
1786 // vertical coordinate in pixels, relative to document body
1787 };
1788 place.__Rectangle = {
1789 // x: Integer
1790 // horizontal offset in pixels, relative to document body
1791 // y: Integer
1792 // vertical offset in pixels, relative to document body
1793 // w: Integer
1794 // width in pixels. Can also be specified as "width" for backwards-compatibility.
1795 // h: Integer
1796 // height in pixels. Can also be specified as "height" for backwards-compatibility.
1797 };
1798 =====*/
1799
1800 return dijit.place = place; // setting dijit.place for back-compat, remove for 2.0
1801 });
1802
1803 },
1804 'dijit/_HasDropDown':function(){
1805 define("dijit/_HasDropDown", [
1806 "dojo/_base/declare", // declare
1807 "dojo/_base/Deferred",
1808 "dojo/_base/event", // event.stop
1809 "dojo/dom", // dom.isDescendant
1810 "dojo/dom-attr", // domAttr.set
1811 "dojo/dom-class", // domClass.add domClass.contains domClass.remove
1812 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
1813 "dojo/dom-style", // domStyle.set
1814 "dojo/has", // has("touch")
1815 "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
1816 "dojo/_base/lang", // lang.hitch lang.isFunction
1817 "dojo/on",
1818 "dojo/window", // winUtils.getBox
1819 "./registry", // registry.byNode()
1820 "./focus",
1821 "./popup",
1822 "./_FocusMixin"
1823 ], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, on,
1824 winUtils, registry, focus, popup, _FocusMixin){
1825
1826
1827 // module:
1828 // dijit/_HasDropDown
1829
1830 return declare("dijit._HasDropDown", _FocusMixin, {
1831 // summary:
1832 // Mixin for widgets that need drop down ability.
1833
1834 // _buttonNode: [protected] DomNode
1835 // The button/icon/node to click to display the drop down.
1836 // Can be set via a data-dojo-attach-point assignment.
1837 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
1838 _buttonNode: null,
1839
1840 // _arrowWrapperNode: [protected] DomNode
1841 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
1842 // on where the drop down is set to be positioned.
1843 // Can be set via a data-dojo-attach-point assignment.
1844 // If missing, then _buttonNode will be used.
1845 _arrowWrapperNode: null,
1846
1847 // _popupStateNode: [protected] DomNode
1848 // The node to set the popupActive class on.
1849 // Can be set via a data-dojo-attach-point assignment.
1850 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
1851 _popupStateNode: null,
1852
1853 // _aroundNode: [protected] DomNode
1854 // The node to display the popup around.
1855 // Can be set via a data-dojo-attach-point assignment.
1856 // If missing, then domNode will be used.
1857 _aroundNode: null,
1858
1859 // dropDown: [protected] Widget
1860 // The widget to display as a popup. This widget *must* be
1861 // defined before the startup function is called.
1862 dropDown: null,
1863
1864 // autoWidth: [protected] Boolean
1865 // Set to true to make the drop down at least as wide as this
1866 // widget. Set to false if the drop down should just be its
1867 // default width
1868 autoWidth: true,
1869
1870 // forceWidth: [protected] Boolean
1871 // Set to true to make the drop down exactly as wide as this
1872 // widget. Overrides autoWidth.
1873 forceWidth: false,
1874
1875 // maxHeight: [protected] Integer
1876 // The max height for our dropdown.
1877 // Any dropdown taller than this will have scrollbars.
1878 // Set to 0 for no max height, or -1 to limit height to available space in viewport
1879 maxHeight: 0,
1880
1881 // dropDownPosition: [const] String[]
1882 // This variable controls the position of the drop down.
1883 // It's an array of strings with the following values:
1884 //
1885 // - before: places drop down to the left of the target node/widget, or to the right in
1886 // the case of RTL scripts like Hebrew and Arabic
1887 // - after: places drop down to the right of the target node/widget, or to the left in
1888 // the case of RTL scripts like Hebrew and Arabic
1889 // - above: drop down goes above target node
1890 // - below: drop down goes below target node
1891 //
1892 // The list is positions is tried, in order, until a position is found where the drop down fits
1893 // within the viewport.
1894 //
1895 dropDownPosition: ["below","above"],
1896
1897 // _stopClickEvents: Boolean
1898 // When set to false, the click events will not be stopped, in
1899 // case you want to use them in your subclass
1900 _stopClickEvents: true,
1901
1902 _onDropDownMouseDown: function(/*Event*/ e){
1903 // summary:
1904 // Callback when the user mousedown's on the arrow icon
1905 if(this.disabled || this.readOnly){ return; }
1906
1907 // Prevent default to stop things like text selection, but don't stop propagation, so that:
1908 // 1. TimeTextBox etc. can focus the <input> on mousedown
1909 // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
1910 // 3. user defined onMouseDown handler fires
1911 e.preventDefault();
1912
1913 this._docHandler = this.connect(this.ownerDocument, "mouseup", "_onDropDownMouseUp");
1914
1915 this.toggleDropDown();
1916 },
1917
1918 _onDropDownMouseUp: function(/*Event?*/ e){
1919 // summary:
1920 // Callback when the user lifts their mouse after mouse down on the arrow icon.
1921 // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
1922 // drop down widget. If the event is missing, then we are not
1923 // a mouseup event.
1924 //
1925 // This is useful for the common mouse movement pattern
1926 // with native browser `<select>` nodes:
1927 //
1928 // 1. mouse down on the select node (probably on the arrow)
1929 // 2. move mouse to a menu item while holding down the mouse button
1930 // 3. mouse up. this selects the menu item as though the user had clicked it.
1931 if(e && this._docHandler){
1932 this.disconnect(this._docHandler);
1933 }
1934 var dropDown = this.dropDown, overMenu = false;
1935
1936 if(e && this._opened){
1937 // This code deals with the corner-case when the drop down covers the original widget,
1938 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
1939 // Find out if our target is somewhere in our dropdown widget,
1940 // but not over our _buttonNode (the clickable node)
1941 var c = domGeometry.position(this._buttonNode, true);
1942 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
1943 !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
1944 var t = e.target;
1945 while(t && !overMenu){
1946 if(domClass.contains(t, "dijitPopup")){
1947 overMenu = true;
1948 }else{
1949 t = t.parentNode;
1950 }
1951 }
1952 if(overMenu){
1953 t = e.target;
1954 if(dropDown.onItemClick){
1955 var menuItem;
1956 while(t && !(menuItem = registry.byNode(t))){
1957 t = t.parentNode;
1958 }
1959 if(menuItem && menuItem.onClick && menuItem.getParent){
1960 menuItem.getParent().onItemClick(menuItem, e);
1961 }
1962 }
1963 return;
1964 }
1965 }
1966 }
1967 if(this._opened){
1968 if(dropDown.focus && dropDown.autoFocus !== false){
1969 // Focus the dropdown widget - do it on a delay so that we
1970 // don't steal back focus from the dropdown.
1971 this._focusDropDownTimer = this.defer(function(){
1972 dropDown.focus();
1973 delete this._focusDropDownTimer;
1974 });
1975 }
1976 }else{
1977 // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
1978 // defer() needed to make it work on IE (test DateTextBox)
1979 this.defer("focus");
1980 }
1981
1982 if(has("touch")){
1983 this._justGotMouseUp = true;
1984 this.defer(function(){
1985 this._justGotMouseUp = false;
1986 });
1987 }
1988 },
1989
1990 _onDropDownClick: function(/*Event*/ e){
1991 if(has("touch") && !this._justGotMouseUp){
1992 // If there was no preceding mousedown/mouseup (like on android), then simulate them to
1993 // toggle the drop down.
1994 //
1995 // The if(has("touch") is necessary since IE and desktop safari get spurious onclick events
1996 // when there are nested tables (specifically, clicking on a table that holds a dijit/form/Select,
1997 // but not on the Select itself, causes an onclick event on the Select)
1998 this._onDropDownMouseDown(e);
1999 this._onDropDownMouseUp(e);
2000 }
2001
2002 // The drop down was already opened on mousedown/keydown; just need to call stopEvent().
2003 if(this._stopClickEvents){
2004 event.stop(e);
2005 }
2006 },
2007
2008 buildRendering: function(){
2009 this.inherited(arguments);
2010
2011 this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
2012 this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
2013
2014 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
2015 // based on where drop down will normally appear
2016 var defaultPos = {
2017 "after" : this.isLeftToRight() ? "Right" : "Left",
2018 "before" : this.isLeftToRight() ? "Left" : "Right",
2019 "above" : "Up",
2020 "below" : "Down",
2021 "left" : "Left",
2022 "right" : "Right"
2023 }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
2024 domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
2025 },
2026
2027 postCreate: function(){
2028 // summary:
2029 // set up nodes and connect our mouse and keyboard events
2030
2031 this.inherited(arguments);
2032
2033 var keyboardEventNode = this.focusNode || this.domNode;
2034 this.own(
2035 on(this._buttonNode, "mousedown", lang.hitch(this, "_onDropDownMouseDown")),
2036 on(this._buttonNode, "click", lang.hitch(this, "_onDropDownClick")),
2037 on(keyboardEventNode, "keydown", lang.hitch(this, "_onKey")),
2038 on(keyboardEventNode, "keyup", lang.hitch(this, "_onKeyUp"))
2039 );
2040 },
2041
2042 destroy: function(){
2043 if(this.dropDown){
2044 // Destroy the drop down, unless it's already been destroyed. This can happen because
2045 // the drop down is a direct child of <body> even though it's logically my child.
2046 if(!this.dropDown._destroyed){
2047 this.dropDown.destroyRecursive();
2048 }
2049 delete this.dropDown;
2050 }
2051 this.inherited(arguments);
2052 },
2053
2054 _onKey: function(/*Event*/ e){
2055 // summary:
2056 // Callback when the user presses a key while focused on the button node
2057
2058 if(this.disabled || this.readOnly){ return; }
2059 var d = this.dropDown, target = e.target;
2060 if(d && this._opened && d.handleKey){
2061 if(d.handleKey(e) === false){
2062 /* false return code means that the drop down handled the key */
2063 event.stop(e);
2064 return;
2065 }
2066 }
2067 if(d && this._opened && e.keyCode == keys.ESCAPE){
2068 this.closeDropDown();
2069 event.stop(e);
2070 }else if(!this._opened &&
2071 (e.keyCode == keys.DOWN_ARROW ||
2072 ( (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
2073 //ignore enter and space if the event is for a text input
2074 ((target.tagName || "").toLowerCase() !== 'input' ||
2075 (target.type && target.type.toLowerCase() !== 'text'))))){
2076 // Toggle the drop down, but wait until keyup so that the drop down doesn't
2077 // get a stray keyup event, or in the case of key-repeat (because user held
2078 // down key for too long), stray keydown events
2079 this._toggleOnKeyUp = true;
2080 event.stop(e);
2081 }
2082 },
2083
2084 _onKeyUp: function(){
2085 if(this._toggleOnKeyUp){
2086 delete this._toggleOnKeyUp;
2087 this.toggleDropDown();
2088 var d = this.dropDown; // drop down may not exist until toggleDropDown() call
2089 if(d && d.focus){
2090 this.defer(lang.hitch(d, "focus"), 1);
2091 }
2092 }
2093 },
2094
2095 _onBlur: function(){
2096 // summary:
2097 // Called magically when focus has shifted away from this widget and it's dropdown
2098
2099 // Don't focus on button if the user has explicitly focused on something else (happens
2100 // when user clicks another control causing the current popup to close)..
2101 // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
2102 // it when you display:none a node with focus.
2103 var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode);
2104
2105 this.closeDropDown(focusMe);
2106
2107 this.inherited(arguments);
2108 },
2109
2110 isLoaded: function(){
2111 // summary:
2112 // Returns true if the dropdown exists and it's data is loaded. This can
2113 // be overridden in order to force a call to loadDropDown().
2114 // tags:
2115 // protected
2116
2117 return true;
2118 },
2119
2120 loadDropDown: function(/*Function*/ loadCallback){
2121 // summary:
2122 // Creates the drop down if it doesn't exist, loads the data
2123 // if there's an href and it hasn't been loaded yet, and then calls
2124 // the given callback.
2125 // tags:
2126 // protected
2127
2128 // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
2129 loadCallback();
2130 },
2131
2132 loadAndOpenDropDown: function(){
2133 // summary:
2134 // Creates the drop down if it doesn't exist, loads the data
2135 // if there's an href and it hasn't been loaded yet, and
2136 // then opens the drop down. This is basically a callback when the
2137 // user presses the down arrow button to open the drop down.
2138 // returns: Deferred
2139 // Deferred for the drop down widget that
2140 // fires when drop down is created and loaded
2141 // tags:
2142 // protected
2143 var d = new Deferred(),
2144 afterLoad = lang.hitch(this, function(){
2145 this.openDropDown();
2146 d.resolve(this.dropDown);
2147 });
2148 if(!this.isLoaded()){
2149 this.loadDropDown(afterLoad);
2150 }else{
2151 afterLoad();
2152 }
2153 return d;
2154 },
2155
2156 toggleDropDown: function(){
2157 // summary:
2158 // Callback when the user presses the down arrow button or presses
2159 // the down arrow key to open/close the drop down.
2160 // Toggle the drop-down widget; if it is up, close it, if not, open it
2161 // tags:
2162 // protected
2163
2164 if(this.disabled || this.readOnly){ return; }
2165 if(!this._opened){
2166 this.loadAndOpenDropDown();
2167 }else{
2168 this.closeDropDown();
2169 }
2170 },
2171
2172 openDropDown: function(){
2173 // summary:
2174 // Opens the dropdown for this widget. To be called only when this.dropDown
2175 // has been created and is ready to display (ie, it's data is loaded).
2176 // returns:
2177 // return value of dijit/popup.open()
2178 // tags:
2179 // protected
2180
2181 var dropDown = this.dropDown,
2182 ddNode = dropDown.domNode,
2183 aroundNode = this._aroundNode || this.domNode,
2184 self = this;
2185
2186 // Prepare our popup's height and honor maxHeight if it exists.
2187
2188 // TODO: isn't maxHeight dependent on the return value from dijit/popup.open(),
2189 // ie, dependent on how much space is available (BK)
2190
2191 if(!this._preparedNode){
2192 this._preparedNode = true;
2193 // Check if we have explicitly set width and height on the dropdown widget dom node
2194 if(ddNode.style.width){
2195 this._explicitDDWidth = true;
2196 }
2197 if(ddNode.style.height){
2198 this._explicitDDHeight = true;
2199 }
2200 }
2201
2202 // Code for resizing dropdown (height limitation, or increasing width to match my width)
2203 if(this.maxHeight || this.forceWidth || this.autoWidth){
2204 var myStyle = {
2205 display: "",
2206 visibility: "hidden"
2207 };
2208 if(!this._explicitDDWidth){
2209 myStyle.width = "";
2210 }
2211 if(!this._explicitDDHeight){
2212 myStyle.height = "";
2213 }
2214 domStyle.set(ddNode, myStyle);
2215
2216 // Figure out maximum height allowed (if there is a height restriction)
2217 var maxHeight = this.maxHeight;
2218 if(maxHeight == -1){
2219 // limit height to space available in viewport either above or below my domNode
2220 // (whichever side has more room)
2221 var viewport = winUtils.getBox(this.ownerDocument),
2222 position = domGeometry.position(aroundNode, false);
2223 maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
2224 }
2225
2226 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
2227 // so we call startup() and also get the size
2228 popup.moveOffScreen(dropDown);
2229
2230 if(dropDown.startup && !dropDown._started){
2231 dropDown.startup(); // this has to be done after being added to the DOM
2232 }
2233 // Get size of drop down, and determine if vertical scroll bar needed. If no scroll bar needed,
2234 // use overflow:visible rather than overflow:hidden so off-by-one errors don't hide drop down border.
2235 var mb = domGeometry.getMarginSize(ddNode);
2236 var overHeight = (maxHeight && mb.h > maxHeight);
2237 domStyle.set(ddNode, {
2238 overflowX: "visible",
2239 overflowY: overHeight ? "auto" : "visible"
2240 });
2241 if(overHeight){
2242 mb.h = maxHeight;
2243 if("w" in mb){
2244 mb.w += 16; // room for vertical scrollbar
2245 }
2246 }else{
2247 delete mb.h;
2248 }
2249
2250 // Adjust dropdown width to match or be larger than my width
2251 if(this.forceWidth){
2252 mb.w = aroundNode.offsetWidth;
2253 }else if(this.autoWidth){
2254 mb.w = Math.max(mb.w, aroundNode.offsetWidth);
2255 }else{
2256 delete mb.w;
2257 }
2258
2259 // And finally, resize the dropdown to calculated height and width
2260 if(lang.isFunction(dropDown.resize)){
2261 dropDown.resize(mb);
2262 }else{
2263 domGeometry.setMarginBox(ddNode, mb);
2264 }
2265 }
2266
2267 var retVal = popup.open({
2268 parent: this,
2269 popup: dropDown,
2270 around: aroundNode,
2271 orient: this.dropDownPosition,
2272 onExecute: function(){
2273 self.closeDropDown(true);
2274 },
2275 onCancel: function(){
2276 self.closeDropDown(true);
2277 },
2278 onClose: function(){
2279 domAttr.set(self._popupStateNode, "popupActive", false);
2280 domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
2281 self._set("_opened", false); // use set() because _CssStateMixin is watching
2282 }
2283 });
2284 domAttr.set(this._popupStateNode, "popupActive", "true");
2285 domClass.add(this._popupStateNode, "dijitHasDropDownOpen");
2286 this._set("_opened", true); // use set() because _CssStateMixin is watching
2287 this.domNode.setAttribute("aria-expanded", "true");
2288
2289 return retVal;
2290 },
2291
2292 closeDropDown: function(/*Boolean*/ focus){
2293 // summary:
2294 // Closes the drop down on this widget
2295 // focus:
2296 // If true, refocuses the button widget
2297 // tags:
2298 // protected
2299
2300 if(this._focusDropDownTimer){
2301 this._focusDropDownTimer.remove();
2302 delete this._focusDropDownTimer;
2303 }
2304 if(this._opened){
2305 this.domNode.setAttribute("aria-expanded", "false");
2306 if(focus){ this.focus(); }
2307 popup.close(this.dropDown);
2308 this._opened = false;
2309 }
2310 }
2311
2312 });
2313 });
2314
2315 },
2316 'dijit/tree/TreeStoreModel':function(){
2317 define("dijit/tree/TreeStoreModel", [
2318 "dojo/_base/array", // array.filter array.forEach array.indexOf array.some
2319 "dojo/aspect", // aspect.after
2320 "dojo/_base/declare", // declare
2321 "dojo/_base/lang" // lang.hitch
2322 ], function(array, aspect, declare, lang){
2323
2324 // module:
2325 // dijit/tree/TreeStoreModel
2326
2327 return declare("dijit.tree.TreeStoreModel", null, {
2328 // summary:
2329 // Implements dijit/Tree/model connecting to a dojo.data store with a single
2330 // root item. Any methods passed into the constructor will override
2331 // the ones defined here.
2332
2333 // store: dojo/data/api/Read
2334 // Underlying store
2335 store: null,
2336
2337 // childrenAttrs: String[]
2338 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
2339 childrenAttrs: ["children"],
2340
2341 // newItemIdAttr: String
2342 // Name of attribute in the Object passed to newItem() that specifies the id.
2343 //
2344 // If newItemIdAttr is set then it's used when newItem() is called to see if an
2345 // item with the same id already exists, and if so just links to the old item
2346 // (so that the old item ends up with two parents).
2347 //
2348 // Setting this to null or "" will make every drop create a new item.
2349 newItemIdAttr: "id",
2350
2351 // labelAttr: String
2352 // If specified, get label for tree node from this attribute, rather
2353 // than by calling store.getLabel()
2354 labelAttr: "",
2355
2356 // root: [readonly] dojo/data/Item
2357 // Pointer to the root item (read only, not a parameter)
2358 root: null,
2359
2360 // query: anything
2361 // Specifies datastore query to return the root item for the tree.
2362 // Must only return a single item. Alternately can just pass in pointer
2363 // to root item.
2364 // example:
2365 // | {id:'ROOT'}
2366 query: null,
2367
2368 // deferItemLoadingUntilExpand: Boolean
2369 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
2370 // until they are expanded. This allows for lazying loading where only one
2371 // loadItem (and generally one network call, consequently) per expansion
2372 // (rather than one for each child).
2373 // This relies on partial loading of the children items; each children item of a
2374 // fully loaded item should contain the label and info about having children.
2375 deferItemLoadingUntilExpand: false,
2376
2377 constructor: function(/* Object */ args){
2378 // summary:
2379 // Passed the arguments listed above (store, etc)
2380 // tags:
2381 // private
2382
2383 lang.mixin(this, args);
2384
2385 this.connects = [];
2386
2387 var store = this.store;
2388 if(!store.getFeatures()['dojo.data.api.Identity']){
2389 throw new Error("dijit.tree.TreeStoreModel: store must support dojo.data.Identity");
2390 }
2391
2392 // if the store supports Notification, subscribe to the notification events
2393 if(store.getFeatures()['dojo.data.api.Notification']){
2394 this.connects = this.connects.concat([
2395 aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true),
2396 aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true),
2397 aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true)
2398 ]);
2399 }
2400 },
2401
2402 destroy: function(){
2403 var h;
2404 while(h = this.connects.pop()){ h.remove(); }
2405 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
2406 },
2407
2408 // =======================================================================
2409 // Methods for traversing hierarchy
2410
2411 getRoot: function(onItem, onError){
2412 // summary:
2413 // Calls onItem with the root item for the tree, possibly a fabricated item.
2414 // Calls onError on error.
2415 if(this.root){
2416 onItem(this.root);
2417 }else{
2418 this.store.fetch({
2419 query: this.query,
2420 onComplete: lang.hitch(this, function(items){
2421 if(items.length != 1){
2422 throw new Error("dijit.tree.TreeStoreModel: root query returned " + items.length +
2423 " items, but must return exactly one");
2424 }
2425 this.root = items[0];
2426 onItem(this.root);
2427 }),
2428 onError: onError
2429 });
2430 }
2431 },
2432
2433 mayHaveChildren: function(/*dojo/data/Item*/ item){
2434 // summary:
2435 // Tells if an item has or may have children. Implementing logic here
2436 // avoids showing +/- expando icon for nodes that we know don't have children.
2437 // (For efficiency reasons we may not want to check if an element actually
2438 // has children until user clicks the expando node)
2439 return array.some(this.childrenAttrs, function(attr){
2440 return this.store.hasAttribute(item, attr);
2441 }, this);
2442 },
2443
2444 getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
2445 // summary:
2446 // Calls onComplete() with array of child items of given parent item, all loaded.
2447
2448 var store = this.store;
2449 if(!store.isItemLoaded(parentItem)){
2450 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
2451 // mode, so we will load it and just return the children (without loading each
2452 // child item)
2453 var getChildren = lang.hitch(this, arguments.callee);
2454 store.loadItem({
2455 item: parentItem,
2456 onItem: function(parentItem){
2457 getChildren(parentItem, onComplete, onError);
2458 },
2459 onError: onError
2460 });
2461 return;
2462 }
2463 // get children of specified item
2464 var childItems = [];
2465 for(var i=0; i<this.childrenAttrs.length; i++){
2466 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
2467 childItems = childItems.concat(vals);
2468 }
2469
2470 // count how many items need to be loaded
2471 var _waitCount = 0;
2472 if(!this.deferItemLoadingUntilExpand){
2473 array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
2474 }
2475
2476 if(_waitCount == 0){
2477 // all items are already loaded (or we aren't loading them). proceed...
2478 onComplete(childItems);
2479 }else{
2480 // still waiting for some or all of the items to load
2481 array.forEach(childItems, function(item, idx){
2482 if(!store.isItemLoaded(item)){
2483 store.loadItem({
2484 item: item,
2485 onItem: function(item){
2486 childItems[idx] = item;
2487 if(--_waitCount == 0){
2488 // all nodes have been loaded, send them to the tree
2489 onComplete(childItems);
2490 }
2491 },
2492 onError: onError
2493 });
2494 }
2495 });
2496 }
2497 },
2498
2499 // =======================================================================
2500 // Inspecting items
2501
2502 isItem: function(/* anything */ something){
2503 return this.store.isItem(something); // Boolean
2504 },
2505
2506 fetchItemByIdentity: function(/* object */ keywordArgs){
2507 this.store.fetchItemByIdentity(keywordArgs);
2508 },
2509
2510 getIdentity: function(/* item */ item){
2511 return this.store.getIdentity(item); // Object
2512 },
2513
2514 getLabel: function(/*dojo/data/Item*/ item){
2515 // summary:
2516 // Get the label for an item
2517 if(this.labelAttr){
2518 return this.store.getValue(item,this.labelAttr); // String
2519 }else{
2520 return this.store.getLabel(item); // String
2521 }
2522 },
2523
2524 // =======================================================================
2525 // Write interface
2526
2527 newItem: function(/* dijit/tree/dndSource.__Item */ args, /*dojo/data/api/Item*/ parent, /*int?*/ insertIndex){
2528 // summary:
2529 // Creates a new item. See `dojo/data/api/Write` for details on args.
2530 // Used in drag & drop when item from external source dropped onto tree.
2531 // description:
2532 // Developers will need to override this method if new items get added
2533 // to parents with multiple children attributes, in order to define which
2534 // children attribute points to the new item.
2535
2536 var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
2537
2538 if(this.newItemIdAttr && args[this.newItemIdAttr]){
2539 // Maybe there's already a corresponding item in the store; if so, reuse it.
2540 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
2541 if(item){
2542 // There's already a matching item in store, use it
2543 this.pasteItem(item, null, parent, true, insertIndex);
2544 }else{
2545 // Create new item in the tree, based on the drag source.
2546 LnewItem=this.store.newItem(args, pInfo);
2547 if(LnewItem && (insertIndex!=undefined)){
2548 // Move new item to desired position
2549 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
2550 }
2551 }
2552 }});
2553 }else{
2554 // [as far as we know] there is no id so we must assume this is a new item
2555 LnewItem=this.store.newItem(args, pInfo);
2556 if(LnewItem && (insertIndex!=undefined)){
2557 // Move new item to desired position
2558 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
2559 }
2560 }
2561 },
2562
2563 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
2564 // summary:
2565 // Move or copy an item from one parent item to another.
2566 // Used in drag & drop
2567 var store = this.store,
2568 parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
2569
2570 // remove child from source item, and record the attribute that child occurred in
2571 if(oldParentItem){
2572 array.forEach(this.childrenAttrs, function(attr){
2573 if(store.containsValue(oldParentItem, attr, childItem)){
2574 if(!bCopy){
2575 var values = array.filter(store.getValues(oldParentItem, attr), function(x){
2576 return x != childItem;
2577 });
2578 store.setValues(oldParentItem, attr, values);
2579 }
2580 parentAttr = attr;
2581 }
2582 });
2583 }
2584
2585 // modify target item's children attribute to include this item
2586 if(newParentItem){
2587 if(typeof insertIndex == "number"){
2588 // call slice() to avoid modifying the original array, confusing the data store
2589 var childItems = store.getValues(newParentItem, parentAttr).slice();
2590 childItems.splice(insertIndex, 0, childItem);
2591 store.setValues(newParentItem, parentAttr, childItems);
2592 }else{
2593 store.setValues(newParentItem, parentAttr,
2594 store.getValues(newParentItem, parentAttr).concat(childItem));
2595 }
2596 }
2597 },
2598
2599 // =======================================================================
2600 // Callbacks
2601
2602 onChange: function(/*dojo/data/Item*/ /*===== item =====*/){
2603 // summary:
2604 // Callback whenever an item has changed, so that Tree
2605 // can update the label, icon, etc. Note that changes
2606 // to an item's children or parent(s) will trigger an
2607 // onChildrenChange() so you can ignore those changes here.
2608 // tags:
2609 // callback
2610 },
2611
2612 onChildrenChange: function(/*===== parent, newChildrenList =====*/){
2613 // summary:
2614 // Callback to do notifications about new, updated, or deleted items.
2615 // parent: dojo/data/Item
2616 // newChildrenList: dojo/data/Item[]
2617 // tags:
2618 // callback
2619 },
2620
2621 onDelete: function(/*dojo/data/Item*/ /*===== item =====*/){
2622 // summary:
2623 // Callback when an item has been deleted.
2624 // description:
2625 // Note that there will also be an onChildrenChange() callback for the parent
2626 // of this item.
2627 // tags:
2628 // callback
2629 },
2630
2631 // =======================================================================
2632 // Events from data store
2633
2634 onNewItem: function(/* dojo/data/Item */ item, /* Object */ parentInfo){
2635 // summary:
2636 // Handler for when new items appear in the store, either from a drop operation
2637 // or some other way. Updates the tree view (if necessary).
2638 // description:
2639 // If the new item is a child of an existing item,
2640 // calls onChildrenChange() with the new list of children
2641 // for that existing item.
2642 //
2643 // tags:
2644 // extension
2645
2646 // We only care about the new item if it has a parent that corresponds to a TreeNode
2647 // we are currently displaying
2648 if(!parentInfo){
2649 return;
2650 }
2651
2652 // Call onChildrenChange() on parent (ie, existing) item with new list of children
2653 // In the common case, the new list of children is simply parentInfo.newValue or
2654 // [ parentInfo.newValue ], although if items in the store has multiple
2655 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
2656 // so call getChildren() to be sure to get right answer.
2657 this.getChildren(parentInfo.item, lang.hitch(this, function(children){
2658 this.onChildrenChange(parentInfo.item, children);
2659 }));
2660 },
2661
2662 onDeleteItem: function(/*Object*/ item){
2663 // summary:
2664 // Handler for delete notifications from underlying store
2665 this.onDelete(item);
2666 },
2667
2668 onSetItem: function(item, attribute /*===== , oldValue, newValue =====*/){
2669 // summary:
2670 // Updates the tree view according to changes in the data store.
2671 // description:
2672 // Handles updates to an item's children by calling onChildrenChange(), and
2673 // other updates to an item by calling onChange().
2674 //
2675 // See `onNewItem` for more details on handling updates to an item's children.
2676 // item: Item
2677 // attribute: attribute-name-string
2678 // oldValue: Object|Array
2679 // newValue: Object|Array
2680 // tags:
2681 // extension
2682
2683 if(array.indexOf(this.childrenAttrs, attribute) != -1){
2684 // item's children list changed
2685 this.getChildren(item, lang.hitch(this, function(children){
2686 // See comments in onNewItem() about calling getChildren()
2687 this.onChildrenChange(item, children);
2688 }));
2689 }else{
2690 // item's label/icon/etc. changed.
2691 this.onChange(item);
2692 }
2693 }
2694 });
2695 });
2696
2697 },
2698 'dijit/_MenuBase':function(){
2699 define("dijit/_MenuBase", [
2700 "dojo/_base/array", // array.indexOf
2701 "dojo/_base/declare", // declare
2702 "dojo/dom", // dom.isDescendant domClass.replace
2703 "dojo/dom-attr",
2704 "dojo/dom-class", // domClass.replace
2705 "dojo/_base/lang", // lang.hitch
2706 "dojo/mouse", // mouse.enter, mouse.leave
2707 "dojo/on",
2708 "dojo/window",
2709 "./a11yclick",
2710 "./popup",
2711 "./registry",
2712 "./_Widget",
2713 "./_KeyNavContainer",
2714 "./_TemplatedMixin"
2715 ], function(array, declare, dom, domAttr, domClass, lang, mouse, on, winUtils,
2716 a11yclick, pm, registry, _Widget, _KeyNavContainer, _TemplatedMixin){
2717
2718
2719 // module:
2720 // dijit/_MenuBase
2721
2722 return declare("dijit._MenuBase",
2723 [_Widget, _TemplatedMixin, _KeyNavContainer],
2724 {
2725 // summary:
2726 // Base class for Menu and MenuBar
2727
2728 // parentMenu: [readonly] Widget
2729 // pointer to menu that displayed me
2730 parentMenu: null,
2731
2732 // popupDelay: Integer
2733 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
2734 popupDelay: 500,
2735
2736 // autoFocus: Boolean
2737 // A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
2738 // or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
2739 autoFocus: false,
2740
2741 childSelector: function(/*DOMNode*/ node){
2742 // summary:
2743 // Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
2744 // like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
2745 // tags:
2746 // protected
2747
2748 var widget = registry.byNode(node);
2749 return node.parentNode == this.containerNode && widget && widget.focus;
2750 },
2751
2752 postCreate: function(){
2753 var self = this,
2754 matches = typeof this.childSelector == "string" ? this.childSelector : lang.hitch(this, "childSelector");
2755 this.own(
2756 on(this.containerNode, on.selector(matches, mouse.enter), function(){
2757 self.onItemHover(registry.byNode(this));
2758 }),
2759 on(this.containerNode, on.selector(matches, mouse.leave), function(){
2760 self.onItemUnhover(registry.byNode(this));
2761 }),
2762 on(this.containerNode, on.selector(matches, a11yclick), function(evt){
2763 self.onItemClick(registry.byNode(this), evt);
2764 evt.stopPropagation();
2765 evt.preventDefault();
2766 })
2767 );
2768 this.inherited(arguments);
2769 },
2770
2771 onExecute: function(){
2772 // summary:
2773 // Attach point for notification about when a menu item has been executed.
2774 // This is an internal mechanism used for Menus to signal to their parent to
2775 // close them, because they are about to execute the onClick handler. In
2776 // general developers should not attach to or override this method.
2777 // tags:
2778 // protected
2779 },
2780
2781 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
2782 // summary:
2783 // Attach point for notification about when the user cancels the current menu
2784 // This is an internal mechanism used for Menus to signal to their parent to
2785 // close them. In general developers should not attach to or override this method.
2786 // tags:
2787 // protected
2788 },
2789
2790 _moveToPopup: function(/*Event*/ evt){
2791 // summary:
2792 // This handles the right arrow key (left arrow key on RTL systems),
2793 // which will either open a submenu, or move to the next item in the
2794 // ancestor MenuBar
2795 // tags:
2796 // private
2797
2798 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
2799 this.onItemClick(this.focusedChild, evt);
2800 }else{
2801 var topMenu = this._getTopMenu();
2802 if(topMenu && topMenu._isMenuBar){
2803 topMenu.focusNext();
2804 }
2805 }
2806 },
2807
2808 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
2809 // summary:
2810 // This handler is called when the mouse moves over the popup.
2811 // tags:
2812 // private
2813
2814 // if the mouse hovers over a menu popup that is in pending-close state,
2815 // then stop the close operation.
2816 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
2817 if(this.currentPopup && this.currentPopup._pendingClose_timer){
2818 var parentMenu = this.currentPopup.parentMenu;
2819 // highlight the parent menu item pointing to this popup
2820 if(parentMenu.focusedChild){
2821 parentMenu.focusedChild._setSelected(false);
2822 }
2823 parentMenu.focusedChild = this.currentPopup.from_item;
2824 parentMenu.focusedChild._setSelected(true);
2825 // cancel the pending close
2826 this._stopPendingCloseTimer(this.currentPopup);
2827 }
2828 },
2829
2830 onItemHover: function(/*MenuItem*/ item){
2831 // summary:
2832 // Called when cursor is over a MenuItem.
2833 // tags:
2834 // protected
2835
2836 // Don't do anything unless user has "activated" the menu by:
2837 // 1) clicking it
2838 // 2) opening it from a parent menu (which automatically focuses it)
2839 if(this.isActive){
2840 this.focusChild(item);
2841 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
2842 this.hover_timer = this.defer("_openPopup", this.popupDelay);
2843 }
2844 }
2845 // if the user is mixing mouse and keyboard navigation,
2846 // then the menu may not be active but a menu item has focus,
2847 // but it's not the item that the mouse just hovered over.
2848 // To avoid both keyboard and mouse selections, use the latest.
2849 if(this.focusedChild){
2850 this.focusChild(item);
2851 }
2852 this._hoveredChild = item;
2853
2854 item._set("hovering", true);
2855 },
2856
2857 _onChildBlur: function(item){
2858 // summary:
2859 // Called when a child MenuItem becomes inactive because focus
2860 // has been removed from the MenuItem *and* it's descendant menus.
2861 // tags:
2862 // private
2863 this._stopPopupTimer();
2864 item._setSelected(false);
2865 // Close all popups that are open and descendants of this menu
2866 var itemPopup = item.popup;
2867 if(itemPopup){
2868 this._stopPendingCloseTimer(itemPopup);
2869 itemPopup._pendingClose_timer = this.defer(function(){
2870 itemPopup._pendingClose_timer = null;
2871 if(itemPopup.parentMenu){
2872 itemPopup.parentMenu.currentPopup = null;
2873 }
2874 pm.close(itemPopup); // this calls onClose
2875 }, this.popupDelay);
2876 }
2877 },
2878
2879 onItemUnhover: function(/*MenuItem*/ item){
2880 // summary:
2881 // Callback fires when mouse exits a MenuItem
2882 // tags:
2883 // protected
2884
2885 if(this.isActive){
2886 this._stopPopupTimer();
2887 }
2888 if(this._hoveredChild == item){ this._hoveredChild = null; }
2889
2890 item._set("hovering", false);
2891 },
2892
2893 _stopPopupTimer: function(){
2894 // summary:
2895 // Cancels the popup timer because the user has stop hovering
2896 // on the MenuItem, etc.
2897 // tags:
2898 // private
2899 if(this.hover_timer){
2900 this.hover_timer = this.hover_timer.remove();
2901 }
2902 },
2903
2904 _stopPendingCloseTimer: function(/*dijit/_WidgetBase*/ popup){
2905 // summary:
2906 // Cancels the pending-close timer because the close has been preempted
2907 // tags:
2908 // private
2909 if(popup._pendingClose_timer){
2910 popup._pendingClose_timer = popup._pendingClose_timer.remove();
2911 }
2912 },
2913
2914 _stopFocusTimer: function(){
2915 // summary:
2916 // Cancels the pending-focus timer because the menu was closed before focus occured
2917 // tags:
2918 // private
2919 if(this._focus_timer){
2920 this._focus_timer = this._focus_timer.remove();
2921 }
2922 },
2923
2924 _getTopMenu: function(){
2925 // summary:
2926 // Returns the top menu in this chain of Menus
2927 // tags:
2928 // private
2929 for(var top=this; top.parentMenu; top=top.parentMenu);
2930 return top;
2931 },
2932
2933 onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
2934 // summary:
2935 // Handle clicks on an item.
2936 // tags:
2937 // private
2938
2939 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
2940 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
2941 this._markActive();
2942 }
2943
2944 this.focusChild(item);
2945
2946 if(item.disabled){ return false; }
2947
2948 if(item.popup){
2949 this._openPopup(evt.type == "keypress");
2950 }else{
2951 // before calling user defined handler, close hierarchy of menus
2952 // and restore focus to place it was when menu was opened
2953 this.onExecute();
2954
2955 // user defined handler for click
2956 item._onClick ? item._onClick(evt) : item.onClick(evt);
2957 }
2958 },
2959
2960 _openPopup: function(/*Boolean*/ focus){
2961 // summary:
2962 // Open the popup to the side of/underneath the current menu item, and optionally focus first item
2963 // tags:
2964 // protected
2965
2966 this._stopPopupTimer();
2967 var from_item = this.focusedChild;
2968 if(!from_item){ return; } // the focused child lost focus since the timer was started
2969 var popup = from_item.popup;
2970 if(!popup.isShowingNow){
2971 if(this.currentPopup){
2972 this._stopPendingCloseTimer(this.currentPopup);
2973 pm.close(this.currentPopup);
2974 }
2975 popup.parentMenu = this;
2976 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
2977 var self = this;
2978 pm.open({
2979 parent: this,
2980 popup: popup,
2981 around: from_item.domNode,
2982 orient: this._orient || ["after", "before"],
2983 onCancel: function(){ // called when the child menu is canceled
2984 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
2985 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
2986 self.focusChild(from_item); // put focus back on my node
2987 self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
2988 from_item._setSelected(true); // oops, _cleanUp() deselected the item
2989 self.focusedChild = from_item; // and unset focusedChild
2990 },
2991 onExecute: lang.hitch(this, "_cleanUp")
2992 });
2993
2994 this.currentPopup = popup;
2995 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
2996 popup.connect(popup.domNode, "onmouseenter", lang.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
2997 }
2998
2999 if(focus && popup.focus){
3000 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar), then focus the popup.
3001 // If the cursor happens to collide with the popup, it will generate an onmouseover event
3002 // even though the mouse wasn't moved. Use defer() to call popup.focus so that
3003 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
3004 popup._focus_timer = this.defer(lang.hitch(popup, function(){
3005 this._focus_timer = null;
3006 this.focus();
3007 }));
3008 }
3009 },
3010
3011 _markActive: function(){
3012 // summary:
3013 // Mark this menu's state as active.
3014 // Called when this Menu gets focus from:
3015 //
3016 // 1. clicking it (mouse or via space/arrow key)
3017 // 2. being opened by a parent menu.
3018 //
3019 // This is not called just from mouse hover.
3020 // Focusing a menu via TAB does NOT automatically set isActive
3021 // since TAB is a navigation operation and not a selection one.
3022 // For Windows apps, pressing the ALT key focuses the menubar
3023 // menus (similar to TAB navigation) but the menu is not active
3024 // (ie no dropdown) until an item is clicked.
3025 this.isActive = true;
3026 domClass.replace(this.domNode, "dijitMenuActive", "dijitMenuPassive");
3027 },
3028
3029 onOpen: function(/*Event*/ /*===== e =====*/){
3030 // summary:
3031 // Callback when this menu is opened.
3032 // This is called by the popup manager as notification that the menu
3033 // was opened.
3034 // tags:
3035 // private
3036
3037 this.isShowingNow = true;
3038 this._markActive();
3039 },
3040
3041 _markInactive: function(){
3042 // summary:
3043 // Mark this menu's state as inactive.
3044 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
3045 domClass.replace(this.domNode, "dijitMenuPassive", "dijitMenuActive");
3046 },
3047
3048 onClose: function(){
3049 // summary:
3050 // Callback when this menu is closed.
3051 // This is called by the popup manager as notification that the menu
3052 // was closed.
3053 // tags:
3054 // private
3055
3056 this._stopFocusTimer();
3057 this._markInactive();
3058 this.isShowingNow = false;
3059 this.parentMenu = null;
3060 },
3061
3062 _closeChild: function(){
3063 // summary:
3064 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
3065 // tags:
3066 // private
3067 this._stopPopupTimer();
3068
3069 if(this.currentPopup){
3070 // If focus is on a descendant MenuItem then move focus to me,
3071 // because IE doesn't like it when you display:none a node with focus,
3072 // and also so keyboard users don't lose control.
3073 // Likely, immediately after a user defined onClick handler will move focus somewhere
3074 // else, like a Dialog.
3075 if(array.indexOf(this._focusManager.activeStack, this.id) >= 0){
3076 domAttr.set(this.focusedChild.focusNode, "tabIndex", this.tabIndex);
3077 this.focusedChild.focusNode.focus();
3078 }
3079 // Close all popups that are open and descendants of this menu
3080 pm.close(this.currentPopup);
3081 this.currentPopup = null;
3082 }
3083
3084 if(this.focusedChild){ // unhighlight the focused item
3085 this.focusedChild._setSelected(false);
3086 this.onItemUnhover(this.focusedChild);
3087 this.focusedChild = null;
3088 }
3089 },
3090
3091 _onItemFocus: function(/*MenuItem*/ item){
3092 // summary:
3093 // Called when child of this Menu gets focus from:
3094 //
3095 // 1. clicking it
3096 // 2. tabbing into it
3097 // 3. being opened by a parent menu.
3098 //
3099 // This is not called just from mouse hover.
3100 if(this._hoveredChild && this._hoveredChild != item){
3101 this.onItemUnhover(this._hoveredChild); // any previous mouse movement is trumped by focus selection
3102 }
3103 },
3104
3105 _onBlur: function(){
3106 // summary:
3107 // Called when focus is moved away from this Menu and it's submenus.
3108 // tags:
3109 // protected
3110 this._cleanUp();
3111 this.inherited(arguments);
3112 },
3113
3114 _cleanUp: function(){
3115 // summary:
3116 // Called when the user is done with this menu. Closes hierarchy of menus.
3117 // tags:
3118 // private
3119
3120 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
3121 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
3122 this._markInactive();
3123 }
3124 }
3125 });
3126
3127 });
3128
3129 },
3130 'dijit/focus':function(){
3131 define("dijit/focus", [
3132 "dojo/aspect",
3133 "dojo/_base/declare", // declare
3134 "dojo/dom", // domAttr.get dom.isDescendant
3135 "dojo/dom-attr", // domAttr.get dom.isDescendant
3136 "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
3137 "dojo/Evented",
3138 "dojo/_base/lang", // lang.hitch
3139 "dojo/on",
3140 "dojo/ready",
3141 "dojo/sniff", // has("ie")
3142 "dojo/Stateful",
3143 "dojo/_base/unload", // unload.addOnWindowUnload
3144 "dojo/_base/window", // win.body
3145 "dojo/window", // winUtils.get
3146 "./a11y", // a11y.isTabNavigable
3147 "./registry", // registry.byId
3148 "./main" // to set dijit.focus
3149 ], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils,
3150 a11y, registry, dijit){
3151
3152 // module:
3153 // dijit/focus
3154
3155 var FocusManager = declare([Stateful, Evented], {
3156 // summary:
3157 // Tracks the currently focused node, and which widgets are currently "active".
3158 // Access via require(["dijit/focus"], function(focus){ ... }).
3159 //
3160 // A widget is considered active if it or a descendant widget has focus,
3161 // or if a non-focusable node of this widget or a descendant was recently clicked.
3162 //
3163 // Call focus.watch("curNode", callback) to track the current focused DOMNode,
3164 // or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
3165 //
3166 // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
3167 // when widgets become active/inactive
3168 //
3169 // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
3170
3171 // curNode: DomNode
3172 // Currently focused item on screen
3173 curNode: null,
3174
3175 // activeStack: dijit/_WidgetBase[]
3176 // List of currently active widgets (focused widget and it's ancestors)
3177 activeStack: [],
3178
3179 constructor: function(){
3180 // Don't leave curNode/prevNode pointing to bogus elements
3181 var check = lang.hitch(this, function(node){
3182 if(dom.isDescendant(this.curNode, node)){
3183 this.set("curNode", null);
3184 }
3185 if(dom.isDescendant(this.prevNode, node)){
3186 this.set("prevNode", null);
3187 }
3188 });
3189 aspect.before(domConstruct, "empty", check);
3190 aspect.before(domConstruct, "destroy", check);
3191 },
3192
3193 registerIframe: function(/*DomNode*/ iframe){
3194 // summary:
3195 // Registers listeners on the specified iframe so that any click
3196 // or focus event on that iframe (or anything in it) is reported
3197 // as a focus/click event on the `<iframe>` itself.
3198 // description:
3199 // Currently only used by editor.
3200 // returns:
3201 // Handle with remove() method to deregister.
3202 return this.registerWin(iframe.contentWindow, iframe);
3203 },
3204
3205 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
3206 // summary:
3207 // Registers listeners on the specified window (either the main
3208 // window or an iframe's window) to detect when the user has clicked somewhere
3209 // or focused somewhere.
3210 // description:
3211 // Users should call registerIframe() instead of this method.
3212 // targetWindow:
3213 // If specified this is the window associated with the iframe,
3214 // i.e. iframe.contentWindow.
3215 // effectiveNode:
3216 // If specified, report any focus events inside targetWindow as
3217 // an event on effectiveNode, rather than on evt.target.
3218 // returns:
3219 // Handle with remove() method to deregister.
3220
3221 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
3222
3223 var _this = this;
3224 var mousedownListener = function(evt){
3225 _this._justMouseDowned = true;
3226 setTimeout(function(){ _this._justMouseDowned = false; }, 0);
3227
3228 // workaround weird IE bug where the click is on an orphaned node
3229 // (first time clicking a Select/DropDownButton inside a TooltipDialog)
3230 if(has("ie") && evt && evt.srcElement && evt.srcElement.parentNode == null){
3231 return;
3232 }
3233
3234 _this._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
3235 };
3236
3237 // Listen for blur and focus events on targetWindow's document.
3238 // Using attachEvent()/addEventListener() rather than on() to try to catch mouseDown events even
3239 // if other code calls evt.stopPropagation(). But rethink for 2.0 since that doesn't work for attachEvent(),
3240 // which watches events at the bubbling phase rather than capturing phase, like addEventListener(..., false).
3241 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
3242 // (at least for FF) the focus event doesn't fire on <html> or <body>.
3243 var doc = has("ie") ? targetWindow.document.documentElement : targetWindow.document;
3244 if(doc){
3245 if(has("ie")){
3246 targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
3247 var focusinListener = function(evt){
3248 // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
3249 // ignore those events
3250 var tag = evt.srcElement.tagName.toLowerCase();
3251 if(tag == "#document" || tag == "body"){ return; }
3252
3253 // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
3254 // probably just ignore such an event as it will be handled by onmousedown handler above, but
3255 // leaving the code for now.
3256 if(a11y.isTabNavigable(evt.srcElement)){
3257 _this._onFocusNode(effectiveNode || evt.srcElement);
3258 }else{
3259 _this._onTouchNode(effectiveNode || evt.srcElement);
3260 }
3261 };
3262 doc.attachEvent('onfocusin', focusinListener);
3263 var focusoutListener = function(evt){
3264 _this._onBlurNode(effectiveNode || evt.srcElement);
3265 };
3266 doc.attachEvent('onfocusout', focusoutListener);
3267
3268 return {
3269 remove: function(){
3270 targetWindow.document.detachEvent('onmousedown', mousedownListener);
3271 doc.detachEvent('onfocusin', focusinListener);
3272 doc.detachEvent('onfocusout', focusoutListener);
3273 doc = null; // prevent memory leak (apparent circular reference via closure)
3274 }
3275 };
3276 }else{
3277 doc.body.addEventListener('mousedown', mousedownListener, true);
3278 doc.body.addEventListener('touchstart', mousedownListener, true);
3279 var focusListener = function(evt){
3280 _this._onFocusNode(effectiveNode || evt.target);
3281 };
3282 doc.addEventListener('focus', focusListener, true);
3283 var blurListener = function(evt){
3284 _this._onBlurNode(effectiveNode || evt.target);
3285 };
3286 doc.addEventListener('blur', blurListener, true);
3287
3288 return {
3289 remove: function(){
3290 doc.body.removeEventListener('mousedown', mousedownListener, true);
3291 doc.body.removeEventListener('touchstart', mousedownListener, true);
3292 doc.removeEventListener('focus', focusListener, true);
3293 doc.removeEventListener('blur', blurListener, true);
3294 doc = null; // prevent memory leak (apparent circular reference via closure)
3295 }
3296 };
3297 }
3298 }
3299 },
3300
3301 _onBlurNode: function(/*DomNode*/ node){
3302 // summary:
3303 // Called when focus leaves a node.
3304 // Usually ignored, _unless_ it *isn't* followed by touching another node,
3305 // which indicates that we tabbed off the last field on the page,
3306 // in which case every widget is marked inactive
3307
3308 // If the blur event isn't followed by a focus event, it means the user clicked on something unfocusable,
3309 // so clear focus.
3310 if(this._clearFocusTimer){
3311 clearTimeout(this._clearFocusTimer);
3312 }
3313 this._clearFocusTimer = setTimeout(lang.hitch(this, function(){
3314 this.set("prevNode", this.curNode);
3315 this.set("curNode", null);
3316 }), 0);
3317
3318 if(this._justMouseDowned){
3319 // the mouse down caused a new widget to be marked as active; this blur event
3320 // is coming late, so ignore it.
3321 return;
3322 }
3323
3324 // If the blur event isn't followed by a focus or touch event then mark all widgets as inactive.
3325 if(this._clearActiveWidgetsTimer){
3326 clearTimeout(this._clearActiveWidgetsTimer);
3327 }
3328 this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){
3329 delete this._clearActiveWidgetsTimer;
3330 this._setStack([]);
3331 }), 0);
3332 },
3333
3334 _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
3335 // summary:
3336 // Callback when node is focused or mouse-downed
3337 // node:
3338 // The node that was touched.
3339 // by:
3340 // "mouse" if the focus/touch was caused by a mouse down event
3341
3342 // ignore the recent blurNode event
3343 if(this._clearActiveWidgetsTimer){
3344 clearTimeout(this._clearActiveWidgetsTimer);
3345 delete this._clearActiveWidgetsTimer;
3346 }
3347
3348 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
3349 var newStack=[];
3350 try{
3351 while(node){
3352 var popupParent = domAttr.get(node, "dijitPopupParent");
3353 if(popupParent){
3354 node=registry.byId(popupParent).domNode;
3355 }else if(node.tagName && node.tagName.toLowerCase() == "body"){
3356 // is this the root of the document or just the root of an iframe?
3357 if(node === win.body()){
3358 // node is the root of the main document
3359 break;
3360 }
3361 // otherwise, find the iframe this node refers to (can't access it via parentNode,
3362 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
3363 node=winUtils.get(node.ownerDocument).frameElement;
3364 }else{
3365 // if this node is the root node of a widget, then add widget id to stack,
3366 // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
3367 // to support MenuItem)
3368 var id = node.getAttribute && node.getAttribute("widgetId"),
3369 widget = id && registry.byId(id);
3370 if(widget && !(by == "mouse" && widget.get("disabled"))){
3371 newStack.unshift(id);
3372 }
3373 node=node.parentNode;
3374 }
3375 }
3376 }catch(e){ /* squelch */ }
3377
3378 this._setStack(newStack, by);
3379 },
3380
3381 _onFocusNode: function(/*DomNode*/ node){
3382 // summary:
3383 // Callback when node is focused
3384
3385 if(!node){
3386 return;
3387 }
3388
3389 if(node.nodeType == 9){
3390 // Ignore focus events on the document itself. This is here so that
3391 // (for example) clicking the up/down arrows of a spinner
3392 // (which don't get focus) won't cause that widget to blur. (FF issue)
3393 return;
3394 }
3395
3396 // There was probably a blur event right before this event, but since we have a new focus, don't
3397 // do anything with the blur
3398 if(this._clearFocusTimer){
3399 clearTimeout(this._clearFocusTimer);
3400 delete this._clearFocusTimer;
3401 }
3402
3403 this._onTouchNode(node);
3404
3405 if(node == this.curNode){ return; }
3406 this.set("prevNode", this.curNode);
3407 this.set("curNode", node);
3408 },
3409
3410 _setStack: function(/*String[]*/ newStack, /*String*/ by){
3411 // summary:
3412 // The stack of active widgets has changed. Send out appropriate events and records new stack.
3413 // newStack:
3414 // array of widget id's, starting from the top (outermost) widget
3415 // by:
3416 // "mouse" if the focus/touch was caused by a mouse down event
3417
3418 var oldStack = this.activeStack;
3419 this.set("activeStack", newStack);
3420
3421 // compare old stack to new stack to see how many elements they have in common
3422 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
3423 if(oldStack[nCommon] != newStack[nCommon]){
3424 break;
3425 }
3426 }
3427
3428 var widget;
3429 // for all elements that have gone out of focus, set focused=false
3430 for(var i=oldStack.length-1; i>=nCommon; i--){
3431 widget = registry.byId(oldStack[i]);
3432 if(widget){
3433 widget._hasBeenBlurred = true; // TODO: used by form widgets, should be moved there
3434 widget.set("focused", false);
3435 if(widget._focusManager == this){
3436 widget._onBlur(by);
3437 }
3438 this.emit("widget-blur", widget, by);
3439 }
3440 }
3441
3442 // for all element that have come into focus, set focused=true
3443 for(i=nCommon; i<newStack.length; i++){
3444 widget = registry.byId(newStack[i]);
3445 if(widget){
3446 widget.set("focused", true);
3447 if(widget._focusManager == this){
3448 widget._onFocus(by);
3449 }
3450 this.emit("widget-focus", widget, by);
3451 }
3452 }
3453 },
3454
3455 focus: function(node){
3456 // summary:
3457 // Focus the specified node, suppressing errors if they occur
3458 if(node){
3459 try{ node.focus(); }catch(e){/*quiet*/}
3460 }
3461 }
3462 });
3463
3464 var singleton = new FocusManager();
3465
3466 // register top window and all the iframes it contains
3467 ready(function(){
3468 var handle = singleton.registerWin(winUtils.get(win.doc));
3469 if(has("ie")){
3470 unload.addOnWindowUnload(function(){
3471 if(handle){ // because this gets called twice when doh.robot is running
3472 handle.remove();
3473 handle = null;
3474 }
3475 });
3476 }
3477 });
3478
3479 // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
3480 // as a function to set focus. Remove for 2.0.
3481 dijit.focus = function(node){
3482 singleton.focus(node); // indirection here allows dijit/_base/focus.js to override behavior
3483 };
3484 for(var attr in singleton){
3485 if(!/^_/.test(attr)){
3486 dijit.focus[attr] = typeof singleton[attr] == "function" ? lang.hitch(singleton, attr) : singleton[attr];
3487 }
3488 }
3489 singleton.watch(function(attr, oldVal, newVal){
3490 dijit.focus[attr] = newVal;
3491 });
3492
3493 return singleton;
3494 });
3495
3496 },
3497 'dojo/i18n':function(){
3498 define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json", "module"],
3499 function(dojo, require, has, array, config, lang, xhr, json, module){
3500
3501 // module:
3502 // dojo/i18n
3503
3504 has.add("dojo-preload-i18n-Api",
3505 // if true, define the preload localizations machinery
3506 1
3507 );
3508
3509 1 || has.add("dojo-v1x-i18n-Api",
3510 // if true, define the v1.x i18n functions
3511 1
3512 );
3513
3514 var
3515 thisModule = dojo.i18n =
3516 {
3517 // summary:
3518 // This module implements the dojo/i18n! plugin and the v1.6- i18n API
3519 // description:
3520 // We choose to include our own plugin to leverage functionality already contained in dojo
3521 // and thereby reduce the size of the plugin compared to various loader implementations. Also, this
3522 // allows foreign AMD loaders to be used without their plugins.
3523 },
3524
3525 nlsRe =
3526 // regexp for reconstructing the master bundle name from parts of the regexp match
3527 // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
3528 // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
3529 // nlsRe.exec("foo/bar/baz/nls/foo") gives:
3530 // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
3531 // so, if match[5] is blank, it means this is the top bundle definition.
3532 // courtesy of http://requirejs.org
3533 /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
3534
3535 getAvailableLocales = function(
3536 root,
3537 locale,
3538 bundlePath,
3539 bundleName
3540 ){
3541 // summary:
3542 // return a vector of module ids containing all available locales with respect to the target locale
3543 // For example, assuming:
3544 //
3545 // - the root bundle indicates specific bundles for "fr" and "fr-ca",
3546 // - bundlePath is "myPackage/nls"
3547 // - bundleName is "myBundle"
3548 //
3549 // Then a locale argument of "fr-ca" would return
3550 //
3551 // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
3552 //
3553 // Notice that bundles are returned least-specific to most-specific, starting with the root.
3554 //
3555 // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
3556 // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
3557
3558 for(var result = [bundlePath + bundleName], localeParts = locale.split("-"), current = "", i = 0; i<localeParts.length; i++){
3559 current += (current ? "-" : "") + localeParts[i];
3560 if(!root || root[current]){
3561 result.push(bundlePath + current + "/" + bundleName);
3562 }
3563 }
3564 return result;
3565 },
3566
3567 cache = {},
3568
3569 getBundleName = function(moduleName, bundleName, locale){
3570 locale = locale ? locale.toLowerCase() : dojo.locale;
3571 moduleName = moduleName.replace(/\./g, "/");
3572 bundleName = bundleName.replace(/\./g, "/");
3573 return (/root/i.test(locale)) ?
3574 (moduleName + "/nls/" + bundleName) :
3575 (moduleName + "/nls/" + locale + "/" + bundleName);
3576 },
3577
3578 getL10nName = dojo.getL10nName = function(moduleName, bundleName, locale){
3579 return moduleName = module.id + "!" + getBundleName(moduleName, bundleName, locale);
3580 },
3581
3582 doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){
3583 // summary:
3584 // get the root bundle which instructs which other bundles are required to construct the localized bundle
3585 require([bundlePathAndName], function(root){
3586 var current = lang.clone(root.root),
3587 availableLocales = getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName);
3588 require(availableLocales, function(){
3589 for (var i = 1; i<availableLocales.length; i++){
3590 current = lang.mixin(lang.clone(current), arguments[i]);
3591 }
3592 // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
3593 var target = bundlePathAndName + "/" + locale;
3594 cache[target] = current;
3595 load();
3596 });
3597 });
3598 },
3599
3600 normalize = function(id, toAbsMid){
3601 // summary:
3602 // id may be relative.
3603 // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and
3604 // therefore never looks like a relative
3605 return /^\./.test(id) ? toAbsMid(id) : id;
3606 },
3607
3608 getLocalesToLoad = function(targetLocale){
3609 var list = config.extraLocale || [];
3610 list = lang.isArray(list) ? list : [list];
3611 list.push(targetLocale);
3612 return list;
3613 },
3614
3615 load = function(id, require, load){
3616 // summary:
3617 // id is in one of the following formats
3618 //
3619 // 1. <path>/nls/<bundle>
3620 // => load the bundle, localized to config.locale; load all bundles localized to
3621 // config.extraLocale (if any); return the loaded bundle localized to config.locale.
3622 //
3623 // 2. <path>/nls/<locale>/<bundle>
3624 // => load then return the bundle localized to <locale>
3625 //
3626 // 3. *preload*<path>/nls/<module>*<JSON array of available locales>
3627 // => for config.locale and all config.extraLocale, load all bundles found
3628 // in the best-matching bundle rollup. A value of 1 is returned, which
3629 // is meaningless other than to say the plugin is executing the requested
3630 // preloads
3631 //
3632 // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
3633 // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder.
3634 //
3635 // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
3636 // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
3637 //
3638 // <path>/nls/<bundle>/<locale>
3639 //
3640 // will hold the value. Similarly, then plugin will publish this value to the loader by
3641 //
3642 // define("<path>/nls/<bundle>/<locale>", <bundle-value>);
3643 //
3644 // Given this algorithm, other machinery can provide fast load paths be preplacing
3645 // values in the plugin's cache, which is public. When a load is demanded the
3646 // cache is inspected before starting any loading. Explicitly placing values in the plugin
3647 // cache is an advanced/experimental feature that should not be needed; use at your own risk.
3648 //
3649 // For the normal AMD algorithm, the root bundle is loaded first, which instructs the
3650 // plugin what additional localized bundles are required for a particular locale. These
3651 // additional locales are loaded and a mix of the root and each progressively-specific
3652 // locale is returned. For example:
3653 //
3654 // 1. The client demands "dojo/i18n!some/path/nls/someBundle
3655 //
3656 // 2. The loader demands load(some/path/nls/someBundle)
3657 //
3658 // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
3659 //
3660 // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
3661 // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
3662 // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
3663 //
3664 // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
3665 // ab-cd-ef as...
3666 //
3667 // mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
3668 // require("some/path/nls/ab/someBundle")),
3669 // require("some/path/nls/ab-cd-ef/someBundle"));
3670 //
3671 // This value is inserted into the cache and published to the loader at the
3672 // key/module-id some/path/nls/someBundle/ab-cd-ef.
3673 //
3674 // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
3675 // (further preload requests will be serviced) until all ongoing preloading has completed.
3676 //
3677 // The preload signature instructs the plugin that a special rollup module is available that contains
3678 // one or more flattened, localized bundles. The JSON array of available locales indicates which locales
3679 // are available. Here is an example:
3680 //
3681 // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
3682 //
3683 // This indicates the following rollup modules are available:
3684 //
3685 // some/path/nls/someModule_ROOT
3686 // some/path/nls/someModule_ab
3687 // some/path/nls/someModule_ab-cd-ef
3688 //
3689 // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
3690 // For example, assume someModule contained the bundles some/bundle/path/someBundle and
3691 // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows:
3692 //
3693 // define({
3694 // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
3695 // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
3696 // });
3697 //
3698 // E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
3699 //
3700 // require(["some/path/nls/someModule_ab"], function(rollup){
3701 // for(var p in rollup){
3702 // var id = p + "/ab",
3703 // cache[id] = rollup[p];
3704 // define(id, rollup[p]);
3705 // }
3706 // });
3707 //
3708 // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
3709 // load accordingly.
3710 //
3711 // The builder will write such rollups for every layer if a non-empty localeList profile property is
3712 // provided. Further, the builder will include the following cache entry in the cache associated with
3713 // any layer.
3714 //
3715 // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
3716 //
3717 // The *now special cache module instructs the loader to apply the provided function to context-require
3718 // with respect to the particular layer being defined. This causes the plugin to hold all normal service
3719 // requests until all preloading is complete.
3720 //
3721 // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
3722 // where the target locale has a single segment and a layer depends on a single bundle:
3723 //
3724 // Without Preloads:
3725 //
3726 // 1. Layer loads root bundle.
3727 // 2. bundle is demanded; plugin loads single localized bundle.
3728 //
3729 // With Preloads:
3730 //
3731 // 1. Layer causes preloading of target bundle.
3732 // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
3733 //
3734 // In each case a single transaction is required to load the target bundle. In cases where multiple bundles
3735 // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
3736 // the normal path requires an additional transaction for each additional bundle/locale-segment. However all
3737 // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
3738 // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
3739
3740 if(has("dojo-preload-i18n-Api")){
3741 var split = id.split("*"),
3742 preloadDemand = split[1] == "preload";
3743 if(preloadDemand){
3744 if(!cache[id]){
3745 // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
3746 // who knows what over-aggressive human optimizers may attempt
3747 cache[id] = 1;
3748 preloadL10n(split[2], json.parse(split[3]), 1, require);
3749 }
3750 // don't stall the loader!
3751 load(1);
3752 }
3753 if(preloadDemand || waitForPreloads(id, require, load)){
3754 return;
3755 }
3756 }
3757
3758 var match = nlsRe.exec(id),
3759 bundlePath = match[1] + "/",
3760 bundleName = match[5] || match[4],
3761 bundlePathAndName = bundlePath + bundleName,
3762 localeSpecified = (match[5] && match[4]),
3763 targetLocale = localeSpecified || dojo.locale,
3764 loadTarget = bundlePathAndName + "/" + targetLocale,
3765 loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale),
3766 remaining = loadList.length,
3767 finish = function(){
3768 if(!--remaining){
3769 load(lang.delegate(cache[loadTarget]));
3770 }
3771 };
3772 array.forEach(loadList, function(locale){
3773 var target = bundlePathAndName + "/" + locale;
3774 if(has("dojo-preload-i18n-Api")){
3775 checkForLegacyModules(target);
3776 }
3777 if(!cache[target]){
3778 doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish);
3779 }else{
3780 finish();
3781 }
3782 });
3783 };
3784
3785 if(has("dojo-unit-tests")){
3786 var unitTests = thisModule.unitTests = [];
3787 }
3788
3789 if(has("dojo-preload-i18n-Api") || 1 ){
3790 var normalizeLocale = thisModule.normalizeLocale = function(locale){
3791 var result = locale ? locale.toLowerCase() : dojo.locale;
3792 return result == "root" ? "ROOT" : result;
3793 },
3794
3795 isXd = function(mid, contextRequire){
3796 return ( 1 && 1 ) ?
3797 contextRequire.isXdUrl(require.toUrl(mid + ".js")) :
3798 true;
3799 },
3800
3801 preloading = 0,
3802
3803 preloadWaitQueue = [],
3804
3805 preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean?*/ guaranteedAmdFormat, /*function?*/ contextRequire){
3806 // summary:
3807 // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any)
3808 // description:
3809 // Only called by built layer files. The entire locale hierarchy is loaded. For example,
3810 // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
3811 // in that the v1.6- would only load ab-cd...which was *always* flattened.
3812 //
3813 // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
3814 // and the extra possible extra transaction.
3815
3816 // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function
3817 // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which
3818 // itself may have been mapped.
3819 contextRequire = contextRequire || require;
3820
3821 function doRequire(mid, callback){
3822 if(isXd(mid, contextRequire) || guaranteedAmdFormat){
3823 contextRequire([mid], callback);
3824 }else{
3825 syncRequire([mid], callback, contextRequire);
3826 }
3827 }
3828
3829 function forEachLocale(locale, func){
3830 // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
3831 var parts = locale.split("-");
3832 while(parts.length){
3833 if(func(parts.join("-"))){
3834 return;
3835 }
3836 parts.pop();
3837 }
3838 func("ROOT");
3839 }
3840
3841 function preload(locale){
3842 locale = normalizeLocale(locale);
3843 forEachLocale(locale, function(loc){
3844 if(array.indexOf(localesGenerated, loc)>=0){
3845 var mid = bundlePrefix.replace(/\./g, "/")+"_"+loc;
3846 preloading++;
3847 doRequire(mid, function(rollup){
3848 for(var p in rollup){
3849 cache[require.toAbsMid(p) + "/" + loc] = rollup[p];
3850 }
3851 --preloading;
3852 while(!preloading && preloadWaitQueue.length){
3853 load.apply(null, preloadWaitQueue.shift());
3854 }
3855 });
3856 return true;
3857 }
3858 return false;
3859 });
3860 }
3861
3862 preload();
3863 array.forEach(dojo.config.extraLocale, preload);
3864 },
3865
3866 waitForPreloads = function(id, require, load){
3867 if(preloading){
3868 preloadWaitQueue.push([id, require, load]);
3869 }
3870 return preloading;
3871 },
3872
3873 checkForLegacyModules = function()
3874 {};
3875 }
3876
3877 if( 1 ){
3878 // this code path assumes the dojo loader and won't work with a standard AMD loader
3879 var amdValue = {},
3880 evalBundle =
3881 // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
3882 new Function(
3883 "__bundle", // the bundle to evalutate
3884 "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
3885 "__mid", // the mid that __bundle is intended to define
3886 "__amdValue",
3887
3888 // returns one of:
3889 // 1 => the bundle was an AMD bundle
3890 // a legacy bundle object that is the value of __mid
3891 // instance of Error => could not figure out how to evaluate bundle
3892
3893 // used to detect when __bundle calls define
3894 "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;},"
3895 + " require = function(){define.called = 1;};"
3896
3897 + "try{"
3898 + "define.called = 0;"
3899 + "eval(__bundle);"
3900 + "if(define.called==1)"
3901 // bundle called define; therefore signal it's an AMD bundle
3902 + "return __amdValue;"
3903
3904 + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
3905 // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
3906 + "return __checkForLegacyModules;"
3907
3908 + "}catch(e){}"
3909 // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
3910 // either way, re-eval *after* surrounding with parentheses
3911
3912 + "try{"
3913 + "return eval('('+__bundle+')');"
3914 + "}catch(e){"
3915 + "return e;"
3916 + "}"
3917 ),
3918
3919 syncRequire = function(deps, callback, require){
3920 var results = [];
3921 array.forEach(deps, function(mid){
3922 var url = require.toUrl(mid + ".js");
3923
3924 function load(text){
3925 var result = evalBundle(text, checkForLegacyModules, mid, amdValue);
3926 if(result===amdValue){
3927 // the bundle was an AMD module; re-inject it through the normal AMD path
3928 // we gotta do this since it could be an anonymous module and simply evaluating
3929 // the text here won't provide the loader with the context to know what
3930 // module is being defined()'d. With browser caching, this should be free; further
3931 // this entire code path can be circumvented by using the AMD format to begin with
3932 results.push(cache[url] = amdValue.result);
3933 }else{
3934 if(result instanceof Error){
3935 console.error("failed to evaluate i18n bundle; url=" + url, result);
3936 result = {};
3937 }
3938 // nls/<locale>/<bundle-name> indicates not the root.
3939 results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1}));
3940 }
3941 }
3942
3943 if(cache[url]){
3944 results.push(cache[url]);
3945 }else{
3946 var bundle = require.syncLoadNls(mid);
3947 // don't need to check for legacy since syncLoadNls returns a module if the module
3948 // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
3949 // from getLocalization --> load, then load will have called checkForLegacyModules() before
3950 // calling syncRequire; if syncRequire is called from preloadLocalizations, then we
3951 // don't care about checkForLegacyModules() because that will be done when a particular
3952 // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
3953 // because cached modules are always v1.7+ built modules.
3954 if(bundle){
3955 results.push(bundle);
3956 }else{
3957 if(!xhr){
3958 try{
3959 require.getText(url, true, load);
3960 }catch(e){
3961 results.push(cache[url] = {});
3962 }
3963 }else{
3964 xhr.get({
3965 url:url,
3966 sync:true,
3967 load:load,
3968 error:function(){
3969 results.push(cache[url] = {});
3970 }
3971 });
3972 }
3973 }
3974 }
3975 });
3976 callback && callback.apply(null, results);
3977 };
3978
3979 checkForLegacyModules = function(target){
3980 // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
3981 for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){}
3982 if(object){
3983 result = object[names[i]];
3984 if(!result){
3985 // fallback for incorrect bundle build of 1.6
3986 result = object[names[i].replace(/-/g,"_")];
3987 }
3988 if(result){
3989 cache[target] = result;
3990 }
3991 }
3992 return result;
3993 };
3994
3995 thisModule.getLocalization = function(moduleName, bundleName, locale){
3996 var result,
3997 l10nName = getBundleName(moduleName, bundleName, locale);
3998 load(
3999 l10nName,
4000
4001 // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module.
4002 // Since this legacy function does not have the concept of a reference module, resolve with respect to this
4003 // dojo/i18n module, which, itself may have been mapped.
4004 (!isXd(l10nName, require) ? function(deps, callback){ syncRequire(deps, callback, require); } : require),
4005
4006 function(result_){ result = result_; }
4007 );
4008 return result;
4009 };
4010
4011 if(has("dojo-unit-tests")){
4012 unitTests.push(function(doh){
4013 doh.register("tests.i18n.unit", function(t){
4014 var check;
4015
4016 check = evalBundle("{prop:1}", checkForLegacyModules, "nonsense", amdValue);
4017 t.is({prop:1}, check); t.is(undefined, check[1]);
4018
4019 check = evalBundle("({prop:1})", checkForLegacyModules, "nonsense", amdValue);
4020 t.is({prop:1}, check); t.is(undefined, check[1]);
4021
4022 check = evalBundle("{'prop-x':1}", checkForLegacyModules, "nonsense", amdValue);
4023 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
4024
4025 check = evalBundle("({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4026 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
4027
4028 check = evalBundle("define({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4029 t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
4030
4031 check = evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
4032 t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
4033
4034 check = evalBundle("this is total nonsense and should throw an error", checkForLegacyModules, "nonsense", amdValue);
4035 t.is(check instanceof Error, true);
4036 });
4037 });
4038 }
4039 }
4040
4041 return lang.mixin(thisModule, {
4042 dynamic:true,
4043 normalize:normalize,
4044 load:load,
4045 cache:cache
4046 });
4047 });
4048
4049 },
4050 'dijit/hccss':function(){
4051 define("dijit/hccss", ["dojo/dom-class", "dojo/hccss", "dojo/ready", "dojo/_base/window"], function(domClass, has, ready, win){
4052
4053 // module:
4054 // dijit/hccss
4055
4056 /*=====
4057 return function(){
4058 // summary:
4059 // Test if computer is in high contrast mode, and sets `dijit_a11y` flag on `<body>` if it is.
4060 // Deprecated, use ``dojo/hccss`` instead.
4061 };
4062 =====*/
4063
4064 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
4065 // change this module to depend on dojo/domReady!
4066 ready(90, function(){
4067 if(has("highcontrast")){
4068 domClass.add(win.body(), "dijit_a11y");
4069 }
4070 });
4071
4072 return has;
4073 });
4074
4075 },
4076 'dijit/tree/ForestStoreModel':function(){
4077 define("dijit/tree/ForestStoreModel", [
4078 "dojo/_base/array", // array.indexOf array.some
4079 "dojo/_base/declare", // declare
4080 "dojo/_base/kernel", // global
4081 "dojo/_base/lang", // lang.hitch
4082 "./TreeStoreModel"
4083 ], function(array, declare, kernel, lang, TreeStoreModel){
4084
4085 // module:
4086 // dijit/tree/ForestStoreModel
4087
4088 return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
4089 // summary:
4090 // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4091 // a.k.a. a store that has multiple "top level" items.
4092 //
4093 // description:
4094 // Use this class to wrap a dojo.data store, making all the items matching the specified query
4095 // appear as children of a fabricated "root item". If no query is specified then all the
4096 // items returned by fetch() on the underlying store become children of the root item.
4097 // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
4098 //
4099 // When using this class the developer must override a number of methods according to their app and
4100 // data, including:
4101 //
4102 // - onNewRootItem
4103 // - onAddToRoot
4104 // - onLeaveRoot
4105 // - onNewItem
4106 // - onSetItem
4107
4108 // Parameters to constructor
4109
4110 // rootId: String
4111 // ID of fabricated root item
4112 rootId: "$root$",
4113
4114 // rootLabel: String
4115 // Label of fabricated root item
4116 rootLabel: "ROOT",
4117
4118 // query: String
4119 // Specifies the set of children of the root item.
4120 // example:
4121 // | {type:'continent'}
4122 query: null,
4123
4124 // End of parameters to constructor
4125
4126 constructor: function(params){
4127 // summary:
4128 // Sets up variables, etc.
4129 // tags:
4130 // private
4131
4132 // Make dummy root item
4133 this.root = {
4134 store: this,
4135 root: true,
4136 id: params.rootId,
4137 label: params.rootLabel,
4138 children: params.rootChildren // optional param
4139 };
4140 },
4141
4142 // =======================================================================
4143 // Methods for traversing hierarchy
4144
4145 mayHaveChildren: function(/*dojo/data/Item*/ item){
4146 // summary:
4147 // Tells if an item has or may have children. Implementing logic here
4148 // avoids showing +/- expando icon for nodes that we know don't have children.
4149 // (For efficiency reasons we may not want to check if an element actually
4150 // has children until user clicks the expando node)
4151 // tags:
4152 // extension
4153 return item === this.root || this.inherited(arguments);
4154 },
4155
4156 getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
4157 // summary:
4158 // Calls onComplete() with array of child items of given parent item, all loaded.
4159 if(parentItem === this.root){
4160 if(this.root.children){
4161 // already loaded, just return
4162 callback(this.root.children);
4163 }else{
4164 this.store.fetch({
4165 query: this.query,
4166 onComplete: lang.hitch(this, function(items){
4167 this.root.children = items;
4168 callback(items);
4169 }),
4170 onError: onError
4171 });
4172 }
4173 }else{
4174 this.inherited(arguments);
4175 }
4176 },
4177
4178 // =======================================================================
4179 // Inspecting items
4180
4181 isItem: function(/* anything */ something){
4182 return (something === this.root) ? true : this.inherited(arguments);
4183 },
4184
4185 fetchItemByIdentity: function(/* object */ keywordArgs){
4186 if(keywordArgs.identity == this.root.id){
4187 var scope = keywordArgs.scope || kernel.global;
4188 if(keywordArgs.onItem){
4189 keywordArgs.onItem.call(scope, this.root);
4190 }
4191 }else{
4192 this.inherited(arguments);
4193 }
4194 },
4195
4196 getIdentity: function(/* item */ item){
4197 return (item === this.root) ? this.root.id : this.inherited(arguments);
4198 },
4199
4200 getLabel: function(/* item */ item){
4201 return (item === this.root) ? this.root.label : this.inherited(arguments);
4202 },
4203
4204 // =======================================================================
4205 // Write interface
4206
4207 newItem: function(/* dijit/tree/dndSource.__Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
4208 // summary:
4209 // Creates a new item. See dojo/data/api/Write for details on args.
4210 // Used in drag & drop when item from external source dropped onto tree.
4211 if(parent === this.root){
4212 this.onNewRootItem(args);
4213 return this.store.newItem(args);
4214 }else{
4215 return this.inherited(arguments);
4216 }
4217 },
4218
4219 onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
4220 // summary:
4221 // User can override this method to modify a new element that's being
4222 // added to the root of the tree, for example to add a flag like root=true
4223 },
4224
4225 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
4226 // summary:
4227 // Move or copy an item from one parent item to another.
4228 // Used in drag & drop
4229 if(oldParentItem === this.root){
4230 if(!bCopy){
4231 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
4232 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4233 // that this element is no longer a child of the root node
4234 this.onLeaveRoot(childItem);
4235 }
4236 }
4237 this.inherited(arguments, [childItem,
4238 oldParentItem === this.root ? null : oldParentItem,
4239 newParentItem === this.root ? null : newParentItem,
4240 bCopy,
4241 insertIndex
4242 ]);
4243 if(newParentItem === this.root){
4244 // It's onAddToRoot()'s responsibility to modify the item so it matches
4245 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4246 // that this element is now a child of the root node
4247 this.onAddToRoot(childItem);
4248 }
4249 },
4250
4251 // =======================================================================
4252 // Handling for top level children
4253
4254 onAddToRoot: function(/* item */ item){
4255 // summary:
4256 // Called when item added to root of tree; user must override this method
4257 // to modify the item so that it matches the query for top level items
4258 // example:
4259 // | store.setValue(item, "root", true);
4260 // tags:
4261 // extension
4262 console.log(this, ": item ", item, " added to root");
4263 },
4264
4265 onLeaveRoot: function(/* item */ item){
4266 // summary:
4267 // Called when item removed from root of tree; user must override this method
4268 // to modify the item so it doesn't match the query for top level items
4269 // example:
4270 // | store.unsetAttribute(item, "root");
4271 // tags:
4272 // extension
4273 console.log(this, ": item ", item, " removed from root");
4274 },
4275
4276 // =======================================================================
4277 // Events from data store
4278
4279 _requeryTop: function(){
4280 // reruns the query for the children of the root node,
4281 // sending out an onSet notification if those children have changed
4282 var oldChildren = this.root.children || [];
4283 this.store.fetch({
4284 query: this.query,
4285 onComplete: lang.hitch(this, function(newChildren){
4286 this.root.children = newChildren;
4287
4288 // If the list of children or the order of children has changed...
4289 if(oldChildren.length != newChildren.length ||
4290 array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
4291 this.onChildrenChange(this.root, newChildren);
4292 }
4293 })
4294 });
4295 },
4296
4297 onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo){
4298 // summary:
4299 // Handler for when new items appear in the store. Developers should override this
4300 // method to be more efficient based on their app/data.
4301 // description:
4302 // Note that the default implementation requeries the top level items every time
4303 // a new item is created, since any new item could be a top level item (even in
4304 // addition to being a child of another item, since items can have multiple parents).
4305 //
4306 // If developers can detect which items are possible top level items (based on the item and the
4307 // parentInfo parameters), they should override this method to only call _requeryTop() for top
4308 // level items. Often all top level items have parentInfo==null, but
4309 // that will depend on which store you use and what your data is like.
4310 // tags:
4311 // extension
4312 this._requeryTop();
4313
4314 this.inherited(arguments);
4315 },
4316
4317 onDeleteItem: function(/*Object*/ item){
4318 // summary:
4319 // Handler for delete notifications from underlying store
4320
4321 // check if this was a child of root, and if so send notification that root's children
4322 // have changed
4323 if(array.indexOf(this.root.children, item) != -1){
4324 this._requeryTop();
4325 }
4326
4327 this.inherited(arguments);
4328 },
4329
4330 onSetItem: function(/* item */ item,
4331 /* attribute-name-string */ attribute,
4332 /* Object|Array */ oldValue,
4333 /* Object|Array */ newValue){
4334 // summary:
4335 // Updates the tree view according to changes to an item in the data store.
4336 // Developers should override this method to be more efficient based on their app/data.
4337 // description:
4338 // Handles updates to an item's children by calling onChildrenChange(), and
4339 // other updates to an item by calling onChange().
4340 //
4341 // Also, any change to any item re-executes the query for the tree's top-level items,
4342 // since this modified item may have started/stopped matching the query for top level items.
4343 //
4344 // If possible, developers should override this function to only call _requeryTop() when
4345 // the change to the item has caused it to stop/start being a top level item in the tree.
4346 // tags:
4347 // extension
4348
4349 this._requeryTop();
4350 this.inherited(arguments);
4351 }
4352
4353 });
4354
4355 });
4356
4357 },
4358 '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",
4359 'dijit/form/_ComboBoxMenuMixin':function(){
4360 define("dijit/form/_ComboBoxMenuMixin", [
4361 "dojo/_base/array", // array.forEach
4362 "dojo/_base/declare", // declare
4363 "dojo/dom-attr", // domAttr.set
4364 "dojo/i18n", // i18n.getLocalization
4365 "dojo/i18n!./nls/ComboBox"
4366 ], function(array, declare, domAttr, i18n){
4367
4368 // module:
4369 // dijit/form/_ComboBoxMenuMixin
4370
4371 return declare( "dijit.form._ComboBoxMenuMixin", null, {
4372 // summary:
4373 // Focus-less menu for internal use in `dijit/form/ComboBox`
4374 // tags:
4375 // private
4376
4377 // _messages: Object
4378 // Holds "next" and "previous" text for paging buttons on drop down
4379 _messages: null,
4380
4381 postMixInProperties: function(){
4382 this.inherited(arguments);
4383 this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
4384 },
4385
4386 buildRendering: function(){
4387 this.inherited(arguments);
4388
4389 // fill in template with i18n messages
4390 this.previousButton.innerHTML = this._messages["previousMessage"];
4391 this.nextButton.innerHTML = this._messages["nextMessage"];
4392 },
4393
4394 _setValueAttr: function(/*Object*/ value){
4395 this.value = value;
4396 this.onChange(value);
4397 },
4398
4399 onClick: function(/*DomNode*/ node){
4400 if(node == this.previousButton){
4401 this._setSelectedAttr(null);
4402 this.onPage(-1);
4403 }else if(node == this.nextButton){
4404 this._setSelectedAttr(null);
4405 this.onPage(1);
4406 }else{
4407 this.onChange(node);
4408 }
4409 },
4410
4411 // stubs
4412 onChange: function(/*Number*/ /*===== direction =====*/){
4413 // summary:
4414 // Notifies ComboBox/FilteringSelect that user selected an option.
4415 // tags:
4416 // callback
4417 },
4418
4419 onPage: function(/*Number*/ /*===== direction =====*/){
4420 // summary:
4421 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
4422 // tags:
4423 // callback
4424 },
4425
4426 onClose: function(){
4427 // summary:
4428 // Callback from dijit.popup code to this widget, notifying it that it closed
4429 // tags:
4430 // private
4431 this._setSelectedAttr(null);
4432 },
4433
4434 _createOption: function(/*Object*/ item, labelFunc){
4435 // summary:
4436 // Creates an option to appear on the popup menu subclassed by
4437 // `dijit/form/FilteringSelect`.
4438
4439 var menuitem = this._createMenuItem();
4440 var labelObject = labelFunc(item);
4441 if(labelObject.html){
4442 menuitem.innerHTML = labelObject.label;
4443 }else{
4444 menuitem.appendChild(
4445 menuitem.ownerDocument.createTextNode(labelObject.label)
4446 );
4447 }
4448 // #3250: in blank options, assign a normal height
4449 if(menuitem.innerHTML == ""){
4450 menuitem.innerHTML = "&#160;"; // &nbsp;
4451 }
4452
4453 // update menuitem.dir if BidiSupport was required
4454 this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
4455
4456 return menuitem;
4457 },
4458
4459 createOptions: function(results, options, labelFunc){
4460 // summary:
4461 // Fills in the items in the drop down list
4462 // results:
4463 // Array of items
4464 // options:
4465 // The options to the query function of the store
4466 //
4467 // labelFunc:
4468 // Function to produce a label in the drop down list from a dojo.data item
4469
4470 this.items = results;
4471
4472 // display "Previous . . ." button
4473 this.previousButton.style.display = (options.start == 0) ? "none" : "";
4474 domAttr.set(this.previousButton, "id", this.id + "_prev");
4475 // create options using _createOption function defined by parent
4476 // ComboBox (or FilteringSelect) class
4477 // #2309:
4478 // iterate over cache nondestructively
4479 array.forEach(results, function(item, i){
4480 var menuitem = this._createOption(item, labelFunc);
4481 menuitem.setAttribute("item", i); // index to this.items; use indirection to avoid mem leak
4482 domAttr.set(menuitem, "id", this.id + i);
4483 this.nextButton.parentNode.insertBefore(menuitem, this.nextButton);
4484 }, this);
4485 // display "Next . . ." button
4486 var displayMore = false;
4487 // Try to determine if we should show 'more'...
4488 if(results.total && !results.total.then && results.total != -1){
4489 if((options.start + options.count) < results.total){
4490 displayMore = true;
4491 }else if((options.start + options.count) > results.total && options.count == results.length){
4492 // Weird return from a data store, where a start + count > maxOptions
4493 // implies maxOptions isn't really valid and we have to go into faking it.
4494 // And more or less assume more if count == results.length
4495 displayMore = true;
4496 }
4497 }else if(options.count == results.length){
4498 //Don't know the size, so we do the best we can based off count alone.
4499 //So, if we have an exact match to count, assume more.
4500 displayMore = true;
4501 }
4502
4503 this.nextButton.style.display = displayMore ? "" : "none";
4504 domAttr.set(this.nextButton,"id", this.id + "_next");
4505 },
4506
4507 clearResultList: function(){
4508 // summary:
4509 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
4510 var container = this.containerNode;
4511 while(container.childNodes.length > 2){
4512 container.removeChild(container.childNodes[container.childNodes.length-2]);
4513 }
4514 this._setSelectedAttr(null);
4515 },
4516
4517 highlightFirstOption: function(){
4518 // summary:
4519 // Highlight the first real item in the list (not Previous Choices).
4520 this.selectFirstNode();
4521 },
4522
4523 highlightLastOption: function(){
4524 // summary:
4525 // Highlight the last real item in the list (not More Choices).
4526 this.selectLastNode();
4527 },
4528
4529 selectFirstNode: function(){
4530 this.inherited(arguments);
4531 if(this.getHighlightedOption() == this.previousButton){
4532 this.selectNextNode();
4533 }
4534 },
4535
4536 selectLastNode: function(){
4537 this.inherited(arguments);
4538 if(this.getHighlightedOption() == this.nextButton){
4539 this.selectPreviousNode();
4540 }
4541 },
4542
4543 getHighlightedOption: function(){
4544 return this.selected;
4545 }
4546 });
4547
4548 });
4549
4550 },
4551 'dijit/form/_SearchMixin':function(){
4552 define("dijit/form/_SearchMixin", [
4553 "dojo/data/util/filter", // patternToRegExp
4554 "dojo/_base/declare", // declare
4555 "dojo/_base/event", // event.stop
4556 "dojo/keys", // keys
4557 "dojo/_base/lang", // lang.clone lang.hitch
4558 "dojo/query", // query
4559 "dojo/sniff", // has("ie")
4560 "dojo/string", // string.substitute
4561 "dojo/when",
4562 "../registry" // registry.byId
4563 ], function(filter, declare, event, keys, lang, query, has, string, when, registry){
4564
4565 // module:
4566 // dijit/form/_SearchMixin
4567
4568
4569 return declare("dijit.form._SearchMixin", null, {
4570 // summary:
4571 // A mixin that implements the base functionality to search a store based upon user-entered text such as
4572 // with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
4573 // tags:
4574 // protected
4575
4576 // pageSize: Integer
4577 // Argument to data provider.
4578 // Specifies maximum number of search results to return per query
4579 pageSize: Infinity,
4580
4581 // store: [const] dojo/store/api/Store
4582 // Reference to data provider object used by this ComboBox.
4583 // The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
4584 store: null,
4585
4586 // fetchProperties: Object
4587 // Mixin to the store's fetch.
4588 // For example, to set the sort order of the ComboBox menu, pass:
4589 // | { sort: [{attribute:"name",descending: true}] }
4590 // To override the default queryOptions so that deep=false, do:
4591 // | { queryOptions: {ignoreCase: true, deep: false} }
4592 fetchProperties:{},
4593
4594 // query: Object
4595 // A query that can be passed to `store` to initially filter the items.
4596 // ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
4597 query: {},
4598
4599 // searchDelay: Integer
4600 // Delay in milliseconds between when user types something and we start
4601 // searching based on that value
4602 searchDelay: 200,
4603
4604 // searchAttr: String
4605 // Search for items in the data store where this attribute (in the item)
4606 // matches what the user typed
4607 searchAttr: "name",
4608
4609 // queryExpr: String
4610 // This specifies what query is sent to the data store,
4611 // based on what the user has typed. Changing this expression will modify
4612 // whether the results are only exact matches, a "starting with" match,
4613 // etc.
4614 // dojo.data query expression pattern.
4615 // `${0}` will be substituted for the user text.
4616 // `*` is used for wildcards.
4617 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
4618 queryExpr: "${0}*",
4619
4620 // ignoreCase: Boolean
4621 // Set true if the query should ignore case when matching possible items
4622 ignoreCase: true,
4623
4624 _abortQuery: function(){
4625 // stop in-progress query
4626 if(this.searchTimer){
4627 this.searchTimer = this.searchTimer.remove();
4628 }
4629 if(this._queryDeferHandle){
4630 this._queryDeferHandle = this._queryDeferHandle.remove();
4631 }
4632 if(this._fetchHandle){
4633 if(this._fetchHandle.abort){
4634 this._cancelingQuery = true;
4635 this._fetchHandle.abort();
4636 this._cancelingQuery = false;
4637 }
4638 if(this._fetchHandle.cancel){
4639 this._cancelingQuery = true;
4640 this._fetchHandle.cancel();
4641 this._cancelingQuery = false;
4642 }
4643 this._fetchHandle = null;
4644 }
4645 },
4646
4647 _processInput: function(/*Event*/ evt){
4648 // summary:
4649 // Handles input (keyboard/paste) events
4650 if(this.disabled || this.readOnly){ return; }
4651 var key = evt.charOrCode;
4652
4653 // except for cutting/pasting case - ctrl + x/v
4654 if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
4655 return; // throw out weird key combinations and spurious events
4656 }
4657
4658 var doSearch = false;
4659 this._prev_key_backspace = false;
4660
4661 switch(key){
4662 case keys.DELETE:
4663 case keys.BACKSPACE:
4664 this._prev_key_backspace = true;
4665 this._maskValidSubsetError = true;
4666 doSearch = true;
4667 break;
4668
4669 default:
4670 // Non char keys (F1-F12 etc..) shouldn't start a search..
4671 // Ascii characters and IME input (Chinese, Japanese etc.) should.
4672 //IME input produces keycode == 229.
4673 doSearch = typeof key == 'string' || key == 229;
4674 }
4675 if(doSearch){
4676 // need to wait a tad before start search so that the event
4677 // bubbles through DOM and we have value visible
4678 if(!this.store){
4679 this.onSearch();
4680 }else{
4681 this.searchTimer = this.defer("_startSearchFromInput", 1);
4682 }
4683 }
4684 },
4685
4686 onSearch: function(/*===== results, query, options =====*/){
4687 // summary:
4688 // Callback when a search completes.
4689 //
4690 // results: Object
4691 // An array of items from the originating _SearchMixin's store.
4692 //
4693 // query: Object
4694 // A copy of the originating _SearchMixin's query property.
4695 //
4696 // options: Object
4697 // The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
4698 //
4699 // tags:
4700 // callback
4701 },
4702
4703 _startSearchFromInput: function(){
4704 this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
4705 },
4706
4707 _startSearch: function(/*String*/ text){
4708 // summary:
4709 // Starts a search for elements matching text (text=="" means to return all items),
4710 // and calls onSearch(...) when the search completes, to display the results.
4711
4712 this._abortQuery();
4713 var
4714 _this = this,
4715 // Setup parameters to be passed to store.query().
4716 // Create a new query to prevent accidentally querying for a hidden
4717 // value from FilteringSelect's keyField
4718 query = lang.clone(this.query), // #5970
4719 options = {
4720 start: 0,
4721 count: this.pageSize,
4722 queryOptions: { // remove for 2.0
4723 ignoreCase: this.ignoreCase,
4724 deep: true
4725 }
4726 },
4727 qs = string.substitute(this.queryExpr, [text]),
4728 q,
4729 startQuery = function(){
4730 var resPromise = _this._fetchHandle = _this.store.query(query, options);
4731 if(_this.disabled || _this.readOnly || (q !== _this._lastQuery)){
4732 return;
4733 } // avoid getting unwanted notify
4734 when(resPromise, function(res){
4735 _this._fetchHandle = null;
4736 if(!_this.disabled && !_this.readOnly && (q === _this._lastQuery)){ // avoid getting unwanted notify
4737 when(resPromise.total, function(total){
4738 res.total = total;
4739 var pageSize = _this.pageSize;
4740 if(isNaN(pageSize) || pageSize > res.total){ pageSize = res.total; }
4741 // Setup method to fetching the next page of results
4742 res.nextPage = function(direction){
4743 // tell callback the direction of the paging so the screen
4744 // reader knows which menu option to shout
4745 options.direction = direction = direction !== false;
4746 options.count = pageSize;
4747 if(direction){
4748 options.start += res.length;
4749 if(options.start >= res.total){
4750 options.count = 0;
4751 }
4752 }else{
4753 options.start -= pageSize;
4754 if(options.start < 0){
4755 options.count = Math.max(pageSize + options.start, 0);
4756 options.start = 0;
4757 }
4758 }
4759 if(options.count <= 0){
4760 res.length = 0;
4761 _this.onSearch(res, query, options);
4762 }else{
4763 startQuery();
4764 }
4765 };
4766 _this.onSearch(res, query, options);
4767 });
4768 }
4769 }, function(err){
4770 _this._fetchHandle = null;
4771 if(!_this._cancelingQuery){ // don't treat canceled query as an error
4772 console.error(_this.declaredClass + ' ' + err.toString());
4773 }
4774 });
4775 };
4776
4777 lang.mixin(options, this.fetchProperties);
4778
4779 // Generate query
4780 if(this.store._oldAPI){
4781 // remove this branch for 2.0
4782 q = qs;
4783 }else{
4784 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
4785 // but with a toString() method to help dojo/store/JsonRest.
4786 // Search string like "Co*" converted to regex like /^Co.*$/i.
4787 q = filter.patternToRegExp(qs, this.ignoreCase);
4788 q.toString = function(){ return qs; };
4789 }
4790
4791 // set _lastQuery, *then* start the timeout
4792 // otherwise, if the user types and the last query returns before the timeout,
4793 // _lastQuery won't be set and their input gets rewritten
4794 this._lastQuery = query[this.searchAttr] = q;
4795 this._queryDeferHandle = this.defer(startQuery, this.searchDelay);
4796 },
4797
4798 //////////// INITIALIZATION METHODS ///////////////////////////////////////
4799
4800 constructor: function(){
4801 this.query={};
4802 this.fetchProperties={};
4803 },
4804
4805 postMixInProperties: function(){
4806 if(!this.store){
4807 var list = this.list;
4808 if(list){
4809 this.store = registry.byId(list);
4810 }
4811 }
4812 this.inherited(arguments);
4813 }
4814 });
4815 });
4816
4817 },
4818 'dojo/parser':function(){
4819 define(
4820 "dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window",
4821 "./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"],
4822 function(require, dojo, dlang, darray, config, dhtml, dwindow, _Url, djson, aspect, dates, Deferred, has, query, don, ready){
4823
4824 // module:
4825 // dojo/parser
4826
4827 new Date("X"); // workaround for #11279, new Date("") == NaN
4828
4829
4830 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
4831 // If BorderContainer is loaded after _Widget's parameter list has been cached,
4832 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
4833 var extendCnt = 0;
4834 aspect.after(dlang, "extend", function(){
4835 extendCnt++;
4836 }, true);
4837
4838 function getNameMap(ctor){
4839 // summary:
4840 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
4841 var map = ctor._nameCaseMap, proto = ctor.prototype;
4842
4843 // Create the map if it's undefined.
4844 // Refresh the map if a superclass was possibly extended with new methods since the map was created.
4845 if(!map || map._extendCnt < extendCnt){
4846 map = ctor._nameCaseMap = {};
4847 for(var name in proto){
4848 if(name.charAt(0) === "_"){ continue; } // skip internal properties
4849 map[name.toLowerCase()] = name;
4850 }
4851 map._extendCnt = extendCnt;
4852 }
4853 return map;
4854 }
4855
4856 // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor.
4857 var _ctorMap = {};
4858
4859 function getCtor(/*String[]*/ types){
4860 // summary:
4861 // Retrieves a constructor. If the types array contains more than one class/MID then the
4862 // subsequent classes will be mixed into the first class and a unique constructor will be
4863 // returned for that array.
4864
4865 var ts = types.join();
4866 if(!_ctorMap[ts]){
4867 var mixins = [];
4868 for(var i = 0, l = types.length; i < l; i++){
4869 var t = types[i];
4870 // TODO: Consider swapping getObject and require in the future
4871 mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && require(t))));
4872 }
4873 var ctor = mixins.shift();
4874 _ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor;
4875 }
4876
4877 return _ctorMap[ts];
4878 }
4879
4880 var parser = {
4881 // summary:
4882 // The Dom/Widget parsing package
4883
4884 _clearCache: function(){
4885 // summary:
4886 // Clear cached data. Used mainly for benchmarking.
4887 extendCnt++;
4888 _ctorMap = {};
4889 },
4890
4891 _functionFromScript: function(script, attrData){
4892 // summary:
4893 // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>`
4894 // into a function
4895 // script: DOMNode
4896 // The `<script>` DOMNode
4897 // attrData: String
4898 // For HTML5 compliance, searches for attrData + "args" (typically
4899 // "data-dojo-args") instead of "args"
4900 var preamble = "",
4901 suffix = "",
4902 argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")),
4903 withStr = script.getAttribute("with");
4904
4905 // Convert any arguments supplied in script tag into an array to be passed to the
4906 var fnArgs = (argsStr || "").split(/\s*,\s*/);
4907
4908 if(withStr && withStr.length){
4909 darray.forEach(withStr.split(/\s*,\s*/), function(part){
4910 preamble += "with("+part+"){";
4911 suffix += "}";
4912 });
4913 }
4914
4915 return new Function(fnArgs, preamble + script.innerHTML + suffix);
4916 },
4917
4918 instantiate: function(nodes, mixin, options){
4919 // summary:
4920 // Takes array of nodes, and turns them into class instances and
4921 // potentially calls a startup method to allow them to connect with
4922 // any children.
4923 // nodes: Array
4924 // Array of DOM nodes
4925 // mixin: Object?
4926 // An object that will be mixed in with each node in the array.
4927 // Values in the mixin will override values in the node, if they
4928 // exist.
4929 // options: Object?
4930 // An object used to hold kwArgs for instantiation.
4931 // See parse.options argument for details.
4932
4933 mixin = mixin || {};
4934 options = options || {};
4935
4936 var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
4937 attrData = "data-" + (options.scope || dojo._scopeName) + "-",// typically "data-dojo-"
4938 dataDojoType = attrData + "type", // typically "data-dojo-type"
4939 dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
4940
4941 var list = [];
4942 darray.forEach(nodes, function(node){
4943 var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
4944 if(type){
4945 var mixinsValue = node.getAttribute(dataDojoMixins),
4946 types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
4947
4948 list.push({
4949 node: node,
4950 types: types
4951 });
4952 }
4953 });
4954
4955 // Instantiate the nodes and return the objects
4956 return this._instantiate(list, mixin, options);
4957 },
4958
4959 _instantiate: function(nodes, mixin, options){
4960 // summary:
4961 // Takes array of objects representing nodes, and turns them into class instances and
4962 // potentially calls a startup method to allow them to connect with
4963 // any children.
4964 // nodes: Array
4965 // Array of objects like
4966 // | {
4967 // | ctor: Function (may be null)
4968 // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified)
4969 // | node: DOMNode,
4970 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
4971 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
4972 // | }
4973 // mixin: Object
4974 // An object that will be mixed in with each node in the array.
4975 // Values in the mixin will override values in the node, if they
4976 // exist.
4977 // options: Object
4978 // An options object used to hold kwArgs for instantiation.
4979 // See parse.options argument for details.
4980
4981 // Call widget constructors
4982 var thelist = darray.map(nodes, function(obj){
4983 var ctor = obj.ctor || getCtor(obj.types);
4984 // If we still haven't resolved a ctor, it is fatal now
4985 if(!ctor){
4986 throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'");
4987 }
4988 return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited);
4989 }, this);
4990
4991 // Call startup on each top level instance if it makes sense (as for
4992 // widgets). Parent widgets will recursively call startup on their
4993 // (non-top level) children
4994 if(!mixin._started && !options.noStart){
4995 darray.forEach(thelist, function(instance){
4996 if(typeof instance.startup === "function" && !instance._started){
4997 instance.startup();
4998 }
4999 });
5000 }
5001
5002 return thelist;
5003 },
5004
5005 construct: function(ctor, node, mixin, options, scripts, inherited){
5006 // summary:
5007 // Calls new ctor(params, node), where params is the hash of parameters specified on the node,
5008 // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget.
5009 // ctor: Function
5010 // Widget constructor.
5011 // node: DOMNode
5012 // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor.
5013 // mixin: Object?
5014 // Attributes in this object will be passed as parameters to ctor,
5015 // overriding attributes specified on the node.
5016 // options: Object?
5017 // An options object used to hold kwArgs for instantiation. See parse.options argument for details.
5018 // scripts: DomNode[]?
5019 // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node.
5020 // inherited: Object?
5021 // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited.
5022
5023 var proto = ctor && ctor.prototype;
5024 options = options || {};
5025
5026 // Setup hash to hold parameter settings for this widget. Start with the parameter
5027 // settings inherited from ancestors ("dir" and "lang").
5028 // Inherited setting may later be overridden by explicit settings on node itself.
5029 var params = {};
5030
5031 if(options.defaults){
5032 // settings for the document itself (or whatever subtree is being parsed)
5033 dlang.mixin(params, options.defaults);
5034 }
5035 if(inherited){
5036 // settings from dir=rtl or lang=... on a node above this node
5037 dlang.mixin(params, inherited);
5038 }
5039
5040 // Get list of attributes explicitly listed in the markup
5041 var attributes;
5042 if(has("dom-attributes-explicit")){
5043 // Standard path to get list of user specified attributes
5044 attributes = node.attributes;
5045 }else if(has("dom-attributes-specified-flag")){
5046 // Special processing needed for IE8, to skip a few faux values in attributes[]
5047 attributes = darray.filter(node.attributes, function(a){ return a.specified;});
5048 }else{
5049 // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes
5050 var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false),
5051 attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, "");
5052
5053 attributes = darray.map(attrs.split(/\s+/), function(name){
5054 var lcName = name.toLowerCase();
5055 return {
5056 name: name,
5057 // getAttribute() doesn't work for button.value, returns innerHTML of button.
5058 // but getAttributeNode().value doesn't work for the form.encType or li.value
5059 value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ?
5060 node.getAttribute(lcName) : node.getAttributeNode(lcName).value
5061 };
5062 });
5063 }
5064
5065 // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params)
5066 // TODO: remove scope for 2.0
5067 var scope = options.scope || dojo._scopeName,
5068 attrData = "data-" + scope + "-", // typically "data-dojo-"
5069 hash = {};
5070 if(scope !== "dojo"){
5071 hash[attrData + "props"] = "data-dojo-props";
5072 hash[attrData + "type"] = "data-dojo-type";
5073 hash[attrData + "mixins"] = "data-dojo-mixins";
5074 hash[scope + "type"] = "dojoType";
5075 hash[attrData + "id"] = "data-dojo-id";
5076 }
5077
5078 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
5079 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
5080 var i=0, item, funcAttrs=[], jsname, extra;
5081 while(item = attributes[i++]){
5082 var name = item.name,
5083 lcName = name.toLowerCase(),
5084 value = item.value;
5085
5086 switch(hash[lcName] || lcName){
5087 // Already processed, just ignore
5088 case "data-dojo-type":
5089 case "dojotype":
5090 case "data-dojo-mixins":
5091 break;
5092
5093 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
5094 case "data-dojo-props":
5095 extra = value;
5096 break;
5097
5098 // data-dojo-id or jsId. TODO: drop jsId in 2.0
5099 case "data-dojo-id":
5100 case "jsid":
5101 jsname = value;
5102 break;
5103
5104 // For the benefit of _Templated
5105 case "data-dojo-attach-point":
5106 case "dojoattachpoint":
5107 params.dojoAttachPoint = value;
5108 break;
5109 case "data-dojo-attach-event":
5110 case "dojoattachevent":
5111 params.dojoAttachEvent = value;
5112 break;
5113
5114 // Special parameter handling needed for IE
5115 case "class":
5116 params["class"] = node.className;
5117 break;
5118 case "style":
5119 params["style"] = node.style && node.style.cssText;
5120 break;
5121 default:
5122 // Normal attribute, ex: value="123"
5123
5124 // Find attribute in widget corresponding to specified name.
5125 // May involve case conversion, ex: onclick --> onClick
5126 if(!(name in proto)){
5127 var map = getNameMap(ctor);
5128 name = map[lcName] || name;
5129 }
5130
5131 // Set params[name] to value, doing type conversion
5132 if(name in proto){
5133 switch(typeof proto[name]){
5134 case "string":
5135 params[name] = value;
5136 break;
5137 case "number":
5138 params[name] = value.length ? Number(value) : NaN;
5139 break;
5140 case "boolean":
5141 // for checked/disabled value might be "" or "checked". interpret as true.
5142 params[name] = value.toLowerCase() != "false";
5143 break;
5144 case "function":
5145 if(value === "" || value.search(/[^\w\.]+/i) != -1){
5146 // The user has specified some text for a function like "return x+5"
5147 params[name] = new Function(value);
5148 }else{
5149 // The user has specified the name of a global function like "myOnClick"
5150 // or a single word function "return"
5151 params[name] = dlang.getObject(value, false) || new Function(value);
5152 }
5153 funcAttrs.push(name); // prevent "double connect", see #15026
5154 break;
5155 default:
5156 var pVal = proto[name];
5157 params[name] =
5158 (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array
5159 (pVal instanceof Date) ?
5160 (value == "" ? new Date("") : // the NaN of dates
5161 value == "now" ? new Date() : // current date
5162 dates.fromISOString(value)) :
5163 (pVal instanceof _Url) ? (dojo.baseUrl + value) :
5164 djson.fromJson(value);
5165 }
5166 }else{
5167 params[name] = value;
5168 }
5169 }
5170 }
5171
5172 // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026.
5173 // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though).
5174 for(var j=0; j<funcAttrs.length; j++){
5175 var lcfname = funcAttrs[j].toLowerCase();
5176 node.removeAttribute(lcfname);
5177 node[lcfname] = null;
5178 }
5179
5180 // Mix things found in data-dojo-props into the params, overriding any direct settings
5181 if(extra){
5182 try{
5183 extra = djson.fromJson.call(options.propsThis, "{" + extra + "}");
5184 dlang.mixin(params, extra);
5185 }catch(e){
5186 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
5187 throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
5188 }
5189 }
5190
5191 // Any parameters specified in "mixin" override everything else.
5192 dlang.mixin(params, mixin);
5193
5194 // Get <script> nodes associated with this widget, if they weren't specified explicitly
5195 if(!scripts){
5196 scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node));
5197 }
5198
5199 // Process <script type="dojo/*"> script tags
5200 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
5201 // the widget on instantiation.
5202 // <script type="dojo/method"> tags (with no event) are executed after instantiation
5203 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
5204 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
5205 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
5206 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
5207 var aspects = [], // aspects to connect after instantiation
5208 calls = [], // functions to call after instantiation
5209 watches = [], // functions to watch after instantiation
5210 ons = []; // functions to on after instantiation
5211
5212 if(scripts){
5213 for(i=0; i<scripts.length; i++){
5214 var script = scripts[i];
5215 node.removeChild(script);
5216 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
5217 var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
5218 prop = script.getAttribute(attrData + "prop"),
5219 method = script.getAttribute(attrData + "method"),
5220 advice = script.getAttribute(attrData + "advice"),
5221 scriptType = script.getAttribute("type"),
5222 nf = this._functionFromScript(script, attrData);
5223 if(event){
5224 if(scriptType == "dojo/connect"){
5225 aspects.push({ method: event, func: nf });
5226 }else if(scriptType == "dojo/on"){
5227 ons.push({ event: event, func: nf });
5228 }else{
5229 params[event] = nf;
5230 }
5231 }else if(scriptType == "dojo/aspect"){
5232 aspects.push({ method: method, advice: advice, func: nf });
5233 }else if(scriptType == "dojo/watch"){
5234 watches.push({ prop: prop, func: nf });
5235 }else{
5236 calls.push(nf);
5237 }
5238 }
5239 }
5240
5241 // create the instance
5242 var markupFactory = ctor.markupFactory || proto.markupFactory;
5243 var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node);
5244
5245 // map it to the JS namespace if that makes sense
5246 if(jsname){
5247 dlang.setObject(jsname, instance);
5248 }
5249
5250 // process connections and startup functions
5251 for(i=0; i<aspects.length; i++){
5252 aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true);
5253 }
5254 for(i=0; i<calls.length; i++){
5255 calls[i].call(instance);
5256 }
5257 for(i=0; i<watches.length; i++){
5258 instance.watch(watches[i].prop, watches[i].func);
5259 }
5260 for(i=0; i<ons.length; i++){
5261 don(instance, ons[i].event, ons[i].func);
5262 }
5263
5264 return instance;
5265 },
5266
5267 scan: function(root, options){
5268 // summary:
5269 // Scan a DOM tree and return an array of objects representing the DOMNodes
5270 // that need to be turned into widgets.
5271 // description:
5272 // Search specified node (or document root node) recursively for class instances
5273 // and return an array of objects that represent potential widgets to be
5274 // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where
5275 // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name
5276 // like "dijit/form/Button". If the MID is not currently available, scan will
5277 // attempt to require() in the module.
5278 //
5279 // See parser.parse() for details of markup.
5280 // root: DomNode?
5281 // A default starting root node from which to start the parsing. Can be
5282 // omitted, defaulting to the entire document. If omitted, the `options`
5283 // object can be passed in this place. If the `options` object has a
5284 // `rootNode` member, that is used.
5285 // options: Object
5286 // a kwArgs options object, see parse() for details
5287 //
5288 // returns: Promise
5289 // A promise that is resolved with the nodes that have been parsed.
5290
5291 var list = [], // Output List
5292 mids = [], // An array of modules that are not yet loaded
5293 midsHash = {}; // Used to keep the mids array unique
5294
5295 var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
5296 attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-"
5297 dataDojoType = attrData + "type", // typically "data-dojo-type"
5298 dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir"
5299 dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
5300
5301 // Info on DOMNode currently being processed
5302 var node = root.firstChild;
5303
5304 // Info on parent of DOMNode currently being processed
5305 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
5306 // - parent: pointer to identical structure for my parent (or null if no parent)
5307 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
5308 var inherited = options.inherited;
5309 if(!inherited){
5310 function findAncestorAttr(node, attr){
5311 return (node.getAttribute && node.getAttribute(attr)) ||
5312 (node.parentNode && findAncestorAttr(node.parentNode, attr));
5313 }
5314 inherited = {
5315 dir: findAncestorAttr(root, "dir"),
5316 lang: findAncestorAttr(root, "lang"),
5317 textDir: findAncestorAttr(root, dataDojoTextDir)
5318 };
5319 for(var key in inherited){
5320 if(!inherited[key]){ delete inherited[key]; }
5321 }
5322 }
5323
5324 // Metadata about parent node
5325 var parent = {
5326 inherited: inherited
5327 };
5328
5329 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
5330 var scripts;
5331
5332 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
5333 var scriptsOnly;
5334
5335 function getEffective(parent){
5336 // summary:
5337 // Get effective dir, lang, textDir settings for specified obj
5338 // (matching "parent" object structure above), and do caching.
5339 // Take care not to return null entries.
5340 if(!parent.inherited){
5341 parent.inherited = {};
5342 var node = parent.node,
5343 grandparent = getEffective(parent.parent);
5344 var inherited = {
5345 dir: node.getAttribute("dir") || grandparent.dir,
5346 lang: node.getAttribute("lang") || grandparent.lang,
5347 textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir
5348 };
5349 for(var key in inherited){
5350 if(inherited[key]){
5351 parent.inherited[key] = inherited[key];
5352 }
5353 }
5354 }
5355 return parent.inherited;
5356 }
5357
5358 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
5359 while(true){
5360 if(!node){
5361 // Finished this level, continue to parent's next sibling
5362 if(!parent || !parent.node){
5363 break;
5364 }
5365 node = parent.node.nextSibling;
5366 scriptsOnly = false;
5367 parent = parent.parent;
5368 scripts = parent.scripts;
5369 continue;
5370 }
5371
5372 if(node.nodeType != 1){
5373 // Text or comment node, skip to next sibling
5374 node = node.nextSibling;
5375 continue;
5376 }
5377
5378 if(scripts && node.nodeName.toLowerCase() == "script"){
5379 // Save <script type="dojo/..."> for parent, then continue to next sibling
5380 type = node.getAttribute("type");
5381 if(type && /^dojo\/\w/i.test(type)){
5382 scripts.push(node);
5383 }
5384 node = node.nextSibling;
5385 continue;
5386 }
5387 if(scriptsOnly){
5388 // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't
5389 // continue further analysis of the node and will continue to the next sibling
5390 node = node.nextSibling;
5391 continue;
5392 }
5393
5394 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
5395 // TODO: Remove dojoType in 2.0
5396 var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
5397
5398 // Short circuit for leaf nodes containing nothing [but text]
5399 var firstChild = node.firstChild;
5400 if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){
5401 node = node.nextSibling;
5402 continue;
5403 }
5404
5405 // Meta data about current node
5406 var current;
5407
5408 var ctor = null;
5409 if(type){
5410 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate.
5411 var mixinsValue = node.getAttribute(dataDojoMixins),
5412 types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
5413
5414 // Note: won't find classes declared via dojo/Declaration or any modules that haven't been
5415 // loaded yet so use try/catch to avoid throw from require()
5416 try{
5417 ctor = getCtor(types);
5418 }catch(e){}
5419
5420 // If the constructor was not found, check to see if it has modules that can be loaded
5421 if(!ctor){
5422 darray.forEach(types, function(t){
5423 if(~t.indexOf('/') && !midsHash[t]){
5424 // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it.
5425 midsHash[t] = true;
5426 mids[mids.length] = t;
5427 }
5428 });
5429 }
5430
5431 var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children
5432
5433 // Setup meta data about this widget node, and save it to list of nodes to instantiate
5434 current = {
5435 types: types,
5436 ctor: ctor,
5437 parent: parent,
5438 node: node,
5439 scripts: childScripts
5440 };
5441 current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited
5442 list.push(current);
5443 }else{
5444 // Meta data about this non-widget node
5445 current = {
5446 node: node,
5447 scripts: scripts,
5448 parent: parent
5449 };
5450 }
5451
5452 // Recurse, collecting <script type="dojo/..."> children, and also looking for
5453 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
5454 // When finished with children, go to my next sibling.
5455 node = firstChild;
5456 scripts = childScripts;
5457 scriptsOnly = ctor && ctor.prototype.stopParser && !(options.template);
5458 parent = current;
5459 }
5460
5461 var d = new Deferred();
5462
5463 // If there are modules to load then require them in
5464 if(mids.length){
5465 // Warn that there are modules being auto-required
5466 if(has("dojo-debug-messages")){
5467 console.warn("WARNING: Modules being Auto-Required: " + mids.join(", "));
5468 }
5469 require(mids, function(){
5470 // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't
5471 // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to
5472 // auto-require of a module like ContentPane. Assumes list is in DFS order.
5473 d.resolve(darray.filter(list, function(widget){
5474 if(!widget.ctor){
5475 // Attempt to find the constructor again. Still won't find classes defined via
5476 // dijit/Declaration so need to try/catch.
5477 try{
5478 widget.ctor = getCtor(widget.types);
5479 }catch(e){}
5480 }
5481
5482 // Get the parent widget
5483 var parent = widget.parent;
5484 while(parent && !parent.types){
5485 parent = parent.parent;
5486 }
5487
5488 // Return false if this node should be skipped due to stopParser on an ancestor.
5489 // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before
5490 // trying to compute widget.instantiate.
5491 var proto = widget.ctor && widget.ctor.prototype;
5492 widget.instantiateChildren = !(proto && proto.stopParser && !(options.template));
5493 widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren);
5494 return widget.instantiate;
5495 }));
5496 });
5497 }else{
5498 // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for
5499 // efficiency, to avoid running the require() and the callback code above.
5500 d.resolve(list);
5501 }
5502
5503 // Return the promise
5504 return d.promise;
5505 },
5506
5507 _require: function(/*DOMNode*/ script){
5508 // summary:
5509 // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node,
5510 // calls require() to load the specified modules and (asynchronously) assign them to the specified global
5511 // variables, and returns a Promise for when that operation completes.
5512 //
5513 // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
5514
5515 var hash = djson.fromJson("{" + script.innerHTML + "}"),
5516 vars = [],
5517 mids = [],
5518 d = new Deferred();
5519
5520 for(var name in hash){
5521 vars.push(name);
5522 mids.push(hash[name]);
5523 }
5524
5525 require(mids, function(){
5526 for(var i=0; i<vars.length; i++){
5527 dlang.setObject(vars[i], arguments[i]);
5528 }
5529 d.resolve(arguments);
5530 });
5531
5532 return d.promise;
5533 },
5534
5535 _scanAmd: function(root){
5536 // summary:
5537 // Scans the DOM for any declarative requires and returns their values.
5538 // description:
5539 // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the
5540 // specified modules and (asynchronously) assign them to the specified global variables,
5541 // and returns a Promise for when those operations complete.
5542 // root: DomNode
5543 // The node to base the scan from.
5544
5545 // Promise that resolves when all the <script type=dojo/require> nodes have finished loading.
5546 var deferred = new Deferred(),
5547 promise = deferred.promise;
5548 deferred.resolve(true);
5549
5550 var self = this;
5551 query("script[type='dojo/require']", root).forEach(function(node){
5552 // Fire off require() call for specified modules. Chain this require to fire after
5553 // any previous requires complete, so that layers can be loaded before individual module require()'s fire.
5554 promise = promise.then(function(){ return self._require(node); });
5555
5556 // Remove from DOM so it isn't seen again
5557 node.parentNode.removeChild(node);
5558 });
5559
5560 return promise;
5561 },
5562
5563 parse: function(rootNode, options){
5564 // summary:
5565 // Scan the DOM for class instances, and instantiate them.
5566 // description:
5567 // Search specified node (or root node) recursively for class instances,
5568 // and instantiate them. Searches for either data-dojo-type="Class" or
5569 // dojoType="Class" where "Class" is a a fully qualified class name,
5570 // like `dijit/form/Button`
5571 //
5572 // Using `data-dojo-type`:
5573 // Attributes using can be mixed into the parameters used to instantiate the
5574 // Class by using a `data-dojo-props` attribute on the node being converted.
5575 // `data-dojo-props` should be a string attribute to be converted from JSON.
5576 //
5577 // Using `dojoType`:
5578 // Attributes are read from the original domNode and converted to appropriate
5579 // types by looking up the Class prototype values. This is the default behavior
5580 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
5581 // go away in Dojo 2.0.
5582 // rootNode: DomNode?
5583 // A default starting root node from which to start the parsing. Can be
5584 // omitted, defaulting to the entire document. If omitted, the `options`
5585 // object can be passed in this place. If the `options` object has a
5586 // `rootNode` member, that is used.
5587 // options: Object?
5588 // A hash of options.
5589 //
5590 // - noStart: Boolean?:
5591 // when set will prevent the parser from calling .startup()
5592 // when locating the nodes.
5593 // - rootNode: DomNode?:
5594 // identical to the function's `rootNode` argument, though
5595 // allowed to be passed in via this `options object.
5596 // - template: Boolean:
5597 // If true, ignores ContentPane's stopParser flag and parses contents inside of
5598 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
5599 // nested inside the ContentPane to work.
5600 // - inherited: Object:
5601 // Hash possibly containing dir and lang settings to be applied to
5602 // parsed widgets, unless there's another setting on a sub-node that overrides
5603 // - scope: String:
5604 // Root for attribute names to search for. If scopeName is dojo,
5605 // will search for data-dojo-type (or dojoType). For backwards compatibility
5606 // reasons defaults to dojo._scopeName (which is "dojo" except when
5607 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
5608 // - propsThis: Object:
5609 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
5610 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
5611 // returns: Mixed
5612 // Returns a blended object that is an array of the instantiated objects, but also can include
5613 // a promise that is resolved with the instantiated objects. This is done for backwards
5614 // compatibility. If the parser auto-requires modules, it will always behave in a promise
5615 // fashion and `parser.parse().then(function(instances){...})` should be used.
5616 // example:
5617 // Parse all widgets on a page:
5618 // | parser.parse();
5619 // example:
5620 // Parse all classes within the node with id="foo"
5621 // | parser.parse(dojo.byId('foo'));
5622 // example:
5623 // Parse all classes in a page, but do not call .startup() on any
5624 // child
5625 // | parser.parse({ noStart: true })
5626 // example:
5627 // Parse all classes in a node, but do not call .startup()
5628 // | parser.parse(someNode, { noStart:true });
5629 // | // or
5630 // | parser.parse({ noStart:true, rootNode: someNode });
5631
5632 // determine the root node and options based on the passed arguments.
5633 var root;
5634 if(!options && rootNode && rootNode.rootNode){
5635 options = rootNode;
5636 root = options.rootNode;
5637 }else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){
5638 options = rootNode;
5639 }else{
5640 root = rootNode;
5641 }
5642 root = root ? dhtml.byId(root) : dwindow.body();
5643
5644 options = options || {};
5645
5646 var mixin = options.template ? { template: true } : {},
5647 instances = [],
5648 self = this;
5649
5650 // First scan for any <script type=dojo/require> nodes, and execute.
5651 // Then scan for all nodes with data-dojo-type, and load any unloaded modules.
5652 // Then build the object instances. Add instances to already existing (but empty) instances[] array,
5653 // which may already have been returned to caller. Also, use otherwise to collect and throw any errors
5654 // that occur during the parse().
5655 var p =
5656 this._scanAmd(root, options).then(function(){
5657 return self.scan(root, options);
5658 }).then(function(parsedNodes){
5659 return instances = instances.concat(self._instantiate(parsedNodes, mixin, options));
5660 }).otherwise(function(e){
5661 // TODO Modify to follow better pattern for promise error managment when available
5662 console.error("dojo/parser::parse() error", e);
5663 throw e;
5664 });
5665
5666 // Blend the array with the promise
5667 dlang.mixin(instances, p);
5668 return instances;
5669 }
5670 };
5671
5672 if( 1 ){
5673 dojo.parser = parser;
5674 }
5675
5676 // Register the parser callback. It should be the first callback
5677 // after the a11y test.
5678 if(config.parseOnLoad){
5679 ready(100, parser, "parse");
5680 }
5681
5682 return parser;
5683 });
5684
5685 },
5686 '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",
5687 'dojo/dnd/Manager':function(){
5688 define("dojo/dnd/Manager", [
5689 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window",
5690 "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch",
5691 "./common", "./autoscroll", "./Avatar"
5692 ], function(array, declare, event, lang, win, domClass, Evented, has, keys, on, topic, touch,
5693 dnd, autoscroll, Avatar){
5694
5695 // module:
5696 // dojo/dnd/Manager
5697
5698 var Manager = declare("dojo.dnd.Manager", [Evented], {
5699 // summary:
5700 // the manager of DnD operations (usually a singleton)
5701 constructor: function(){
5702 this.avatar = null;
5703 this.source = null;
5704 this.nodes = [];
5705 this.copy = true;
5706 this.target = null;
5707 this.canDropFlag = false;
5708 this.events = [];
5709 },
5710
5711 // avatar's offset from the mouse
5712 OFFSET_X: has("touch") ? 0 : 16,
5713 OFFSET_Y: has("touch") ? -64 : 16,
5714
5715 // methods
5716 overSource: function(source){
5717 // summary:
5718 // called when a source detected a mouse-over condition
5719 // source: Object
5720 // the reporter
5721 if(this.avatar){
5722 this.target = (source && source.targetState != "Disabled") ? source : null;
5723 this.canDropFlag = Boolean(this.target);
5724 this.avatar.update();
5725 }
5726 topic.publish("/dnd/source/over", source);
5727 },
5728 outSource: function(source){
5729 // summary:
5730 // called when a source detected a mouse-out condition
5731 // source: Object
5732 // the reporter
5733 if(this.avatar){
5734 if(this.target == source){
5735 this.target = null;
5736 this.canDropFlag = false;
5737 this.avatar.update();
5738 topic.publish("/dnd/source/over", null);
5739 }
5740 }else{
5741 topic.publish("/dnd/source/over", null);
5742 }
5743 },
5744 startDrag: function(source, nodes, copy){
5745 // summary:
5746 // called to initiate the DnD operation
5747 // source: Object
5748 // the source which provides items
5749 // nodes: Array
5750 // the list of transferred items
5751 // copy: Boolean
5752 // copy items, if true, move items otherwise
5753
5754 // Tell autoscroll that a drag is starting
5755 autoscroll.autoScrollStart(win.doc);
5756
5757 this.source = source;
5758 this.nodes = nodes;
5759 this.copy = Boolean(copy); // normalizing to true boolean
5760 this.avatar = this.makeAvatar();
5761 win.body().appendChild(this.avatar.node);
5762 topic.publish("/dnd/start", source, nodes, this.copy);
5763 this.events = [
5764 on(win.doc, touch.move, lang.hitch(this, "onMouseMove")),
5765 on(win.doc, touch.release, lang.hitch(this, "onMouseUp")),
5766 on(win.doc, "keydown", lang.hitch(this, "onKeyDown")),
5767 on(win.doc, "keyup", lang.hitch(this, "onKeyUp")),
5768 // cancel text selection and text dragging
5769 on(win.doc, "dragstart", event.stop),
5770 on(win.body(), "selectstart", event.stop)
5771 ];
5772 var c = "dojoDnd" + (copy ? "Copy" : "Move");
5773 domClass.add(win.body(), c);
5774 },
5775 canDrop: function(flag){
5776 // summary:
5777 // called to notify if the current target can accept items
5778 var canDropFlag = Boolean(this.target && flag);
5779 if(this.canDropFlag != canDropFlag){
5780 this.canDropFlag = canDropFlag;
5781 this.avatar.update();
5782 }
5783 },
5784 stopDrag: function(){
5785 // summary:
5786 // stop the DnD in progress
5787 domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]);
5788 array.forEach(this.events, function(handle){ handle.remove(); });
5789 this.events = [];
5790 this.avatar.destroy();
5791 this.avatar = null;
5792 this.source = this.target = null;
5793 this.nodes = [];
5794 },
5795 makeAvatar: function(){
5796 // summary:
5797 // makes the avatar; it is separate to be overwritten dynamically, if needed
5798 return new Avatar(this);
5799 },
5800 updateAvatar: function(){
5801 // summary:
5802 // updates the avatar; it is separate to be overwritten dynamically, if needed
5803 this.avatar.update();
5804 },
5805
5806 // mouse event processors
5807 onMouseMove: function(e){
5808 // summary:
5809 // event processor for onmousemove
5810 // e: Event
5811 // mouse event
5812 var a = this.avatar;
5813 if(a){
5814 autoscroll.autoScrollNodes(e);
5815 //autoscroll.autoScroll(e);
5816 var s = a.node.style;
5817 s.left = (e.pageX + this.OFFSET_X) + "px";
5818 s.top = (e.pageY + this.OFFSET_Y) + "px";
5819 var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
5820 if(this.copy != copy){
5821 this._setCopyStatus(copy);
5822 }
5823 }
5824 if(has("touch")){
5825 // Prevent page from scrolling so that user can drag instead.
5826 e.preventDefault();
5827 }
5828 },
5829 onMouseUp: function(e){
5830 // summary:
5831 // event processor for onmouseup
5832 // e: Event
5833 // mouse event
5834 if(this.avatar){
5835 if(this.target && this.canDropFlag){
5836 var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
5837 topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e);
5838 topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e);
5839 }else{
5840 topic.publish("/dnd/cancel");
5841 }
5842 this.stopDrag();
5843 }
5844 },
5845
5846 // keyboard event processors
5847 onKeyDown: function(e){
5848 // summary:
5849 // event processor for onkeydown:
5850 // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
5851 // e: Event
5852 // keyboard event
5853 if(this.avatar){
5854 switch(e.keyCode){
5855 case keys.CTRL:
5856 var copy = Boolean(this.source.copyState(true));
5857 if(this.copy != copy){
5858 this._setCopyStatus(copy);
5859 }
5860 break;
5861 case keys.ESCAPE:
5862 topic.publish("/dnd/cancel");
5863 this.stopDrag();
5864 break;
5865 }
5866 }
5867 },
5868 onKeyUp: function(e){
5869 // summary:
5870 // event processor for onkeyup, watching for CTRL for copy/move status
5871 // e: Event
5872 // keyboard event
5873 if(this.avatar && e.keyCode == keys.CTRL){
5874 var copy = Boolean(this.source.copyState(false));
5875 if(this.copy != copy){
5876 this._setCopyStatus(copy);
5877 }
5878 }
5879 },
5880
5881 // utilities
5882 _setCopyStatus: function(copy){
5883 // summary:
5884 // changes the copy status
5885 // copy: Boolean
5886 // the copy status
5887 this.copy = copy;
5888 this.source._markDndStatus(this.copy);
5889 this.updateAvatar();
5890 domClass.replace(win.body(),
5891 "dojoDnd" + (this.copy ? "Copy" : "Move"),
5892 "dojoDnd" + (this.copy ? "Move" : "Copy"));
5893 }
5894 });
5895
5896 // dnd._manager:
5897 // The manager singleton variable. Can be overwritten if needed.
5898 dnd._manager = null;
5899
5900 Manager.manager = dnd.manager = function(){
5901 // summary:
5902 // Returns the current DnD manager. Creates one if it is not created yet.
5903 if(!dnd._manager){
5904 dnd._manager = new Manager();
5905 }
5906 return dnd._manager; // Object
5907 };
5908
5909 return Manager;
5910 });
5911
5912 },
5913 'dijit/form/ToggleButton':function(){
5914 define("dijit/form/ToggleButton", [
5915 "dojo/_base/declare", // declare
5916 "dojo/_base/kernel", // kernel.deprecated
5917 "./Button",
5918 "./_ToggleButtonMixin"
5919 ], function(declare, kernel, Button, _ToggleButtonMixin){
5920
5921 // module:
5922 // dijit/form/ToggleButton
5923
5924
5925 return declare("dijit.form.ToggleButton", [Button, _ToggleButtonMixin], {
5926 // summary:
5927 // A templated button widget that can be in two states (checked or not).
5928 // Can be base class for things like tabs or checkbox or radio buttons.
5929
5930 baseClass: "dijitToggleButton",
5931
5932 setChecked: function(/*Boolean*/ checked){
5933 // summary:
5934 // Deprecated. Use set('checked', true/false) instead.
5935 kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
5936 this.set('checked', checked);
5937 }
5938 });
5939 });
5940
5941 },
5942 'dojo/date/stamp':function(){
5943 define("dojo/date/stamp", ["../_base/lang", "../_base/array"], function(lang, array){
5944
5945 // module:
5946 // dojo/date/stamp
5947
5948 var stamp = {
5949 // summary:
5950 // TODOC
5951 };
5952 lang.setObject("dojo.date.stamp", stamp);
5953
5954 // Methods to convert dates to or from a wire (string) format using well-known conventions
5955
5956 stamp.fromISOString = function(/*String*/ formattedString, /*Number?*/ defaultTime){
5957 // summary:
5958 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
5959 //
5960 // description:
5961 // Accepts a string formatted according to a profile of ISO8601 as defined by
5962 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
5963 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
5964 // The following combinations are valid:
5965 //
5966 // - dates only
5967 // - yyyy
5968 // - yyyy-MM
5969 // - yyyy-MM-dd
5970 // - times only, with an optional time zone appended
5971 // - THH:mm
5972 // - THH:mm:ss
5973 // - THH:mm:ss.SSS
5974 // - and "datetimes" which could be any combination of the above
5975 //
5976 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
5977 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
5978 // input may return null. Arguments which are out of bounds will be handled
5979 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
5980 // Only years between 100 and 9999 are supported.
5981 // formattedString:
5982 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
5983 // defaultTime:
5984 // Used for defaults for fields omitted in the formattedString.
5985 // Uses 1970-01-01T00:00:00.0Z by default.
5986
5987 if(!stamp._isoRegExp){
5988 stamp._isoRegExp =
5989 //TODO: could be more restrictive and check for 00-59, etc.
5990 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
5991 }
5992
5993 var match = stamp._isoRegExp.exec(formattedString),
5994 result = null;
5995
5996 if(match){
5997 match.shift();
5998 if(match[1]){match[1]--;} // Javascript Date months are 0-based
5999 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
6000
6001 if(defaultTime){
6002 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
6003 defaultTime = new Date(defaultTime);
6004 array.forEach(array.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
6005 return defaultTime["get" + prop]();
6006 }), function(value, index){
6007 match[index] = match[index] || value;
6008 });
6009 }
6010 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
6011 if(match[0] < 100){
6012 result.setFullYear(match[0] || 1970);
6013 }
6014
6015 var offset = 0,
6016 zoneSign = match[7] && match[7].charAt(0);
6017 if(zoneSign != 'Z'){
6018 offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
6019 if(zoneSign != '-'){ offset *= -1; }
6020 }
6021 if(zoneSign){
6022 offset -= result.getTimezoneOffset();
6023 }
6024 if(offset){
6025 result.setTime(result.getTime() + offset * 60000);
6026 }
6027 }
6028
6029 return result; // Date or null
6030 };
6031
6032 /*=====
6033 var __Options = {
6034 // selector: String
6035 // "date" or "time" for partial formatting of the Date object.
6036 // Both date and time will be formatted by default.
6037 // zulu: Boolean
6038 // if true, UTC/GMT is used for a timezone
6039 // milliseconds: Boolean
6040 // if true, output milliseconds
6041 };
6042 =====*/
6043
6044 stamp.toISOString = function(/*Date*/ dateObject, /*__Options?*/ options){
6045 // summary:
6046 // Format a Date object as a string according a subset of the ISO-8601 standard
6047 //
6048 // description:
6049 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
6050 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
6051 // Does not check bounds. Only years between 100 and 9999 are supported.
6052 //
6053 // dateObject:
6054 // A Date object
6055
6056 var _ = function(n){ return (n < 10) ? "0" + n : n; };
6057 options = options || {};
6058 var formattedDate = [],
6059 getter = options.zulu ? "getUTC" : "get",
6060 date = "";
6061 if(options.selector != "time"){
6062 var year = dateObject[getter+"FullYear"]();
6063 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
6064 }
6065 formattedDate.push(date);
6066 if(options.selector != "date"){
6067 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
6068 var millis = dateObject[getter+"Milliseconds"]();
6069 if(options.milliseconds){
6070 time += "."+ (millis < 100 ? "0" : "") + _(millis);
6071 }
6072 if(options.zulu){
6073 time += "Z";
6074 }else if(options.selector != "time"){
6075 var timezoneOffset = dateObject.getTimezoneOffset();
6076 var absOffset = Math.abs(timezoneOffset);
6077 time += (timezoneOffset > 0 ? "-" : "+") +
6078 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
6079 }
6080 formattedDate.push(time);
6081 }
6082 return formattedDate.join('T'); // String
6083 };
6084
6085 return stamp;
6086 });
6087
6088 },
6089 'dojo/Stateful':function(){
6090 define("dojo/Stateful", ["./_base/declare", "./_base/lang", "./_base/array", "dojo/when"], function(declare, lang, array, when){
6091 // module:
6092 // dojo/Stateful
6093
6094 return declare("dojo.Stateful", null, {
6095 // summary:
6096 // Base class for objects that provide named properties with optional getter/setter
6097 // control and the ability to watch for property changes
6098 //
6099 // The class also provides the functionality to auto-magically manage getters
6100 // and setters for object attributes/properties.
6101 //
6102 // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
6103 // the xxx is a name of the attribute to handle. So an attribute of "foo"
6104 // would have a custom getter of _fooGetter and a custom setter of _fooSetter.
6105 //
6106 // example:
6107 // | var obj = new dojo.Stateful();
6108 // | obj.watch("foo", function(){
6109 // | console.log("foo changed to " + this.get("foo"));
6110 // | });
6111 // | obj.set("foo","bar");
6112
6113 // _attrPairNames: Hash
6114 // Used across all instances a hash to cache attribute names and their getter
6115 // and setter names.
6116 _attrPairNames: {},
6117
6118 _getAttrNames: function(name){
6119 // summary:
6120 // Helper function for get() and set().
6121 // Caches attribute name values so we don't do the string ops every time.
6122 // tags:
6123 // private
6124
6125 var apn = this._attrPairNames;
6126 if(apn[name]){ return apn[name]; }
6127 return (apn[name] = {
6128 s: "_" + name + "Setter",
6129 g: "_" + name + "Getter"
6130 });
6131 },
6132
6133 postscript: function(/*Object?*/ params){
6134 // Automatic setting of params during construction
6135 if (params){ this.set(params); }
6136 },
6137
6138 _get: function(name, names){
6139 // summary:
6140 // Private function that does a get based off a hash of names
6141 // names:
6142 // Hash of names of custom attributes
6143 return typeof this[names.g] === "function" ? this[names.g]() : this[name];
6144 },
6145 get: function(/*String*/name){
6146 // summary:
6147 // Get a property on a Stateful instance.
6148 // name:
6149 // The property to get.
6150 // returns:
6151 // The property value on this Stateful instance.
6152 // description:
6153 // Get a named property on a Stateful object. The property may
6154 // potentially be retrieved via a getter method in subclasses. In the base class
6155 // this just retrieves the object's property.
6156 // For example:
6157 // | stateful = new dojo.Stateful({foo: 3});
6158 // | stateful.get("foo") // returns 3
6159 // | stateful.foo // returns 3
6160
6161 return this._get(name, this._getAttrNames(name)); //Any
6162 },
6163 set: function(/*String*/name, /*Object*/value){
6164 // summary:
6165 // Set a property on a Stateful instance
6166 // name:
6167 // The property to set.
6168 // value:
6169 // The value to set in the property.
6170 // returns:
6171 // The function returns this dojo.Stateful instance.
6172 // description:
6173 // Sets named properties on a stateful object and notifies any watchers of
6174 // the property. A programmatic setter may be defined in subclasses.
6175 // For example:
6176 // | stateful = new dojo.Stateful();
6177 // | stateful.watch(function(name, oldValue, value){
6178 // | // this will be called on the set below
6179 // | }
6180 // | stateful.set(foo, 5);
6181 //
6182 // set() may also be called with a hash of name/value pairs, ex:
6183 // | myObj.set({
6184 // | foo: "Howdy",
6185 // | bar: 3
6186 // | })
6187 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
6188
6189 // If an object is used, iterate through object
6190 if(typeof name === "object"){
6191 for(var x in name){
6192 if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
6193 this.set(x, name[x]);
6194 }
6195 }
6196 return this;
6197 }
6198
6199 var names = this._getAttrNames(name),
6200 oldValue = this._get(name, names),
6201 setter = this[names.s],
6202 result;
6203 if(typeof setter === "function"){
6204 // use the explicit setter
6205 result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
6206 }else{
6207 // no setter so set attribute directly
6208 this[name] = value;
6209 }
6210 if(this._watchCallbacks){
6211 var self = this;
6212 // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
6213 when(result, function(){
6214 self._watchCallbacks(name, oldValue, value);
6215 });
6216 }
6217 return this; // dojo/Stateful
6218 },
6219 _changeAttrValue: function(name, value){
6220 // summary:
6221 // Internal helper for directly changing an attribute value.
6222 //
6223 // name: String
6224 // The property to set.
6225 // value: Mixed
6226 // The value to set in the property.
6227 //
6228 // description:
6229 // Directly change the value of an attribute on an object, bypassing any
6230 // accessor setter. Also handles the calling of watch and emitting events.
6231 // It is designed to be used by descendent class when there are two values
6232 // of attributes that are linked, but calling .set() is not appropriate.
6233
6234 var oldValue = this.get(name);
6235 this[name] = value;
6236 if(this._watchCallbacks){
6237 this._watchCallbacks(name, oldValue, value);
6238 }
6239 return this; // dojo/Stateful
6240 },
6241 watch: function(/*String?*/name, /*Function*/callback){
6242 // summary:
6243 // Watches a property for changes
6244 // name:
6245 // Indicates the property to watch. This is optional (the callback may be the
6246 // only parameter), and if omitted, all the properties will be watched
6247 // returns:
6248 // An object handle for the watch. The unwatch method of this object
6249 // can be used to discontinue watching this property:
6250 // | var watchHandle = obj.watch("foo", callback);
6251 // | watchHandle.unwatch(); // callback won't be called now
6252 // callback:
6253 // The function to execute when the property changes. This will be called after
6254 // the property has been changed. The callback will be called with the |this|
6255 // set to the instance, the first argument as the name of the property, the
6256 // second argument as the old value and the third argument as the new value.
6257
6258 var callbacks = this._watchCallbacks;
6259 if(!callbacks){
6260 var self = this;
6261 callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
6262 var notify = function(propertyCallbacks){
6263 if(propertyCallbacks){
6264 propertyCallbacks = propertyCallbacks.slice();
6265 for(var i = 0, l = propertyCallbacks.length; i < l; i++){
6266 propertyCallbacks[i].call(self, name, oldValue, value);
6267 }
6268 }
6269 };
6270 notify(callbacks['_' + name]);
6271 if(!ignoreCatchall){
6272 notify(callbacks["*"]); // the catch-all
6273 }
6274 }; // we use a function instead of an object so it will be ignored by JSON conversion
6275 }
6276 if(!callback && typeof name === "function"){
6277 callback = name;
6278 name = "*";
6279 }else{
6280 // prepend with dash to prevent name conflicts with function (like "name" property)
6281 name = '_' + name;
6282 }
6283 var propertyCallbacks = callbacks[name];
6284 if(typeof propertyCallbacks !== "object"){
6285 propertyCallbacks = callbacks[name] = [];
6286 }
6287 propertyCallbacks.push(callback);
6288
6289 // TODO: Remove unwatch in 2.0
6290 var handle = {};
6291 handle.unwatch = handle.remove = function(){
6292 var index = array.indexOf(propertyCallbacks, callback);
6293 if(index > -1){
6294 propertyCallbacks.splice(index, 1);
6295 }
6296 };
6297 return handle; //Object
6298 }
6299
6300 });
6301
6302 });
6303
6304 },
6305 'dijit/layout/AccordionContainer':function(){
6306 require({cache:{
6307 '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"}});
6308 define("dijit/layout/AccordionContainer", [
6309 "require",
6310 "dojo/_base/array", // array.forEach array.map
6311 "dojo/_base/declare", // declare
6312 "dojo/_base/event", // event.stop
6313 "dojo/_base/fx", // fx.Animation
6314 "dojo/dom", // dom.setSelectable
6315 "dojo/dom-attr", // domAttr.attr
6316 "dojo/dom-class", // domClass.remove
6317 "dojo/dom-construct", // domConstruct.place
6318 "dojo/dom-geometry",
6319 "dojo/keys", // keys
6320 "dojo/_base/lang", // lang.getObject lang.hitch
6321 "dojo/sniff", // has("ie") has("dijit-legacy-requires")
6322 "dojo/topic", // publish
6323 "../focus", // focus.focus()
6324 "../_base/manager", // manager.defaultDuration
6325 "dojo/ready",
6326 "../_Widget",
6327 "../_Container",
6328 "../_TemplatedMixin",
6329 "../_CssStateMixin",
6330 "./StackContainer",
6331 "./ContentPane",
6332 "dojo/text!./templates/AccordionButton.html"
6333 ], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
6334 keys, lang, has, topic, focus, manager, ready,
6335 _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
6336
6337 // module:
6338 // dijit/layout/AccordionContainer
6339
6340
6341 // Design notes:
6342 //
6343 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
6344 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
6345 //
6346 // The resulting markup will look like:
6347 //
6348 // <div class=dijitAccordionContainer>
6349 // <div class=dijitAccordionInnerContainer> (one pane)
6350 // <div class=dijitAccordionTitle> (title bar) ... </div>
6351 // <div class=dijtAccordionChildWrapper> (content pane) </div>
6352 // </div>
6353 // </div>
6354 //
6355 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
6356 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
6357 // which on claro has a 1px border plus a 2px bottom margin.
6358 //
6359 // During animation there are two dijtAccordionChildWrapper's shown, so we need
6360 // to compensate for that.
6361
6362
6363 var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
6364 // summary:
6365 // The title bar to click to open up an accordion pane.
6366 // Internal widget used by AccordionContainer.
6367 // tags:
6368 // private
6369
6370 templateString: template,
6371
6372 // label: String
6373 // Title of the pane
6374 label: "",
6375 _setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
6376
6377 // title: String
6378 // Tooltip that appears on hover
6379 title: "",
6380 _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
6381
6382 // iconClassAttr: String
6383 // CSS class for icon to left of label
6384 iconClassAttr: "",
6385 _setIconClassAttr: { node: "iconNode", type: "class" },
6386
6387 baseClass: "dijitAccordionTitle",
6388
6389 getParent: function(){
6390 // summary:
6391 // Returns the AccordionContainer parent.
6392 // tags:
6393 // private
6394 return this.parent;
6395 },
6396
6397 buildRendering: function(){
6398 this.inherited(arguments);
6399 var titleTextNodeId = this.id.replace(' ','_');
6400 domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
6401 this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
6402 dom.setSelectable(this.domNode, false);
6403 },
6404
6405 getTitleHeight: function(){
6406 // summary:
6407 // Returns the height of the title dom node.
6408 return domGeometry.getMarginSize(this.domNode).h; // Integer
6409 },
6410
6411 // TODO: maybe the parent should set these methods directly rather than forcing the code
6412 // into the button widget?
6413 _onTitleClick: function(){
6414 // summary:
6415 // Callback when someone clicks my title.
6416 var parent = this.getParent();
6417 parent.selectChild(this.contentWidget, true);
6418 focus.focus(this.focusNode);
6419 },
6420
6421 _onTitleKeyPress: function(/*Event*/ evt){
6422 return this.getParent()._onKeyPress(evt, this.contentWidget);
6423 },
6424
6425 _setSelectedAttr: function(/*Boolean*/ isSelected){
6426 this._set("selected", isSelected);
6427 this.focusNode.setAttribute("aria-expanded", isSelected ? "true" : "false");
6428 this.focusNode.setAttribute("aria-selected", isSelected ? "true" : "false");
6429 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
6430 }
6431 });
6432
6433 var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
6434 // summary:
6435 // Internal widget placed as direct child of AccordionContainer.containerNode.
6436 // When other widgets are added as children to an AccordionContainer they are wrapped in
6437 // this widget.
6438
6439 /*=====
6440 // buttonWidget: Function|String
6441 // Class to use to instantiate title
6442 // (Wish we didn't have a separate widget for just the title but maintaining it
6443 // for backwards compatibility, is it worth it?)
6444 buttonWidget: null,
6445 =====*/
6446
6447 /*=====
6448 // contentWidget: dijit/_WidgetBase
6449 // Pointer to the real child widget
6450 contentWidget: null,
6451 =====*/
6452
6453 baseClass: "dijitAccordionInnerContainer",
6454
6455 // tell nested layout widget that we will take care of sizing
6456 isLayoutContainer: true,
6457
6458 buildRendering: function(){
6459 // Builds a template like:
6460 // <div class=dijitAccordionInnerContainer>
6461 // Button
6462 // <div class=dijitAccordionChildWrapper>
6463 // ContentPane
6464 // </div>
6465 // </div>
6466
6467 // Create wrapper div, placed where the child is now
6468 this.domNode = domConstruct.place("<div class='" + this.baseClass +
6469 "' role='presentation'>", this.contentWidget.domNode, "after");
6470
6471 // wrapper div's first child is the button widget (ie, the title bar)
6472 var child = this.contentWidget,
6473 cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
6474 this.button = child._buttonWidget = (new cls({
6475 contentWidget: child,
6476 label: child.title,
6477 title: child.tooltip,
6478 dir: child.dir,
6479 lang: child.lang,
6480 textDir: child.textDir,
6481 iconClass: child.iconClass,
6482 id: child.id + "_button",
6483 parent: this.parent
6484 })).placeAt(this.domNode);
6485
6486 // and then the actual content widget (changing it from prior-sibling to last-child),
6487 // wrapped by a <div class=dijitAccordionChildWrapper>
6488 this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
6489 domConstruct.place(this.contentWidget.domNode, this.containerNode);
6490 },
6491
6492 postCreate: function(){
6493 this.inherited(arguments);
6494
6495 // Map changes in content widget's title etc. to changes in the button
6496 var button = this.button;
6497 this._contentWidgetWatches = [
6498 this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
6499 button.set("label", newValue);
6500 })),
6501 this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
6502 button.set("title", newValue);
6503 })),
6504 this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
6505 button.set("iconClass", newValue);
6506 }))
6507 ];
6508 },
6509
6510 _setSelectedAttr: function(/*Boolean*/ isSelected){
6511 this._set("selected", isSelected);
6512 this.button.set("selected", isSelected);
6513 if(isSelected){
6514 var cw = this.contentWidget;
6515 if(cw.onSelected){ cw.onSelected(); }
6516 }
6517 },
6518
6519 startup: function(){
6520 // Called by _Container.addChild()
6521 this.contentWidget.startup();
6522 },
6523
6524 destroy: function(){
6525 this.button.destroyRecursive();
6526
6527 array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
6528
6529 delete this.contentWidget._buttonWidget;
6530 delete this.contentWidget._wrapperWidget;
6531
6532 this.inherited(arguments);
6533 },
6534
6535 destroyDescendants: function(/*Boolean*/ preserveDom){
6536 // since getChildren isn't working for me, have to code this manually
6537 this.contentWidget.destroyRecursive(preserveDom);
6538 }
6539 });
6540
6541 var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
6542 // summary:
6543 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
6544 // and switching between panes is visualized by sliding the other panes up/down.
6545 // example:
6546 // | <div data-dojo-type="dijit/layout/AccordionContainer">
6547 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
6548 // | </div>
6549 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
6550 // | <p>This is some text</p>
6551 // | </div>
6552 // | </div>
6553
6554 // duration: Integer
6555 // Amount of time (in ms) it takes to slide panes
6556 duration: manager.defaultDuration,
6557
6558 // buttonWidget: [const] String
6559 // The name of the widget used to display the title of each pane
6560 buttonWidget: AccordionButton,
6561
6562 /*=====
6563 // _verticalSpace: Number
6564 // Pixels of space available for the open pane
6565 // (my content box size minus the cumulative size of all the title bars)
6566 _verticalSpace: 0,
6567 =====*/
6568 baseClass: "dijitAccordionContainer",
6569
6570 buildRendering: function(){
6571 this.inherited(arguments);
6572 this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
6573 this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
6574 },
6575
6576 startup: function(){
6577 if(this._started){ return; }
6578 this.inherited(arguments);
6579 if(this.selectedChildWidget){
6580 this.selectedChildWidget._wrapperWidget.set("selected", true);
6581 }
6582 },
6583
6584 layout: function(){
6585 // Implement _LayoutWidget.layout() virtual method.
6586 // Set the height of the open pane based on what room remains.
6587
6588 var openPane = this.selectedChildWidget;
6589
6590 if(!openPane){ return;}
6591
6592 // space taken up by title, plus wrapper div (with border/margin) for open pane
6593 var wrapperDomNode = openPane._wrapperWidget.domNode,
6594 wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
6595 wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
6596 wrapperContainerNode = openPane._wrapperWidget.containerNode,
6597 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
6598 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
6599 mySize = this._contentBox;
6600
6601 // get cumulative height of all the unselected title bars
6602 var totalCollapsedHeight = 0;
6603 array.forEach(this.getChildren(), function(child){
6604 if(child != openPane){
6605 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
6606 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
6607 // margin below the bottom pane (even though we don't want one).
6608 totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
6609 }
6610 });
6611 this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
6612 - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
6613 - openPane._buttonWidget.getTitleHeight();
6614
6615 // Memo size to make displayed child
6616 this._containerContentBox = {
6617 h: this._verticalSpace,
6618 w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
6619 - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
6620 };
6621
6622 if(openPane){
6623 openPane.resize(this._containerContentBox);
6624 }
6625 },
6626
6627 _setupChild: function(child){
6628 // Overrides _LayoutWidget._setupChild().
6629 // Put wrapper widget around the child widget, showing title
6630
6631 child._wrapperWidget = AccordionInnerContainer({
6632 contentWidget: child,
6633 buttonWidget: this.buttonWidget,
6634 id: child.id + "_wrapper",
6635 dir: child.dir,
6636 lang: child.lang,
6637 textDir: child.textDir,
6638 parent: this
6639 });
6640
6641 this.inherited(arguments);
6642 },
6643
6644 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
6645 // Overrides _LayoutWidget.addChild().
6646 if(this._started){
6647 // Adding a child to a started Accordion is complicated because children have
6648 // wrapper widgets. Default code path (calling this.inherited()) would add
6649 // the new child inside another child's wrapper.
6650
6651 // First add in child as a direct child of this AccordionContainer
6652 var refNode = this.containerNode;
6653 if(insertIndex && typeof insertIndex == "number"){
6654 var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
6655 if(children && children.length >= insertIndex){
6656 refNode = children[insertIndex-1].domNode;
6657 insertIndex = "after";
6658 }
6659 }
6660 domConstruct.place(child.domNode, refNode, insertIndex);
6661
6662 if(!child._started){
6663 child.startup();
6664 }
6665
6666 // Then stick the wrapper widget around the child widget
6667 this._setupChild(child);
6668
6669 // Code below copied from StackContainer
6670 topic.publish(this.id+"-addChild", child, insertIndex); // publish
6671 this.layout();
6672 if(!this.selectedChildWidget){
6673 this.selectChild(child);
6674 }
6675 }else{
6676 // We haven't been started yet so just add in the child widget directly,
6677 // and the wrapper will be created on startup()
6678 this.inherited(arguments);
6679 }
6680 },
6681
6682 removeChild: function(child){
6683 // Overrides _LayoutWidget.removeChild().
6684
6685 // Destroy wrapper widget first, before StackContainer.getChildren() call.
6686 // Replace wrapper widget with true child widget (ContentPane etc.).
6687 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
6688 if(child._wrapperWidget){
6689 domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
6690 child._wrapperWidget.destroy();
6691 delete child._wrapperWidget;
6692 }
6693
6694 domClass.remove(child.domNode, "dijitHidden");
6695
6696 this.inherited(arguments);
6697 },
6698
6699 getChildren: function(){
6700 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
6701 return array.map(this.inherited(arguments), function(child){
6702 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
6703 }, this);
6704 },
6705
6706 destroy: function(){
6707 if(this._animation){
6708 this._animation.stop();
6709 }
6710 array.forEach(this.getChildren(), function(child){
6711 // If AccordionContainer has been started, then each child has a wrapper widget which
6712 // also needs to be destroyed.
6713 if(child._wrapperWidget){
6714 child._wrapperWidget.destroy();
6715 }else{
6716 child.destroyRecursive();
6717 }
6718 });
6719 this.inherited(arguments);
6720 },
6721
6722 _showChild: function(child){
6723 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6724 child._wrapperWidget.containerNode.style.display="block";
6725 return this.inherited(arguments);
6726 },
6727
6728 _hideChild: function(child){
6729 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6730 child._wrapperWidget.containerNode.style.display="none";
6731 this.inherited(arguments);
6732 },
6733
6734 _transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate){
6735 // Overrides StackContainer._transition() to provide sliding of title bars etc.
6736
6737 if(has("ie") < 8){
6738 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
6739 animate = false;
6740 }
6741
6742 if(this._animation){
6743 // there's an in-progress animation. speedily end it so we can do the newly requested one
6744 this._animation.stop(true);
6745 delete this._animation;
6746 }
6747
6748 var self = this;
6749
6750 if(newWidget){
6751 newWidget._wrapperWidget.set("selected", true);
6752
6753 var d = this._showChild(newWidget); // prepare widget to be slid in
6754
6755 // Size the new widget, in case this is the first time it's being shown,
6756 // or I have been resized since the last time it was shown.
6757 // Note that page must be visible for resizing to work.
6758 if(this.doLayout && newWidget.resize){
6759 newWidget.resize(this._containerContentBox);
6760 }
6761 }
6762
6763 if(oldWidget){
6764 oldWidget._wrapperWidget.set("selected", false);
6765 if(!animate){
6766 this._hideChild(oldWidget);
6767 }
6768 }
6769
6770 if(animate){
6771 var newContents = newWidget._wrapperWidget.containerNode,
6772 oldContents = oldWidget._wrapperWidget.containerNode;
6773
6774 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
6775 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
6776 // Have to compensate for that by immediately shrinking the pane being closed.
6777 var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
6778 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
6779 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
6780 animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
6781
6782 oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
6783
6784 this._animation = new fx.Animation({
6785 node: newContents,
6786 duration: this.duration,
6787 curve: [1, this._verticalSpace - animationHeightOverhead - 1],
6788 onAnimate: function(value){
6789 value = Math.floor(value); // avoid fractional values
6790 newContents.style.height = value + "px";
6791 oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
6792 },
6793 onEnd: function(){
6794 delete self._animation;
6795 newContents.style.height = "auto";
6796 oldWidget._wrapperWidget.containerNode.style.display = "none";
6797 oldContents.style.height = "auto";
6798 self._hideChild(oldWidget);
6799 }
6800 });
6801 this._animation.onStop = this._animation.onEnd;
6802 this._animation.play();
6803 }
6804
6805 return d; // If child has an href, promise that fires when the widget has finished loading
6806 },
6807
6808 // note: we are treating the container as controller here
6809 _onKeyPress: function(/*Event*/ e, /*dijit/_WidgetBase*/ fromTitle){
6810 // summary:
6811 // Handle keypress events
6812 // description:
6813 // This is called from a handler on AccordionContainer.domNode
6814 // (setup in StackContainer), and is also called directly from
6815 // the click handler for accordion labels
6816 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
6817 return;
6818 }
6819 var c = e.charOrCode;
6820 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
6821 (e.ctrlKey && c == keys.PAGE_UP)){
6822 this._adjacent(false)._buttonWidget._onTitleClick();
6823 event.stop(e);
6824 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
6825 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
6826 this._adjacent(true)._buttonWidget._onTitleClick();
6827 event.stop(e);
6828 }
6829 }
6830 });
6831
6832 // Back compat w/1.6, remove for 2.0
6833 if(has("dijit-legacy-requires")){
6834 ready(0, function(){
6835 var requires = ["dijit/layout/AccordionPane"];
6836 require(requires); // use indirection so modules not rolled into a build
6837 });
6838 }
6839
6840 // For monkey patching
6841 AccordionContainer._InnerContainer = AccordionInnerContainer;
6842 AccordionContainer._Button = AccordionButton;
6843
6844 return AccordionContainer;
6845 });
6846
6847 },
6848 'dijit/form/_AutoCompleterMixin':function(){
6849 define("dijit/form/_AutoCompleterMixin", [
6850 "dojo/data/util/filter", // patternToRegExp
6851 "dojo/_base/declare", // declare
6852 "dojo/dom-attr", // domAttr.get
6853 "dojo/_base/event", // event.stop
6854 "dojo/keys",
6855 "dojo/_base/lang", // lang.clone lang.hitch
6856 "dojo/query", // query
6857 "dojo/regexp", // regexp.escapeString
6858 "dojo/sniff", // has("ie")
6859 "dojo/string", // string.substitute
6860 "./DataList",
6861 "../registry", // registry.byId
6862 "./_TextBoxMixin", // defines _TextBoxMixin.selectInputText
6863 "./_SearchMixin"
6864 ], function(filter, declare, domAttr, event, keys, lang, query, regexp, has, string,
6865 DataList, registry, _TextBoxMixin, SearchMixin){
6866
6867 // module:
6868 // dijit/form/_AutoCompleterMixin
6869
6870 return declare("dijit.form._AutoCompleterMixin", SearchMixin, {
6871 // summary:
6872 // A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect`
6873 // description:
6874 // All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`.
6875 // tags:
6876 // protected
6877
6878 // item: Object
6879 // This is the item returned by the dojo/store/api/Store implementation that
6880 // provides the data for this ComboBox, it's the currently selected item.
6881 item: null,
6882
6883 // autoComplete: Boolean
6884 // If user types in a partial string, and then tab out of the `<input>` box,
6885 // automatically copy the first entry displayed in the drop down list to
6886 // the `<input>` field
6887 autoComplete: true,
6888
6889 // highlightMatch: String
6890 // One of: "first", "all" or "none".
6891 //
6892 // If the ComboBox/FilteringSelect opens with the search results and the searched
6893 // string can be found, it will be highlighted. If set to "all"
6894 // then will probably want to change `queryExpr` parameter to '*${0}*'
6895 //
6896 // Highlighting is only performed when `labelType` is "text", so as to not
6897 // interfere with any HTML markup an HTML label might contain.
6898 highlightMatch: "first",
6899
6900 // labelAttr: String?
6901 // The entries in the drop down list come from this attribute in the
6902 // dojo.data items.
6903 // If not specified, the searchAttr attribute is used instead.
6904 labelAttr: "",
6905
6906 // labelType: String
6907 // Specifies how to interpret the labelAttr in the data store items.
6908 // Can be "html" or "text".
6909 labelType: "text",
6910
6911 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
6912 maxHeight: -1,
6913
6914 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
6915 _stopClickEvents: false,
6916
6917 _getCaretPos: function(/*DomNode*/ element){
6918 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
6919 var pos = 0;
6920 if(typeof(element.selectionStart) == "number"){
6921 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
6922 pos = element.selectionStart;
6923 }else if(has("ie")){
6924 // in the case of a mouse click in a popup being handled,
6925 // then the win.doc.selection is not the textarea, but the popup
6926 // var r = win.doc.selection.createRange();
6927 // hack to get IE 6 to play nice. What a POS browser.
6928 var tr = element.ownerDocument.selection.createRange().duplicate();
6929 var ntr = element.createTextRange();
6930 tr.move("character",0);
6931 ntr.move("character",0);
6932 try{
6933 // If control doesn't have focus, you get an exception.
6934 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
6935 // There appears to be no workaround for this - googled for quite a while.
6936 ntr.setEndPoint("EndToEnd", tr);
6937 pos = String(ntr.text).replace(/\r/g,"").length;
6938 }catch(e){
6939 // If focus has shifted, 0 is fine for caret pos.
6940 }
6941 }
6942 return pos;
6943 },
6944
6945 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
6946 location = parseInt(location);
6947 _TextBoxMixin.selectInputText(element, location, location);
6948 },
6949
6950 _setDisabledAttr: function(/*Boolean*/ value){
6951 // Additional code to set disabled state of ComboBox node.
6952 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
6953 this.inherited(arguments);
6954 this.domNode.setAttribute("aria-disabled", value ? "true" : "false");
6955 },
6956
6957 _onKey: function(/*Event*/ evt){
6958 // summary:
6959 // Handles keyboard events
6960
6961 if(evt.charCode >= 32){ return; } // alphanumeric reserved for searching
6962
6963 var key = evt.charCode || evt.keyCode;
6964
6965 // except for cutting/pasting case - ctrl + x/v
6966 if(key == keys.ALT || key == keys.CTRL || key == keys.META || key == keys.SHIFT){
6967 return; // throw out spurious events
6968 }
6969
6970 var pw = this.dropDown;
6971 var highlighted = null;
6972 this._abortQuery();
6973
6974 // _HasDropDown will do some of the work:
6975 //
6976 // 1. when drop down is not yet shown:
6977 // - if user presses the down arrow key, call loadDropDown()
6978 // 2. when drop down is already displayed:
6979 // - on ESC key, call closeDropDown()
6980 // - otherwise, call dropDown.handleKey() to process the keystroke
6981 this.inherited(arguments);
6982
6983 if(evt.altKey || evt.ctrlKey || evt.metaKey){ return; } // don't process keys with modifiers - but we want shift+TAB
6984
6985 if(this._opened){
6986 highlighted = pw.getHighlightedOption();
6987 }
6988 switch(key){
6989 case keys.PAGE_DOWN:
6990 case keys.DOWN_ARROW:
6991 case keys.PAGE_UP:
6992 case keys.UP_ARROW:
6993 // Keystroke caused ComboBox_menu to move to a different item.
6994 // Copy new item to <input> box.
6995 if(this._opened){
6996 this._announceOption(highlighted);
6997 }
6998 event.stop(evt);
6999 break;
7000
7001 case keys.ENTER:
7002 // prevent submitting form if user presses enter. Also
7003 // prevent accepting the value if either Next or Previous
7004 // are selected
7005 if(highlighted){
7006 // only stop event on prev/next
7007 if(highlighted == pw.nextButton){
7008 this._nextSearch(1);
7009 event.stop(evt); // prevent submit
7010 break;
7011 }else if(highlighted == pw.previousButton){
7012 this._nextSearch(-1);
7013 event.stop(evt); // prevent submit
7014 break;
7015 }
7016 event.stop(evt); // prevent submit if ENTER was to choose an item
7017 }else{
7018 // Update 'value' (ex: KY) according to currently displayed text
7019 this._setBlurValue(); // set value if needed
7020 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
7021 }
7022 // fall through
7023
7024 case keys.TAB:
7025 var newvalue = this.get('displayedValue');
7026 // if the user had More Choices selected fall into the
7027 // _onBlur handler
7028 if(pw && (
7029 newvalue == pw._messages["previousMessage"] ||
7030 newvalue == pw._messages["nextMessage"])
7031 ){
7032 break;
7033 }
7034 if(highlighted){
7035 this._selectOption(highlighted);
7036 }
7037 // fall through
7038
7039 case keys.ESCAPE:
7040 if(this._opened){
7041 this._lastQuery = null; // in case results come back later
7042 this.closeDropDown();
7043 }
7044 break;
7045 }
7046 },
7047
7048 _autoCompleteText: function(/*String*/ text){
7049 // summary:
7050 // Fill in the textbox with the first item from the drop down
7051 // list, and highlight the characters that were
7052 // auto-completed. For example, if user typed "CA" and the
7053 // drop down list appeared, the textbox would be changed to
7054 // "California" and "ifornia" would be highlighted.
7055
7056 var fn = this.focusNode;
7057
7058 // IE7: clear selection so next highlight works all the time
7059 _TextBoxMixin.selectInputText(fn, fn.value.length);
7060 // does text autoComplete the value in the textbox?
7061 var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
7062 if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
7063 var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length;
7064 // only try to extend if we added the last character at the end of the input
7065 if((cpos+1) > fn.value.length){
7066 // only add to input node as we would overwrite Capitalisation of chars
7067 // actually, that is ok
7068 fn.value = text;//.substr(cpos);
7069 // visually highlight the autocompleted characters
7070 _TextBoxMixin.selectInputText(fn, cpos);
7071 }
7072 }else{
7073 // text does not autoComplete; replace the whole value and highlight
7074 fn.value = text;
7075 _TextBoxMixin.selectInputText(fn);
7076 }
7077 },
7078
7079 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
7080 // summary:
7081 // Callback when a search completes.
7082 // description:
7083 // 1. generates drop-down list and calls _showResultList() to display it
7084 // 2. if this result list is from user pressing "more choices"/"previous choices"
7085 // then tell screen reader to announce new option
7086 var wasSelected = this.dropDown.getHighlightedOption();
7087 this.dropDown.clearResultList();
7088 if(!results.length && options.start == 0){ // if no results and not just the previous choices button
7089 this.closeDropDown();
7090 return;
7091 }
7092 this._nextSearch = this.dropDown.onPage = lang.hitch(this, function(direction){
7093 results.nextPage(direction !== -1);
7094 this.focus();
7095 });
7096
7097 // Fill in the textbox with the first item from the drop down list,
7098 // and highlight the characters that were auto-completed. For
7099 // example, if user typed "CA" and the drop down list appeared, the
7100 // textbox would be changed to "California" and "ifornia" would be
7101 // highlighted.
7102
7103 this.dropDown.createOptions(
7104 results,
7105 options,
7106 lang.hitch(this, "_getMenuLabelFromItem")
7107 );
7108
7109 // show our list (only if we have content, else nothing)
7110 this._showResultList();
7111
7112 // #4091:
7113 // tell the screen reader that the paging callback finished by
7114 // shouting the next choice
7115 if("direction" in options){
7116 if(options.direction){
7117 this.dropDown.highlightFirstOption();
7118 }else if(!options.direction){
7119 this.dropDown.highlightLastOption();
7120 }
7121 if(wasSelected){
7122 this._announceOption(this.dropDown.getHighlightedOption());
7123 }
7124 }else if(this.autoComplete && !this._prev_key_backspace
7125 // when the user clicks the arrow button to show the full list,
7126 // startSearch looks for "*".
7127 // it does not make sense to autocomplete
7128 // if they are just previewing the options available.
7129 && !/^[*]+$/.test(query[this.searchAttr].toString())){
7130 this._announceOption(this.dropDown.containerNode.firstChild.nextSibling); // 1st real item
7131 }
7132 },
7133
7134 _showResultList: function(){
7135 // summary:
7136 // Display the drop down if not already displayed, or if it is displayed, then
7137 // reposition it if necessary (reposition may be necessary if drop down's height changed).
7138 this.closeDropDown(true);
7139 this.openDropDown();
7140 this.domNode.setAttribute("aria-expanded", "true");
7141 },
7142
7143 loadDropDown: function(/*Function*/ /*===== callback =====*/){
7144 // Overrides _HasDropDown.loadDropDown().
7145 // This is called when user has pressed button icon or pressed the down arrow key
7146 // to open the drop down.
7147 this._startSearchAll();
7148 },
7149
7150 isLoaded: function(){
7151 // signal to _HasDropDown that it needs to call loadDropDown() to load the
7152 // drop down asynchronously before displaying it
7153 return false;
7154 },
7155
7156 closeDropDown: function(){
7157 // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
7158 // This method is the callback when the user types ESC or clicking
7159 // the button icon while the drop down is open. It's also called by other code.
7160 this._abortQuery();
7161 if(this._opened){
7162 this.inherited(arguments);
7163 this.domNode.setAttribute("aria-expanded", "false");
7164 this.focusNode.removeAttribute("aria-activedescendant");
7165 }
7166 },
7167
7168 _setBlurValue: function(){
7169 // if the user clicks away from the textbox OR tabs away, set the
7170 // value to the textbox value
7171 // #4617:
7172 // if value is now more choices or previous choices, revert
7173 // the value
7174 var newvalue = this.get('displayedValue');
7175 var pw = this.dropDown;
7176 if(pw && (
7177 newvalue == pw._messages["previousMessage"] ||
7178 newvalue == pw._messages["nextMessage"]
7179 )
7180 ){
7181 this._setValueAttr(this._lastValueReported, true);
7182 }else if(typeof this.item == "undefined"){
7183 // Update 'value' (ex: KY) according to currently displayed text
7184 this.item = null;
7185 this.set('displayedValue', newvalue);
7186 }else{
7187 if(this.value != this._lastValueReported){
7188 this._handleOnChange(this.value, true);
7189 }
7190 this._refreshState();
7191 }
7192 },
7193
7194 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
7195 // summary:
7196 // Set the displayed valued in the input box, and the hidden value
7197 // that gets submitted, based on a dojo.data store item.
7198 // description:
7199 // Users shouldn't call this function; they should be calling
7200 // set('item', value)
7201 // tags:
7202 // private
7203 var value = '';
7204 if(item){
7205 if(!displayedValue){
7206 displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
7207 this.store.getValue(item, this.searchAttr) : item[this.searchAttr];
7208 }
7209 value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue;
7210 }
7211 this.set('value', value, priorityChange, displayedValue, item);
7212 },
7213
7214 _announceOption: function(/*Node*/ node){
7215 // summary:
7216 // a11y code that puts the highlighted option in the textbox.
7217 // This way screen readers will know what is happening in the
7218 // menu.
7219
7220 if(!node){
7221 return;
7222 }
7223 // pull the text value from the item attached to the DOM node
7224 var newValue;
7225 if(node == this.dropDown.nextButton ||
7226 node == this.dropDown.previousButton){
7227 newValue = node.innerHTML;
7228 this.item = undefined;
7229 this.value = '';
7230 }else{
7231 var item = this.dropDown.items[node.getAttribute("item")];
7232 newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
7233 this.store.getValue(item, this.searchAttr) : item[this.searchAttr]).toString();
7234 this.set('item', item, false, newValue);
7235 }
7236 // get the text that the user manually entered (cut off autocompleted text)
7237 this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
7238 // set up ARIA activedescendant
7239 this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
7240 // autocomplete the rest of the option to announce change
7241 this._autoCompleteText(newValue);
7242 },
7243
7244 _selectOption: function(/*DomNode*/ target){
7245 // summary:
7246 // Menu callback function, called when an item in the menu is selected.
7247 this.closeDropDown();
7248 if(target){
7249 this._announceOption(target);
7250 }
7251 this._setCaretPos(this.focusNode, this.focusNode.value.length);
7252 this._handleOnChange(this.value, true);
7253 },
7254
7255 _startSearchAll: function(){
7256 this._startSearch('');
7257 },
7258
7259 _startSearchFromInput: function(){
7260 this.item = undefined; // undefined means item needs to be set
7261 this.inherited(arguments);
7262 },
7263
7264 _startSearch: function(/*String*/ key){
7265 // summary:
7266 // Starts a search for elements matching key (key=="" means to return all items),
7267 // and calls _openResultList() when the search completes, to display the results.
7268 if(!this.dropDown){
7269 var popupId = this.id + "_popup",
7270 dropDownConstructor = lang.isString(this.dropDownClass) ?
7271 lang.getObject(this.dropDownClass, false) : this.dropDownClass;
7272 this.dropDown = new dropDownConstructor({
7273 onChange: lang.hitch(this, this._selectOption),
7274 id: popupId,
7275 dir: this.dir,
7276 textDir: this.textDir
7277 });
7278 this.focusNode.removeAttribute("aria-activedescendant");
7279 this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox
7280 }
7281 this._lastInput = key; // Store exactly what was entered by the user.
7282 this.inherited(arguments);
7283 },
7284
7285 _getValueField: function(){
7286 // summary:
7287 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
7288 // Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value.
7289 return this.searchAttr;
7290 },
7291
7292 //////////// INITIALIZATION METHODS ///////////////////////////////////////
7293
7294 postMixInProperties: function(){
7295 this.inherited(arguments);
7296 if(!this.store){
7297 var srcNodeRef = this.srcNodeRef;
7298 // if user didn't specify store, then assume there are option tags
7299 this.store = new DataList({}, srcNodeRef);
7300
7301 // if there is no value set and there is an option list, set
7302 // the value to the first value to be consistent with native Select
7303 // Firefox and Safari set value
7304 // IE6 and Opera set selectedIndex, which is automatically set
7305 // by the selected attribute of an option tag
7306 // IE6 does not set value, Opera sets value = selectedIndex
7307 if(!("value" in this.params)){
7308 var item = (this.item = this.store.fetchSelectedItem());
7309 if(item){
7310 var valueField = this._getValueField();
7311 // remove getValue() for 2.0 (old dojo.data API)
7312 this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField];
7313 }
7314 }
7315 }
7316 },
7317
7318 postCreate: function(){
7319 // summary:
7320 // Subclasses must call this method from their postCreate() methods
7321 // tags:
7322 // protected
7323
7324 // find any associated label element and add to ComboBox node.
7325 var label=query('label[for="'+this.id+'"]');
7326 if(label.length){
7327 if(!label[0].id){ label[0].id = this.id + "_label"; }
7328 this.domNode.setAttribute("aria-labelledby", label[0].id);
7329
7330 }
7331 this.inherited(arguments);
7332 this.connect(this, "onSearch", "_openResultList");
7333 },
7334
7335 _getMenuLabelFromItem: function(/*Item*/ item){
7336 var label = this.labelFunc(item, this.store),
7337 labelType = this.labelType;
7338 // If labelType is not "text" we don't want to screw any markup ot whatever.
7339 if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
7340 label = this.doHighlight(label, this._lastInput);
7341 labelType = "html";
7342 }
7343 return {html: labelType == "html", label: label};
7344 },
7345
7346 doHighlight: function(/*String*/ label, /*String*/ find){
7347 // summary:
7348 // Highlights the string entered by the user in the menu. By default this
7349 // highlights the first occurrence found. Override this method
7350 // to implement your custom highlighting.
7351 // tags:
7352 // protected
7353
7354 var
7355 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
7356 modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
7357 i = this.queryExpr.indexOf("${0}");
7358 find = regexp.escapeString(find); // escape regexp special chars
7359 //If < appears in label, and user presses t, we don't want to highlight the t in the escaped "&lt;"
7360 //first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which
7361 //should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the
7362 //HTML highlight markup.
7363 return this._escapeHtml(label.replace(
7364 new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
7365 '\uFFFF$1\uFFFF')).replace(
7366 /\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>'
7367 ); // returns String, (almost) valid HTML (entities encoded)
7368 },
7369
7370 _escapeHtml: function(/*String*/ str){
7371 // TODO Should become dojo.html.entities(), when exists use instead
7372 // summary:
7373 // Adds escape sequences for special characters in XML: `&<>"'`
7374 str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
7375 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); //balance"
7376 return str; // string
7377 },
7378
7379 reset: function(){
7380 // Overrides the _FormWidget.reset().
7381 // Additionally reset the .item (to clean up).
7382 this.item = null;
7383 this.inherited(arguments);
7384 },
7385
7386 labelFunc: function(item, store){
7387 // summary:
7388 // Computes the label to display based on the dojo.data store item.
7389 // item: Object
7390 // The item from the store
7391 // store: dojo/store/api/Store
7392 // The store.
7393 // returns:
7394 // The label that the ComboBox should display
7395 // tags:
7396 // private
7397
7398 // Use toString() because XMLStore returns an XMLItem whereas this
7399 // method is expected to return a String (#9354).
7400 // Remove getValue() for 2.0 (old dojo.data API)
7401 return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) :
7402 item[this.labelAttr || this.searchAttr]).toString(); // String
7403 },
7404
7405 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
7406 // summary:
7407 // Hook so set('value', value) works.
7408 // description:
7409 // Sets the value of the select.
7410 this._set("item", item||null); // value not looked up in store
7411 if(value == null /* or undefined */){ value = ''; } // null translates to blank
7412 this.inherited(arguments);
7413 },
7414 _setTextDirAttr: function(/*String*/ textDir){
7415 // summary:
7416 // Setter for textDir, needed for the dropDown's textDir update.
7417 // description:
7418 // Users shouldn't call this function; they should be calling
7419 // set('textDir', value)
7420 // tags:
7421 // private
7422 this.inherited(arguments);
7423 // update the drop down also (_ComboBoxMenuMixin)
7424 if(this.dropDown){
7425 this.dropDown._set("textDir", textDir);
7426 }
7427 }
7428 });
7429 });
7430
7431 },
7432 '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",
7433 '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>",
7434 'dijit/form/MappedTextBox':function(){
7435 define("dijit/form/MappedTextBox", [
7436 "dojo/_base/declare", // declare
7437 "dojo/dom-construct", // domConstruct.place
7438 "./ValidationTextBox"
7439 ], function(declare, domConstruct, ValidationTextBox){
7440
7441 // module:
7442 // dijit/form/MappedTextBox
7443
7444 return declare("dijit.form.MappedTextBox", ValidationTextBox, {
7445 // summary:
7446 // A dijit/form/ValidationTextBox subclass which provides a base class for widgets that have
7447 // a visible formatted display value, and a serializable
7448 // value in a hidden input field which is actually sent to the server.
7449 // description:
7450 // The visible display may
7451 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
7452 // input field which uses the `name` attribute declared by the original widget. That value sent
7453 // to the server is defined by the dijit/form/MappedTextBox.serialize() method and is typically
7454 // locale-neutral.
7455 // tags:
7456 // protected
7457
7458 postMixInProperties: function(){
7459 this.inherited(arguments);
7460
7461 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
7462 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
7463 this.nameAttrSetting = "";
7464 },
7465
7466 // Override default behavior to assign name to focusNode
7467 _setNameAttr: null,
7468
7469 serialize: function(val /*=====, options =====*/){
7470 // summary:
7471 // Overridable function used to convert the get('value') result to a canonical
7472 // (non-localized) string. For example, will print dates in ISO format, and
7473 // numbers the same way as they are represented in javascript.
7474 // val: anything
7475 // options: Object?
7476 // tags:
7477 // protected extension
7478 return val.toString ? val.toString() : ""; // String
7479 },
7480
7481 toString: function(){
7482 // summary:
7483 // Returns widget as a printable string using the widget's value
7484 // tags:
7485 // protected
7486 var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
7487 return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
7488 },
7489
7490 validate: function(){
7491 // Overrides `dijit/form/TextBox.validate`
7492 this.valueNode.value = this.toString();
7493 return this.inherited(arguments);
7494 },
7495
7496 buildRendering: function(){
7497 // Overrides `dijit/_TemplatedMixin/buildRendering`
7498
7499 this.inherited(arguments);
7500
7501 // Create a hidden <input> node with the serialized value used for submit
7502 // (as opposed to the displayed value).
7503 // Passing in name as markup rather than calling domConstruct.create() with an attrs argument
7504 // to make query(input[name=...]) work on IE. (see #8660)
7505 this.valueNode = domConstruct.place("<input type='hidden'" + (this.name ? ' name="' + this.name.replace(/"/g, "&quot;") + '"' : "") + "/>", this.textbox, "after");
7506 },
7507
7508 reset: function(){
7509 // Overrides `dijit/form/ValidationTextBox.reset` to
7510 // reset the hidden textbox value to ''
7511 this.valueNode.value = '';
7512 this.inherited(arguments);
7513 }
7514 });
7515 });
7516
7517 },
7518 'dijit/form/ComboBoxMixin':function(){
7519 require({cache:{
7520 '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"}});
7521 define("dijit/form/ComboBoxMixin", [
7522 "dojo/_base/declare", // declare
7523 "dojo/_base/Deferred",
7524 "dojo/_base/kernel", // kernel.deprecated
7525 "dojo/_base/lang", // lang.mixin
7526 "dojo/store/util/QueryResults",
7527 "./_AutoCompleterMixin",
7528 "./_ComboBoxMenu",
7529 "../_HasDropDown",
7530 "dojo/text!./templates/DropDownBox.html"
7531 ], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
7532
7533
7534 // module:
7535 // dijit/form/ComboBoxMixin
7536
7537 return declare("dijit.form.ComboBoxMixin", [_HasDropDown, _AutoCompleterMixin], {
7538 // summary:
7539 // Provides main functionality of ComboBox widget
7540
7541 // dropDownClass: [protected extension] Function String
7542 // Dropdown widget class used to select a date/time.
7543 // Subclasses should specify this.
7544 dropDownClass: _ComboBoxMenu,
7545
7546 // hasDownArrow: Boolean
7547 // Set this textbox to have a down arrow button, to display the drop down list.
7548 // Defaults to true.
7549 hasDownArrow: true,
7550
7551 templateString: template,
7552
7553 baseClass: "dijitTextBox dijitComboBox",
7554
7555 /*=====
7556 // store: [const] dojo/store/api/Store|dojo/data/api/Read
7557 // Reference to data provider object used by this ComboBox.
7558 //
7559 // Should be dojo/store/api/Store, but dojo/data/api/Read supported
7560 // for backwards compatibility.
7561 store: null,
7562 =====*/
7563
7564 // Set classes like dijitDownArrowButtonHover depending on
7565 // mouse action over button node
7566 cssStateNodes: {
7567 "_buttonNode": "dijitDownArrowButton"
7568 },
7569
7570 _setHasDownArrowAttr: function(/*Boolean*/ val){
7571 this._set("hasDownArrow", val);
7572 this._buttonNode.style.display = val ? "" : "none";
7573 },
7574
7575 _showResultList: function(){
7576 // hide the tooltip
7577 this.displayMessage("");
7578 this.inherited(arguments);
7579 },
7580
7581 _setStoreAttr: function(store){
7582 // For backwards-compatibility, accept dojo.data store in addition to dojo/store/api/Store. Remove in 2.0.
7583 if(!store.get){
7584 lang.mixin(store, {
7585 _oldAPI: true,
7586 get: function(id){
7587 // summary:
7588 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
7589 // Like dojo/store/DataStore.get() except returns native item.
7590 var deferred = new Deferred();
7591 this.fetchItemByIdentity({
7592 identity: id,
7593 onItem: function(object){
7594 deferred.resolve(object);
7595 },
7596 onError: function(error){
7597 deferred.reject(error);
7598 }
7599 });
7600 return deferred.promise;
7601 },
7602 query: function(query, options){
7603 // summary:
7604 // Queries the store for objects. Like dojo/store/DataStore.query()
7605 // except returned Deferred contains array of native items.
7606 var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
7607 deferred.total = new Deferred();
7608 var fetchHandle = this.fetch(lang.mixin({
7609 query: query,
7610 onBegin: function(count){
7611 deferred.total.resolve(count);
7612 },
7613 onComplete: function(results){
7614 deferred.resolve(results);
7615 },
7616 onError: function(error){
7617 deferred.reject(error);
7618 }
7619 }, options));
7620 return QueryResults(deferred);
7621 }
7622 });
7623 }
7624 this._set("store", store);
7625 },
7626
7627 postMixInProperties: function(){
7628 // Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
7629 // Unfortunately, without special code, it ends up executing second.
7630 var store = this.params.store || this.store;
7631 if(store){
7632 this._setStoreAttr(store);
7633 }
7634
7635 this.inherited(arguments);
7636
7637 // User may try to access this.store.getValue() etc. in a custom labelFunc() function.
7638 // It's not available with the new data store for handling inline <option> tags, so add it.
7639 if(!this.params.store && !this.store._oldAPI){
7640 var clazz = this.declaredClass;
7641 lang.mixin(this.store, {
7642 getValue: function(item, attr){
7643 kernel.deprecated(clazz + ".store.getValue(item, attr) is deprecated for builtin store. Use item.attr directly", "", "2.0");
7644 return item[attr];
7645 },
7646 getLabel: function(item){
7647 kernel.deprecated(clazz + ".store.getLabel(item) is deprecated for builtin store. Use item.label directly", "", "2.0");
7648 return item.name;
7649 },
7650 fetch: function(args){
7651 kernel.deprecated(clazz + ".store.fetch() is deprecated for builtin store.", "Use store.query()", "2.0");
7652 var shim = ["dojo/data/ObjectStore"]; // indirection so it doesn't get rolled into a build
7653 require(shim, lang.hitch(this, function(ObjectStore){
7654 new ObjectStore({objectStore: this}).fetch(args);
7655 }));
7656 }
7657 });
7658 }
7659 }
7660 });
7661 });
7662
7663 },
7664 'dijit/form/_TextBoxMixin':function(){
7665 define("dijit/form/_TextBoxMixin", [
7666 "dojo/_base/array", // array.forEach
7667 "dojo/_base/declare", // declare
7668 "dojo/dom", // dom.byId
7669 "dojo/_base/event", // event.stop
7670 "dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
7671 "dojo/_base/lang", // lang.mixin
7672 "dojo/on", // on
7673 "../main" // for exporting dijit._setSelectionRange, dijit.selectInputText
7674 ], function(array, declare, dom, event, keys, lang, on, dijit){
7675
7676 // module:
7677 // dijit/form/_TextBoxMixin
7678
7679 var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
7680 // summary:
7681 // A mixin for textbox form input widgets
7682
7683 // trim: Boolean
7684 // Removes leading and trailing whitespace if true. Default is false.
7685 trim: false,
7686
7687 // uppercase: Boolean
7688 // Converts all characters to uppercase if true. Default is false.
7689 uppercase: false,
7690
7691 // lowercase: Boolean
7692 // Converts all characters to lowercase if true. Default is false.
7693 lowercase: false,
7694
7695 // propercase: Boolean
7696 // Converts the first character of each word to uppercase if true.
7697 propercase: false,
7698
7699 // maxLength: String
7700 // HTML INPUT tag maxLength declaration.
7701 maxLength: "",
7702
7703 // selectOnClick: [const] Boolean
7704 // If true, all text will be selected when focused with mouse
7705 selectOnClick: false,
7706
7707 // placeHolder: String
7708 // Defines a hint to help users fill out the input field (as defined in HTML 5).
7709 // This should only contain plain text (no html markup).
7710 placeHolder: "",
7711
7712 _getValueAttr: function(){
7713 // summary:
7714 // Hook so get('value') works as we like.
7715 // description:
7716 // For `dijit/form/TextBox` this basically returns the value of the `<input>`.
7717 //
7718 // For `dijit/form/MappedTextBox` subclasses, which have both
7719 // a "displayed value" and a separate "submit value",
7720 // This treats the "displayed value" as the master value, computing the
7721 // submit value from it via this.parse().
7722 return this.parse(this.get('displayedValue'), this.constraints);
7723 },
7724
7725 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
7726 // summary:
7727 // Hook so set('value', ...) works.
7728 //
7729 // description:
7730 // Sets the value of the widget to "value" which can be of
7731 // any type as determined by the widget.
7732 //
7733 // value:
7734 // The visual element value is also set to a corresponding,
7735 // but not necessarily the same, value.
7736 //
7737 // formattedValue:
7738 // If specified, used to set the visual element value,
7739 // otherwise a computed visual value is used.
7740 //
7741 // priorityChange:
7742 // If true, an onChange event is fired immediately instead of
7743 // waiting for the next blur event.
7744
7745 var filteredValue;
7746 if(value !== undefined){
7747 // TODO: this is calling filter() on both the display value and the actual value.
7748 // I added a comment to the filter() definition about this, but it should be changed.
7749 filteredValue = this.filter(value);
7750 if(typeof formattedValue != "string"){
7751 if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
7752 formattedValue = this.filter(this.format(filteredValue, this.constraints));
7753 }else{ formattedValue = ''; }
7754 }
7755 }
7756 if(formattedValue != null /* and !undefined */ && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
7757 this.textbox.value = formattedValue;
7758 this._set("displayedValue", this.get("displayedValue"));
7759 }
7760
7761 if(this.textDir == "auto"){
7762 this.applyTextDir(this.focusNode, formattedValue);
7763 }
7764
7765 this.inherited(arguments, [filteredValue, priorityChange]);
7766 },
7767
7768 // displayedValue: String
7769 // For subclasses like ComboBox where the displayed value
7770 // (ex: Kentucky) and the serialized value (ex: KY) are different,
7771 // this represents the displayed value.
7772 //
7773 // Setting 'displayedValue' through set('displayedValue', ...)
7774 // updates 'value', and vice-versa. Otherwise 'value' is updated
7775 // from 'displayedValue' periodically, like onBlur etc.
7776 //
7777 // TODO: move declaration to MappedTextBox?
7778 // Problem is that ComboBox references displayedValue,
7779 // for benefit of FilteringSelect.
7780 displayedValue: "",
7781
7782 _getDisplayedValueAttr: function(){
7783 // summary:
7784 // Hook so get('displayedValue') works.
7785 // description:
7786 // Returns the displayed value (what the user sees on the screen),
7787 // after filtering (ie, trimming spaces etc.).
7788 //
7789 // For some subclasses of TextBox (like ComboBox), the displayed value
7790 // is different from the serialized value that's actually
7791 // sent to the server (see `dijit/form/ValidationTextBox.serialize()`)
7792
7793 // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
7794 // this method
7795 // TODO: this isn't really the displayed value when the user is typing
7796 return this.filter(this.textbox.value);
7797 },
7798
7799 _setDisplayedValueAttr: function(/*String*/ value){
7800 // summary:
7801 // Hook so set('displayedValue', ...) works.
7802 // description:
7803 // Sets the value of the visual element to the string "value".
7804 // The widget value is also set to a corresponding,
7805 // but not necessarily the same, value.
7806
7807 if(value == null /* or undefined */){ value = '' }
7808 else if(typeof value != "string"){ value = String(value) }
7809
7810 this.textbox.value = value;
7811
7812 // sets the serialized value to something corresponding to specified displayedValue
7813 // (if possible), and also updates the textbox.value, for example converting "123"
7814 // to "123.00"
7815 this._setValueAttr(this.get('value'), undefined);
7816
7817 this._set("displayedValue", this.get('displayedValue'));
7818
7819 // textDir support
7820 if(this.textDir == "auto"){
7821 this.applyTextDir(this.focusNode, value);
7822 }
7823 },
7824
7825 format: function(value /*=====, constraints =====*/){
7826 // summary:
7827 // Replaceable function to convert a value to a properly formatted string.
7828 // value: String
7829 // constraints: Object
7830 // tags:
7831 // protected extension
7832 return value == null /* or undefined */ ? "" : (value.toString ? value.toString() : value);
7833 },
7834
7835 parse: function(value /*=====, constraints =====*/){
7836 // summary:
7837 // Replaceable function to convert a formatted string to a value
7838 // value: String
7839 // constraints: Object
7840 // tags:
7841 // protected extension
7842
7843 return value; // String
7844 },
7845
7846 _refreshState: function(){
7847 // summary:
7848 // After the user types some characters, etc., this method is
7849 // called to check the field for validity etc. The base method
7850 // in `dijit/form/TextBox` does nothing, but subclasses override.
7851 // tags:
7852 // protected
7853 },
7854
7855 /*=====
7856 onInput: function(event){
7857 // summary:
7858 // Connect to this function to receive notifications of various user data-input events.
7859 // Return false to cancel the event and prevent it from being processed.
7860 // event:
7861 // keydown | keypress | cut | paste | input
7862 // tags:
7863 // callback
7864 },
7865 =====*/
7866 onInput: function(){},
7867
7868 __skipInputEvent: false,
7869 _onInput: function(/*Event*/ evt){
7870 // summary:
7871 // Called AFTER the input event has happened
7872
7873 // set text direction according to textDir that was defined in creation
7874 if(this.textDir == "auto"){
7875 this.applyTextDir(this.focusNode, this.focusNode.value);
7876 }
7877
7878 this._processInput(evt);
7879 },
7880
7881 _processInput: function(/*Event*/ evt){
7882 // summary:
7883 // Default action handler for user input events
7884
7885 this._refreshState();
7886
7887 // In case someone is watch()'ing for changes to displayedValue
7888 this._set("displayedValue", this.get("displayedValue"));
7889 },
7890
7891 postCreate: function(){
7892 // setting the value here is needed since value="" in the template causes "undefined"
7893 // and setting in the DOM (instead of the JS object) helps with form reset actions
7894 this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
7895
7896 this.inherited(arguments);
7897
7898 // normalize input events to reduce spurious event processing
7899 // onkeydown: do not forward modifier keys
7900 // set charOrCode to numeric keycode
7901 // onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
7902 // onpaste & oncut: set charOrCode to 229 (IME)
7903 // oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
7904 var handleEvent = function(e){
7905 var charOrCode;
7906 if(e.type == "keydown"){
7907 charOrCode = e.keyCode;
7908 switch(charOrCode){ // ignore state keys
7909 case keys.SHIFT:
7910 case keys.ALT:
7911 case keys.CTRL:
7912 case keys.META:
7913 case keys.CAPS_LOCK:
7914 case keys.NUM_LOCK:
7915 case keys.SCROLL_LOCK:
7916 return;
7917 }
7918 if(!e.ctrlKey && !e.metaKey && !e.altKey){ // no modifiers
7919 switch(charOrCode){ // ignore location keys
7920 case keys.NUMPAD_0:
7921 case keys.NUMPAD_1:
7922 case keys.NUMPAD_2:
7923 case keys.NUMPAD_3:
7924 case keys.NUMPAD_4:
7925 case keys.NUMPAD_5:
7926 case keys.NUMPAD_6:
7927 case keys.NUMPAD_7:
7928 case keys.NUMPAD_8:
7929 case keys.NUMPAD_9:
7930 case keys.NUMPAD_MULTIPLY:
7931 case keys.NUMPAD_PLUS:
7932 case keys.NUMPAD_ENTER:
7933 case keys.NUMPAD_MINUS:
7934 case keys.NUMPAD_PERIOD:
7935 case keys.NUMPAD_DIVIDE:
7936 return;
7937 }
7938 if((charOrCode >= 65 && charOrCode <= 90) || (charOrCode >= 48 && charOrCode <= 57) || charOrCode == keys.SPACE){
7939 return; // keypress will handle simple non-modified printable keys
7940 }
7941 var named = false;
7942 for(var i in keys){
7943 if(keys[i] === e.keyCode){
7944 named = true;
7945 break;
7946 }
7947 }
7948 if(!named){ return; } // only allow named ones through
7949 }
7950 }
7951 charOrCode = e.charCode >= 32 ? String.fromCharCode(e.charCode) : e.charCode;
7952 if(!charOrCode){
7953 charOrCode = (e.keyCode >= 65 && e.keyCode <= 90) || (e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode == keys.SPACE ? String.fromCharCode(e.keyCode) : e.keyCode;
7954 }
7955 if(!charOrCode){
7956 charOrCode = 229; // IME
7957 }
7958 if(e.type == "keypress"){
7959 if(typeof charOrCode != "string"){ return; }
7960 if((charOrCode >= 'a' && charOrCode <= 'z') || (charOrCode >= 'A' && charOrCode <= 'Z') || (charOrCode >= '0' && charOrCode <= '9') || (charOrCode === ' ')){
7961 if(e.ctrlKey || e.metaKey || e.altKey){ return; } // can only be stopped reliably in keydown
7962 }
7963 }
7964 if(e.type == "input"){
7965 if(this.__skipInputEvent){ // duplicate event
7966 this.__skipInputEvent = false;
7967 return;
7968 }
7969 }else{
7970 this.__skipInputEvent = true;
7971 }
7972 // create fake event to set charOrCode and to know if preventDefault() was called
7973 var faux = { faux: true }, attr;
7974 for(attr in e){
7975 if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
7976 var v = e[attr];
7977 if(typeof v != "function" && typeof v != "undefined"){ faux[attr] = v; }
7978 }
7979 }
7980 lang.mixin(faux, {
7981 charOrCode: charOrCode,
7982 _wasConsumed: false,
7983 preventDefault: function(){
7984 faux._wasConsumed = true;
7985 e.preventDefault();
7986 },
7987 stopPropagation: function(){ e.stopPropagation(); }
7988 });
7989 // give web page author a chance to consume the event
7990 //console.log(faux.type + ', charOrCode = (' + (typeof charOrCode) + ') ' + charOrCode + ', ctrl ' + !!faux.ctrlKey + ', alt ' + !!faux.altKey + ', meta ' + !!faux.metaKey + ', shift ' + !!faux.shiftKey);
7991 if(this.onInput(faux) === false){ // return false means stop
7992 faux.preventDefault();
7993 faux.stopPropagation();
7994 }
7995 if(faux._wasConsumed){ return; } // if preventDefault was called
7996 this.defer(function(){ this._onInput(faux); }); // widget notification after key has posted
7997 };
7998 this.own(on(this.textbox, "keydown, keypress, paste, cut, input, compositionend", lang.hitch(this, handleEvent)));
7999 },
8000
8001 _blankValue: '', // if the textbox is blank, what value should be reported
8002 filter: function(val){
8003 // summary:
8004 // Auto-corrections (such as trimming) that are applied to textbox
8005 // value on blur or form submit.
8006 // description:
8007 // For MappedTextBox subclasses, this is called twice
8008 //
8009 // - once with the display value
8010 // - once the value as set/returned by set('value', ...)
8011 //
8012 // and get('value'), ex: a Number for NumberTextBox.
8013 //
8014 // In the latter case it does corrections like converting null to NaN. In
8015 // the former case the NumberTextBox.filter() method calls this.inherited()
8016 // to execute standard trimming code in TextBox.filter().
8017 //
8018 // TODO: break this into two methods in 2.0
8019 //
8020 // tags:
8021 // protected extension
8022 if(val === null){ return this._blankValue; }
8023 if(typeof val != "string"){ return val; }
8024 if(this.trim){
8025 val = lang.trim(val);
8026 }
8027 if(this.uppercase){
8028 val = val.toUpperCase();
8029 }
8030 if(this.lowercase){
8031 val = val.toLowerCase();
8032 }
8033 if(this.propercase){
8034 val = val.replace(/[^\s]+/g, function(word){
8035 return word.substring(0,1).toUpperCase() + word.substring(1);
8036 });
8037 }
8038 return val;
8039 },
8040
8041 _setBlurValue: function(){
8042 this._setValueAttr(this.get('value'), true);
8043 },
8044
8045 _onBlur: function(e){
8046 if(this.disabled){ return; }
8047 this._setBlurValue();
8048 this.inherited(arguments);
8049 },
8050
8051 _isTextSelected: function(){
8052 return this.textbox.selectionStart != this.textbox.selectionEnd;
8053 },
8054
8055 _onFocus: function(/*String*/ by){
8056 if(this.disabled || this.readOnly){ return; }
8057
8058 // Select all text on focus via click if nothing already selected.
8059 // Since mouse-up will clear the selection, need to defer selection until after mouse-up.
8060 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
8061 if(this.selectOnClick && by == "mouse"){
8062 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
8063 // Only select all text on first click; otherwise users would have no way to clear
8064 // the selection.
8065 this.disconnect(this._selectOnClickHandle);
8066 this._selectOnClickHandle = null;
8067
8068 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
8069 // and if not, then select all the text
8070 if(!this._isTextSelected()){
8071 _TextBoxMixin.selectInputText(this.textbox);
8072 }
8073 });
8074 // in case the mouseup never comes
8075 this.defer(function(){
8076 if(this._selectOnClickHandle){
8077 this.disconnect(this._selectOnClickHandle);
8078 this._selectOnClickHandle = null;
8079 }
8080 }, 500); // if mouseup not received soon, then treat it as some gesture
8081 }
8082 // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
8083 // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
8084 this.inherited(arguments);
8085
8086 this._refreshState();
8087 },
8088
8089 reset: function(){
8090 // Overrides `dijit/_FormWidget/reset()`.
8091 // Additionally resets the displayed textbox value to ''
8092 this.textbox.value = '';
8093 this.inherited(arguments);
8094 },
8095
8096 _setTextDirAttr: function(/*String*/ textDir){
8097 // summary:
8098 // Setter for textDir.
8099 // description:
8100 // Users shouldn't call this function; they should be calling
8101 // set('textDir', value)
8102 // tags:
8103 // private
8104
8105 // only if new textDir is different from the old one
8106 // and on widgets creation.
8107 if(!this._created
8108 || this.textDir != textDir){
8109 this._set("textDir", textDir);
8110 // so the change of the textDir will take place immediately.
8111 this.applyTextDir(this.focusNode, this.focusNode.value);
8112 }
8113 }
8114 });
8115
8116
8117 _TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
8118 if(element.setSelectionRange){
8119 element.setSelectionRange(start, stop);
8120 }
8121 };
8122
8123 _TextBoxMixin.selectInputText = dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
8124 // summary:
8125 // Select text in the input element argument, from start (default 0), to stop (default end).
8126
8127 // TODO: use functions in _editor/selection.js?
8128 element = dom.byId(element);
8129 if(isNaN(start)){ start = 0; }
8130 if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
8131 try{
8132 element.focus();
8133 _TextBoxMixin._setSelectionRange(element, start, stop);
8134 }catch(e){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
8135 };
8136
8137 return _TextBoxMixin;
8138 });
8139
8140 },
8141 'dijit/form/SimpleTextarea':function(){
8142 define("dijit/form/SimpleTextarea", [
8143 "dojo/_base/declare", // declare
8144 "dojo/dom-class", // domClass.add
8145 "dojo/sniff", // has("ie") has("opera")
8146 "./TextBox"
8147 ], function(declare, domClass, has, TextBox){
8148
8149 // module:
8150 // dijit/form/SimpleTextarea
8151
8152
8153 return declare("dijit.form.SimpleTextarea", TextBox, {
8154 // summary:
8155 // A simple textarea that degrades, and responds to
8156 // minimal LayoutContainer usage, and works with dijit/form/Form.
8157 // Doesn't automatically size according to input, like Textarea.
8158 //
8159 // example:
8160 // | <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
8161 //
8162 // example:
8163 // | new SimpleTextarea({ rows:20, cols:30 }, "foo");
8164
8165 baseClass: "dijitTextBox dijitTextArea",
8166
8167 // rows: Number
8168 // The number of rows of text.
8169 rows: "3",
8170
8171 // rows: Number
8172 // The number of characters per line.
8173 cols: "20",
8174
8175 templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
8176
8177 postMixInProperties: function(){
8178 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
8179 // TODO: parser will handle this in 2.0
8180 if(!this.value && this.srcNodeRef){
8181 this.value = this.srcNodeRef.value;
8182 }
8183 this.inherited(arguments);
8184 },
8185
8186 buildRendering: function(){
8187 this.inherited(arguments);
8188 if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
8189 domClass.add(this.textbox, "dijitTextAreaCols");
8190 }
8191 },
8192
8193 filter: function(/*String*/ value){
8194 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
8195 // as \r\n instead of just \n
8196 if(value){
8197 value = value.replace(/\r/g,"");
8198 }
8199 return this.inherited(arguments);
8200 },
8201
8202 _onInput: function(/*Event?*/ e){
8203 // Override TextBox._onInput() to enforce maxLength restriction
8204 if(this.maxLength){
8205 var maxLength = parseInt(this.maxLength);
8206 var value = this.textbox.value.replace(/\r/g,'');
8207 var overflow = value.length - maxLength;
8208 if(overflow > 0){
8209 var textarea = this.textbox;
8210 if(textarea.selectionStart){
8211 var pos = textarea.selectionStart;
8212 var cr = 0;
8213 if(has("opera")){
8214 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
8215 }
8216 this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
8217 textarea.setSelectionRange(pos-overflow, pos-overflow);
8218 }else if(this.ownerDocument.selection){ //IE
8219 textarea.focus();
8220 var range = this.ownerDocument.selection.createRange();
8221 // delete overflow characters
8222 range.moveStart("character", -overflow);
8223 range.text = '';
8224 // show cursor
8225 range.select();
8226 }
8227 }
8228 }
8229 this.inherited(arguments);
8230 }
8231 });
8232
8233 });
8234
8235 },
8236 '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",
8237 'dijit/_base/window':function(){
8238 define("dijit/_base/window", [
8239 "dojo/window", // windowUtils.get
8240 "../main" // export symbol to dijit
8241 ], function(windowUtils, dijit){
8242 // module:
8243 // dijit/_base/window
8244
8245 /*=====
8246 return {
8247 // summary:
8248 // Back compatibility module, new code should use windowUtils directly instead of using this module.
8249 };
8250 =====*/
8251
8252 dijit.getDocumentWindow = function(doc){
8253 return windowUtils.get(doc);
8254 };
8255 });
8256
8257 },
8258 'dojo/hccss':function(){
8259 define("dojo/hccss", [
8260 "require", // require.toUrl
8261 "./_base/config", // config.blankGif
8262 "./dom-class", // domClass.add
8263 "./dom-style", // domStyle.getComputedStyle
8264 "./has",
8265 "./ready", // ready
8266 "./_base/window" // win.body
8267 ], function(require, config, domClass, domStyle, has, ready, win){
8268
8269 // module:
8270 // dojo/hccss
8271
8272 /*=====
8273 return function(){
8274 // summary:
8275 // Test if computer is in high contrast mode (i.e. if browser is not displaying background images).
8276 // Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode.
8277 // Returns `has()` method;
8278 };
8279 =====*/
8280
8281 // Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!.
8282 has.add("highcontrast", function(){
8283 // note: if multiple documents, doesn't matter which one we use
8284 var div = win.doc.createElement("div");
8285 div.style.cssText = "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" +
8286 "background-image: url(" + (config.blankGif || require.toUrl("./resources/blank.gif")) + ");";
8287 win.body().appendChild(div);
8288
8289 var cs = domStyle.getComputedStyle(div),
8290 bkImg = cs.backgroundImage,
8291 hc = (cs.borderTopColor == cs.borderRightColor) ||
8292 (bkImg && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
8293
8294 if(has("ie") <= 8){
8295 div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
8296 }else{
8297 win.body().removeChild(div);
8298 }
8299
8300 return hc;
8301 });
8302
8303 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
8304 // change this module to depend on dojo/domReady!
8305 ready(90, function(){
8306 if(has("highcontrast")){
8307 domClass.add(win.body(), "dj_a11y");
8308 }
8309 });
8310
8311 return has;
8312 });
8313
8314 },
8315 'dijit/form/RadioButton':function(){
8316 define("dijit/form/RadioButton", [
8317 "dojo/_base/declare", // declare
8318 "./CheckBox",
8319 "./_RadioButtonMixin"
8320 ], function(declare, CheckBox, _RadioButtonMixin){
8321
8322 // module:
8323 // dijit/form/RadioButton
8324
8325 return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
8326 // summary:
8327 // Same as an HTML radio, but with fancy styling.
8328
8329 baseClass: "dijitRadio"
8330 });
8331 });
8332
8333 },
8334 'dijit/main':function(){
8335 define("dijit/main", [
8336 "dojo/_base/kernel"
8337 ], function(dojo){
8338 // module:
8339 // dijit/main
8340
8341 /*=====
8342 return {
8343 // summary:
8344 // The dijit package main module.
8345 // Deprecated. Users should access individual modules (ex: dijit/registry) directly.
8346 };
8347 =====*/
8348
8349 return dojo.dijit;
8350 });
8351
8352 },
8353 'dijit/_OnDijitClickMixin':function(){
8354 define("dijit/_OnDijitClickMixin", [
8355 "dojo/on",
8356 "dojo/_base/array", // array.forEach
8357 "dojo/keys", // keys.ENTER keys.SPACE
8358 "dojo/_base/declare", // declare
8359 "dojo/has", // has("dom-addeventlistener")
8360 "dojo/_base/unload", // unload.addOnWindowUnload
8361 "dojo/_base/window", // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
8362 "./a11yclick"
8363 ], function(on, array, keys, declare, has, unload, win, a11yclick){
8364
8365 // module:
8366 // dijit/_OnDijitClickMixin
8367
8368 var ret = declare("dijit._OnDijitClickMixin", null, {
8369 connect: function(
8370 /*Object|null*/ obj,
8371 /*String|Function*/ event,
8372 /*String|Function*/ method){
8373 // summary:
8374 // Connects specified obj/event to specified method of this object
8375 // and registers for disconnect() on widget destroy.
8376 // description:
8377 // Provide widget-specific analog to connect.connect, except with the
8378 // implicit use of this widget as the target object.
8379 // This version of connect also provides a special "ondijitclick"
8380 // event which triggers on a click or space or enter keyup.
8381 // Events connected with `this.connect` are disconnected upon
8382 // destruction.
8383 // returns:
8384 // A handle that can be passed to `disconnect` in order to disconnect before
8385 // the widget is destroyed.
8386 // example:
8387 // | var btn = new Button();
8388 // | // when foo.bar() is called, call the listener we're going to
8389 // | // provide in the scope of btn
8390 // | btn.connect(foo, "bar", function(){
8391 // | console.debug(this.toString());
8392 // | });
8393 // tags:
8394 // protected
8395
8396 return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
8397 }
8398 });
8399
8400 ret.a11yclick = a11yclick; // back compat
8401
8402 return ret;
8403 });
8404
8405 },
8406 'dijit/InlineEditBox':function(){
8407 require({cache:{
8408 '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"}});
8409 define("dijit/InlineEditBox", [
8410 "require",
8411 "dojo/_base/array", // array.forEach
8412 "dojo/_base/declare", // declare
8413 "dojo/dom-attr", // domAttr.set domAttr.get
8414 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
8415 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
8416 "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
8417 "dojo/_base/event", // event.stop
8418 "dojo/i18n", // i18n.getLocalization
8419 "dojo/_base/kernel", // kernel.deprecated
8420 "dojo/keys", // keys.ENTER keys.ESCAPE
8421 "dojo/_base/lang", // lang.getObject
8422 "dojo/sniff", // has("ie")
8423 "dojo/when",
8424 "./focus",
8425 "./_Widget",
8426 "./_TemplatedMixin",
8427 "./_WidgetsInTemplateMixin",
8428 "./_Container",
8429 "./form/Button",
8430 "./form/_TextBoxMixin",
8431 "./form/TextBox",
8432 "dojo/text!./templates/InlineEditBox.html",
8433 "dojo/i18n!./nls/common"
8434 ], function(require, array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has, when,
8435 fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
8436
8437 // module:
8438 // dijit/InlineEditBox
8439
8440 var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
8441 // summary:
8442 // Internal widget used by InlineEditBox, displayed when in editing mode
8443 // to display the editor and maybe save/cancel buttons. Calling code should
8444 // connect to save/cancel methods to detect when editing is finished
8445 //
8446 // Has mainly the same parameters as InlineEditBox, plus these values:
8447 //
8448 // style: Object
8449 // Set of CSS attributes of display node, to replicate in editor
8450 //
8451 // value: String
8452 // Value as an HTML string or plain text string, depending on renderAsHTML flag
8453
8454 templateString: template,
8455
8456 postMixInProperties: function(){
8457 this.inherited(arguments);
8458 this.messages = i18n.getLocalization("dijit", "common", this.lang);
8459 array.forEach(["buttonSave", "buttonCancel"], function(prop){
8460 if(!this[prop]){
8461 this[prop] = this.messages[prop];
8462 }
8463 }, this);
8464 },
8465
8466 buildRendering: function(){
8467 this.inherited(arguments);
8468
8469 // Create edit widget in place in the template
8470 // TODO: remove getObject() for 2.0
8471 var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor;
8472
8473 // Copy the style from the source
8474 // Don't copy ALL properties though, just the necessary/applicable ones.
8475 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
8476 // is a relative value like 200%, rather than an absolute value like 24px, and
8477 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
8478 var srcStyle = this.sourceStyle,
8479 editStyle = "line-height:" + srcStyle.lineHeight + ";",
8480 destStyle = domStyle.getComputedStyle(this.domNode);
8481 array.forEach(["Weight", "Family", "Size", "Style"], function(prop){
8482 var textStyle = srcStyle["font" + prop],
8483 wrapperStyle = destStyle["font" + prop];
8484 if(wrapperStyle != textStyle){
8485 editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";";
8486 }
8487 }, this);
8488 array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){
8489 this.domNode.style[prop] = srcStyle[prop];
8490 }, this);
8491 var width = this.inlineEditBox.width;
8492 if(width == "100%"){
8493 // block mode
8494 editStyle += "width:100%;";
8495 this.domNode.style.display = "block";
8496 }else{
8497 // inline-block mode
8498 editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
8499 }
8500 var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
8501 style: editStyle,
8502 dir: this.dir,
8503 lang: this.lang,
8504 textDir: this.textDir
8505 });
8506 editorParams[ "displayedValue" in Cls.prototype ? "displayedValue" : "value"] = this.value;
8507 this.editWidget = new Cls(editorParams, this.editorPlaceholder);
8508
8509 if(this.inlineEditBox.autoSave){
8510 // Remove the save/cancel buttons since saving is done by simply tabbing away or
8511 // selecting a value from the drop down list
8512 domConstruct.destroy(this.buttonContainer);
8513 }
8514 },
8515
8516 postCreate: function(){
8517 this.inherited(arguments);
8518
8519 var ew = this.editWidget;
8520
8521 if(this.inlineEditBox.autoSave){
8522 // Selecting a value from a drop down list causes an onChange event and then we save
8523 this.connect(ew, "onChange", "_onChange");
8524
8525 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
8526 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
8527 // so this is the only way we can see the key press event.
8528 this.connect(ew, "onKeyPress", "_onKeyPress");
8529 }else{
8530 // If possible, enable/disable save button based on whether the user has changed the value
8531 if("intermediateChanges" in ew){
8532 ew.set("intermediateChanges", true);
8533 this.connect(ew, "onChange", "_onIntermediateChange");
8534 this.saveButton.set("disabled", true);
8535 }
8536 }
8537 },
8538
8539 startup: function(){
8540 this.editWidget.startup();
8541 this.inherited(arguments);
8542 },
8543
8544 _onIntermediateChange: function(/*===== val =====*/){
8545 // summary:
8546 // Called for editor widgets that support the intermediateChanges=true flag as a way
8547 // to detect when to enable/disabled the save button
8548 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
8549 },
8550
8551 destroy: function(){
8552 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
8553 this.inherited(arguments);
8554 },
8555
8556 getValue: function(){
8557 // summary:
8558 // Return the [display] value of the edit widget
8559 var ew = this.editWidget;
8560 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
8561 },
8562
8563 _onKeyPress: function(e){
8564 // summary:
8565 // Handler for keypress in the edit box in autoSave mode.
8566 // description:
8567 // For autoSave widgets, if Esc/Enter, call cancel/save.
8568 // tags:
8569 // private
8570
8571 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8572 if(e.altKey || e.ctrlKey){
8573 return;
8574 }
8575 // If Enter/Esc pressed, treat as save/cancel.
8576 if(e.charOrCode == keys.ESCAPE){
8577 event.stop(e);
8578 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
8579 }else if(e.charOrCode == keys.ENTER && e.target.tagName == "INPUT"){
8580 event.stop(e);
8581 this._onChange(); // fire _onBlur and then save
8582 }
8583
8584 // _onBlur will handle TAB automatically by allowing
8585 // the TAB to change focus before we mess with the DOM: #6227
8586 // Expounding by request:
8587 // The current focus is on the edit widget input field.
8588 // save() will hide and destroy this widget.
8589 // We want the focus to jump from the currently hidden
8590 // displayNode, but since it's hidden, it's impossible to
8591 // unhide it, focus it, and then have the browser focus
8592 // away from it to the next focusable element since each
8593 // of these events is asynchronous and the focus-to-next-element
8594 // is already queued.
8595 // So we allow the browser time to unqueue the move-focus event
8596 // before we do all the hide/show stuff.
8597 }
8598 },
8599
8600 _onBlur: function(){
8601 // summary:
8602 // Called when focus moves outside the editor
8603 // tags:
8604 // private
8605
8606 this.inherited(arguments);
8607 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8608 if(this.getValue() == this._resetValue){
8609 this.cancel(false);
8610 }else if(this.enableSave()){
8611 this.save(false);
8612 }
8613 }
8614 },
8615
8616 _onChange: function(){
8617 // summary:
8618 // Called when the underlying widget fires an onChange event,
8619 // such as when the user selects a value from the drop down list of a ComboBox,
8620 // which means that the user has finished entering the value and we should save.
8621 // tags:
8622 // private
8623
8624 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
8625 fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
8626 }
8627 },
8628
8629 enableSave: function(){
8630 // summary:
8631 // User overridable function returning a Boolean to indicate
8632 // if the Save button should be enabled or not - usually due to invalid conditions
8633 // tags:
8634 // extension
8635 return this.editWidget.isValid ? this.editWidget.isValid() : true;
8636 },
8637
8638 focus: function(){
8639 // summary:
8640 // Focus the edit widget.
8641 // tags:
8642 // protected
8643
8644 this.editWidget.focus();
8645
8646 if(this.editWidget.focusNode){
8647 // IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
8648 fm._onFocusNode(this.editWidget.focusNode);
8649
8650 if(this.editWidget.focusNode.tagName == "INPUT"){
8651 this.defer(function(){
8652 _TextBoxMixin.selectInputText(this.editWidget.focusNode);
8653 });
8654 }
8655 }
8656 }
8657 });
8658
8659
8660 var InlineEditBox = declare("dijit.InlineEditBox", _Widget, {
8661 // summary:
8662 // An element with in-line edit capabilities
8663 //
8664 // description:
8665 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
8666 // when you click it, an editor shows up in place of the original
8667 // text. Optionally, Save and Cancel button are displayed below the edit widget.
8668 // When Save is clicked, the text is pulled from the edit
8669 // widget and redisplayed and the edit widget is again hidden.
8670 // By default a plain Textarea widget is used as the editor (or for
8671 // inline values a TextBox), but you can specify an editor such as
8672 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
8673 // An edit widget must support the following API to be used:
8674 //
8675 // - displayedValue or value as initialization parameter,
8676 // and available through set('displayedValue') / set('value')
8677 // - void focus()
8678 // - DOM-node focusNode = node containing editable text
8679
8680 // editing: [readonly] Boolean
8681 // Is the node currently in edit mode?
8682 editing: false,
8683
8684 // autoSave: Boolean
8685 // Changing the value automatically saves it; don't have to push save button
8686 // (and save button isn't even displayed)
8687 autoSave: true,
8688
8689 // buttonSave: String
8690 // Save button label
8691 buttonSave: "",
8692
8693 // buttonCancel: String
8694 // Cancel button label
8695 buttonCancel: "",
8696
8697 // renderAsHtml: Boolean
8698 // Set this to true if the specified Editor's value should be interpreted as HTML
8699 // rather than plain text (ex: `dijit.Editor`)
8700 renderAsHtml: false,
8701
8702 // editor: String|Function
8703 // MID (ex: "dijit/form/TextBox") or constructor for editor widget
8704 editor: TextBox,
8705
8706 // editorWrapper: String|Function
8707 // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
8708 // buttons.
8709 editorWrapper: InlineEditor,
8710
8711 // editorParams: Object
8712 // Set of parameters for editor, like {required: true}
8713 editorParams: {},
8714
8715 // disabled: Boolean
8716 // If true, clicking the InlineEditBox to edit it will have no effect.
8717 disabled: false,
8718
8719 onChange: function(/*===== value =====*/){
8720 // summary:
8721 // Set this handler to be notified of changes to value.
8722 // tags:
8723 // callback
8724 },
8725
8726 onCancel: function(){
8727 // summary:
8728 // Set this handler to be notified when editing is cancelled.
8729 // tags:
8730 // callback
8731 },
8732
8733 // width: String
8734 // Width of editor. By default it's width=100% (ie, block mode).
8735 width: "100%",
8736
8737 // value: String
8738 // The display value of the widget in read-only mode
8739 value: "",
8740
8741 // noValueIndicator: [const] String
8742 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
8743 noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
8744 "<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
8745 "<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // &#160; == &nbsp;
8746
8747 constructor: function(/*===== params, srcNodeRef =====*/){
8748 // summary:
8749 // Create the widget.
8750 // params: Object|null
8751 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
8752 // and functions, typically callbacks like onClick.
8753 // The hash can contain any of the widget's properties, excluding read-only properties.
8754 // srcNodeRef: DOMNode|String?
8755 // If a srcNodeRef (DOM node) is specified:
8756 //
8757 // - use srcNodeRef.innerHTML as my value
8758 // - replace srcNodeRef with my generated DOM tree
8759
8760 this.editorParams = {};
8761 },
8762
8763 postMixInProperties: function(){
8764 this.inherited(arguments);
8765
8766 // save pointer to original source node, since Widget nulls-out srcNodeRef
8767 this.displayNode = this.srcNodeRef;
8768
8769 // connect handlers to the display node
8770 var events = {
8771 ondijitclick: "_onClick",
8772 onmouseover: "_onMouseOver",
8773 onmouseout: "_onMouseOut",
8774 onfocus: "_onMouseOver",
8775 onblur: "_onMouseOut"
8776 };
8777 for(var name in events){
8778 this.connect(this.displayNode, name, events[name]);
8779 }
8780 this.displayNode.setAttribute("role", "button");
8781 if(!this.displayNode.getAttribute("tabIndex")){
8782 this.displayNode.setAttribute("tabIndex", 0);
8783 }
8784
8785 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
8786 this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
8787 (this.displayNode.innerText || this.displayNode.textContent || ""));
8788 }
8789 if(!this.value){
8790 this.displayNode.innerHTML = this.noValueIndicator;
8791 }
8792
8793 domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
8794 },
8795
8796 setDisabled: function(/*Boolean*/ disabled){
8797 // summary:
8798 // Deprecated. Use set('disabled', ...) instead.
8799 // tags:
8800 // deprecated
8801 kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
8802 this.set('disabled', disabled);
8803 },
8804
8805 _setDisabledAttr: function(/*Boolean*/ disabled){
8806 // summary:
8807 // Hook to make set("disabled", ...) work.
8808 // Set disabled state of widget.
8809 this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false");
8810 if(disabled){
8811 this.displayNode.removeAttribute("tabIndex");
8812 }else{
8813 this.displayNode.setAttribute("tabIndex", 0);
8814 }
8815 domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
8816 this._set("disabled", disabled);
8817 },
8818
8819 _onMouseOver: function(){
8820 // summary:
8821 // Handler for onmouseover and onfocus event.
8822 // tags:
8823 // private
8824 if(!this.disabled){
8825 domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
8826 }
8827 },
8828
8829 _onMouseOut: function(){
8830 // summary:
8831 // Handler for onmouseout and onblur event.
8832 // tags:
8833 // private
8834 domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
8835 },
8836
8837 _onClick: function(/*Event*/ e){
8838 // summary:
8839 // Handler for onclick event.
8840 // tags:
8841 // private
8842 if(this.disabled){
8843 return;
8844 }
8845 if(e){
8846 event.stop(e);
8847 }
8848 this._onMouseOut();
8849
8850 // Since FF gets upset if you move a node while in an event handler for that node...
8851 this.defer("edit");
8852 },
8853
8854 edit: function(){
8855 // summary:
8856 // Display the editor widget in place of the original (read only) markup.
8857 // tags:
8858 // private
8859
8860 if(this.disabled || this.editing){
8861 return;
8862 }
8863 this._set('editing', true);
8864
8865 // save some display node values that can be restored later
8866 this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
8867
8868 if(this.wrapperWidget){
8869 var ew = this.wrapperWidget.editWidget;
8870 ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
8871 }else{
8872 // Placeholder for edit widget
8873 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
8874 // when Calendar dropdown appears, which happens automatically on focus.
8875 var placeholder = domConstruct.create("span", null, this.domNode, "before");
8876
8877 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
8878 var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
8879 this.wrapperWidget = new Ewc({
8880 value: this.value,
8881 buttonSave: this.buttonSave,
8882 buttonCancel: this.buttonCancel,
8883 dir: this.dir,
8884 lang: this.lang,
8885 tabIndex: this._savedTabIndex,
8886 editor: this.editor,
8887 inlineEditBox: this,
8888 sourceStyle: domStyle.getComputedStyle(this.displayNode),
8889 save: lang.hitch(this, "save"),
8890 cancel: lang.hitch(this, "cancel"),
8891 textDir: this.textDir
8892 }, placeholder);
8893 if(!this.wrapperWidget._started){
8894 this.wrapperWidget.startup();
8895 }
8896 if(!this._started){
8897 this.startup();
8898 }
8899 }
8900 var ww = this.wrapperWidget;
8901
8902 // to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
8903 // and then when it's finished rendering, we switch from display mode to editor
8904 // position: absolute releases screen space allocated to the display node
8905 // opacity:0 is the same as visibility: hidden but is still focusable
8906 // visibility: hidden removes focus outline
8907
8908 domClass.add(this.displayNode, "dijitOffScreen");
8909 domClass.remove(ww.domNode, "dijitOffScreen");
8910 domStyle.set(ww.domNode, { visibility: "visible" });
8911 domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
8912
8913 // After edit widget has finished initializing (in particular need to wait for dijit.Editor),
8914 // or immediately if there is no onLoadDeferred Deferred,
8915 // replace the display widget with edit widget, leaving them both displayed for a brief time so that
8916 // focus can be shifted without incident.
8917 when(ww.editWidget.onLoadDeferred, lang.hitch(ww, function(){
8918 this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
8919 this.focus(); // both nodes are showing, so we can switch focus safely
8920 this._resetValue = this.getValue();
8921 });
8922 }));
8923 },
8924
8925 _onBlur: function(){
8926 // summary:
8927 // Called when focus moves outside the InlineEditBox.
8928 // Performs garbage collection.
8929 // tags:
8930 // private
8931
8932 this.inherited(arguments);
8933 if(!this.editing){
8934 /* causes IE focus problems, see TooltipDialog_a11y.html...
8935 this.defer(function(){
8936 if(this.wrapperWidget){
8937 this.wrapperWidget.destroy();
8938 delete this.wrapperWidget;
8939 }
8940 });
8941 */
8942 }
8943 },
8944
8945 destroy: function(){
8946 if(this.wrapperWidget && !this.wrapperWidget._destroyed){
8947 this.wrapperWidget.destroy();
8948 delete this.wrapperWidget;
8949 }
8950 this.inherited(arguments);
8951 },
8952
8953 _showText: function(/*Boolean*/ focus){
8954 // summary:
8955 // Revert to display mode, and optionally focus on display node
8956 // tags:
8957 // private
8958
8959 var ww = this.wrapperWidget;
8960 domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events
8961 domClass.add(ww.domNode, "dijitOffScreen");
8962 domClass.remove(this.displayNode, "dijitOffScreen");
8963 domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
8964 if(focus){
8965 fm.focus(this.displayNode);
8966 }
8967 },
8968
8969 save: function(/*Boolean*/ focus){
8970 // summary:
8971 // Save the contents of the editor and revert to display mode.
8972 // focus: Boolean
8973 // Focus on the display mode text
8974 // tags:
8975 // private
8976
8977 if(this.disabled || !this.editing){
8978 return;
8979 }
8980 this._set('editing', false);
8981
8982 var ww = this.wrapperWidget;
8983 var value = ww.getValue();
8984 this.set('value', value); // display changed, formatted value
8985
8986 this._showText(focus); // set focus as needed
8987 },
8988
8989 setValue: function(/*String*/ val){
8990 // summary:
8991 // Deprecated. Use set('value', ...) instead.
8992 // tags:
8993 // deprecated
8994 kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
8995 return this.set("value", val);
8996 },
8997
8998 _setValueAttr: function(/*String*/ val){
8999 // summary:
9000 // Hook to make set("value", ...) work.
9001 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
9002
9003 val = lang.trim(val);
9004 var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
9005 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
9006 this._set("value", val);
9007
9008 if(this._started){
9009 // tell the world that we have changed
9010 this.defer(function(){
9011 this.onChange(val);
9012 }); // defer prevents browser freeze for long-running event handlers
9013 }
9014 // contextual (auto) text direction depends on the text value
9015 if(this.textDir == "auto"){
9016 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9017 }
9018 },
9019
9020 getValue: function(){
9021 // summary:
9022 // Deprecated. Use get('value') instead.
9023 // tags:
9024 // deprecated
9025 kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
9026 return this.get("value");
9027 },
9028
9029 cancel: function(/*Boolean*/ focus){
9030 // summary:
9031 // Revert to display mode, discarding any changes made in the editor
9032 // tags:
9033 // private
9034
9035 if(this.disabled || !this.editing){
9036 return;
9037 }
9038 this._set('editing', false);
9039
9040 // tell the world that we have no changes
9041 this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers
9042
9043 this._showText(focus);
9044 },
9045
9046 _setTextDirAttr: function(/*String*/ textDir){
9047 // summary:
9048 // Setter for textDir.
9049 // description:
9050 // Users shouldn't call this function; they should be calling
9051 // set('textDir', value)
9052 // tags:
9053 // private
9054 if(!this._created || this.textDir != textDir){
9055 this._set("textDir", textDir);
9056 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9057 this.displayNode.align = this.dir == "rtl" ? "right" : "left"; //fix the text alignment
9058 }
9059 }
9060 });
9061
9062 InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
9063
9064 return InlineEditBox;
9065 });
9066 },
9067 'dojo/selector/acme':function(){
9068 define("dojo/selector/acme", [
9069 "../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window"
9070 ], function(dom, has, array, lang, win){
9071
9072 // module:
9073 // dojo/selector/acme
9074
9075 /*
9076 acme architectural overview:
9077
9078 acme is a relatively full-featured CSS3 query library. It is
9079 designed to take any valid CSS3 selector and return the nodes matching
9080 the selector. To do this quickly, it processes queries in several
9081 steps, applying caching where profitable.
9082
9083 The steps (roughly in reverse order of the way they appear in the code):
9084 1.) check to see if we already have a "query dispatcher"
9085 - if so, use that with the given parameterization. Skip to step 4.
9086 2.) attempt to determine which branch to dispatch the query to:
9087 - JS (optimized DOM iteration)
9088 - native (FF3.1+, Safari 3.1+, IE 8+)
9089 3.) tokenize and convert to executable "query dispatcher"
9090 - this is where the lion's share of the complexity in the
9091 system lies. In the DOM version, the query dispatcher is
9092 assembled as a chain of "yes/no" test functions pertaining to
9093 a section of a simple query statement (".blah:nth-child(odd)"
9094 but not "div div", which is 2 simple statements). Individual
9095 statement dispatchers are cached (to prevent re-definition)
9096 as are entire dispatch chains (to make re-execution of the
9097 same query fast)
9098 4.) the resulting query dispatcher is called in the passed scope
9099 (by default the top-level document)
9100 - for DOM queries, this results in a recursive, top-down
9101 evaluation of nodes based on each simple query section
9102 - for native implementations, this may mean working around spec
9103 bugs. So be it.
9104 5.) matched nodes are pruned to ensure they are unique (if necessary)
9105 */
9106
9107
9108 ////////////////////////////////////////////////////////////////////////
9109 // Toolkit aliases
9110 ////////////////////////////////////////////////////////////////////////
9111
9112 // if you are extracting acme for use in your own system, you will
9113 // need to provide these methods and properties. No other porting should be
9114 // necessary, save for configuring the system to use a class other than
9115 // dojo/NodeList as the return instance instantiator
9116 var trim = lang.trim;
9117 var each = array.forEach;
9118
9119 var getDoc = function(){ return win.doc; };
9120 // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
9121 var cssCaseBug = (getDoc().compatMode) == "BackCompat";
9122
9123 ////////////////////////////////////////////////////////////////////////
9124 // Global utilities
9125 ////////////////////////////////////////////////////////////////////////
9126
9127
9128 var specials = ">~+";
9129
9130 // global thunk to determine whether we should treat the current query as
9131 // case sensitive or not. This switch is flipped by the query evaluator
9132 // based on the document passed as the context to search.
9133 var caseSensitive = false;
9134
9135 // how high?
9136 var yesman = function(){ return true; };
9137
9138 ////////////////////////////////////////////////////////////////////////
9139 // Tokenizer
9140 ////////////////////////////////////////////////////////////////////////
9141
9142 var getQueryParts = function(query){
9143 // summary:
9144 // state machine for query tokenization
9145 // description:
9146 // instead of using a brittle and slow regex-based CSS parser,
9147 // acme implements an AST-style query representation. This
9148 // representation is only generated once per query. For example,
9149 // the same query run multiple times or under different root nodes
9150 // does not re-parse the selector expression but instead uses the
9151 // cached data structure. The state machine implemented here
9152 // terminates on the last " " (space) character and returns an
9153 // ordered array of query component structures (or "parts"). Each
9154 // part represents an operator or a simple CSS filtering
9155 // expression. The structure for parts is documented in the code
9156 // below.
9157
9158
9159 // NOTE:
9160 // this code is designed to run fast and compress well. Sacrifices
9161 // to readability and maintainability have been made. Your best
9162 // bet when hacking the tokenizer is to put The Donnas on *really*
9163 // loud (may we recommend their "Spend The Night" release?) and
9164 // just assume you're gonna make mistakes. Keep the unit tests
9165 // open and run them frequently. Knowing is half the battle ;-)
9166 if(specials.indexOf(query.slice(-1)) >= 0){
9167 // if we end with a ">", "+", or "~", that means we're implicitly
9168 // searching all children, so make it explicit
9169 query += " * ";
9170 }else{
9171 // if you have not provided a terminator, one will be provided for
9172 // you...
9173 query += " ";
9174 }
9175
9176 var ts = function(/*Integer*/ s, /*Integer*/ e){
9177 // trim and slice.
9178
9179 // take an index to start a string slice from and an end position
9180 // and return a trimmed copy of that sub-string
9181 return trim(query.slice(s, e));
9182 };
9183
9184 // the overall data graph of the full query, as represented by queryPart objects
9185 var queryParts = [];
9186
9187
9188 // state keeping vars
9189 var inBrackets = -1, inParens = -1, inMatchFor = -1,
9190 inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
9191 lc = "", cc = "", pStart;
9192
9193 // iteration vars
9194 var x = 0, // index in the query
9195 ql = query.length,
9196 currentPart = null, // data structure representing the entire clause
9197 _cp = null; // the current pseudo or attr matcher
9198
9199 // several temporary variables are assigned to this structure during a
9200 // potential sub-expression match:
9201 // attr:
9202 // a string representing the current full attribute match in a
9203 // bracket expression
9204 // type:
9205 // if there's an operator in a bracket expression, this is
9206 // used to keep track of it
9207 // value:
9208 // the internals of parenthetical expression for a pseudo. for
9209 // :nth-child(2n+1), value might be "2n+1"
9210
9211 var endTag = function(){
9212 // called when the tokenizer hits the end of a particular tag name.
9213 // Re-sets state variables for tag matching and sets up the matcher
9214 // to handle the next type of token (tag or operator).
9215 if(inTag >= 0){
9216 var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
9217 currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
9218 inTag = -1;
9219 }
9220 };
9221
9222 var endId = function(){
9223 // called when the tokenizer might be at the end of an ID portion of a match
9224 if(inId >= 0){
9225 currentPart.id = ts(inId, x).replace(/\\/g, "");
9226 inId = -1;
9227 }
9228 };
9229
9230 var endClass = function(){
9231 // called when the tokenizer might be at the end of a class name
9232 // match. CSS allows for multiple classes, so we augment the
9233 // current item with another class in its list
9234 if(inClass >= 0){
9235 currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
9236 inClass = -1;
9237 }
9238 };
9239
9240 var endAll = function(){
9241 // at the end of a simple fragment, so wall off the matches
9242 endId();
9243 endTag();
9244 endClass();
9245 };
9246
9247 var endPart = function(){
9248 endAll();
9249 if(inPseudo >= 0){
9250 currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
9251 }
9252 // hint to the selector engine to tell it whether or not it
9253 // needs to do any iteration. Many simple selectors don't, and
9254 // we can avoid significant construction-time work by advising
9255 // the system to skip them
9256 currentPart.loops = (
9257 currentPart.pseudos.length ||
9258 currentPart.attrs.length ||
9259 currentPart.classes.length );
9260
9261 currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
9262
9263
9264 // otag/tag are hints to suggest to the system whether or not
9265 // it's an operator or a tag. We save a copy of otag since the
9266 // tag name is cast to upper-case in regular HTML matches. The
9267 // system has a global switch to figure out if the current
9268 // expression needs to be case sensitive or not and it will use
9269 // otag or tag accordingly
9270 currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
9271
9272 if(currentPart.tag){
9273 // if we're in a case-insensitive HTML doc, we likely want
9274 // the toUpperCase when matching on element.tagName. If we
9275 // do it here, we can skip the string op per node
9276 // comparison
9277 currentPart.tag = currentPart.tag.toUpperCase();
9278 }
9279
9280 // add the part to the list
9281 if(queryParts.length && (queryParts[queryParts.length-1].oper)){
9282 // operators are always infix, so we remove them from the
9283 // list and attach them to the next match. The evaluator is
9284 // responsible for sorting out how to handle them.
9285 currentPart.infixOper = queryParts.pop();
9286 currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
9287 /*
9288 console.debug( "swapping out the infix",
9289 currentPart.infixOper,
9290 "and attaching it to",
9291 currentPart);
9292 */
9293 }
9294 queryParts.push(currentPart);
9295
9296 currentPart = null;
9297 };
9298
9299 // iterate over the query, character by character, building up a
9300 // list of query part objects
9301 for(; lc=cc, cc=query.charAt(x), x < ql; x++){
9302 // cc: the current character in the match
9303 // lc: the last character (if any)
9304
9305 // someone is trying to escape something, so don't try to match any
9306 // fragments. We assume we're inside a literal.
9307 if(lc == "\\"){ continue; }
9308 if(!currentPart){ // a part was just ended or none has yet been created
9309 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
9310 pStart = x;
9311 // rules describe full CSS sub-expressions, like:
9312 // #someId
9313 // .className:first-child
9314 // but not:
9315 // thinger > div.howdy[type=thinger]
9316 // the indidual components of the previous query would be
9317 // split into 3 parts that would be represented a structure like:
9318 // [
9319 // {
9320 // query: "thinger",
9321 // tag: "thinger",
9322 // },
9323 // {
9324 // query: "div.howdy[type=thinger]",
9325 // classes: ["howdy"],
9326 // infixOper: {
9327 // query: ">",
9328 // oper: ">",
9329 // }
9330 // },
9331 // ]
9332 currentPart = {
9333 query: null, // the full text of the part's rule
9334 pseudos: [], // CSS supports multiple pseud-class matches in a single rule
9335 attrs: [], // CSS supports multi-attribute match, so we need an array
9336 classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
9337 tag: null, // only one tag...
9338 oper: null, // ...or operator per component. Note that these wind up being exclusive.
9339 id: null, // the id component of a rule
9340 getTag: function(){
9341 return caseSensitive ? this.otag : this.tag;
9342 }
9343 };
9344
9345 // if we don't have a part, we assume we're going to start at
9346 // the beginning of a match, which should be a tag name. This
9347 // might fault a little later on, but we detect that and this
9348 // iteration will still be fine.
9349 inTag = x;
9350 }
9351
9352 // Skip processing all quoted characters.
9353 // If we are inside quoted text then currentQuoteChar stores the character that began the quote,
9354 // thus that character that will end it.
9355 if(currentQuoteChar){
9356 if(cc == currentQuoteChar){
9357 currentQuoteChar = null;
9358 }
9359 continue;
9360 }else if (cc == "'" || cc == '"'){
9361 currentQuoteChar = cc;
9362 continue;
9363 }
9364
9365 if(inBrackets >= 0){
9366 // look for a the close first
9367 if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
9368 if(!_cp.attr){
9369 // no attribute match was previously begun, so we
9370 // assume this is an attribute existence match in the
9371 // form of [someAttributeName]
9372 _cp.attr = ts(inBrackets+1, x);
9373 }else{
9374 // we had an attribute already, so we know that we're
9375 // matching some sort of value, as in [attrName=howdy]
9376 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
9377 }
9378 var cmf = _cp.matchFor;
9379 if(cmf){
9380 // try to strip quotes from the matchFor value. We want
9381 // [attrName=howdy] to match the same
9382 // as [attrName = 'howdy' ]
9383 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
9384 _cp.matchFor = cmf.slice(1, -1);
9385 }
9386 }
9387 // remove backslash escapes from an attribute match, since DOM
9388 // querying will get attribute values without backslashes
9389 if(_cp.matchFor){
9390 _cp.matchFor = _cp.matchFor.replace(/\\/g, "");
9391 }
9392
9393 // end the attribute by adding it to the list of attributes.
9394 currentPart.attrs.push(_cp);
9395 _cp = null; // necessary?
9396 inBrackets = inMatchFor = -1;
9397 }else if(cc == "="){
9398 // if the last char was an operator prefix, make sure we
9399 // record it along with the "=" operator.
9400 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
9401 _cp.type = addToCc+cc;
9402 _cp.attr = ts(inBrackets+1, x-addToCc.length);
9403 inMatchFor = x+1;
9404 }
9405 // now look for other clause parts
9406 }else if(inParens >= 0){
9407 // if we're in a parenthetical expression, we need to figure
9408 // out if it's attached to a pseudo-selector rule like
9409 // :nth-child(1)
9410 if(cc == ")"){
9411 if(inPseudo >= 0){
9412 _cp.value = ts(inParens+1, x);
9413 }
9414 inPseudo = inParens = -1;
9415 }
9416 }else if(cc == "#"){
9417 // start of an ID match
9418 endAll();
9419 inId = x+1;
9420 }else if(cc == "."){
9421 // start of a class match
9422 endAll();
9423 inClass = x;
9424 }else if(cc == ":"){
9425 // start of a pseudo-selector match
9426 endAll();
9427 inPseudo = x;
9428 }else if(cc == "["){
9429 // start of an attribute match.
9430 endAll();
9431 inBrackets = x;
9432 // provide a new structure for the attribute match to fill-in
9433 _cp = {
9434 /*=====
9435 attr: null, type: null, matchFor: null
9436 =====*/
9437 };
9438 }else if(cc == "("){
9439 // we really only care if we've entered a parenthetical
9440 // expression if we're already inside a pseudo-selector match
9441 if(inPseudo >= 0){
9442 // provide a new structure for the pseudo match to fill-in
9443 _cp = {
9444 name: ts(inPseudo+1, x),
9445 value: null
9446 };
9447 currentPart.pseudos.push(_cp);
9448 }
9449 inParens = x;
9450 }else if(
9451 (cc == " ") &&
9452 // if it's a space char and the last char is too, consume the
9453 // current one without doing more work
9454 (lc != cc)
9455 ){
9456 endPart();
9457 }
9458 }
9459 return queryParts;
9460 };
9461
9462
9463 ////////////////////////////////////////////////////////////////////////
9464 // DOM query infrastructure
9465 ////////////////////////////////////////////////////////////////////////
9466
9467 var agree = function(first, second){
9468 // the basic building block of the yes/no chaining system. agree(f1,
9469 // f2) generates a new function which returns the boolean results of
9470 // both of the passed functions to a single logical-anded result. If
9471 // either are not passed, the other is used exclusively.
9472 if(!first){ return second; }
9473 if(!second){ return first; }
9474
9475 return function(){
9476 return first.apply(window, arguments) && second.apply(window, arguments);
9477 };
9478 };
9479
9480 var getArr = function(i, arr){
9481 // helps us avoid array alloc when we don't need it
9482 var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
9483 if(i){ r.push(i); }
9484 return r;
9485 };
9486
9487 var _isElement = function(n){ return (1 == n.nodeType); };
9488
9489 // FIXME: need to coalesce _getAttr with defaultGetter
9490 var blank = "";
9491 var _getAttr = function(elem, attr){
9492 if(!elem){ return blank; }
9493 if(attr == "class"){
9494 return elem.className || blank;
9495 }
9496 if(attr == "for"){
9497 return elem.htmlFor || blank;
9498 }
9499 if(attr == "style"){
9500 return elem.style.cssText || blank;
9501 }
9502 return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
9503 };
9504
9505 var attrs = {
9506 "*=": function(attr, value){
9507 return function(elem){
9508 // E[foo*="bar"]
9509 // an E element whose "foo" attribute value contains
9510 // the substring "bar"
9511 return (_getAttr(elem, attr).indexOf(value)>=0);
9512 };
9513 },
9514 "^=": function(attr, value){
9515 // E[foo^="bar"]
9516 // an E element whose "foo" attribute value begins exactly
9517 // with the string "bar"
9518 return function(elem){
9519 return (_getAttr(elem, attr).indexOf(value)==0);
9520 };
9521 },
9522 "$=": function(attr, value){
9523 // E[foo$="bar"]
9524 // an E element whose "foo" attribute value ends exactly
9525 // with the string "bar"
9526 return function(elem){
9527 var ea = " "+_getAttr(elem, attr);
9528 var lastIndex = ea.lastIndexOf(value);
9529 return lastIndex > -1 && (lastIndex==(ea.length-value.length));
9530 };
9531 },
9532 "~=": function(attr, value){
9533 // E[foo~="bar"]
9534 // an E element whose "foo" attribute value is a list of
9535 // space-separated values, one of which is exactly equal
9536 // to "bar"
9537
9538 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
9539 var tval = " "+value+" ";
9540 return function(elem){
9541 var ea = " "+_getAttr(elem, attr)+" ";
9542 return (ea.indexOf(tval)>=0);
9543 };
9544 },
9545 "|=": function(attr, value){
9546 // E[hreflang|="en"]
9547 // an E element whose "hreflang" attribute has a
9548 // hyphen-separated list of values beginning (from the
9549 // left) with "en"
9550 var valueDash = value+"-";
9551 return function(elem){
9552 var ea = _getAttr(elem, attr);
9553 return (
9554 (ea == value) ||
9555 (ea.indexOf(valueDash)==0)
9556 );
9557 };
9558 },
9559 "=": function(attr, value){
9560 return function(elem){
9561 return (_getAttr(elem, attr) == value);
9562 };
9563 }
9564 };
9565
9566 // avoid testing for node type if we can. Defining this in the negative
9567 // here to avoid negation in the fast path.
9568 var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
9569 var _ns = !_noNES ? "nextElementSibling" : "nextSibling";
9570 var _ps = !_noNES ? "previousElementSibling" : "previousSibling";
9571 var _simpleNodeTest = (_noNES ? _isElement : yesman);
9572
9573 var _lookLeft = function(node){
9574 // look left
9575 while(node = node[_ps]){
9576 if(_simpleNodeTest(node)){ return false; }
9577 }
9578 return true;
9579 };
9580
9581 var _lookRight = function(node){
9582 // look right
9583 while(node = node[_ns]){
9584 if(_simpleNodeTest(node)){ return false; }
9585 }
9586 return true;
9587 };
9588
9589 var getNodeIndex = function(node){
9590 var root = node.parentNode;
9591 root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
9592 var i = 0,
9593 tret = root.children || root.childNodes,
9594 ci = (node["_i"]||node.getAttribute("_i")||-1),
9595 cl = (root["_l"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l") : -1));
9596
9597 if(!tret){ return -1; }
9598 var l = tret.length;
9599
9600 // we calculate the parent length as a cheap way to invalidate the
9601 // cache. It's not 100% accurate, but it's much more honest than what
9602 // other libraries do
9603 if( cl == l && ci >= 0 && cl >= 0 ){
9604 // if it's legit, tag and release
9605 return ci;
9606 }
9607
9608 // else re-key things
9609 if(has("ie") && typeof root.setAttribute !== "undefined"){
9610 root.setAttribute("_l", l);
9611 }else{
9612 root["_l"] = l;
9613 }
9614 ci = -1;
9615 for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
9616 if(_simpleNodeTest(te)){
9617 if(has("ie")){
9618 te.setAttribute("_i", ++i);
9619 }else{
9620 te["_i"] = ++i;
9621 }
9622 if(node === te){
9623 // NOTE:
9624 // shortcutting the return at this step in indexing works
9625 // very well for benchmarking but we avoid it here since
9626 // it leads to potential O(n^2) behavior in sequential
9627 // getNodexIndex operations on a previously un-indexed
9628 // parent. We may revisit this at a later time, but for
9629 // now we just want to get the right answer more often
9630 // than not.
9631 ci = i;
9632 }
9633 }
9634 }
9635 return ci;
9636 };
9637
9638 var isEven = function(elem){
9639 return !((getNodeIndex(elem)) % 2);
9640 };
9641
9642 var isOdd = function(elem){
9643 return ((getNodeIndex(elem)) % 2);
9644 };
9645
9646 var pseudos = {
9647 "checked": function(name, condition){
9648 return function(elem){
9649 return !!("checked" in elem ? elem.checked : elem.selected);
9650 };
9651 },
9652 "disabled": function(name, condition){
9653 return function(elem){
9654 return elem.disabled;
9655 };
9656 },
9657 "enabled": function(name, condition){
9658 return function(elem){
9659 return !elem.disabled;
9660 };
9661 },
9662 "first-child": function(){ return _lookLeft; },
9663 "last-child": function(){ return _lookRight; },
9664 "only-child": function(name, condition){
9665 return function(node){
9666 return _lookLeft(node) && _lookRight(node);
9667 };
9668 },
9669 "empty": function(name, condition){
9670 return function(elem){
9671 // DomQuery and jQuery get this wrong, oddly enough.
9672 // The CSS 3 selectors spec is pretty explicit about it, too.
9673 var cn = elem.childNodes;
9674 var cnl = elem.childNodes.length;
9675 // if(!cnl){ return true; }
9676 for(var x=cnl-1; x >= 0; x--){
9677 var nt = cn[x].nodeType;
9678 if((nt === 1)||(nt == 3)){ return false; }
9679 }
9680 return true;
9681 };
9682 },
9683 "contains": function(name, condition){
9684 var cz = condition.charAt(0);
9685 if( cz == '"' || cz == "'" ){ //remove quote
9686 condition = condition.slice(1, -1);
9687 }
9688 return function(elem){
9689 return (elem.innerHTML.indexOf(condition) >= 0);
9690 };
9691 },
9692 "not": function(name, condition){
9693 var p = getQueryParts(condition)[0];
9694 var ignores = { el: 1 };
9695 if(p.tag != "*"){
9696 ignores.tag = 1;
9697 }
9698 if(!p.classes.length){
9699 ignores.classes = 1;
9700 }
9701 var ntf = getSimpleFilterFunc(p, ignores);
9702 return function(elem){
9703 return (!ntf(elem));
9704 };
9705 },
9706 "nth-child": function(name, condition){
9707 var pi = parseInt;
9708 // avoid re-defining function objects if we can
9709 if(condition == "odd"){
9710 return isOdd;
9711 }else if(condition == "even"){
9712 return isEven;
9713 }
9714 // FIXME: can we shorten this?
9715 if(condition.indexOf("n") != -1){
9716 var tparts = condition.split("n", 2);
9717 var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
9718 var idx = tparts[1] ? pi(tparts[1]) : 0;
9719 var lb = 0, ub = -1;
9720 if(pred > 0){
9721 if(idx < 0){
9722 idx = (idx % pred) && (pred + (idx % pred));
9723 }else if(idx>0){
9724 if(idx >= pred){
9725 lb = idx - idx % pred;
9726 }
9727 idx = idx % pred;
9728 }
9729 }else if(pred<0){
9730 pred *= -1;
9731 // idx has to be greater than 0 when pred is negative;
9732 // shall we throw an error here?
9733 if(idx > 0){
9734 ub = idx;
9735 idx = idx % pred;
9736 }
9737 }
9738 if(pred > 0){
9739 return function(elem){
9740 var i = getNodeIndex(elem);
9741 return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
9742 };
9743 }else{
9744 condition = idx;
9745 }
9746 }
9747 var ncount = pi(condition);
9748 return function(elem){
9749 return (getNodeIndex(elem) == ncount);
9750 };
9751 }
9752 };
9753
9754 var defaultGetter = (has("ie") < 9 || has("ie") == 9 && has("quirks")) ? function(cond){
9755 var clc = cond.toLowerCase();
9756 if(clc == "class"){ cond = "className"; }
9757 return function(elem){
9758 return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
9759 };
9760 } : function(cond){
9761 return function(elem){
9762 return (elem && elem.getAttribute && elem.hasAttribute(cond));
9763 };
9764 };
9765
9766 var getSimpleFilterFunc = function(query, ignores){
9767 // generates a node tester function based on the passed query part. The
9768 // query part is one of the structures generated by the query parser
9769 // when it creates the query AST. The "ignores" object specifies which
9770 // (if any) tests to skip, allowing the system to avoid duplicating
9771 // work where it may have already been taken into account by other
9772 // factors such as how the nodes to test were fetched in the first
9773 // place
9774 if(!query){ return yesman; }
9775 ignores = ignores||{};
9776
9777 var ff = null;
9778
9779 if(!("el" in ignores)){
9780 ff = agree(ff, _isElement);
9781 }
9782
9783 if(!("tag" in ignores)){
9784 if(query.tag != "*"){
9785 ff = agree(ff, function(elem){
9786 return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
9787 });
9788 }
9789 }
9790
9791 if(!("classes" in ignores)){
9792 each(query.classes, function(cname, idx, arr){
9793 // get the class name
9794 /*
9795 var isWildcard = cname.charAt(cname.length-1) == "*";
9796 if(isWildcard){
9797 cname = cname.substr(0, cname.length-1);
9798 }
9799 // I dislike the regex thing, even if memoized in a cache, but it's VERY short
9800 var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
9801 */
9802 var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)");
9803 ff = agree(ff, function(elem){
9804 return re.test(elem.className);
9805 });
9806 ff.count = idx;
9807 });
9808 }
9809
9810 if(!("pseudos" in ignores)){
9811 each(query.pseudos, function(pseudo){
9812 var pn = pseudo.name;
9813 if(pseudos[pn]){
9814 ff = agree(ff, pseudos[pn](pn, pseudo.value));
9815 }
9816 });
9817 }
9818
9819 if(!("attrs" in ignores)){
9820 each(query.attrs, function(attr){
9821 var matcher;
9822 var a = attr.attr;
9823 // type, attr, matchFor
9824 if(attr.type && attrs[attr.type]){
9825 matcher = attrs[attr.type](a, attr.matchFor);
9826 }else if(a.length){
9827 matcher = defaultGetter(a);
9828 }
9829 if(matcher){
9830 ff = agree(ff, matcher);
9831 }
9832 });
9833 }
9834
9835 if(!("id" in ignores)){
9836 if(query.id){
9837 ff = agree(ff, function(elem){
9838 return (!!elem && (elem.id == query.id));
9839 });
9840 }
9841 }
9842
9843 if(!ff){
9844 if(!("default" in ignores)){
9845 ff = yesman;
9846 }
9847 }
9848 return ff;
9849 };
9850
9851 var _nextSibling = function(filterFunc){
9852 return function(node, ret, bag){
9853 while(node = node[_ns]){
9854 if(_noNES && (!_isElement(node))){ continue; }
9855 if(
9856 (!bag || _isUnique(node, bag)) &&
9857 filterFunc(node)
9858 ){
9859 ret.push(node);
9860 }
9861 break;
9862 }
9863 return ret;
9864 };
9865 };
9866
9867 var _nextSiblings = function(filterFunc){
9868 return function(root, ret, bag){
9869 var te = root[_ns];
9870 while(te){
9871 if(_simpleNodeTest(te)){
9872 if(bag && !_isUnique(te, bag)){
9873 break;
9874 }
9875 if(filterFunc(te)){
9876 ret.push(te);
9877 }
9878 }
9879 te = te[_ns];
9880 }
9881 return ret;
9882 };
9883 };
9884
9885 // get an array of child *elements*, skipping text and comment nodes
9886 var _childElements = function(filterFunc){
9887 filterFunc = filterFunc||yesman;
9888 return function(root, ret, bag){
9889 // get an array of child elements, skipping text and comment nodes
9890 var te, x = 0, tret = root.children || root.childNodes;
9891 while(te = tret[x++]){
9892 if(
9893 _simpleNodeTest(te) &&
9894 (!bag || _isUnique(te, bag)) &&
9895 (filterFunc(te, x))
9896 ){
9897 ret.push(te);
9898 }
9899 }
9900 return ret;
9901 };
9902 };
9903
9904 // test to see if node is below root
9905 var _isDescendant = function(node, root){
9906 var pn = node.parentNode;
9907 while(pn){
9908 if(pn == root){
9909 break;
9910 }
9911 pn = pn.parentNode;
9912 }
9913 return !!pn;
9914 };
9915
9916 var _getElementsFuncCache = {};
9917
9918 var getElementsFunc = function(query){
9919 var retFunc = _getElementsFuncCache[query.query];
9920 // if we've got a cached dispatcher, just use that
9921 if(retFunc){ return retFunc; }
9922 // else, generate a new on
9923
9924 // NOTE:
9925 // this function returns a function that searches for nodes and
9926 // filters them. The search may be specialized by infix operators
9927 // (">", "~", or "+") else it will default to searching all
9928 // descendants (the " " selector). Once a group of children is
9929 // found, a test function is applied to weed out the ones we
9930 // don't want. Many common cases can be fast-pathed. We spend a
9931 // lot of cycles to create a dispatcher that doesn't do more work
9932 // than necessary at any point since, unlike this function, the
9933 // dispatchers will be called every time. The logic of generating
9934 // efficient dispatchers looks like this in pseudo code:
9935 //
9936 // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
9937 // if infixOperator == " ":
9938 // if only(id):
9939 // return def(root):
9940 // return d.byId(id, root);
9941 //
9942 // elif id:
9943 // return def(root):
9944 // return filter(d.byId(id, root));
9945 //
9946 // elif cssClass && getElementsByClassName:
9947 // return def(root):
9948 // return filter(root.getElementsByClassName(cssClass));
9949 //
9950 // elif only(tag):
9951 // return def(root):
9952 // return root.getElementsByTagName(tagName);
9953 //
9954 // else:
9955 // # search by tag name, then filter
9956 // return def(root):
9957 // return filter(root.getElementsByTagName(tagName||"*"));
9958 //
9959 // elif infixOperator == ">":
9960 // # search direct children
9961 // return def(root):
9962 // return filter(root.children);
9963 //
9964 // elif infixOperator == "+":
9965 // # search next sibling
9966 // return def(root):
9967 // return filter(root.nextElementSibling);
9968 //
9969 // elif infixOperator == "~":
9970 // # search rightward siblings
9971 // return def(root):
9972 // return filter(nextSiblings(root));
9973
9974 var io = query.infixOper;
9975 var oper = (io ? io.oper : "");
9976 // the default filter func which tests for all conditions in the query
9977 // part. This is potentially inefficient, so some optimized paths may
9978 // re-define it to test fewer things.
9979 var filterFunc = getSimpleFilterFunc(query, { el: 1 });
9980 var qt = query.tag;
9981 var wildcardTag = ("*" == qt);
9982 var ecs = getDoc()["getElementsByClassName"];
9983
9984 if(!oper){
9985 // if there's no infix operator, then it's a descendant query. ID
9986 // and "elements by class name" variants can be accelerated so we
9987 // call them out explicitly:
9988 if(query.id){
9989 // testing shows that the overhead of yesman() is acceptable
9990 // and can save us some bytes vs. re-defining the function
9991 // everywhere.
9992 filterFunc = (!query.loops && wildcardTag) ?
9993 yesman :
9994 getSimpleFilterFunc(query, { el: 1, id: 1 });
9995
9996 retFunc = function(root, arr){
9997 var te = dom.byId(query.id, (root.ownerDocument||root));
9998 if(!te || !filterFunc(te)){ return; }
9999 if(9 == root.nodeType){ // if root's a doc, we just return directly
10000 return getArr(te, arr);
10001 }else{ // otherwise check ancestry
10002 if(_isDescendant(te, root)){
10003 return getArr(te, arr);
10004 }
10005 }
10006 };
10007 }else if(
10008 ecs &&
10009 // isAlien check. Workaround for Prototype.js being totally evil/dumb.
10010 /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
10011 query.classes.length &&
10012 !cssCaseBug
10013 ){
10014 // it's a class-based query and we've got a fast way to run it.
10015
10016 // ignore class and ID filters since we will have handled both
10017 filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
10018 var classesString = query.classes.join(" ");
10019 retFunc = function(root, arr, bag){
10020 var ret = getArr(0, arr), te, x=0;
10021 var tret = root.getElementsByClassName(classesString);
10022 while((te = tret[x++])){
10023 if(filterFunc(te, root) && _isUnique(te, bag)){
10024 ret.push(te);
10025 }
10026 }
10027 return ret;
10028 };
10029
10030 }else if(!wildcardTag && !query.loops){
10031 // it's tag only. Fast-path it.
10032 retFunc = function(root, arr, bag){
10033 var ret = getArr(0, arr), te, x=0;
10034 var tag = query.getTag(),
10035 tret = tag ? root.getElementsByTagName(tag) : [];
10036 while((te = tret[x++])){
10037 if(_isUnique(te, bag)){
10038 ret.push(te);
10039 }
10040 }
10041 return ret;
10042 };
10043 }else{
10044 // the common case:
10045 // a descendant selector without a fast path. By now it's got
10046 // to have a tag selector, even if it's just "*" so we query
10047 // by that and filter
10048 filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
10049 retFunc = function(root, arr, bag){
10050 var ret = getArr(0, arr), te, x=0;
10051 // we use getTag() to avoid case sensitivity issues
10052 var tag = query.getTag(),
10053 tret = tag ? root.getElementsByTagName(tag) : [];
10054 while((te = tret[x++])){
10055 if(filterFunc(te, root) && _isUnique(te, bag)){
10056 ret.push(te);
10057 }
10058 }
10059 return ret;
10060 };
10061 }
10062 }else{
10063 // the query is scoped in some way. Instead of querying by tag we
10064 // use some other collection to find candidate nodes
10065 var skipFilters = { el: 1 };
10066 if(wildcardTag){
10067 skipFilters.tag = 1;
10068 }
10069 filterFunc = getSimpleFilterFunc(query, skipFilters);
10070 if("+" == oper){
10071 retFunc = _nextSibling(filterFunc);
10072 }else if("~" == oper){
10073 retFunc = _nextSiblings(filterFunc);
10074 }else if(">" == oper){
10075 retFunc = _childElements(filterFunc);
10076 }
10077 }
10078 // cache it and return
10079 return _getElementsFuncCache[query.query] = retFunc;
10080 };
10081
10082 var filterDown = function(root, queryParts){
10083 // NOTE:
10084 // this is the guts of the DOM query system. It takes a list of
10085 // parsed query parts and a root and finds children which match
10086 // the selector represented by the parts
10087 var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
10088
10089 for(var i = 0; i < qpl; i++){
10090 ret = [];
10091 qp = queryParts[i];
10092 x = candidates.length - 1;
10093 if(x > 0){
10094 // if we have more than one root at this level, provide a new
10095 // hash to use for checking group membership but tell the
10096 // system not to post-filter us since we will already have been
10097 // guaranteed to be unique
10098 bag = {};
10099 ret.nozip = true;
10100 }
10101 var gef = getElementsFunc(qp);
10102 for(var j = 0; (te = candidates[j]); j++){
10103 // for every root, get the elements that match the descendant
10104 // selector, adding them to the "ret" array and filtering them
10105 // via membership in this level's bag. If there are more query
10106 // parts, then this level's return will be used as the next
10107 // level's candidates
10108 gef(te, ret, bag);
10109 }
10110 if(!ret.length){ break; }
10111 candidates = ret;
10112 }
10113 return ret;
10114 };
10115
10116 ////////////////////////////////////////////////////////////////////////
10117 // the query runner
10118 ////////////////////////////////////////////////////////////////////////
10119
10120 // these are the primary caches for full-query results. The query
10121 // dispatcher functions are generated then stored here for hash lookup in
10122 // the future
10123 var _queryFuncCacheDOM = {},
10124 _queryFuncCacheQSA = {};
10125
10126 // this is the second level of splitting, from full-length queries (e.g.,
10127 // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
10128 // ".bar"])
10129 var getStepQueryFunc = function(query){
10130 var qparts = getQueryParts(trim(query));
10131
10132 // if it's trivial, avoid iteration and zipping costs
10133 if(qparts.length == 1){
10134 // we optimize this case here to prevent dispatch further down the
10135 // chain, potentially slowing things down. We could more elegantly
10136 // handle this in filterDown(), but it's slower for simple things
10137 // that need to be fast (e.g., "#someId").
10138 var tef = getElementsFunc(qparts[0]);
10139 return function(root){
10140 var r = tef(root, []);
10141 if(r){ r.nozip = true; }
10142 return r;
10143 };
10144 }
10145
10146 // otherwise, break it up and return a runner that iterates over the parts recursively
10147 return function(root){
10148 return filterDown(root, qparts);
10149 };
10150 };
10151
10152 // NOTES:
10153 // * we can't trust QSA for anything but document-rooted queries, so
10154 // caching is split into DOM query evaluators and QSA query evaluators
10155 // * caching query results is dirty and leak-prone (or, at a minimum,
10156 // prone to unbounded growth). Other toolkits may go this route, but
10157 // they totally destroy their own ability to manage their memory
10158 // footprint. If we implement it, it should only ever be with a fixed
10159 // total element reference # limit and an LRU-style algorithm since JS
10160 // has no weakref support. Caching compiled query evaluators is also
10161 // potentially problematic, but even on large documents the size of the
10162 // query evaluators is often < 100 function objects per evaluator (and
10163 // LRU can be applied if it's ever shown to be an issue).
10164 // * since IE's QSA support is currently only for HTML documents and even
10165 // then only in IE 8's "standards mode", we have to detect our dispatch
10166 // route at query time and keep 2 separate caches. Ugg.
10167
10168 // we need to determine if we think we can run a given query via
10169 // querySelectorAll or if we'll need to fall back on DOM queries to get
10170 // there. We need a lot of information about the environment and the query
10171 // to make the determination (e.g. does it support QSA, does the query in
10172 // question work in the native QSA impl, etc.).
10173
10174 // IE QSA queries may incorrectly include comment nodes, so we throw the
10175 // zipping function into "remove" comments mode instead of the normal "skip
10176 // it" which every other QSA-clued browser enjoys
10177 var noZip = has("ie") ? "commentStrip" : "nozip";
10178
10179 var qsa = "querySelectorAll";
10180 var qsaAvail = !!getDoc()[qsa];
10181
10182 //Don't bother with n+3 type of matches, IE complains if we modify those.
10183 var infixSpaceRe = /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
10184 var infixSpaceFunc = function(match, pre, ch, post){
10185 return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match;
10186 };
10187
10188 //Don't apply the infixSpaceRe to attribute value selectors
10189 var attRe = /([^[]*)([^\]]*])?/g;
10190 var attFunc = function(match, nonAtt, att){
10191 return nonAtt.replace(infixSpaceRe, infixSpaceFunc) + (att||"");
10192 };
10193 var getQueryFunc = function(query, forceDOM){
10194 //Normalize query. The CSS3 selectors spec allows for omitting spaces around
10195 //infix operators, >, ~ and +
10196 //Do the work here since detection for spaces is used as a simple "not use QSA"
10197 //test below.
10198 query = query.replace(attRe, attFunc);
10199
10200 if(qsaAvail){
10201 // if we've got a cached variant and we think we can do it, run it!
10202 var qsaCached = _queryFuncCacheQSA[query];
10203 if(qsaCached && !forceDOM){ return qsaCached; }
10204 }
10205
10206 // else if we've got a DOM cached variant, assume that we already know
10207 // all we need to and use it
10208 var domCached = _queryFuncCacheDOM[query];
10209 if(domCached){ return domCached; }
10210
10211 // TODO:
10212 // today we're caching DOM and QSA branches separately so we
10213 // recalc useQSA every time. If we had a way to tag root+query
10214 // efficiently, we'd be in good shape to do a global cache.
10215
10216 var qcz = query.charAt(0);
10217 var nospace = (-1 == query.indexOf(" "));
10218
10219 // byId searches are wicked fast compared to QSA, even when filtering
10220 // is required
10221 if( (query.indexOf("#") >= 0) && (nospace) ){
10222 forceDOM = true;
10223 }
10224
10225 var useQSA = (
10226 qsaAvail && (!forceDOM) &&
10227 // as per CSS 3, we can't currently start w/ combinator:
10228 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
10229 (specials.indexOf(qcz) == -1) &&
10230 // IE's QSA impl sucks on pseudos
10231 (!has("ie") || (query.indexOf(":") == -1)) &&
10232
10233 (!(cssCaseBug && (query.indexOf(".") >= 0))) &&
10234
10235 // FIXME:
10236 // need to tighten up browser rules on ":contains" and "|=" to
10237 // figure out which aren't good
10238 // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
10239 // elements, even though according to spec, selected options should
10240 // match :checked. So go nonQSA for it:
10241 // http://bugs.dojotoolkit.org/ticket/5179
10242 (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) &&
10243 (query.indexOf("|=") == -1) // some browsers don't grok it
10244 );
10245
10246 // TODO:
10247 // if we've got a descendant query (e.g., "> .thinger" instead of
10248 // just ".thinger") in a QSA-able doc, but are passed a child as a
10249 // root, it should be possible to give the item a synthetic ID and
10250 // trivially rewrite the query to the form "#synid > .thinger" to
10251 // use the QSA branch
10252
10253
10254 if(useQSA){
10255 var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ?
10256 (query + " *") : query;
10257 return _queryFuncCacheQSA[query] = function(root){
10258 try{
10259 // the QSA system contains an egregious spec bug which
10260 // limits us, effectively, to only running QSA queries over
10261 // entire documents. See:
10262 // http://ejohn.org/blog/thoughts-on-queryselectorall/
10263 // despite this, we can also handle QSA runs on simple
10264 // selectors, but we don't want detection to be expensive
10265 // so we're just checking for the presence of a space char
10266 // right now. Not elegant, but it's cheaper than running
10267 // the query parser when we might not need to
10268 if(!((9 == root.nodeType) || nospace)){ throw ""; }
10269 var r = root[qsa](tq);
10270 // skip expensive duplication checks and just wrap in a NodeList
10271 r[noZip] = true;
10272 return r;
10273 }catch(e){
10274 // else run the DOM branch on this query, ensuring that we
10275 // default that way in the future
10276 return getQueryFunc(query, true)(root);
10277 }
10278 };
10279 }else{
10280 // DOM branch
10281 var parts = query.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
10282 return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
10283 // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
10284 getStepQueryFunc(query) :
10285 // if it *is* a complex query, break it up into its
10286 // constituent parts and return a dispatcher that will
10287 // merge the parts when run
10288 function(root){
10289 var pindex = 0, // avoid array alloc for every invocation
10290 ret = [],
10291 tp;
10292 while((tp = parts[pindex++])){
10293 ret = ret.concat(getStepQueryFunc(tp)(root));
10294 }
10295 return ret;
10296 }
10297 );
10298 }
10299 };
10300
10301 var _zipIdx = 0;
10302
10303 // NOTE:
10304 // this function is Moo inspired, but our own impl to deal correctly
10305 // with XML in IE
10306 var _nodeUID = has("ie") ? function(node){
10307 if(caseSensitive){
10308 // XML docs don't have uniqueID on their nodes
10309 return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
10310
10311 }else{
10312 return node.uniqueID;
10313 }
10314 } :
10315 function(node){
10316 return (node._uid || (node._uid = ++_zipIdx));
10317 };
10318
10319 // determine if a node in is unique in a "bag". In this case we don't want
10320 // to flatten a list of unique items, but rather just tell if the item in
10321 // question is already in the bag. Normally we'd just use hash lookup to do
10322 // this for us but IE's DOM is busted so we can't really count on that. On
10323 // the upside, it gives us a built in unique ID function.
10324 var _isUnique = function(node, bag){
10325 if(!bag){ return 1; }
10326 var id = _nodeUID(node);
10327 if(!bag[id]){ return bag[id] = 1; }
10328 return 0;
10329 };
10330
10331 // attempt to efficiently determine if an item in a list is a dupe,
10332 // returning a list of "uniques", hopefully in document order
10333 var _zipIdxName = "_zipIdx";
10334 var _zip = function(arr){
10335 if(arr && arr.nozip){
10336 return arr;
10337 }
10338 var ret = [];
10339 if(!arr || !arr.length){ return ret; }
10340 if(arr[0]){
10341 ret.push(arr[0]);
10342 }
10343 if(arr.length < 2){ return ret; }
10344
10345 _zipIdx++;
10346
10347 // we have to fork here for IE and XML docs because we can't set
10348 // expandos on their nodes (apparently). *sigh*
10349 var x, te;
10350 if(has("ie") && caseSensitive){
10351 var szidx = _zipIdx+"";
10352 arr[0].setAttribute(_zipIdxName, szidx);
10353 for(x = 1; te = arr[x]; x++){
10354 if(arr[x].getAttribute(_zipIdxName) != szidx){
10355 ret.push(te);
10356 }
10357 te.setAttribute(_zipIdxName, szidx);
10358 }
10359 }else if(has("ie") && arr.commentStrip){
10360 try{
10361 for(x = 1; te = arr[x]; x++){
10362 if(_isElement(te)){
10363 ret.push(te);
10364 }
10365 }
10366 }catch(e){ /* squelch */ }
10367 }else{
10368 if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; }
10369 for(x = 1; te = arr[x]; x++){
10370 if(arr[x][_zipIdxName] != _zipIdx){
10371 ret.push(te);
10372 }
10373 te[_zipIdxName] = _zipIdx;
10374 }
10375 }
10376 return ret;
10377 };
10378
10379 // the main executor
10380 var query = function(/*String*/ query, /*String|DOMNode?*/ root){
10381 // summary:
10382 // Returns nodes which match the given CSS3 selector, searching the
10383 // entire document by default but optionally taking a node to scope
10384 // the search by. Returns an array.
10385 // description:
10386 // dojo.query() is the swiss army knife of DOM node manipulation in
10387 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
10388 // "$" function, dojo.query provides robust, high-performance
10389 // CSS-based node selector support with the option of scoping searches
10390 // to a particular sub-tree of a document.
10391 //
10392 // Supported Selectors:
10393 // --------------------
10394 //
10395 // acme supports a rich set of CSS3 selectors, including:
10396 //
10397 // - class selectors (e.g., `.foo`)
10398 // - node type selectors like `span`
10399 // - ` ` descendant selectors
10400 // - `>` child element selectors
10401 // - `#foo` style ID selectors
10402 // - `*` universal selector
10403 // - `~`, the preceded-by sibling selector
10404 // - `+`, the immediately preceded-by sibling selector
10405 // - attribute queries:
10406 // - `[foo]` attribute presence selector
10407 // - `[foo='bar']` attribute value exact match
10408 // - `[foo~='bar']` attribute value list item match
10409 // - `[foo^='bar']` attribute start match
10410 // - `[foo$='bar']` attribute end match
10411 // - `[foo*='bar']` attribute substring match
10412 // - `:first-child`, `:last-child`, and `:only-child` positional selectors
10413 // - `:empty` content emtpy selector
10414 // - `:checked` pseudo selector
10415 // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
10416 // - `:nth-child(even)`, `:nth-child(odd)` positional selectors
10417 // - `:not(...)` negation pseudo selectors
10418 //
10419 // Any legal combination of these selectors will work with
10420 // `dojo.query()`, including compound selectors ("," delimited).
10421 // Very complex and useful searches can be constructed with this
10422 // palette of selectors and when combined with functions for
10423 // manipulation presented by dojo/NodeList, many types of DOM
10424 // manipulation operations become very straightforward.
10425 //
10426 // Unsupported Selectors:
10427 // ----------------------
10428 //
10429 // While dojo.query handles many CSS3 selectors, some fall outside of
10430 // what's reasonable for a programmatic node querying engine to
10431 // handle. Currently unsupported selectors include:
10432 //
10433 // - namespace-differentiated selectors of any form
10434 // - all `::` pseduo-element selectors
10435 // - certain pseudo-selectors which don't get a lot of day-to-day use:
10436 // - `:root`, `:lang()`, `:target`, `:focus`
10437 // - all visual and state selectors:
10438 // - `:root`, `:active`, `:hover`, `:visited`, `:link`,
10439 // `:enabled`, `:disabled`
10440 // - `:*-of-type` pseudo selectors
10441 //
10442 // dojo.query and XML Documents:
10443 // -----------------------------
10444 //
10445 // `dojo.query` (as of dojo 1.2) supports searching XML documents
10446 // in a case-sensitive manner. If an HTML document is served with
10447 // a doctype that forces case-sensitivity (e.g., XHTML 1.1
10448 // Strict), dojo.query() will detect this and "do the right
10449 // thing". Case sensitivity is dependent upon the document being
10450 // searched and not the query used. It is therefore possible to
10451 // use case-sensitive queries on strict sub-documents (iframes,
10452 // etc.) or XML documents while still assuming case-insensitivity
10453 // for a host/root document.
10454 //
10455 // Non-selector Queries:
10456 // ---------------------
10457 //
10458 // If something other than a String is passed for the query,
10459 // `dojo.query` will return a new `dojo/NodeList` instance
10460 // constructed from that parameter alone and all further
10461 // processing will stop. This means that if you have a reference
10462 // to a node or NodeList, you can quickly construct a new NodeList
10463 // from the original by calling `dojo.query(node)` or
10464 // `dojo.query(list)`.
10465 //
10466 // query:
10467 // The CSS3 expression to match against. For details on the syntax of
10468 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
10469 // root:
10470 // A DOMNode (or node id) to scope the search from. Optional.
10471 // returns: Array
10472 // example:
10473 // search the entire document for elements with the class "foo":
10474 // | dojo.query(".foo");
10475 // these elements will match:
10476 // | <span class="foo"></span>
10477 // | <span class="foo bar"></span>
10478 // | <p class="thud foo"></p>
10479 // example:
10480 // search the entire document for elements with the classes "foo" *and* "bar":
10481 // | dojo.query(".foo.bar");
10482 // these elements will match:
10483 // | <span class="foo bar"></span>
10484 // while these will not:
10485 // | <span class="foo"></span>
10486 // | <p class="thud foo"></p>
10487 // example:
10488 // find `<span>` elements which are descendants of paragraphs and
10489 // which have a "highlighted" class:
10490 // | dojo.query("p span.highlighted");
10491 // the innermost span in this fragment matches:
10492 // | <p class="foo">
10493 // | <span>...
10494 // | <span class="highlighted foo bar">...</span>
10495 // | </span>
10496 // | </p>
10497 // example:
10498 // set an "odd" class on all odd table rows inside of the table
10499 // `#tabular_data`, using the `>` (direct child) selector to avoid
10500 // affecting any nested tables:
10501 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
10502 // example:
10503 // remove all elements with the class "error" from the document
10504 // and store them in a list:
10505 // | var errors = dojo.query(".error").orphan();
10506 // example:
10507 // add an onclick handler to every submit button in the document
10508 // which causes the form to be sent via Ajax instead:
10509 // | dojo.query("input[type='submit']").onclick(function(e){
10510 // | dojo.stopEvent(e); // prevent sending the form
10511 // | var btn = e.target;
10512 // | dojo.xhrPost({
10513 // | form: btn.form,
10514 // | load: function(data){
10515 // | // replace the form with the response
10516 // | var div = dojo.doc.createElement("div");
10517 // | dojo.place(div, btn.form, "after");
10518 // | div.innerHTML = data;
10519 // | dojo.style(btn.form, "display", "none");
10520 // | }
10521 // | });
10522 // | });
10523
10524 root = root || getDoc();
10525
10526 // throw the big case sensitivity switch
10527 var od = root.ownerDocument || root; // root is either Document or a node inside the document
10528 caseSensitive = (od.createElement("div").tagName === "div");
10529
10530 // NOTE:
10531 // adding "true" as the 2nd argument to getQueryFunc is useful for
10532 // testing the DOM branch without worrying about the
10533 // behavior/performance of the QSA branch.
10534 var r = getQueryFunc(query)(root);
10535
10536 // FIXME:
10537 // need to investigate this branch WRT #8074 and #8075
10538 if(r && r.nozip){
10539 return r;
10540 }
10541 return _zip(r); // dojo/NodeList
10542 };
10543 query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
10544 // summary:
10545 // function for filtering a NodeList based on a selector, optimized for simple selectors
10546 var tmpNodeList = [],
10547 parts = getQueryParts(filter),
10548 filterFunc =
10549 (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
10550 getSimpleFilterFunc(parts[0]) :
10551 function(node){
10552 return array.indexOf(query(filter, dom.byId(root)), node) != -1;
10553 };
10554 for(var x = 0, te; te = nodeList[x]; x++){
10555 if(filterFunc(te)){ tmpNodeList.push(te); }
10556 }
10557 return tmpNodeList;
10558 };
10559 return query;
10560 });
10561
10562 },
10563 'dojo/dnd/autoscroll':function(){
10564 define("dojo/dnd/autoscroll", ["../_base/lang", "../sniff", "../_base/window", "../dom-geometry", "../dom-style", "../window"],
10565 function(lang, has, win, domGeom, domStyle, winUtils){
10566
10567 // module:
10568 // dojo/dnd/autoscroll
10569
10570 var exports = {
10571 // summary:
10572 // Used by dojo/dnd/Manager to scroll document or internal node when the user
10573 // drags near the edge of the viewport or a scrollable node
10574 };
10575 lang.setObject("dojo.dnd.autoscroll", exports);
10576
10577 exports.getViewport = winUtils.getBox;
10578
10579 exports.V_TRIGGER_AUTOSCROLL = 32;
10580 exports.H_TRIGGER_AUTOSCROLL = 32;
10581
10582 exports.V_AUTOSCROLL_VALUE = 16;
10583 exports.H_AUTOSCROLL_VALUE = 16;
10584
10585 // These are set by autoScrollStart().
10586 // Set to default values in case autoScrollStart() isn't called. (back-compat, remove for 2.0)
10587 var viewport,
10588 doc = win.doc,
10589 maxScrollTop = Infinity,
10590 maxScrollLeft = Infinity;
10591
10592 exports.autoScrollStart = function(d){
10593 // summary:
10594 // Called at the start of a drag.
10595 // d: Document
10596 // The document of the node being dragged.
10597
10598 doc = d;
10599 viewport = winUtils.getBox(doc);
10600
10601 // Save height/width of document at start of drag, before it gets distorted by a user dragging an avatar past
10602 // the document's edge
10603 var html = win.body(doc).parentNode;
10604 maxScrollTop = Math.max(html.scrollHeight - viewport.h, 0);
10605 maxScrollLeft = Math.max(html.scrollWidth - viewport.w, 0); // usually 0
10606 };
10607
10608 exports.autoScroll = function(e){
10609 // summary:
10610 // a handler for mousemove and touchmove events, which scrolls the window, if
10611 // necessary
10612 // e: Event
10613 // mousemove/touchmove event
10614
10615 // FIXME: needs more docs!
10616 var v = viewport || winUtils.getBox(doc), // getBox() call for back-compat, in case autoScrollStart() wasn't called
10617 html = win.body(doc).parentNode,
10618 dx = 0, dy = 0;
10619 if(e.clientX < exports.H_TRIGGER_AUTOSCROLL){
10620 dx = -exports.H_AUTOSCROLL_VALUE;
10621 }else if(e.clientX > v.w - exports.H_TRIGGER_AUTOSCROLL){
10622 dx = Math.min(exports.H_AUTOSCROLL_VALUE, maxScrollLeft - html.scrollLeft); // don't scroll past edge of doc
10623 }
10624 if(e.clientY < exports.V_TRIGGER_AUTOSCROLL){
10625 dy = -exports.V_AUTOSCROLL_VALUE;
10626 }else if(e.clientY > v.h - exports.V_TRIGGER_AUTOSCROLL){
10627 dy = Math.min(exports.V_AUTOSCROLL_VALUE, maxScrollTop - html.scrollTop); // don't scroll past edge of doc
10628 }
10629 window.scrollBy(dx, dy);
10630 };
10631
10632 exports._validNodes = {"div": 1, "p": 1, "td": 1};
10633 exports._validOverflow = {"auto": 1, "scroll": 1};
10634
10635 exports.autoScrollNodes = function(e){
10636 // summary:
10637 // a handler for mousemove and touchmove events, which scrolls the first available
10638 // Dom element, it falls back to exports.autoScroll()
10639 // e: Event
10640 // mousemove/touchmove event
10641
10642 // FIXME: needs more docs!
10643
10644 var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
10645
10646 for(var n = e.target; n;){
10647 if(n.nodeType == 1 && (n.tagName.toLowerCase() in exports._validNodes)){
10648 var s = domStyle.getComputedStyle(n),
10649 overflow = (s.overflow.toLowerCase() in exports._validOverflow),
10650 overflowX = (s.overflowX.toLowerCase() in exports._validOverflow),
10651 overflowY = (s.overflowY.toLowerCase() in exports._validOverflow);
10652 if(overflow || overflowX || overflowY){
10653 b = domGeom.getContentBox(n, s);
10654 t = domGeom.position(n, true);
10655 }
10656 // overflow-x
10657 if(overflow || overflowX){
10658 w = Math.min(exports.H_TRIGGER_AUTOSCROLL, b.w / 2);
10659 rx = e.pageX - t.x;
10660 if(has("webkit") || has("opera")){
10661 // FIXME: this code should not be here, it should be taken into account
10662 // either by the event fixing code, or the domGeom.position()
10663 // FIXME: this code doesn't work on Opera 9.5 Beta
10664 rx += win.body().scrollLeft;
10665 }
10666 dx = 0;
10667 if(rx > 0 && rx < b.w){
10668 if(rx < w){
10669 dx = -w;
10670 }else if(rx > b.w - w){
10671 dx = w;
10672 }
10673 oldLeft = n.scrollLeft;
10674 n.scrollLeft = n.scrollLeft + dx;
10675 }
10676 }
10677 // overflow-y
10678 if(overflow || overflowY){
10679 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
10680 h = Math.min(exports.V_TRIGGER_AUTOSCROLL, b.h / 2);
10681 ry = e.pageY - t.y;
10682 if(has("webkit") || has("opera")){
10683 // FIXME: this code should not be here, it should be taken into account
10684 // either by the event fixing code, or the domGeom.position()
10685 // FIXME: this code doesn't work on Opera 9.5 Beta
10686 ry += win.body().scrollTop;
10687 }
10688 dy = 0;
10689 if(ry > 0 && ry < b.h){
10690 if(ry < h){
10691 dy = -h;
10692 }else if(ry > b.h - h){
10693 dy = h;
10694 }
10695 oldTop = n.scrollTop;
10696 n.scrollTop = n.scrollTop + dy;
10697 }
10698 }
10699 if(dx || dy){ return; }
10700 }
10701 try{
10702 n = n.parentNode;
10703 }catch(x){
10704 n = null;
10705 }
10706 }
10707 exports.autoScroll(e);
10708 };
10709
10710 return exports;
10711
10712 });
10713
10714 },
10715 'dijit/form/_RadioButtonMixin':function(){
10716 define("dijit/form/_RadioButtonMixin", [
10717 "dojo/_base/array", // array.forEach
10718 "dojo/_base/declare", // declare
10719 "dojo/dom-attr", // domAttr.set
10720 "dojo/_base/event", // event.stop
10721 "dojo/_base/lang", // lang.hitch
10722 "dojo/query", // query
10723 "../registry" // registry.getEnclosingWidget
10724 ], function(array, declare, domAttr, event, lang, query, registry){
10725
10726 // module:
10727 // dijit/form/_RadioButtonMixin
10728
10729 return declare("dijit.form._RadioButtonMixin", null, {
10730 // summary:
10731 // Mixin to provide widget functionality for an HTML radio button
10732
10733 // type: [private] String
10734 // type attribute on `<input>` node.
10735 // Users should not change this value.
10736 type: "radio",
10737
10738 _getRelatedWidgets: function(){
10739 // Private function needed to help iterate over all radio buttons in a group.
10740 var ary = [];
10741 query("input[type=radio]", this.focusNode.form || this.ownerDocument).forEach( // can't use name= since query doesn't support [] in the name
10742 lang.hitch(this, function(inputNode){
10743 if(inputNode.name == this.name && inputNode.form == this.focusNode.form){
10744 var widget = registry.getEnclosingWidget(inputNode);
10745 if(widget){
10746 ary.push(widget);
10747 }
10748 }
10749 })
10750 );
10751 return ary;
10752 },
10753
10754 _setCheckedAttr: function(/*Boolean*/ value){
10755 // If I am being checked then have to deselect currently checked radio button
10756 this.inherited(arguments);
10757 if(!this._created){ return; }
10758 if(value){
10759 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
10760 if(widget != this && widget.checked){
10761 widget.set('checked', false);
10762 }
10763 }));
10764 }
10765 },
10766
10767 _getSubmitValue: function(/*String*/ value){
10768 return value === null ? "on" : value;
10769 },
10770
10771 _onClick: function(/*Event*/ e){
10772 if(this.checked || this.disabled){ // nothing to do
10773 event.stop(e);
10774 return false;
10775 }
10776 if(this.readOnly){ // ignored by some browsers so we have to resync the DOM elements with widget values
10777 event.stop(e);
10778 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
10779 domAttr.set(this.focusNode || this.domNode, 'checked', widget.checked);
10780 }));
10781 return false;
10782 }
10783 return this.inherited(arguments);
10784 }
10785 });
10786 });
10787
10788 },
10789 'dojo/data/ItemFileWriteStore':function(){
10790 define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/kernel",
10791 "./ItemFileReadStore", "../date/stamp"
10792 ], function(lang, declare, arrayUtil, jsonUtil, kernel, ItemFileReadStore, dateStamp){
10793
10794 // module:
10795 // dojo/data/ItemFileWriteStore
10796
10797 return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore, {
10798 // summary:
10799 // TODOC
10800
10801 constructor: function(/* object */ keywordParameters){
10802 // keywordParameters:
10803 // The structure of the typeMap object is as follows:
10804 // | {
10805 // | type0: function || object,
10806 // | type1: function || object,
10807 // | ...
10808 // | typeN: function || object
10809 // | }
10810 // Where if it is a function, it is assumed to be an object constructor that takes the
10811 // value of _value as the initialization parameters. It is serialized assuming object.toString()
10812 // serialization. If it is an object, then it is assumed
10813 // to be an object of general form:
10814 // | {
10815 // | type: function, //constructor.
10816 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
10817 // | serialize: function(object) //The function that converts the object back into the proper file format form.
10818 // | }
10819
10820 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
10821 this._features['dojo.data.api.Write'] = true;
10822 this._features['dojo.data.api.Notification'] = true;
10823
10824 // For keeping track of changes so that we can implement isDirty and revert
10825 this._pending = {
10826 _newItems:{},
10827 _modifiedItems:{},
10828 _deletedItems:{}
10829 };
10830
10831 if(!this._datatypeMap['Date'].serialize){
10832 this._datatypeMap['Date'].serialize = function(obj){
10833 return dateStamp.toISOString(obj, {zulu:true});
10834 };
10835 }
10836 //Disable only if explicitly set to false.
10837 if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
10838 this.referenceIntegrity = false;
10839 }
10840
10841 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
10842 this._saveInProgress = false;
10843 },
10844
10845 referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
10846
10847 _assert: function(/* boolean */ condition){
10848 if(!condition){
10849 throw new Error("assertion failed in ItemFileWriteStore");
10850 }
10851 },
10852
10853 _getIdentifierAttribute: function(){
10854 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
10855 return this.getFeatures()['dojo.data.api.Identity'];
10856 },
10857
10858
10859 /* dojo/data/api/Write */
10860
10861 newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
10862 // summary:
10863 // See dojo/data/api/Write.newItem()
10864
10865 this._assert(!this._saveInProgress);
10866
10867 if(!this._loadFinished){
10868 // We need to do this here so that we'll be able to find out what
10869 // identifierAttribute was specified in the data file.
10870 this._forceLoad();
10871 }
10872
10873 if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
10874 throw new Error("newItem() was passed something other than an object");
10875 }
10876 var newIdentity = null;
10877 var identifierAttribute = this._getIdentifierAttribute();
10878 if(identifierAttribute === Number){
10879 newIdentity = this._arrayOfAllItems.length;
10880 }else{
10881 newIdentity = keywordArgs[identifierAttribute];
10882 if(typeof newIdentity === "undefined"){
10883 throw new Error("newItem() was not passed an identity for the new item");
10884 }
10885 if(lang.isArray(newIdentity)){
10886 throw new Error("newItem() was not passed an single-valued identity");
10887 }
10888 }
10889
10890 // make sure this identity is not already in use by another item, if identifiers were
10891 // defined in the file. Otherwise it would be the item count,
10892 // which should always be unique in this case.
10893 if(this._itemsByIdentity){
10894 this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
10895 }
10896 this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
10897 this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
10898
10899 var newItem = {};
10900 newItem[this._storeRefPropName] = this;
10901 newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
10902 if(this._itemsByIdentity){
10903 this._itemsByIdentity[newIdentity] = newItem;
10904 //We have to set the identifier now, otherwise we can't look it
10905 //up at calls to setValueorValues in parentInfo handling.
10906 newItem[identifierAttribute] = [newIdentity];
10907 }
10908 this._arrayOfAllItems.push(newItem);
10909
10910 //We need to construct some data for the onNew call too...
10911 var pInfo = null;
10912
10913 // Now we need to check to see where we want to assign this thingm if any.
10914 if(parentInfo && parentInfo.parent && parentInfo.attribute){
10915 pInfo = {
10916 item: parentInfo.parent,
10917 attribute: parentInfo.attribute,
10918 oldValue: undefined
10919 };
10920
10921 //See if it is multi-valued or not and handle appropriately
10922 //Generally, all attributes are multi-valued for this store
10923 //So, we only need to append if there are already values present.
10924 var values = this.getValues(parentInfo.parent, parentInfo.attribute);
10925 if(values && values.length > 0){
10926 var tempValues = values.slice(0, values.length);
10927 if(values.length === 1){
10928 pInfo.oldValue = values[0];
10929 }else{
10930 pInfo.oldValue = values.slice(0, values.length);
10931 }
10932 tempValues.push(newItem);
10933 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
10934 pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
10935 }else{
10936 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
10937 pInfo.newValue = newItem;
10938 }
10939 }else{
10940 //Toplevel item, add to both top list as well as all list.
10941 newItem[this._rootItemPropName]=true;
10942 this._arrayOfTopLevelItems.push(newItem);
10943 }
10944
10945 this._pending._newItems[newIdentity] = newItem;
10946
10947 //Clone over the properties to the new item
10948 for(var key in keywordArgs){
10949 if(key === this._storeRefPropName || key === this._itemNumPropName){
10950 // Bummer, the user is trying to do something like
10951 // newItem({_S:"foo"}). Unfortunately, our superclass,
10952 // ItemFileReadStore, is already using _S in each of our items
10953 // to hold private info. To avoid a naming collision, we
10954 // need to move all our private info to some other property
10955 // of all the items/objects. So, we need to iterate over all
10956 // the items and do something like:
10957 // item.__S = item._S;
10958 // item._S = undefined;
10959 // But first we have to make sure the new "__S" variable is
10960 // not in use, which means we have to iterate over all the
10961 // items checking for that.
10962 throw new Error("encountered bug in ItemFileWriteStore.newItem");
10963 }
10964 var value = keywordArgs[key];
10965 if(!lang.isArray(value)){
10966 value = [value];
10967 }
10968 newItem[key] = value;
10969 if(this.referenceIntegrity){
10970 for(var i = 0; i < value.length; i++){
10971 var val = value[i];
10972 if(this.isItem(val)){
10973 this._addReferenceToMap(val, newItem, key);
10974 }
10975 }
10976 }
10977 }
10978 this.onNew(newItem, pInfo); // dojo/data/api/Notification call
10979 return newItem; // item
10980 },
10981
10982 _removeArrayElement: function(/* Array */ array, /* anything */ element){
10983 var index = arrayUtil.indexOf(array, element);
10984 if(index != -1){
10985 array.splice(index, 1);
10986 return true;
10987 }
10988 return false;
10989 },
10990
10991 deleteItem: function(/* dojo/data/api/Item */ item){
10992 // summary:
10993 // See dojo/data/api/Write.deleteItem()
10994 this._assert(!this._saveInProgress);
10995 this._assertIsItem(item);
10996
10997 // Remove this item from the _arrayOfAllItems, but leave a null value in place
10998 // of the item, so as not to change the length of the array, so that in newItem()
10999 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
11000 var indexInArrayOfAllItems = item[this._itemNumPropName];
11001 var identity = this.getIdentity(item);
11002
11003 //If we have reference integrity on, we need to do reference cleanup for the deleted item
11004 if(this.referenceIntegrity){
11005 //First scan all the attributes of this items for references and clean them up in the map
11006 //As this item is going away, no need to track its references anymore.
11007
11008 //Get the attributes list before we generate the backup so it
11009 //doesn't pollute the attributes list.
11010 var attributes = this.getAttributes(item);
11011
11012 //Backup the map, we'll have to restore it potentially, in a revert.
11013 if(item[this._reverseRefMap]){
11014 item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
11015 }
11016
11017 //TODO: This causes a reversion problem. This list won't be restored on revert since it is
11018 //attached to the 'value'. item, not ours. Need to back tese up somehow too.
11019 //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
11020 //later. Or just record them and call _addReferenceToMap on them in revert.
11021 arrayUtil.forEach(attributes, function(attribute){
11022 arrayUtil.forEach(this.getValues(item, attribute), function(value){
11023 if(this.isItem(value)){
11024 //We have to back up all the references we had to others so they can be restored on a revert.
11025 if(!item["backupRefs_" + this._reverseRefMap]){
11026 item["backupRefs_" + this._reverseRefMap] = [];
11027 }
11028 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
11029 this._removeReferenceFromMap(value, item, attribute);
11030 }
11031 }, this);
11032 }, this);
11033
11034 //Next, see if we have references to this item, if we do, we have to clean them up too.
11035 var references = item[this._reverseRefMap];
11036 if(references){
11037 //Look through all the items noted as references to clean them up.
11038 for(var itemId in references){
11039 var containingItem = null;
11040 if(this._itemsByIdentity){
11041 containingItem = this._itemsByIdentity[itemId];
11042 }else{
11043 containingItem = this._arrayOfAllItems[itemId];
11044 }
11045 //We have a reference to a containing item, now we have to process the
11046 //attributes and clear all references to the item being deleted.
11047 if(containingItem){
11048 for(var attribute in references[itemId]){
11049 var oldValues = this.getValues(containingItem, attribute) || [];
11050 var newValues = arrayUtil.filter(oldValues, function(possibleItem){
11051 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
11052 }, this);
11053 //Remove the note of the reference to the item and set the values on the modified attribute.
11054 this._removeReferenceFromMap(item, containingItem, attribute);
11055 if(newValues.length < oldValues.length){
11056 this._setValueOrValues(containingItem, attribute, newValues, true);
11057 }
11058 }
11059 }
11060 }
11061 }
11062 }
11063
11064 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
11065
11066 item[this._storeRefPropName] = null;
11067 if(this._itemsByIdentity){
11068 delete this._itemsByIdentity[identity];
11069 }
11070 this._pending._deletedItems[identity] = item;
11071
11072 //Remove from the toplevel items, if necessary...
11073 if(item[this._rootItemPropName]){
11074 this._removeArrayElement(this._arrayOfTopLevelItems, item);
11075 }
11076 this.onDelete(item); // dojo/data/api/Notification call
11077 return true;
11078 },
11079
11080 setValue: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
11081 // summary:
11082 // See dojo/data/api/Write.set()
11083 return this._setValueOrValues(item, attribute, value, true); // boolean
11084 },
11085
11086 setValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* array */ values){
11087 // summary:
11088 // See dojo/data/api/Write.setValues()
11089 return this._setValueOrValues(item, attribute, values, true); // boolean
11090 },
11091
11092 unsetAttribute: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
11093 // summary:
11094 // See dojo/data/api/Write.unsetAttribute()
11095 return this._setValueOrValues(item, attribute, [], true);
11096 },
11097
11098 _setValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
11099 this._assert(!this._saveInProgress);
11100
11101 // Check for valid arguments
11102 this._assertIsItem(item);
11103 this._assert(lang.isString(attribute));
11104 this._assert(typeof newValueOrValues !== "undefined");
11105
11106 // Make sure the user isn't trying to change the item's identity
11107 var identifierAttribute = this._getIdentifierAttribute();
11108 if(attribute == identifierAttribute){
11109 throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
11110 }
11111
11112 // To implement the Notification API, we need to make a note of what
11113 // the old attribute value was, so that we can pass that info when
11114 // we call the onSet method.
11115 var oldValueOrValues = this._getValueOrValues(item, attribute);
11116
11117 var identity = this.getIdentity(item);
11118 if(!this._pending._modifiedItems[identity]){
11119 // Before we actually change the item, we make a copy of it to
11120 // record the original state, so that we'll be able to revert if
11121 // the revert method gets called. If the item has already been
11122 // modified then there's no need to do this now, since we already
11123 // have a record of the original state.
11124 var copyOfItemState = {};
11125 for(var key in item){
11126 if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
11127 copyOfItemState[key] = item[key];
11128 }else if(key === this._reverseRefMap){
11129 copyOfItemState[key] = lang.clone(item[key]);
11130 }else{
11131 copyOfItemState[key] = item[key].slice(0, item[key].length);
11132 }
11133 }
11134 // Now mark the item as dirty, and save the copy of the original state
11135 this._pending._modifiedItems[identity] = copyOfItemState;
11136 }
11137
11138 // Okay, now we can actually change this attribute on the item
11139 var success = false;
11140
11141 if(lang.isArray(newValueOrValues) && newValueOrValues.length === 0){
11142
11143 // If we were passed an empty array as the value, that counts
11144 // as "unsetting" the attribute, so we need to remove this
11145 // attribute from the item.
11146 success = delete item[attribute];
11147 newValueOrValues = undefined; // used in the onSet Notification call below
11148
11149 if(this.referenceIntegrity && oldValueOrValues){
11150 var oldValues = oldValueOrValues;
11151 if(!lang.isArray(oldValues)){
11152 oldValues = [oldValues];
11153 }
11154 for(var i = 0; i < oldValues.length; i++){
11155 var value = oldValues[i];
11156 if(this.isItem(value)){
11157 this._removeReferenceFromMap(value, item, attribute);
11158 }
11159 }
11160 }
11161 }else{
11162 var newValueArray;
11163 if(lang.isArray(newValueOrValues)){
11164 // Unfortunately, it's not safe to just do this:
11165 // newValueArray = newValueOrValues;
11166 // Instead, we need to copy the array, which slice() does very nicely.
11167 // This is so that our internal data structure won't
11168 // get corrupted if the user mucks with the values array *after*
11169 // calling setValues().
11170 newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
11171 }else{
11172 newValueArray = [newValueOrValues];
11173 }
11174
11175 //We need to handle reference integrity if this is on.
11176 //In the case of set, we need to see if references were added or removed
11177 //and update the reference tracking map accordingly.
11178 if(this.referenceIntegrity){
11179 if(oldValueOrValues){
11180 var oldValues = oldValueOrValues;
11181 if(!lang.isArray(oldValues)){
11182 oldValues = [oldValues];
11183 }
11184 //Use an associative map to determine what was added/removed from the list.
11185 //Should be O(n) performant. First look at all the old values and make a list of them
11186 //Then for any item not in the old list, we add it. If it was already present, we remove it.
11187 //Then we pass over the map and any references left it it need to be removed (IE, no match in
11188 //the new values list).
11189 var map = {};
11190 arrayUtil.forEach(oldValues, function(possibleItem){
11191 if(this.isItem(possibleItem)){
11192 var id = this.getIdentity(possibleItem);
11193 map[id.toString()] = true;
11194 }
11195 }, this);
11196 arrayUtil.forEach(newValueArray, function(possibleItem){
11197 if(this.isItem(possibleItem)){
11198 var id = this.getIdentity(possibleItem);
11199 if(map[id.toString()]){
11200 delete map[id.toString()];
11201 }else{
11202 this._addReferenceToMap(possibleItem, item, attribute);
11203 }
11204 }
11205 }, this);
11206 for(var rId in map){
11207 var removedItem;
11208 if(this._itemsByIdentity){
11209 removedItem = this._itemsByIdentity[rId];
11210 }else{
11211 removedItem = this._arrayOfAllItems[rId];
11212 }
11213 this._removeReferenceFromMap(removedItem, item, attribute);
11214 }
11215 }else{
11216 //Everything is new (no old values) so we have to just
11217 //insert all the references, if any.
11218 for(var i = 0; i < newValueArray.length; i++){
11219 var value = newValueArray[i];
11220 if(this.isItem(value)){
11221 this._addReferenceToMap(value, item, attribute);
11222 }
11223 }
11224 }
11225 }
11226 item[attribute] = newValueArray;
11227 success = true;
11228 }
11229
11230 // Now we make the dojo/data/api/Notification call
11231 if(callOnSet){
11232 this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
11233 }
11234 return success; // boolean
11235 },
11236
11237 _addReferenceToMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
11238 // summary:
11239 // Method to add an reference map entry for an item and attribute.
11240 // description:
11241 // Method to add an reference map entry for an item and attribute.
11242 // refItem:
11243 // The item that is referenced.
11244 // parentItem:
11245 // The item that holds the new reference to refItem.
11246 // attribute:
11247 // The attribute on parentItem that contains the new reference.
11248
11249 var parentId = this.getIdentity(parentItem);
11250 var references = refItem[this._reverseRefMap];
11251
11252 if(!references){
11253 references = refItem[this._reverseRefMap] = {};
11254 }
11255 var itemRef = references[parentId];
11256 if(!itemRef){
11257 itemRef = references[parentId] = {};
11258 }
11259 itemRef[attribute] = true;
11260 },
11261
11262 _removeReferenceFromMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
11263 // summary:
11264 // Method to remove an reference map entry for an item and attribute.
11265 // description:
11266 // Method to remove an reference map entry for an item and attribute. This will
11267 // also perform cleanup on the map such that if there are no more references at all to
11268 // the item, its reference object and entry are removed.
11269 // refItem:
11270 // The item that is referenced.
11271 // parentItem:
11272 // The item holding a reference to refItem.
11273 // attribute:
11274 // The attribute on parentItem that contains the reference.
11275 var identity = this.getIdentity(parentItem);
11276 var references = refItem[this._reverseRefMap];
11277 var itemId;
11278 if(references){
11279 for(itemId in references){
11280 if(itemId == identity){
11281 delete references[itemId][attribute];
11282 if(this._isEmpty(references[itemId])){
11283 delete references[itemId];
11284 }
11285 }
11286 }
11287 if(this._isEmpty(references)){
11288 delete refItem[this._reverseRefMap];
11289 }
11290 }
11291 },
11292
11293 _dumpReferenceMap: function(){
11294 // summary:
11295 // Function to dump the reverse reference map of all items in the store for debug purposes.
11296 // description:
11297 // Function to dump the reverse reference map of all items in the store for debug purposes.
11298 var i;
11299 for(i = 0; i < this._arrayOfAllItems.length; i++){
11300 var item = this._arrayOfAllItems[i];
11301 if(item && item[this._reverseRefMap]){
11302 console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + jsonUtil.toJson(item[this._reverseRefMap]));
11303 }
11304 }
11305 },
11306
11307 _getValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
11308 var valueOrValues = undefined;
11309 if(this.hasAttribute(item, attribute)){
11310 var valueArray = this.getValues(item, attribute);
11311 if(valueArray.length == 1){
11312 valueOrValues = valueArray[0];
11313 }else{
11314 valueOrValues = valueArray;
11315 }
11316 }
11317 return valueOrValues;
11318 },
11319
11320 _flatten: function(/* anything */ value){
11321 if(this.isItem(value)){
11322 // Given an item, return an serializable object that provides a
11323 // reference to the item.
11324 // For example, given kermit:
11325 // var kermit = store.newItem({id:2, name:"Kermit"});
11326 // we want to return
11327 // {_reference:2}
11328 return {_reference: this.getIdentity(value)};
11329 }else{
11330 if(typeof value === "object"){
11331 for(var type in this._datatypeMap){
11332 var typeMap = this._datatypeMap[type];
11333 if(lang.isObject(typeMap) && !lang.isFunction(typeMap)){
11334 if(value instanceof typeMap.type){
11335 if(!typeMap.serialize){
11336 throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
11337 }
11338 return {_type: type, _value: typeMap.serialize(value)};
11339 }
11340 }else if(value instanceof typeMap){
11341 //SImple mapping, therefore, return as a toString serialization.
11342 return {_type: type, _value: value.toString()};
11343 }
11344 }
11345 }
11346 return value;
11347 }
11348 },
11349
11350 _getNewFileContentString: function(){
11351 // summary:
11352 // Generate a string that can be saved to a file.
11353 // The result should look similar to:
11354 // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
11355 var serializableStructure = {};
11356
11357 var identifierAttribute = this._getIdentifierAttribute();
11358 if(identifierAttribute !== Number){
11359 serializableStructure.identifier = identifierAttribute;
11360 }
11361 if(this._labelAttr){
11362 serializableStructure.label = this._labelAttr;
11363 }
11364 serializableStructure.items = [];
11365 for(var i = 0; i < this._arrayOfAllItems.length; ++i){
11366 var item = this._arrayOfAllItems[i];
11367 if(item !== null){
11368 var serializableItem = {};
11369 for(var key in item){
11370 if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
11371 var valueArray = this.getValues(item, key);
11372 if(valueArray.length == 1){
11373 serializableItem[key] = this._flatten(valueArray[0]);
11374 }else{
11375 var serializableArray = [];
11376 for(var j = 0; j < valueArray.length; ++j){
11377 serializableArray.push(this._flatten(valueArray[j]));
11378 serializableItem[key] = serializableArray;
11379 }
11380 }
11381 }
11382 }
11383 serializableStructure.items.push(serializableItem);
11384 }
11385 }
11386 var prettyPrint = true;
11387 return jsonUtil.toJson(serializableStructure, prettyPrint);
11388 },
11389
11390 _isEmpty: function(something){
11391 // summary:
11392 // Function to determine if an array or object has no properties or values.
11393 // something:
11394 // The array or object to examine.
11395 var empty = true;
11396 if(lang.isObject(something)){
11397 var i;
11398 for(i in something){
11399 empty = false;
11400 break;
11401 }
11402 }else if(lang.isArray(something)){
11403 if(something.length > 0){
11404 empty = false;
11405 }
11406 }
11407 return empty; //boolean
11408 },
11409
11410 save: function(/* object */ keywordArgs){
11411 // summary:
11412 // See dojo/data/api/Write.save()
11413 this._assert(!this._saveInProgress);
11414
11415 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
11416 this._saveInProgress = true;
11417
11418 var self = this;
11419 var saveCompleteCallback = function(){
11420 self._pending = {
11421 _newItems:{},
11422 _modifiedItems:{},
11423 _deletedItems:{}
11424 };
11425
11426 self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
11427 if(keywordArgs && keywordArgs.onComplete){
11428 var scope = keywordArgs.scope || kernel.global;
11429 keywordArgs.onComplete.call(scope);
11430 }
11431 };
11432 var saveFailedCallback = function(err){
11433 self._saveInProgress = false;
11434 if(keywordArgs && keywordArgs.onError){
11435 var scope = keywordArgs.scope || kernel.global;
11436 keywordArgs.onError.call(scope, err);
11437 }
11438 };
11439
11440 if(this._saveEverything){
11441 var newFileContentString = this._getNewFileContentString();
11442 this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
11443 }
11444 if(this._saveCustom){
11445 this._saveCustom(saveCompleteCallback, saveFailedCallback);
11446 }
11447 if(!this._saveEverything && !this._saveCustom){
11448 // Looks like there is no user-defined save-handler function.
11449 // That's fine, it just means the datastore is acting as a "mock-write"
11450 // store -- changes get saved in memory but don't get saved to disk.
11451 saveCompleteCallback();
11452 }
11453 },
11454
11455 revert: function(){
11456 // summary:
11457 // See dojo/data/api/Write.revert()
11458 this._assert(!this._saveInProgress);
11459
11460 var identity;
11461 for(identity in this._pending._modifiedItems){
11462 // find the original item and the modified item that replaced it
11463 var copyOfItemState = this._pending._modifiedItems[identity];
11464 var modifiedItem = null;
11465 if(this._itemsByIdentity){
11466 modifiedItem = this._itemsByIdentity[identity];
11467 }else{
11468 modifiedItem = this._arrayOfAllItems[identity];
11469 }
11470
11471 // Restore the original item into a full-fledged item again, we want to try to
11472 // keep the same object instance as if we don't it, causes bugs like #9022.
11473 copyOfItemState[this._storeRefPropName] = this;
11474 for(var key in modifiedItem){
11475 delete modifiedItem[key];
11476 }
11477 lang.mixin(modifiedItem, copyOfItemState);
11478 }
11479 var deletedItem;
11480 for(identity in this._pending._deletedItems){
11481 deletedItem = this._pending._deletedItems[identity];
11482 deletedItem[this._storeRefPropName] = this;
11483 var index = deletedItem[this._itemNumPropName];
11484
11485 //Restore the reverse refererence map, if any.
11486 if(deletedItem["backup_" + this._reverseRefMap]){
11487 deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
11488 delete deletedItem["backup_" + this._reverseRefMap];
11489 }
11490 this._arrayOfAllItems[index] = deletedItem;
11491 if(this._itemsByIdentity){
11492 this._itemsByIdentity[identity] = deletedItem;
11493 }
11494 if(deletedItem[this._rootItemPropName]){
11495 this._arrayOfTopLevelItems.push(deletedItem);
11496 }
11497 }
11498 //We have to pass through it again and restore the reference maps after all the
11499 //undeletes have occurred.
11500 for(identity in this._pending._deletedItems){
11501 deletedItem = this._pending._deletedItems[identity];
11502 if(deletedItem["backupRefs_" + this._reverseRefMap]){
11503 arrayUtil.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
11504 var refItem;
11505 if(this._itemsByIdentity){
11506 refItem = this._itemsByIdentity[reference.id];
11507 }else{
11508 refItem = this._arrayOfAllItems[reference.id];
11509 }
11510 this._addReferenceToMap(refItem, deletedItem, reference.attr);
11511 }, this);
11512 delete deletedItem["backupRefs_" + this._reverseRefMap];
11513 }
11514 }
11515
11516 for(identity in this._pending._newItems){
11517 var newItem = this._pending._newItems[identity];
11518 newItem[this._storeRefPropName] = null;
11519 // null out the new item, but don't change the array index so
11520 // so we can keep using _arrayOfAllItems.length.
11521 this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
11522 if(newItem[this._rootItemPropName]){
11523 this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
11524 }
11525 if(this._itemsByIdentity){
11526 delete this._itemsByIdentity[identity];
11527 }
11528 }
11529
11530 this._pending = {
11531 _newItems:{},
11532 _modifiedItems:{},
11533 _deletedItems:{}
11534 };
11535 return true; // boolean
11536 },
11537
11538 isDirty: function(/* item? */ item){
11539 // summary:
11540 // See dojo/data/api/Write.isDirty()
11541 if(item){
11542 // return true if the item is dirty
11543 var identity = this.getIdentity(item);
11544 return new Boolean(this._pending._newItems[identity] ||
11545 this._pending._modifiedItems[identity] ||
11546 this._pending._deletedItems[identity]).valueOf(); // boolean
11547 }else{
11548 // return true if the store is dirty -- which means return true
11549 // if there are any new items, dirty items, or modified items
11550 return !this._isEmpty(this._pending._newItems) ||
11551 !this._isEmpty(this._pending._modifiedItems) ||
11552 !this._isEmpty(this._pending._deletedItems); // boolean
11553 }
11554 },
11555
11556 /* dojo/data/api/Notification */
11557
11558 onSet: function(/* dojo/data/api/Item */ item,
11559 /*attribute-name-string*/ attribute,
11560 /*object|array*/ oldValue,
11561 /*object|array*/ newValue){
11562 // summary:
11563 // See dojo/data/api/Notification.onSet()
11564
11565 // No need to do anything. This method is here just so that the
11566 // client code can connect observers to it.
11567 },
11568
11569 onNew: function(/* dojo/data/api/Item */ newItem, /*object?*/ parentInfo){
11570 // summary:
11571 // See dojo/data/api/Notification.onNew()
11572
11573 // No need to do anything. This method is here just so that the
11574 // client code can connect observers to it.
11575 },
11576
11577 onDelete: function(/* dojo/data/api/Item */ deletedItem){
11578 // summary:
11579 // See dojo/data/api/Notification.onDelete()
11580
11581 // No need to do anything. This method is here just so that the
11582 // client code can connect observers to it.
11583 },
11584
11585 close: function(/* object? */ request){
11586 // summary:
11587 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11588 // description:
11589 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11590 // If the store is still dirty (unsaved changes), then an error will be thrown instead of
11591 // clearing the internal state for reload from the url.
11592
11593 //Clear if not dirty ... or throw an error
11594 if(this.clearOnClose){
11595 if(!this.isDirty()){
11596 this.inherited(arguments);
11597 }else{
11598 //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
11599 throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
11600 }
11601 }
11602 }
11603 });
11604
11605 });
11606
11607 },
11608 '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",
11609 'dojo/dnd/TimedMoveable':function(){
11610 define("dojo/dnd/TimedMoveable", ["../_base/declare", "./Moveable" /*=====, "./Mover" =====*/], function(declare, Moveable /*=====, Mover =====*/){
11611 // module:
11612 // dojo/dnd/TimedMoveable
11613
11614 /*=====
11615 var __TimedMoveableArgs = declare([Moveable.__MoveableArgs], {
11616 // timeout: Number
11617 // delay move by this number of ms,
11618 // accumulating position changes during the timeout
11619 timeout: 0
11620 });
11621 =====*/
11622
11623 // precalculate long expressions
11624 var oldOnMove = Moveable.prototype.onMove;
11625
11626 return declare("dojo.dnd.TimedMoveable", Moveable, {
11627 // summary:
11628 // A specialized version of Moveable to support an FPS throttling.
11629 // This class puts an upper restriction on FPS, which may reduce
11630 // the CPU load. The additional parameter "timeout" regulates
11631 // the delay before actually moving the moveable object.
11632
11633 // object attributes (for markup)
11634 timeout: 40, // in ms, 40ms corresponds to 25 fps
11635
11636 constructor: function(node, params){
11637 // summary:
11638 // an object that makes a node moveable with a timer
11639 // node: Node||String
11640 // a node (or node's id) to be moved
11641 // params: __TimedMoveableArgs
11642 // object with additional parameters.
11643
11644 // sanitize parameters
11645 if(!params){ params = {}; }
11646 if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
11647 this.timeout = params.timeout;
11648 }
11649 },
11650
11651 onMoveStop: function(/*Mover*/ mover){
11652 if(mover._timer){
11653 // stop timer
11654 clearTimeout(mover._timer);
11655 // reflect the last received position
11656 oldOnMove.call(this, mover, mover._leftTop);
11657 }
11658 Moveable.prototype.onMoveStop.apply(this, arguments);
11659 },
11660 onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
11661 mover._leftTop = leftTop;
11662 if(!mover._timer){
11663 var _t = this; // to avoid using dojo.hitch()
11664 mover._timer = setTimeout(function(){
11665 // we don't have any pending requests
11666 mover._timer = null;
11667 // reflect the last received position
11668 oldOnMove.call(_t, mover, mover._leftTop);
11669 }, this.timeout);
11670 }
11671 }
11672 });
11673 });
11674
11675 },
11676 'dojo/NodeList-fx':function(){
11677 define("dojo/NodeList-fx", ["./query", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
11678 function(query, lang, connectLib, baseFx, coreFx){
11679
11680 // module:
11681 // dojo/NodeList-fx
11682
11683 /*=====
11684 return function(){
11685 // summary:
11686 // Adds dojo.fx animation support to dojo.query() by extending the NodeList class
11687 // with additional FX functions. NodeList is the array-like object used to hold query results.
11688 };
11689 =====*/
11690
11691 var NodeList = query.NodeList;
11692
11693 lang.extend(NodeList, {
11694 _anim: function(obj, method, args){
11695 args = args||{};
11696 var a = coreFx.combine(
11697 this.map(function(item){
11698 var tmpArgs = { node: item };
11699 lang.mixin(tmpArgs, args);
11700 return obj[method](tmpArgs);
11701 })
11702 );
11703 return args.auto ? a.play() && this : a; // dojo/_base/fx.Animation|dojo/NodeList
11704 },
11705
11706 wipeIn: function(args){
11707 // summary:
11708 // wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
11709 //
11710 // args: Object?
11711 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11712 // an `auto` parameter.
11713 //
11714 // returns: dojo/_base/fx.Animation|dojo/NodeList
11715 // A special args member `auto` can be passed to automatically play the animation.
11716 // If args.auto is present, the original dojo/NodeList will be returned for further
11717 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11718 //
11719 // example:
11720 // Fade in all tables with class "blah":
11721 // | dojo.query("table.blah").wipeIn().play();
11722 //
11723 // example:
11724 // Utilizing `auto` to get the NodeList back:
11725 // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
11726 //
11727 return this._anim(coreFx, "wipeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
11728 },
11729
11730 wipeOut: function(args){
11731 // summary:
11732 // wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
11733 //
11734 // args: Object?
11735 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11736 // an `auto` parameter.
11737 //
11738 // returns: dojo/_base/fx.Animation|dojo/NodeList
11739 // A special args member `auto` can be passed to automatically play the animation.
11740 // If args.auto is present, the original dojo/NodeList will be returned for further
11741 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11742 //
11743 // example:
11744 // Wipe out all tables with class "blah":
11745 // | dojo.query("table.blah").wipeOut().play();
11746 return this._anim(coreFx, "wipeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
11747 },
11748
11749 slideTo: function(args){
11750 // summary:
11751 // slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
11752 //
11753 // args: Object?
11754 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11755 // an `auto` parameter.
11756 //
11757 // returns: dojo/_base/fx.Animation|dojo/NodeList
11758 // A special args member `auto` can be passed to automatically play the animation.
11759 // If args.auto is present, the original dojo/NodeList will be returned for further
11760 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11761 //
11762 // example:
11763 // | Move all tables with class "blah" to 300/300:
11764 // | dojo.query("table.blah").slideTo({
11765 // | left: 40,
11766 // | top: 50
11767 // | }).play();
11768 return this._anim(coreFx, "slideTo", args); // dojo/_base/fx.Animation|dojo/NodeList
11769 },
11770
11771
11772 fadeIn: function(args){
11773 // summary:
11774 // fade in all elements of this NodeList via `dojo.fadeIn`
11775 //
11776 // args: Object?
11777 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11778 // an `auto` parameter.
11779 //
11780 // returns: dojo/_base/fx.Animation|dojo/NodeList
11781 // A special args member `auto` can be passed to automatically play the animation.
11782 // If args.auto is present, the original dojo/NodeList will be returned for further
11783 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11784 //
11785 // example:
11786 // Fade in all tables with class "blah":
11787 // | dojo.query("table.blah").fadeIn().play();
11788 return this._anim(baseFx, "fadeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
11789 },
11790
11791 fadeOut: function(args){
11792 // summary:
11793 // fade out all elements of this NodeList via `dojo.fadeOut`
11794 //
11795 // args: Object?
11796 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11797 // an `auto` parameter.
11798 //
11799 // returns: dojo/_base/fx.Animation|dojo/NodeList
11800 // A special args member `auto` can be passed to automatically play the animation.
11801 // If args.auto is present, the original dojo/NodeList will be returned for further
11802 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11803 //
11804 // example:
11805 // Fade out all elements with class "zork":
11806 // | dojo.query(".zork").fadeOut().play();
11807 // example:
11808 // Fade them on a delay and do something at the end:
11809 // | var fo = dojo.query(".zork").fadeOut();
11810 // | dojo.connect(fo, "onEnd", function(){ /*...*/ });
11811 // | fo.play();
11812 // example:
11813 // Using `auto`:
11814 // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
11815 //
11816 return this._anim(baseFx, "fadeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
11817 },
11818
11819 animateProperty: function(args){
11820 // summary:
11821 // Animate all elements of this NodeList across the properties specified.
11822 // syntax identical to `dojo.animateProperty`
11823 //
11824 // args: Object?
11825 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11826 // an `auto` parameter.
11827 //
11828 // returns: dojo/_base/fx.Animation|dojo/NodeList
11829 // A special args member `auto` can be passed to automatically play the animation.
11830 // If args.auto is present, the original dojo/NodeList will be returned for further
11831 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11832 //
11833 // example:
11834 // | dojo.query(".zork").animateProperty({
11835 // | duration: 500,
11836 // | properties: {
11837 // | color: { start: "black", end: "white" },
11838 // | left: { end: 300 }
11839 // | }
11840 // | }).play();
11841 //
11842 // example:
11843 // | dojo.query(".grue").animateProperty({
11844 // | auto:true,
11845 // | properties: {
11846 // | height:240
11847 // | }
11848 // | }).onclick(handler);
11849 return this._anim(baseFx, "animateProperty", args); // dojo/_base/fx.Animation|dojo/NodeList
11850 },
11851
11852 anim: function( /*Object*/ properties,
11853 /*Integer?*/ duration,
11854 /*Function?*/ easing,
11855 /*Function?*/ onEnd,
11856 /*Integer?*/ delay){
11857 // summary:
11858 // Animate one or more CSS properties for all nodes in this list.
11859 // The returned animation object will already be playing when it
11860 // is returned. See the docs for `dojo.anim` for full details.
11861 // properties: Object
11862 // the properties to animate. does NOT support the `auto` parameter like other
11863 // NodeList-fx methods.
11864 // duration: Integer?
11865 // Optional. The time to run the animations for
11866 // easing: Function?
11867 // Optional. The easing function to use.
11868 // onEnd: Function?
11869 // A function to be called when the animation ends
11870 // delay:
11871 // how long to delay playing the returned animation
11872 // example:
11873 // Another way to fade out:
11874 // | dojo.query(".thinger").anim({ opacity: 0 });
11875 // example:
11876 // animate all elements with the "thigner" class to a width of 500
11877 // pixels over half a second
11878 // | dojo.query(".thinger").anim({ width: 500 }, 700);
11879 var canim = coreFx.combine(
11880 this.map(function(item){
11881 return baseFx.animateProperty({
11882 node: item,
11883 properties: properties,
11884 duration: duration||350,
11885 easing: easing
11886 });
11887 })
11888 );
11889 if(onEnd){
11890 connectLib.connect(canim, "onEnd", onEnd);
11891 }
11892 return canim.play(delay||0); // dojo/_base/fx.Animation
11893 }
11894 });
11895
11896 return NodeList;
11897 });
11898
11899 },
11900 'dijit/form/_ListMouseMixin':function(){
11901 define("dijit/form/_ListMouseMixin", [
11902 "dojo/_base/declare", // declare
11903 "dojo/mouse",
11904 "dojo/on",
11905 "dojo/touch",
11906 "./_ListBase"
11907 ], function(declare, mouse, on, touch, _ListBase){
11908
11909 // module:
11910 // dijit/form/_ListMouseMixin
11911
11912 return declare( "dijit.form._ListMouseMixin", _ListBase, {
11913 // summary:
11914 // a Mixin to handle mouse or touch events for a focus-less menu
11915 // Abstract methods that must be defined externally:
11916 //
11917 // - onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
11918 // tags:
11919 // private
11920
11921 postCreate: function(){
11922 this.inherited(arguments);
11923
11924 this.own(on(this.domNode, touch.press, function(evt){ evt.preventDefault(); })); // prevent focus shift on list scrollbar press
11925
11926 this._listConnect(touch.press, "_onMouseDown");
11927 this._listConnect(touch.release, "_onMouseUp");
11928 this._listConnect(mouse.enter, "_onMouseOver");
11929 this._listConnect(mouse.leave, "_onMouseOut");
11930 },
11931
11932 _onMouseDown: function(/*Event*/ evt, /*DomNode*/ target){
11933 if(this._hoveredNode){
11934 this.onUnhover(this._hoveredNode);
11935 this._hoveredNode = null;
11936 }
11937 this._isDragging = true;
11938 this._setSelectedAttr(target);
11939 },
11940
11941 _onMouseUp: function(/*Event*/ evt, /*DomNode*/ target){
11942 this._isDragging = false;
11943 var selectedNode = this.selected;
11944 var hoveredNode = this._hoveredNode;
11945 if(selectedNode && target == selectedNode){
11946 this.onClick(selectedNode);
11947 }else if(hoveredNode && target == hoveredNode){ // drag to select
11948 this._setSelectedAttr(hoveredNode);
11949 this.onClick(hoveredNode);
11950 }
11951 },
11952
11953 _onMouseOut: function(/*Event*/ evt, /*DomNode*/ target){
11954 if(this._hoveredNode){
11955 this.onUnhover(this._hoveredNode);
11956 this._hoveredNode = null;
11957 }
11958 if(this._isDragging){
11959 this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
11960 }
11961 },
11962
11963 _onMouseOver: function(/*Event*/ evt, /*DomNode*/ target){
11964 if(this._cancelDrag){
11965 var time = (new Date()).getTime();
11966 if(time > this._cancelDrag){
11967 this._isDragging = false;
11968 }
11969 this._cancelDrag = null;
11970 }
11971 this._hoveredNode = target;
11972 this.onHover(target);
11973 if(this._isDragging){
11974 this._setSelectedAttr(target);
11975 }
11976 }
11977 });
11978
11979 });
11980
11981 },
11982 '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",
11983 'dojo/cookie':function(){
11984 define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo, regexp){
11985
11986 // module:
11987 // dojo/cookie
11988
11989 /*=====
11990 var __cookieProps = {
11991 // expires: Date|String|Number?
11992 // If a number, the number of days from today at which the cookie
11993 // will expire. If a date, the date past which the cookie will expire.
11994 // If expires is in the past, the cookie will be deleted.
11995 // If expires is omitted or is 0, the cookie will expire when the browser closes.
11996 // path: String?
11997 // The path to use for the cookie.
11998 // domain: String?
11999 // The domain to use for the cookie.
12000 // secure: Boolean?
12001 // Whether to only send the cookie on secure connections
12002 };
12003 =====*/
12004
12005
12006 dojo.cookie = function(/*String*/name, /*String?*/ value, /*__cookieProps?*/ props){
12007 // summary:
12008 // Get or set a cookie.
12009 // description:
12010 // If one argument is passed, returns the value of the cookie
12011 // For two or more arguments, acts as a setter.
12012 // name:
12013 // Name of the cookie
12014 // value:
12015 // Value for the cookie
12016 // props:
12017 // Properties for the cookie
12018 // example:
12019 // set a cookie with the JSON-serialized contents of an object which
12020 // will expire 5 days from now:
12021 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12022 // | cookie("configObj", json.stringify(config, {expires: 5 }));
12023 // | });
12024 //
12025 // example:
12026 // de-serialize a cookie back into a JavaScript object:
12027 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12028 // | config = json.parse(cookie("configObj"));
12029 // | });
12030 //
12031 // example:
12032 // delete a cookie:
12033 // | require(["dojo/cookie"], function(cookie){
12034 // | cookie("configObj", null, {expires: -1});
12035 // | });
12036 var c = document.cookie, ret;
12037 if(arguments.length == 1){
12038 var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)"));
12039 ret = matches ? decodeURIComponent(matches[1]) : undefined;
12040 }else{
12041 props = props || {};
12042 // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
12043 var exp = props.expires;
12044 if(typeof exp == "number"){
12045 var d = new Date();
12046 d.setTime(d.getTime() + exp*24*60*60*1000);
12047 exp = props.expires = d;
12048 }
12049 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
12050
12051 value = encodeURIComponent(value);
12052 var updatedCookie = name + "=" + value, propName;
12053 for(propName in props){
12054 updatedCookie += "; " + propName;
12055 var propValue = props[propName];
12056 if(propValue !== true){ updatedCookie += "=" + propValue; }
12057 }
12058 document.cookie = updatedCookie;
12059 }
12060 return ret; // String|undefined
12061 };
12062
12063 dojo.cookie.isSupported = function(){
12064 // summary:
12065 // Use to determine if the current browser supports cookies or not.
12066 //
12067 // Returns true if user allows cookies.
12068 // Returns false if user doesn't allow cookies.
12069
12070 if(!("cookieEnabled" in navigator)){
12071 this("__djCookieTest__", "CookiesAllowed");
12072 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
12073 if(navigator.cookieEnabled){
12074 this("__djCookieTest__", "", {expires: -1});
12075 }
12076 }
12077 return navigator.cookieEnabled;
12078 };
12079
12080 return dojo.cookie;
12081 });
12082
12083 },
12084 'dojo/cache':function(){
12085 define("dojo/cache", ["./_base/kernel", "./text"], function(dojo){
12086 // module:
12087 // dojo/cache
12088
12089 // dojo.cache is defined in dojo/text
12090 return dojo.cache;
12091 });
12092
12093 },
12094 '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",
12095 'dijit/ProgressBar':function(){
12096 require({cache:{
12097 '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"}});
12098 define("dijit/ProgressBar", [
12099 "require", // require.toUrl
12100 "dojo/_base/declare", // declare
12101 "dojo/dom-class", // domClass.toggle
12102 "dojo/_base/lang", // lang.mixin
12103 "dojo/number", // number.format
12104 "./_Widget",
12105 "./_TemplatedMixin",
12106 "dojo/text!./templates/ProgressBar.html"
12107 ], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
12108
12109 // module:
12110 // dijit/ProgressBar
12111
12112
12113 return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
12114 // summary:
12115 // A progress indication widget, showing the amount completed
12116 // (often the percentage completed) of a task.
12117 //
12118 // example:
12119 // | <div data-dojo-type="ProgressBar"
12120 // | places="0"
12121 // | value="..." maximum="...">
12122 // | </div>
12123
12124 // progress: [const] String (Percentage or Number)
12125 // Number or percentage indicating amount of task completed.
12126 // Deprecated. Use "value" instead.
12127 progress: "0",
12128
12129 // value: String (Percentage or Number)
12130 // Number or percentage indicating amount of task completed.
12131 // With "%": percentage value, 0% <= progress <= 100%, or
12132 // without "%": absolute value, 0 <= progress <= maximum.
12133 // Infinity means that the progress bar is indeterminate.
12134 value: "",
12135
12136 // maximum: [const] Float
12137 // Max sample number
12138 maximum: 100,
12139
12140 // places: [const] Number
12141 // Number of places to show in values; 0 by default
12142 places: 0,
12143
12144 // indeterminate: [const] Boolean
12145 // If false: show progress value (number or percentage).
12146 // If true: show that a process is underway but that the amount completed is unknown.
12147 // Deprecated. Use "value" instead.
12148 indeterminate: false,
12149
12150 // label: String?
12151 // Label on progress bar. Defaults to percentage for determinate progress bar and
12152 // blank for indeterminate progress bar.
12153 label:"",
12154
12155 // name: String
12156 // this is the field name (for a form) if set. This needs to be set if you want to use
12157 // this widget in a dijit/form/Form widget (such as dijit/Dialog)
12158 name: '',
12159
12160 templateString: template,
12161
12162 // _indeterminateHighContrastImagePath: [private] URL
12163 // URL to image to use for indeterminate progress bar when display is in high contrast mode
12164 _indeterminateHighContrastImagePath:
12165 require.toUrl("./themes/a11y/indeterminate_progress.gif"),
12166
12167 postMixInProperties: function(){
12168 this.inherited(arguments);
12169
12170 // Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
12171 if(!(this.params && "value" in this.params)){
12172 this.value = this.indeterminate ? Infinity : this.progress;
12173 }
12174 },
12175
12176 buildRendering: function(){
12177 this.inherited(arguments);
12178 this.indeterminateHighContrastImage.setAttribute("src",
12179 this._indeterminateHighContrastImagePath.toString());
12180 this.update();
12181 },
12182
12183 update: function(/*Object?*/attributes){
12184 // summary:
12185 // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
12186 // set("value", ...) rather than calling this method directly.
12187 // attributes:
12188 // May provide progress and/or maximum properties on this parameter;
12189 // see attribute specs for details.
12190 // example:
12191 // | myProgressBar.update({'indeterminate': true});
12192 // | myProgressBar.update({'progress': 80});
12193 // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
12194 // tags:
12195 // private
12196
12197 // TODO: deprecate this method and use set() instead
12198
12199 lang.mixin(this, attributes || {});
12200 var tip = this.internalProgress, ap = this.domNode;
12201 var percent = 1;
12202 if(this.indeterminate){
12203 ap.removeAttribute("aria-valuenow");
12204 }else{
12205 if(String(this.progress).indexOf("%") != -1){
12206 percent = Math.min(parseFloat(this.progress)/100, 1);
12207 this.progress = percent * this.maximum;
12208 }else{
12209 this.progress = Math.min(this.progress, this.maximum);
12210 percent = this.maximum ? this.progress / this.maximum : 0;
12211 }
12212 ap.setAttribute("aria-valuenow", this.progress);
12213 }
12214
12215 // Even indeterminate ProgressBars should have these attributes
12216 ap.setAttribute("aria-describedby", this.labelNode.id);
12217 ap.setAttribute("aria-valuemin", 0);
12218 ap.setAttribute("aria-valuemax", this.maximum);
12219
12220 this.labelNode.innerHTML = this.report(percent);
12221
12222 domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
12223 tip.style.width = (percent * 100) + "%";
12224 this.onChange();
12225 },
12226
12227 _setValueAttr: function(v){
12228 this._set("value", v);
12229 if(v == Infinity){
12230 this.update({indeterminate:true});
12231 }else{
12232 this.update({indeterminate:false, progress:v});
12233 }
12234 },
12235
12236 _setLabelAttr: function(label){
12237 this._set("label", label);
12238 this.update();
12239 },
12240
12241 _setIndeterminateAttr: function(indeterminate){
12242 // Deprecated, use set("value", ...) instead
12243 this.indeterminate = indeterminate;
12244 this.update();
12245 },
12246
12247 report: function(/*float*/percent){
12248 // summary:
12249 // Generates message to show inside progress bar (normally indicating amount of task completed).
12250 // May be overridden.
12251 // tags:
12252 // extension
12253
12254 return this.label ? this.label :
12255 (this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
12256 },
12257
12258 onChange: function(){
12259 // summary:
12260 // Callback fired when progress updates.
12261 // tags:
12262 // extension
12263 }
12264 });
12265
12266 });
12267
12268 },
12269 'dijit/_base/popup':function(){
12270 define("dijit/_base/popup", [
12271 "dojo/dom-class", // domClass.contains
12272 "dojo/_base/window",
12273 "../popup",
12274 "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
12275 ], function(domClass, win, popup){
12276
12277 // module:
12278 // dijit/_base/popup
12279
12280 /*=====
12281 return {
12282 // summary:
12283 // Deprecated. Old module for popups, new code should use dijit/popup directly.
12284 };
12285 =====*/
12286
12287
12288 // Hack support for old API passing in node instead of a widget (to various methods)
12289 var origCreateWrapper = popup._createWrapper;
12290 popup._createWrapper = function(widget){
12291 if(!widget.declaredClass){
12292 // make fake widget to pass to new API
12293 widget = {
12294 _popupWrapper: (widget.parentNode && domClass.contains(widget.parentNode, "dijitPopup")) ?
12295 widget.parentNode : null,
12296 domNode: widget,
12297 destroy: function(){},
12298 ownerDocument: widget.ownerDocument,
12299 ownerDocumentBody: win.body(widget.ownerDocument)
12300 };
12301 }
12302 return origCreateWrapper.call(this, widget);
12303 };
12304
12305 // Support old format of orient parameter
12306 var origOpen = popup.open;
12307 popup.open = function(/*__OpenArgs*/ args){
12308 // Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
12309 // Don't do conversion for:
12310 // - null parameter (that means to use the default positioning)
12311 // - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
12312 // - new format, ex: ["below", "above"]
12313 // - return value from deprecated dijit.getPopupAroundAlignment() method,
12314 // ex: ["below", "above"]
12315 if(args.orient && typeof args.orient != "string" && !("length" in args.orient)){
12316 var ary = [];
12317 for(var key in args.orient){
12318 ary.push({aroundCorner: key, corner: args.orient[key]});
12319 }
12320 args.orient = ary;
12321 }
12322
12323 return origOpen.call(this, args);
12324 };
12325
12326 return popup;
12327 });
12328
12329 },
12330 'dijit/ColorPalette':function(){
12331 require({cache:{
12332 '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"}});
12333 define("dijit/ColorPalette", [
12334 "require", // require.toUrl
12335 "dojo/text!./templates/ColorPalette.html",
12336 "./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
12337 "./_TemplatedMixin",
12338 "./_PaletteMixin",
12339 "./hccss", // has("highcontrast")
12340 "dojo/i18n", // i18n.getLocalization
12341 "dojo/_base/Color", // dojo.Color dojo.Color.named
12342 "dojo/_base/declare", // declare
12343 "dojo/dom-construct", // domConstruct.place
12344 "dojo/string", // string.substitute
12345 "dojo/i18n!dojo/nls/colors", // translations
12346 "dojo/colors" // extend dojo.Color w/names of other colors
12347 ], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, has, i18n, Color,
12348 declare, domConstruct, string){
12349
12350 // module:
12351 // dijit/ColorPalette
12352
12353 var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
12354 // summary:
12355 // A keyboard accessible color-picking widget
12356 // description:
12357 // Grid showing various colors, so the user can pick a certain color.
12358 // Can be used standalone, or as a popup.
12359 //
12360 // example:
12361 // | <div data-dojo-type="dijit/ColorPalette"></div>
12362 //
12363 // example:
12364 // | var picker = new dijit.ColorPalette({ },srcNode);
12365 // | picker.startup();
12366
12367
12368 // palette: [const] String
12369 // Size of grid, either "7x10" or "3x4".
12370 palette: "7x10",
12371
12372 // _palettes: [protected] Map
12373 // This represents the value of the colors.
12374 // The first level is a hashmap of the different palettes available.
12375 // The next two dimensions represent the columns and rows of colors.
12376 _palettes: {
12377 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
12378 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
12379 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
12380 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
12381 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
12382 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
12383 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
12384
12385 "3x4": [["white", "lime", "green", "blue"],
12386 ["silver", "yellow", "fuchsia", "navy"],
12387 ["gray", "red", "purple", "black"]]
12388 },
12389
12390 // templateString: String
12391 // The template of this widget.
12392 templateString: template,
12393
12394 baseClass: "dijitColorPalette",
12395
12396 _dyeFactory: function(value, row, col, title){
12397 // Overrides _PaletteMixin._dyeFactory().
12398 return new this._dyeClass(value, row, col, title);
12399 },
12400
12401 buildRendering: function(){
12402 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
12403 // <img> nodes
12404 this.inherited(arguments);
12405
12406 // Creates customized constructor for dye class (color of a single cell) for
12407 // specified palette and high-contrast vs. normal mode. Used in _getDye().
12408 this._dyeClass = declare(ColorPalette._Color, {
12409 palette: this.palette
12410 });
12411
12412 // Creates <img> nodes in each cell of the template.
12413 this._preparePalette(
12414 this._palettes[this.palette],
12415 i18n.getLocalization("dojo", "colors", this.lang));
12416 }
12417 });
12418
12419 ColorPalette._Color = declare("dijit._Color", Color, {
12420 // summary:
12421 // Object associated with each cell in a ColorPalette palette.
12422 // Implements dijit/Dye.
12423
12424 // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
12425 // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
12426 // for showing the color.
12427 template:
12428 "<span class='dijitInline dijitPaletteImg'>" +
12429 "<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
12430 "</span>",
12431
12432 // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
12433 // but scrolled and clipped to show the correct color only
12434 hcTemplate:
12435 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
12436 "<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
12437 "</span>",
12438
12439 // _imagePaths: [protected] Map
12440 // This is stores the path to the palette images used for high-contrast mode display
12441 _imagePaths: {
12442 "7x10": require.toUrl("./themes/a11y/colors7x10.png"),
12443 "3x4": require.toUrl("./themes/a11y/colors3x4.png")
12444 },
12445
12446 constructor: function(alias, row, col, title){
12447 // summary:
12448 // Constructor for ColorPalette._Color
12449 // alias: String
12450 // English name of the color.
12451 // row: Number
12452 // Vertical position in grid.
12453 // column: Number
12454 // Horizontal position in grid.
12455 // title: String
12456 // Localized name of the color.
12457 this._title = title;
12458 this._row = row;
12459 this._col = col;
12460 this.setColor(Color.named[alias]);
12461 },
12462
12463 getValue: function(){
12464 // summary:
12465 // Note that although dijit._Color is initialized with a value like "white" getValue() always
12466 // returns a hex value
12467 return this.toHex();
12468 },
12469
12470 fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
12471 var html = string.substitute(has("highcontrast") ? this.hcTemplate : this.template, {
12472 // substitution variables for normal mode
12473 color: this.toHex(),
12474 blankGif: blankGif,
12475 alt: this._title,
12476 title: this._title,
12477
12478 // variables used for high contrast mode
12479 image: this._imagePaths[this.palette].toString(),
12480 left: this._col * -20 - 5,
12481 top: this._row * -20 - 5,
12482 size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
12483 });
12484
12485 domConstruct.place(html, cell);
12486 }
12487 });
12488
12489
12490 return ColorPalette;
12491 });
12492
12493 },
12494 '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",
12495 'dojo/_base/url':function(){
12496 define("dojo/_base/url", ["./kernel"], function(dojo){
12497 // module:
12498 // dojo/url
12499
12500 var
12501 ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
12502 ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
12503 _Url = function(){
12504 var n = null,
12505 _a = arguments,
12506 uri = [_a[0]];
12507 // resolve uri components relative to each other
12508 for(var i = 1; i<_a.length; i++){
12509 if(!_a[i]){ continue; }
12510
12511 // Safari doesn't support this.constructor so we have to be explicit
12512 // FIXME: Tracked (and fixed) in Webkit bug 3537.
12513 // http://bugs.webkit.org/show_bug.cgi?id=3537
12514 var relobj = new _Url(_a[i]+""),
12515 uriobj = new _Url(uri[0]+"");
12516
12517 if(
12518 relobj.path == "" &&
12519 !relobj.scheme &&
12520 !relobj.authority &&
12521 !relobj.query
12522 ){
12523 if(relobj.fragment != n){
12524 uriobj.fragment = relobj.fragment;
12525 }
12526 relobj = uriobj;
12527 }else if(!relobj.scheme){
12528 relobj.scheme = uriobj.scheme;
12529
12530 if(!relobj.authority){
12531 relobj.authority = uriobj.authority;
12532
12533 if(relobj.path.charAt(0) != "/"){
12534 var path = uriobj.path.substring(0,
12535 uriobj.path.lastIndexOf("/") + 1) + relobj.path;
12536
12537 var segs = path.split("/");
12538 for(var j = 0; j < segs.length; j++){
12539 if(segs[j] == "."){
12540 // flatten "./" references
12541 if(j == segs.length - 1){
12542 segs[j] = "";
12543 }else{
12544 segs.splice(j, 1);
12545 j--;
12546 }
12547 }else if(j > 0 && !(j == 1 && segs[0] == "") &&
12548 segs[j] == ".." && segs[j-1] != ".."){
12549 // flatten "../" references
12550 if(j == (segs.length - 1)){
12551 segs.splice(j, 1);
12552 segs[j - 1] = "";
12553 }else{
12554 segs.splice(j - 1, 2);
12555 j -= 2;
12556 }
12557 }
12558 }
12559 relobj.path = segs.join("/");
12560 }
12561 }
12562 }
12563
12564 uri = [];
12565 if(relobj.scheme){
12566 uri.push(relobj.scheme, ":");
12567 }
12568 if(relobj.authority){
12569 uri.push("//", relobj.authority);
12570 }
12571 uri.push(relobj.path);
12572 if(relobj.query){
12573 uri.push("?", relobj.query);
12574 }
12575 if(relobj.fragment){
12576 uri.push("#", relobj.fragment);
12577 }
12578 }
12579
12580 this.uri = uri.join("");
12581
12582 // break the uri into its main components
12583 var r = this.uri.match(ore);
12584
12585 this.scheme = r[2] || (r[1] ? "" : n);
12586 this.authority = r[4] || (r[3] ? "" : n);
12587 this.path = r[5]; // can never be undefined
12588 this.query = r[7] || (r[6] ? "" : n);
12589 this.fragment = r[9] || (r[8] ? "" : n);
12590
12591 if(this.authority != n){
12592 // server based naming authority
12593 r = this.authority.match(ire);
12594
12595 this.user = r[3] || n;
12596 this.password = r[4] || n;
12597 this.host = r[6] || r[7]; // ipv6 || ipv4
12598 this.port = r[9] || n;
12599 }
12600 };
12601 _Url.prototype.toString = function(){ return this.uri; };
12602
12603 return dojo._Url = _Url;
12604 });
12605
12606 },
12607 '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",
12608 'dojo/text':function(){
12609 define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo, require, has, xhr){
12610 // module:
12611 // dojo/text
12612
12613 var getText;
12614 if( 1 ){
12615 getText= function(url, sync, load){
12616 xhr("GET", {url: url, sync:!!sync, load: load, headers: dojo.config.textPluginHeaders || {}});
12617 };
12618 }else{
12619 // TODOC: only works for dojo AMD loader
12620 if(require.getText){
12621 getText= require.getText;
12622 }else{
12623 console.error("dojo/text plugin failed to load because loader does not support getText");
12624 }
12625 }
12626
12627 var
12628 theCache = {},
12629
12630 strip= function(text){
12631 //Strips <?xml ...?> declarations so that external SVG and XML
12632 //documents can be added to a document without worry. Also, if the string
12633 //is an HTML document, only the part inside the body tag is returned.
12634 if(text){
12635 text= text.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
12636 var matches= text.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
12637 if(matches){
12638 text= matches[1];
12639 }
12640 }else{
12641 text = "";
12642 }
12643 return text;
12644 },
12645
12646 notFound = {},
12647
12648 pending = {};
12649
12650 dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
12651 // summary:
12652 // A getter and setter for storing the string content associated with the
12653 // module and url arguments.
12654 // description:
12655 // If module is a string that contains slashes, then it is interpretted as a fully
12656 // resolved path (typically a result returned by require.toUrl), and url should not be
12657 // provided. This is the preferred signature. If module is a string that does not
12658 // contain slashes, then url must also be provided and module and url are used to
12659 // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
12660 // If value is specified, the cache value for the moduleUrl will be set to
12661 // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
12662 // in its internal cache and return that cached value for the URL. To clear
12663 // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
12664 // the URL contents, only modules on the same domain of the page can use this capability.
12665 // The build system can inline the cache values though, to allow for xdomain hosting.
12666 // module: String||Object
12667 // If a String with slashes, a fully resolved path; if a String without slashes, the
12668 // module name to use for the base part of the URL, similar to module argument
12669 // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
12670 // generates a valid path for the cache item. For example, a dojo._Url object.
12671 // url: String
12672 // The rest of the path to append to the path derived from the module argument. If
12673 // module is an object, then this second argument should be the "value" argument instead.
12674 // value: String||Object?
12675 // If a String, the value to use in the cache for the module/url combination.
12676 // If an Object, it can have two properties: value and sanitize. The value property
12677 // should be the value to use in the cache, and sanitize can be set to true or false,
12678 // to indicate if XML declarations should be removed from the value and if the HTML
12679 // inside a body tag in the value should be extracted as the real value. The value argument
12680 // or the value property on the value argument are usually only used by the build system
12681 // as it inlines cache content.
12682 // example:
12683 // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
12684 // of call is used to avoid an issue with the build system erroneously trying to intern
12685 // this example. To get the build system to intern your dojo.cache calls, use the
12686 // "dojo.cache" style of call):
12687 // | //If template.html contains "<h1>Hello</h1>" that will be
12688 // | //the value for the text variable.
12689 // | var text = dojo["cache"]("my.module", "template.html");
12690 // example:
12691 // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
12692 // (the dojo["cache"] style of call is used to avoid an issue with the build system
12693 // erroneously trying to intern this example. To get the build system to intern your
12694 // dojo.cache calls, use the "dojo.cache" style of call):
12695 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12696 // | //text variable will contain just "<h1>Hello</h1>".
12697 // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
12698 // example:
12699 // Same example as previous, but demonstrates how an object can be passed in as
12700 // the first argument, then the value argument can then be the second argument.
12701 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12702 // | //text variable will contain just "<h1>Hello</h1>".
12703 // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
12704
12705 // * (string string [value]) => (module, url, value)
12706 // * (object [value]) => (module, value), url defaults to ""
12707 //
12708 // * if module is an object, then it must be convertable to a string
12709 // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
12710 // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
12711 var key;
12712 if(typeof module=="string"){
12713 if(/\//.test(module)){
12714 // module is a version 1.7+ resolved path
12715 key = module;
12716 value = url;
12717 }else{
12718 // module is a version 1.6- argument to dojo.moduleUrl
12719 key = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : ""));
12720 }
12721 }else{
12722 key = module + "";
12723 value = url;
12724 }
12725 var
12726 val = (value != undefined && typeof value != "string") ? value.value : value,
12727 sanitize = value && value.sanitize;
12728
12729 if(typeof val == "string"){
12730 //We have a string, set cache value
12731 theCache[key] = val;
12732 return sanitize ? strip(val) : val;
12733 }else if(val === null){
12734 //Remove cached value
12735 delete theCache[key];
12736 return null;
12737 }else{
12738 //Allow cache values to be empty strings. If key property does
12739 //not exist, fetch it.
12740 if(!(key in theCache)){
12741 getText(key, true, function(text){
12742 theCache[key]= text;
12743 });
12744 }
12745 return sanitize ? strip(theCache[key]) : theCache[key];
12746 }
12747 };
12748
12749 return {
12750 // summary:
12751 // This module implements the dojo/text! plugin and the dojo.cache API.
12752 // description:
12753 // We choose to include our own plugin to leverage functionality already contained in dojo
12754 // and thereby reduce the size of the plugin compared to various foreign loader implementations.
12755 // Also, this allows foreign AMD loaders to be used without their plugins.
12756 //
12757 // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
12758 // loader. This feature is outside the scope of the CommonJS plugins specification.
12759
12760 // the dojo/text caches it's own resources because of dojo.cache
12761 dynamic: true,
12762
12763 normalize: function(id, toAbsMid){
12764 // id is something like (path may be relative):
12765 //
12766 // "path/to/text.html"
12767 // "path/to/text.html!strip"
12768 var parts= id.split("!"),
12769 url= parts[0];
12770 return (/^\./.test(url) ? toAbsMid(url) : url) + (parts[1] ? "!" + parts[1] : "");
12771 },
12772
12773 load: function(id, require, load){
12774 // id: String
12775 // Path to the resource.
12776 // require: Function
12777 // Object that include the function toUrl with given id returns a valid URL from which to load the text.
12778 // load: Function
12779 // Callback function which will be called, when the loading finished.
12780
12781 // id is something like (path is always absolute):
12782 //
12783 // "path/to/text.html"
12784 // "path/to/text.html!strip"
12785 var
12786 parts= id.split("!"),
12787 stripFlag= parts.length>1,
12788 absMid= parts[0],
12789 url = require.toUrl(parts[0]),
12790 requireCacheUrl = "url:" + url,
12791 text = notFound,
12792 finish = function(text){
12793 load(stripFlag ? strip(text) : text);
12794 };
12795 if(absMid in theCache){
12796 text = theCache[absMid];
12797 }else if(requireCacheUrl in require.cache){
12798 text = require.cache[requireCacheUrl];
12799 }else if(url in theCache){
12800 text = theCache[url];
12801 }
12802 if(text===notFound){
12803 if(pending[url]){
12804 pending[url].push(finish);
12805 }else{
12806 var pendingList = pending[url] = [finish];
12807 getText(url, !require.async, function(text){
12808 theCache[absMid]= theCache[url]= text;
12809 for(var i = 0; i<pendingList.length;){
12810 pendingList[i++](text);
12811 }
12812 delete pending[url];
12813 });
12814 }
12815 }else{
12816 finish(text);
12817 }
12818 }
12819 };
12820
12821 });
12822
12823
12824 },
12825 '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",
12826 'dojo/uacss':function(){
12827 define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./sniff", "./_base/window"],
12828 function(geometry, lang, ready, has, baseWindow){
12829
12830 // module:
12831 // dojo/uacss
12832
12833 /*=====
12834 return {
12835 // summary:
12836 // Applies pre-set CSS classes to the top-level HTML node, based on:
12837 //
12838 // - browser (ex: dj_ie)
12839 // - browser version (ex: dj_ie6)
12840 // - box model (ex: dj_contentBox)
12841 // - text direction (ex: dijitRtl)
12842 //
12843 // In addition, browser, browser version, and box model are
12844 // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
12845 //
12846 // Returns the has() method.
12847 };
12848 =====*/
12849
12850 var
12851 html = baseWindow.doc.documentElement,
12852 ie = has("ie"),
12853 opera = has("opera"),
12854 maj = Math.floor,
12855 ff = has("ff"),
12856 boxModel = geometry.boxModel.replace(/-/,''),
12857
12858 classes = {
12859 "dj_quirks": has("quirks"),
12860
12861 // NOTE: Opera not supported by dijit
12862 "dj_opera": opera,
12863
12864 "dj_khtml": has("khtml"),
12865
12866 "dj_webkit": has("webkit"),
12867 "dj_safari": has("safari"),
12868 "dj_chrome": has("chrome"),
12869
12870 "dj_gecko": has("mozilla")
12871 }; // no dojo unsupported browsers
12872
12873 if(ie){
12874 classes["dj_ie"] = true;
12875 classes["dj_ie" + maj(ie)] = true;
12876 classes["dj_iequirks"] = has("quirks");
12877 }
12878 if(ff){
12879 classes["dj_ff" + maj(ff)] = true;
12880 }
12881
12882 classes["dj_" + boxModel] = true;
12883
12884 // apply browser, browser version, and box model class names
12885 var classStr = "";
12886 for(var clz in classes){
12887 if(classes[clz]){
12888 classStr += clz + " ";
12889 }
12890 }
12891 html.className = lang.trim(html.className + " " + classStr);
12892
12893 // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
12894 // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
12895 // priority is 90 to run ahead of parser priority of 100
12896 ready(90, function(){
12897 if(!geometry.isBodyLtr()){
12898 var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ");
12899 html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "));
12900 }
12901 });
12902 return has;
12903 });
12904
12905 },
12906 'dijit/Tooltip':function(){
12907 require({cache:{
12908 '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"}});
12909 define("dijit/Tooltip", [
12910 "dojo/_base/array", // array.forEach array.indexOf array.map
12911 "dojo/_base/declare", // declare
12912 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
12913 "dojo/dom", // dom.byId
12914 "dojo/dom-class", // domClass.add
12915 "dojo/dom-geometry", // domGeometry.position
12916 "dojo/dom-style", // domStyle.set, domStyle.get
12917 "dojo/_base/lang", // lang.hitch lang.isArrayLike
12918 "dojo/mouse",
12919 "dojo/on",
12920 "dojo/sniff", // has("ie")
12921 "./_base/manager", // manager.defaultDuration
12922 "./place",
12923 "./_Widget",
12924 "./_TemplatedMixin",
12925 "./BackgroundIframe",
12926 "dojo/text!./templates/Tooltip.html",
12927 "./main" // sets dijit.showTooltip etc. for back-compat
12928 ], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, mouse, on, has,
12929 manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
12930
12931 // module:
12932 // dijit/Tooltip
12933
12934
12935 // TODO: Tooltip should really share more positioning code with TooltipDialog, like:
12936 // - the orient() method
12937 // - the connector positioning code in show()
12938 // - the dijitTooltip[Dialog] class
12939 //
12940 // The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
12941 // with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
12942
12943 var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
12944 // summary:
12945 // Internal widget that holds the actual tooltip markup,
12946 // which occurs once per page.
12947 // Called by Tooltip widgets which are just containers to hold
12948 // the markup
12949 // tags:
12950 // protected
12951
12952 // duration: Integer
12953 // Milliseconds to fade in/fade out
12954 duration: manager.defaultDuration,
12955
12956 templateString: template,
12957
12958 postCreate: function(){
12959 this.ownerDocumentBody.appendChild(this.domNode);
12960
12961 this.bgIframe = new BackgroundIframe(this.domNode);
12962
12963 // Setup fade-in and fade-out functions.
12964 this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
12965 this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
12966 },
12967
12968 show: function(innerHTML, aroundNode, position, rtl, textDir){
12969 // summary:
12970 // Display tooltip w/specified contents to right of specified node
12971 // (To left if there's no space on the right, or if rtl == true)
12972 // innerHTML: String
12973 // Contents of the tooltip
12974 // aroundNode: DomNode|dijit/place.__Rectangle
12975 // Specifies that tooltip should be next to this node / area
12976 // position: String[]?
12977 // List of positions to try to position tooltip (ex: ["right", "above"])
12978 // rtl: Boolean?
12979 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
12980 // means "rtl"; specifies GUI direction, not text direction.
12981 // textDir: String?
12982 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
12983
12984
12985 if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
12986 return;
12987 }
12988
12989 if(this.fadeOut.status() == "playing"){
12990 // previous tooltip is being hidden; wait until the hide completes then show new one
12991 this._onDeck=arguments;
12992 return;
12993 }
12994 this.containerNode.innerHTML=innerHTML;
12995
12996 if(textDir){
12997 this.set("textDir", textDir);
12998 }
12999
13000 this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
13001
13002 var pos = place.around(this.domNode, aroundNode,
13003 position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
13004
13005 // Position the tooltip connector for middle alignment.
13006 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
13007 var aroundNodeCoords = pos.aroundNodePos;
13008 if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
13009 this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
13010 this.connectorNode.style.left = "";
13011 }else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
13012 this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
13013 }else{
13014 // Not *-centered, but just above/below/after/before
13015 this.connectorNode.style.left = "";
13016 this.connectorNode.style.top = "";
13017 }
13018
13019 // show it
13020 domStyle.set(this.domNode, "opacity", 0);
13021 this.fadeIn.play();
13022 this.isShowingNow = true;
13023 this.aroundNode = aroundNode;
13024 },
13025
13026 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
13027 // summary:
13028 // Private function to set CSS for tooltip node based on which position it's in.
13029 // This is called by the dijit popup code. It will also reduce the tooltip's
13030 // width to whatever width is available
13031 // tags:
13032 // protected
13033
13034 this.connectorNode.style.top = ""; //reset to default
13035
13036 var heightAvailable = spaceAvailable.h,
13037 widthAvailable = spaceAvailable.w;
13038
13039 node.className = "dijitTooltip " +
13040 {
13041 "MR-ML": "dijitTooltipRight",
13042 "ML-MR": "dijitTooltipLeft",
13043 "TM-BM": "dijitTooltipAbove",
13044 "BM-TM": "dijitTooltipBelow",
13045 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
13046 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
13047 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
13048 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
13049 "BR-BL": "dijitTooltipRight",
13050 "BL-BR": "dijitTooltipLeft"
13051 }[aroundCorner + "-" + tooltipCorner];
13052
13053 // reset width; it may have been set by orient() on a previous tooltip show()
13054 this.domNode.style.width = "auto";
13055
13056 // Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
13057 // Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
13058 // negative number since that causes an exception on IE.
13059 var size = domGeometry.position(this.domNode);
13060 if(has("ie") == 9){
13061 // workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
13062 size.w += 2;
13063 }
13064
13065 var width = Math.min((Math.max(widthAvailable,1)), size.w);
13066
13067 domGeometry.setMarginBox(this.domNode, {w: width});
13068
13069 // Reposition the tooltip connector.
13070 if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
13071 var bb = domGeometry.position(node);
13072 var tooltipConnectorHeight = this.connectorNode.offsetHeight;
13073 if(bb.h > heightAvailable){
13074 // The tooltip starts at the top of the page and will extend past the aroundNode
13075 var aroundNodePlacement = heightAvailable - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
13076 this.connectorNode.style.top = aroundNodePlacement + "px";
13077 this.connectorNode.style.bottom = "";
13078 }else{
13079 // Align center of connector with center of aroundNode, except don't let bottom
13080 // of connector extend below bottom of tooltip content, or top of connector
13081 // extend past top of tooltip content
13082 this.connectorNode.style.bottom = Math.min(
13083 Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
13084 bb.h - tooltipConnectorHeight) + "px";
13085 this.connectorNode.style.top = "";
13086 }
13087 }else{
13088 // reset the tooltip back to the defaults
13089 this.connectorNode.style.top = "";
13090 this.connectorNode.style.bottom = "";
13091 }
13092
13093 return Math.max(0, size.w - widthAvailable);
13094 },
13095
13096 _onShow: function(){
13097 // summary:
13098 // Called at end of fade-in operation
13099 // tags:
13100 // protected
13101 if(has("ie")){
13102 // the arrow won't show up on a node w/an opacity filter
13103 this.domNode.style.filter="";
13104 }
13105 },
13106
13107 hide: function(aroundNode){
13108 // summary:
13109 // Hide the tooltip
13110
13111 if(this._onDeck && this._onDeck[1] == aroundNode){
13112 // this hide request is for a show() that hasn't even started yet;
13113 // just cancel the pending show()
13114 this._onDeck=null;
13115 }else if(this.aroundNode === aroundNode){
13116 // this hide request is for the currently displayed tooltip
13117 this.fadeIn.stop();
13118 this.isShowingNow = false;
13119 this.aroundNode = null;
13120 this.fadeOut.play();
13121 }else{
13122 // just ignore the call, it's for a tooltip that has already been erased
13123 }
13124 },
13125
13126 _onHide: function(){
13127 // summary:
13128 // Called at end of fade-out operation
13129 // tags:
13130 // protected
13131
13132 this.domNode.style.cssText=""; // to position offscreen again
13133 this.containerNode.innerHTML="";
13134 if(this._onDeck){
13135 // a show request has been queued up; do it now
13136 this.show.apply(this, this._onDeck);
13137 this._onDeck=null;
13138 }
13139 },
13140
13141 _setAutoTextDir: function(/*Object*/node){
13142 // summary:
13143 // Resolve "auto" text direction for children nodes
13144 // tags:
13145 // private
13146
13147 this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
13148 array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
13149 },
13150
13151 _setTextDirAttr: function(/*String*/ textDir){
13152 // summary:
13153 // Setter for textDir.
13154 // description:
13155 // Users shouldn't call this function; they should be calling
13156 // set('textDir', value)
13157 // tags:
13158 // private
13159
13160 this._set("textDir", textDir);
13161
13162 if (textDir == "auto"){
13163 this._setAutoTextDir(this.containerNode);
13164 }else{
13165 this.containerNode.dir = this.textDir;
13166 }
13167 }
13168 });
13169
13170 dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
13171 // summary:
13172 // Static method to display tooltip w/specified contents in specified position.
13173 // See description of dijit/Tooltip.defaultPosition for details on position parameter.
13174 // If position is not specified then dijit/Tooltip.defaultPosition is used.
13175 // innerHTML: String
13176 // Contents of the tooltip
13177 // aroundNode: place.__Rectangle
13178 // Specifies that tooltip should be next to this node / area
13179 // position: String[]?
13180 // List of positions to try to position tooltip (ex: ["right", "above"])
13181 // rtl: Boolean?
13182 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13183 // means "rtl"; specifies GUI direction, not text direction.
13184 // textDir: String?
13185 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13186
13187 // After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
13188 // Possibly remove this in 2.0. Alternately, get before/after to work.
13189 if(position){
13190 position = array.map(position, function(val){
13191 return {after: "after-centered", before: "before-centered"}[val] || val;
13192 });
13193 }
13194
13195 if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
13196 return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
13197 };
13198
13199 dijit.hideTooltip = function(aroundNode){
13200 // summary:
13201 // Static method to hide the tooltip displayed via showTooltip()
13202 return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
13203 };
13204
13205 var Tooltip = declare("dijit.Tooltip", _Widget, {
13206 // summary:
13207 // Pops up a tooltip (a help message) when you hover over a node.
13208 // Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
13209
13210 // label: String
13211 // Text to display in the tooltip.
13212 // Specified as innerHTML when creating the widget from markup.
13213 label: "",
13214
13215 // showDelay: Integer
13216 // Number of milliseconds to wait after hovering over/focusing on the object, before
13217 // the tooltip is displayed.
13218 showDelay: 400,
13219
13220 // connectId: String|String[]|DomNode|DomNode[]
13221 // Id of domNode(s) to attach the tooltip to.
13222 // When user hovers over specified dom node(s), the tooltip will appear.
13223 connectId: [],
13224
13225 // position: String[]
13226 // See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
13227 position: [],
13228
13229 // selector: String?
13230 // CSS expression to apply this Tooltip to descendants of connectIds, rather than to
13231 // the nodes specified by connectIds themselves. Useful for applying a Tooltip to
13232 // a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
13233 // Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
13234 //
13235 // The application must require() an appropriate level of dojo/query to handle the selector.
13236 selector: "",
13237
13238 // TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
13239 // So, change connectId to a "", remove addTarget()/removeTarget(), etc.
13240
13241 _setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId){
13242 // summary:
13243 // Connect to specified node(s)
13244
13245 // Remove connections to old nodes (if there are any)
13246 array.forEach(this._connections || [], function(nested){
13247 array.forEach(nested, function(handle){ handle.remove(); });
13248 }, this);
13249
13250 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
13251 this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
13252 function(id){ return dom.byId(id, this.ownerDocument); }, this);
13253
13254 // Make connections
13255 this._connections = array.map(this._connectIds, function(id){
13256 var node = dom.byId(id, this.ownerDocument),
13257 selector = this.selector,
13258 delegatedEvent = selector ?
13259 function(eventType){ return on.selector(selector, eventType); } :
13260 function(eventType){ return eventType; },
13261 self = this;
13262 return [
13263 on(node, delegatedEvent(mouse.enter), function(){
13264 self._onHover(this);
13265 }),
13266 on(node, delegatedEvent("focusin"), function(){
13267 self._onHover(this);
13268 }),
13269 on(node, delegatedEvent(mouse.leave), lang.hitch(self, "_onUnHover")),
13270 on(node, delegatedEvent("focusout"), lang.hitch(self, "_onUnHover"))
13271 ];
13272 }, this);
13273
13274 this._set("connectId", newId);
13275 },
13276
13277 addTarget: function(/*OomNode|String*/ node){
13278 // summary:
13279 // Attach tooltip to specified node if it's not already connected
13280
13281 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13282
13283 var id = node.id || node;
13284 if(array.indexOf(this._connectIds, id) == -1){
13285 this.set("connectId", this._connectIds.concat(id));
13286 }
13287 },
13288
13289 removeTarget: function(/*DomNode|String*/ node){
13290 // summary:
13291 // Detach tooltip from specified node
13292
13293 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13294
13295 var id = node.id || node, // map from DOMNode back to plain id string
13296 idx = array.indexOf(this._connectIds, id);
13297 if(idx >= 0){
13298 // remove id (modifies original this._connectIds but that's OK in this case)
13299 this._connectIds.splice(idx, 1);
13300 this.set("connectId", this._connectIds);
13301 }
13302 },
13303
13304 buildRendering: function(){
13305 this.inherited(arguments);
13306 domClass.add(this.domNode,"dijitTooltipData");
13307 },
13308
13309 startup: function(){
13310 this.inherited(arguments);
13311
13312 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
13313 // didn't exist during the widget's initialization, then connect now.
13314 var ids = this.connectId;
13315 array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
13316 },
13317
13318 getContent: function(/*DomNode*/ node){
13319 // summary:
13320 // User overridable function that return the text to display in the tooltip.
13321 // tags:
13322 // extension
13323 return this.label || this.domNode.innerHTML;
13324 },
13325
13326 _onHover: function(/*DomNode*/ target){
13327 // summary:
13328 // Despite the name of this method, it actually handles both hover and focus
13329 // events on the target node, setting a timer to show the tooltip.
13330 // tags:
13331 // private
13332 if(!this._showTimer){
13333 this._showTimer = this.defer(function(){ this.open(target); }, this.showDelay);
13334 }
13335 },
13336
13337 _onUnHover: function(){
13338 // summary:
13339 // Despite the name of this method, it actually handles both mouseleave and blur
13340 // events on the target node, hiding the tooltip.
13341 // tags:
13342 // private
13343
13344 if(this._showTimer){
13345 this._showTimer.remove();
13346 delete this._showTimer;
13347 }
13348 this.close();
13349 },
13350
13351 open: function(/*DomNode*/ target){
13352 // summary:
13353 // Display the tooltip; usually not called directly.
13354 // tags:
13355 // private
13356
13357 if(this._showTimer){
13358 this._showTimer.remove();
13359 delete this._showTimer;
13360 }
13361
13362 var content = this.getContent(target);
13363 if(!content){
13364 return;
13365 }
13366 Tooltip.show(content, target, this.position, !this.isLeftToRight(), this.textDir);
13367
13368 this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
13369 this.onShow(target, this.position);
13370 },
13371
13372 close: function(){
13373 // summary:
13374 // Hide the tooltip or cancel timer for show of tooltip
13375 // tags:
13376 // private
13377
13378 if(this._connectNode){
13379 // if tooltip is currently shown
13380 Tooltip.hide(this._connectNode);
13381 delete this._connectNode;
13382 this.onHide();
13383 }
13384 if(this._showTimer){
13385 // if tooltip is scheduled to be shown (after a brief delay)
13386 this._showTimer.remove();
13387 delete this._showTimer;
13388 }
13389 },
13390
13391 onShow: function(/*===== target, position =====*/){
13392 // summary:
13393 // Called when the tooltip is shown
13394 // tags:
13395 // callback
13396 },
13397
13398 onHide: function(){
13399 // summary:
13400 // Called when the tooltip is hidden
13401 // tags:
13402 // callback
13403 },
13404
13405 destroy: function(){
13406 this.close();
13407
13408 // Remove connections manually since they aren't registered to be removed by _WidgetBase
13409 array.forEach(this._connections || [], function(nested){
13410 array.forEach(nested, function(handle){ handle.remove(); });
13411 }, this);
13412
13413 this.inherited(arguments);
13414 }
13415 });
13416
13417 Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
13418 Tooltip.show = dijit.showTooltip; // export function through module return value
13419 Tooltip.hide = dijit.hideTooltip; // export function through module return value
13420
13421 Tooltip.defaultPosition = ["after-centered", "before-centered"];
13422
13423 /*=====
13424 lang.mixin(Tooltip, {
13425 // defaultPosition: String[]
13426 // This variable controls the position of tooltips, if the position is not specified to
13427 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
13428 // possible for `dijit/place.around()`. The recommended values are:
13429 //
13430 // - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
13431 // in the case of RTL scripts like Hebrew and Arabic
13432 // - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
13433 // in the case of RTL scripts like Hebrew and Arabic
13434 // - above-centered: tooltip is centered above anchor node
13435 // - below-centered: tooltip is centered above anchor node
13436 //
13437 // The list is positions is tried, in order, until a position is found where the tooltip fits
13438 // within the viewport.
13439 //
13440 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
13441 // the screen so that there's no room above the target node. Nodes with drop downs, like
13442 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
13443 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
13444 // is only room below (or above) the target node, but not both.
13445 });
13446 =====*/
13447 return Tooltip;
13448 });
13449
13450 },
13451 'dojo/string':function(){
13452 define("dojo/string", [
13453 "./_base/kernel", // kernel.global
13454 "./_base/lang"
13455 ], function(kernel, lang){
13456
13457 // module:
13458 // dojo/string
13459
13460 var string = {
13461 // summary:
13462 // String utilities for Dojo
13463 };
13464 lang.setObject("dojo.string", string);
13465
13466 string.rep = function(/*String*/str, /*Integer*/num){
13467 // summary:
13468 // Efficiently replicate a string `n` times.
13469 // str:
13470 // the string to replicate
13471 // num:
13472 // number of times to replicate the string
13473
13474 if(num <= 0 || !str){ return ""; }
13475
13476 var buf = [];
13477 for(;;){
13478 if(num & 1){
13479 buf.push(str);
13480 }
13481 if(!(num >>= 1)){ break; }
13482 str += str;
13483 }
13484 return buf.join(""); // String
13485 };
13486
13487 string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
13488 // summary:
13489 // Pad a string to guarantee that it is at least `size` length by
13490 // filling with the character `ch` at either the start or end of the
13491 // string. Pads at the start, by default.
13492 // text:
13493 // the string to pad
13494 // size:
13495 // length to provide padding
13496 // ch:
13497 // character to pad, defaults to '0'
13498 // end:
13499 // adds padding at the end if true, otherwise pads at start
13500 // example:
13501 // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
13502 // | string.pad("Dojo", 10, "+", true);
13503
13504 if(!ch){
13505 ch = '0';
13506 }
13507 var out = String(text),
13508 pad = string.rep(ch, Math.ceil((size - out.length) / ch.length));
13509 return end ? out + pad : pad + out; // String
13510 };
13511
13512 string.substitute = function( /*String*/ template,
13513 /*Object|Array*/map,
13514 /*Function?*/ transform,
13515 /*Object?*/ thisObject){
13516 // summary:
13517 // Performs parameterized substitutions on a string. Throws an
13518 // exception if any parameter is unmatched.
13519 // template:
13520 // a string with expressions in the form `${key}` to be replaced or
13521 // `${key:format}` which specifies a format function. keys are case-sensitive.
13522 // map:
13523 // hash to search for substitutions
13524 // transform:
13525 // a function to process all parameters before substitution takes
13526 // place, e.g. mylib.encodeXML
13527 // thisObject:
13528 // where to look for optional format function; default to the global
13529 // namespace
13530 // example:
13531 // Substitutes two expressions in a string from an Array or Object
13532 // | // returns "File 'foo.html' is not found in directory '/temp'."
13533 // | // by providing substitution data in an Array
13534 // | string.substitute(
13535 // | "File '${0}' is not found in directory '${1}'.",
13536 // | ["foo.html","/temp"]
13537 // | );
13538 // |
13539 // | // also returns "File 'foo.html' is not found in directory '/temp'."
13540 // | // but provides substitution data in an Object structure. Dotted
13541 // | // notation may be used to traverse the structure.
13542 // | string.substitute(
13543 // | "File '${name}' is not found in directory '${info.dir}'.",
13544 // | { name: "foo.html", info: { dir: "/temp" } }
13545 // | );
13546 // example:
13547 // Use a transform function to modify the values:
13548 // | // returns "file 'foo.html' is not found in directory '/temp'."
13549 // | string.substitute(
13550 // | "${0} is not found in ${1}.",
13551 // | ["foo.html","/temp"],
13552 // | function(str){
13553 // | // try to figure out the type
13554 // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
13555 // | return prefix + " '" + str + "'";
13556 // | }
13557 // | );
13558 // example:
13559 // Use a formatter
13560 // | // returns "thinger -- howdy"
13561 // | string.substitute(
13562 // | "${0:postfix}", ["thinger"], null, {
13563 // | postfix: function(value, key){
13564 // | return value + " -- howdy";
13565 // | }
13566 // | }
13567 // | );
13568
13569 thisObject = thisObject || kernel.global;
13570 transform = transform ?
13571 lang.hitch(thisObject, transform) : function(v){ return v; };
13572
13573 return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
13574 function(match, key, format){
13575 var value = lang.getObject(key, false, map);
13576 if(format){
13577 value = lang.getObject(format, false, thisObject).call(thisObject, value, key);
13578 }
13579 return transform(value, key).toString();
13580 }); // String
13581 };
13582
13583 string.trim = String.prototype.trim ?
13584 lang.trim : // aliasing to the native function
13585 function(str){
13586 str = str.replace(/^\s+/, '');
13587 for(var i = str.length - 1; i >= 0; i--){
13588 if(/\S/.test(str.charAt(i))){
13589 str = str.substring(0, i + 1);
13590 break;
13591 }
13592 }
13593 return str;
13594 };
13595
13596 /*=====
13597 string.trim = function(str){
13598 // summary:
13599 // Trims whitespace from both sides of the string
13600 // str: String
13601 // String to be trimmed
13602 // returns: String
13603 // Returns the trimmed string
13604 // description:
13605 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
13606 // The short yet performant version of this function is dojo.trim(),
13607 // which is part of Dojo base. Uses String.prototype.trim instead, if available.
13608 return ""; // String
13609 };
13610 =====*/
13611
13612 return string;
13613 });
13614
13615 },
13616 '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>",
13617 'dijit/dijit':function(){
13618 define("dijit/dijit", [
13619 "./main",
13620 "./_base",
13621 "dojo/parser",
13622 "./_Widget",
13623 "./_TemplatedMixin",
13624 "./_Container",
13625 "./layout/_LayoutWidget",
13626 "./form/_FormWidget",
13627 "./form/_FormValueWidget"
13628 ], function(dijit){
13629
13630 // module:
13631 // dijit/dijit
13632
13633 /*=====
13634 return {
13635 // summary:
13636 // A roll-up for common dijit methods
13637 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
13638 // And some other stuff that we tend to pull in all the time anyway
13639 };
13640 =====*/
13641
13642 return dijit;
13643 });
13644
13645 },
13646 'dijit/form/DropDownButton':function(){
13647 require({cache:{
13648 '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"}});
13649 define("dijit/form/DropDownButton", [
13650 "dojo/_base/declare", // declare
13651 "dojo/_base/lang", // hitch
13652 "dojo/query", // query
13653 "../registry", // registry.byNode
13654 "../popup", // dijit.popup2.hide
13655 "./Button",
13656 "../_Container",
13657 "../_HasDropDown",
13658 "dojo/text!./templates/DropDownButton.html"
13659 ], function(declare, lang, query, registry, popup, Button, _Container, _HasDropDown, template){
13660
13661 // module:
13662 // dijit/form/DropDownButton
13663
13664
13665 return declare("dijit.form.DropDownButton", [Button, _Container, _HasDropDown], {
13666 // summary:
13667 // A button with a drop down
13668 //
13669 // example:
13670 // | <button data-dojo-type="dijit/form/DropDownButton">
13671 // | Hello world
13672 // | <div data-dojo-type="dijit/Menu">...</div>
13673 // | </button>
13674 //
13675 // example:
13676 // | var button1 = new DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
13677 // | win.body().appendChild(button1);
13678 //
13679
13680 baseClass : "dijitDropDownButton",
13681
13682 templateString: template,
13683
13684 _fillContent: function(){
13685 // Overrides Button._fillContent().
13686 //
13687 // My inner HTML contains both the button contents and a drop down widget, like
13688 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
13689 // The first node is assumed to be the button content. The widget is the popup.
13690
13691 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
13692 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
13693 // content, not just nodes[0]
13694 var nodes = query("*", this.srcNodeRef);
13695 this.inherited(arguments, [nodes[0]]);
13696
13697 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
13698 this.dropDownContainer = this.srcNodeRef;
13699 }
13700 },
13701
13702 startup: function(){
13703 if(this._started){ return; }
13704
13705 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
13706 // make it invisible, and store a reference to pass to the popup code.
13707 if(!this.dropDown && this.dropDownContainer){
13708 var dropDownNode = query("[widgetId]", this.dropDownContainer)[0];
13709 this.dropDown = registry.byNode(dropDownNode);
13710 delete this.dropDownContainer;
13711 }
13712 if(this.dropDown){
13713 popup.hide(this.dropDown);
13714 }
13715
13716 this.inherited(arguments);
13717 },
13718
13719 isLoaded: function(){
13720 // Returns whether or not we are loaded - if our dropdown has an href,
13721 // then we want to check that.
13722 var dropDown = this.dropDown;
13723 return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
13724 },
13725
13726 loadDropDown: function(/*Function*/ callback){
13727 // Default implementation assumes that drop down already exists,
13728 // but hasn't loaded it's data (ex: ContentPane w/href).
13729 // App must override if the drop down is lazy-created.
13730 var dropDown = this.dropDown;
13731 var handler = dropDown.on("load", lang.hitch(this, function(){
13732 handler.remove();
13733 callback();
13734 }));
13735 dropDown.refresh(); // tell it to load
13736 },
13737
13738 isFocusable: function(){
13739 // Overridden so that focus is handled by the _HasDropDown mixin, not by
13740 // the _FormWidget mixin.
13741 return this.inherited(arguments) && !this._mouseDown;
13742 }
13743 });
13744
13745 });
13746
13747 },
13748 'dijit/form/_FormValueMixin':function(){
13749 define("dijit/form/_FormValueMixin", [
13750 "dojo/_base/declare", // declare
13751 "dojo/dom-attr", // domAttr.set
13752 "dojo/keys", // keys.ESCAPE
13753 "dojo/sniff", // has("ie"), has("quirks")
13754 "./_FormWidgetMixin"
13755 ], function(declare, domAttr, keys, has, _FormWidgetMixin){
13756
13757 // module:
13758 // dijit/form/_FormValueMixin
13759
13760 return declare("dijit.form._FormValueMixin", _FormWidgetMixin, {
13761 // summary:
13762 // Mixin for widgets corresponding to native HTML elements such as `<input>` or `<select>`
13763 // that have user changeable values.
13764 // description:
13765 // Each _FormValueMixin represents a single input value, and has a (possibly hidden) `<input>` element,
13766 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
13767 // works as expected.
13768
13769 // readOnly: Boolean
13770 // Should this widget respond to user input?
13771 // In markup, this is specified as "readOnly".
13772 // Similar to disabled except readOnly form values are submitted.
13773 readOnly: false,
13774
13775 _setReadOnlyAttr: function(/*Boolean*/ value){
13776 domAttr.set(this.focusNode, 'readOnly', value);
13777 this._set("readOnly", value);
13778 },
13779
13780 postCreate: function(){
13781 this.inherited(arguments);
13782
13783 if(has("ie")){ // IE won't stop the event with keypress
13784 this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
13785 }
13786 // Update our reset value if it hasn't yet been set (because this.set()
13787 // is only called when there *is* a value)
13788 if(this._resetValue === undefined){
13789 this._lastValueReported = this._resetValue = this.value;
13790 }
13791 },
13792
13793 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13794 // summary:
13795 // Hook so set('value', value) works.
13796 // description:
13797 // Sets the value of the widget.
13798 // If the value has changed, then fire onChange event, unless priorityChange
13799 // is specified as null (or false?)
13800 this._handleOnChange(newValue, priorityChange);
13801 },
13802
13803 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13804 // summary:
13805 // Called when the value of the widget has changed. Saves the new value in this.value,
13806 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
13807 this._set("value", newValue);
13808 this.inherited(arguments);
13809 },
13810
13811 undo: function(){
13812 // summary:
13813 // Restore the value to the last value passed to onChange
13814 this._setValueAttr(this._lastValueReported, false);
13815 },
13816
13817 reset: function(){
13818 // summary:
13819 // Reset the widget's value to what it was at initialization time
13820 this._hasBeenBlurred = false;
13821 this._setValueAttr(this._resetValue, true);
13822 },
13823
13824 _onKeyDown: function(e){
13825 if(e.keyCode == keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
13826 if(has("ie") < 9 || (has("ie") && has("quirks"))){
13827 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
13828 var node = e.srcElement,
13829 te = node.ownerDocument.createEventObject();
13830 te.keyCode = keys.ESCAPE;
13831 te.shiftKey = e.shiftKey;
13832 node.fireEvent('onkeypress', te);
13833 }
13834 }
13835 }
13836 });
13837 });
13838
13839 },
13840 'dijit/form/_FormWidgetMixin':function(){
13841 define("dijit/form/_FormWidgetMixin", [
13842 "dojo/_base/array", // array.forEach
13843 "dojo/_base/declare", // declare
13844 "dojo/dom-attr", // domAttr.set
13845 "dojo/dom-style", // domStyle.get
13846 "dojo/_base/lang", // lang.hitch lang.isArray
13847 "dojo/mouse", // mouse.isLeft
13848 "dojo/sniff", // has("webkit")
13849 "dojo/window", // winUtils.scrollIntoView
13850 "../a11y" // a11y.hasDefaultTabStop
13851 ], function(array, declare, domAttr, domStyle, lang, mouse, has, winUtils, a11y){
13852
13853 // module:
13854 // dijit/form/_FormWidgetMixin
13855
13856 return declare("dijit.form._FormWidgetMixin", null, {
13857 // summary:
13858 // Mixin for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
13859 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
13860 //
13861 // description:
13862 // Represents a single HTML element.
13863 // All these widgets should have these attributes just like native HTML input elements.
13864 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
13865 //
13866 // They also share some common methods.
13867
13868 // name: [const] String
13869 // Name used when submitting form; same as "name" attribute or plain HTML elements
13870 name: "",
13871
13872 // alt: String
13873 // Corresponds to the native HTML `<input>` element's attribute.
13874 alt: "",
13875
13876 // value: String
13877 // Corresponds to the native HTML `<input>` element's attribute.
13878 value: "",
13879
13880 // type: [const] String
13881 // Corresponds to the native HTML `<input>` element's attribute.
13882 type: "text",
13883
13884 // type: String
13885 // Apply aria-label in markup to the widget's focusNode
13886 "aria-label": "focusNode",
13887
13888 // tabIndex: String
13889 // Order fields are traversed when user hits the tab key
13890 tabIndex: "0",
13891 _setTabIndexAttr: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
13892
13893 // disabled: Boolean
13894 // Should this widget respond to user input?
13895 // In markup, this is specified as "disabled='disabled'", or just "disabled".
13896 disabled: false,
13897
13898 // intermediateChanges: Boolean
13899 // Fires onChange for each value change or only on demand
13900 intermediateChanges: false,
13901
13902 // scrollOnFocus: Boolean
13903 // On focus, should this widget scroll into view?
13904 scrollOnFocus: true,
13905
13906 // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
13907 // works with screen reader
13908 _setIdAttr: "focusNode",
13909
13910 _setDisabledAttr: function(/*Boolean*/ value){
13911 this._set("disabled", value);
13912 domAttr.set(this.focusNode, 'disabled', value);
13913 if(this.valueNode){
13914 domAttr.set(this.valueNode, 'disabled', value);
13915 }
13916 this.focusNode.setAttribute("aria-disabled", value ? "true" : "false");
13917
13918 if(value){
13919 // reset these, because after the domNode is disabled, we can no longer receive
13920 // mouse related events, see #4200
13921 this._set("hovering", false);
13922 this._set("active", false);
13923
13924 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
13925 var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex :
13926 ("_setTabIndexAttr" in this) ? this._setTabIndexAttr : "focusNode";
13927 array.forEach(lang.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
13928 var node = this[attachPointName];
13929 // complex code because tabIndex=-1 on a <div> doesn't work on FF
13930 if(has("webkit") || a11y.hasDefaultTabStop(node)){ // see #11064 about webkit bug
13931 node.setAttribute('tabIndex', "-1");
13932 }else{
13933 node.removeAttribute('tabIndex');
13934 }
13935 }, this);
13936 }else{
13937 if(this.tabIndex != ""){
13938 this.set('tabIndex', this.tabIndex);
13939 }
13940 }
13941 },
13942
13943 _onFocus: function(/*String*/ by){
13944 // If user clicks on the widget, even if the mouse is released outside of it,
13945 // this widget's focusNode should get focus (to mimic native browser hehavior).
13946 // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
13947 if(by == "mouse" && this.isFocusable()){
13948 // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
13949 var focusConnector = this.connect(this.focusNode, "onfocus", function(){
13950 this.disconnect(mouseUpConnector);
13951 this.disconnect(focusConnector);
13952 });
13953 // Set a global event to handle mouseup, so it fires properly
13954 // even if the cursor leaves this.domNode before the mouse up event.
13955 var mouseUpConnector = this.connect(this.ownerDocumentBody, "onmouseup", function(){
13956 this.disconnect(mouseUpConnector);
13957 this.disconnect(focusConnector);
13958 // if here, then the mousedown did not focus the focusNode as the default action
13959 if(this.focused){
13960 this.focus();
13961 }
13962 });
13963 }
13964 if(this.scrollOnFocus){
13965 this.defer(function(){ winUtils.scrollIntoView(this.domNode); }); // without defer, the input caret position can change on mouse click
13966 }
13967 this.inherited(arguments);
13968 },
13969
13970 isFocusable: function(){
13971 // summary:
13972 // Tells if this widget is focusable or not. Used internally by dijit.
13973 // tags:
13974 // protected
13975 return !this.disabled && this.focusNode && (domStyle.get(this.domNode, "display") != "none");
13976 },
13977
13978 focus: function(){
13979 // summary:
13980 // Put focus on this widget
13981 if(!this.disabled && this.focusNode.focus){
13982 try{ this.focusNode.focus(); }catch(e){}/*squelch errors from hidden nodes*/
13983 }
13984 },
13985
13986 compare: function(/*anything*/ val1, /*anything*/ val2){
13987 // summary:
13988 // Compare 2 values (as returned by get('value') for this widget).
13989 // tags:
13990 // protected
13991 if(typeof val1 == "number" && typeof val2 == "number"){
13992 return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
13993 }else if(val1 > val2){
13994 return 1;
13995 }else if(val1 < val2){
13996 return -1;
13997 }else{
13998 return 0;
13999 }
14000 },
14001
14002 onChange: function(/*===== newValue =====*/){
14003 // summary:
14004 // Callback when this widget's value is changed.
14005 // tags:
14006 // callback
14007 },
14008
14009 // _onChangeActive: [private] Boolean
14010 // Indicates that changes to the value should call onChange() callback.
14011 // This is false during widget initialization, to avoid calling onChange()
14012 // when the initial value is set.
14013 _onChangeActive: false,
14014
14015 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
14016 // summary:
14017 // Called when the value of the widget is set. Calls onChange() if appropriate
14018 // newValue:
14019 // the new value
14020 // priorityChange:
14021 // For a slider, for example, dragging the slider is priorityChange==false,
14022 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
14023 // onChange is only called form priorityChange=true events.
14024 // tags:
14025 // private
14026 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
14027 // this block executes not for a change, but during initialization,
14028 // and is used to store away the original value (or for ToggleButton, the original checked state)
14029 this._resetValue = this._lastValueReported = newValue;
14030 }
14031 this._pendingOnChange = this._pendingOnChange
14032 || (typeof newValue != typeof this._lastValueReported)
14033 || (this.compare(newValue, this._lastValueReported) != 0);
14034 if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
14035 this._lastValueReported = newValue;
14036 this._pendingOnChange = false;
14037 if(this._onChangeActive){
14038 if(this._onChangeHandle){
14039 this._onChangeHandle.remove();
14040 }
14041 // defer allows hidden value processing to run and
14042 // also the onChange handler can safely adjust focus, etc
14043 this._onChangeHandle = this.defer(
14044 function(){
14045 this._onChangeHandle = null;
14046 this.onChange(newValue);
14047 }); // try to collapse multiple onChange's fired faster than can be processed
14048 }
14049 }
14050 },
14051
14052 create: function(){
14053 // Overrides _Widget.create()
14054 this.inherited(arguments);
14055 this._onChangeActive = true;
14056 },
14057
14058 destroy: function(){
14059 if(this._onChangeHandle){ // destroy called before last onChange has fired
14060 this._onChangeHandle.remove();
14061 this.onChange(this._lastValueReported);
14062 }
14063 this.inherited(arguments);
14064 }
14065 });
14066
14067 });
14068
14069 },
14070 'dijit/a11yclick':function(){
14071 define("dijit/a11yclick", [
14072 "dojo/on",
14073 "dojo/_base/array", // array.forEach
14074 "dojo/keys", // keys.ENTER keys.SPACE
14075 "dojo/_base/declare", // declare
14076 "dojo/has", // has("dom-addeventlistener")
14077 "dojo/_base/unload", // unload.addOnWindowUnload
14078 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
14079 ], function(on, array, keys, declare, has, unload, win){
14080
14081 // module:
14082 // dijit/a11yclick
14083
14084 // Keep track of where the last keydown event was, to help avoid generating
14085 // spurious ondijitclick events when:
14086 // 1. focus is on a <button> or <a>
14087 // 2. user presses then releases the ENTER key
14088 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
14089 // 4. onkeyup event fires, causing the ondijitclick handler to fire
14090 var lastKeyDownNode = null;
14091 if(has("dom-addeventlistener")){
14092 win.doc.addEventListener('keydown', function(evt){
14093 lastKeyDownNode = evt.target;
14094 }, true);
14095 }else{
14096 // Fallback path for IE6-8
14097 (function(){
14098 var keydownCallback = function(evt){
14099 lastKeyDownNode = evt.srcElement;
14100 };
14101 win.doc.attachEvent('onkeydown', keydownCallback);
14102 unload.addOnWindowUnload(function(){
14103 win.doc.detachEvent('onkeydown', keydownCallback);
14104 });
14105 })();
14106 }
14107
14108 function clickKey(/*Event*/ e){
14109 return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
14110 !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
14111 }
14112
14113 return function(node, listener){
14114 // summary:
14115 // Custom a11yclick (a.k.a. ondijitclick) event
14116 // which triggers on a mouse click, touch, or space/enter keyup.
14117
14118 if(/input|button/i.test(node.nodeName)){
14119 // pass through, the browser already generates click event on SPACE/ENTER key
14120 return on(node, "click", listener);
14121 }else{
14122 // Don't fire the click event unless both the keydown and keyup occur on this node.
14123 // Avoids problems where focus shifted to this node or away from the node on keydown,
14124 // either causing this node to process a stray keyup event, or causing another node
14125 // to get a stray keyup event.
14126
14127 var handles = [
14128 on(node, "keydown", function(e){
14129 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14130 if(clickKey(e)){
14131 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
14132 lastKeyDownNode = e.target;
14133
14134 // Prevent viewport scrolling on space key in IE<9.
14135 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
14136 e.preventDefault();
14137 }
14138 }),
14139
14140 on(node, "keyup", function(e){
14141 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14142 if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
14143 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
14144 lastKeyDownNode = null;
14145 on.emit(e.target, "click", {
14146 cancelable: true,
14147 bubbles: true
14148 });
14149 }
14150 }),
14151
14152 on(node, "click", function(e){
14153 // catch mouse clicks, plus the on.emit() calls from above and below
14154 listener.call(this, e);
14155 })
14156 ];
14157
14158 if(has("touch")){
14159 // touchstart-->touchend will automatically generate a click event, but there are problems
14160 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
14161 // if click doesn't fire naturally.
14162
14163 var clickTimer;
14164 handles.push(
14165 on(node, "touchend", function(e){
14166 var target = e.target;
14167 clickTimer = setTimeout(function(){
14168 clickTimer = null;
14169 on.emit(target, "click", {
14170 cancelable: true,
14171 bubbles: true
14172 });
14173 }, 600);
14174 }),
14175 on(node, "click", function(e){
14176 // If browser generates a click naturally, clear the timer to fire a synthetic click event
14177 if(clickTimer){
14178 clearTimeout(clickTimer);
14179 }
14180 })
14181 // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
14182 // event <300ms after the touchend event, then clear the synthetic click timer, because user
14183 // is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
14184 // zoom level has changed.
14185 );
14186 }
14187
14188 return {
14189 remove: function(){
14190 array.forEach(handles, function(h){ h.remove(); });
14191 if(clickTimer){
14192 clearTimeout(clickTimer);
14193 clickTimer = null;
14194 }
14195 }
14196 };
14197 }
14198 };
14199
14200 return ret;
14201 });
14202
14203 },
14204 '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",
14205 'dijit/Destroyable':function(){
14206 define("dijit/Destroyable", [
14207 "dojo/_base/array", // array.forEach array.map
14208 "dojo/aspect",
14209 "dojo/_base/declare"
14210 ], function(array, aspect, declare){
14211
14212 // module:
14213 // dijit/Destroyable
14214
14215 return declare("dijit.Destroyable", null, {
14216 // summary:
14217 // Mixin to track handles and release them when instance is destroyed.
14218 // description:
14219 // Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
14220 // dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
14221 // Then call destroy() later to destroy this instance and release the resources.
14222
14223 destroy: function(/*Boolean*/ preserveDom){
14224 // summary:
14225 // Destroy this class, releasing any resources registered via own().
14226 this._destroyed = true;
14227 },
14228
14229 own: function(){
14230 // summary:
14231 // Track specified handles and remove/destroy them when this instance is destroyed, unless they were
14232 // already removed/destroyed manually.
14233 // tags:
14234 // protected
14235 // returns:
14236 // The array of specified handles, so you can do for example:
14237 // | var handle = this.own(on(...))[0];
14238
14239 array.forEach(arguments, function(handle){
14240 var destroyMethodName =
14241 "destroyRecursive" in handle ? "destroyRecursive" : // remove "destroyRecursive" for 2.0
14242 "destroy" in handle ? "destroy" :
14243 "remove";
14244
14245 // When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
14246 // the handle will be destroyed before a subclass's destroy() method starts running, before it calls
14247 // this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
14248 // onDestroy() method and connect to that instead.
14249 var odh = aspect.before(this, "destroy", function(preserveDom){
14250 handle[destroyMethodName](preserveDom);
14251 });
14252
14253 // If handle is destroyed manually before this.destroy() is called, remove the listener set directly above.
14254 var hdh = aspect.after(handle, destroyMethodName, function(){
14255 odh.remove();
14256 hdh.remove();
14257 }, true);
14258 }, this);
14259
14260 return arguments; // handle
14261 }
14262 });
14263
14264 });
14265
14266 },
14267 'dijit/layout/_ContentPaneResizeMixin':function(){
14268 define("dijit/layout/_ContentPaneResizeMixin", [
14269 "dojo/_base/array", // array.filter array.forEach
14270 "dojo/_base/declare", // declare
14271 "dojo/dom-class", // domClass.contains domClass.toggle
14272 "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
14273 "dojo/dom-style",
14274 "dojo/_base/lang", // lang.mixin
14275 "dojo/query", // query
14276 "dojo/sniff", // has("ie")
14277 "../registry", // registry.byId
14278 "../Viewport",
14279 "./utils" // marginBox2contextBox
14280 ], function(array, declare, domClass, domGeometry, domStyle, lang, query, has,
14281 registry, Viewport, layoutUtils){
14282
14283 // module:
14284 // dijit/layout/_ContentPaneResizeMixin
14285
14286
14287 return declare("dijit.layout._ContentPaneResizeMixin", null, {
14288 // summary:
14289 // Resize() functionality of ContentPane. If there's a single layout widget
14290 // child then it will call resize() with the same dimensions as the ContentPane.
14291 // Otherwise just calls resize on each child.
14292 //
14293 // Also implements basic startup() functionality, where starting the parent
14294 // will start the children
14295
14296 // doLayout: Boolean
14297 // - false - don't adjust size of children
14298 // - true - if there is a single visible child widget, set it's size to however big the ContentPane is
14299 doLayout: true,
14300
14301 // isLayoutContainer: [protected] Boolean
14302 // Indicates that this widget will call resize() on it's child widgets
14303 // when they become visible.
14304 isLayoutContainer: true,
14305
14306 startup: function(){
14307 // summary:
14308 // See `dijit/layout/_LayoutWidget.startup()` for description.
14309 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14310 // the same API.
14311
14312 if(this._started){ return; }
14313
14314 var parent = this.getParent();
14315 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
14316
14317 // I need to call resize() on my child/children (when I become visible), unless
14318 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
14319 this._needLayout = !this._childOfLayoutWidget;
14320
14321 this.inherited(arguments);
14322
14323 if(this._isShown()){
14324 this._onShow();
14325 }
14326
14327 if(!this._childOfLayoutWidget){
14328 // Since my parent isn't a layout container, and my style *may be* width=height=100%
14329 // or something similar (either set directly or via a CSS class),
14330 // monitor when viewport size changes so that I can re-layout.
14331 // This is more for subclasses of ContentPane than ContentPane itself, although it
14332 // could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size.
14333 this.own(Viewport.on("resize", lang.hitch(this, "resize")));
14334 }
14335 },
14336
14337 _checkIfSingleChild: function(){
14338 // summary:
14339 // Test if we have exactly one visible widget as a child,
14340 // and if so assume that we are a container for that widget,
14341 // and should propagate startup() and resize() calls to it.
14342 // Skips over things like data stores since they aren't visible.
14343
14344 var candidateWidgets = [],
14345 otherVisibleNodes = false;
14346
14347 query("> *", this.containerNode).some(function(node){
14348 var widget = registry.byNode(node);
14349 if(widget && widget.resize){
14350 candidateWidgets.push(widget);
14351 }else if(node.offsetHeight){
14352 otherVisibleNodes = true;
14353 }
14354 });
14355
14356 this._singleChild = candidateWidgets.length == 1 && !otherVisibleNodes ?
14357 candidateWidgets[0] : null;
14358
14359 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
14360 domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
14361 },
14362
14363 resize: function(changeSize, resultSize){
14364 // summary:
14365 // See `dijit/layout/_LayoutWidget.resize()` for description.
14366 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14367 // the same API.
14368
14369 this._resizeCalled = true;
14370
14371 this._scheduleLayout(changeSize, resultSize);
14372 },
14373
14374 _scheduleLayout: function(changeSize, resultSize){
14375 // summary:
14376 // Resize myself, and call resize() on each of my child layout widgets, either now
14377 // (if I'm currently visible) or when I become visible
14378 if(this._isShown()){
14379 this._layout(changeSize, resultSize);
14380 }else{
14381 this._needLayout = true;
14382 this._changeSize = changeSize;
14383 this._resultSize = resultSize;
14384 }
14385 },
14386
14387 _layout: function(changeSize, resultSize){
14388 // summary:
14389 // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
14390 // Also, since I am an isLayoutContainer widget, each of my children expects me to
14391 // call resize() or layout() on it.
14392 //
14393 // Should be called on initialization and also whenever we get new content
14394 // (from an href, or from set('content', ...))... but deferred until
14395 // the ContentPane is visible
14396
14397 delete this._needLayout;
14398
14399 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
14400 // never called directly, so resize() is our trigger to do the initial href download (see [20099]).
14401 // However, don't load href for closed TitlePanes.
14402 if(!this._wasShown && this.open !== false){
14403 this._onShow();
14404 }
14405
14406 // Set margin box size, unless it wasn't specified, in which case use current size.
14407 if(changeSize){
14408 domGeometry.setMarginBox(this.domNode, changeSize);
14409 }
14410
14411 // Compute content box size of containerNode in case we [later] need to size our single child.
14412 var cn = this.containerNode;
14413 if(cn === this.domNode){
14414 // If changeSize or resultSize was passed to this method and this.containerNode ==
14415 // this.domNode then we can compute the content-box size without querying the node,
14416 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
14417 var mb = resultSize || {};
14418 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
14419 if(!("h" in mb) || !("w" in mb)){
14420 mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
14421 }
14422 this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
14423 }else{
14424 this._contentBox = domGeometry.getContentBox(cn);
14425 }
14426
14427 this._layoutChildren();
14428 },
14429
14430 _layoutChildren: function(){
14431 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
14432 // of the ContentPane (rather than changing it through the set("content", ...) API.
14433 if(this.doLayout){
14434 this._checkIfSingleChild();
14435 }
14436
14437 if(this._singleChild && this._singleChild.resize){
14438 var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
14439
14440 // note: if widget has padding this._contentBox will have l and t set,
14441 // but don't pass them to resize() or it will doubly-offset the child
14442 this._singleChild.resize({w: cb.w, h: cb.h});
14443 }else{
14444 // All my child widgets are independently sized (rather than matching my size),
14445 // but I still need to call resize() on each child to make it layout.
14446 array.forEach(this.getChildren(), function(widget){
14447 if(widget.resize){
14448 widget.resize();
14449 }
14450 });
14451 }
14452 },
14453
14454 _isShown: function(){
14455 // summary:
14456 // Returns true if the content is currently shown.
14457 // description:
14458 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
14459 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
14460 // tree every call, and at least solves the performance problem on page load by deferring loading
14461 // hidden ContentPanes until they are first shown
14462
14463 if(this._childOfLayoutWidget){
14464 // If we are TitlePane, etc - we return that only *IF* we've been resized
14465 if(this._resizeCalled && "open" in this){
14466 return this.open;
14467 }
14468 return this._resizeCalled;
14469 }else if("open" in this){
14470 return this.open; // for TitlePane, etc.
14471 }else{
14472 var node = this.domNode, parent = this.domNode.parentNode;
14473 return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
14474 parent && parent.style && (parent.style.display != 'none');
14475 }
14476 },
14477
14478 _onShow: function(){
14479 // summary:
14480 // Called when the ContentPane is made visible
14481 // description:
14482 // For a plain ContentPane, this is called on initialization, from startup().
14483 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
14484 // called whenever the pane is made visible.
14485 //
14486 // Does layout/resize of child widget(s)
14487
14488 // Need to keep track of whether ContentPane has been shown (which is different than
14489 // whether or not it's currently visible).
14490 this._wasShown = true;
14491
14492 if(this._needLayout){
14493 // If a layout has been scheduled for when we become visible, do it now
14494 this._layout(this._changeSize, this._resultSize);
14495 }
14496
14497 this.inherited(arguments);
14498 }
14499 });
14500
14501 });
14502
14503 },
14504 'dijit/WidgetSet':function(){
14505 define("dijit/WidgetSet", [
14506 "dojo/_base/array", // array.forEach array.map
14507 "dojo/_base/declare", // declare
14508 "dojo/_base/kernel", // kernel.global
14509 "./registry" // to add functions to dijit.registry
14510 ], function(array, declare, kernel, registry){
14511
14512 // module:
14513 // dijit/WidgetSet
14514
14515 var WidgetSet = declare("dijit.WidgetSet", null, {
14516 // summary:
14517 // A set of widgets indexed by id.
14518 // Deprecated, will be removed in 2.0.
14519 //
14520 // example:
14521 // Create a small list of widgets:
14522 // | require(["dijit/WidgetSet", "dijit/registry"],
14523 // | function(WidgetSet, registry){
14524 // | var ws = new WidgetSet();
14525 // | ws.add(registry.byId("one"));
14526 // | ws.add(registry.byId("two"));
14527 // | // destroy both:
14528 // | ws.forEach(function(w){ w.destroy(); });
14529 // | });
14530
14531 constructor: function(){
14532 this._hash = {};
14533 this.length = 0;
14534 },
14535
14536 add: function(/*dijit/_WidgetBase*/ widget){
14537 // summary:
14538 // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
14539 //
14540 // widget: dijit/_WidgetBase
14541 // Any dijit/_WidgetBase subclass.
14542 if(this._hash[widget.id]){
14543 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
14544 }
14545 this._hash[widget.id] = widget;
14546 this.length++;
14547 },
14548
14549 remove: function(/*String*/ id){
14550 // summary:
14551 // Remove a widget from this WidgetSet. Does not destroy the widget; simply
14552 // removes the reference.
14553 if(this._hash[id]){
14554 delete this._hash[id];
14555 this.length--;
14556 }
14557 },
14558
14559 forEach: function(/*Function*/ func, /* Object? */thisObj){
14560 // summary:
14561 // Call specified function for each widget in this set.
14562 //
14563 // func:
14564 // A callback function to run for each item. Is passed the widget, the index
14565 // in the iteration, and the full hash, similar to `array.forEach`.
14566 //
14567 // thisObj:
14568 // An optional scope parameter
14569 //
14570 // example:
14571 // Using the default `dijit.registry` instance:
14572 // | require(["dijit/WidgetSet", "dijit/registry"],
14573 // | function(WidgetSet, registry){
14574 // | registry.forEach(function(widget){
14575 // | console.log(widget.declaredClass);
14576 // | });
14577 // | });
14578 //
14579 // returns:
14580 // Returns self, in order to allow for further chaining.
14581
14582 thisObj = thisObj || kernel.global;
14583 var i = 0, id;
14584 for(id in this._hash){
14585 func.call(thisObj, this._hash[id], i++, this._hash);
14586 }
14587 return this; // dijit/WidgetSet
14588 },
14589
14590 filter: function(/*Function*/ filter, /* Object? */thisObj){
14591 // summary:
14592 // Filter down this WidgetSet to a smaller new WidgetSet
14593 // Works the same as `array.filter` and `NodeList.filter`
14594 //
14595 // filter:
14596 // Callback function to test truthiness. Is passed the widget
14597 // reference and the pseudo-index in the object.
14598 //
14599 // thisObj: Object?
14600 // Option scope to use for the filter function.
14601 //
14602 // example:
14603 // Arbitrary: select the odd widgets in this list
14604 // |
14605 // |
14606 // |
14607 // | require(["dijit/WidgetSet", "dijit/registry"],
14608 // | function(WidgetSet, registry){
14609 // | registry.filter(function(w, i){
14610 // | return i % 2 == 0;
14611 // | }).forEach(function(w){ /* odd ones */ });
14612 // | });
14613
14614 thisObj = thisObj || kernel.global;
14615 var res = new WidgetSet(), i = 0, id;
14616 for(id in this._hash){
14617 var w = this._hash[id];
14618 if(filter.call(thisObj, w, i++, this._hash)){
14619 res.add(w);
14620 }
14621 }
14622 return res; // dijit/WidgetSet
14623 },
14624
14625 byId: function(/*String*/ id){
14626 // summary:
14627 // Find a widget in this list by it's id.
14628 // example:
14629 // Test if an id is in a particular WidgetSet
14630 // | require(["dijit/WidgetSet", "dijit/registry"],
14631 // | function(WidgetSet, registry){
14632 // | var ws = new WidgetSet();
14633 // | ws.add(registry.byId("bar"));
14634 // | var t = ws.byId("bar") // returns a widget
14635 // | var x = ws.byId("foo"); // returns undefined
14636 // | });
14637
14638 return this._hash[id]; // dijit/_WidgetBase
14639 },
14640
14641 byClass: function(/*String*/ cls){
14642 // summary:
14643 // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
14644 //
14645 // cls: String
14646 // The Class to scan for. Full dot-notated string.
14647 //
14648 // example:
14649 // Find all `dijit.TitlePane`s in a page:
14650 // | require(["dijit/WidgetSet", "dijit/registry"],
14651 // | function(WidgetSet, registry){
14652 // | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
14653 // | });
14654
14655 var res = new WidgetSet(), id, widget;
14656 for(id in this._hash){
14657 widget = this._hash[id];
14658 if(widget.declaredClass == cls){
14659 res.add(widget);
14660 }
14661 }
14662 return res; // dijit/WidgetSet
14663 },
14664
14665 toArray: function(){
14666 // summary:
14667 // Convert this WidgetSet into a true Array
14668 //
14669 // example:
14670 // Work with the widget .domNodes in a real Array
14671 // | require(["dijit/WidgetSet", "dijit/registry"],
14672 // | function(WidgetSet, registry){
14673 // | array.map(registry.toArray(), function(w){ return w.domNode; });
14674 // | });
14675
14676
14677 var ar = [];
14678 for(var id in this._hash){
14679 ar.push(this._hash[id]);
14680 }
14681 return ar; // dijit/_WidgetBase[]
14682 },
14683
14684 map: function(/* Function */func, /* Object? */thisObj){
14685 // summary:
14686 // Create a new Array from this WidgetSet, following the same rules as `array.map`
14687 // example:
14688 // | require(["dijit/WidgetSet", "dijit/registry"],
14689 // | function(WidgetSet, registry){
14690 // | var nodes = registry.map(function(w){ return w.domNode; });
14691 // | });
14692 //
14693 // returns:
14694 // A new array of the returned values.
14695 return array.map(this.toArray(), func, thisObj); // Array
14696 },
14697
14698 every: function(func, thisObj){
14699 // summary:
14700 // A synthetic clone of `array.every` acting explicitly on this WidgetSet
14701 //
14702 // func: Function
14703 // A callback function run for every widget in this list. Exits loop
14704 // when the first false return is encountered.
14705 //
14706 // thisObj: Object?
14707 // Optional scope parameter to use for the callback
14708
14709 thisObj = thisObj || kernel.global;
14710 var x = 0, i;
14711 for(i in this._hash){
14712 if(!func.call(thisObj, this._hash[i], x++, this._hash)){
14713 return false; // Boolean
14714 }
14715 }
14716 return true; // Boolean
14717 },
14718
14719 some: function(func, thisObj){
14720 // summary:
14721 // A synthetic clone of `array.some` acting explicitly on this WidgetSet
14722 //
14723 // func: Function
14724 // A callback function run for every widget in this list. Exits loop
14725 // when the first true return is encountered.
14726 //
14727 // thisObj: Object?
14728 // Optional scope parameter to use for the callback
14729
14730 thisObj = thisObj || kernel.global;
14731 var x = 0, i;
14732 for(i in this._hash){
14733 if(func.call(thisObj, this._hash[i], x++, this._hash)){
14734 return true; // Boolean
14735 }
14736 }
14737 return false; // Boolean
14738 }
14739
14740 });
14741
14742 // Add in 1.x compatibility methods to dijit/registry.
14743 // These functions won't show up in the API doc but since they are deprecated anyway,
14744 // that's probably for the best.
14745 array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
14746 registry[func] = WidgetSet.prototype[func];
14747 });
14748
14749
14750 return WidgetSet;
14751 });
14752
14753 },
14754 'dojo/dnd/Moveable':function(){
14755 define("dojo/dnd/Moveable", [
14756 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang",
14757 "../dom", "../dom-class", "../Evented", "../on", "../topic", "../touch", "./common", "./Mover", "../_base/window"
14758 ], function(array, declare, event, lang, dom, domClass, Evented, on, topic, touch, dnd, Mover, win){
14759
14760 // module:
14761 // dojo/dnd/Moveable
14762
14763
14764 var Moveable = declare("dojo.dnd.Moveable", [Evented], {
14765 // summary:
14766 // an object, which makes a node movable
14767
14768 // object attributes (for markup)
14769 handle: "",
14770 delay: 0,
14771 skip: false,
14772
14773 constructor: function(node, params){
14774 // node: Node
14775 // a node (or node's id) to be moved
14776 // params: Moveable.__MoveableArgs?
14777 // optional parameters
14778 this.node = dom.byId(node);
14779 if(!params){ params = {}; }
14780 this.handle = params.handle ? dom.byId(params.handle) : null;
14781 if(!this.handle){ this.handle = this.node; }
14782 this.delay = params.delay > 0 ? params.delay : 0;
14783 this.skip = params.skip;
14784 this.mover = params.mover ? params.mover : Mover;
14785 this.events = [
14786 on(this.handle, touch.press, lang.hitch(this, "onMouseDown")),
14787 // cancel text selection and text dragging
14788 on(this.handle, "dragstart", lang.hitch(this, "onSelectStart")),
14789 on(this.handle, "selectstart", lang.hitch(this, "onSelectStart"))
14790 ];
14791 },
14792
14793 // markup methods
14794 markupFactory: function(params, node, Ctor){
14795 return new Ctor(node, params);
14796 },
14797
14798 // methods
14799 destroy: function(){
14800 // summary:
14801 // stops watching for possible move, deletes all references, so the object can be garbage-collected
14802 array.forEach(this.events, function(handle){ handle.remove(); });
14803 this.events = this.node = this.handle = null;
14804 },
14805
14806 // mouse event processors
14807 onMouseDown: function(e){
14808 // summary:
14809 // event processor for onmousedown/ontouchstart, creates a Mover for the node
14810 // e: Event
14811 // mouse/touch event
14812 if(this.skip && dnd.isFormElement(e)){ return; }
14813 if(this.delay){
14814 this.events.push(
14815 on(this.handle, touch.move, lang.hitch(this, "onMouseMove")),
14816 on(this.handle, touch.release, lang.hitch(this, "onMouseUp"))
14817 );
14818 this._lastX = e.pageX;
14819 this._lastY = e.pageY;
14820 }else{
14821 this.onDragDetected(e);
14822 }
14823 event.stop(e);
14824 },
14825 onMouseMove: function(e){
14826 // summary:
14827 // event processor for onmousemove/ontouchmove, used only for delayed drags
14828 // e: Event
14829 // mouse/touch event
14830 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
14831 this.onMouseUp(e);
14832 this.onDragDetected(e);
14833 }
14834 event.stop(e);
14835 },
14836 onMouseUp: function(e){
14837 // summary:
14838 // event processor for onmouseup, used only for delayed drags
14839 // e: Event
14840 // mouse event
14841 for(var i = 0; i < 2; ++i){
14842 this.events.pop().remove();
14843 }
14844 event.stop(e);
14845 },
14846 onSelectStart: function(e){
14847 // summary:
14848 // event processor for onselectevent and ondragevent
14849 // e: Event
14850 // mouse event
14851 if(!this.skip || !dnd.isFormElement(e)){
14852 event.stop(e);
14853 }
14854 },
14855
14856 // local events
14857 onDragDetected: function(/*Event*/ e){
14858 // summary:
14859 // called when the drag is detected;
14860 // responsible for creation of the mover
14861 new this.mover(this.node, e, this);
14862 },
14863 onMoveStart: function(/*Mover*/ mover){
14864 // summary:
14865 // called before every move operation
14866 topic.publish("/dnd/move/start", mover);
14867 domClass.add(win.body(), "dojoMove");
14868 domClass.add(this.node, "dojoMoveItem");
14869 },
14870 onMoveStop: function(/*Mover*/ mover){
14871 // summary:
14872 // called after every move operation
14873 topic.publish("/dnd/move/stop", mover);
14874 domClass.remove(win.body(), "dojoMove");
14875 domClass.remove(this.node, "dojoMoveItem");
14876 },
14877 onFirstMove: function(/*===== mover, e =====*/){
14878 // summary:
14879 // called during the very first move notification;
14880 // can be used to initialize coordinates, can be overwritten.
14881 // mover: Mover
14882 // e: Event
14883
14884 // default implementation does nothing
14885 },
14886 onMove: function(mover, leftTop /*=====, e =====*/){
14887 // summary:
14888 // called during every move notification;
14889 // should actually move the node; can be overwritten.
14890 // mover: Mover
14891 // leftTop: Object
14892 // e: Event
14893 this.onMoving(mover, leftTop);
14894 var s = mover.node.style;
14895 s.left = leftTop.l + "px";
14896 s.top = leftTop.t + "px";
14897 this.onMoved(mover, leftTop);
14898 },
14899 onMoving: function(/*===== mover, leftTop =====*/){
14900 // summary:
14901 // called before every incremental move; can be overwritten.
14902 // mover: Mover
14903 // leftTop: Object
14904
14905 // default implementation does nothing
14906 },
14907 onMoved: function(/*===== mover, leftTop =====*/){
14908 // summary:
14909 // called after every incremental move; can be overwritten.
14910 // mover: Mover
14911 // leftTop: Object
14912
14913 // default implementation does nothing
14914 }
14915 });
14916
14917 /*=====
14918 Moveable.__MoveableArgs = declare([], {
14919 // handle: Node||String
14920 // A node (or node's id), which is used as a mouse handle.
14921 // If omitted, the node itself is used as a handle.
14922 handle: null,
14923
14924 // delay: Number
14925 // delay move by this number of pixels
14926 delay: 0,
14927
14928 // skip: Boolean
14929 // skip move of form elements
14930 skip: false,
14931
14932 // mover: Object
14933 // a constructor of custom Mover
14934 mover: dnd.Mover
14935 });
14936 =====*/
14937
14938 return Moveable;
14939 });
14940
14941 },
14942 'dojo/store/util/SimpleQueryEngine':function(){
14943 define("dojo/store/util/SimpleQueryEngine", ["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil /*=====, Store =====*/){
14944
14945 // module:
14946 // dojo/store/util/SimpleQueryEngine
14947
14948 return function(query, options){
14949 // summary:
14950 // Simple query engine that matches using filter functions, named filter
14951 // functions or objects by name-value on a query object hash
14952 //
14953 // description:
14954 // The SimpleQueryEngine provides a way of getting a QueryResults through
14955 // the use of a simple object hash as a filter. The hash will be used to
14956 // match properties on data objects with the corresponding value given. In
14957 // other words, only exact matches will be returned.
14958 //
14959 // This function can be used as a template for more complex query engines;
14960 // for example, an engine can be created that accepts an object hash that
14961 // contains filtering functions, or a string that gets evaluated, etc.
14962 //
14963 // When creating a new dojo.store, simply set the store's queryEngine
14964 // field as a reference to this function.
14965 //
14966 // query: Object
14967 // An object hash with fields that may match fields of items in the store.
14968 // Values in the hash will be compared by normal == operator, but regular expressions
14969 // or any object that provides a test() method are also supported and can be
14970 // used to match strings by more complex expressions
14971 // (and then the regex's or object's test() method will be used to match values).
14972 //
14973 // options: dojo/store/api/Store.QueryOptions?
14974 // An object that contains optional information such as sort, start, and count.
14975 //
14976 // returns: Function
14977 // A function that caches the passed query under the field "matches". See any
14978 // of the "query" methods on dojo.stores.
14979 //
14980 // example:
14981 // Define a store with a reference to this engine, and set up a query method.
14982 //
14983 // | var myStore = function(options){
14984 // | // ...more properties here
14985 // | this.queryEngine = SimpleQueryEngine;
14986 // | // define our query method
14987 // | this.query = function(query, options){
14988 // | return QueryResults(this.queryEngine(query, options)(this.data));
14989 // | };
14990 // | };
14991
14992 // create our matching query function
14993 switch(typeof query){
14994 default:
14995 throw new Error("Can not query with a " + typeof query);
14996 case "object": case "undefined":
14997 var queryObject = query;
14998 query = function(object){
14999 for(var key in queryObject){
15000 var required = queryObject[key];
15001 if(required && required.test){
15002 // an object can provide a test method, which makes it work with regex
15003 if(!required.test(object[key], object)){
15004 return false;
15005 }
15006 }else if(required != object[key]){
15007 return false;
15008 }
15009 }
15010 return true;
15011 };
15012 break;
15013 case "string":
15014 // named query
15015 if(!this[query]){
15016 throw new Error("No filter function " + query + " was found in store");
15017 }
15018 query = this[query];
15019 // fall through
15020 case "function":
15021 // fall through
15022 }
15023 function execute(array){
15024 // execute the whole query, first we filter
15025 var results = arrayUtil.filter(array, query);
15026 // next we sort
15027 var sortSet = options && options.sort;
15028 if(sortSet){
15029 results.sort(typeof sortSet == "function" ? sortSet : function(a, b){
15030 for(var sort, i=0; sort = sortSet[i]; i++){
15031 var aValue = a[sort.attribute];
15032 var bValue = b[sort.attribute];
15033 if (aValue != bValue){
15034 return !!sort.descending == (aValue == null || aValue > bValue) ? -1 : 1;
15035 }
15036 }
15037 return 0;
15038 });
15039 }
15040 // now we paginate
15041 if(options && (options.start || options.count)){
15042 var total = results.length;
15043 results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
15044 results.total = total;
15045 }
15046 return results;
15047 }
15048 execute.matches = query;
15049 return execute;
15050 };
15051
15052 });
15053
15054 },
15055 'dijit/typematic':function(){
15056 define("dijit/typematic", [
15057 "dojo/_base/array", // array.forEach
15058 "dojo/_base/connect", // connect.connect
15059 "dojo/_base/event", // event.stop
15060 "dojo/_base/kernel", // kernel.deprecated
15061 "dojo/_base/lang", // lang.mixin, lang.hitch
15062 "dojo/on",
15063 "dojo/sniff", // has("ie")
15064 "./main" // setting dijit.typematic global
15065 ], function(array, connect, event, kernel, lang, on, has, dijit){
15066
15067 // module:
15068 // dijit/typematic
15069
15070 var typematic = (dijit.typematic = {
15071 // summary:
15072 // These functions are used to repetitively call a user specified callback
15073 // method when a specific key or mouse click over a specific DOM node is
15074 // held down for a specific amount of time.
15075 // Only 1 such event is allowed to occur on the browser page at 1 time.
15076
15077 _fireEventAndReload: function(){
15078 this._timer = null;
15079 this._callback(++this._count, this._node, this._evt);
15080
15081 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
15082 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
15083 this._currentTimeout = Math.max(
15084 this._currentTimeout < 0 ? this._initialDelay :
15085 (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
15086 this._minDelay);
15087 this._timer = setTimeout(lang.hitch(this, "_fireEventAndReload"), this._currentTimeout);
15088 },
15089
15090 trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number?*/ subsequentDelay, /*Number?*/ initialDelay, /*Number?*/ minDelay){
15091 // summary:
15092 // Start a timed, repeating callback sequence.
15093 // If already started, the function call is ignored.
15094 // This method is not normally called by the user but can be
15095 // when the normal listener code is insufficient.
15096 // evt:
15097 // key or mouse event object to pass to the user callback
15098 // _this:
15099 // pointer to the user's widget space.
15100 // node:
15101 // the DOM node object to pass the the callback function
15102 // callback:
15103 // function to call until the sequence is stopped called with 3 parameters:
15104 // count:
15105 // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
15106 // node:
15107 // the DOM node object passed in
15108 // evt:
15109 // key or mouse event object
15110 // obj:
15111 // user space object used to uniquely identify each typematic sequence
15112 // subsequentDelay:
15113 // if > 1, the number of milliseconds until the 3->n events occur
15114 // or else the fractional time multiplier for the next event's delay, default=0.9
15115 // initialDelay:
15116 // the number of milliseconds until the 2nd event occurs, default=500ms
15117 // minDelay:
15118 // the maximum delay in milliseconds for event to fire, default=10ms
15119 if(obj != this._obj){
15120 this.stop();
15121 this._initialDelay = initialDelay || 500;
15122 this._subsequentDelay = subsequentDelay || 0.90;
15123 this._minDelay = minDelay || 10;
15124 this._obj = obj;
15125 this._node = node;
15126 this._currentTimeout = -1;
15127 this._count = -1;
15128 this._callback = lang.hitch(_this, callback);
15129 this._evt = { faux: true };
15130 for(var attr in evt){
15131 if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
15132 var v = evt[attr];
15133 if(typeof v != "function" && typeof v != "undefined"){ this._evt[attr] = v }
15134 }
15135 }
15136 this._fireEventAndReload();
15137 }
15138 },
15139
15140 stop: function(){
15141 // summary:
15142 // Stop an ongoing timed, repeating callback sequence.
15143 if(this._timer){
15144 clearTimeout(this._timer);
15145 this._timer = null;
15146 }
15147 if(this._obj){
15148 this._callback(-1, this._node, this._evt);
15149 this._obj = null;
15150 }
15151 },
15152
15153 addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15154 // summary:
15155 // Start listening for a specific typematic key.
15156 // See also the trigger method for other parameters.
15157 // keyObject:
15158 // an object defining the key to listen for:
15159 //
15160 // - charOrCode: the printable character (string) or keyCode (number) to listen for.
15161 // - keyCode: (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
15162 // - charCode: (deprecated - use charOrCode) the charCode (number) to listen for.
15163 // - ctrlKey: desired ctrl key state to initiate the callback sequence:
15164 // - pressed (true)
15165 // - released (false)
15166 // - either (unspecified)
15167 // - altKey: same as ctrlKey but for the alt key
15168 // - shiftKey: same as ctrlKey but for the shift key
15169 // returns:
15170 // a connection handle
15171
15172 if(keyObject.keyCode){
15173 keyObject.charOrCode = keyObject.keyCode;
15174 kernel.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15175 }else if(keyObject.charCode){
15176 keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
15177 kernel.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15178 }
15179 var handles = [
15180 on(node, connect._keypress, lang.hitch(this, function(evt){
15181 if(evt.charOrCode == keyObject.charOrCode &&
15182 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
15183 (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
15184 (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
15185 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
15186 event.stop(evt);
15187 typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
15188 }else if(typematic._obj == keyObject){
15189 typematic.stop();
15190 }
15191 })),
15192 on(node, "keyup", lang.hitch(this, function(){
15193 if(typematic._obj == keyObject){
15194 typematic.stop();
15195 }
15196 }))
15197 ];
15198 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15199 },
15200
15201 addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15202 // summary:
15203 // Start listening for a typematic mouse click.
15204 // See the trigger method for other parameters.
15205 // returns:
15206 // a connection handle
15207 var handles = [
15208 on(node, "mousedown", lang.hitch(this, function(evt){
15209 evt.preventDefault();
15210 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
15211 })),
15212 on(node, "mouseup", lang.hitch(this, function(evt){
15213 if(this._obj){
15214 evt.preventDefault();
15215 }
15216 typematic.stop();
15217 })),
15218 on(node, "mouseout", lang.hitch(this, function(evt){
15219 if(this._obj){
15220 evt.preventDefault();
15221 }
15222 typematic.stop();
15223 })),
15224 on(node, "dblclick", lang.hitch(this, function(evt){
15225 evt.preventDefault();
15226 if(has("ie") < 9){
15227 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
15228 setTimeout(lang.hitch(this, typematic.stop), 50);
15229 }
15230 }))
15231 ];
15232 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15233 },
15234
15235 addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
15236 // summary:
15237 // Start listening for a specific typematic key and mouseclick.
15238 // This is a thin wrapper to addKeyListener and addMouseListener.
15239 // See the addMouseListener and addKeyListener methods for other parameters.
15240 // mouseNode:
15241 // the DOM node object to listen on for mouse events.
15242 // keyNode:
15243 // the DOM node object to listen on for key events.
15244 // returns:
15245 // a connection handle
15246 var handles = [
15247 this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay),
15248 this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)
15249 ];
15250 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
15251 }
15252 });
15253
15254 return typematic;
15255
15256 });
15257
15258 },
15259 'dijit/MenuItem':function(){
15260 require({cache:{
15261 '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"}});
15262 define("dijit/MenuItem", [
15263 "dojo/_base/declare", // declare
15264 "dojo/dom", // dom.setSelectable
15265 "dojo/dom-attr", // domAttr.set
15266 "dojo/dom-class", // domClass.toggle
15267 "dojo/_base/kernel", // kernel.deprecated
15268 "dojo/sniff", // has("ie")
15269 "./_Widget",
15270 "./_TemplatedMixin",
15271 "./_Contained",
15272 "./_CssStateMixin",
15273 "dojo/text!./templates/MenuItem.html"
15274 ], function(declare, dom, domAttr, domClass, kernel, has,
15275 _Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
15276
15277 // module:
15278 // dijit/MenuItem
15279
15280 return declare("dijit.MenuItem",
15281 [_Widget, _TemplatedMixin, _Contained, _CssStateMixin],
15282 {
15283 // summary:
15284 // A line item in a Menu Widget
15285
15286 // Make 3 columns
15287 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15288 templateString: template,
15289
15290 baseClass: "dijitMenuItem",
15291
15292 // label: String
15293 // Menu text
15294 label: "",
15295 _setLabelAttr: function(val){
15296 this.containerNode.innerHTML = val;
15297 this._set("label", val);
15298 if(this.textDir === "auto"){
15299 this.applyTextDir(this.focusNode, this.label);
15300 }
15301 },
15302
15303 // iconClass: String
15304 // Class to apply to DOMNode to make it display an icon.
15305 iconClass: "dijitNoIcon",
15306 _setIconClassAttr: { node: "iconNode", type: "class" },
15307
15308 // accelKey: String
15309 // Text for the accelerator (shortcut) key combination.
15310 // Note that although Menu can display accelerator keys there
15311 // is no infrastructure to actually catch and execute these
15312 // accelerators.
15313 accelKey: "",
15314
15315 // disabled: Boolean
15316 // If true, the menu item is disabled.
15317 // If false, the menu item is enabled.
15318 disabled: false,
15319
15320 _fillContent: function(/*DomNode*/ source){
15321 // If button label is specified as srcNodeRef.innerHTML rather than
15322 // this.params.label, handle it here.
15323 if(source && !("label" in this.params)){
15324 this.set('label', source.innerHTML);
15325 }
15326 },
15327
15328 buildRendering: function(){
15329 this.inherited(arguments);
15330 var label = this.id+"_text";
15331 domAttr.set(this.containerNode, "id", label);
15332 if(this.accelKeyNode){
15333 domAttr.set(this.accelKeyNode, "id", this.id + "_accel");
15334 label += " " + this.id + "_accel";
15335 }
15336 this.domNode.setAttribute("aria-labelledby", label);
15337 dom.setSelectable(this.domNode, false);
15338 },
15339
15340 onClick: function(/*Event*/){
15341 // summary:
15342 // User defined function to handle clicks
15343 // tags:
15344 // callback
15345 },
15346
15347 focus: function(){
15348 // summary:
15349 // Focus on this MenuItem
15350 try{
15351 if(has("ie") == 8){
15352 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15353 this.containerNode.focus();
15354 }
15355 this.focusNode.focus();
15356 }catch(e){
15357 // this throws on IE (at least) in some scenarios
15358 }
15359 },
15360
15361 _onFocus: function(){
15362 // summary:
15363 // This is called by the focus manager when focus
15364 // goes to this MenuItem or a child menu.
15365 // tags:
15366 // protected
15367 this._setSelected(true);
15368 this.getParent()._onItemFocus(this);
15369
15370 this.inherited(arguments);
15371 },
15372
15373 _setSelected: function(selected){
15374 // summary:
15375 // Indicate that this node is the currently selected one
15376 // tags:
15377 // private
15378
15379 /***
15380 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15381 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15382 * That's not supposed to happen, but the problem is:
15383 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15384 * points to the parent Menu, bypassing the parent MenuItem... thus the
15385 * MenuItem is not in the chain of active widgets and gets a premature call to
15386 * _onBlur()
15387 */
15388
15389 domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
15390 },
15391
15392 setLabel: function(/*String*/ content){
15393 // summary:
15394 // Deprecated. Use set('label', ...) instead.
15395 // tags:
15396 // deprecated
15397 kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
15398 this.set("label", content);
15399 },
15400
15401 setDisabled: function(/*Boolean*/ disabled){
15402 // summary:
15403 // Deprecated. Use set('disabled', bool) instead.
15404 // tags:
15405 // deprecated
15406 kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
15407 this.set('disabled', disabled);
15408 },
15409 _setDisabledAttr: function(/*Boolean*/ value){
15410 // summary:
15411 // Hook for attr('disabled', ...) to work.
15412 // Enable or disable this menu item.
15413
15414 this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
15415 this._set("disabled", value);
15416 },
15417 _setAccelKeyAttr: function(/*String*/ value){
15418 // summary:
15419 // Hook for attr('accelKey', ...) to work.
15420 // Set accelKey on this menu item.
15421
15422 this.accelKeyNode.style.display=value?"":"none";
15423 this.accelKeyNode.innerHTML=value;
15424 //have to use colSpan to make it work in IE
15425 domAttr.set(this.containerNode,'colSpan',value?"1":"2");
15426
15427 this._set("accelKey", value);
15428 },
15429 _setTextDirAttr: function(/*String*/ textDir){
15430 // summary:
15431 // Setter for textDir.
15432 // description:
15433 // Users shouldn't call this function; they should be calling
15434 // set('textDir', value)
15435 // tags:
15436 // private
15437
15438 // only if new textDir is different from the old one
15439 // and on widgets creation.
15440 if(!this._created || this.textDir != textDir){
15441 this._set("textDir", textDir);
15442 this.applyTextDir(this.focusNode, this.label);
15443 }
15444 }
15445 });
15446 });
15447
15448 },
15449 'dijit/layout/TabController':function(){
15450 require({cache:{
15451 '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"}});
15452 define("dijit/layout/TabController", [
15453 "dojo/_base/declare", // declare
15454 "dojo/dom", // dom.setSelectable
15455 "dojo/dom-attr", // domAttr.attr
15456 "dojo/dom-class", // domClass.toggle
15457 "dojo/i18n", // i18n.getLocalization
15458 "dojo/_base/lang", // lang.hitch lang.trim
15459 "./StackController",
15460 "../registry",
15461 "../Menu",
15462 "../MenuItem",
15463 "dojo/text!./templates/_TabButton.html",
15464 "dojo/i18n!../nls/common"
15465 ], function(declare, dom, domAttr, domClass, i18n, lang, StackController, registry, Menu, MenuItem, template){
15466
15467 // module:
15468 // dijit/layout/TabController
15469
15470 var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, {
15471 // summary:
15472 // A tab (the thing you click to select a pane).
15473 // description:
15474 // Contains the title of the pane, and optionally a close-button to destroy the pane.
15475 // This is an internal widget and should not be instantiated directly.
15476 // tags:
15477 // private
15478
15479 // baseClass: String
15480 // The CSS class applied to the domNode.
15481 baseClass: "dijitTab",
15482
15483 // Apply dijitTabCloseButtonHover when close button is hovered
15484 cssStateNodes: {
15485 closeNode: "dijitTabCloseButton"
15486 },
15487
15488 templateString: template,
15489
15490 // Override _FormWidget.scrollOnFocus.
15491 // Don't scroll the whole tab container into view when the button is focused.
15492 scrollOnFocus: false,
15493
15494 buildRendering: function(){
15495 this.inherited(arguments);
15496
15497 dom.setSelectable(this.containerNode, false);
15498 },
15499
15500 startup: function(){
15501 this.inherited(arguments);
15502 var n = this.domNode;
15503
15504 // Required to give IE6 a kick, as it initially hides the
15505 // tabs until they are focused on.
15506 this.defer(function(){
15507 n.className = n.className;
15508 }, 1);
15509 },
15510
15511 _setCloseButtonAttr: function(/*Boolean*/ disp){
15512 // summary:
15513 // Hide/show close button
15514 this._set("closeButton", disp);
15515 domClass.toggle(this.domNode, "dijitClosable", disp);
15516 this.closeNode.style.display = disp ? "" : "none";
15517 if(disp){
15518 var _nlsResources = i18n.getLocalization("dijit", "common");
15519 if(this.closeNode){
15520 domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
15521 }
15522 }
15523 },
15524
15525 _setDisabledAttr: function(/*Boolean*/ disabled){
15526 // summary:
15527 // Make tab selected/unselectable
15528
15529 this.inherited(arguments);
15530
15531 // Don't show tooltip for close button when tab is disabled
15532 if(this.closeNode){
15533 if(disabled){
15534 domAttr.remove(this.closeNode, "title");
15535 }else{
15536 var _nlsResources = i18n.getLocalization("dijit", "common");
15537 domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
15538 }
15539 }
15540 },
15541
15542 _setLabelAttr: function(/*String*/ content){
15543 // summary:
15544 // Hook for set('label', ...) to work.
15545 // description:
15546 // takes an HTML string.
15547 // Inherited ToggleButton implementation will Set the label (text) of the button;
15548 // Need to set the alt attribute of icon on tab buttons if no label displayed
15549 this.inherited(arguments);
15550 if(!this.showLabel && !this.params.title){
15551 this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
15552 }
15553 }
15554 });
15555
15556 var TabController = declare("dijit.layout.TabController", StackController, {
15557 // summary:
15558 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
15559 // Used internally by `dijit/layout/TabContainer`.
15560 // description:
15561 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
15562 // TabController also monitors the TabContainer, and whenever a pane is
15563 // added or deleted updates itself accordingly.
15564 // tags:
15565 // private
15566
15567 baseClass: "dijitTabController",
15568
15569 templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
15570
15571 // tabPosition: String
15572 // Defines where tabs go relative to the content.
15573 // "top", "bottom", "left-h", "right-h"
15574 tabPosition: "top",
15575
15576 // buttonWidget: Constructor
15577 // The tab widget to create to correspond to each page
15578 buttonWidget: TabButton,
15579
15580 // buttonWidgetCloseClass: String
15581 // Class of [x] close icon, used by event delegation code to tell when close button was clicked
15582 buttonWidgetCloseClass: "dijitTabCloseButton",
15583
15584 postCreate: function(){
15585 this.inherited(arguments);
15586
15587 // Setup a close menu to be shared between all the closable tabs (excluding disabled tabs)
15588 var closeMenu = new Menu({
15589 id: this.id+"_Menu",
15590 ownerDocument: this.ownerDocument,
15591 dir: this.dir,
15592 lang: this.lang,
15593 textDir: this.textDir,
15594 targetNodeIds: [this.domNode],
15595 selector: function(node){
15596 return domClass.contains(node, "dijitClosable") && !domClass.contains(node, "dijitTabDisabled");
15597 }
15598 });
15599 this.own(closeMenu);
15600
15601 var _nlsResources = i18n.getLocalization("dijit", "common"),
15602 controller = this;
15603 closeMenu.addChild(new MenuItem({
15604 label: _nlsResources.itemClose,
15605 ownerDocument: this.ownerDocument,
15606 dir: this.dir,
15607 lang: this.lang,
15608 textDir: this.textDir,
15609 onClick: function(evt){
15610 var button = registry.byNode(this.getParent().currentTarget);
15611 controller.onCloseButtonClick(button.page);
15612 }
15613 }));
15614 }
15615 });
15616
15617 TabController.TabButton = TabButton; // for monkey patching
15618
15619 return TabController;
15620 });
15621
15622 },
15623 'dijit/layout/_LayoutWidget':function(){
15624 define("dijit/layout/_LayoutWidget", [
15625 "dojo/_base/lang", // lang.mixin
15626 "../_Widget",
15627 "../_Container",
15628 "../_Contained",
15629 "../Viewport",
15630 "dojo/_base/declare", // declare
15631 "dojo/dom-class", // domClass.add domClass.remove
15632 "dojo/dom-geometry", // domGeometry.marginBox
15633 "dojo/dom-style" // domStyle.getComputedStyle
15634 ], function(lang, _Widget, _Container, _Contained, Viewport,
15635 declare, domClass, domGeometry, domStyle){
15636
15637 // module:
15638 // dijit/layout/_LayoutWidget
15639
15640
15641 return declare("dijit.layout._LayoutWidget", [_Widget, _Container, _Contained], {
15642 // summary:
15643 // Base class for a _Container widget which is responsible for laying out its children.
15644 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
15645
15646 // baseClass: [protected extension] String
15647 // This class name is applied to the widget's domNode
15648 // and also may be used to generate names for sub nodes,
15649 // for example dijitTabContainer-content.
15650 baseClass: "dijitLayoutContainer",
15651
15652 // isLayoutContainer: [protected] Boolean
15653 // Indicates that this widget is going to call resize() on its
15654 // children widgets, setting their size, when they become visible.
15655 isLayoutContainer: true,
15656
15657 buildRendering: function(){
15658 this.inherited(arguments);
15659 domClass.add(this.domNode, "dijitContainer");
15660 },
15661
15662 startup: function(){
15663 // summary:
15664 // Called after all the widgets have been instantiated and their
15665 // dom nodes have been inserted somewhere under win.doc.body.
15666 //
15667 // Widgets should override this method to do any initialization
15668 // dependent on other widgets existing, and then call
15669 // this superclass method to finish things off.
15670 //
15671 // startup() in subclasses shouldn't do anything
15672 // size related because the size of the widget hasn't been set yet.
15673
15674 if(this._started){ return; }
15675
15676 // Need to call inherited first - so that child widgets get started
15677 // up correctly
15678 this.inherited(arguments);
15679
15680 // If I am a not being controlled by a parent layout widget...
15681 var parent = this.getParent && this.getParent();
15682 if(!(parent && parent.isLayoutContainer)){
15683 // Do recursive sizing and layout of all my descendants
15684 // (passing in no argument to resize means that it has to glean the size itself)
15685 this.resize();
15686
15687 // Since my parent isn't a layout container, and my style *may be* width=height=100%
15688 // or something similar (either set directly or via a CSS class),
15689 // monitor when viewport size changes so that I can re-layout.
15690 this.own(Viewport.on("resize", lang.hitch(this, "resize")));
15691 }
15692 },
15693
15694 resize: function(changeSize, resultSize){
15695 // summary:
15696 // Call this to resize a widget, or after its size has changed.
15697 // description:
15698 // ####Change size mode:
15699 //
15700 // When changeSize is specified, changes the marginBox of this widget
15701 // and forces it to re-layout its contents accordingly.
15702 // changeSize may specify height, width, or both.
15703 //
15704 // If resultSize is specified it indicates the size the widget will
15705 // become after changeSize has been applied.
15706 //
15707 // ####Notification mode:
15708 //
15709 // When changeSize is null, indicates that the caller has already changed
15710 // the size of the widget, or perhaps it changed because the browser
15711 // window was resized. Tells widget to re-layout its contents accordingly.
15712 //
15713 // If resultSize is also specified it indicates the size the widget has
15714 // become.
15715 //
15716 // In either mode, this method also:
15717 //
15718 // 1. Sets this._borderBox and this._contentBox to the new size of
15719 // the widget. Queries the current domNode size if necessary.
15720 // 2. Calls layout() to resize contents (and maybe adjust child widgets).
15721 // changeSize: Object?
15722 // Sets the widget to this margin-box size and position.
15723 // May include any/all of the following properties:
15724 // | {w: int, h: int, l: int, t: int}
15725 // resultSize: Object?
15726 // The margin-box size of this widget after applying changeSize (if
15727 // changeSize is specified). If caller knows this size and
15728 // passes it in, we don't need to query the browser to get the size.
15729 // | {w: int, h: int}
15730
15731 var node = this.domNode;
15732
15733 // set margin box size, unless it wasn't specified, in which case use current size
15734 if(changeSize){
15735 domGeometry.setMarginBox(node, changeSize);
15736 }
15737
15738 // If either height or width wasn't specified by the user, then query node for it.
15739 // But note that setting the margin box and then immediately querying dimensions may return
15740 // inaccurate results, so try not to depend on it.
15741 var mb = resultSize || {};
15742 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
15743 if( !("h" in mb) || !("w" in mb) ){
15744 mb = lang.mixin(domGeometry.getMarginBox(node), mb); // just use domGeometry.marginBox() to fill in missing values
15745 }
15746
15747 // Compute and save the size of my border box and content box
15748 // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
15749 var cs = domStyle.getComputedStyle(node);
15750 var me = domGeometry.getMarginExtents(node, cs);
15751 var be = domGeometry.getBorderExtents(node, cs);
15752 var bb = (this._borderBox = {
15753 w: mb.w - (me.w + be.w),
15754 h: mb.h - (me.h + be.h)
15755 });
15756 var pe = domGeometry.getPadExtents(node, cs);
15757 this._contentBox = {
15758 l: domStyle.toPixelValue(node, cs.paddingLeft),
15759 t: domStyle.toPixelValue(node, cs.paddingTop),
15760 w: bb.w - pe.w,
15761 h: bb.h - pe.h
15762 };
15763
15764 // Callback for widget to adjust size of its children
15765 this.layout();
15766 },
15767
15768 layout: function(){
15769 // summary:
15770 // Widgets override this method to size and position their contents/children.
15771 // When this is called this._contentBox is guaranteed to be set (see resize()).
15772 //
15773 // This is called after startup(), and also when the widget's size has been
15774 // changed.
15775 // tags:
15776 // protected extension
15777 },
15778
15779 _setupChild: function(/*dijit/_WidgetBase*/child){
15780 // summary:
15781 // Common setup for initial children and children which are added after startup
15782 // tags:
15783 // protected extension
15784
15785 var cls = this.baseClass + "-child "
15786 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
15787 domClass.add(child.domNode, cls);
15788 },
15789
15790 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
15791 // Overrides _Container.addChild() to call _setupChild()
15792 this.inherited(arguments);
15793 if(this._started){
15794 this._setupChild(child);
15795 }
15796 },
15797
15798 removeChild: function(/*dijit/_WidgetBase*/ child){
15799 // Overrides _Container.removeChild() to remove class added by _setupChild()
15800 var cls = this.baseClass + "-child"
15801 + (child.baseClass ?
15802 " " + this.baseClass + "-" + child.baseClass : "");
15803 domClass.remove(child.domNode, cls);
15804
15805 this.inherited(arguments);
15806 }
15807 });
15808 });
15809
15810 },
15811 'dijit/popup':function(){
15812 define("dijit/popup", [
15813 "dojo/_base/array", // array.forEach array.some
15814 "dojo/aspect",
15815 "dojo/_base/connect", // connect._keypress
15816 "dojo/_base/declare", // declare
15817 "dojo/dom", // dom.isDescendant
15818 "dojo/dom-attr", // domAttr.set
15819 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
15820 "dojo/dom-geometry", // domGeometry.isBodyLtr
15821 "dojo/dom-style", // domStyle.set
15822 "dojo/_base/event", // event.stop
15823 "dojo/keys",
15824 "dojo/_base/lang", // lang.hitch
15825 "dojo/on",
15826 "dojo/sniff", // has("ie") has("mozilla")
15827 "./place",
15828 "./BackgroundIframe",
15829 "./main" // dijit (defining dijit.popup to match API doc)
15830 ], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has,
15831 place, BackgroundIframe, dijit){
15832
15833 // module:
15834 // dijit/popup
15835
15836 /*=====
15837 var __OpenArgs = {
15838 // popup: Widget
15839 // widget to display
15840 // parent: Widget
15841 // the button etc. that is displaying this popup
15842 // around: DomNode
15843 // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
15844 // x: Integer
15845 // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15846 // y: Integer
15847 // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15848 // orient: Object|String
15849 // When the around parameter is specified, orient should be a list of positions to try, ex:
15850 // | [ "below", "above" ]
15851 // For backwards compatibility it can also be an (ordered) hash of tuples of the form
15852 // (around-node-corner, popup-node-corner), ex:
15853 // | { "BL": "TL", "TL": "BL" }
15854 // where BL means "bottom left" and "TL" means "top left", etc.
15855 //
15856 // dijit/popup.open() tries to position the popup according to each specified position, in order,
15857 // until the popup appears fully within the viewport.
15858 //
15859 // The default value is ["below", "above"]
15860 //
15861 // When an (x,y) position is specified rather than an around node, orient is either
15862 // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
15863 // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
15864 // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
15865 // and the top-right corner.
15866 // onCancel: Function
15867 // callback when user has canceled the popup by:
15868 //
15869 // 1. hitting ESC or
15870 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
15871 // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
15872 // onClose: Function
15873 // callback whenever this popup is closed
15874 // onExecute: Function
15875 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
15876 // padding: place.__Position
15877 // adding a buffer around the opening position. This is only useful when around is not set.
15878 };
15879 =====*/
15880
15881 function destroyWrapper(){
15882 // summary:
15883 // Function to destroy wrapper when popup widget is destroyed.
15884 // Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
15885 if(this._popupWrapper){
15886 domConstruct.destroy(this._popupWrapper);
15887 delete this._popupWrapper;
15888 }
15889 }
15890
15891 var PopupManager = declare(null, {
15892 // summary:
15893 // Used to show drop downs (ex: the select list of a ComboBox)
15894 // or popups (ex: right-click context menus).
15895
15896 // _stack: dijit/_WidgetBase[]
15897 // Stack of currently popped up widgets.
15898 // (someone opened _stack[0], and then it opened _stack[1], etc.)
15899 _stack: [],
15900
15901 // _beginZIndex: Number
15902 // Z-index of the first popup. (If first popup opens other
15903 // popups they get a higher z-index.)
15904 _beginZIndex: 1000,
15905
15906 _idGen: 1,
15907
15908 _createWrapper: function(/*Widget*/ widget){
15909 // summary:
15910 // Initialization for widgets that will be used as popups.
15911 // Puts widget inside a wrapper DIV (if not already in one),
15912 // and returns pointer to that wrapper DIV.
15913
15914 var wrapper = widget._popupWrapper,
15915 node = widget.domNode;
15916
15917 if(!wrapper){
15918 // Create wrapper <div> for when this widget [in the future] will be used as a popup.
15919 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
15920 // to go wonky, see tests/robot/Toolbar.html to reproduce
15921 wrapper = domConstruct.create("div", {
15922 "class":"dijitPopup",
15923 style:{ display: "none"},
15924 role: "presentation"
15925 }, widget.ownerDocumentBody);
15926 wrapper.appendChild(node);
15927
15928 var s = node.style;
15929 s.display = "";
15930 s.visibility = "";
15931 s.position = "";
15932 s.top = "0px";
15933
15934 widget._popupWrapper = wrapper;
15935 aspect.after(widget, "destroy", destroyWrapper, true);
15936 }
15937
15938 return wrapper;
15939 },
15940
15941 moveOffScreen: function(/*Widget*/ widget){
15942 // summary:
15943 // Moves the popup widget off-screen.
15944 // Do not use this method to hide popups when not in use, because
15945 // that will create an accessibility issue: the offscreen popup is
15946 // still in the tabbing order.
15947
15948 // Create wrapper if not already there
15949 var wrapper = this._createWrapper(widget);
15950
15951 domStyle.set(wrapper, {
15952 visibility: "hidden",
15953 top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
15954 display: ""
15955 });
15956 },
15957
15958 hide: function(/*Widget*/ widget){
15959 // summary:
15960 // Hide this popup widget (until it is ready to be shown).
15961 // Initialization for widgets that will be used as popups
15962 //
15963 // Also puts widget inside a wrapper DIV (if not already in one)
15964 //
15965 // If popup widget needs to layout it should
15966 // do so when it is made visible, and popup._onShow() is called.
15967
15968 // Create wrapper if not already there
15969 var wrapper = this._createWrapper(widget);
15970
15971 domStyle.set(wrapper, "display", "none");
15972 },
15973
15974 getTopPopup: function(){
15975 // summary:
15976 // Compute the closest ancestor popup that's *not* a child of another popup.
15977 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
15978 var stack = this._stack;
15979 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
15980 /* do nothing, just trying to get right value for pi */
15981 }
15982 return stack[pi];
15983 },
15984
15985 open: function(/*__OpenArgs*/ args){
15986 // summary:
15987 // Popup the widget at the specified position
15988 //
15989 // example:
15990 // opening at the mouse position
15991 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
15992 //
15993 // example:
15994 // opening the widget as a dropdown
15995 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
15996 //
15997 // Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
15998 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
15999
16000 var stack = this._stack,
16001 widget = args.popup,
16002 orient = args.orient || ["below", "below-alt", "above", "above-alt"],
16003 ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(widget.ownerDocument),
16004 around = args.around,
16005 id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
16006
16007 // If we are opening a new popup that isn't a child of a currently opened popup, then
16008 // close currently opened popup(s). This should happen automatically when the old popups
16009 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
16010 while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
16011 this.close(stack[stack.length-1].widget);
16012 }
16013
16014 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
16015 var wrapper = this._createWrapper(widget);
16016
16017
16018 domAttr.set(wrapper, {
16019 id: id,
16020 style: {
16021 zIndex: this._beginZIndex + stack.length
16022 },
16023 "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
16024 dijitPopupParent: args.parent ? args.parent.id : ""
16025 });
16026
16027 if(has("ie") || has("mozilla")){
16028 if(!widget.bgIframe){
16029 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
16030 widget.bgIframe = new BackgroundIframe(wrapper);
16031 }
16032 }
16033
16034 // position the wrapper node and make it visible
16035 var best = around ?
16036 place.around(wrapper, around, orient, ltr, widget.orient ? lang.hitch(widget, "orient") : null) :
16037 place.at(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
16038
16039 wrapper.style.display = "";
16040 wrapper.style.visibility = "visible";
16041 widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown
16042
16043 var handlers = [];
16044
16045 // provide default escape and tab key handling
16046 // (this will work for any widget, not just menu)
16047 handlers.push(on(wrapper, connect._keypress, lang.hitch(this, function(evt){
16048 if(evt.charOrCode == keys.ESCAPE && args.onCancel){
16049 event.stop(evt);
16050 args.onCancel();
16051 }else if(evt.charOrCode === keys.TAB){
16052 event.stop(evt);
16053 var topPopup = this.getTopPopup();
16054 if(topPopup && topPopup.onCancel){
16055 topPopup.onCancel();
16056 }
16057 }
16058 })));
16059
16060 // watch for cancel/execute events on the popup and notify the caller
16061 // (for a menu, "execute" means clicking an item)
16062 if(widget.onCancel && args.onCancel){
16063 handlers.push(widget.on("cancel", args.onCancel));
16064 }
16065
16066 handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
16067 var topPopup = this.getTopPopup();
16068 if(topPopup && topPopup.onExecute){
16069 topPopup.onExecute();
16070 }
16071 })));
16072
16073 stack.push({
16074 widget: widget,
16075 parent: args.parent,
16076 onExecute: args.onExecute,
16077 onCancel: args.onCancel,
16078 onClose: args.onClose,
16079 handlers: handlers
16080 });
16081
16082 if(widget.onOpen){
16083 // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
16084 widget.onOpen(best);
16085 }
16086
16087 return best;
16088 },
16089
16090 close: function(/*Widget?*/ popup){
16091 // summary:
16092 // Close specified popup and any popups that it parented.
16093 // If no popup is specified, closes all popups.
16094
16095 var stack = this._stack;
16096
16097 // Basically work backwards from the top of the stack closing popups
16098 // until we hit the specified popup, but IIRC there was some issue where closing
16099 // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
16100 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
16101 // so the while condition is constructed defensively.
16102 while((popup && array.some(stack, function(elem){return elem.widget == popup;})) ||
16103 (!popup && stack.length)){
16104 var top = stack.pop(),
16105 widget = top.widget,
16106 onClose = top.onClose;
16107
16108 if(widget.onClose){
16109 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
16110 widget.onClose();
16111 }
16112
16113 var h;
16114 while(h = top.handlers.pop()){ h.remove(); }
16115
16116 // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
16117 if(widget && widget.domNode){
16118 this.hide(widget);
16119 }
16120
16121 if(onClose){
16122 onClose();
16123 }
16124 }
16125 }
16126 });
16127
16128 return (dijit.popup = new PopupManager());
16129 });
16130
16131 },
16132 'dijit/_base/manager':function(){
16133 define("dijit/_base/manager", [
16134 "dojo/_base/array",
16135 "dojo/_base/config", // defaultDuration
16136 "dojo/_base/lang",
16137 "../registry",
16138 "../main" // for setting exports to dijit namespace
16139 ], function(array, config, lang, registry, dijit){
16140
16141 // module:
16142 // dijit/_base/manager
16143
16144 var exports = {
16145 // summary:
16146 // Deprecated. Shim to methods on registry, plus a few other declarations.
16147 // New code should access dijit/registry directly when possible.
16148 };
16149
16150 array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){
16151 exports[name] = registry[name];
16152 });
16153
16154 lang.mixin(exports, {
16155 // defaultDuration: Integer
16156 // The default fx.animation speed (in ms) to use for all Dijit
16157 // transitional fx.animations, unless otherwise specified
16158 // on a per-instance basis. Defaults to 200, overrided by
16159 // `djConfig.defaultDuration`
16160 defaultDuration: config["defaultDuration"] || 200
16161 });
16162
16163 lang.mixin(dijit, exports);
16164
16165 /*===== return exports; =====*/
16166 return dijit; // for back compat :-(
16167 });
16168
16169 },
16170 'dijit/layout/StackController':function(){
16171 define("dijit/layout/StackController", [
16172 "dojo/_base/array", // array.forEach array.indexOf array.map
16173 "dojo/_base/declare", // declare
16174 "dojo/dom-class",
16175 "dojo/_base/event", // event.stop
16176 "dojo/keys", // keys
16177 "dojo/_base/lang", // lang.getObject
16178 "dojo/on",
16179 "../focus", // focus.focus()
16180 "../registry", // registry.byId
16181 "../_Widget",
16182 "../_TemplatedMixin",
16183 "../_Container",
16184 "../form/ToggleButton",
16185 "dojo/i18n!../nls/common"
16186 ], function(array, declare, domClass, event, keys, lang, on,
16187 focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
16188
16189 // module:
16190 // dijit/layout/StackController
16191
16192 var StackButton = declare("dijit.layout._StackButton", ToggleButton, {
16193 // summary:
16194 // Internal widget used by StackContainer.
16195 // description:
16196 // The button-like or tab-like object you click to select or delete a page
16197 // tags:
16198 // private
16199
16200 // Override _FormWidget.tabIndex.
16201 // StackContainer buttons are not in the tab order by default.
16202 // Probably we should be calling this.startupKeyNavChildren() instead.
16203 tabIndex: "-1",
16204
16205 // closeButton: Boolean
16206 // When true, display close button for this tab
16207 closeButton: false,
16208
16209 _aria_attr: "aria-selected",
16210
16211 buildRendering: function(/*Event*/ evt){
16212 this.inherited(arguments);
16213 (this.focusNode || this.domNode).setAttribute("role", "tab");
16214 }
16215 });
16216
16217
16218 var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], {
16219 // summary:
16220 // Set of buttons to select a page in a `dijit/layout/StackContainer`
16221 // description:
16222 // Monitors the specified StackContainer, and whenever a page is
16223 // added, deleted, or selected, updates itself accordingly.
16224
16225 baseClass: "dijitStackController",
16226
16227 templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
16228
16229 // containerId: [const] String
16230 // The id of the page container that I point to
16231 containerId: "",
16232
16233 // buttonWidget: [const] Constructor
16234 // The button widget to create to correspond to each page
16235 buttonWidget: StackButton,
16236
16237 // buttonWidgetCloseClass: String
16238 // CSS class of [x] close icon, used by event delegation code to tell when close button was clicked
16239 buttonWidgetCloseClass: "dijitStackCloseButton",
16240
16241 constructor: function(params /*===== , srcNodeRef =====*/){
16242 // summary:
16243 // Create the widget.
16244 // params: Object|null
16245 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
16246 // and functions, typically callbacks like onClick.
16247 // The hash can contain any of the widget's properties, excluding read-only properties.
16248 // srcNodeRef: DOMNode|String?
16249 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
16250
16251 this.pane2button = {}; // mapping from pane id to buttons
16252 },
16253
16254 postCreate: function(){
16255 this.inherited(arguments);
16256
16257 // Listen to notifications from StackContainer.
16258 // TODO: do this through bubbled events instead of topics
16259 this.subscribe(this.containerId+"-startup", "onStartup");
16260 this.subscribe(this.containerId+"-addChild", "onAddChild");
16261 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
16262 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
16263 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
16264
16265 // Listen for click events to select or close tabs.
16266 // No need to worry about ENTER/SPACE key handling: tabs are selected via left/right arrow keys,
16267 // and closed via shift-F10 (to show the close menu).
16268 this.connect(this.containerNode, 'click', function(evt){
16269 var button = registry.getEnclosingWidget(evt.target);
16270 if(button != this.containerNode && !button.disabled && button.page){
16271 for(var target = evt.target; target !== this.containerNode; target = target.parentNode){
16272 if(domClass.contains(target, this.buttonWidgetCloseClass)){
16273 this.onCloseButtonClick(button.page);
16274 break;
16275 }else if(target == button.domNode){
16276 this.onButtonClick(button.page);
16277 break;
16278 }
16279 }
16280 }
16281 });
16282 },
16283
16284 onStartup: function(/*Object*/ info){
16285 // summary:
16286 // Called after StackContainer has finished initializing
16287 // tags:
16288 // private
16289 array.forEach(info.children, this.onAddChild, this);
16290 if(info.selected){
16291 // Show button corresponding to selected pane (unless selected
16292 // is null because there are no panes)
16293 this.onSelectChild(info.selected);
16294 }
16295
16296 // Reflect events like page title changes to tab buttons
16297 var containerNode = registry.byId(this.containerId).containerNode,
16298 pane2button = this.pane2button,
16299 paneToButtonAttr = {
16300 "title": "label",
16301 "showtitle": "showLabel",
16302 "iconclass": "iconClass",
16303 "closable": "closeButton",
16304 "tooltip": "title",
16305 "disabled": "disabled"
16306 },
16307 connectFunc = function(attr, buttonAttr){
16308 return on(containerNode, "attrmodified-" + attr, function(evt){
16309 var button = pane2button[evt.detail && evt.detail.widget && evt.detail.widget.id];
16310 if(button){
16311 button.set(buttonAttr, evt.detail.newValue);
16312 }
16313 });
16314 };
16315 for(var attr in paneToButtonAttr){
16316 this.own(connectFunc(attr, paneToButtonAttr[attr]));
16317 }
16318 },
16319
16320 destroy: function(){
16321 // Since the buttons are internal to the StackController widget, destroy() should remove them, which is
16322 // done by calling onRemoveChild().
16323 for(var pane in this.pane2button){
16324 this.onRemoveChild(registry.byId(pane));
16325 }
16326
16327 // TODO: destroyRecursive() will call destroy() on each child button twice. Once from the above code,
16328 // and once because _WidgetBase.destroyDescendants() deletes anything inside of this.containerNode.
16329 // Probably shouldn't attach that DOMNode as this.containerNode.
16330
16331 this.inherited(arguments);
16332 },
16333
16334 onAddChild: function(/*dijit/_WidgetBase*/ page, /*Integer?*/ insertIndex){
16335 // summary:
16336 // Called whenever a page is added to the container.
16337 // Create button corresponding to the page.
16338 // tags:
16339 // private
16340
16341 // create an instance of the button widget
16342 // (remove typeof buttonWidget == string support in 2.0)
16343 var Cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
16344 var button = new Cls({
16345 id: this.id + "_" + page.id,
16346 name: this.id + "_" + page.id,
16347 label: page.title,
16348 disabled: page.disabled,
16349 ownerDocument: this.ownerDocument,
16350 dir: page.dir,
16351 lang: page.lang,
16352 textDir: page.textDir,
16353 showLabel: page.showTitle,
16354 iconClass: page.iconClass,
16355 closeButton: page.closable,
16356 title: page.tooltip,
16357 page: page
16358 });
16359
16360 this.addChild(button, insertIndex);
16361 this.pane2button[page.id] = button;
16362 page.controlButton = button; // this value might be overwritten if two tabs point to same container
16363 if(!this._currentChild){
16364 // If this is the first child then StackContainer will soon publish that it's selected,
16365 // but before that StackContainer calls layout(), and before layout() is called the
16366 // StackController needs to have the proper height... which means that the button needs
16367 // to be marked as selected now. See test_TabContainer_CSS.html for test.
16368 this.onSelectChild(page);
16369 }
16370 },
16371
16372 onRemoveChild: function(/*dijit/_WidgetBase*/ page){
16373 // summary:
16374 // Called whenever a page is removed from the container.
16375 // Remove the button corresponding to the page.
16376 // tags:
16377 // private
16378
16379 if(this._currentChild === page){ this._currentChild = null; }
16380
16381 var button = this.pane2button[page.id];
16382 if(button){
16383 this.removeChild(button);
16384 delete this.pane2button[page.id];
16385 button.destroy();
16386 }
16387 delete page.controlButton;
16388 },
16389
16390 onSelectChild: function(/*dijit/_WidgetBase*/ page){
16391 // summary:
16392 // Called when a page has been selected in the StackContainer, either by me or by another StackController
16393 // tags:
16394 // private
16395
16396 if(!page){ return; }
16397
16398 if(this._currentChild){
16399 var oldButton=this.pane2button[this._currentChild.id];
16400 oldButton.set('checked', false);
16401 oldButton.focusNode.setAttribute("tabIndex", "-1");
16402 }
16403
16404 var newButton=this.pane2button[page.id];
16405 newButton.set('checked', true);
16406 this._currentChild = page;
16407 newButton.focusNode.setAttribute("tabIndex", "0");
16408 var container = registry.byId(this.containerId);
16409 container.containerNode.setAttribute("aria-labelledby", newButton.id);
16410 },
16411
16412 onButtonClick: function(/*dijit/_WidgetBase*/ page){
16413 // summary:
16414 // Called whenever one of my child buttons is pressed in an attempt to select a page
16415 // tags:
16416 // private
16417
16418 var button = this.pane2button[page.id];
16419
16420 // For TabContainer where the tabs are <span>, need to set focus explicitly when left/right arrow
16421 focus.focus(button.focusNode);
16422
16423 if(this._currentChild && this._currentChild.id === page.id) {
16424 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
16425 button.set('checked', true);
16426 }
16427 var container = registry.byId(this.containerId);
16428 container.selectChild(page);
16429 },
16430
16431 onCloseButtonClick: function(/*dijit/_WidgetBase*/ page){
16432 // summary:
16433 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
16434 // tags:
16435 // private
16436
16437 var container = registry.byId(this.containerId);
16438 container.closeChild(page);
16439 if(this._currentChild){
16440 var b = this.pane2button[this._currentChild.id];
16441 if(b){
16442 focus.focus(b.focusNode || b.domNode);
16443 }
16444 }
16445 },
16446
16447 // TODO: this is a bit redundant with forward, back api in StackContainer
16448 adjacent: function(/*Boolean*/ forward){
16449 // summary:
16450 // Helper for onkeypress to find next/previous button
16451 // tags:
16452 // private
16453
16454 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
16455 // find currently focused button in children array
16456 var children = this.getChildren();
16457 var idx = array.indexOf(children, this.pane2button[this._currentChild.id]),
16458 current = children[idx];
16459
16460 // Pick next/previous non-disabled button to focus on. If we get back to the original button it means
16461 // that all buttons must be disabled, so return current child to avoid an infinite loop.
16462 var child;
16463 do{
16464 idx = (idx + (forward ? 1 : children.length - 1)) % children.length;
16465 child = children[idx];
16466 }while(child.disabled && child != current);
16467
16468 return child; // dijit/_WidgetBase
16469 },
16470
16471 onkeypress: function(/*Event*/ e){
16472 // summary:
16473 // Handle keystrokes on the page list, for advancing to next/previous button
16474 // and closing the current page if the page is closable.
16475 // tags:
16476 // private
16477
16478 if(this.disabled || e.altKey ){ return; }
16479 var forward = null;
16480 if(e.ctrlKey || !e._djpage){
16481 switch(e.charOrCode){
16482 case keys.LEFT_ARROW:
16483 case keys.UP_ARROW:
16484 if(!e._djpage){ forward = false; }
16485 break;
16486 case keys.PAGE_UP:
16487 if(e.ctrlKey){ forward = false; }
16488 break;
16489 case keys.RIGHT_ARROW:
16490 case keys.DOWN_ARROW:
16491 if(!e._djpage){ forward = true; }
16492 break;
16493 case keys.PAGE_DOWN:
16494 if(e.ctrlKey){ forward = true; }
16495 break;
16496 case keys.HOME:
16497 // Navigate to first non-disabled child
16498 var children = this.getChildren();
16499 for(var idx = 0; idx < children.length; idx++){
16500 var child = children[idx];
16501 if(!child.disabled){
16502 this.onButtonClick(child.page);
16503 break;
16504 }
16505 }
16506 event.stop(e);
16507 break;
16508 case keys.END:
16509 // Navigate to last non-disabled child
16510 var children = this.getChildren();
16511 for(var idx = children.length-1; idx >= 0; idx--){
16512 var child = children[idx];
16513 if(!child.disabled){
16514 this.onButtonClick(child.page);
16515 break;
16516 }
16517 }
16518 event.stop(e);
16519 break;
16520 case keys.DELETE:
16521 if(this._currentChild.closable){
16522 this.onCloseButtonClick(this._currentChild);
16523 }
16524 event.stop(e);
16525 break;
16526 default:
16527 if(e.ctrlKey){
16528 if(e.charOrCode === keys.TAB){
16529 this.onButtonClick(this.adjacent(!e.shiftKey).page);
16530 event.stop(e);
16531 }else if(e.charOrCode == "w"){
16532 if(this._currentChild.closable){
16533 this.onCloseButtonClick(this._currentChild);
16534 }
16535 event.stop(e); // avoid browser tab closing.
16536 }
16537 }
16538 }
16539 // handle next/previous page navigation (left/right arrow, etc.)
16540 if(forward !== null){
16541 this.onButtonClick(this.adjacent(forward).page);
16542 event.stop(e);
16543 }
16544 }
16545 },
16546
16547 onContainerKeyPress: function(/*Object*/ info){
16548 // summary:
16549 // Called when there was a keypress on the container
16550 // tags:
16551 // private
16552 info.e._djpage = info.page;
16553 this.onkeypress(info.e);
16554 }
16555 });
16556
16557 StackController.StackButton = StackButton; // for monkey patching
16558
16559 return StackController;
16560 });
16561
16562 },
16563 'dojo/dnd/Mover':function(){
16564 define("dojo/dnd/Mover", [
16565 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window",
16566 "../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll"
16567 ], function(array, declare, event, lang, has, win, dom, domGeom, domStyle, Evented, on, touch, dnd, autoscroll){
16568
16569 // module:
16570 // dojo/dnd/Mover
16571
16572 return declare("dojo.dnd.Mover", [Evented], {
16573 // summary:
16574 // an object which makes a node follow the mouse, or touch-drag on touch devices.
16575 // Used as a default mover, and as a base class for custom movers.
16576
16577 constructor: function(node, e, host){
16578 // node: Node
16579 // a node (or node's id) to be moved
16580 // e: Event
16581 // a mouse event, which started the move;
16582 // only pageX and pageY properties are used
16583 // host: Object?
16584 // object which implements the functionality of the move,
16585 // and defines proper events (onMoveStart and onMoveStop)
16586 this.node = dom.byId(node);
16587 this.marginBox = {l: e.pageX, t: e.pageY};
16588 this.mouseButton = e.button;
16589 var h = (this.host = host), d = node.ownerDocument;
16590 this.events = [
16591 // At the start of a drag, onFirstMove is called, and then the following
16592 // listener is disconnected.
16593 on(d, touch.move, lang.hitch(this, "onFirstMove")),
16594
16595 // These are called continually during the drag
16596 on(d, touch.move, lang.hitch(this, "onMouseMove")),
16597
16598 // And these are called at the end of the drag
16599 on(d, touch.release, lang.hitch(this, "onMouseUp")),
16600
16601 // cancel text selection and text dragging
16602 on(d, "dragstart", event.stop),
16603 on(d.body, "selectstart", event.stop)
16604 ];
16605
16606 // Tell autoscroll that a drag is starting
16607 autoscroll.autoScrollStart(d);
16608
16609 // notify that the move has started
16610 if(h && h.onMoveStart){
16611 h.onMoveStart(this);
16612 }
16613 },
16614 // mouse event processors
16615 onMouseMove: function(e){
16616 // summary:
16617 // event processor for onmousemove/ontouchmove
16618 // e: Event
16619 // mouse/touch event
16620 autoscroll.autoScroll(e);
16621 var m = this.marginBox;
16622 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
16623 event.stop(e);
16624 },
16625 onMouseUp: function(e){
16626 if(has("webkit") && has("mac") && this.mouseButton == 2 ?
16627 e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
16628 this.destroy();
16629 }
16630 event.stop(e);
16631 },
16632 // utilities
16633 onFirstMove: function(e){
16634 // summary:
16635 // makes the node absolute; it is meant to be called only once.
16636 // relative and absolutely positioned nodes are assumed to use pixel units
16637 var s = this.node.style, l, t, h = this.host;
16638 switch(s.position){
16639 case "relative":
16640 case "absolute":
16641 // assume that left and top values are in pixels already
16642 l = Math.round(parseFloat(s.left)) || 0;
16643 t = Math.round(parseFloat(s.top)) || 0;
16644 break;
16645 default:
16646 s.position = "absolute"; // enforcing the absolute mode
16647 var m = domGeom.getMarginBox(this.node);
16648 // event.pageX/pageY (which we used to generate the initial
16649 // margin box) includes padding and margin set on the body.
16650 // However, setting the node's position to absolute and then
16651 // doing domGeom.marginBox on it *doesn't* take that additional
16652 // space into account - so we need to subtract the combined
16653 // padding and margin. We use getComputedStyle and
16654 // _getMarginBox/_getContentBox to avoid the extra lookup of
16655 // the computed style.
16656 var b = win.doc.body;
16657 var bs = domStyle.getComputedStyle(b);
16658 var bm = domGeom.getMarginBox(b, bs);
16659 var bc = domGeom.getContentBox(b, bs);
16660 l = m.l - (bc.l - bm.l);
16661 t = m.t - (bc.t - bm.t);
16662 break;
16663 }
16664 this.marginBox.l = l - this.marginBox.l;
16665 this.marginBox.t = t - this.marginBox.t;
16666 if(h && h.onFirstMove){
16667 h.onFirstMove(this, e);
16668 }
16669
16670 // Disconnect touch.move that call this function
16671 this.events.shift().remove();
16672 },
16673 destroy: function(){
16674 // summary:
16675 // stops the move, deletes all references, so the object can be garbage-collected
16676 array.forEach(this.events, function(handle){ handle.remove(); });
16677 // undo global settings
16678 var h = this.host;
16679 if(h && h.onMoveStop){
16680 h.onMoveStop(this);
16681 }
16682 // destroy objects
16683 this.events = this.node = this.host = null;
16684 }
16685 });
16686
16687 });
16688
16689 },
16690 'dijit/layout/TabContainer':function(){
16691 define("dijit/layout/TabContainer", [
16692 "dojo/_base/lang", // lang.getObject
16693 "dojo/_base/declare", // declare
16694 "./_TabContainerBase",
16695 "./TabController",
16696 "./ScrollingTabController"
16697 ], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){
16698
16699 // module:
16700 // dijit/layout/TabContainer
16701
16702
16703 return declare("dijit.layout.TabContainer", _TabContainerBase, {
16704 // summary:
16705 // A Container with tabs to select each child (only one of which is displayed at a time).
16706 // description:
16707 // A TabContainer is a container that has multiple panes, but shows only
16708 // one pane at a time. There are a set of tabs corresponding to each pane,
16709 // where each tab has the name (aka title) of the pane, and optionally a close button.
16710 //
16711 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
16712 // children of a `TabContainer`.
16713
16714 // useMenu: [const] Boolean
16715 // True if a menu should be used to select tabs when they are too
16716 // wide to fit the TabContainer, false otherwise.
16717 useMenu: true,
16718
16719 // useSlider: [const] Boolean
16720 // True if a slider should be used to select tabs when they are too
16721 // wide to fit the TabContainer, false otherwise.
16722 useSlider: true,
16723
16724 // controllerWidget: Class
16725 // An optional parameter to override the widget used to display the tab labels
16726 controllerWidget: "",
16727
16728 _makeController: function(/*DomNode*/ srcNode){
16729 // summary:
16730 // Instantiate tablist controller widget and return reference to it.
16731 // Callback from _TabContainerBase.postCreate().
16732 // tags:
16733 // protected extension
16734
16735 // "string" branch for back-compat, remove for 2.0
16736 var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
16737 TabController = typeof this.controllerWidget == "string" ? lang.getObject(this.controllerWidget) :
16738 this.controllerWidget;
16739
16740 return new TabController({
16741 id: this.id + "_tablist",
16742 ownerDocument: this.ownerDocument,
16743 dir: this.dir,
16744 lang: this.lang,
16745 textDir: this.textDir,
16746 tabPosition: this.tabPosition,
16747 doLayout: this.doLayout,
16748 containerId: this.id,
16749 "class": cls,
16750 nested: this.nested,
16751 useMenu: this.useMenu,
16752 useSlider: this.useSlider,
16753 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
16754 }, srcNode);
16755 },
16756
16757 postMixInProperties: function(){
16758 this.inherited(arguments);
16759
16760 // Scrolling controller only works for horizontal non-nested tabs
16761 if(!this.controllerWidget){
16762 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
16763 ScrollingTabController : TabController;
16764 }
16765 }
16766 });
16767 });
16768
16769 },
16770 'dijit/BackgroundIframe':function(){
16771 define("dijit/BackgroundIframe", [
16772 "require", // require.toUrl
16773 "./main", // to export dijit.BackgroundIframe
16774 "dojo/_base/config",
16775 "dojo/dom-construct", // domConstruct.create
16776 "dojo/dom-style", // domStyle.set
16777 "dojo/_base/lang", // lang.extend lang.hitch
16778 "dojo/on",
16779 "dojo/sniff", // has("ie"), has("mozilla"), has("quirks")
16780 "dojo/_base/window" // win.doc.createElement
16781 ], function(require, dijit, config, domConstruct, domStyle, lang, on, has, win){
16782
16783 // module:
16784 // dijit/BackgroundIFrame
16785
16786 // TODO: remove _frames, it isn't being used much, since popups never release their
16787 // iframes (see [22236])
16788 var _frames = new function(){
16789 // summary:
16790 // cache of iframes
16791
16792 var queue = [];
16793
16794 this.pop = function(){
16795 var iframe;
16796 if(queue.length){
16797 iframe = queue.pop();
16798 iframe.style.display="";
16799 }else{
16800 if(has("ie") < 9){
16801 var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
16802 var html="<iframe src='" + burl + "' role='presentation'"
16803 + " style='position: absolute; left: 0px; top: 0px;"
16804 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
16805 iframe = win.doc.createElement(html);
16806 }else{
16807 iframe = domConstruct.create("iframe");
16808 iframe.src = 'javascript:""';
16809 iframe.className = "dijitBackgroundIframe";
16810 iframe.setAttribute("role", "presentation");
16811 domStyle.set(iframe, "opacity", 0.1);
16812 }
16813 iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
16814 }
16815 return iframe;
16816 };
16817
16818 this.push = function(iframe){
16819 iframe.style.display="none";
16820 queue.push(iframe);
16821 }
16822 }();
16823
16824
16825 dijit.BackgroundIframe = function(/*DomNode*/ node){
16826 // summary:
16827 // For IE/FF z-index schenanigans. id attribute is required.
16828 //
16829 // description:
16830 // new dijit.BackgroundIframe(node).
16831 //
16832 // Makes a background iframe as a child of node, that fills
16833 // area (and position) of node
16834
16835 if(!node.id){ throw new Error("no id"); }
16836 if(has("ie") || has("mozilla")){
16837 var iframe = (this.iframe = _frames.pop());
16838 node.appendChild(iframe);
16839 if(has("ie")<7 || has("quirks")){
16840 this.resize(node);
16841 this._conn = on(node, 'resize', lang.hitch(this, function(){
16842 this.resize(node);
16843 }));
16844 }else{
16845 domStyle.set(iframe, {
16846 width: '100%',
16847 height: '100%'
16848 });
16849 }
16850 }
16851 };
16852
16853 lang.extend(dijit.BackgroundIframe, {
16854 resize: function(node){
16855 // summary:
16856 // Resize the iframe so it's the same size as node.
16857 // Needed on IE6 and IE/quirks because height:100% doesn't work right.
16858 if(this.iframe){
16859 domStyle.set(this.iframe, {
16860 width: node.offsetWidth + 'px',
16861 height: node.offsetHeight + 'px'
16862 });
16863 }
16864 },
16865 destroy: function(){
16866 // summary:
16867 // destroy the iframe
16868 if(this._conn){
16869 this._conn.remove();
16870 this._conn = null;
16871 }
16872 if(this.iframe){
16873 _frames.push(this.iframe);
16874 delete this.iframe;
16875 }
16876 }
16877 });
16878
16879 return dijit.BackgroundIframe;
16880 });
16881
16882 },
16883 '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",
16884 'dojo/dnd/Avatar':function(){
16885 define("dojo/dnd/Avatar", [
16886 "../_base/declare",
16887 "../_base/window",
16888 "../dom",
16889 "../dom-attr",
16890 "../dom-class",
16891 "../dom-construct",
16892 "../hccss",
16893 "../query"
16894 ], function(declare, win, dom, domAttr, domClass, domConstruct, has, query){
16895
16896 // module:
16897 // dojo/dnd/Avatar
16898
16899 return declare("dojo.dnd.Avatar", null, {
16900 // summary:
16901 // Object that represents transferred DnD items visually
16902 // manager: Object
16903 // a DnD manager object
16904
16905 constructor: function(manager){
16906 this.manager = manager;
16907 this.construct();
16908 },
16909
16910 // methods
16911 construct: function(){
16912 // summary:
16913 // constructor function;
16914 // it is separate so it can be (dynamically) overwritten in case of need
16915
16916 var a = domConstruct.create("table", {
16917 "class": "dojoDndAvatar",
16918 style: {
16919 position: "absolute",
16920 zIndex: "1999",
16921 margin: "0px"
16922 }
16923 }),
16924 source = this.manager.source, node,
16925 b = domConstruct.create("tbody", null, a),
16926 tr = domConstruct.create("tr", null, b),
16927 td = domConstruct.create("td", null, tr),
16928 k = Math.min(5, this.manager.nodes.length), i = 0;
16929
16930 if(has("highcontrast")){
16931 domConstruct.create("span", {
16932 id : "a11yIcon",
16933 innerHTML : this.manager.copy ? '+' : "<"
16934 }, td)
16935 }
16936 domConstruct.create("span", {
16937 innerHTML: source.generateText ? this._generateText() : ""
16938 }, td);
16939
16940 // we have to set the opacity on IE only after the node is live
16941 domAttr.set(tr, {
16942 "class": "dojoDndAvatarHeader",
16943 style: {opacity: 0.9}
16944 });
16945 for(; i < k; ++i){
16946 if(source.creator){
16947 // create an avatar representation of the node
16948 node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
16949 }else{
16950 // or just clone the node and hope it works
16951 node = this.manager.nodes[i].cloneNode(true);
16952 if(node.tagName.toLowerCase() == "tr"){
16953 // insert extra table nodes
16954 var table = domConstruct.create("table"),
16955 tbody = domConstruct.create("tbody", null, table);
16956 tbody.appendChild(node);
16957 node = table;
16958 }
16959 }
16960 node.id = "";
16961 tr = domConstruct.create("tr", null, b);
16962 td = domConstruct.create("td", null, tr);
16963 td.appendChild(node);
16964 domAttr.set(tr, {
16965 "class": "dojoDndAvatarItem",
16966 style: {opacity: (9 - i) / 10}
16967 });
16968 }
16969 this.node = a;
16970 },
16971 destroy: function(){
16972 // summary:
16973 // destructor for the avatar; called to remove all references so it can be garbage-collected
16974 domConstruct.destroy(this.node);
16975 this.node = false;
16976 },
16977 update: function(){
16978 // summary:
16979 // updates the avatar to reflect the current DnD state
16980 domClass.toggle(this.node, "dojoDndAvatarCanDrop", this.manager.canDropFlag);
16981 if(has("highcontrast")){
16982 var icon = dom.byId("a11yIcon");
16983 var text = '+'; // assume canDrop && copy
16984 if (this.manager.canDropFlag && !this.manager.copy){
16985 text = '< '; // canDrop && move
16986 }else if (!this.manager.canDropFlag && !this.manager.copy){
16987 text = "o"; //!canDrop && move
16988 }else if(!this.manager.canDropFlag){
16989 text = 'x'; // !canDrop && copy
16990 }
16991 icon.innerHTML=text;
16992 }
16993 // replace text
16994 query(("tr.dojoDndAvatarHeader td span" +(has("highcontrast") ? " span" : "")), this.node).forEach(
16995 function(node){
16996 node.innerHTML = this.manager.source.generateText ? this._generateText() : "";
16997 }, this);
16998 },
16999 _generateText: function(){
17000 // summary:
17001 // generates a proper text to reflect copying or moving of items
17002 return this.manager.nodes.length.toString();
17003 }
17004 });
17005
17006 });
17007
17008 },
17009 'dijit/form/Button':function(){
17010 require({cache:{
17011 '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"}});
17012 define("dijit/form/Button", [
17013 "require",
17014 "dojo/_base/declare", // declare
17015 "dojo/dom-class", // domClass.toggle
17016 "dojo/has", // has("dijit-legacy-requires")
17017 "dojo/_base/kernel", // kernel.deprecated
17018 "dojo/_base/lang", // lang.trim
17019 "dojo/ready",
17020 "./_FormWidget",
17021 "./_ButtonMixin",
17022 "dojo/text!./templates/Button.html"
17023 ], function(require, declare, domClass, has, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
17024
17025 // module:
17026 // dijit/form/Button
17027
17028 // Back compat w/1.6, remove for 2.0
17029 if(has("dijit-legacy-requires")){
17030 ready(0, function(){
17031 var requires = ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
17032 require(requires); // use indirection so modules not rolled into a build
17033 });
17034 }
17035
17036 return declare("dijit.form.Button", [_FormWidget, _ButtonMixin], {
17037 // summary:
17038 // Basically the same thing as a normal HTML button, but with special styling.
17039 // description:
17040 // Buttons can display a label, an icon, or both.
17041 // A label should always be specified (through innerHTML) or the label
17042 // attribute. It can be hidden via showLabel=false.
17043 // example:
17044 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
17045 //
17046 // example:
17047 // | var button1 = new Button({label: "hello world", onClick: foo});
17048 // | dojo.body().appendChild(button1.domNode);
17049
17050 // showLabel: Boolean
17051 // Set this to true to hide the label text and display only the icon.
17052 // (If showLabel=false then iconClass must be specified.)
17053 // Especially useful for toolbars.
17054 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
17055 //
17056 // The exception case is for computers in high-contrast mode, where the label
17057 // will still be displayed, since the icon doesn't appear.
17058 showLabel: true,
17059
17060 // iconClass: String
17061 // Class to apply to DOMNode in button to make it display an icon
17062 iconClass: "dijitNoIcon",
17063 _setIconClassAttr: { node: "iconNode", type: "class" },
17064
17065 baseClass: "dijitButton",
17066
17067 templateString: template,
17068
17069 // Map widget attributes to DOMNode attributes.
17070 _setValueAttr: "valueNode",
17071
17072 _onClick: function(/*Event*/ e){
17073 // summary:
17074 // Internal function to handle click actions
17075 var ok = this.inherited(arguments);
17076 if(ok){
17077 if(this.valueNode){
17078 this.valueNode.click();
17079 e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
17080 e.stopPropagation(); // avoid two events bubbling from Button widget
17081 // leave ok = true so that subclasses can do what they need to do
17082 }
17083 }
17084 return ok;
17085 },
17086
17087 _fillContent: function(/*DomNode*/ source){
17088 // Overrides _Templated._fillContent().
17089 // If button label is specified as srcNodeRef.innerHTML rather than
17090 // this.params.label, handle it here.
17091 // TODO: remove the method in 2.0, parser will do it all for me
17092 if(source && (!this.params || !("label" in this.params))){
17093 var sourceLabel = lang.trim(source.innerHTML);
17094 if(sourceLabel){
17095 this.label = sourceLabel; // _applyAttributes will be called after buildRendering completes to update the DOM
17096 }
17097 }
17098 },
17099
17100 _setShowLabelAttr: function(val){
17101 if(this.containerNode){
17102 domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
17103 }
17104 this._set("showLabel", val);
17105 },
17106
17107 setLabel: function(/*String*/ content){
17108 // summary:
17109 // Deprecated. Use set('label', ...) instead.
17110 kernel.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
17111 this.set("label", content);
17112 },
17113
17114 _setLabelAttr: function(/*String*/ content){
17115 // summary:
17116 // Hook for set('label', ...) to work.
17117 // description:
17118 // Set the label (text) of the button; takes an HTML string.
17119 // If the label is hidden (showLabel=false) then and no title has
17120 // been specified, then label is also set as title attribute of icon.
17121 this.inherited(arguments);
17122 if(!this.showLabel && !("title" in this.params)){
17123 this.titleNode.title = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
17124 }
17125 }
17126 });
17127
17128
17129 });
17130
17131
17132 },
17133 '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",
17134 'dojo/dnd/move':function(){
17135 define("dojo/dnd/move", [
17136 "../_base/declare",
17137 "../dom-geometry", "../dom-style",
17138 "./common", "./Mover", "./Moveable"
17139 ], function(declare, domGeom, domStyle, dnd, Mover, Moveable){
17140
17141 // module:
17142 // dojo/dnd/move
17143
17144 /*=====
17145 var __constrainedMoveableArgs = declare([Moveable.__MoveableArgs], {
17146 // constraints: Function
17147 // Calculates a constraint box.
17148 // It is called in a context of the moveable object.
17149 constraints: function(){},
17150
17151 // within: Boolean
17152 // restrict move within boundaries.
17153 within: false
17154 });
17155 =====*/
17156
17157 var constrainedMoveable = declare("dojo.dnd.move.constrainedMoveable", Moveable, {
17158 // object attributes (for markup)
17159 constraints: function(){},
17160 within: false,
17161
17162 constructor: function(node, params){
17163 // summary:
17164 // an object that makes a node moveable
17165 // node: Node
17166 // a node (or node's id) to be moved
17167 // params: __constrainedMoveableArgs?
17168 // an optional object with additional parameters;
17169 // the rest is passed to the base class
17170 if(!params){ params = {}; }
17171 this.constraints = params.constraints;
17172 this.within = params.within;
17173 },
17174 onFirstMove: function(/*Mover*/ mover){
17175 // summary:
17176 // called during the very first move notification;
17177 // can be used to initialize coordinates, can be overwritten.
17178 var c = this.constraintBox = this.constraints.call(this, mover);
17179 c.r = c.l + c.w;
17180 c.b = c.t + c.h;
17181 if(this.within){
17182 var mb = domGeom.getMarginSize(mover.node);
17183 c.r -= mb.w;
17184 c.b -= mb.h;
17185 }
17186 },
17187 onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
17188 // summary:
17189 // called during every move notification;
17190 // should actually move the node; can be overwritten.
17191 var c = this.constraintBox, s = mover.node.style;
17192 this.onMoving(mover, leftTop);
17193 leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
17194 leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
17195 s.left = leftTop.l + "px";
17196 s.top = leftTop.t + "px";
17197 this.onMoved(mover, leftTop);
17198 }
17199 });
17200
17201 /*=====
17202 var __boxConstrainedMoveableArgs = declare([__constrainedMoveableArgs], {
17203 // box: Object
17204 // a constraint box
17205 box: {}
17206 });
17207 =====*/
17208
17209 var boxConstrainedMoveable = declare("dojo.dnd.move.boxConstrainedMoveable", constrainedMoveable, {
17210 // box:
17211 // object attributes (for markup)
17212 box: {},
17213
17214 constructor: function(node, params){
17215 // summary:
17216 // an object, which makes a node moveable
17217 // node: Node
17218 // a node (or node's id) to be moved
17219 // params: __boxConstrainedMoveableArgs?
17220 // an optional object with parameters
17221 var box = params && params.box;
17222 this.constraints = function(){ return box; };
17223 }
17224 });
17225
17226 /*=====
17227 var __parentConstrainedMoveableArgs = declare( [__constrainedMoveableArgs], {
17228 // area: String
17229 // A parent's area to restrict the move.
17230 // Can be "margin", "border", "padding", or "content".
17231 area: ""
17232 });
17233 =====*/
17234
17235 var parentConstrainedMoveable = declare("dojo.dnd.move.parentConstrainedMoveable", constrainedMoveable, {
17236 // area:
17237 // object attributes (for markup)
17238 area: "content",
17239
17240 constructor: function(node, params){
17241 // summary:
17242 // an object, which makes a node moveable
17243 // node: Node
17244 // a node (or node's id) to be moved
17245 // params: __parentConstrainedMoveableArgs?
17246 // an optional object with parameters
17247 var area = params && params.area;
17248 this.constraints = function(){
17249 var n = this.node.parentNode,
17250 s = domStyle.getComputedStyle(n),
17251 mb = domGeom.getMarginBox(n, s);
17252 if(area == "margin"){
17253 return mb; // Object
17254 }
17255 var t = domGeom.getMarginExtents(n, s);
17256 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17257 if(area == "border"){
17258 return mb; // Object
17259 }
17260 t = domGeom.getBorderExtents(n, s);
17261 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17262 if(area == "padding"){
17263 return mb; // Object
17264 }
17265 t = domGeom.getPadExtents(n, s);
17266 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
17267 return mb; // Object
17268 };
17269 }
17270 });
17271
17272
17273 return {
17274 // summary:
17275 // TODOC
17276 constrainedMoveable: constrainedMoveable,
17277 boxConstrainedMoveable: boxConstrainedMoveable,
17278 parentConstrainedMoveable: parentConstrainedMoveable
17279 };
17280
17281 });
17282
17283 },
17284 'dijit/_WidgetBase':function(){
17285 define("dijit/_WidgetBase", [
17286 "require", // require.toUrl
17287 "dojo/_base/array", // array.forEach array.map
17288 "dojo/aspect",
17289 "dojo/_base/config", // config.blankGif
17290 "dojo/_base/connect", // connect.connect
17291 "dojo/_base/declare", // declare
17292 "dojo/dom", // dom.byId
17293 "dojo/dom-attr", // domAttr.set domAttr.remove
17294 "dojo/dom-class", // domClass.add domClass.replace
17295 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
17296 "dojo/dom-geometry", // isBodyLtr
17297 "dojo/dom-style", // domStyle.set, domStyle.get
17298 "dojo/has",
17299 "dojo/_base/kernel",
17300 "dojo/_base/lang", // mixin(), isArray(), etc.
17301 "dojo/on",
17302 "dojo/ready",
17303 "dojo/Stateful", // Stateful
17304 "dojo/topic",
17305 "dojo/_base/window", // win.doc, win.body()
17306 "./Destroyable",
17307 "./registry" // registry.getUniqueId(), registry.findWidgets()
17308 ], function(require, array, aspect, config, connect, declare,
17309 dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel,
17310 lang, on, ready, Stateful, topic, win, Destroyable, registry){
17311
17312 // module:
17313 // dijit/_WidgetBase
17314
17315 // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
17316 has.add("dijit-legacy-requires", !kernel.isAsync);
17317
17318 // For back-compat, remove in 2.0.
17319 if(has("dijit-legacy-requires")){
17320 ready(0, function(){
17321 var requires = ["dijit/_base/manager"];
17322 require(requires); // use indirection so modules not rolled into a build
17323 });
17324 }
17325
17326 // Nested hash listing attributes for each tag, all strings in lowercase.
17327 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
17328 var tagAttrs = {};
17329 function getAttrs(obj){
17330 var ret = {};
17331 for(var attr in obj){
17332 ret[attr.toLowerCase()] = true;
17333 }
17334 return ret;
17335 }
17336
17337 function nonEmptyAttrToDom(attr){
17338 // summary:
17339 // Returns a setter function that copies the attribute to this.domNode,
17340 // or removes the attribute from this.domNode, depending on whether the
17341 // value is defined or not.
17342 return function(val){
17343 domAttr[val ? "set" : "remove"](this.domNode, attr, val);
17344 this._set(attr, val);
17345 };
17346 }
17347
17348 return declare("dijit._WidgetBase", [Stateful, Destroyable], {
17349 // summary:
17350 // Future base class for all Dijit widgets.
17351 // description:
17352 // Future base class for all Dijit widgets.
17353 // _Widget extends this class adding support for various features needed by desktop.
17354 //
17355 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
17356 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
17357 //
17358 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
17359 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
17360 //
17361 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
17362 //
17363 // - DOM node attribute
17364 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
17365 // | _setFocusAttr: "focusNode" (shorthand)
17366 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
17367 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
17368 //
17369 // - DOM node innerHTML
17370 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
17371 // Maps this.title to this.titleNode.innerHTML
17372 //
17373 // - DOM node innerText
17374 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
17375 // Maps this.title to this.titleNode.innerText
17376 //
17377 // - DOM node CSS class
17378 // | _setMyClassAttr: { node: "domNode", type: "class" }
17379 // Maps this.myClass to this.domNode.className
17380 //
17381 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
17382 // formats of the above list.
17383 //
17384 // If the custom setter is null, no action is performed other than saving the new value
17385 // in the widget (in this).
17386 //
17387 // If no custom setter is defined for an attribute, then it will be copied
17388 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
17389 // That's only done though for attributes that match DOMNode attributes (title,
17390 // alt, aria-labelledby, etc.)
17391
17392 // id: [const] String
17393 // A unique, opaque ID string that can be assigned by users or by the
17394 // system. If the developer passes an ID which is known not to be
17395 // unique, the specified ID is ignored and the system-generated ID is
17396 // used instead.
17397 id: "",
17398 _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's
17399
17400 // lang: [const] String
17401 // Rarely used. Overrides the default Dojo locale used to render this widget,
17402 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
17403 // Value must be among the list of locales specified during by the Dojo bootstrap,
17404 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
17405 lang: "",
17406 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
17407 _setLangAttr: nonEmptyAttrToDom("lang"),
17408
17409 // dir: [const] String
17410 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
17411 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
17412 // default direction.
17413 dir: "",
17414 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
17415 _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
17416
17417 // textDir: String
17418 // Bi-directional support, the main variable which is responsible for the direction of the text.
17419 // The text direction can be different than the GUI direction by using this parameter in creation
17420 // of a widget.
17421 //
17422 // Allowed values:
17423 //
17424 // 1. "ltr"
17425 // 2. "rtl"
17426 // 3. "auto" - contextual the direction of a text defined by first strong letter.
17427 //
17428 // By default is as the page direction.
17429 textDir: "",
17430
17431 // class: String
17432 // HTML class attribute
17433 "class": "",
17434 _setClassAttr: { node: "domNode", type: "class" },
17435
17436 // style: String||Object
17437 // HTML style attributes as cssText string or name/value hash
17438 style: "",
17439
17440 // title: String
17441 // HTML title attribute.
17442 //
17443 // For form widgets this specifies a tooltip to display when hovering over
17444 // the widget (just like the native HTML title attribute).
17445 //
17446 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
17447 // etc., it's used to specify the tab label, accordion pane title, etc.
17448 title: "",
17449
17450 // tooltip: String
17451 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
17452 // this specifies the tooltip to appear when the mouse is hovered over that text.
17453 tooltip: "",
17454
17455 // baseClass: [protected] String
17456 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
17457 // widget state.
17458 baseClass: "",
17459
17460 // srcNodeRef: [readonly] DomNode
17461 // pointer to original DOM node
17462 srcNodeRef: null,
17463
17464 // domNode: [readonly] DomNode
17465 // This is our visible representation of the widget! Other DOM
17466 // Nodes may by assigned to other properties, usually through the
17467 // template system's data-dojo-attach-point syntax, but the domNode
17468 // property is the canonical "top level" node in widget UI.
17469 domNode: null,
17470
17471 // containerNode: [readonly] DomNode
17472 // Designates where children of the source DOM node will be placed.
17473 // "Children" in this case refers to both DOM nodes and widgets.
17474 // For example, for myWidget:
17475 //
17476 // | <div data-dojo-type=myWidget>
17477 // | <b> here's a plain DOM node
17478 // | <span data-dojo-type=subWidget>and a widget</span>
17479 // | <i> and another plain DOM node </i>
17480 // | </div>
17481 //
17482 // containerNode would point to:
17483 //
17484 // | <b> here's a plain DOM node
17485 // | <span data-dojo-type=subWidget>and a widget</span>
17486 // | <i> and another plain DOM node </i>
17487 //
17488 // In templated widgets, "containerNode" is set via a
17489 // data-dojo-attach-point assignment.
17490 //
17491 // containerNode must be defined for any widget that accepts innerHTML
17492 // (like ContentPane or BorderContainer or even Button), and conversely
17493 // is null for widgets that don't, like TextBox.
17494 containerNode: null,
17495
17496 // ownerDocument: [const] Document?
17497 // The document this widget belongs to. If not specified to constructor, will default to
17498 // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
17499 ownerDocument: null,
17500 _setOwnerDocumentAttr: function(val){
17501 // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
17502 this._set("ownerDocument", val);
17503 },
17504
17505 /*=====
17506 // _started: [readonly] Boolean
17507 // startup() has completed.
17508 _started: false,
17509 =====*/
17510
17511 // attributeMap: [protected] Object
17512 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
17513 // for each XXX attribute to be mapped to the DOM.
17514 //
17515 // attributeMap sets up a "binding" between attributes (aka properties)
17516 // of the widget and the widget's DOM.
17517 // Changes to widget attributes listed in attributeMap will be
17518 // reflected into the DOM.
17519 //
17520 // For example, calling set('title', 'hello')
17521 // on a TitlePane will automatically cause the TitlePane's DOM to update
17522 // with the new title.
17523 //
17524 // attributeMap is a hash where the key is an attribute of the widget,
17525 // and the value reflects a binding to a:
17526 //
17527 // - DOM node attribute
17528 // | focus: {node: "focusNode", type: "attribute"}
17529 // Maps this.focus to this.focusNode.focus
17530 //
17531 // - DOM node innerHTML
17532 // | title: { node: "titleNode", type: "innerHTML" }
17533 // Maps this.title to this.titleNode.innerHTML
17534 //
17535 // - DOM node innerText
17536 // | title: { node: "titleNode", type: "innerText" }
17537 // Maps this.title to this.titleNode.innerText
17538 //
17539 // - DOM node CSS class
17540 // | myClass: { node: "domNode", type: "class" }
17541 // Maps this.myClass to this.domNode.className
17542 //
17543 // If the value is an array, then each element in the array matches one of the
17544 // formats of the above list.
17545 //
17546 // There are also some shorthands for backwards compatibility:
17547 //
17548 // - string --> { node: string, type: "attribute" }, for example:
17549 //
17550 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
17551 //
17552 // - "" --> { node: "domNode", type: "attribute" }
17553 attributeMap: {},
17554
17555 // _blankGif: [protected] String
17556 // Path to a blank 1x1 image.
17557 // Used by `<img>` nodes in templates that really get their image via CSS background-image.
17558 _blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"),
17559
17560 //////////// INITIALIZATION METHODS ///////////////////////////////////////
17561
17562 /*=====
17563 constructor: function(params, srcNodeRef){
17564 // summary:
17565 // Create the widget.
17566 // params: Object|null
17567 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17568 // and functions, typically callbacks like onClick.
17569 // The hash can contain any of the widget's properties, excluding read-only properties.
17570 // srcNodeRef: DOMNode|String?
17571 // If a srcNodeRef (DOM node) is specified:
17572 //
17573 // - use srcNodeRef.innerHTML as my contents
17574 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17575 // - otherwise, replace srcNodeRef with my generated DOM tree
17576 },
17577 =====*/
17578
17579 postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
17580 // summary:
17581 // Kicks off widget instantiation. See create() for details.
17582 // tags:
17583 // private
17584 this.create(params, srcNodeRef);
17585 },
17586
17587 create: function(params, srcNodeRef){
17588 // summary:
17589 // Kick off the life-cycle of a widget
17590 // description:
17591 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
17592 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
17593 // for a discussion of the widget creation lifecycle.
17594 //
17595 // Of course, adventurous developers could override create entirely, but this should
17596 // only be done as a last resort.
17597 // params: Object|null
17598 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17599 // and functions, typically callbacks like onClick.
17600 // The hash can contain any of the widget's properties, excluding read-only properties.
17601 // srcNodeRef: DOMNode|String?
17602 // If a srcNodeRef (DOM node) is specified:
17603 //
17604 // - use srcNodeRef.innerHTML as my contents
17605 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17606 // - otherwise, replace srcNodeRef with my generated DOM tree
17607 // tags:
17608 // private
17609
17610 // store pointer to original DOM tree
17611 this.srcNodeRef = dom.byId(srcNodeRef);
17612
17613 // No longer used, remove for 2.0.
17614 this._connects = [];
17615 this._supportingWidgets = [];
17616
17617 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
17618 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
17619
17620 // mix in our passed parameters
17621 if(params){
17622 this.params = params;
17623 lang.mixin(this, params);
17624 }
17625 this.postMixInProperties();
17626
17627 // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
17628 // Do this before buildRendering() because it might expect the id to be there.
17629 if(!this.id){
17630 this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
17631 if(this.params){
17632 // if params contains {id: undefined}, prevent _applyAttributes() from processing it
17633 delete this.params.id;
17634 }
17635 }
17636
17637 // The document and <body> node this widget is associated with
17638 this.ownerDocument = this.ownerDocument || (this.srcNodeRef ? this.srcNodeRef.ownerDocument : win.doc);
17639 this.ownerDocumentBody = win.body(this.ownerDocument);
17640
17641 registry.add(this);
17642
17643 this.buildRendering();
17644
17645 var deleteSrcNodeRef;
17646
17647 if(this.domNode){
17648 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
17649 // Also calls custom setters for all attributes with custom setters.
17650 this._applyAttributes();
17651
17652 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
17653 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
17654 // widget being attached to the DOM since it isn't when a widget is created programmatically like
17655 // new MyWidget({}). See #11635.
17656 var source = this.srcNodeRef;
17657 if(source && source.parentNode && this.domNode !== source){
17658 source.parentNode.replaceChild(this.domNode, source);
17659 deleteSrcNodeRef = true;
17660 }
17661
17662 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
17663 // assuming that dojo._scopeName even exists in 2.0
17664 this.domNode.setAttribute("widgetId", this.id);
17665 }
17666 this.postCreate();
17667
17668 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
17669 // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
17670 if(deleteSrcNodeRef){
17671 delete this.srcNodeRef;
17672 }
17673
17674 this._created = true;
17675 },
17676
17677 _applyAttributes: function(){
17678 // summary:
17679 // Step during widget creation to copy widget attributes to the
17680 // DOM according to attributeMap and _setXXXAttr objects, and also to call
17681 // custom _setXXXAttr() methods.
17682 //
17683 // Skips over blank/false attribute values, unless they were explicitly specified
17684 // as parameters to the widget, since those are the default anyway,
17685 // and setting tabIndex="" is different than not setting tabIndex at all.
17686 //
17687 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
17688 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
17689 // tags:
17690 // private
17691
17692 // Get list of attributes where this.set(name, value) will do something beyond
17693 // setting this[name] = value. Specifically, attributes that have:
17694 // - associated _setXXXAttr() method/hash/string/array
17695 // - entries in attributeMap (remove this for 2.0);
17696 var ctor = this.constructor,
17697 list = ctor._setterAttrs;
17698 if(!list){
17699 list = (ctor._setterAttrs = []);
17700 for(var attr in this.attributeMap){
17701 list.push(attr);
17702 }
17703
17704 var proto = ctor.prototype;
17705 for(var fxName in proto){
17706 if(fxName in this.attributeMap){ continue; }
17707 var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr";
17708 if(setterName in proto){
17709 list.push(fxName);
17710 }
17711 }
17712 }
17713
17714 // Call this.set() for each property that was either specified as parameter to constructor,
17715 // or is in the list found above. For correlated properties like value and displayedValue, the one
17716 // specified as a parameter should take precedence.
17717 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
17718 // NaN and thus is not ignored like a default value of "".
17719
17720 // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
17721 // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
17722 var params = {};
17723 for(var key in this.params || {}){
17724 params[key] = this[key];
17725 }
17726
17727 // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
17728 array.forEach(list, function(attr){
17729 if(attr in params){
17730 // skip this one, do it below
17731 }else if(this[attr]){
17732 this.set(attr, this[attr]);
17733 }
17734 }, this);
17735
17736 // Step 3: Call set() for each property that was specified as parameter to constructor.
17737 // Use params hash created above to ignore side effects from step #2 above.
17738 for(key in params){
17739 this.set(key, params[key]);
17740 }
17741 },
17742
17743 postMixInProperties: function(){
17744 // summary:
17745 // Called after the parameters to the widget have been read-in,
17746 // but before the widget template is instantiated. Especially
17747 // useful to set properties that are referenced in the widget
17748 // template.
17749 // tags:
17750 // protected
17751 },
17752
17753 buildRendering: function(){
17754 // summary:
17755 // Construct the UI for this widget, setting this.domNode.
17756 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
17757 // tags:
17758 // protected
17759
17760 if(!this.domNode){
17761 // Create root node if it wasn't created by _Templated
17762 this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div");
17763 }
17764
17765 // baseClass is a single class name or occasionally a space-separated list of names.
17766 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
17767 // TODO: make baseClass custom setter
17768 if(this.baseClass){
17769 var classes = this.baseClass.split(" ");
17770 if(!this.isLeftToRight()){
17771 classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; }));
17772 }
17773 domClass.add(this.domNode, classes);
17774 }
17775 },
17776
17777 postCreate: function(){
17778 // summary:
17779 // Processing after the DOM fragment is created
17780 // description:
17781 // Called after the DOM fragment has been created, but not necessarily
17782 // added to the document. Do not include any operations which rely on
17783 // node dimensions or placement.
17784 // tags:
17785 // protected
17786 },
17787
17788 startup: function(){
17789 // summary:
17790 // Processing after the DOM fragment is added to the document
17791 // description:
17792 // Called after a widget and its children have been created and added to the page,
17793 // and all related widgets have finished their create() cycle, up through postCreate().
17794 // This is useful for composite widgets that need to control or layout sub-widgets.
17795 // Many layout widgets can use this as a wiring phase.
17796 if(this._started){ return; }
17797 this._started = true;
17798 array.forEach(this.getChildren(), function(obj){
17799 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
17800 obj.startup();
17801 obj._started = true;
17802 }
17803 });
17804 },
17805
17806 //////////// DESTROY FUNCTIONS ////////////////////////////////
17807
17808 destroyRecursive: function(/*Boolean?*/ preserveDom){
17809 // summary:
17810 // Destroy this widget and its descendants
17811 // description:
17812 // This is the generic "destructor" function that all widget users
17813 // should call to cleanly discard with a widget. Once a widget is
17814 // destroyed, it is removed from the manager object.
17815 // preserveDom:
17816 // If true, this method will leave the original DOM structure
17817 // alone of descendant Widgets. Note: This will NOT work with
17818 // dijit._Templated widgets.
17819
17820 this._beingDestroyed = true;
17821 this.destroyDescendants(preserveDom);
17822 this.destroy(preserveDom);
17823 },
17824
17825 destroy: function(/*Boolean*/ preserveDom){
17826 // summary:
17827 // Destroy this widget, but not its descendants.
17828 // This method will, however, destroy internal widgets such as those used within a template.
17829 // preserveDom: Boolean
17830 // If true, this method will leave the original DOM structure alone.
17831 // Note: This will not yet work with _Templated widgets
17832
17833 this._beingDestroyed = true;
17834 this.uninitialize();
17835
17836 function destroy(w){
17837 if(w.destroyRecursive){
17838 w.destroyRecursive(preserveDom);
17839 }else if(w.destroy){
17840 w.destroy(preserveDom);
17841 }
17842 }
17843
17844 // Back-compat, remove for 2.0
17845 array.forEach(this._connects, lang.hitch(this, "disconnect"));
17846 array.forEach(this._supportingWidgets, destroy);
17847
17848 // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
17849 // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
17850 if(this.domNode){
17851 array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy);
17852 }
17853
17854 this.destroyRendering(preserveDom);
17855 registry.remove(this.id);
17856 this._destroyed = true;
17857 },
17858
17859 destroyRendering: function(/*Boolean?*/ preserveDom){
17860 // summary:
17861 // Destroys the DOM nodes associated with this widget
17862 // preserveDom:
17863 // If true, this method will leave the original DOM structure alone
17864 // during tear-down. Note: this will not work with _Templated
17865 // widgets yet.
17866 // tags:
17867 // protected
17868
17869 if(this.bgIframe){
17870 this.bgIframe.destroy(preserveDom);
17871 delete this.bgIframe;
17872 }
17873
17874 if(this.domNode){
17875 if(preserveDom){
17876 domAttr.remove(this.domNode, "widgetId");
17877 }else{
17878 domConstruct.destroy(this.domNode);
17879 }
17880 delete this.domNode;
17881 }
17882
17883 if(this.srcNodeRef){
17884 if(!preserveDom){
17885 domConstruct.destroy(this.srcNodeRef);
17886 }
17887 delete this.srcNodeRef;
17888 }
17889 },
17890
17891 destroyDescendants: function(/*Boolean?*/ preserveDom){
17892 // summary:
17893 // Recursively destroy the children of this widget and their
17894 // descendants.
17895 // preserveDom:
17896 // If true, the preserveDom attribute is passed to all descendant
17897 // widget's .destroy() method. Not for use with _Templated
17898 // widgets.
17899
17900 // get all direct descendants and destroy them recursively
17901 array.forEach(this.getChildren(), function(widget){
17902 if(widget.destroyRecursive){
17903 widget.destroyRecursive(preserveDom);
17904 }
17905 });
17906 },
17907
17908 uninitialize: function(){
17909 // summary:
17910 // Deprecated. Override destroy() instead to implement custom widget tear-down
17911 // behavior.
17912 // tags:
17913 // protected
17914 return false;
17915 },
17916
17917 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
17918
17919 _setStyleAttr: function(/*String||Object*/ value){
17920 // summary:
17921 // Sets the style attribute of the widget according to value,
17922 // which is either a hash like {height: "5px", width: "3px"}
17923 // or a plain string
17924 // description:
17925 // Determines which node to set the style on based on style setting
17926 // in attributeMap.
17927 // tags:
17928 // protected
17929
17930 var mapNode = this.domNode;
17931
17932 // Note: technically we should revert any style setting made in a previous call
17933 // to his method, but that's difficult to keep track of.
17934
17935 if(lang.isObject(value)){
17936 domStyle.set(mapNode, value);
17937 }else{
17938 if(mapNode.style.cssText){
17939 mapNode.style.cssText += "; " + value;
17940 }else{
17941 mapNode.style.cssText = value;
17942 }
17943 }
17944
17945 this._set("style", value);
17946 },
17947
17948 _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
17949 // summary:
17950 // Reflect a widget attribute (title, tabIndex, duration etc.) to
17951 // the widget DOM, as specified by commands parameter.
17952 // If commands isn't specified then it's looked up from attributeMap.
17953 // Note some attributes like "type"
17954 // cannot be processed this way as they are not mutable.
17955 // attr:
17956 // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
17957 // to DOMNode inside the widget, or alternately pointing to a subwidget
17958 // tags:
17959 // private
17960
17961 commands = arguments.length >= 3 ? commands : this.attributeMap[attr];
17962
17963 array.forEach(lang.isArray(commands) ? commands : [commands], function(command){
17964
17965 // Get target node and what we are doing to that node
17966 var mapNode = this[command.node || command || "domNode"]; // DOM node
17967 var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
17968
17969 switch(type){
17970 case "attribute":
17971 if(lang.isFunction(value)){ // functions execute in the context of the widget
17972 value = lang.hitch(this, value);
17973 }
17974
17975 // Get the name of the DOM node attribute; usually it's the same
17976 // as the name of the attribute in the widget (attr), but can be overridden.
17977 // Also maps handler names to lowercase, like onSubmit --> onsubmit
17978 var attrName = command.attribute ? command.attribute :
17979 (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
17980
17981 if(mapNode.tagName){
17982 // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
17983 // method, but for consistency we still call domAttr
17984 domAttr.set(mapNode, attrName, value);
17985 }else{
17986 // mapping to a sub-widget
17987 mapNode.set(attrName, value);
17988 }
17989 break;
17990 case "innerText":
17991 mapNode.innerHTML = "";
17992 mapNode.appendChild(this.ownerDocument.createTextNode(value));
17993 break;
17994 case "innerHTML":
17995 mapNode.innerHTML = value;
17996 break;
17997 case "class":
17998 domClass.replace(mapNode, value, this[attr]);
17999 break;
18000 }
18001 }, this);
18002 },
18003
18004 get: function(name){
18005 // summary:
18006 // Get a property from a widget.
18007 // name:
18008 // The property to get.
18009 // description:
18010 // Get a named property from a widget. The property may
18011 // potentially be retrieved via a getter method. If no getter is defined, this
18012 // just retrieves the object's property.
18013 //
18014 // For example, if the widget has properties `foo` and `bar`
18015 // and a method named `_getFooAttr()`, calling:
18016 // `myWidget.get("foo")` would be equivalent to calling
18017 // `widget._getFooAttr()` and `myWidget.get("bar")`
18018 // would be equivalent to the expression
18019 // `widget.bar2`
18020 var names = this._getAttrNames(name);
18021 return this[names.g] ? this[names.g]() : this[name];
18022 },
18023
18024 set: function(name, value){
18025 // summary:
18026 // Set a property on a widget
18027 // name:
18028 // The property to set.
18029 // value:
18030 // The value to set in the property.
18031 // description:
18032 // Sets named properties on a widget which may potentially be handled by a
18033 // setter in the widget.
18034 //
18035 // For example, if the widget has properties `foo` and `bar`
18036 // and a method named `_setFooAttr()`, calling
18037 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
18038 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
18039 // would be equivalent to the statement `widget.bar = 3;`
18040 //
18041 // set() may also be called with a hash of name/value pairs, ex:
18042 //
18043 // | myWidget.set({
18044 // | foo: "Howdy",
18045 // | bar: 3
18046 // | });
18047 //
18048 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
18049
18050 if(typeof name === "object"){
18051 for(var x in name){
18052 this.set(x, name[x]);
18053 }
18054 return this;
18055 }
18056 var names = this._getAttrNames(name),
18057 setter = this[names.s];
18058 if(lang.isFunction(setter)){
18059 // use the explicit setter
18060 var result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
18061 }else{
18062 // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
18063 // Map according to:
18064 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
18065 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
18066 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
18067 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
18068 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
18069 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
18070 var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode",
18071 tag = this[defaultNode].tagName,
18072 attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])),
18073 map = name in this.attributeMap ? this.attributeMap[name] :
18074 names.s in this ? this[names.s] :
18075 ((names.l in attrsForTag && typeof value != "function") ||
18076 /^aria-|^data-|^role$/.test(name)) ? defaultNode : null;
18077 if(map != null){
18078 this._attrToDom(name, value, map);
18079 }
18080 this._set(name, value);
18081 }
18082 return result || this;
18083 },
18084
18085 _attrPairNames: {}, // shared between all widgets
18086 _getAttrNames: function(name){
18087 // summary:
18088 // Helper function for get() and set().
18089 // Caches attribute name values so we don't do the string ops every time.
18090 // tags:
18091 // private
18092
18093 var apn = this._attrPairNames;
18094 if(apn[name]){ return apn[name]; }
18095 var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); });
18096 return (apn[name] = {
18097 n: name+"Node",
18098 s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
18099 g: "_get"+uc+"Attr",
18100 l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
18101 });
18102 },
18103
18104 _set: function(/*String*/ name, /*anything*/ value){
18105 // summary:
18106 // Helper function to set new value for specified attribute, and call handlers
18107 // registered with watch() if the value has changed.
18108 var oldValue = this[name];
18109 this[name] = value;
18110 if(this._created && value !== oldValue){
18111 if(this._watchCallbacks){
18112 this._watchCallbacks(name, oldValue, value);
18113 }
18114 this.emit("attrmodified-" + name, {
18115 detail: {
18116 prevValue: oldValue,
18117 newValue: value
18118 }
18119 });
18120 }
18121 },
18122
18123 emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){
18124 // summary:
18125 // Used by widgets to signal that a synthetic event occurred, ex:
18126 // | myWidget.emit("attrmodified-selectedChildWidget", {}).
18127 //
18128 // Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
18129 // Also calls onType() method, if present, and returns value from that method.
18130 // By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
18131 // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
18132 // tags:
18133 // protected
18134
18135 // Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
18136 // Also set pointer to widget, although since we can't add a pointer to the widget for native events
18137 // (see #14729), maybe we shouldn't do it here?
18138 eventObj = eventObj || {};
18139 if(eventObj.bubbles === undefined){ eventObj.bubbles = true; }
18140 if(eventObj.cancelable === undefined){ eventObj.cancelable = true; }
18141 if(!eventObj.detail){ eventObj.detail = {}; }
18142 eventObj.detail.widget = this;
18143
18144 var ret, callback = this["on"+type];
18145 if(callback){
18146 ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]);
18147 }
18148
18149 // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
18150 if(this._started && !this._beingDestroyed){
18151 on.emit(this.domNode, type.toLowerCase(), eventObj);
18152 }
18153
18154 return ret;
18155 },
18156
18157 on: function(/*String|Function*/ type, /*Function*/ func){
18158 // summary:
18159 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
18160 // type:
18161 // Name of event (ex: "click") or extension event like touch.press.
18162 // description:
18163 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
18164 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
18165 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
18166
18167 // For backwards compatibility, if there's an onType() method in the widget then connect to that.
18168 // Remove in 2.0.
18169 var widgetMethod = this._onMap(type);
18170 if(widgetMethod){
18171 return aspect.after(this, widgetMethod, func, true);
18172 }
18173
18174 // Otherwise, just listen for the event on this.domNode.
18175 return this.own(on(this.domNode, type, func))[0];
18176 },
18177
18178 _onMap: function(/*String|Function*/ type){
18179 // summary:
18180 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
18181 // If type is a synthetic event like touch.press then returns undefined.
18182 var ctor = this.constructor, map = ctor._onMap;
18183 if(!map){
18184 map = (ctor._onMap = {});
18185 for(var attr in ctor.prototype){
18186 if(/^on/.test(attr)){
18187 map[attr.replace(/^on/, "").toLowerCase()] = attr;
18188 }
18189 }
18190 }
18191 return map[typeof type == "string" && type.toLowerCase()]; // String
18192 },
18193
18194 toString: function(){
18195 // summary:
18196 // Returns a string that represents the widget
18197 // description:
18198 // When a widget is cast to a string, this method will be used to generate the
18199 // output. Currently, it does not implement any sort of reversible
18200 // serialization.
18201 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
18202 },
18203
18204 getChildren: function(){
18205 // summary:
18206 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
18207 // Does not return nested widgets, nor widgets that are part of this widget's template.
18208 return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit/_WidgetBase[]
18209 },
18210
18211 getParent: function(){
18212 // summary:
18213 // Returns the parent widget of this widget
18214 return registry.getEnclosingWidget(this.domNode.parentNode);
18215 },
18216
18217 connect: function(
18218 /*Object|null*/ obj,
18219 /*String|Function*/ event,
18220 /*String|Function*/ method){
18221 // summary:
18222 // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
18223 //
18224 // Connects specified obj/event to specified method of this object
18225 // and registers for disconnect() on widget destroy.
18226 //
18227 // Provide widget-specific analog to dojo.connect, except with the
18228 // implicit use of this widget as the target object.
18229 // Events connected with `this.connect` are disconnected upon
18230 // destruction.
18231 // returns:
18232 // A handle that can be passed to `disconnect` in order to disconnect before
18233 // the widget is destroyed.
18234 // example:
18235 // | var btn = new Button();
18236 // | // when foo.bar() is called, call the listener we're going to
18237 // | // provide in the scope of btn
18238 // | btn.connect(foo, "bar", function(){
18239 // | console.debug(this.toString());
18240 // | });
18241 // tags:
18242 // protected
18243
18244 return this.own(connect.connect(obj, event, this, method))[0]; // handle
18245 },
18246
18247 disconnect: function(handle){
18248 // summary:
18249 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18250 //
18251 // Disconnects handle created by `connect`.
18252 // tags:
18253 // protected
18254
18255 handle.remove();
18256 },
18257
18258 subscribe: function(t, method){
18259 // summary:
18260 // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
18261 //
18262 // Subscribes to the specified topic and calls the specified method
18263 // of this object and registers for unsubscribe() on widget destroy.
18264 //
18265 // Provide widget-specific analog to dojo.subscribe, except with the
18266 // implicit use of this widget as the target object.
18267 // t: String
18268 // The topic
18269 // method: Function
18270 // The callback
18271 // example:
18272 // | var btn = new Button();
18273 // | // when /my/topic is published, this button changes its label to
18274 // | // be the parameter of the topic.
18275 // | btn.subscribe("/my/topic", function(v){
18276 // | this.set("label", v);
18277 // | });
18278 // tags:
18279 // protected
18280 return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle
18281 },
18282
18283 unsubscribe: function(/*Object*/ handle){
18284 // summary:
18285 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18286 //
18287 // Unsubscribes handle created by this.subscribe.
18288 // Also removes handle from this widget's list of subscriptions
18289 // tags:
18290 // protected
18291
18292 handle.remove();
18293 },
18294
18295 isLeftToRight: function(){
18296 // summary:
18297 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
18298 // tags:
18299 // protected
18300 return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean
18301 },
18302
18303 isFocusable: function(){
18304 // summary:
18305 // Return true if this widget can currently be focused
18306 // and false if not
18307 return this.focus && (domStyle.get(this.domNode, "display") != "none");
18308 },
18309
18310 placeAt: function(/* String|DomNode|_Widget */ reference, /* String|Int? */ position){
18311 // summary:
18312 // Place this widget somewhere in the DOM based
18313 // on standard domConstruct.place() conventions.
18314 // description:
18315 // A convenience function provided in all _Widgets, providing a simple
18316 // shorthand mechanism to put an existing (or newly created) Widget
18317 // somewhere in the dom, and allow chaining.
18318 // reference:
18319 // Widget, DOMNode, or id of widget or DOMNode
18320 // position:
18321 // If reference is a widget (or id of widget), and that widget has an ".addChild" method,
18322 // it will be called passing this widget instance into that method, supplying the optional
18323 // position index passed. In this case position (if specified) should be an integer.
18324 //
18325 // If reference is a DOMNode (or id matching a DOMNode but not a widget),
18326 // the position argument can be a numeric index or a string
18327 // "first", "last", "before", or "after", same as dojo/dom-construct::place().
18328 // returns: dijit/_WidgetBase
18329 // Provides a useful return of the newly created dijit._Widget instance so you
18330 // can "chain" this function by instantiating, placing, then saving the return value
18331 // to a variable.
18332 // example:
18333 // | // create a Button with no srcNodeRef, and place it in the body:
18334 // | var button = new Button({ label:"click" }).placeAt(win.body());
18335 // | // now, 'button' is still the widget reference to the newly created button
18336 // | button.on("click", function(e){ console.log('click'); }));
18337 // example:
18338 // | // create a button out of a node with id="src" and append it to id="wrapper":
18339 // | var button = new Button({},"src").placeAt("wrapper");
18340 // example:
18341 // | // place a new button as the first element of some div
18342 // | var button = new Button({ label:"click" }).placeAt("wrapper","first");
18343 // example:
18344 // | // create a contentpane and add it to a TabContainer
18345 // | var tc = dijit.byId("myTabs");
18346 // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
18347
18348 var refWidget = !reference.tagName && registry.byId(reference);
18349 if(refWidget && refWidget.addChild && (!position || typeof position === "number")){
18350 // Adding this to refWidget and can use refWidget.addChild() to handle everything.
18351 refWidget.addChild(this, position);
18352 }else{
18353 // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
18354 // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
18355 // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
18356 var ref = refWidget ?
18357 (refWidget.containerNode && !/after|before|replace/.test(position||"") ?
18358 refWidget.containerNode : refWidget.domNode) : dom.byId(reference, this.ownerDocument);
18359 domConstruct.place(this.domNode, ref, position);
18360
18361 // Start this iff it has a parent widget that's already started.
18362 if(!this._started && (this.getParent() || {})._started){
18363 this.startup();
18364 }
18365 }
18366 return this;
18367 },
18368
18369 getTextDir: function(/*String*/ text,/*String*/ originalDir){
18370 // summary:
18371 // Return direction of the text.
18372 // The function overridden in the _BidiSupport module,
18373 // its main purpose is to calculate the direction of the
18374 // text, if was defined by the programmer through textDir.
18375 // tags:
18376 // protected.
18377 return originalDir;
18378 },
18379
18380 applyTextDir: function(/*===== element, text =====*/){
18381 // summary:
18382 // The function overridden in the _BidiSupport module,
18383 // originally used for setting element.dir according to this.textDir.
18384 // In this case does nothing.
18385 // element: DOMNode
18386 // text: String
18387 // tags:
18388 // protected.
18389 },
18390
18391 defer: function(fcn, delay){
18392 // summary:
18393 // Wrapper to setTimeout to avoid deferred functions executing
18394 // after the originating widget has been destroyed.
18395 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
18396 // fcn: function reference
18397 // delay: Optional number (defaults to 0)
18398 // tags:
18399 // protected.
18400 var timer = setTimeout(lang.hitch(this,
18401 function(){
18402 timer = null;
18403 if(!this._destroyed){
18404 lang.hitch(this, fcn)();
18405 }
18406 }),
18407 delay || 0
18408 );
18409 return {
18410 remove: function(){
18411 if(timer){
18412 clearTimeout(timer);
18413 timer = null;
18414 }
18415 return null; // so this works well: handle = handle.remove();
18416 }
18417 };
18418 }
18419 });
18420
18421 });
18422
18423 },
18424 'dijit/layout/_TabContainerBase':function(){
18425 require({cache:{
18426 '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"}});
18427 define("dijit/layout/_TabContainerBase", [
18428 "dojo/text!./templates/TabContainer.html",
18429 "./StackContainer",
18430 "./utils", // marginBox2contextBox, layoutChildren
18431 "../_TemplatedMixin",
18432 "dojo/_base/declare", // declare
18433 "dojo/dom-class", // domClass.add
18434 "dojo/dom-geometry", // domGeometry.contentBox
18435 "dojo/dom-style" // domStyle.style
18436 ], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){
18437
18438 // module:
18439 // dijit/layout/_TabContainerBase
18440
18441
18442 return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
18443 // summary:
18444 // Abstract base class for TabContainer. Must define _makeController() to instantiate
18445 // and return the widget that displays the tab labels
18446 // description:
18447 // A TabContainer is a container that has multiple panes, but shows only
18448 // one pane at a time. There are a set of tabs corresponding to each pane,
18449 // where each tab has the name (aka title) of the pane, and optionally a close button.
18450
18451 // tabPosition: String
18452 // Defines where tabs go relative to tab content.
18453 // "top", "bottom", "left-h", "right-h"
18454 tabPosition: "top",
18455
18456 baseClass: "dijitTabContainer",
18457
18458 // tabStrip: [const] Boolean
18459 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
18460 // around the set of tabs. Not supported by claro theme.
18461 tabStrip: false,
18462
18463 // nested: [const] Boolean
18464 // If true, use styling for a TabContainer nested inside another TabContainer.
18465 // For tundra etc., makes tabs look like links, and hides the outer
18466 // border since the outer TabContainer already has a border.
18467 nested: false,
18468
18469 templateString: template,
18470
18471 postMixInProperties: function(){
18472 // set class name according to tab position, ex: dijitTabContainerTop
18473 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
18474
18475 this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden");
18476
18477 this.inherited(arguments);
18478 },
18479
18480 buildRendering: function(){
18481 this.inherited(arguments);
18482
18483 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
18484 this.tablist = this._makeController(this.tablistNode);
18485
18486 if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); }
18487
18488 if(this.nested){
18489 /* workaround IE's lack of support for "a > b" selectors by
18490 * tagging each node in the template.
18491 */
18492 domClass.add(this.domNode, "dijitTabContainerNested");
18493 domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested");
18494 domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested");
18495 domClass.add(this.containerNode, "dijitTabPaneWrapperNested");
18496 }else{
18497 domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
18498 }
18499 },
18500
18501 _setupChild: function(/*dijit/_WidgetBase*/ tab){
18502 // Overrides StackContainer._setupChild().
18503 domClass.add(tab.domNode, "dijitTabPane");
18504 this.inherited(arguments);
18505 },
18506
18507 startup: function(){
18508 if(this._started){ return; }
18509
18510 // wire up the tablist and its tabs
18511 this.tablist.startup();
18512
18513 this.inherited(arguments);
18514 },
18515
18516 layout: function(){
18517 // Overrides StackContainer.layout().
18518 // Configure the content pane to take up all the space except for where the tabs are
18519
18520 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
18521
18522 var sc = this.selectedChildWidget;
18523
18524 if(this.doLayout){
18525 // position and size the titles and the container node
18526 var titleAlign = this.tabPosition.replace(/-h/, "");
18527 this.tablist.layoutAlign = titleAlign;
18528 var children = [this.tablist, {
18529 domNode: this.tablistSpacer,
18530 layoutAlign: titleAlign
18531 }, {
18532 domNode: this.containerNode,
18533 layoutAlign: "client"
18534 }];
18535 layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
18536
18537 // Compute size to make each of my children.
18538 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
18539 this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
18540
18541 if(sc && sc.resize){
18542 sc.resize(this._containerContentBox);
18543 }
18544 }else{
18545 // just layout the tab controller, so it can position left/right buttons etc.
18546 if(this.tablist.resize){
18547 //make the tabs zero width so that they don't interfere with width calc, then reset
18548 var s = this.tablist.domNode.style;
18549 s.width="0";
18550 var width = domGeometry.getContentBox(this.domNode).w;
18551 s.width="";
18552 this.tablist.resize({w: width});
18553 }
18554
18555 // and call resize() on the selected pane just to tell it that it's been made visible
18556 if(sc && sc.resize){
18557 sc.resize();
18558 }
18559 }
18560 },
18561
18562 destroy: function(){
18563 if(this.tablist){
18564 this.tablist.destroy();
18565 }
18566 this.inherited(arguments);
18567 }
18568 });
18569
18570 });
18571
18572 },
18573 'dijit/form/Form':function(){
18574 define("dijit/form/Form", [
18575 "dojo/_base/declare", // declare
18576 "dojo/dom-attr", // domAttr.set
18577 "dojo/_base/event", // event.stop
18578 "dojo/_base/kernel", // kernel.deprecated
18579 "dojo/sniff", // has("ie")
18580 "../_Widget",
18581 "../_TemplatedMixin",
18582 "./_FormMixin",
18583 "../layout/_ContentPaneResizeMixin"
18584 ], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
18585
18586 // module:
18587 // dijit/form/Form
18588
18589
18590 return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
18591 // summary:
18592 // Widget corresponding to HTML form tag, for validation and serialization
18593 //
18594 // example:
18595 // | <form data-dojo-type="dijit/form/Form" id="myForm">
18596 // | Name: <input type="text" name="name" />
18597 // | </form>
18598 // | myObj = {name: "John Doe"};
18599 // | dijit.byId('myForm').set('value', myObj);
18600 // |
18601 // | myObj=dijit.byId('myForm').get('value');
18602
18603 // HTML <FORM> attributes
18604
18605 // name: String?
18606 // Name of form for scripting.
18607 name: "",
18608
18609 // action: String?
18610 // Server-side form handler.
18611 action: "",
18612
18613 // method: String?
18614 // HTTP method used to submit the form, either "GET" or "POST".
18615 method: "",
18616
18617 // encType: String?
18618 // Encoding type for the form, ex: application/x-www-form-urlencoded.
18619 encType: "",
18620
18621 // accept-charset: String?
18622 // List of supported charsets.
18623 "accept-charset": "",
18624
18625 // accept: String?
18626 // List of MIME types for file upload.
18627 accept: "",
18628
18629 // target: String?
18630 // Target frame for the document to be opened in.
18631 target: "",
18632
18633 templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
18634
18635 postMixInProperties: function(){
18636 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
18637 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
18638 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
18639 this.inherited(arguments);
18640 },
18641
18642 execute: function(/*Object*/ /*===== formContents =====*/){
18643 // summary:
18644 // Deprecated: use submit()
18645 // tags:
18646 // deprecated
18647 },
18648
18649 onExecute: function(){
18650 // summary:
18651 // Deprecated: use onSubmit()
18652 // tags:
18653 // deprecated
18654 },
18655
18656 _setEncTypeAttr: function(/*String*/ value){
18657 this.encType = value;
18658 domAttr.set(this.domNode, "encType", value);
18659 if(has("ie")){ this.domNode.encoding = value; }
18660 },
18661
18662 reset: function(/*Event?*/ e){
18663 // summary:
18664 // restores all widget values back to their init values,
18665 // calls onReset() which can cancel the reset by returning false
18666
18667 // create fake event so we can know if preventDefault() is called
18668 var faux = {
18669 returnValue: true, // the IE way
18670 preventDefault: function(){ // not IE
18671 this.returnValue = false;
18672 },
18673 stopPropagation: function(){},
18674 currentTarget: e ? e.target : this.domNode,
18675 target: e ? e.target : this.domNode
18676 };
18677 // if return value is not exactly false, and haven't called preventDefault(), then reset
18678 if(!(this.onReset(faux) === false) && faux.returnValue){
18679 this.inherited(arguments, []);
18680 }
18681 },
18682
18683 onReset: function(/*Event?*/ /*===== e =====*/){
18684 // summary:
18685 // Callback when user resets the form. This method is intended
18686 // to be over-ridden. When the `reset` method is called
18687 // programmatically, the return value from `onReset` is used
18688 // to compute whether or not resetting should proceed
18689 // tags:
18690 // callback
18691 return true; // Boolean
18692 },
18693
18694 _onReset: function(e){
18695 this.reset(e);
18696 event.stop(e);
18697 return false;
18698 },
18699
18700 _onSubmit: function(e){
18701 var fp = this.constructor.prototype;
18702 // TODO: remove this if statement beginning with 2.0
18703 if(this.execute != fp.execute || this.onExecute != fp.onExecute){
18704 kernel.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
18705 this.onExecute();
18706 this.execute(this.getValues());
18707 }
18708 if(this.onSubmit(e) === false){ // only exactly false stops submit
18709 event.stop(e);
18710 }
18711 },
18712
18713 onSubmit: function(/*Event?*/ /*===== e =====*/){
18714 // summary:
18715 // Callback when user submits the form.
18716 // description:
18717 // This method is intended to be over-ridden, but by default it checks and
18718 // returns the validity of form elements. When the `submit`
18719 // method is called programmatically, the return value from
18720 // `onSubmit` is used to compute whether or not submission
18721 // should proceed
18722 // tags:
18723 // extension
18724
18725 return this.isValid(); // Boolean
18726 },
18727
18728 submit: function(){
18729 // summary:
18730 // programmatically submit form if and only if the `onSubmit` returns true
18731 if(!(this.onSubmit() === false)){
18732 this.containerNode.submit();
18733 }
18734 }
18735 });
18736 });
18737
18738 },
18739 'dojo/store/Memory':function(){
18740 define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
18741 function(declare, QueryResults, SimpleQueryEngine /*=====, Store =====*/){
18742
18743 // module:
18744 // dojo/store/Memory
18745
18746 // No base class, but for purposes of documentation, the base class is dojo/store/api/Store
18747 var base = null;
18748 /*===== base = Store; =====*/
18749
18750 return declare("dojo.store.Memory", base, {
18751 // summary:
18752 // This is a basic in-memory object store. It implements dojo/store/api/Store.
18753 constructor: function(options){
18754 // summary:
18755 // Creates a memory object store.
18756 // options: dojo/store/Memory
18757 // This provides any configuration information that will be mixed into the store.
18758 // This should generally include the data property to provide the starting set of data.
18759 for(var i in options){
18760 this[i] = options[i];
18761 }
18762 this.setData(this.data || []);
18763 },
18764 // data: Array
18765 // The array of all the objects in the memory store
18766 data:null,
18767
18768 // idProperty: String
18769 // Indicates the property to use as the identity property. The values of this
18770 // property should be unique.
18771 idProperty: "id",
18772
18773 // index: Object
18774 // An index of data indices into the data array by id
18775 index:null,
18776
18777 // queryEngine: Function
18778 // Defines the query engine to use for querying the data store
18779 queryEngine: SimpleQueryEngine,
18780 get: function(id){
18781 // summary:
18782 // Retrieves an object by its identity
18783 // id: Number
18784 // The identity to use to lookup the object
18785 // returns: Object
18786 // The object in the store that matches the given id.
18787 return this.data[this.index[id]];
18788 },
18789 getIdentity: function(object){
18790 // summary:
18791 // Returns an object's identity
18792 // object: Object
18793 // The object to get the identity from
18794 // returns: Number
18795 return object[this.idProperty];
18796 },
18797 put: function(object, options){
18798 // summary:
18799 // Stores an object
18800 // object: Object
18801 // The object to store.
18802 // options: dojo/store/api/Store.PutDirectives?
18803 // Additional metadata for storing the data. Includes an "id"
18804 // property if a specific id is to be used.
18805 // returns: Number
18806 var data = this.data,
18807 index = this.index,
18808 idProperty = this.idProperty;
18809 var id = object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
18810 if(id in index){
18811 // object exists
18812 if(options && options.overwrite === false){
18813 throw new Error("Object already exists");
18814 }
18815 // replace the entry in data
18816 data[index[id]] = object;
18817 }else{
18818 // add the new object
18819 index[id] = data.push(object) - 1;
18820 }
18821 return id;
18822 },
18823 add: function(object, options){
18824 // summary:
18825 // Creates an object, throws an error if the object already exists
18826 // object: Object
18827 // The object to store.
18828 // options: dojo/store/api/Store.PutDirectives?
18829 // Additional metadata for storing the data. Includes an "id"
18830 // property if a specific id is to be used.
18831 // returns: Number
18832 (options = options || {}).overwrite = false;
18833 // call put with overwrite being false
18834 return this.put(object, options);
18835 },
18836 remove: function(id){
18837 // summary:
18838 // Deletes an object by its identity
18839 // id: Number
18840 // The identity to use to delete the object
18841 // returns: Boolean
18842 // Returns true if an object was removed, falsy (undefined) if no object matched the id
18843 var index = this.index;
18844 var data = this.data;
18845 if(id in index){
18846 data.splice(index[id], 1);
18847 // now we have to reindex
18848 this.setData(data);
18849 return true;
18850 }
18851 },
18852 query: function(query, options){
18853 // summary:
18854 // Queries the store for objects.
18855 // query: Object
18856 // The query to use for retrieving objects from the store.
18857 // options: dojo/store/api/Store.QueryOptions?
18858 // The optional arguments to apply to the resultset.
18859 // returns: dojo/store/api/Store.QueryResults
18860 // The results of the query, extended with iterative methods.
18861 //
18862 // example:
18863 // Given the following store:
18864 //
18865 // | var store = new Memory({
18866 // | data: [
18867 // | {id: 1, name: "one", prime: false },
18868 // | {id: 2, name: "two", even: true, prime: true},
18869 // | {id: 3, name: "three", prime: true},
18870 // | {id: 4, name: "four", even: true, prime: false},
18871 // | {id: 5, name: "five", prime: true}
18872 // | ]
18873 // | });
18874 //
18875 // ...find all items where "prime" is true:
18876 //
18877 // | var results = store.query({ prime: true });
18878 //
18879 // ...or find all items where "even" is true:
18880 //
18881 // | var results = store.query({ even: true });
18882 return QueryResults(this.queryEngine(query, options)(this.data));
18883 },
18884 setData: function(data){
18885 // summary:
18886 // Sets the given data as the source for this store, and indexes it
18887 // data: Object[]
18888 // An array of objects to use as the source of data.
18889 if(data.items){
18890 // just for convenience with the data format IFRS expects
18891 this.idProperty = data.identifier;
18892 data = this.data = data.items;
18893 }else{
18894 this.data = data;
18895 }
18896 this.index = {};
18897 for(var i = 0, l = data.length; i < l; i++){
18898 this.index[data[i][this.idProperty]] = i;
18899 }
18900 }
18901 });
18902
18903 });
18904
18905 },
18906 '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",
18907 'dijit/_base/sniff':function(){
18908 define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
18909
18910 // module:
18911 // dijit/_base/sniff
18912
18913 /*=====
18914 return {
18915 // summary:
18916 // Deprecated, back compatibility module, new code should require dojo/uacss directly instead of this module.
18917 };
18918 =====*/
18919 });
18920
18921 },
18922 'dijit/Toolbar':function(){
18923 define("dijit/Toolbar", [
18924 "require",
18925 "dojo/_base/declare", // declare
18926 "dojo/has",
18927 "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
18928 "dojo/ready",
18929 "./_Widget",
18930 "./_KeyNavContainer",
18931 "./_TemplatedMixin"
18932 ], function(require, declare, has, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
18933
18934 // module:
18935 // dijit/Toolbar
18936
18937
18938 // Back compat w/1.6, remove for 2.0
18939 if(has("dijit-legacy-requires")){
18940 ready(0, function(){
18941 var requires = ["dijit/ToolbarSeparator"];
18942 require(requires); // use indirection so modules not rolled into a build
18943 });
18944 }
18945
18946 return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
18947 // summary:
18948 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
18949
18950 templateString:
18951 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
18952 '</div>',
18953
18954 baseClass: "dijitToolbar",
18955
18956 postCreate: function(){
18957 this.inherited(arguments);
18958
18959 this.connectKeyNavHandlers(
18960 this.isLeftToRight() ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
18961 this.isLeftToRight() ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
18962 );
18963 }
18964 });
18965 });
18966
18967 },
18968 'dijit/layout/StackContainer':function(){
18969 define("dijit/layout/StackContainer", [
18970 "dojo/_base/array", // array.forEach array.indexOf array.some
18971 "dojo/cookie", // cookie
18972 "dojo/_base/declare", // declare
18973 "dojo/dom-class", // domClass.add domClass.replace
18974 "dojo/has", // has("dijit-legacy-requires")
18975 "dojo/_base/lang", // lang.extend
18976 "dojo/ready",
18977 "dojo/topic", // publish
18978 "../registry", // registry.byId
18979 "../_WidgetBase",
18980 "./_LayoutWidget",
18981 "dojo/i18n!../nls/common"
18982 ], function(array, cookie, declare, domClass, has, lang, ready, topic,
18983 registry, _WidgetBase, _LayoutWidget){
18984
18985 // module:
18986 // dijit/layout/StackContainer
18987
18988 // Back compat w/1.6, remove for 2.0
18989 if(has("dijit-legacy-requires")){
18990 ready(0, function(){
18991 var requires = ["dijit/layout/StackController"];
18992 require(requires); // use indirection so modules not rolled into a build
18993 });
18994 }
18995
18996 var StackContainer = declare("dijit.layout.StackContainer", _LayoutWidget, {
18997 // summary:
18998 // A container that has multiple children, but shows only
18999 // one child at a time
19000 //
19001 // description:
19002 // A container for widgets (ContentPanes, for example) That displays
19003 // only one Widget at a time.
19004 //
19005 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
19006 //
19007 // Can be base class for container, Wizard, Show, etc.
19008 //
19009 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
19010 // children of a `StackContainer`.
19011
19012 // doLayout: Boolean
19013 // If true, change the size of my currently displayed child to match my size
19014 doLayout: true,
19015
19016 // persist: Boolean
19017 // Remembers the selected child across sessions
19018 persist: false,
19019
19020 baseClass: "dijitStackContainer",
19021
19022 /*=====
19023 // selectedChildWidget: [readonly] dijit._Widget
19024 // References the currently selected child widget, if any.
19025 // Adjust selected child with selectChild() method.
19026 selectedChildWidget: null,
19027 =====*/
19028
19029 buildRendering: function(){
19030 this.inherited(arguments);
19031 domClass.add(this.domNode, "dijitLayoutContainer");
19032 this.containerNode.setAttribute("role", "tabpanel");
19033 },
19034
19035 postCreate: function(){
19036 this.inherited(arguments);
19037 this.connect(this.domNode, "onkeypress", this._onKeyPress);
19038 },
19039
19040 startup: function(){
19041 if(this._started){ return; }
19042
19043 var children = this.getChildren();
19044
19045 // Setup each page panel to be initially hidden
19046 array.forEach(children, this._setupChild, this);
19047
19048 // Figure out which child to initially display, defaulting to first one
19049 if(this.persist){
19050 this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
19051 }else{
19052 array.some(children, function(child){
19053 if(child.selected){
19054 this.selectedChildWidget = child;
19055 }
19056 return child.selected;
19057 }, this);
19058 }
19059 var selected = this.selectedChildWidget;
19060 if(!selected && children[0]){
19061 selected = this.selectedChildWidget = children[0];
19062 selected.selected = true;
19063 }
19064
19065 // Publish information about myself so any StackControllers can initialize.
19066 // This needs to happen before this.inherited(arguments) so that for
19067 // TabContainer, this._contentBox doesn't include the space for the tab labels.
19068 topic.publish(this.id+"-startup", {children: children, selected: selected});
19069
19070 // Startup each child widget, and do initial layout like setting this._contentBox,
19071 // then calls this.resize() which does the initial sizing on the selected child.
19072 this.inherited(arguments);
19073 },
19074
19075 resize: function(){
19076 // Overrides _LayoutWidget.resize()
19077 // Resize is called when we are first made visible (it's called from startup()
19078 // if we are initially visible). If this is the first time we've been made
19079 // visible then show our first child.
19080 if(!this._hasBeenShown){
19081 this._hasBeenShown = true;
19082 var selected = this.selectedChildWidget;
19083 if(selected){
19084 this._showChild(selected);
19085 }
19086 }
19087 this.inherited(arguments);
19088 },
19089
19090 _setupChild: function(/*dijit/_WidgetBase*/ child){
19091 // Overrides _LayoutWidget._setupChild()
19092
19093 this.inherited(arguments);
19094
19095 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
19096
19097 // remove the title attribute so it doesn't show up when i hover
19098 // over a node
19099 child.domNode.title = "";
19100 },
19101
19102 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
19103 // Overrides _Container.addChild() to do layout and publish events
19104
19105 this.inherited(arguments);
19106
19107 if(this._started){
19108 topic.publish(this.id+"-addChild", child, insertIndex); // publish
19109
19110 // in case the tab titles have overflowed from one line to two lines
19111 // (or, if this if first child, from zero lines to one line)
19112 // TODO: w/ScrollingTabController this is no longer necessary, although
19113 // ScrollTabController.resize() does need to get called to show/hide
19114 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
19115 // If this is updated to not layout [except for initial child added / last child removed], update
19116 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
19117 this.layout();
19118
19119 // if this is the first child, then select it
19120 if(!this.selectedChildWidget){
19121 this.selectChild(child);
19122 }
19123 }
19124 },
19125
19126 removeChild: function(/*dijit/_WidgetBase*/ page){
19127 // Overrides _Container.removeChild() to do layout and publish events
19128
19129 this.inherited(arguments);
19130
19131 if(this._started){
19132 // this will notify any tablists to remove a button; do this first because it may affect sizing
19133 topic.publish(this.id + "-removeChild", page); // publish
19134 }
19135
19136 // If all our children are being destroyed than don't run the code below (to select another page),
19137 // because we are deleting every page one by one
19138 if(this._descendantsBeingDestroyed){ return; }
19139
19140 // Select new page to display, also updating TabController to show the respective tab.
19141 // Do this before layout call because it can affect the height of the TabController.
19142 if(this.selectedChildWidget === page){
19143 this.selectedChildWidget = undefined;
19144 if(this._started){
19145 var children = this.getChildren();
19146 if(children.length){
19147 this.selectChild(children[0]);
19148 }
19149 }
19150 }
19151
19152 if(this._started){
19153 // In case the tab titles now take up one line instead of two lines
19154 // (note though that ScrollingTabController never overflows to multiple lines),
19155 // or the height has changed slightly because of addition/removal of tab which close icon
19156 this.layout();
19157 }
19158 },
19159
19160 selectChild: function(/*dijit/_WidgetBase|String*/ page, /*Boolean*/ animate){
19161 // summary:
19162 // Show the given widget (which must be one of my children)
19163 // page:
19164 // Reference to child widget or id of child widget
19165
19166 page = registry.byId(page);
19167
19168 if(this.selectedChildWidget != page){
19169 // Deselect old page and select new one
19170 var d = this._transition(page, this.selectedChildWidget, animate);
19171 this._set("selectedChildWidget", page);
19172 topic.publish(this.id+"-selectChild", page); // publish
19173
19174 if(this.persist){
19175 cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
19176 }
19177 }
19178
19179 return d; // If child has an href, promise that fires when the child's href finishes loading
19180 },
19181
19182 _transition: function(newWidget, oldWidget /*===== , animate =====*/){
19183 // summary:
19184 // Hide the old widget and display the new widget.
19185 // Subclasses should override this.
19186 // newWidget: dijit/_WidgetBase
19187 // The newly selected widget.
19188 // oldWidget: dijit/_WidgetBase
19189 // The previously selected widget.
19190 // animate: Boolean
19191 // Used by AccordionContainer to turn on/off slide effect.
19192 // tags:
19193 // protected extension
19194 if(oldWidget){
19195 this._hideChild(oldWidget);
19196 }
19197 var d = this._showChild(newWidget);
19198
19199 // Size the new widget, in case this is the first time it's being shown,
19200 // or I have been resized since the last time it was shown.
19201 // Note that page must be visible for resizing to work.
19202 if(newWidget.resize){
19203 if(this.doLayout){
19204 newWidget.resize(this._containerContentBox || this._contentBox);
19205 }else{
19206 // the child should pick it's own size but we still need to call resize()
19207 // (with no arguments) to let the widget lay itself out
19208 newWidget.resize();
19209 }
19210 }
19211
19212 return d; // If child has an href, promise that fires when the child's href finishes loading
19213 },
19214
19215 _adjacent: function(/*Boolean*/ forward){
19216 // summary:
19217 // Gets the next/previous child widget in this container from the current selection.
19218
19219 // TODO: remove for 2.0 if this isn't being used. Otherwise, fix to skip disabled tabs.
19220
19221 var children = this.getChildren();
19222 var index = array.indexOf(children, this.selectedChildWidget);
19223 index += forward ? 1 : children.length - 1;
19224 return children[ index % children.length ]; // dijit/_WidgetBase
19225 },
19226
19227 forward: function(){
19228 // summary:
19229 // Advance to next page.
19230 return this.selectChild(this._adjacent(true), true);
19231 },
19232
19233 back: function(){
19234 // summary:
19235 // Go back to previous page.
19236 return this.selectChild(this._adjacent(false), true);
19237 },
19238
19239 _onKeyPress: function(e){
19240 topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
19241 },
19242
19243 layout: function(){
19244 // Implement _LayoutWidget.layout() virtual method.
19245 var child = this.selectedChildWidget;
19246 if(child && child.resize){
19247 if(this.doLayout){
19248 child.resize(this._containerContentBox || this._contentBox);
19249 }else{
19250 child.resize();
19251 }
19252 }
19253 },
19254
19255 _showChild: function(/*dijit/_WidgetBase*/ page){
19256 // summary:
19257 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
19258 // it can do any updates it needs regarding loading href's etc.
19259 // returns:
19260 // Promise that fires when page has finished showing, or true if there's no href
19261 var children = this.getChildren();
19262 page.isFirstChild = (page == children[0]);
19263 page.isLastChild = (page == children[children.length-1]);
19264 page._set("selected", true);
19265
19266 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
19267
19268 return (page._onShow && page._onShow()) || true;
19269 },
19270
19271 _hideChild: function(/*dijit/_WidgetBase*/ page){
19272 // summary:
19273 // Hide the specified child by changing it's CSS, and call _onHide() so
19274 // it's notified.
19275 page._set("selected", false);
19276 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
19277
19278 page.onHide && page.onHide();
19279 },
19280
19281 closeChild: function(/*dijit/_WidgetBase*/ page){
19282 // summary:
19283 // Callback when user clicks the [X] to remove a page.
19284 // If onClose() returns true then remove and destroy the child.
19285 // tags:
19286 // private
19287 var remove = page.onClose(this, page);
19288 if(remove){
19289 this.removeChild(page);
19290 // makes sure we can clean up executeScripts in ContentPane onUnLoad
19291 page.destroyRecursive();
19292 }
19293 },
19294
19295 destroyDescendants: function(/*Boolean*/ preserveDom){
19296 this._descendantsBeingDestroyed = true;
19297 this.selectedChildWidget = undefined;
19298 array.forEach(this.getChildren(), function(child){
19299 if(!preserveDom){
19300 this.removeChild(child);
19301 }
19302 child.destroyRecursive(preserveDom);
19303 }, this);
19304 this._descendantsBeingDestroyed = false;
19305 }
19306 });
19307
19308 StackContainer.ChildWidgetProperties = {
19309 // summary:
19310 // These properties can be specified for the children of a StackContainer.
19311
19312 // selected: Boolean
19313 // Specifies that this widget should be the initially displayed pane.
19314 // Note: to change the selected child use `dijit/layout/StackContainer.selectChild`
19315 selected: false,
19316
19317 // disabled: Boolean
19318 // Specifies that the button to select this pane should be disabled.
19319 // Doesn't affect programmatic selection of the pane, nor does it deselect the pane if it is currently selected.
19320 disabled: false,
19321
19322 // closable: Boolean
19323 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
19324 closable: false,
19325
19326 // iconClass: String
19327 // CSS Class specifying icon to use in label associated with this pane.
19328 iconClass: "dijitNoIcon",
19329
19330 // showTitle: Boolean
19331 // When true, display title of this widget as tab label etc., rather than just using
19332 // icon specified in iconClass
19333 showTitle: true
19334 };
19335
19336 // Since any widget can be specified as a StackContainer child, mix them
19337 // into the base widget class. (This is a hack, but it's effective.)
19338 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
19339 lang.extend(_WidgetBase, /*===== {} || =====*/ StackContainer.ChildWidgetProperties);
19340
19341 return StackContainer;
19342 });
19343
19344 },
19345 'dojo/regexp':function(){
19346 define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang){
19347
19348 // module:
19349 // dojo/regexp
19350
19351 var regexp = {
19352 // summary:
19353 // Regular expressions and Builder resources
19354 };
19355 lang.setObject("dojo.regexp", regexp);
19356
19357 regexp.escapeString = function(/*String*/str, /*String?*/except){
19358 // summary:
19359 // Adds escape sequences for special characters in regular expressions
19360 // except:
19361 // a String with special characters to be left unescaped
19362
19363 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
19364 if(except && except.indexOf(ch) != -1){
19365 return ch;
19366 }
19367 return "\\" + ch;
19368 }); // String
19369 };
19370
19371 regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
19372 // summary:
19373 // Builds a regular expression that groups subexpressions
19374 // description:
19375 // A utility function used by some of the RE generators. The
19376 // subexpressions are constructed by the function, re, in the second
19377 // parameter. re builds one subexpression for each elem in the array
19378 // a, in the first parameter. Returns a string for a regular
19379 // expression that groups all the subexpressions.
19380 // arr:
19381 // A single value or an array of values.
19382 // re:
19383 // A function. Takes one parameter and converts it to a regular
19384 // expression.
19385 // nonCapture:
19386 // If true, uses non-capturing match, otherwise matches are retained
19387 // by regular expression. Defaults to false
19388
19389 // case 1: a is a single value.
19390 if(!(arr instanceof Array)){
19391 return re(arr); // String
19392 }
19393
19394 // case 2: a is an array
19395 var b = [];
19396 for(var i = 0; i < arr.length; i++){
19397 // convert each elem to a RE
19398 b.push(re(arr[i]));
19399 }
19400
19401 // join the REs as alternatives in a RE group.
19402 return regexp.group(b.join("|"), nonCapture); // String
19403 };
19404
19405 regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
19406 // summary:
19407 // adds group match to expression
19408 // nonCapture:
19409 // If true, uses non-capturing match, otherwise matches are retained
19410 // by regular expression.
19411 return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
19412 };
19413
19414 return regexp;
19415 });
19416
19417 },
19418 'dijit/DropDownMenu':function(){
19419 require({cache:{
19420 '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"}});
19421 define("dijit/DropDownMenu", [
19422 "dojo/_base/declare", // declare
19423 "dojo/_base/event", // event.stop
19424 "dojo/keys", // keys
19425 "dojo/text!./templates/Menu.html",
19426 "./_OnDijitClickMixin",
19427 "./_MenuBase"
19428 ], function(declare, event, keys, template, _OnDijitClickMixin, _MenuBase){
19429
19430 // module:
19431 // dijit/DropDownMenu
19432
19433 return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
19434 // summary:
19435 // A menu, without features for context menu (Meaning, drop down menu)
19436
19437 templateString: template,
19438
19439 baseClass: "dijitMenu",
19440
19441 postCreate: function(){
19442 this.inherited(arguments);
19443 var l = this.isLeftToRight();
19444 this._openSubMenuKey = l ? keys.RIGHT_ARROW : keys.LEFT_ARROW;
19445 this._closeSubMenuKey = l ? keys.LEFT_ARROW : keys.RIGHT_ARROW;
19446 this.connectKeyNavHandlers([keys.UP_ARROW], [keys.DOWN_ARROW]);
19447 },
19448
19449 _onKeyPress: function(/*Event*/ evt){
19450 // summary:
19451 // Handle keyboard based menu navigation.
19452 // tags:
19453 // protected
19454
19455 if(evt.ctrlKey || evt.altKey){ return; }
19456
19457 switch(evt.charOrCode){
19458 case this._openSubMenuKey:
19459 this._moveToPopup(evt);
19460 event.stop(evt);
19461 break;
19462 case this._closeSubMenuKey:
19463 if(this.parentMenu){
19464 if(this.parentMenu._isMenuBar){
19465 this.parentMenu.focusPrev();
19466 }else{
19467 this.onCancel(false);
19468 }
19469 }else{
19470 event.stop(evt);
19471 }
19472 break;
19473 }
19474 }
19475 });
19476 });
19477
19478 },
19479 'dijit/form/_FormMixin':function(){
19480 define("dijit/form/_FormMixin", [
19481 "dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
19482 "dojo/_base/declare", // declare
19483 "dojo/_base/kernel", // kernel.deprecated
19484 "dojo/_base/lang", // lang.hitch lang.isArray
19485 "dojo/on",
19486 "dojo/window" // winUtils.scrollIntoView
19487 ], function(array, declare, kernel, lang, on, winUtils){
19488
19489 // module:
19490 // dijit/form/_FormMixin
19491
19492 return declare("dijit.form._FormMixin", null, {
19493 // summary:
19494 // Mixin for containers of form widgets (i.e. widgets that represent a single value
19495 // and can be children of a `<form>` node or `dijit/form/Form` widget)
19496 // description:
19497 // Can extract all the form widgets
19498 // values and combine them into a single javascript object, or alternately
19499 // take such an object and set the values for all the contained
19500 // form widgets
19501
19502 /*=====
19503 // value: Object
19504 // Name/value hash for each child widget with a name and value.
19505 // Child widgets without names are not part of the hash.
19506 //
19507 // If there are multiple child widgets w/the same name, value is an array,
19508 // unless they are radio buttons in which case value is a scalar (since only
19509 // one radio button can be checked at a time).
19510 //
19511 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
19512 //
19513 // Example:
19514 // | { name: "John Smith", interests: ["sports", "movies"] }
19515 =====*/
19516
19517 // state: [readonly] String
19518 // Will be "Error" if one or more of the child widgets has an invalid value,
19519 // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
19520 // which indicates that the form is ready to be submitted.
19521 state: "",
19522
19523 // TODO:
19524 // * Repeater
19525 // * better handling for arrays. Often form elements have names with [] like
19526 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
19527
19528
19529 _getDescendantFormWidgets: function(/*dijit/_WidgetBase[]?*/ children){
19530 // summary:
19531 // Returns all form widget descendants, searching through non-form child widgets like BorderContainer
19532 var res = [];
19533 array.forEach(children || this.getChildren(), function(child){
19534 if("value" in child){
19535 res.push(child);
19536 }else{
19537 res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
19538 }
19539 }, this);
19540 return res;
19541 },
19542
19543 reset: function(){
19544 array.forEach(this._getDescendantFormWidgets(), function(widget){
19545 if(widget.reset){
19546 widget.reset();
19547 }
19548 });
19549 },
19550
19551 validate: function(){
19552 // summary:
19553 // returns if the form is valid - same as isValid - but
19554 // provides a few additional (ui-specific) features:
19555 //
19556 // 1. it will highlight any sub-widgets that are not valid
19557 // 2. it will call focus() on the first invalid sub-widget
19558 var didFocus = false;
19559 return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
19560 // Need to set this so that "required" widgets get their
19561 // state set.
19562 widget._hasBeenBlurred = true;
19563 var valid = widget.disabled || !widget.validate || widget.validate();
19564 if(!valid && !didFocus){
19565 // Set focus of the first non-valid widget
19566 winUtils.scrollIntoView(widget.containerNode || widget.domNode);
19567 widget.focus();
19568 didFocus = true;
19569 }
19570 return valid;
19571 }), function(item){ return item; });
19572 },
19573
19574 setValues: function(val){
19575 kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
19576 return this.set('value', val);
19577 },
19578 _setValueAttr: function(/*Object*/ obj){
19579 // summary:
19580 // Fill in form values from according to an Object (in the format returned by get('value'))
19581
19582 // generate map from name --> [list of widgets with that name]
19583 var map = { };
19584 array.forEach(this._getDescendantFormWidgets(), function(widget){
19585 if(!widget.name){ return; }
19586 var entry = map[widget.name] || (map[widget.name] = [] );
19587 entry.push(widget);
19588 });
19589
19590 for(var name in map){
19591 if(!map.hasOwnProperty(name)){
19592 continue;
19593 }
19594 var widgets = map[name], // array of widgets w/this name
19595 values = lang.getObject(name, false, obj); // list of values for those widgets
19596
19597 if(values === undefined){
19598 continue;
19599 }
19600 if(!lang.isArray(values)){
19601 values = [ values ];
19602 }
19603 if(typeof widgets[0].checked == 'boolean'){
19604 // for checkbox/radio, values is a list of which widgets should be checked
19605 array.forEach(widgets, function(w){
19606 w.set('value', array.indexOf(values, w.value) != -1);
19607 });
19608 }else if(widgets[0].multiple){
19609 // it takes an array (e.g. multi-select)
19610 widgets[0].set('value', values);
19611 }else{
19612 // otherwise, values is a list of values to be assigned sequentially to each widget
19613 array.forEach(widgets, function(w, i){
19614 w.set('value', values[i]);
19615 });
19616 }
19617 }
19618
19619 /***
19620 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
19621
19622 array.forEach(this.containerNode.elements, function(element){
19623 if(element.name == ''){return}; // like "continue"
19624 var namePath = element.name.split(".");
19625 var myObj=obj;
19626 var name=namePath[namePath.length-1];
19627 for(var j=1,len2=namePath.length;j<len2;++j){
19628 var p=namePath[j - 1];
19629 // repeater support block
19630 var nameA=p.split("[");
19631 if(nameA.length > 1){
19632 if(typeof(myObj[nameA[0]]) == "undefined"){
19633 myObj[nameA[0]]=[ ];
19634 } // if
19635
19636 nameIndex=parseInt(nameA[1]);
19637 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
19638 myObj[nameA[0]][nameIndex] = { };
19639 }
19640 myObj=myObj[nameA[0]][nameIndex];
19641 continue;
19642 } // repeater support ends
19643
19644 if(typeof(myObj[p]) == "undefined"){
19645 myObj=undefined;
19646 break;
19647 };
19648 myObj=myObj[p];
19649 }
19650
19651 if(typeof(myObj) == "undefined"){
19652 return; // like "continue"
19653 }
19654 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
19655 return; // like "continue"
19656 }
19657
19658 // TODO: widget values (just call set('value', ...) on the widget)
19659
19660 // TODO: maybe should call dojo.getNodeProp() instead
19661 switch(element.type){
19662 case "checkbox":
19663 element.checked = (name in myObj) &&
19664 array.some(myObj[name], function(val){ return val == element.value; });
19665 break;
19666 case "radio":
19667 element.checked = (name in myObj) && myObj[name] == element.value;
19668 break;
19669 case "select-multiple":
19670 element.selectedIndex=-1;
19671 array.forEach(element.options, function(option){
19672 option.selected = array.some(myObj[name], function(val){ return option.value == val; });
19673 });
19674 break;
19675 case "select-one":
19676 element.selectedIndex="0";
19677 array.forEach(element.options, function(option){
19678 option.selected = option.value == myObj[name];
19679 });
19680 break;
19681 case "hidden":
19682 case "text":
19683 case "textarea":
19684 case "password":
19685 element.value = myObj[name] || "";
19686 break;
19687 }
19688 });
19689 */
19690
19691 // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
19692 // which I am monitoring.
19693 },
19694
19695 getValues: function(){
19696 kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
19697 return this.get('value');
19698 },
19699 _getValueAttr: function(){
19700 // summary:
19701 // Returns Object representing form values. See description of `value` for details.
19702 // description:
19703
19704 // The value is updated into this.value every time a child has an onChange event,
19705 // so in the common case this function could just return this.value. However,
19706 // that wouldn't work when:
19707 //
19708 // 1. User presses return key to submit a form. That doesn't fire an onchange event,
19709 // and even if it did it would come too late due to the defer(...) in _handleOnChange()
19710 //
19711 // 2. app for some reason calls this.get("value") while the user is typing into a
19712 // form field. Not sure if that case needs to be supported or not.
19713
19714 // get widget values
19715 var obj = { };
19716 array.forEach(this._getDescendantFormWidgets(), function(widget){
19717 var name = widget.name;
19718 if(!name || widget.disabled){ return; }
19719
19720 // Single value widget (checkbox, radio, or plain <input> type widget)
19721 var value = widget.get('value');
19722
19723 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
19724 if(typeof widget.checked == 'boolean'){
19725 if(/Radio/.test(widget.declaredClass)){
19726 // radio button
19727 if(value !== false){
19728 lang.setObject(name, value, obj);
19729 }else{
19730 // give radio widgets a default of null
19731 value = lang.getObject(name, false, obj);
19732 if(value === undefined){
19733 lang.setObject(name, null, obj);
19734 }
19735 }
19736 }else{
19737 // checkbox/toggle button
19738 var ary=lang.getObject(name, false, obj);
19739 if(!ary){
19740 ary=[];
19741 lang.setObject(name, ary, obj);
19742 }
19743 if(value !== false){
19744 ary.push(value);
19745 }
19746 }
19747 }else{
19748 var prev=lang.getObject(name, false, obj);
19749 if(typeof prev != "undefined"){
19750 if(lang.isArray(prev)){
19751 prev.push(value);
19752 }else{
19753 lang.setObject(name, [prev, value], obj);
19754 }
19755 }else{
19756 // unique name
19757 lang.setObject(name, value, obj);
19758 }
19759 }
19760 });
19761
19762 /***
19763 * code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
19764 * but it doesn't understand [] notation, presumably)
19765 var obj = { };
19766 array.forEach(this.containerNode.elements, function(elm){
19767 if(!elm.name) {
19768 return; // like "continue"
19769 }
19770 var namePath = elm.name.split(".");
19771 var myObj=obj;
19772 var name=namePath[namePath.length-1];
19773 for(var j=1,len2=namePath.length;j<len2;++j){
19774 var nameIndex = null;
19775 var p=namePath[j - 1];
19776 var nameA=p.split("[");
19777 if(nameA.length > 1){
19778 if(typeof(myObj[nameA[0]]) == "undefined"){
19779 myObj[nameA[0]]=[ ];
19780 } // if
19781 nameIndex=parseInt(nameA[1]);
19782 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
19783 myObj[nameA[0]][nameIndex] = { };
19784 }
19785 }else if(typeof(myObj[nameA[0]]) == "undefined"){
19786 myObj[nameA[0]] = { }
19787 } // if
19788
19789 if(nameA.length == 1){
19790 myObj=myObj[nameA[0]];
19791 }else{
19792 myObj=myObj[nameA[0]][nameIndex];
19793 } // if
19794 } // for
19795
19796 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
19797 if(name == name.split("[")[0]){
19798 myObj[name]=elm.value;
19799 }else{
19800 // can not set value when there is no name
19801 }
19802 }else if(elm.type == "checkbox" && elm.checked){
19803 if(typeof(myObj[name]) == 'undefined'){
19804 myObj[name]=[ ];
19805 }
19806 myObj[name].push(elm.value);
19807 }else if(elm.type == "select-multiple"){
19808 if(typeof(myObj[name]) == 'undefined'){
19809 myObj[name]=[ ];
19810 }
19811 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
19812 if(elm.options[jdx].selected){
19813 myObj[name].push(elm.options[jdx].value);
19814 }
19815 }
19816 } // if
19817 name=undefined;
19818 }); // forEach
19819 ***/
19820 return obj;
19821 },
19822
19823 isValid: function(){
19824 // summary:
19825 // Returns true if all of the widgets are valid.
19826 // Deprecated, will be removed in 2.0. Use get("state") instead.
19827
19828 return this.state == "";
19829 },
19830
19831 onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
19832 // summary:
19833 // Stub function to connect to if you want to do something
19834 // (like disable/enable a submit button) when the valid
19835 // state changes on the form as a whole.
19836 //
19837 // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
19838 },
19839
19840 _getState: function(){
19841 // summary:
19842 // Compute what this.state should be based on state of children
19843 var states = array.map(this._descendants, function(w){
19844 return w.get("state") || "";
19845 });
19846
19847 return array.indexOf(states, "Error") >= 0 ? "Error" :
19848 array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
19849 },
19850
19851 disconnectChildren: function(){
19852 // summary:
19853 // Deprecated method. Applications no longer need to call this. Remove for 2.0.
19854 },
19855
19856 connectChildren: function(/*Boolean*/ inStartup){
19857 // summary:
19858 // You can call this function directly, ex. in the event that you
19859 // programmatically add a widget to the form *after* the form has been
19860 // initialized.
19861
19862 // TODO: rename for 2.0
19863
19864 this._descendants = this._getDescendantFormWidgets();
19865
19866 // To get notifications from children they need to be started. Children didn't used to need to be started,
19867 // so for back-compat, start them here
19868 array.forEach(this._descendants, function(child){
19869 if(!child._started){ child.startup(); }
19870 });
19871
19872 if(!inStartup){
19873 this._onChildChange();
19874 }
19875 },
19876
19877 _onChildChange: function(/*String*/ attr){
19878 // summary:
19879 // Called when child's value or disabled state changes
19880
19881 // The unit tests expect state update to be synchronous, so update it immediately.
19882 if(!attr || attr == "state" || attr == "disabled"){
19883 this._set("state", this._getState());
19884 }
19885
19886 // Use defer() to collapse value changes in multiple children into a single
19887 // update to my value. Multiple updates will occur on:
19888 // 1. Form.set()
19889 // 2. Form.reset()
19890 // 3. user selecting a radio button (which will de-select another radio button,
19891 // causing two onChange events)
19892 if(!attr || attr == "value" || attr == "disabled" || attr == "checked"){
19893 if(this._onChangeDelayTimer){
19894 this._onChangeDelayTimer.remove();
19895 }
19896 this._onChangeDelayTimer = this.defer(function(){
19897 delete this._onChangeDelayTimer;
19898 this._set("value", this.get("value"));
19899 }, 10);
19900 }
19901 },
19902
19903 startup: function(){
19904 this.inherited(arguments);
19905
19906 // Set initial this.value and this.state. Don't emit watch() notifications.
19907 this._descendants = this._getDescendantFormWidgets();
19908 this.value = this.get("value");
19909 this.state = this._getState();
19910
19911 // Initialize value and valid/invalid state tracking.
19912 var self = this;
19913 this.own(
19914 on(
19915 this.containerNode,
19916 "attrmodified-state, attrmodified-disabled, attrmodified-value, attrmodified-checked",
19917 function(evt){
19918 if(evt.target == self.domNode){
19919 return; // ignore events that I fire on myself because my children changed
19920 }
19921 self._onChildChange(evt.type.replace("attrmodified-", ""));
19922 }
19923 )
19924 );
19925
19926 // Make state change call onValidStateChange(), will be removed in 2.0
19927 this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
19928 },
19929
19930 destroy: function(){
19931 this.inherited(arguments);
19932 }
19933
19934 });
19935 });
19936
19937 },
19938 'dojo/data/util/simpleFetch':function(){
19939 define("dojo/data/util/simpleFetch", ["../../_base/lang", "../../_base/kernel", "./sorter"],
19940 function(lang, kernel, sorter){
19941 // module:
19942 // dojo/data/util/simpleFetch
19943 // summary:
19944 // The simpleFetch mixin is designed to serve as a set of function(s) that can
19945 // be mixed into other datastore implementations to accelerate their development.
19946
19947 var simpleFetch = {};
19948 lang.setObject("dojo.data.util.simpleFetch", simpleFetch);
19949
19950 simpleFetch.errorHandler = function(/*Object*/ errorData, /*Object*/ requestObject){
19951 // summary:
19952 // The error handler when there is an error fetching items. This function should not be called
19953 // directly and is used by simpleFetch.fetch().
19954 if(requestObject.onError){
19955 var scope = requestObject.scope || kernel.global;
19956 requestObject.onError.call(scope, errorData, requestObject);
19957 }
19958 };
19959
19960 simpleFetch.fetchHandler = function(/*Array*/ items, /*Object*/ requestObject){
19961 // summary:
19962 // The handler when items are sucessfully fetched. This function should not be called directly
19963 // and is used by simpleFetch.fetch().
19964 var oldAbortFunction = requestObject.abort || null,
19965 aborted = false,
19966
19967 startIndex = requestObject.start?requestObject.start: 0,
19968 endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
19969
19970 requestObject.abort = function(){
19971 aborted = true;
19972 if(oldAbortFunction){
19973 oldAbortFunction.call(requestObject);
19974 }
19975 };
19976
19977 var scope = requestObject.scope || kernel.global;
19978 if(!requestObject.store){
19979 requestObject.store = this;
19980 }
19981 if(requestObject.onBegin){
19982 requestObject.onBegin.call(scope, items.length, requestObject);
19983 }
19984 if(requestObject.sort){
19985 items.sort(sorter.createSortFunction(requestObject.sort, this));
19986 }
19987 if(requestObject.onItem){
19988 for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
19989 var item = items[i];
19990 if(!aborted){
19991 requestObject.onItem.call(scope, item, requestObject);
19992 }
19993 }
19994 }
19995 if(requestObject.onComplete && !aborted){
19996 var subset = null;
19997 if(!requestObject.onItem){
19998 subset = items.slice(startIndex, endIndex);
19999 }
20000 requestObject.onComplete.call(scope, subset, requestObject);
20001 }
20002 };
20003
20004 simpleFetch.fetch = function(/* Object? */ request){
20005 // summary:
20006 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20007 // be mixed into other datastore implementations to accelerate their development.
20008 // description:
20009 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
20010 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
20011 // is not designed to work for datastores that respond to a fetch() call by incrementally
20012 // loading items, or sequentially loading partial batches of the result
20013 // set. For datastores that mixin simpleFetch, simpleFetch
20014 // implements a fetch method that automatically handles eight of the fetch()
20015 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
20016 // The class mixing in simpleFetch should not implement fetch(),
20017 // but should instead implement a _fetchItems() method. The _fetchItems()
20018 // method takes three arguments, the keywordArgs object that was passed
20019 // to fetch(), a callback function to be called when the result array is
20020 // available, and an error callback to be called if something goes wrong.
20021 // The _fetchItems() method should ignore any keywordArgs parameters for
20022 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
20023 // The _fetchItems() method needs to correctly handle any other keywordArgs
20024 // parameters, including the query parameter and any optional parameters
20025 // (such as includeChildren). The _fetchItems() method should create an array of
20026 // result items and pass it to the fetchHandler along with the original request object --
20027 // or, the _fetchItems() method may, if it wants to, create an new request object
20028 // with other specifics about the request that are specific to the datastore and pass
20029 // that as the request object to the handler.
20030 //
20031 // For more information on this specific function, see dojo/data/api/Read.fetch()
20032 //
20033 // request:
20034 // The keywordArgs parameter may either be an instance of
20035 // conforming to dojo/data/api/Request or may be a simple anonymous object
20036 // that may contain any of the following:
20037 // | {
20038 // | query: query-object or query-string,
20039 // | queryOptions: object,
20040 // | onBegin: Function,
20041 // | onItem: Function,
20042 // | onComplete: Function,
20043 // | onError: Function,
20044 // | scope: object,
20045 // | start: int
20046 // | count: int
20047 // | sort: array
20048 // | }
20049 // All implementations should accept keywordArgs objects with any of
20050 // the 9 standard properties: query, onBegin, onItem, onComplete, onError
20051 // scope, sort, start, and count. Some implementations may accept additional
20052 // properties in the keywordArgs object as valid parameters, such as
20053 // {includeOutliers:true}.
20054 //
20055 // ####The *query* parameter
20056 //
20057 // The query may be optional in some data store implementations.
20058 // The dojo/data/api/Read API does not specify the syntax or semantics
20059 // of the query itself -- each different data store implementation
20060 // may have its own notion of what a query should look like.
20061 // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data
20062 // and dojox.data support an object structure query, where the object is a set of
20063 // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the
20064 // dijit widgets, such as ComboBox assume this to be the case when working with a datastore
20065 // when they dynamically update the query. Therefore, for maximum compatibility with dijit
20066 // widgets the recommended query parameter is a key/value object. That does not mean that the
20067 // the datastore may not take alternative query forms, such as a simple string, a Date, a number,
20068 // or a mix of such. Ultimately, The dojo/data/api/Read API is agnostic about what the query
20069 // format.
20070 //
20071 // Further note: In general for query objects that accept strings as attribute
20072 // value matches, the store should also support basic filtering capability, such as *
20073 // (match any character) and ? (match single character). An example query that is a query object
20074 // would be like: { attrFoo: "value*"}. Which generally means match all items where they have
20075 // an attribute named attrFoo, with a value that starts with 'value'.
20076 //
20077 // ####The *queryOptions* parameter
20078 //
20079 // The queryOptions parameter is an optional parameter used to specify options that may modify
20080 // the query in some fashion, such as doing a case insensitive search, or doing a deep search
20081 // where all items in a hierarchical representation of data are scanned instead of just the root
20082 // items. It currently defines two options that all datastores should attempt to honor if possible:
20083 // | {
20084 // | ignoreCase: boolean, // Whether or not the query should match case sensitively or not. Default behaviour is false.
20085 // | deep: boolean // Whether or not a fetch should do a deep search of items and all child
20086 // | // items instead of just root-level items in a datastore. Default is false.
20087 // | }
20088 //
20089 // ####The *onBegin* parameter.
20090 //
20091 // function(size, request);
20092 // If an onBegin callback function is provided, the callback function
20093 // will be called just once, before the first onItem callback is called.
20094 // The onBegin callback function will be passed two arguments, the
20095 // the total number of items identified and the Request object. If the total number is
20096 // unknown, then size will be -1. Note that size is not necessarily the size of the
20097 // collection of items returned from the query, as the request may have specified to return only a
20098 // subset of the total set of items through the use of the start and count parameters.
20099 //
20100 // ####The *onItem* parameter.
20101 //
20102 // function(item, request);
20103 //
20104 // If an onItem callback function is provided, the callback function
20105 // will be called as each item in the result is received. The callback
20106 // function will be passed two arguments: the item itself, and the
20107 // Request object.
20108 //
20109 // ####The *onComplete* parameter.
20110 //
20111 // function(items, request);
20112 //
20113 // If an onComplete callback function is provided, the callback function
20114 // will be called just once, after the last onItem callback is called.
20115 // Note that if the onItem callback is not present, then onComplete will be passed
20116 // an array containing all items which matched the query and the request object.
20117 // If the onItem callback is present, then onComplete is called as:
20118 // onComplete(null, request).
20119 //
20120 // ####The *onError* parameter.
20121 //
20122 // function(errorData, request);
20123 //
20124 // If an onError callback function is provided, the callback function
20125 // will be called if there is any sort of error while attempting to
20126 // execute the query.
20127 // The onError callback function will be passed two arguments:
20128 // an Error object and the Request object.
20129 //
20130 // ####The *scope* parameter.
20131 //
20132 // If a scope object is provided, all of the callback functions (onItem,
20133 // onComplete, onError, etc) will be invoked in the context of the scope
20134 // object. In the body of the callback function, the value of the "this"
20135 // keyword will be the scope object. If no scope object is provided,
20136 // the callback functions will be called in the context of dojo.global().
20137 // For example, onItem.call(scope, item, request) vs.
20138 // onItem.call(dojo.global(), item, request)
20139 //
20140 // ####The *start* parameter.
20141 //
20142 // If a start parameter is specified, this is a indication to the datastore to
20143 // only start returning items once the start number of items have been located and
20144 // skipped. When this parameter is paired with 'count', the store should be able
20145 // to page across queries with millions of hits by only returning subsets of the
20146 // hits for each query
20147 //
20148 // ####The *count* parameter.
20149 //
20150 // If a count parameter is specified, this is a indication to the datastore to
20151 // only return up to that many items. This allows a fetch call that may have
20152 // millions of item matches to be paired down to something reasonable.
20153 //
20154 // ####The *sort* parameter.
20155 //
20156 // If a sort parameter is specified, this is a indication to the datastore to
20157 // sort the items in some manner before returning the items. The array is an array of
20158 // javascript objects that must conform to the following format to be applied to the
20159 // fetching of items:
20160 // | {
20161 // | attribute: attribute || attribute-name-string,
20162 // | descending: true|false; // Optional. Default is false.
20163 // | }
20164 // Note that when comparing attributes, if an item contains no value for the attribute
20165 // (undefined), then it the default ascending sort logic should push it to the bottom
20166 // of the list. In the descending order case, it such items should appear at the top of the list.
20167
20168 request = request || {};
20169 if(!request.store){
20170 request.store = this;
20171 }
20172
20173 this._fetchItems(request, lang.hitch(this, "fetchHandler"), lang.hitch(this, "errorHandler"));
20174 return request; // Object
20175 };
20176
20177 return simpleFetch;
20178 });
20179
20180 },
20181 'dijit/Menu':function(){
20182 define("dijit/Menu", [
20183 "require",
20184 "dojo/_base/array", // array.forEach
20185 "dojo/_base/declare", // declare
20186 "dojo/_base/event", // event.stop
20187 "dojo/dom", // dom.byId dom.isDescendant
20188 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
20189 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
20190 "dojo/dom-style", // domStyle.getComputedStyle
20191 "dojo/keys", // keys.F10
20192 "dojo/_base/lang", // lang.hitch
20193 "dojo/on",
20194 "dojo/sniff", // has("ie"), has("quirks")
20195 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames
20196 "dojo/window", // winUtils.get
20197 "./popup",
20198 "./DropDownMenu",
20199 "dojo/ready"
20200 ], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, keys, lang, on,
20201 has, win, winUtils, pm, DropDownMenu, ready){
20202
20203 // module:
20204 // dijit/Menu
20205
20206 // Back compat w/1.6, remove for 2.0
20207 if(has("dijit-legacy-requires")){
20208 ready(0, function(){
20209 var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
20210 require(requires); // use indirection so modules not rolled into a build
20211 });
20212 }
20213
20214 return declare("dijit.Menu", DropDownMenu, {
20215 // summary:
20216 // A context menu you can assign to multiple elements
20217
20218 constructor: function(/*===== params, srcNodeRef =====*/){
20219 // summary:
20220 // Create the widget.
20221 // params: Object|null
20222 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
20223 // and functions, typically callbacks like onClick.
20224 // The hash can contain any of the widget's properties, excluding read-only properties.
20225 // srcNodeRef: DOMNode|String?
20226 // If a srcNodeRef (DOM node) is specified:
20227 //
20228 // - use srcNodeRef.innerHTML as my contents
20229 // - replace srcNodeRef with my generated DOM tree
20230
20231 this._bindings = [];
20232 },
20233
20234 // targetNodeIds: [const] String[]
20235 // Array of dom node ids of nodes to attach to.
20236 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
20237 targetNodeIds: [],
20238
20239 // selector: String?
20240 // CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
20241 // the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
20242 // a range of rows in a table, tree, etc.
20243 //
20244 // The application must require() an appropriate level of dojo/query to handle the selector.
20245 selector: "",
20246
20247 // TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
20248 // So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
20249
20250 /*=====
20251 // currentTarget: [readonly] DOMNode
20252 // For context menus, set to the current node that the Menu is being displayed for.
20253 // Useful so that the menu actions can be tailored according to the node
20254 currentTarget: null,
20255 =====*/
20256
20257 // contextMenuForWindow: [const] Boolean
20258 // If true, right clicking anywhere on the window will cause this context menu to open.
20259 // If false, must specify targetNodeIds.
20260 contextMenuForWindow: false,
20261
20262 // leftClickToOpen: [const] Boolean
20263 // If true, menu will open on left click instead of right click, similar to a file menu.
20264 leftClickToOpen: false,
20265
20266 // refocus: Boolean
20267 // When this menu closes, re-focus the element which had focus before it was opened.
20268 refocus: true,
20269
20270 postCreate: function(){
20271 if(this.contextMenuForWindow){
20272 this.bindDomNode(this.ownerDocumentBody);
20273 }else{
20274 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
20275 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
20276 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
20277 array.forEach(this.targetNodeIds, this.bindDomNode, this);
20278 }
20279 this.inherited(arguments);
20280 },
20281
20282 // thanks burstlib!
20283 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
20284 // summary:
20285 // Returns the window reference of the passed iframe
20286 // tags:
20287 // private
20288 return winUtils.get(this._iframeContentDocument(iframe_el)) ||
20289 // Moz. TODO: is this available when defaultView isn't?
20290 this._iframeContentDocument(iframe_el)['__parent__'] ||
20291 (iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window
20292 },
20293
20294 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
20295 // summary:
20296 // Returns a reference to the document object inside iframe_el
20297 // tags:
20298 // protected
20299 return iframe_el.contentDocument // W3
20300 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
20301 || (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document)
20302 || null; // HTMLDocument
20303 },
20304
20305 bindDomNode: function(/*String|DomNode*/ node){
20306 // summary:
20307 // Attach menu to given node
20308 node = dom.byId(node, this.ownerDocument);
20309
20310 var cn; // Connect node
20311
20312 // Support context menus on iframes. Rather than binding to the iframe itself we need
20313 // to bind to the <body> node inside the iframe.
20314 if(node.tagName.toLowerCase() == "iframe"){
20315 var iframe = node,
20316 window = this._iframeContentWindow(iframe);
20317 cn = win.body(window.document);
20318 }else{
20319 // To capture these events at the top level, attach to <html>, not <body>.
20320 // Otherwise right-click context menu just doesn't work.
20321 cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node);
20322 }
20323
20324
20325 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
20326 var binding = {
20327 node: node,
20328 iframe: iframe
20329 };
20330
20331 // Save info about binding in _bindings[], and make node itself record index(+1) into
20332 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
20333 // start with a number, which fails on FF/safari.
20334 domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
20335
20336 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
20337 // loading yet, in which case we need to wait for the onload event first, and then connect
20338 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
20339 // we need to monitor keyboard events in addition to the oncontextmenu event.
20340 var doConnects = lang.hitch(this, function(cn){
20341 var selector = this.selector,
20342 delegatedEvent = selector ?
20343 function(eventType){ return on.selector(selector, eventType); } :
20344 function(eventType){ return eventType; },
20345 self = this;
20346 return [
20347 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
20348 // rather than shift-F10?
20349 on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){
20350 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
20351 event.stop(evt);
20352 self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY});
20353 }),
20354 on(cn, delegatedEvent("keydown"), function(evt){
20355 if(evt.shiftKey && evt.keyCode == keys.F10){
20356 event.stop(evt);
20357 self._scheduleOpen(this, iframe); // no coords - open near target node
20358 }
20359 })
20360 ];
20361 });
20362 binding.connects = cn ? doConnects(cn) : [];
20363
20364 if(iframe){
20365 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
20366 // and every time the contents change.
20367 // Need to do this b/c we are actually binding to the iframe's <body> node.
20368 // Note: can't use connect.connect(), see #9609.
20369
20370 binding.onloadHandler = lang.hitch(this, function(){
20371 // want to remove old connections, but IE throws exceptions when trying to
20372 // access the <body> node because it's already gone, or at least in a state of limbo
20373
20374 var window = this._iframeContentWindow(iframe);
20375 cn = win.body(window.document)
20376 binding.connects = doConnects(cn);
20377 });
20378 if(iframe.addEventListener){
20379 iframe.addEventListener("load", binding.onloadHandler, false);
20380 }else{
20381 iframe.attachEvent("onload", binding.onloadHandler);
20382 }
20383 }
20384 },
20385
20386 unBindDomNode: function(/*String|DomNode*/ nodeName){
20387 // summary:
20388 // Detach menu from given node
20389
20390 var node;
20391 try{
20392 node = dom.byId(nodeName, this.ownerDocument);
20393 }catch(e){
20394 // On IE the dom.byId() call will get an exception if the attach point was
20395 // the <body> node of an <iframe> that has since been reloaded (and thus the
20396 // <body> node is in a limbo state of destruction.
20397 return;
20398 }
20399
20400 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
20401 var attrName = "_dijitMenu" + this.id;
20402 if(node && domAttr.has(node, attrName)){
20403 var bid = domAttr.get(node, attrName)-1, b = this._bindings[bid], h;
20404 while((h = b.connects.pop())){
20405 h.remove();
20406 }
20407
20408 // Remove listener for iframe onload events
20409 var iframe = b.iframe;
20410 if(iframe){
20411 if(iframe.removeEventListener){
20412 iframe.removeEventListener("load", b.onloadHandler, false);
20413 }else{
20414 iframe.detachEvent("onload", b.onloadHandler);
20415 }
20416 }
20417
20418 domAttr.remove(node, attrName);
20419 delete this._bindings[bid];
20420 }
20421 },
20422
20423 _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
20424 // summary:
20425 // Set timer to display myself. Using a timer rather than displaying immediately solves
20426 // two problems:
20427 //
20428 // 1. IE: without the delay, focus work in "open" causes the system
20429 // context menu to appear in spite of stopEvent.
20430 //
20431 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
20432 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
20433 // oncontextmenu event.)
20434
20435 if(!this._openTimer){
20436 this._openTimer = this.defer(function(){
20437 delete this._openTimer;
20438 this._openMyself({
20439 target: target,
20440 iframe: iframe,
20441 coords: coords
20442 });
20443 }, 1);
20444 }
20445 },
20446
20447 _openMyself: function(args){
20448 // summary:
20449 // Internal function for opening myself when the user does a right-click or something similar.
20450 // args:
20451 // This is an Object containing:
20452 //
20453 // - target: The node that is being clicked
20454 // - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
20455 // - coords: Put menu at specified x/y position in viewport, or if iframe is
20456 // specified, then relative to iframe.
20457 //
20458 // _openMyself() formerly took the event object, and since various code references
20459 // evt.target (after connecting to _openMyself()), using an Object for parameters
20460 // (so that old code still works).
20461
20462 var target = args.target,
20463 iframe = args.iframe,
20464 coords = args.coords;
20465
20466 // To be used by MenuItem event handlers to tell which node the menu was opened on
20467 this.currentTarget = target;
20468
20469 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
20470 // then near the node the menu is assigned to.
20471 if(coords){
20472 if(iframe){
20473 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
20474 var ifc = domGeometry.position(iframe, true),
20475 window = this._iframeContentWindow(iframe),
20476 scroll = domGeometry.docScroll(window.document);
20477
20478 var cs = domStyle.getComputedStyle(iframe),
20479 tp = domStyle.toPixelValue,
20480 left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
20481 top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
20482
20483 coords.x += ifc.x + left - scroll.x;
20484 coords.y += ifc.y + top - scroll.y;
20485 }
20486 }else{
20487 coords = domGeometry.position(target, true);
20488 coords.x += 10;
20489 coords.y += 10;
20490 }
20491
20492 var self=this;
20493 var prevFocusNode = this._focusManager.get("prevNode");
20494 var curFocusNode = this._focusManager.get("curNode");
20495 var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
20496
20497 function closeAndRestoreFocus(){
20498 // user has clicked on a menu or popup
20499 if(self.refocus && savedFocusNode){
20500 savedFocusNode.focus();
20501 }
20502 pm.close(self);
20503 }
20504 pm.open({
20505 popup: this,
20506 x: coords.x,
20507 y: coords.y,
20508 onExecute: closeAndRestoreFocus,
20509 onCancel: closeAndRestoreFocus,
20510 orient: this.isLeftToRight() ? 'L' : 'R'
20511 });
20512 this.focus();
20513
20514 this._onBlur = function(){
20515 this.inherited('_onBlur', arguments);
20516 // Usually the parent closes the child widget but if this is a context
20517 // menu then there is no parent
20518 pm.close(this);
20519 // don't try to restore focus; user has clicked another part of the screen
20520 // and set focus there
20521 };
20522 },
20523
20524 destroy: function(){
20525 array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
20526 this.inherited(arguments);
20527 }
20528 });
20529
20530 });
20531
20532 },
20533 'dijit/form/_CheckBoxMixin':function(){
20534 define("dijit/form/_CheckBoxMixin", [
20535 "dojo/_base/declare", // declare
20536 "dojo/dom-attr", // domAttr.set
20537 "dojo/_base/event" // event.stop
20538 ], function(declare, domAttr, event){
20539
20540 // module:
20541 // dijit/form/_CheckBoxMixin
20542
20543 return declare("dijit.form._CheckBoxMixin", null, {
20544 // summary:
20545 // Mixin to provide widget functionality corresponding to an HTML checkbox
20546 //
20547 // description:
20548 // User interacts with real html inputs.
20549 // On onclick (which occurs by mouse click, space-bar, or
20550 // using the arrow keys to switch the selected radio button),
20551 // we update the state of the checkbox/radio.
20552 //
20553
20554 // type: [private] String
20555 // type attribute on `<input>` node.
20556 // Overrides `dijit/form/Button.type`. Users should not change this value.
20557 type: "checkbox",
20558
20559 // value: String
20560 // As an initialization parameter, equivalent to value field on normal checkbox
20561 // (if checked, the value is passed as the value when form is submitted).
20562 value: "on",
20563
20564 // readOnly: Boolean
20565 // Should this widget respond to user input?
20566 // In markup, this is specified as "readOnly".
20567 // Similar to disabled except readOnly form values are submitted.
20568 readOnly: false,
20569
20570 // aria-pressed for toggle buttons, and aria-checked for checkboxes
20571 _aria_attr: "aria-checked",
20572
20573 _setReadOnlyAttr: function(/*Boolean*/ value){
20574 this._set("readOnly", value);
20575 domAttr.set(this.focusNode, 'readOnly', value);
20576 },
20577
20578 // Override dijit/form/Button._setLabelAttr() since we don't even have a containerNode.
20579 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox/layout/TabContainer
20580 _setLabelAttr: undefined,
20581
20582 _getSubmitValue: function(/*String*/ value){
20583 return !value && value !== 0 ? "on" : value;
20584 },
20585
20586 _setValueAttr: function(newValue){
20587 newValue = this._getSubmitValue(newValue); // "on" to match browser native behavior when value unspecified
20588 this._set("value", newValue);
20589 domAttr.set(this.focusNode, "value", newValue);
20590 },
20591
20592 reset: function(){
20593 this.inherited(arguments);
20594 // Handle unlikely event that the <input type=checkbox> value attribute has changed
20595 this._set("value", this.params.value || "on");
20596 domAttr.set(this.focusNode, 'value', this.value);
20597 },
20598
20599 _onClick: function(/*Event*/ e){
20600 // summary:
20601 // Internal function to handle click actions - need to check
20602 // readOnly, since button no longer does that check.
20603 if(this.readOnly){
20604 event.stop(e);
20605 return false;
20606 }
20607 return this.inherited(arguments);
20608 }
20609 });
20610 });
20611
20612 },
20613 'dijit/layout/ContentPane':function(){
20614 define("dijit/layout/ContentPane", [
20615 "dojo/_base/kernel", // kernel.deprecated
20616 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
20617 "../_Widget",
20618 "../_Container",
20619 "./_ContentPaneResizeMixin",
20620 "dojo/string", // string.substitute
20621 "dojo/html", // html._ContentSetter
20622 "dojo/i18n!../nls/loading",
20623 "dojo/_base/array", // array.forEach
20624 "dojo/_base/declare", // declare
20625 "dojo/_base/Deferred", // Deferred
20626 "dojo/dom", // dom.byId
20627 "dojo/dom-attr", // domAttr.attr
20628 "dojo/dom-construct", // empty()
20629 "dojo/_base/xhr", // xhr.get
20630 "dojo/i18n", // i18n.getLocalization
20631 "dojo/when"
20632 ], function(kernel, lang, _Widget, _Container, _ContentPaneResizeMixin, string, html, nlsLoading,
20633 array, declare, Deferred, dom, domAttr, domConstruct, xhr, i18n, when){
20634
20635 // module:
20636 // dijit/layout/ContentPane
20637
20638
20639 return declare("dijit.layout.ContentPane", [_Widget, _Container, _ContentPaneResizeMixin], {
20640 // summary:
20641 // A widget containing an HTML fragment, specified inline
20642 // or by uri. Fragment may include widgets.
20643 //
20644 // description:
20645 // This widget embeds a document fragment in the page, specified
20646 // either by uri, javascript generated markup or DOM reference.
20647 // Any widgets within this content are instantiated and managed,
20648 // but laid out according to the HTML structure. Unlike IFRAME,
20649 // ContentPane embeds a document fragment as would be found
20650 // inside the BODY tag of a full HTML document. It should not
20651 // contain the HTML, HEAD, or BODY tags.
20652 // For more advanced functionality with scripts and
20653 // stylesheets, see dojox/layout/ContentPane. This widget may be
20654 // used stand alone or as a base class for other widgets.
20655 // ContentPane is useful as a child of other layout containers
20656 // such as BorderContainer or TabContainer, but note that those
20657 // widgets can contain any widget as a child.
20658 //
20659 // example:
20660 // Some quick samples:
20661 // To change the innerHTML:
20662 // | cp.set('content', '<b>new content</b>')`
20663 // Or you can send it a NodeList:
20664 // | cp.set('content', dojo.query('div [class=selected]', userSelection))
20665 // To do an ajax update:
20666 // | cp.set('href', url)
20667
20668 // href: String
20669 // The href of the content that displays now.
20670 // Set this at construction if you want to load data externally when the
20671 // pane is shown. (Set preload=true to load it immediately.)
20672 // Changing href after creation doesn't have any effect; Use set('href', ...);
20673 href: "",
20674
20675 // content: String|DomNode|NodeList|dijit/_Widget
20676 // The innerHTML of the ContentPane.
20677 // Note that the initialization parameter / argument to set("content", ...)
20678 // can be a String, DomNode, Nodelist, or _Widget.
20679 content: "",
20680
20681 // extractContent: Boolean
20682 // Extract visible content from inside of `<body> .... </body>`.
20683 // I.e., strip `<html>` and `<head>` (and it's contents) from the href
20684 extractContent: false,
20685
20686 // parseOnLoad: Boolean
20687 // Parse content and create the widgets, if any.
20688 parseOnLoad: true,
20689
20690 // parserScope: String
20691 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
20692 // will search for data-dojo-type (or dojoType). For backwards compatibility
20693 // reasons defaults to dojo._scopeName (which is "dojo" except when
20694 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
20695 parserScope: kernel._scopeName,
20696
20697 // preventCache: Boolean
20698 // Prevent caching of data from href's by appending a timestamp to the href.
20699 preventCache: false,
20700
20701 // preload: Boolean
20702 // Force load of data on initialization even if pane is hidden.
20703 preload: false,
20704
20705 // refreshOnShow: Boolean
20706 // Refresh (re-download) content when pane goes from hidden to shown
20707 refreshOnShow: false,
20708
20709 // loadingMessage: String
20710 // Message that shows while downloading
20711 loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
20712
20713 // errorMessage: String
20714 // Message that shows if an error occurs
20715 errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
20716
20717 // isLoaded: [readonly] Boolean
20718 // True if the ContentPane has data in it, either specified
20719 // during initialization (via href or inline content), or set
20720 // via set('content', ...) / set('href', ...)
20721 //
20722 // False if it doesn't have any content, or if ContentPane is
20723 // still in the process of downloading href.
20724 isLoaded: false,
20725
20726 baseClass: "dijitContentPane",
20727
20728 /*======
20729 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
20730 // Function that should grab the content specified via href.
20731 ioMethod: dojo.xhrGet,
20732 ======*/
20733
20734 // ioArgs: Object
20735 // Parameters to pass to xhrGet() request, for example:
20736 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
20737 ioArgs: {},
20738
20739 // onLoadDeferred: [readonly] dojo.Deferred
20740 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
20741 // Calling onLoadDeferred.then() registers your
20742 // callback to be called only once, when the prior set('href', ...) call or
20743 // the initial href parameter to the constructor finishes loading.
20744 //
20745 // This is different than an onLoad() handler which gets called any time any href
20746 // or content is loaded.
20747 onLoadDeferred: null,
20748
20749 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
20750 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
20751 // entire pane.
20752 _setTitleAttr: null,
20753
20754 // Flag to parser that I'll parse my contents, so it shouldn't.
20755 stopParser: true,
20756
20757 // template: [private] Boolean
20758 // Flag from the parser that this ContentPane is inside a template
20759 // so the contents are pre-parsed.
20760 // TODO: this declaration can be commented out in 2.0
20761 template: false,
20762
20763 create: function(params, srcNodeRef){
20764 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
20765 // processed in the same way as contents set via set("content", ...), calling the parser etc.
20766 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
20767 if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
20768 srcNodeRef = dom.byId(srcNodeRef);
20769 var df = srcNodeRef.ownerDocument.createDocumentFragment();
20770 while(srcNodeRef.firstChild){
20771 df.appendChild(srcNodeRef.firstChild);
20772 }
20773 params = lang.delegate(params, {content: df});
20774 }
20775 this.inherited(arguments, [params, srcNodeRef]);
20776 },
20777
20778 postMixInProperties: function(){
20779 this.inherited(arguments);
20780 var messages = i18n.getLocalization("dijit", "loading", this.lang);
20781 this.loadingMessage = string.substitute(this.loadingMessage, messages);
20782 this.errorMessage = string.substitute(this.errorMessage, messages);
20783 },
20784
20785 buildRendering: function(){
20786 this.inherited(arguments);
20787
20788 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
20789 // For subclasses of ContentPane that do have a template, does nothing.
20790 if(!this.containerNode){
20791 this.containerNode = this.domNode;
20792 }
20793
20794 // remove the title attribute so it doesn't show up when hovering
20795 // over a node (TODO: remove in 2.0, no longer needed after #11490)
20796 this.domNode.title = "";
20797
20798 if(!domAttr.get(this.domNode,"role")){
20799 this.domNode.setAttribute("role", "group");
20800 }
20801 },
20802
20803 startup: function(){
20804 // summary:
20805 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
20806
20807 // This starts all the widgets
20808 this.inherited(arguments);
20809
20810 // And this catches stuff like dojo/dnd/Source
20811 if(this._contentSetter){
20812 array.forEach(this._contentSetter.parseResults, function(obj){
20813 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
20814 obj.startup();
20815 obj._started = true;
20816 }
20817 }, this);
20818 }
20819 },
20820
20821 _startChildren: function(){
20822 // summary:
20823 // Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
20824 // itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
20825
20826 // This starts all the widgets
20827 array.forEach(this.getChildren(), function(obj){
20828 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
20829 obj.startup();
20830 obj._started = true;
20831 }
20832 });
20833
20834 // And this catches stuff like dojo/dnd/Source
20835 if(this._contentSetter){
20836 array.forEach(this._contentSetter.parseResults, function(obj){
20837 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
20838 obj.startup();
20839 obj._started = true;
20840 }
20841 }, this);
20842 }
20843 },
20844
20845 setHref: function(/*String|Uri*/ href){
20846 // summary:
20847 // Deprecated. Use set('href', ...) instead.
20848 kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
20849 return this.set("href", href);
20850 },
20851 _setHrefAttr: function(/*String|Uri*/ href){
20852 // summary:
20853 // Hook so set("href", ...) works.
20854 // description:
20855 // Reset the (external defined) content of this pane and replace with new url
20856 // Note: It delays the download until widget is shown if preload is false.
20857 // href:
20858 // url to the page you want to get, must be within the same domain as your mainpage
20859
20860 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
20861 this.cancel();
20862
20863 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
20864 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
20865
20866 this._set("href", href);
20867
20868 // _setHrefAttr() is called during creation and by the user, after creation.
20869 // Assuming preload == false, only in the second case do we actually load the URL;
20870 // otherwise it's done in startup(), and only if this widget is shown.
20871 if(this.preload || (this._created && this._isShown())){
20872 this._load();
20873 }else{
20874 // Set flag to indicate that href needs to be loaded the next time the
20875 // ContentPane is made visible
20876 this._hrefChanged = true;
20877 }
20878
20879 return this.onLoadDeferred; // Deferred
20880 },
20881
20882 setContent: function(/*String|DomNode|Nodelist*/data){
20883 // summary:
20884 // Deprecated. Use set('content', ...) instead.
20885 kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
20886 this.set("content", data);
20887 },
20888 _setContentAttr: function(/*String|DomNode|Nodelist*/data){
20889 // summary:
20890 // Hook to make set("content", ...) work.
20891 // Replaces old content with data content, include style classes from old content
20892 // data:
20893 // the new Content may be String, DomNode or NodeList
20894 //
20895 // if data is a NodeList (or an array of nodes) nodes are copied
20896 // so you can import nodes from another document implicitly
20897
20898 // clear href so we can't run refresh and clear content
20899 // refresh should only work if we downloaded the content
20900 this._set("href", "");
20901
20902 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
20903 this.cancel();
20904
20905 // Even though user is just setting content directly, still need to define an onLoadDeferred
20906 // because the _onLoadHandler() handler is still getting called from setContent()
20907 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
20908 if(this._created){
20909 // For back-compat reasons, call onLoad() for set('content', ...)
20910 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
20911 // or as initialization parameter (ie: new ContentPane({content: ...})
20912 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
20913 }
20914
20915 this._setContent(data || "");
20916
20917 this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
20918
20919 return this.onLoadDeferred; // Deferred
20920 },
20921 _getContentAttr: function(){
20922 // summary:
20923 // Hook to make get("content") work
20924 return this.containerNode.innerHTML;
20925 },
20926
20927 cancel: function(){
20928 // summary:
20929 // Cancels an in-flight download of content
20930 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
20931 this._xhrDfd.cancel();
20932 }
20933 delete this._xhrDfd; // garbage collect
20934
20935 this.onLoadDeferred = null;
20936 },
20937
20938 destroy: function(){
20939 this.cancel();
20940 this.inherited(arguments);
20941 },
20942
20943 destroyRecursive: function(/*Boolean*/ preserveDom){
20944 // summary:
20945 // Destroy the ContentPane and its contents
20946
20947 // if we have multiple controllers destroying us, bail after the first
20948 if(this._beingDestroyed){
20949 return;
20950 }
20951 this.inherited(arguments);
20952 },
20953
20954 _onShow: function(){
20955 // summary:
20956 // Called when the ContentPane is made visible
20957 // description:
20958 // For a plain ContentPane, this is called on initialization, from startup().
20959 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
20960 // called whenever the pane is made visible.
20961 //
20962 // Does necessary processing, including href download and layout/resize of
20963 // child widget(s)
20964
20965 this.inherited(arguments);
20966
20967 if(this.href){
20968 if(!this._xhrDfd && // if there's an href that isn't already being loaded
20969 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
20970 ){
20971 return this.refresh(); // If child has an href, promise that fires when the load is complete
20972 }
20973 }
20974 },
20975
20976 refresh: function(){
20977 // summary:
20978 // [Re]download contents of href and display
20979 // description:
20980 // 1. cancels any currently in-flight requests
20981 // 2. posts "loading..." message
20982 // 3. sends XHR to download new data
20983
20984 // Cancel possible prior in-flight request
20985 this.cancel();
20986
20987 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
20988 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
20989 this._load();
20990 return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
20991 },
20992
20993 _load: function(){
20994 // summary:
20995 // Load/reload the href specified in this.href
20996
20997 // display loading message
20998 this._setContent(this.onDownloadStart(), true);
20999
21000 var self = this;
21001 var getArgs = {
21002 preventCache: (this.preventCache || this.refreshOnShow),
21003 url: this.href,
21004 handleAs: "text"
21005 };
21006 if(lang.isObject(this.ioArgs)){
21007 lang.mixin(getArgs, this.ioArgs);
21008 }
21009
21010 var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)),
21011 returnedHtml;
21012
21013 hand.then(
21014 function(html){
21015 returnedHtml = html;
21016 try{
21017 self._isDownloaded = true;
21018 return self._setContent(html, false);
21019 }catch(err){
21020 self._onError('Content', err); // onContentError
21021 }
21022 },
21023 function(err){
21024 if(!hand.canceled){
21025 // show error message in the pane
21026 self._onError('Download', err); // onDownloadError
21027 }
21028 delete self._xhrDfd;
21029 return err;
21030 }
21031 ).then(function(){
21032 self.onDownloadEnd();
21033 delete self._xhrDfd;
21034 return returnedHtml;
21035 });
21036
21037 // Remove flag saying that a load is needed
21038 delete this._hrefChanged;
21039 },
21040
21041 _onLoadHandler: function(data){
21042 // summary:
21043 // This is called whenever new content is being loaded
21044 this._set("isLoaded", true);
21045 try{
21046 this.onLoadDeferred.resolve(data);
21047 }catch(e){
21048 console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
21049 }
21050 },
21051
21052 _onUnloadHandler: function(){
21053 // summary:
21054 // This is called whenever the content is being unloaded
21055 this._set("isLoaded", false);
21056 try{
21057 this.onUnload();
21058 }catch(e){
21059 console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
21060 }
21061 },
21062
21063 destroyDescendants: function(/*Boolean*/ preserveDom){
21064 // summary:
21065 // Destroy all the widgets inside the ContentPane and empty containerNode
21066
21067 // Make sure we call onUnload (but only when the ContentPane has real content)
21068 if(this.isLoaded){
21069 this._onUnloadHandler();
21070 }
21071
21072 // Even if this.isLoaded == false there might still be a "Loading..." message
21073 // to erase, so continue...
21074
21075 // For historical reasons we need to delete all widgets under this.containerNode,
21076 // even ones that the user has created manually.
21077 var setter = this._contentSetter;
21078 array.forEach(this.getChildren(), function(widget){
21079 if(widget.destroyRecursive){
21080 // All widgets will hit this branch
21081 widget.destroyRecursive(preserveDom);
21082 }else if(widget.destroy){
21083 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21084 widget.destroy(preserveDom);
21085 }
21086 widget._destroyed = true;
21087 });
21088 if(setter){
21089 // Most of the widgets in setter.parseResults have already been destroyed, but
21090 // things like Menu that have been moved to <body> haven't yet
21091 array.forEach(setter.parseResults, function(widget){
21092 if(!widget._destroyed){
21093 if(widget.destroyRecursive){
21094 // All widgets will hit this branch
21095 widget.destroyRecursive(preserveDom);
21096 }else if(widget.destroy){
21097 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21098 widget.destroy(preserveDom);
21099 }
21100 widget._destroyed = true;
21101 }
21102 });
21103 delete setter.parseResults;
21104 }
21105
21106 // And then clear away all the DOM nodes
21107 if(!preserveDom){
21108 domConstruct.empty(this.containerNode);
21109 }
21110
21111 // Delete any state information we have about current contents
21112 delete this._singleChild;
21113 },
21114
21115 _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
21116 // summary:
21117 // Insert the content into the container node
21118 // returns:
21119 // Returns a Deferred promise that is resolved when the content is parsed.
21120
21121 // first get rid of child widgets
21122 this.destroyDescendants();
21123
21124 // html.set will take care of the rest of the details
21125 // we provide an override for the error handling to ensure the widget gets the errors
21126 // configure the setter instance with only the relevant widget instance properties
21127 // NOTE: unless we hook into attr, or provide property setters for each property,
21128 // we need to re-configure the ContentSetter with each use
21129 var setter = this._contentSetter;
21130 if(! (setter && setter instanceof html._ContentSetter)){
21131 setter = this._contentSetter = new html._ContentSetter({
21132 node: this.containerNode,
21133 _onError: lang.hitch(this, this._onError),
21134 onContentError: lang.hitch(this, function(e){
21135 // fires if a domfault occurs when we are appending this.errorMessage
21136 // like for instance if domNode is a UL and we try append a DIV
21137 var errMess = this.onContentError(e);
21138 try{
21139 this.containerNode.innerHTML = errMess;
21140 }catch(e){
21141 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
21142 }
21143 })/*,
21144 _onError */
21145 });
21146 }
21147
21148 var setterParams = lang.mixin({
21149 cleanContent: this.cleanContent,
21150 extractContent: this.extractContent,
21151 parseContent: !cont.domNode && this.parseOnLoad,
21152 parserScope: this.parserScope,
21153 startup: false,
21154 dir: this.dir,
21155 lang: this.lang,
21156 textDir: this.textDir
21157 }, this._contentSetterParams || {});
21158
21159 var p = setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
21160
21161 // dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
21162 // dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
21163 // So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
21164 var self = this;
21165 return when(p && p.then ? p : setter.parseDeferred, function(){
21166 // setter params must be pulled afresh from the ContentPane each time
21167 delete self._contentSetterParams;
21168
21169 if(!isFakeContent){
21170 if(self._started){
21171 // Startup each top level child widget (and they will start their children, recursively)
21172 self._startChildren();
21173
21174 // Call resize() on each of my child layout widgets,
21175 // or resize() on my single child layout widget...
21176 // either now (if I'm currently visible) or when I become visible
21177 self._scheduleLayout();
21178 }
21179 self._onLoadHandler(cont);
21180 }
21181 });
21182 },
21183
21184 _onError: function(type, err, consoleText){
21185 this.onLoadDeferred.reject(err);
21186
21187 // shows user the string that is returned by on[type]Error
21188 // override on[type]Error and return your own string to customize
21189 var errText = this['on' + type + 'Error'].call(this, err);
21190 if(consoleText){
21191 console.error(consoleText, err);
21192 }else if(errText){// a empty string won't change current content
21193 this._setContent(errText, true);
21194 }
21195 },
21196
21197 // EVENT's, should be overide-able
21198 onLoad: function(/*===== data =====*/){
21199 // summary:
21200 // Event hook, is called after everything is loaded and widgetified
21201 // tags:
21202 // callback
21203 },
21204
21205 onUnload: function(){
21206 // summary:
21207 // Event hook, is called before old content is cleared
21208 // tags:
21209 // callback
21210 },
21211
21212 onDownloadStart: function(){
21213 // summary:
21214 // Called before download starts.
21215 // description:
21216 // The string returned by this function will be the html
21217 // that tells the user we are loading something.
21218 // Override with your own function if you want to change text.
21219 // tags:
21220 // extension
21221 return this.loadingMessage;
21222 },
21223
21224 onContentError: function(/*Error*/ /*===== error =====*/){
21225 // summary:
21226 // Called on DOM faults, require faults etc. in content.
21227 //
21228 // In order to display an error message in the pane, return
21229 // the error message from this method, as an HTML string.
21230 //
21231 // By default (if this method is not overriden), it returns
21232 // nothing, so the error message is just printed to the console.
21233 // tags:
21234 // extension
21235 },
21236
21237 onDownloadError: function(/*Error*/ /*===== error =====*/){
21238 // summary:
21239 // Called when download error occurs.
21240 //
21241 // In order to display an error message in the pane, return
21242 // the error message from this method, as an HTML string.
21243 //
21244 // Default behavior (if this method is not overriden) is to display
21245 // the error message inside the pane.
21246 // tags:
21247 // extension
21248 return this.errorMessage;
21249 },
21250
21251 onDownloadEnd: function(){
21252 // summary:
21253 // Called when download is finished.
21254 // tags:
21255 // callback
21256 }
21257 });
21258
21259 });
21260
21261 },
21262 '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",
21263 '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",
21264 'dijit/_KeyNavContainer':function(){
21265 define("dijit/_KeyNavContainer", [
21266 "dojo/_base/kernel", // kernel.deprecated
21267 "./_Container",
21268 "./_FocusMixin",
21269 "dojo/_base/array", // array.forEach
21270 "dojo/keys", // keys.END keys.HOME
21271 "dojo/_base/declare", // declare
21272 "dojo/_base/event", // event.stop
21273 "dojo/dom-attr", // domAttr.set
21274 "dojo/_base/lang" // lang.hitch
21275 ], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
21276
21277
21278 // module:
21279 // dijit/_KeyNavContainer
21280
21281 return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
21282 // summary:
21283 // A _Container with keyboard navigation of its children.
21284 // description:
21285 // To use this mixin, call connectKeyNavHandlers() in
21286 // postCreate().
21287 // It provides normalized keyboard and focusing code for Container
21288 // widgets.
21289
21290 /*=====
21291 // focusedChild: [protected] Widget
21292 // The currently focused child widget, or null if there isn't one
21293 focusedChild: null,
21294 =====*/
21295
21296 // tabIndex: String
21297 // Tab index of the container; same as HTML tabIndex attribute.
21298 // Note then when user tabs into the container, focus is immediately
21299 // moved to the first item in the container.
21300 tabIndex: "0",
21301
21302 connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
21303 // summary:
21304 // Call in postCreate() to attach the keyboard handlers
21305 // to the container.
21306 // preKeyCodes: keys[]
21307 // Key codes for navigating to the previous child.
21308 // nextKeyCodes: keys[]
21309 // Key codes for navigating to the next child.
21310 // tags:
21311 // protected
21312
21313 // TODO: call this automatically from my own postCreate()
21314
21315 var keyCodes = (this._keyNavCodes = {});
21316 var prev = lang.hitch(this, "focusPrev");
21317 var next = lang.hitch(this, "focusNext");
21318 array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
21319 array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
21320 keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
21321 keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
21322 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
21323 this.connect(this.domNode, "onfocus", "_onContainerFocus");
21324 },
21325
21326 startupKeyNavChildren: function(){
21327 kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
21328 },
21329
21330 startup: function(){
21331 this.inherited(arguments);
21332 array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
21333 },
21334
21335 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
21336 this.inherited(arguments);
21337 this._startupChild(widget);
21338 },
21339
21340 focus: function(){
21341 // summary:
21342 // Default focus() implementation: focus the first child.
21343 this.focusFirstChild();
21344 },
21345
21346 focusFirstChild: function(){
21347 // summary:
21348 // Focus the first focusable child in the container.
21349 // tags:
21350 // protected
21351 this.focusChild(this._getFirstFocusableChild());
21352 },
21353
21354 focusLastChild: function(){
21355 // summary:
21356 // Focus the last focusable child in the container.
21357 // tags:
21358 // protected
21359 this.focusChild(this._getLastFocusableChild());
21360 },
21361
21362 focusNext: function(){
21363 // summary:
21364 // Focus the next widget
21365 // tags:
21366 // protected
21367 this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
21368 },
21369
21370 focusPrev: function(){
21371 // summary:
21372 // Focus the last focusable node in the previous widget
21373 // (ex: go to the ComboButton icon section rather than button section)
21374 // tags:
21375 // protected
21376 this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
21377 },
21378
21379 focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last){
21380 // summary:
21381 // Focus specified child widget.
21382 // widget:
21383 // Reference to container's child widget
21384 // last:
21385 // If true and if widget has multiple focusable nodes, focus the
21386 // last one instead of the first one
21387 // tags:
21388 // protected
21389
21390 if(!widget){ return; }
21391
21392 if(this.focusedChild && widget !== this.focusedChild){
21393 this._onChildBlur(this.focusedChild); // used by _MenuBase
21394 }
21395 widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
21396 widget.focus(last ? "end" : "start");
21397 this._set("focusedChild", widget);
21398 },
21399
21400 _startupChild: function(/*dijit/_WidgetBase*/ widget){
21401 // summary:
21402 // Setup for each child widget
21403 // description:
21404 // Sets tabIndex=-1 on each child, so that the tab key will
21405 // leave the container rather than visiting each child.
21406 // tags:
21407 // private
21408
21409 widget.set("tabIndex", "-1");
21410
21411 this.connect(widget, "_onFocus", function(){
21412 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
21413 widget.set("tabIndex", this.tabIndex);
21414 });
21415 this.connect(widget, "_onBlur", function(){
21416 widget.set("tabIndex", "-1");
21417 });
21418 },
21419
21420 _onContainerFocus: function(evt){
21421 // summary:
21422 // Handler for when the container gets focus
21423 // description:
21424 // Initially the container itself has a tabIndex, but when it gets
21425 // focus, switch focus to first child...
21426 // tags:
21427 // private
21428
21429 // Note that we can't use _onFocus() because switching focus from the
21430 // _onFocus() handler confuses the focus.js code
21431 // (because it causes _onFocusNode() to be called recursively)
21432 // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
21433
21434 // Ignore spurious focus events:
21435 // 1. focus on a child widget bubbles on FF
21436 // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
21437 if(evt.target !== this.domNode || this.focusedChild){ return; }
21438
21439 this.focusFirstChild();
21440
21441 // and then set the container's tabIndex to -1,
21442 // (don't remove as that breaks Safari 4)
21443 // so that tab or shift-tab will go to the fields after/before
21444 // the container, rather than the container itself
21445 domAttr.set(this.domNode, "tabIndex", "-1");
21446 },
21447
21448 _onBlur: function(evt){
21449 // When focus is moved away the container, and its descendant (popup) widgets,
21450 // then restore the container's tabIndex so that user can tab to it again.
21451 // Note that using _onBlur() so that this doesn't happen when focus is shifted
21452 // to one of my child widgets (typically a popup)
21453 if(this.tabIndex){
21454 domAttr.set(this.domNode, "tabIndex", this.tabIndex);
21455 }
21456 this.focusedChild = null;
21457 this.inherited(arguments);
21458 },
21459
21460 _onContainerKeypress: function(evt){
21461 // summary:
21462 // When a key is pressed, if it's an arrow key etc. then
21463 // it's handled here.
21464 // tags:
21465 // private
21466 if(evt.ctrlKey || evt.altKey){ return; }
21467 var func = this._keyNavCodes[evt.charOrCode];
21468 if(func){
21469 func();
21470 event.stop(evt);
21471 }
21472 },
21473
21474 _onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
21475 // summary:
21476 // Called when focus leaves a child widget to go
21477 // to a sibling widget.
21478 // Used by MenuBase.js (TODO: move code there)
21479 // tags:
21480 // protected
21481 },
21482
21483 _getFirstFocusableChild: function(){
21484 // summary:
21485 // Returns first child that can be focused
21486 return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
21487 },
21488
21489 _getLastFocusableChild: function(){
21490 // summary:
21491 // Returns last child that can be focused
21492 return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
21493 },
21494
21495 _getNextFocusableChild: function(child, dir){
21496 // summary:
21497 // Returns the next or previous focusable child, compared
21498 // to "child"
21499 // child: Widget
21500 // The current widget
21501 // dir: Integer
21502 // - 1 = after
21503 // - -1 = before
21504 if(child){
21505 child = this._getSiblingOfChild(child, dir);
21506 }
21507 var children = this.getChildren();
21508 for(var i=0; i < children.length; i++){
21509 if(!child){
21510 child = children[(dir>0) ? 0 : (children.length-1)];
21511 }
21512 if(child.isFocusable()){
21513 return child; // dijit/_WidgetBase
21514 }
21515 child = this._getSiblingOfChild(child, dir);
21516 }
21517 // no focusable child found
21518 return null; // dijit/_WidgetBase
21519 }
21520 });
21521 });
21522
21523 },
21524 'dijit/layout/utils':function(){
21525 define("dijit/layout/utils", [
21526 "dojo/_base/array", // array.filter array.forEach
21527 "dojo/dom-class", // domClass.add domClass.remove
21528 "dojo/dom-geometry", // domGeometry.marginBox
21529 "dojo/dom-style", // domStyle.getComputedStyle
21530 "dojo/_base/lang", // lang.mixin
21531 "../main" // for exporting symbols to dijit, remove in 2.0
21532 ], function(array, domClass, domGeometry, domStyle, lang, dijit){
21533
21534 // module:
21535 // dijit/layout/utils
21536
21537 var layout = lang.getObject("layout", true, dijit);
21538 /*=====
21539 layout = {
21540 // summary:
21541 // marginBox2contentBox() and layoutChildren()
21542 };
21543 =====*/
21544
21545 layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
21546 // summary:
21547 // Given the margin-box size of a node, return its content box size.
21548 // Functions like domGeometry.contentBox() but is more reliable since it doesn't have
21549 // to wait for the browser to compute sizes.
21550 var cs = domStyle.getComputedStyle(node);
21551 var me = domGeometry.getMarginExtents(node, cs);
21552 var pb = domGeometry.getPadBorderExtents(node, cs);
21553 return {
21554 l: domStyle.toPixelValue(node, cs.paddingLeft),
21555 t: domStyle.toPixelValue(node, cs.paddingTop),
21556 w: mb.w - (me.w + pb.w),
21557 h: mb.h - (me.h + pb.h)
21558 };
21559 };
21560
21561 function capitalize(word){
21562 return word.substring(0,1).toUpperCase() + word.substring(1);
21563 }
21564
21565 function size(widget, dim){
21566 // size the child
21567 var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
21568
21569 // record child's size
21570 if(newSize){
21571 // if the child returned it's new size then use that
21572 lang.mixin(widget, newSize);
21573 }else{
21574 // otherwise, call getMarginBox(), but favor our own numbers when we have them.
21575 // the browser lies sometimes
21576 lang.mixin(widget, domGeometry.getMarginBox(widget.domNode));
21577 lang.mixin(widget, dim);
21578 }
21579 }
21580
21581 layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
21582 /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
21583 // summary:
21584 // Layout a bunch of child dom nodes within a parent dom node
21585 // container:
21586 // parent node
21587 // dim:
21588 // {l, t, w, h} object specifying dimensions of container into which to place children
21589 // children:
21590 // An array of Widgets or at least objects containing:
21591 //
21592 // - domNode: pointer to DOM node to position
21593 // - region or layoutAlign: position to place DOM node
21594 // - resize(): (optional) method to set size of node
21595 // - id: (optional) Id of widgets, referenced from resize object, below.
21596 // changedRegionId:
21597 // If specified, the slider for the region with the specified id has been dragged, and thus
21598 // the region's height or width should be adjusted according to changedRegionSize
21599 // changedRegionSize:
21600 // See changedRegionId.
21601
21602 // copy dim because we are going to modify it
21603 dim = lang.mixin({}, dim);
21604
21605 domClass.add(container, "dijitLayoutContainer");
21606
21607 // Move "client" elements to the end of the array for layout. a11y dictates that the author
21608 // needs to be able to put them in the document in tab-order, but this algorithm requires that
21609 // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
21610 children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
21611 .concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
21612
21613 // set positions/sizes
21614 array.forEach(children, function(child){
21615 var elm = child.domNode,
21616 pos = (child.region || child.layoutAlign);
21617 if(!pos){
21618 throw new Error("No region setting for " + child.id)
21619 }
21620
21621 // set elem to upper left corner of unused space; may move it later
21622 var elmStyle = elm.style;
21623 elmStyle.left = dim.l+"px";
21624 elmStyle.top = dim.t+"px";
21625 elmStyle.position = "absolute";
21626
21627 domClass.add(elm, "dijitAlign" + capitalize(pos));
21628
21629 // Size adjustments to make to this child widget
21630 var sizeSetting = {};
21631
21632 // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
21633 // panes and width adjustment for left/right align panes.
21634 if(changedRegionId && changedRegionId == child.id){
21635 sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
21636 }
21637
21638 // set size && adjust record of remaining space.
21639 // note that setting the width of a <div> may affect its height.
21640 if(pos == "top" || pos == "bottom"){
21641 sizeSetting.w = dim.w;
21642 size(child, sizeSetting);
21643 dim.h -= child.h;
21644 if(pos == "top"){
21645 dim.t += child.h;
21646 }else{
21647 elmStyle.top = dim.t + dim.h + "px";
21648 }
21649 }else if(pos == "left" || pos == "right"){
21650 sizeSetting.h = dim.h;
21651 size(child, sizeSetting);
21652 dim.w -= child.w;
21653 if(pos == "left"){
21654 dim.l += child.w;
21655 }else{
21656 elmStyle.left = dim.l + dim.w + "px";
21657 }
21658 }else if(pos == "client" || pos == "center"){
21659 size(child, dim);
21660 }
21661 });
21662 };
21663
21664
21665 return {
21666 marginBox2contentBox: layout.marginBox2contentBox,
21667 layoutChildren: layout.layoutChildren
21668 };
21669 });
21670
21671 },
21672 'dijit/_Contained':function(){
21673 define("dijit/_Contained", [
21674 "dojo/_base/declare", // declare
21675 "./registry" // registry.getEnclosingWidget(), registry.byNode()
21676 ], function(declare, registry){
21677
21678 // module:
21679 // dijit/_Contained
21680
21681 return declare("dijit._Contained", null, {
21682 // summary:
21683 // Mixin for widgets that are children of a container widget
21684 //
21685 // example:
21686 // | // make a basic custom widget that knows about it's parents
21687 // | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
21688
21689 _getSibling: function(/*String*/ which){
21690 // summary:
21691 // Returns next or previous sibling
21692 // which:
21693 // Either "next" or "previous"
21694 // tags:
21695 // private
21696 var node = this.domNode;
21697 do{
21698 node = node[which+"Sibling"];
21699 }while(node && node.nodeType != 1);
21700 return node && registry.byNode(node); // dijit/_WidgetBase
21701 },
21702
21703 getPreviousSibling: function(){
21704 // summary:
21705 // Returns null if this is the first child of the parent,
21706 // otherwise returns the next element sibling to the "left".
21707
21708 return this._getSibling("previous"); // dijit/_WidgetBase
21709 },
21710
21711 getNextSibling: function(){
21712 // summary:
21713 // Returns null if this is the last child of the parent,
21714 // otherwise returns the next element sibling to the "right".
21715
21716 return this._getSibling("next"); // dijit/_WidgetBase
21717 },
21718
21719 getIndexInParent: function(){
21720 // summary:
21721 // Returns the index of this widget within its container parent.
21722 // It returns -1 if the parent does not exist, or if the parent
21723 // is not a dijit._Container
21724
21725 var p = this.getParent();
21726 if(!p || !p.getIndexOfChild){
21727 return -1; // int
21728 }
21729 return p.getIndexOfChild(this); // int
21730 }
21731 });
21732 });
21733
21734 },
21735 'dijit/form/DataList':function(){
21736 define("dijit/form/DataList", [
21737 "dojo/_base/declare", // declare
21738 "dojo/dom", // dom.byId
21739 "dojo/_base/lang", // lang.trim
21740 "dojo/query", // query
21741 "dojo/store/Memory",
21742 "../registry" // registry.add registry.remove
21743 ], function(declare, dom, lang, query, MemoryStore, registry){
21744
21745 // module:
21746 // dijit/form/DataList
21747
21748 function toItem(/*DOMNode*/ option){
21749 // summary:
21750 // Convert `<option>` node to hash
21751 return {
21752 id: option.value,
21753 value: option.value,
21754 name: lang.trim(option.innerText || option.textContent || '')
21755 };
21756 }
21757
21758 return declare("dijit.form.DataList", MemoryStore, {
21759 // summary:
21760 // Inefficient but small data store specialized for inlined data via OPTION tags
21761 //
21762 // description:
21763 // Provides a store for inlined data like:
21764 //
21765 // | <datalist>
21766 // | <option value="AL">Alabama</option>
21767 // | ...
21768
21769 constructor: function(params, srcNodeRef){
21770 // summary:
21771 // Create the widget.
21772 // params: Object|null
21773 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
21774 // and functions, typically callbacks like onClick.
21775 // The hash can contain any of the widget's properties, excluding read-only properties.
21776 // srcNodeRef: DOMNode|String
21777 // Attach widget to this DOM node.
21778
21779 // store pointer to original DOM tree
21780 this.domNode = dom.byId(srcNodeRef);
21781
21782 lang.mixin(this, params);
21783 if(this.id){
21784 registry.add(this); // add to registry so it can be easily found by id
21785 }
21786 this.domNode.style.display = "none";
21787
21788 this.inherited(arguments, [{
21789 data: query("option", this.domNode).map(toItem)
21790 }]);
21791 },
21792
21793 destroy: function(){
21794 registry.remove(this.id);
21795 },
21796
21797 fetchSelectedItem: function(){
21798 // summary:
21799 // Get the option marked as selected, like `<option selected>`.
21800 // Not part of dojo.data API.
21801 var option = query("> option[selected]", this.domNode)[0] || query("> option", this.domNode)[0];
21802 return option && toItem(option);
21803 }
21804 });
21805 });
21806
21807 },
21808 '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",
21809 'dijit/form/CheckBox':function(){
21810 require({cache:{
21811 '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"}});
21812 define("dijit/form/CheckBox", [
21813 "require",
21814 "dojo/_base/declare", // declare
21815 "dojo/dom-attr", // domAttr.set
21816 "dojo/has", // has("dijit-legacy-requires")
21817 "dojo/query", // query
21818 "dojo/ready",
21819 "./ToggleButton",
21820 "./_CheckBoxMixin",
21821 "dojo/text!./templates/CheckBox.html",
21822 "dojo/NodeList-dom" // NodeList.addClass/removeClass
21823 ], function(require, declare, domAttr, has, query, ready, ToggleButton, _CheckBoxMixin, template){
21824
21825 // module:
21826 // dijit/form/CheckBox
21827
21828 // Back compat w/1.6, remove for 2.0
21829 if(has("dijit-legacy-requires")){
21830 ready(0, function(){
21831 var requires = ["dijit/form/RadioButton"];
21832 require(requires); // use indirection so modules not rolled into a build
21833 });
21834 }
21835
21836 return declare("dijit.form.CheckBox", [ToggleButton, _CheckBoxMixin], {
21837 // summary:
21838 // Same as an HTML checkbox, but with fancy styling.
21839 //
21840 // description:
21841 // User interacts with real html inputs.
21842 // On onclick (which occurs by mouse click, space-bar, or
21843 // using the arrow keys to switch the selected radio button),
21844 // we update the state of the checkbox/radio.
21845 //
21846 // There are two modes:
21847 //
21848 // 1. High contrast mode
21849 // 2. Normal mode
21850 //
21851 // In case 1, the regular html inputs are shown and used by the user.
21852 // In case 2, the regular html inputs are invisible but still used by
21853 // the user. They are turned quasi-invisible and overlay the background-image.
21854
21855 templateString: template,
21856
21857 baseClass: "dijitCheckBox",
21858
21859 _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
21860 // summary:
21861 // Handler for value= attribute to constructor, and also calls to
21862 // set('value', val).
21863 // description:
21864 // During initialization, just saves as attribute to the `<input type=checkbox>`.
21865 //
21866 // After initialization,
21867 // when passed a boolean, controls whether or not the CheckBox is checked.
21868 // If passed a string, changes the value attribute of the CheckBox (the one
21869 // specified as "value" when the CheckBox was constructed
21870 // (ex: `<input data-dojo-type="dijit/CheckBox" value="chicken">`).
21871 //
21872 // `widget.set('value', string)` will check the checkbox and change the value to the
21873 // specified string.
21874 //
21875 // `widget.set('value', boolean)` will change the checked state.
21876
21877 if(typeof newValue == "string"){
21878 this.inherited(arguments);
21879 newValue = true;
21880 }
21881 if(this._created){
21882 this.set('checked', newValue, priorityChange);
21883 }
21884 },
21885 _getValueAttr: function(){
21886 // summary:
21887 // Hook so get('value') works.
21888 // description:
21889 // If the CheckBox is checked, returns the value attribute.
21890 // Otherwise returns false.
21891 return (this.checked ? this.value : false);
21892 },
21893
21894 // Override behavior from Button, since we don't have an iconNode
21895 _setIconClassAttr: null,
21896
21897 postMixInProperties: function(){
21898 this.inherited(arguments);
21899
21900 // Need to set initial checked state as part of template, so that form submit works.
21901 // domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
21902 // to <body>, see #8666
21903 this.checkedAttrSetting = this.checked ? "checked" : "";
21904 },
21905
21906 _fillContent: function(){
21907 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
21908 // since CheckBox doesn't even have a container
21909 },
21910
21911 _onFocus: function(){
21912 if(this.id){
21913 query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
21914 }
21915 this.inherited(arguments);
21916 },
21917
21918 _onBlur: function(){
21919 if(this.id){
21920 query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
21921 }
21922 this.inherited(arguments);
21923 }
21924 });
21925 });
21926
21927 },
21928 'dijit/tree/_dndSelector':function(){
21929 define("dijit/tree/_dndSelector", [
21930 "dojo/_base/array", // array.filter array.forEach array.map
21931 "dojo/_base/connect", // connect.isCopyKey
21932 "dojo/_base/declare", // declare
21933 "dojo/_base/Deferred", // Deferred
21934 "dojo/_base/kernel", // global
21935 "dojo/_base/lang", // lang.hitch
21936 "dojo/cookie", // cookie
21937 "dojo/mouse", // mouse.isLeft
21938 "dojo/on",
21939 "dojo/touch",
21940 "./_dndContainer"
21941 ], function(array, connect, declare, Deferred, kernel, lang, cookie, mouse, on, touch, _dndContainer){
21942
21943 // module:
21944 // dijit/tree/_dndSelector
21945
21946
21947 return declare("dijit.tree._dndSelector", _dndContainer, {
21948 // summary:
21949 // This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
21950 // It's based on `dojo/dnd/Selector`.
21951 // tags:
21952 // protected
21953
21954 /*=====
21955 // selection: Object
21956 // (id to DomNode) map for every TreeNode that's currently selected.
21957 // The DOMNode is the TreeNode.rowNode.
21958 selection: {},
21959 =====*/
21960
21961 constructor: function(){
21962 // summary:
21963 // Initialization
21964 // tags:
21965 // private
21966
21967 this.selection={};
21968 this.anchor = null;
21969
21970 if(!this.cookieName && this.tree.id){
21971 this.cookieName = this.tree.id + "SaveSelectedCookie";
21972 }
21973
21974 this.events.push(
21975 on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
21976 on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
21977 on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
21978 );
21979 },
21980
21981 // singular: Boolean
21982 // Allows selection of only one element, if true.
21983 // Tree hasn't been tested in singular=true mode, unclear if it works.
21984 singular: false,
21985
21986 // methods
21987 getSelectedTreeNodes: function(){
21988 // summary:
21989 // Returns a list of selected node(s).
21990 // Used by dndSource on the start of a drag.
21991 // tags:
21992 // protected
21993 var nodes=[], sel = this.selection;
21994 for(var i in sel){
21995 nodes.push(sel[i]);
21996 }
21997 return nodes;
21998 },
21999
22000 selectNone: function(){
22001 // summary:
22002 // Unselects all items
22003 // tags:
22004 // private
22005
22006 this.setSelection([]);
22007 return this; // self
22008 },
22009
22010 destroy: function(){
22011 // summary:
22012 // Prepares the object to be garbage-collected
22013 this.inherited(arguments);
22014 this.selection = this.anchor = null;
22015 },
22016 addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor){
22017 // summary:
22018 // add node to current selection
22019 // node: Node
22020 // node to add
22021 // isAnchor: Boolean
22022 // Whether the node should become anchor.
22023
22024 this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
22025 if(isAnchor){ this.anchor = node; }
22026 return node;
22027 },
22028 removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
22029 // summary:
22030 // remove node from current selection
22031 // node: Node
22032 // node to remove
22033 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
22034 return node;
22035 },
22036 isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
22037 // summary:
22038 // return true if node is currently selected
22039 // node: Node
22040 // the node to check whether it's in the current selection
22041
22042 return node.id && !!this.selection[node.id];
22043 },
22044 setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
22045 // summary:
22046 // set the list of selected nodes to be exactly newSelection. All changes to the
22047 // selection should be passed through this function, which ensures that derived
22048 // attributes are kept up to date. Anchor will be deleted if it has been removed
22049 // from the selection, but no new anchor will be added by this function.
22050 // newSelection: Node[]
22051 // list of tree nodes to make selected
22052 var oldSelection = this.getSelectedTreeNodes();
22053 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
22054 node.setSelected(false);
22055 if(this.anchor == node){
22056 delete this.anchor;
22057 }
22058 delete this.selection[node.id];
22059 }));
22060 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
22061 node.setSelected(true);
22062 this.selection[node.id] = node;
22063 }));
22064 this._updateSelectionProperties();
22065 },
22066 _setDifference: function(xs,ys){
22067 // summary:
22068 // Returns a copy of xs which lacks any objects
22069 // occurring in ys. Checks for membership by
22070 // modifying and then reading the object, so it will
22071 // not properly handle sets of numbers or strings.
22072
22073 array.forEach(ys, function(y){ y.__exclude__ = true; });
22074 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
22075
22076 // clean up after ourselves.
22077 array.forEach(ys, function(y){ delete y['__exclude__'] });
22078 return ret;
22079 },
22080 _updateSelectionProperties: function(){
22081 // summary:
22082 // Update the following tree properties from the current selection:
22083 // path[s], selectedItem[s], selectedNode[s]
22084
22085 var selected = this.getSelectedTreeNodes();
22086 var paths = [], nodes = [], selects = [];
22087 array.forEach(selected, function(node){
22088 var ary = node.getTreePath(), model = this.tree.model;
22089 nodes.push(node);
22090 paths.push(ary);
22091 ary = array.map(ary, function(item){
22092 return model.getIdentity(item);
22093 }, this);
22094 selects.push(ary.join("/"))
22095 }, this);
22096 var items = array.map(nodes,function(node){ return node.item; });
22097 this.tree._set("paths", paths);
22098 this.tree._set("path", paths[0] || []);
22099 this.tree._set("selectedNodes", nodes);
22100 this.tree._set("selectedNode", nodes[0] || null);
22101 this.tree._set("selectedItems", items);
22102 this.tree._set("selectedItem", items[0] || null);
22103 if (this.tree.persist && selects.length > 0) {
22104 cookie(this.cookieName, selects.join(","), {expires:365});
22105 }
22106 },
22107 _getSavedPaths: function(){
22108 // summary:
22109 // Returns paths of nodes that were selected previously and saved in the cookie.
22110
22111 var tree = this.tree;
22112 if(tree.persist && tree.dndController.cookieName){
22113 var oreo, paths = [];
22114 oreo = cookie(tree.dndController.cookieName);
22115 if(oreo){
22116 paths = array.map(oreo.split(","), function(path){
22117 return path.split("/");
22118 })
22119 }
22120 return paths;
22121 }
22122 },
22123 // mouse events
22124 onMouseDown: function(e){
22125 // summary:
22126 // Event processor for onmousedown/ontouchstart
22127 // e: Event
22128 // onmousedown/ontouchstart event
22129 // tags:
22130 // protected
22131
22132 // ignore click on expando node
22133 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
22134
22135 if(mouse.isLeft(e)){
22136 // Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
22137 // for mobile because it will break things completely, see #15838.
22138 e.preventDefault();
22139 }else if(e.type != "touchstart"){
22140 // Ignore right click
22141 return;
22142 }
22143
22144 var treeNode = this.current,
22145 copy = connect.isCopyKey(e), id = treeNode.id;
22146
22147 // if shift key is not pressed, and the node is already in the selection,
22148 // delay deselection until onmouseup so in the case of DND, deselection
22149 // will be canceled by onmousemove.
22150 if(!this.singular && !e.shiftKey && this.selection[id]){
22151 this._doDeselect = true;
22152 return;
22153 }else{
22154 this._doDeselect = false;
22155 }
22156 this.userSelect(treeNode, copy, e.shiftKey);
22157 },
22158
22159 onMouseUp: function(e){
22160 // summary:
22161 // Event processor for onmouseup/ontouchend
22162 // e: Event
22163 // onmouseup/ontouchend event
22164 // tags:
22165 // protected
22166
22167 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
22168 // a already selected item (to deselect the item), or click on a not-yet selected item
22169 // (which should remove all current selection, and add the clicked item). This can not
22170 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
22171 // the deselection logic here, the user can drags an already selected item.
22172 if(!this._doDeselect){ return; }
22173 this._doDeselect = false;
22174 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
22175 },
22176 onMouseMove: function(/*===== e =====*/){
22177 // summary:
22178 // event processor for onmousemove/ontouchmove
22179 // e: Event
22180 // onmousemove/ontouchmove event
22181 this._doDeselect = false;
22182 },
22183
22184 _compareNodes: function(n1, n2){
22185 if(n1 === n2){
22186 return 0;
22187 }
22188
22189 if('sourceIndex' in document.documentElement){ //IE
22190 //TODO: does not yet work if n1 and/or n2 is a text node
22191 return n1.sourceIndex - n2.sourceIndex;
22192 }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
22193 return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
22194 }else if(document.createRange){ //Webkit
22195 var r1 = doc.createRange();
22196 r1.setStartBefore(n1);
22197
22198 var r2 = doc.createRange();
22199 r2.setStartBefore(n2);
22200
22201 return r1.compareBoundaryPoints(r1.END_TO_END, r2);
22202 }else{
22203 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
22204 }
22205 },
22206
22207 userSelect: function(node, multi, range){
22208 // summary:
22209 // Add or remove the given node from selection, responding
22210 // to a user action such as a click or keypress.
22211 // multi: Boolean
22212 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
22213 // range: Boolean
22214 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
22215 // tags:
22216 // protected
22217
22218 if(this.singular){
22219 if(this.anchor == node && multi){
22220 this.selectNone();
22221 }else{
22222 this.setSelection([node]);
22223 this.anchor = node;
22224 }
22225 }else{
22226 if(range && this.anchor){
22227 var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
22228 begin, end, anchor = this.anchor;
22229
22230 if(cr < 0){ //current is after anchor
22231 begin = anchor;
22232 end = node;
22233 }else{ //current is before anchor
22234 begin = node;
22235 end = anchor;
22236 }
22237 var nodes = [];
22238 //add everything betweeen begin and end inclusively
22239 while(begin != end){
22240 nodes.push(begin);
22241 begin = this.tree._getNextNode(begin);
22242 }
22243 nodes.push(end);
22244
22245 this.setSelection(nodes);
22246 }else{
22247 if( this.selection[ node.id ] && multi ){
22248 this.removeTreeNode( node );
22249 }else if(multi){
22250 this.addTreeNode(node, true);
22251 }else{
22252 this.setSelection([node]);
22253 this.anchor = node;
22254 }
22255 }
22256 }
22257 },
22258
22259 getItem: function(/*String*/ key){
22260 // summary:
22261 // Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
22262 // Called by dojo/dnd/Source.checkAcceptance().
22263 // tags:
22264 // protected
22265
22266 var widget = this.selection[key];
22267 return {
22268 data: widget,
22269 type: ["treeNode"]
22270 }; // dojo/dnd/Container._Item
22271 },
22272
22273 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
22274 // summary:
22275 // Iterates over selected items;
22276 // see `dojo/dnd/Container.forInItems()` for details
22277 o = o || kernel.global;
22278 for(var id in this.selection){
22279 // console.log("selected item id: " + id);
22280 f.call(o, this.getItem(id), id, this);
22281 }
22282 }
22283 });
22284 });
22285
22286 },
22287 'dijit/_Container':function(){
22288 define("dijit/_Container", [
22289 "dojo/_base/array", // array.forEach array.indexOf
22290 "dojo/_base/declare", // declare
22291 "dojo/dom-construct" // domConstruct.place
22292 ], function(array, declare, domConstruct){
22293
22294 // module:
22295 // dijit/_Container
22296
22297 return declare("dijit._Container", null, {
22298 // summary:
22299 // Mixin for widgets that contain HTML and/or a set of widget children.
22300
22301 buildRendering: function(){
22302 this.inherited(arguments);
22303 if(!this.containerNode){
22304 // all widgets with descendants must set containerNode
22305 this.containerNode = this.domNode;
22306 }
22307 },
22308
22309 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
22310 // summary:
22311 // Makes the given widget a child of this widget.
22312 // description:
22313 // Inserts specified child widget's dom node as a child of this widget's
22314 // container node, and possibly does other processing (such as layout).
22315 //
22316 // Functionality is undefined if this widget contains anything besides
22317 // a list of child widgets (ie, if it contains arbitrary non-widget HTML).
22318
22319 var refNode = this.containerNode;
22320 if(insertIndex && typeof insertIndex == "number"){
22321 var children = this.getChildren();
22322 if(children && children.length >= insertIndex){
22323 refNode = children[insertIndex-1].domNode;
22324 insertIndex = "after";
22325 }
22326 }
22327 domConstruct.place(widget.domNode, refNode, insertIndex);
22328
22329 // If I've been started but the child widget hasn't been started,
22330 // start it now. Make sure to do this after widget has been
22331 // inserted into the DOM tree, so it can see that it's being controlled by me,
22332 // so it doesn't try to size itself.
22333 if(this._started && !widget._started){
22334 widget.startup();
22335 }
22336 },
22337
22338 removeChild: function(/*Widget|int*/ widget){
22339 // summary:
22340 // Removes the passed widget instance from this widget but does
22341 // not destroy it. You can also pass in an integer indicating
22342 // the index within the container to remove (ie, removeChild(5) removes the sixth widget).
22343
22344 if(typeof widget == "number"){
22345 widget = this.getChildren()[widget];
22346 }
22347
22348 if(widget){
22349 var node = widget.domNode;
22350 if(node && node.parentNode){
22351 node.parentNode.removeChild(node); // detach but don't destroy
22352 }
22353 }
22354 },
22355
22356 hasChildren: function(){
22357 // summary:
22358 // Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
22359 return this.getChildren().length > 0; // Boolean
22360 },
22361
22362 _getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir){
22363 // summary:
22364 // Get the next or previous widget sibling of child
22365 // dir:
22366 // if 1, get the next sibling
22367 // if -1, get the previous sibling
22368 // tags:
22369 // private
22370 var children = this.getChildren(),
22371 idx = array.indexOf(this.getChildren(), child); // int
22372 return children[idx + dir];
22373 },
22374
22375 getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
22376 // summary:
22377 // Gets the index of the child in this container or -1 if not found
22378 return array.indexOf(this.getChildren(), child); // int
22379 }
22380 });
22381 });
22382
22383 },
22384 'dojo/data/ItemFileReadStore':function(){
22385 define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
22386 "../Evented", "./util/filter", "./util/simpleFetch", "../date/stamp"
22387 ], function(kernel, lang, declare, array, xhr, Evented, filterUtil, simpleFetch, dateStamp){
22388
22389 // module:
22390 // dojo/data/ItemFileReadStore
22391
22392 var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
22393 // summary:
22394 // The ItemFileReadStore implements the dojo/data/api/Read API and reads
22395 // data from JSON files that have contents in this format --
22396 // | { items: [
22397 // | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
22398 // | { name:'Fozzie Bear', wears:['hat', 'tie']},
22399 // | { name:'Miss Piggy', pets:'Foo-Foo'}
22400 // | ]}
22401 // Note that it can also contain an 'identifier' property that specified which attribute on the items
22402 // in the array of items that acts as the unique identifier for that item.
22403
22404 constructor: function(/* Object */ keywordParameters){
22405 // summary:
22406 // constructor
22407 // keywordParameters:
22408 // {url: String} {data: jsonObject} {typeMap: object}
22409 // The structure of the typeMap object is as follows:
22410 // | {
22411 // | type0: function || object,
22412 // | type1: function || object,
22413 // | ...
22414 // | typeN: function || object
22415 // | }
22416 // Where if it is a function, it is assumed to be an object constructor that takes the
22417 // value of _value as the initialization parameters. If it is an object, then it is assumed
22418 // to be an object of general form:
22419 // | {
22420 // | type: function, //constructor.
22421 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
22422 // | }
22423
22424 this._arrayOfAllItems = [];
22425 this._arrayOfTopLevelItems = [];
22426 this._loadFinished = false;
22427 this._jsonFileUrl = keywordParameters.url;
22428 this._ccUrl = keywordParameters.url;
22429 this.url = keywordParameters.url;
22430 this._jsonData = keywordParameters.data;
22431 this.data = null;
22432 this._datatypeMap = keywordParameters.typeMap || {};
22433 if(!this._datatypeMap['Date']){
22434 //If no default mapping for dates, then set this as default.
22435 //We use the dojo/date/stamp here because the ISO format is the 'dojo way'
22436 //of generically representing dates.
22437 this._datatypeMap['Date'] = {
22438 type: Date,
22439 deserialize: function(value){
22440 return dateStamp.fromISOString(value);
22441 }
22442 };
22443 }
22444 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
22445 this._itemsByIdentity = null;
22446 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
22447 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
22448 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
22449 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
22450 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
22451 this._queuedFetches = [];
22452 if(keywordParameters.urlPreventCache !== undefined){
22453 this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
22454 }
22455 if(keywordParameters.hierarchical !== undefined){
22456 this.hierarchical = keywordParameters.hierarchical?true:false;
22457 }
22458 if(keywordParameters.clearOnClose){
22459 this.clearOnClose = true;
22460 }
22461 if("failOk" in keywordParameters){
22462 this.failOk = keywordParameters.failOk?true:false;
22463 }
22464 },
22465
22466 url: "", // use "" rather than undefined for the benefit of the parser (#3539)
22467
22468 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
22469 //when clearOnClose and close is used.
22470 _ccUrl: "",
22471
22472 data: null, // define this so that the parser can populate it
22473
22474 typeMap: null, //Define so parser can populate.
22475
22476 // clearOnClose: Boolean
22477 // Parameter to allow users to specify if a close call should force a reload or not.
22478 // By default, it retains the old behavior of not clearing if close is called. But
22479 // if set true, the store will be reset to default state. Note that by doing this,
22480 // all item handles will become invalid and a new fetch must be issued.
22481 clearOnClose: false,
22482
22483 // urlPreventCache: Boolean
22484 // Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
22485 // Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
22486 // Added for tracker: #6072
22487 urlPreventCache: false,
22488
22489 // failOk: Boolean
22490 // Parameter for specifying that it is OK for the xhrGet call to fail silently.
22491 failOk: false,
22492
22493 // hierarchical: Boolean
22494 // Parameter to indicate to process data from the url as hierarchical
22495 // (data items can contain other data items in js form). Default is true
22496 // for backwards compatibility. False means only root items are processed
22497 // as items, all child objects outside of type-mapped objects and those in
22498 // specific reference format, are left straight JS data objects.
22499 hierarchical: true,
22500
22501 _assertIsItem: function(/* dojo/data/api/Item */ item){
22502 // summary:
22503 // This function tests whether the item passed in is indeed an item in the store.
22504 // item:
22505 // The item to test for being contained by the store.
22506 if(!this.isItem(item)){
22507 throw new Error(this.declaredClass + ": Invalid item argument.");
22508 }
22509 },
22510
22511 _assertIsAttribute: function(/* attribute-name-string */ attribute){
22512 // summary:
22513 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
22514 // attribute:
22515 // The attribute to test for being contained by the store.
22516 if(typeof attribute !== "string"){
22517 throw new Error(this.declaredClass + ": Invalid attribute argument.");
22518 }
22519 },
22520
22521 getValue: function( /* dojo/data/api/Item */ item,
22522 /* attribute-name-string */ attribute,
22523 /* value? */ defaultValue){
22524 // summary:
22525 // See dojo/data/api/Read.getValue()
22526 var values = this.getValues(item, attribute);
22527 return (values.length > 0)?values[0]:defaultValue; // mixed
22528 },
22529
22530 getValues: function(/* dojo/data/api/Item */ item,
22531 /* attribute-name-string */ attribute){
22532 // summary:
22533 // See dojo/data/api/Read.getValues()
22534
22535 this._assertIsItem(item);
22536 this._assertIsAttribute(attribute);
22537 // Clone it before returning. refs: #10474
22538 return (item[attribute] || []).slice(0); // Array
22539 },
22540
22541 getAttributes: function(/* dojo/data/api/Item */ item){
22542 // summary:
22543 // See dojo/data/api/Read.getAttributes()
22544 this._assertIsItem(item);
22545 var attributes = [];
22546 for(var key in item){
22547 // Save off only the real item attributes, not the special id marks for O(1) isItem.
22548 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
22549 attributes.push(key);
22550 }
22551 }
22552 return attributes; // Array
22553 },
22554
22555 hasAttribute: function( /* dojo/data/api/Item */ item,
22556 /* attribute-name-string */ attribute){
22557 // summary:
22558 // See dojo/data/api/Read.hasAttribute()
22559 this._assertIsItem(item);
22560 this._assertIsAttribute(attribute);
22561 return (attribute in item);
22562 },
22563
22564 containsValue: function(/* dojo/data/api/Item */ item,
22565 /* attribute-name-string */ attribute,
22566 /* anything */ value){
22567 // summary:
22568 // See dojo/data/api/Read.containsValue()
22569 var regexp = undefined;
22570 if(typeof value === "string"){
22571 regexp = filterUtil.patternToRegExp(value, false);
22572 }
22573 return this._containsValue(item, attribute, value, regexp); //boolean.
22574 },
22575
22576 _containsValue: function( /* dojo/data/api/Item */ item,
22577 /* attribute-name-string */ attribute,
22578 /* anything */ value,
22579 /* RegExp?*/ regexp){
22580 // summary:
22581 // Internal function for looking at the values contained by the item.
22582 // description:
22583 // Internal function for looking at the values contained by the item. This
22584 // function allows for denoting if the comparison should be case sensitive for
22585 // strings or not (for handling filtering cases where string case should not matter)
22586 // item:
22587 // The data item to examine for attribute values.
22588 // attribute:
22589 // The attribute to inspect.
22590 // value:
22591 // The value to match.
22592 // regexp:
22593 // Optional regular expression generated off value if value was of string type to handle wildcarding.
22594 // If present and attribute values are string, then it can be used for comparison instead of 'value'
22595 return array.some(this.getValues(item, attribute), function(possibleValue){
22596 if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
22597 if(possibleValue.toString().match(regexp)){
22598 return true; // Boolean
22599 }
22600 }else if(value === possibleValue){
22601 return true; // Boolean
22602 }
22603 });
22604 },
22605
22606 isItem: function(/* anything */ something){
22607 // summary:
22608 // See dojo/data/api/Read.isItem()
22609 if(something && something[this._storeRefPropName] === this){
22610 if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
22611 return true;
22612 }
22613 }
22614 return false; // Boolean
22615 },
22616
22617 isItemLoaded: function(/* anything */ something){
22618 // summary:
22619 // See dojo/data/api/Read.isItemLoaded()
22620 return this.isItem(something); //boolean
22621 },
22622
22623 loadItem: function(/* object */ keywordArgs){
22624 // summary:
22625 // See dojo/data/api/Read.loadItem()
22626 this._assertIsItem(keywordArgs.item);
22627 },
22628
22629 getFeatures: function(){
22630 // summary:
22631 // See dojo/data/api/Read.getFeatures()
22632 return this._features; //Object
22633 },
22634
22635 getLabel: function(/* dojo/data/api/Item */ item){
22636 // summary:
22637 // See dojo/data/api/Read.getLabel()
22638 if(this._labelAttr && this.isItem(item)){
22639 return this.getValue(item,this._labelAttr); //String
22640 }
22641 return undefined; //undefined
22642 },
22643
22644 getLabelAttributes: function(/* dojo/data/api/Item */ item){
22645 // summary:
22646 // See dojo/data/api/Read.getLabelAttributes()
22647 if(this._labelAttr){
22648 return [this._labelAttr]; //array
22649 }
22650 return null; //null
22651 },
22652
22653 filter: function(/* Object */ requestArgs, /* item[] */ arrayOfItems, /* Function */ findCallback){
22654 // summary:
22655 // This method handles the basic filtering needs for ItemFile* based stores.
22656 var items = [],
22657 i, key;
22658
22659 if(requestArgs.query){
22660 var value,
22661 ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
22662
22663 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
22664 //same value for each item examined. Much more efficient.
22665 var regexpList = {};
22666 for(key in requestArgs.query){
22667 value = requestArgs.query[key];
22668 if(typeof value === "string"){
22669 regexpList[key] = filterUtil.patternToRegExp(value, ignoreCase);
22670 }else if(value instanceof RegExp){
22671 regexpList[key] = value;
22672 }
22673 }
22674 for(i = 0; i < arrayOfItems.length; ++i){
22675 var match = true;
22676 var candidateItem = arrayOfItems[i];
22677 if(candidateItem === null){
22678 match = false;
22679 }else{
22680 for(key in requestArgs.query){
22681 value = requestArgs.query[key];
22682 if(!this._containsValue(candidateItem, key, value, regexpList[key])){
22683 match = false;
22684 }
22685 }
22686 }
22687 if(match){
22688 items.push(candidateItem);
22689 }
22690 }
22691 findCallback(items, requestArgs);
22692 }else{
22693 // We want a copy to pass back in case the parent wishes to sort the array.
22694 // We shouldn't allow resort of the internal list, so that multiple callers
22695 // can get lists and sort without affecting each other. We also need to
22696 // filter out any null values that have been left as a result of deleteItem()
22697 // calls in ItemFileWriteStore.
22698 for(i = 0; i < arrayOfItems.length; ++i){
22699 var item = arrayOfItems[i];
22700 if(item !== null){
22701 items.push(item);
22702 }
22703 }
22704 findCallback(items, requestArgs);
22705 }
22706 },
22707
22708 _fetchItems: function( /* Object */ keywordArgs,
22709 /* Function */ findCallback,
22710 /* Function */ errorCallback){
22711 // summary:
22712 // See dojo/data/util.simpleFetch.fetch()
22713 var self = this;
22714
22715 if(this._loadFinished){
22716 this.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
22717 }else{
22718 //Do a check on the JsonFileUrl and crosscheck it.
22719 //If it doesn't match the cross-check, it needs to be updated
22720 //This allows for either url or _jsonFileUrl to he changed to
22721 //reset the store load location. Done this way for backwards
22722 //compatibility. People use _jsonFileUrl (even though officially
22723 //private.
22724 if(this._jsonFileUrl !== this._ccUrl){
22725 kernel.deprecated(this.declaredClass + ": ",
22726 "To change the url, set the url property of the store," +
22727 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
22728 this._ccUrl = this._jsonFileUrl;
22729 this.url = this._jsonFileUrl;
22730 }else if(this.url !== this._ccUrl){
22731 this._jsonFileUrl = this.url;
22732 this._ccUrl = this.url;
22733 }
22734
22735 //See if there was any forced reset of data.
22736 if(this.data != null){
22737 this._jsonData = this.data;
22738 this.data = null;
22739 }
22740
22741 if(this._jsonFileUrl){
22742 //If fetches come in before the loading has finished, but while
22743 //a load is in progress, we have to defer the fetching to be
22744 //invoked in the callback.
22745 if(this._loadInProgress){
22746 this._queuedFetches.push({args: keywordArgs, filter: lang.hitch(self, "filter"), findCallback: lang.hitch(self, findCallback)});
22747 }else{
22748 this._loadInProgress = true;
22749 var getArgs = {
22750 url: self._jsonFileUrl,
22751 handleAs: "json-comment-optional",
22752 preventCache: this.urlPreventCache,
22753 failOk: this.failOk
22754 };
22755 var getHandler = xhr.get(getArgs);
22756 getHandler.addCallback(function(data){
22757 try{
22758 self._getItemsFromLoadedData(data);
22759 self._loadFinished = true;
22760 self._loadInProgress = false;
22761
22762 self.filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions), findCallback);
22763 self._handleQueuedFetches();
22764 }catch(e){
22765 self._loadFinished = true;
22766 self._loadInProgress = false;
22767 errorCallback(e, keywordArgs);
22768 }
22769 });
22770 getHandler.addErrback(function(error){
22771 self._loadInProgress = false;
22772 errorCallback(error, keywordArgs);
22773 });
22774
22775 //Wire up the cancel to abort of the request
22776 //This call cancel on the deferred if it hasn't been called
22777 //yet and then will chain to the simple abort of the
22778 //simpleFetch keywordArgs
22779 var oldAbort = null;
22780 if(keywordArgs.abort){
22781 oldAbort = keywordArgs.abort;
22782 }
22783 keywordArgs.abort = function(){
22784 var df = getHandler;
22785 if(df && df.fired === -1){
22786 df.cancel();
22787 df = null;
22788 }
22789 if(oldAbort){
22790 oldAbort.call(keywordArgs);
22791 }
22792 };
22793 }
22794 }else if(this._jsonData){
22795 try{
22796 this._loadFinished = true;
22797 this._getItemsFromLoadedData(this._jsonData);
22798 this._jsonData = null;
22799 self.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
22800 }catch(e){
22801 errorCallback(e, keywordArgs);
22802 }
22803 }else{
22804 errorCallback(new Error(this.declaredClass + ": No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
22805 }
22806 }
22807 },
22808
22809 _handleQueuedFetches: function(){
22810 // summary:
22811 // Internal function to execute delayed request in the store.
22812
22813 //Execute any deferred fetches now.
22814 if(this._queuedFetches.length > 0){
22815 for(var i = 0; i < this._queuedFetches.length; i++){
22816 var fData = this._queuedFetches[i],
22817 delayedQuery = fData.args,
22818 delayedFilter = fData.filter,
22819 delayedFindCallback = fData.findCallback;
22820 if(delayedFilter){
22821 delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions), delayedFindCallback);
22822 }else{
22823 this.fetchItemByIdentity(delayedQuery);
22824 }
22825 }
22826 this._queuedFetches = [];
22827 }
22828 },
22829
22830 _getItemsArray: function(/*object?*/queryOptions){
22831 // summary:
22832 // Internal function to determine which list of items to search over.
22833 // queryOptions: The query options parameter, if any.
22834 if(queryOptions && queryOptions.deep){
22835 return this._arrayOfAllItems;
22836 }
22837 return this._arrayOfTopLevelItems;
22838 },
22839
22840 close: function(/*dojo/data/api/Request|Object?*/ request){
22841 // summary:
22842 // See dojo/data/api/Read.close()
22843 if(this.clearOnClose &&
22844 this._loadFinished &&
22845 !this._loadInProgress){
22846 //Reset all internalsback to default state. This will force a reload
22847 //on next fetch. This also checks that the data or url param was set
22848 //so that the store knows it can get data. Without one of those being set,
22849 //the next fetch will trigger an error.
22850
22851 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
22852 (this.url == "" || this.url == null)
22853 ) && this.data == null){
22854 console.debug(this.declaredClass + ": WARNING! Data reload " +
22855 " information has not been provided." +
22856 " Please set 'url' or 'data' to the appropriate value before" +
22857 " the next fetch");
22858 }
22859 this._arrayOfAllItems = [];
22860 this._arrayOfTopLevelItems = [];
22861 this._loadFinished = false;
22862 this._itemsByIdentity = null;
22863 this._loadInProgress = false;
22864 this._queuedFetches = [];
22865 }
22866 },
22867
22868 _getItemsFromLoadedData: function(/* Object */ dataObject){
22869 // summary:
22870 // Function to parse the loaded data into item format and build the internal items array.
22871 // description:
22872 // Function to parse the loaded data into item format and build the internal items array.
22873 // dataObject:
22874 // The JS data object containing the raw data to convery into item format.
22875 // returns: Array
22876 // Array of items in store item format.
22877
22878 // First, we define a couple little utility functions...
22879 var addingArrays = false,
22880 self = this;
22881
22882 function valueIsAnItem(/* anything */ aValue){
22883 // summary:
22884 // Given any sort of value that could be in the raw json data,
22885 // return true if we should interpret the value as being an
22886 // item itself, rather than a literal value or a reference.
22887 // example:
22888 // | false == valueIsAnItem("Kermit");
22889 // | false == valueIsAnItem(42);
22890 // | false == valueIsAnItem(new Date());
22891 // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
22892 // | false == valueIsAnItem({_reference:'Kermit'});
22893 // | true == valueIsAnItem({name:'Kermit', color:'green'});
22894 // | true == valueIsAnItem({iggy:'pop'});
22895 // | true == valueIsAnItem({foo:42});
22896 return (aValue !== null) &&
22897 (typeof aValue === "object") &&
22898 (!lang.isArray(aValue) || addingArrays) &&
22899 (!lang.isFunction(aValue)) &&
22900 (aValue.constructor == Object || lang.isArray(aValue)) &&
22901 (typeof aValue._reference === "undefined") &&
22902 (typeof aValue._type === "undefined") &&
22903 (typeof aValue._value === "undefined") &&
22904 self.hierarchical;
22905 }
22906
22907 function addItemAndSubItemsToArrayOfAllItems(/* dojo/data/api/Item */ anItem){
22908 self._arrayOfAllItems.push(anItem);
22909 for(var attribute in anItem){
22910 var valueForAttribute = anItem[attribute];
22911 if(valueForAttribute){
22912 if(lang.isArray(valueForAttribute)){
22913 var valueArray = valueForAttribute;
22914 for(var k = 0; k < valueArray.length; ++k){
22915 var singleValue = valueArray[k];
22916 if(valueIsAnItem(singleValue)){
22917 addItemAndSubItemsToArrayOfAllItems(singleValue);
22918 }
22919 }
22920 }else{
22921 if(valueIsAnItem(valueForAttribute)){
22922 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
22923 }
22924 }
22925 }
22926 }
22927 }
22928
22929 this._labelAttr = dataObject.label;
22930
22931 // We need to do some transformations to convert the data structure
22932 // that we read from the file into a format that will be convenient
22933 // to work with in memory.
22934
22935 // Step 1: Walk through the object hierarchy and build a list of all items
22936 var i,
22937 item;
22938 this._arrayOfAllItems = [];
22939 this._arrayOfTopLevelItems = dataObject.items;
22940
22941 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
22942 item = this._arrayOfTopLevelItems[i];
22943 if(lang.isArray(item)){
22944 addingArrays = true;
22945 }
22946 addItemAndSubItemsToArrayOfAllItems(item);
22947 item[this._rootItemPropName]=true;
22948 }
22949
22950 // Step 2: Walk through all the attribute values of all the items,
22951 // and replace single values with arrays. For example, we change this:
22952 // { name:'Miss Piggy', pets:'Foo-Foo'}
22953 // into this:
22954 // { name:['Miss Piggy'], pets:['Foo-Foo']}
22955 //
22956 // We also store the attribute names so we can validate our store
22957 // reference and item id special properties for the O(1) isItem
22958 var allAttributeNames = {},
22959 key;
22960
22961 for(i = 0; i < this._arrayOfAllItems.length; ++i){
22962 item = this._arrayOfAllItems[i];
22963 for(key in item){
22964 if(key !== this._rootItemPropName){
22965 var value = item[key];
22966 if(value !== null){
22967 if(!lang.isArray(value)){
22968 item[key] = [value];
22969 }
22970 }else{
22971 item[key] = [null];
22972 }
22973 }
22974 allAttributeNames[key]=key;
22975 }
22976 }
22977
22978 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
22979 // This should go really fast, it will generally never even run the loop.
22980 while(allAttributeNames[this._storeRefPropName]){
22981 this._storeRefPropName += "_";
22982 }
22983 while(allAttributeNames[this._itemNumPropName]){
22984 this._itemNumPropName += "_";
22985 }
22986 while(allAttributeNames[this._reverseRefMap]){
22987 this._reverseRefMap += "_";
22988 }
22989
22990 // Step 4: Some data files specify an optional 'identifier', which is
22991 // the name of an attribute that holds the identity of each item.
22992 // If this data file specified an identifier attribute, then build a
22993 // hash table of items keyed by the identity of the items.
22994 var arrayOfValues;
22995
22996 var identifier = dataObject.identifier;
22997 if(identifier){
22998 this._itemsByIdentity = {};
22999 this._features['dojo.data.api.Identity'] = identifier;
23000 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23001 item = this._arrayOfAllItems[i];
23002 arrayOfValues = item[identifier];
23003 var identity = arrayOfValues[0];
23004 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23005 this._itemsByIdentity[identity] = item;
23006 }else{
23007 if(this._jsonFileUrl){
23008 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 + "]");
23009 }else if(this._jsonData){
23010 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 + "]");
23011 }
23012 }
23013 }
23014 }else{
23015 this._features['dojo.data.api.Identity'] = Number;
23016 }
23017
23018 // Step 5: Walk through all the items, and set each item's properties
23019 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
23020 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23021 item = this._arrayOfAllItems[i];
23022 item[this._storeRefPropName] = this;
23023 item[this._itemNumPropName] = i;
23024 }
23025
23026 // Step 6: We walk through all the attribute values of all the items,
23027 // looking for type/value literals and item-references.
23028 //
23029 // We replace item-references with pointers to items. For example, we change:
23030 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23031 // into this:
23032 // { name:['Kermit'], friends:[miss_piggy] }
23033 // (where miss_piggy is the object representing the 'Miss Piggy' item).
23034 //
23035 // We replace type/value pairs with typed-literals. For example, we change:
23036 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
23037 // into this:
23038 // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
23039 //
23040 // We also generate the associate map for all items for the O(1) isItem function.
23041 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23042 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23043 for(key in item){
23044 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
23045 for(var j = 0; j < arrayOfValues.length; ++j){
23046 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
23047 if(value !== null && typeof value == "object"){
23048 if(("_type" in value) && ("_value" in value)){
23049 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
23050 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
23051 if(!mappingObj){
23052 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
23053 }else if(lang.isFunction(mappingObj)){
23054 arrayOfValues[j] = new mappingObj(value._value);
23055 }else if(lang.isFunction(mappingObj.deserialize)){
23056 arrayOfValues[j] = mappingObj.deserialize(value._value);
23057 }else{
23058 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
23059 }
23060 }
23061 if(value._reference){
23062 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
23063 if(!lang.isObject(referenceDescription)){
23064 // example: 'Miss Piggy'
23065 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
23066 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
23067 }else{
23068 // example: {name:'Miss Piggy'}
23069 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23070 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
23071 var candidateItem = this._arrayOfAllItems[k],
23072 found = true;
23073 for(var refKey in referenceDescription){
23074 if(candidateItem[refKey] != referenceDescription[refKey]){
23075 found = false;
23076 }
23077 }
23078 if(found){
23079 arrayOfValues[j] = candidateItem;
23080 }
23081 }
23082 }
23083 if(this.referenceIntegrity){
23084 var refItem = arrayOfValues[j];
23085 if(this.isItem(refItem)){
23086 this._addReferenceToMap(refItem, item, key);
23087 }
23088 }
23089 }else if(this.isItem(value)){
23090 //It's a child item (not one referenced through _reference).
23091 //We need to treat this as a referenced item, so it can be cleaned up
23092 //in a write store easily.
23093 if(this.referenceIntegrity){
23094 this._addReferenceToMap(value, item, key);
23095 }
23096 }
23097 }
23098 }
23099 }
23100 }
23101 },
23102
23103 _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
23104 // summary:
23105 // Method to add an reference map entry for an item and attribute.
23106 // description:
23107 // Method to add an reference map entry for an item and attribute.
23108 // refItem:
23109 // The item that is referenced.
23110 // parentItem:
23111 // The item that holds the new reference to refItem.
23112 // attribute:
23113 // The attribute on parentItem that contains the new reference.
23114
23115 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
23116 },
23117
23118 getIdentity: function(/* dojo/data/api/Item */ item){
23119 // summary:
23120 // See dojo/data/api/Identity.getIdentity()
23121 var identifier = this._features['dojo.data.api.Identity'];
23122 if(identifier === Number){
23123 return item[this._itemNumPropName]; // Number
23124 }else{
23125 var arrayOfValues = item[identifier];
23126 if(arrayOfValues){
23127 return arrayOfValues[0]; // Object|String
23128 }
23129 }
23130 return null; // null
23131 },
23132
23133 fetchItemByIdentity: function(/* Object */ keywordArgs){
23134 // summary:
23135 // See dojo/data/api/Identity.fetchItemByIdentity()
23136
23137 // Hasn't loaded yet, we have to trigger the load.
23138 var item,
23139 scope;
23140 if(!this._loadFinished){
23141 var self = this;
23142 //Do a check on the JsonFileUrl and crosscheck it.
23143 //If it doesn't match the cross-check, it needs to be updated
23144 //This allows for either url or _jsonFileUrl to he changed to
23145 //reset the store load location. Done this way for backwards
23146 //compatibility. People use _jsonFileUrl (even though officially
23147 //private.
23148 if(this._jsonFileUrl !== this._ccUrl){
23149 kernel.deprecated(this.declaredClass + ": ",
23150 "To change the url, set the url property of the store," +
23151 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23152 this._ccUrl = this._jsonFileUrl;
23153 this.url = this._jsonFileUrl;
23154 }else if(this.url !== this._ccUrl){
23155 this._jsonFileUrl = this.url;
23156 this._ccUrl = this.url;
23157 }
23158
23159 //See if there was any forced reset of data.
23160 if(this.data != null && this._jsonData == null){
23161 this._jsonData = this.data;
23162 this.data = null;
23163 }
23164
23165 if(this._jsonFileUrl){
23166
23167 if(this._loadInProgress){
23168 this._queuedFetches.push({args: keywordArgs});
23169 }else{
23170 this._loadInProgress = true;
23171 var getArgs = {
23172 url: self._jsonFileUrl,
23173 handleAs: "json-comment-optional",
23174 preventCache: this.urlPreventCache,
23175 failOk: this.failOk
23176 };
23177 var getHandler = xhr.get(getArgs);
23178 getHandler.addCallback(function(data){
23179 var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23180 try{
23181 self._getItemsFromLoadedData(data);
23182 self._loadFinished = true;
23183 self._loadInProgress = false;
23184 item = self._getItemByIdentity(keywordArgs.identity);
23185 if(keywordArgs.onItem){
23186 keywordArgs.onItem.call(scope, item);
23187 }
23188 self._handleQueuedFetches();
23189 }catch(error){
23190 self._loadInProgress = false;
23191 if(keywordArgs.onError){
23192 keywordArgs.onError.call(scope, error);
23193 }
23194 }
23195 });
23196 getHandler.addErrback(function(error){
23197 self._loadInProgress = false;
23198 if(keywordArgs.onError){
23199 var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23200 keywordArgs.onError.call(scope, error);
23201 }
23202 });
23203 }
23204
23205 }else if(this._jsonData){
23206 // Passed in data, no need to xhr.
23207 self._getItemsFromLoadedData(self._jsonData);
23208 self._jsonData = null;
23209 self._loadFinished = true;
23210 item = self._getItemByIdentity(keywordArgs.identity);
23211 if(keywordArgs.onItem){
23212 scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23213 keywordArgs.onItem.call(scope, item);
23214 }
23215 }
23216 }else{
23217 // Already loaded. We can just look it up and call back.
23218 item = this._getItemByIdentity(keywordArgs.identity);
23219 if(keywordArgs.onItem){
23220 scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
23221 keywordArgs.onItem.call(scope, item);
23222 }
23223 }
23224 },
23225
23226 _getItemByIdentity: function(/* Object */ identity){
23227 // summary:
23228 // Internal function to look an item up by its identity map.
23229 var item = null;
23230 if(this._itemsByIdentity){
23231 // If this map is defined, we need to just try to get it. If it fails
23232 // the item does not exist.
23233 if(Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23234 item = this._itemsByIdentity[identity];
23235 }
23236 }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
23237 item = this._arrayOfAllItems[identity];
23238 }
23239 if(item === undefined){
23240 item = null;
23241 }
23242 return item; // Object
23243 },
23244
23245 getIdentityAttributes: function(/* dojo/data/api/Item */ item){
23246 // summary:
23247 // See dojo/data/api/Identity.getIdentityAttributes()
23248
23249 var identifier = this._features['dojo.data.api.Identity'];
23250 if(identifier === Number){
23251 // If (identifier === Number) it means getIdentity() just returns
23252 // an integer item-number for each item. The dojo/data/api/Identity
23253 // spec says we need to return null if the identity is not composed
23254 // of attributes
23255 return null; // null
23256 }else{
23257 return [identifier]; // Array
23258 }
23259 },
23260
23261 _forceLoad: function(){
23262 // summary:
23263 // Internal function to force a load of the store if it hasn't occurred yet. This is required
23264 // for specific functions to work properly.
23265 var self = this;
23266 //Do a check on the JsonFileUrl and crosscheck it.
23267 //If it doesn't match the cross-check, it needs to be updated
23268 //This allows for either url or _jsonFileUrl to he changed to
23269 //reset the store load location. Done this way for backwards
23270 //compatibility. People use _jsonFileUrl (even though officially
23271 //private.
23272 if(this._jsonFileUrl !== this._ccUrl){
23273 kernel.deprecated(this.declaredClass + ": ",
23274 "To change the url, set the url property of the store," +
23275 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23276 this._ccUrl = this._jsonFileUrl;
23277 this.url = this._jsonFileUrl;
23278 }else if(this.url !== this._ccUrl){
23279 this._jsonFileUrl = this.url;
23280 this._ccUrl = this.url;
23281 }
23282
23283 //See if there was any forced reset of data.
23284 if(this.data != null){
23285 this._jsonData = this.data;
23286 this.data = null;
23287 }
23288
23289 if(this._jsonFileUrl){
23290 var getArgs = {
23291 url: this._jsonFileUrl,
23292 handleAs: "json-comment-optional",
23293 preventCache: this.urlPreventCache,
23294 failOk: this.failOk,
23295 sync: true
23296 };
23297 var getHandler = xhr.get(getArgs);
23298 getHandler.addCallback(function(data){
23299 try{
23300 //Check to be sure there wasn't another load going on concurrently
23301 //So we don't clobber data that comes in on it. If there is a load going on
23302 //then do not save this data. It will potentially clobber current data.
23303 //We mainly wanted to sync/wait here.
23304 //TODO: Revisit the loading scheme of this store to improve multi-initial
23305 //request handling.
23306 if(self._loadInProgress !== true && !self._loadFinished){
23307 self._getItemsFromLoadedData(data);
23308 self._loadFinished = true;
23309 }else if(self._loadInProgress){
23310 //Okay, we hit an error state we can't recover from. A forced load occurred
23311 //while an async load was occurring. Since we cannot block at this point, the best
23312 //that can be managed is to throw an error.
23313 throw new Error(this.declaredClass + ": Unable to perform a synchronous load, an async load is in progress.");
23314 }
23315 }catch(e){
23316 console.log(e);
23317 throw e;
23318 }
23319 });
23320 getHandler.addErrback(function(error){
23321 throw error;
23322 });
23323 }else if(this._jsonData){
23324 self._getItemsFromLoadedData(self._jsonData);
23325 self._jsonData = null;
23326 self._loadFinished = true;
23327 }
23328 }
23329 });
23330 //Mix in the simple fetch implementation to this class.
23331 lang.extend(ItemFileReadStore,simpleFetch);
23332
23333 return ItemFileReadStore;
23334
23335 });
23336
23337 },
23338 'dojo/html':function(){
23339 define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
23340 function(kernel, lang, darray, declare, dom, domConstruct, parser){
23341 // module:
23342 // dojo/html
23343
23344 var html = {
23345 // summary:
23346 // TODOC
23347 };
23348 lang.setObject("dojo.html", html);
23349
23350 // the parser might be needed..
23351
23352 // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
23353 var idCounter = 0;
23354
23355 html._secureForInnerHtml = function(/*String*/ cont){
23356 // summary:
23357 // removes !DOCTYPE and title elements from the html string.
23358 //
23359 // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
23360 // must go into head, so we need to cut out those tags
23361 // cont:
23362 // An html string for insertion into the dom
23363 //
23364 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
23365 };
23366
23367 html._emptyNode = domConstruct.empty;
23368 /*=====
23369 dojo.html._emptyNode = function(node){
23370 // summary:
23371 // Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
23372 // instead.
23373 // node: DOMNode
23374 // the parent element
23375 };
23376 =====*/
23377
23378 html._setNodeContent = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont){
23379 // summary:
23380 // inserts the given content into the given node
23381 // node:
23382 // the parent element
23383 // content:
23384 // the content to be set on the parent element.
23385 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23386
23387 // always empty
23388 domConstruct.empty(node);
23389
23390 if(cont){
23391 if(typeof cont == "string"){
23392 cont = domConstruct.toDom(cont, node.ownerDocument);
23393 }
23394 if(!cont.nodeType && lang.isArrayLike(cont)){
23395 // handle as enumerable, but it may shrink as we enumerate it
23396 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0){
23397 domConstruct.place( cont[i], node, "last");
23398 }
23399 }else{
23400 // pass nodes, documentFragments and unknowns through to dojo.place
23401 domConstruct.place(cont, node, "last");
23402 }
23403 }
23404
23405 // return DomNode
23406 return node;
23407 };
23408
23409 // we wrap up the content-setting operation in a object
23410 html._ContentSetter = declare("dojo.html._ContentSetter", null,
23411 {
23412 // node: DomNode|String
23413 // An node which will be the parent element that we set content into
23414 node: "",
23415
23416 // content: String|DomNode|DomNode[]
23417 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
23418 content: "",
23419
23420 // id: String?
23421 // Usually only used internally, and auto-generated with each instance
23422 id: "",
23423
23424 // cleanContent: Boolean
23425 // Should the content be treated as a full html document,
23426 // and the real content stripped of <html>, <body> wrapper before injection
23427 cleanContent: false,
23428
23429 // extractContent: Boolean
23430 // Should the content be treated as a full html document,
23431 // and the real content stripped of `<html> <body>` wrapper before injection
23432 extractContent: false,
23433
23434 // parseContent: Boolean
23435 // Should the node by passed to the parser after the new content is set
23436 parseContent: false,
23437
23438 // parserScope: String
23439 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
23440 // will search for data-dojo-type (or dojoType). For backwards compatibility
23441 // reasons defaults to dojo._scopeName (which is "dojo" except when
23442 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
23443 parserScope: kernel._scopeName,
23444
23445 // startup: Boolean
23446 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
23447 startup: true,
23448
23449 // lifecycle methods
23450 constructor: function(/*Object*/ params, /*String|DomNode*/ node){
23451 // summary:
23452 // Provides a configurable, extensible object to wrap the setting on content on a node
23453 // call the set() method to actually set the content..
23454
23455 // the original params are mixed directly into the instance "this"
23456 lang.mixin(this, params || {});
23457
23458 // give precedence to params.node vs. the node argument
23459 // and ensure its a node, not an id string
23460 node = this.node = dom.byId( this.node || node );
23461
23462 if(!this.id){
23463 this.id = [
23464 "Setter",
23465 (node) ? node.id || node.tagName : "",
23466 idCounter++
23467 ].join("_");
23468 }
23469 },
23470 set: function(/* String|DomNode|NodeList? */ cont, /*Object?*/ params){
23471 // summary:
23472 // front-end to the set-content sequence
23473 // cont:
23474 // An html string, node or enumerable list of nodes for insertion into the dom
23475 // If not provided, the object's content property will be used
23476 if(undefined !== cont){
23477 this.content = cont;
23478 }
23479 // in the re-use scenario, set needs to be able to mixin new configuration
23480 if(params){
23481 this._mixin(params);
23482 }
23483
23484 this.onBegin();
23485 this.setContent();
23486
23487 var ret = this.onEnd();
23488
23489 if(ret && ret.then){
23490 // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
23491 return ret;
23492 }else{
23493 // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
23494 // return a Deferred like above.
23495 return this.node;
23496 }
23497 },
23498
23499 setContent: function(){
23500 // summary:
23501 // sets the content on the node
23502
23503 var node = this.node;
23504 if(!node){
23505 // can't proceed
23506 throw new Error(this.declaredClass + ": setContent given no node");
23507 }
23508 try{
23509 node = html._setNodeContent(node, this.content);
23510 }catch(e){
23511 // check if a domfault occurs when we are appending this.errorMessage
23512 // like for instance if domNode is a UL and we try append a DIV
23513
23514 // FIXME: need to allow the user to provide a content error message string
23515 var errMess = this.onContentError(e);
23516 try{
23517 node.innerHTML = errMess;
23518 }catch(e){
23519 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
23520 }
23521 }
23522 // always put back the node for the next method
23523 this.node = node; // DomNode
23524 },
23525
23526 empty: function(){
23527 // summary:
23528 // cleanly empty out existing content
23529
23530 // If there is a parse in progress, cancel it.
23531 if(this.parseDeferred){
23532 if(!this.parseDeferred.isResolved()){
23533 this.parseDeferred.cancel();
23534 }
23535 delete this.parseDeferred;
23536 }
23537
23538 // destroy any widgets from a previous run
23539 // NOTE: if you don't want this you'll need to empty
23540 // the parseResults array property yourself to avoid bad things happening
23541 if(this.parseResults && this.parseResults.length){
23542 darray.forEach(this.parseResults, function(w){
23543 if(w.destroy){
23544 w.destroy();
23545 }
23546 });
23547 delete this.parseResults;
23548 }
23549 // this is fast, but if you know its already empty or safe, you could
23550 // override empty to skip this step
23551 domConstruct.empty(this.node);
23552 },
23553
23554 onBegin: function(){
23555 // summary:
23556 // Called after instantiation, but before set();
23557 // It allows modification of any of the object properties -
23558 // including the node and content provided - before the set operation actually takes place
23559 // This default implementation checks for cleanContent and extractContent flags to
23560 // optionally pre-process html string content
23561 var cont = this.content;
23562
23563 if(lang.isString(cont)){
23564 if(this.cleanContent){
23565 cont = html._secureForInnerHtml(cont);
23566 }
23567
23568 if(this.extractContent){
23569 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
23570 if(match){ cont = match[1]; }
23571 }
23572 }
23573
23574 // clean out the node and any cruft associated with it - like widgets
23575 this.empty();
23576
23577 this.content = cont;
23578 return this.node; // DomNode
23579 },
23580
23581 onEnd: function(){
23582 // summary:
23583 // Called after set(), when the new content has been pushed into the node
23584 // It provides an opportunity for post-processing before handing back the node to the caller
23585 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
23586 if(this.parseContent){
23587 // populates this.parseResults and this.parseDeferred if you need those..
23588 this._parse();
23589 }
23590 return this.node; // DomNode
23591 // TODO: for 2.0 return a Promise indicating that the parse completed.
23592 },
23593
23594 tearDown: function(){
23595 // summary:
23596 // manually reset the Setter instance if its being re-used for example for another set()
23597 // description:
23598 // tearDown() is not called automatically.
23599 // In normal use, the Setter instance properties are simply allowed to fall out of scope
23600 // but the tearDown method can be called to explicitly reset this instance.
23601 delete this.parseResults;
23602 delete this.parseDeferred;
23603 delete this.node;
23604 delete this.content;
23605 },
23606
23607 onContentError: function(err){
23608 return "Error occurred setting content: " + err;
23609 },
23610
23611 onExecError: function(err){
23612 return "Error occurred executing scripts: " + err;
23613 },
23614
23615 _mixin: function(params){
23616 // mix properties/methods into the instance
23617 // TODO: the intention with tearDown is to put the Setter's state
23618 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
23619 // so we could do something here to move the original properties aside for later restoration
23620 var empty = {}, key;
23621 for(key in params){
23622 if(key in empty){ continue; }
23623 // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
23624 // .. but history shows we'll almost always guess wrong
23625 this[key] = params[key];
23626 }
23627 },
23628 _parse: function(){
23629 // summary:
23630 // runs the dojo parser over the node contents, storing any results in this.parseResults
23631 // and the parse promise in this.parseDeferred
23632 // Any errors resulting from parsing are passed to _onError for handling
23633
23634 var rootNode = this.node;
23635 try{
23636 // store the results (widgets, whatever) for potential retrieval
23637 var inherited = {};
23638 darray.forEach(["dir", "lang", "textDir"], function(name){
23639 if(this[name]){
23640 inherited[name] = this[name];
23641 }
23642 }, this);
23643 var self = this;
23644 this.parseDeferred = parser.parse({
23645 rootNode: rootNode,
23646 noStart: !this.startup,
23647 inherited: inherited,
23648 scope: this.parserScope
23649 }).then(function(results){
23650 return self.parseResults = results;
23651 });
23652 }catch(e){
23653 this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
23654 }
23655 },
23656
23657 _onError: function(type, err, consoleText){
23658 // summary:
23659 // shows user the string that is returned by on[type]Error
23660 // override/implement on[type]Error and return your own string to customize
23661 var errText = this['on' + type + 'Error'].call(this, err);
23662 if(consoleText){
23663 console.error(consoleText, err);
23664 }else if(errText){ // a empty string won't change current content
23665 html._setNodeContent(this.node, errText, true);
23666 }
23667 }
23668 }); // end declare()
23669
23670 html.set = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont, /*Object?*/ params){
23671 // summary:
23672 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
23673 // may be a better choice for simple HTML insertion.
23674 // description:
23675 // Unless you need to use the params capabilities of this method, you should use
23676 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
23677 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
23678 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
23679 // or the other capabilities as defined by the params object for this method.
23680 // node:
23681 // the parent element that will receive the content
23682 // cont:
23683 // the content to be set on the parent element.
23684 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23685 // params:
23686 // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
23687 // example:
23688 // A safe string/node/nodelist content replacement/injection with hooks for extension
23689 // Example Usage:
23690 // | html.set(node, "some string");
23691 // | html.set(node, contentNode, {options});
23692 // | html.set(node, myNode.childNodes, {options});
23693 if(undefined == cont){
23694 console.warn("dojo.html.set: no cont argument provided, using empty string");
23695 cont = "";
23696 }
23697 if(!params){
23698 // simple and fast
23699 return html._setNodeContent(node, cont, true);
23700 }else{
23701 // more options but slower
23702 // note the arguments are reversed in order, to match the convention for instantiation via the parser
23703 var op = new html._ContentSetter(lang.mixin(
23704 params,
23705 { content: cont, node: node }
23706 ));
23707 return op.set();
23708 }
23709 };
23710
23711 return html;
23712 });
23713
23714 },
23715 'dijit/_PaletteMixin':function(){
23716 define("dijit/_PaletteMixin", [
23717 "dojo/_base/declare", // declare
23718 "dojo/dom-attr", // domAttr.set
23719 "dojo/dom-class", // domClass.add domClass.remove
23720 "dojo/dom-construct", // domConstruct.create domConstruct.place
23721 "dojo/_base/event", // event.stop
23722 "dojo/keys", // keys
23723 "dojo/_base/lang", // lang.getObject
23724 "./_CssStateMixin",
23725 "./focus",
23726 "./typematic"
23727 ], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
23728
23729 // module:
23730 // dijit/_PaletteMixin
23731
23732 return declare("dijit._PaletteMixin", [_CssStateMixin], {
23733 // summary:
23734 // A keyboard accessible palette, for picking a color/emoticon/etc.
23735 // description:
23736 // A mixin for a grid showing various entities, so the user can pick a certain entity.
23737
23738 // defaultTimeout: Number
23739 // Number of milliseconds before a held key or button becomes typematic
23740 defaultTimeout: 500,
23741
23742 // timeoutChangeRate: Number
23743 // Fraction of time used to change the typematic timer between events
23744 // 1.0 means that each typematic event fires at defaultTimeout intervals
23745 // Less than 1.0 means that each typematic event fires at an increasing faster rate
23746 timeoutChangeRate: 0.90,
23747
23748 // value: String
23749 // Currently selected color/emoticon/etc.
23750 value: "",
23751
23752 // _selectedCell: [private] Integer
23753 // Index of the currently selected cell. Initially, none selected
23754 _selectedCell: -1,
23755
23756 /*=====
23757 // _currentFocus: [private] DomNode
23758 // The currently focused cell (if the palette itself has focus), or otherwise
23759 // the cell to be focused when the palette itself gets focus.
23760 // Different from value, which represents the selected (i.e. clicked) cell.
23761 _currentFocus: null,
23762 =====*/
23763
23764 /*=====
23765 // _xDim: [protected] Integer
23766 // This is the number of cells horizontally across.
23767 _xDim: null,
23768 =====*/
23769
23770 /*=====
23771 // _yDim: [protected] Integer
23772 // This is the number of cells vertically down.
23773 _yDim: null,
23774 =====*/
23775
23776 // tabIndex: String
23777 // Widget tab index.
23778 tabIndex: "0",
23779
23780 // cellClass: [protected] String
23781 // CSS class applied to each cell in the palette
23782 cellClass: "dijitPaletteCell",
23783
23784 // dyeClass: [protected] Constructor
23785 // Constructor for Object created for each cell of the palette.
23786 // dyeClass should implements dijit.Dye interface
23787 dyeClass: null,
23788
23789 // summary: String
23790 // Localized summary for the palette table
23791 summary: '',
23792 _setSummaryAttr: "paletteTableNode",
23793
23794 _dyeFactory: function(value /*===== , row, col, title =====*/){
23795 // summary:
23796 // Return instance of dijit.Dye for specified cell of palette
23797 // tags:
23798 // extension
23799
23800 // Remove string support for 2.0
23801 var dyeClassObj = typeof this.dyeClass == "string" ? lang.getObject(this.dyeClass) : this.dyeClass;
23802 return new dyeClassObj(value);
23803 },
23804
23805 _preparePalette: function(choices, titles) {
23806 // summary:
23807 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
23808 // for each cell
23809 // choices: String[][]
23810 // id's for each cell of the palette, used to create Dye JS object for each cell
23811 // titles: String[]
23812 // Localized tooltip for each cell
23813
23814 this._cells = [];
23815 var url = this._blankGif;
23816
23817 this.connect(this.gridNode, "ondijitclick", "_onCellClick");
23818
23819 for(var row=0; row < choices.length; row++){
23820 var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
23821 for(var col=0; col < choices[row].length; col++){
23822 var value = choices[row][col];
23823 if(value){
23824 var cellObject = this._dyeFactory(value, row, col, titles[value]);
23825
23826 var cellNode = domConstruct.create("td", {
23827 "class": this.cellClass,
23828 tabIndex: "-1",
23829 title: titles[value],
23830 role: "gridcell"
23831 }, rowNode);
23832
23833 // prepare cell inner structure
23834 cellObject.fillCell(cellNode, url);
23835
23836 cellNode.idx = this._cells.length;
23837
23838 // save cell info into _cells
23839 this._cells.push({node:cellNode, dye:cellObject});
23840 }
23841 }
23842 }
23843 this._xDim = choices[0].length;
23844 this._yDim = choices.length;
23845
23846 // Now set all events
23847 // The palette itself is navigated to with the tab key on the keyboard
23848 // Keyboard navigation within the Palette is with the arrow keys
23849 // Spacebar selects the cell.
23850 // For the up key the index is changed by negative the x dimension.
23851
23852 var keyIncrementMap = {
23853 UP_ARROW: -this._xDim,
23854 // The down key the index is increase by the x dimension.
23855 DOWN_ARROW: this._xDim,
23856 // Right and left move the index by 1.
23857 RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
23858 LEFT_ARROW: this.isLeftToRight() ? -1 : 1
23859 };
23860 for(var key in keyIncrementMap){
23861 this.own(
23862 typematic.addKeyListener(
23863 this.domNode,
23864 {charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
23865 this,
23866 function(){
23867 var increment = keyIncrementMap[key];
23868 return function(count){ this._navigateByKey(increment, count); };
23869 }(),
23870 this.timeoutChangeRate,
23871 this.defaultTimeout
23872 )
23873 );
23874 }
23875 },
23876
23877 postCreate: function(){
23878 this.inherited(arguments);
23879
23880 // Set initial navigable node.
23881 this._setCurrent(this._cells[0].node);
23882 },
23883
23884 focus: function(){
23885 // summary:
23886 // Focus this widget. Puts focus on the most recently focused cell.
23887
23888 // The cell already has tabIndex set, just need to set CSS and focus it
23889 focus.focus(this._currentFocus);
23890 },
23891
23892 _onCellClick: function(/*Event*/ evt){
23893 // summary:
23894 // Handler for click, enter key & space key. Selects the cell.
23895 // evt:
23896 // The event.
23897 // tags:
23898 // private
23899
23900 var target = evt.target;
23901
23902 // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
23903 while(target.tagName != "TD"){
23904 if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
23905 return;
23906 }
23907 target = target.parentNode;
23908 }
23909
23910 var value = this._getDye(target).getValue();
23911
23912 // First focus the clicked cell, and then send onChange() notification.
23913 // onChange() (via _setValueAttr) must be after the focus call, because
23914 // it may trigger a refocus to somewhere else (like the Editor content area), and that
23915 // second focus should win.
23916 this._setCurrent(target);
23917 focus.focus(target);
23918 this._setValueAttr(value, true);
23919
23920 event.stop(evt);
23921 },
23922
23923 _setCurrent: function(/*DomNode*/ node){
23924 // summary:
23925 // Sets which node is the focused cell.
23926 // description:
23927 // At any point in time there's exactly one
23928 // cell with tabIndex != -1. If focus is inside the palette then
23929 // focus is on that cell.
23930 //
23931 // After calling this method, arrow key handlers and mouse click handlers
23932 // should focus the cell in a setTimeout().
23933 // tags:
23934 // protected
23935 if("_currentFocus" in this){
23936 // Remove tabIndex on old cell
23937 domAttr.set(this._currentFocus, "tabIndex", "-1");
23938 }
23939
23940 // Set tabIndex of new cell
23941 this._currentFocus = node;
23942 if(node){
23943 domAttr.set(node, "tabIndex", this.tabIndex);
23944 }
23945 },
23946
23947 _setValueAttr: function(value, priorityChange){
23948 // summary:
23949 // This selects a cell. It triggers the onChange event.
23950 // value: String
23951 // Value of the cell to select
23952 // tags:
23953 // protected
23954 // priorityChange: Boolean?
23955 // Optional parameter used to tell the select whether or not to fire
23956 // onChange event.
23957
23958 // clear old selected cell
23959 if(this._selectedCell >= 0){
23960 domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
23961 }
23962 this._selectedCell = -1;
23963
23964 // search for cell matching specified value
23965 if(value){
23966 for(var i = 0; i < this._cells.length; i++){
23967 if(value == this._cells[i].dye.getValue()){
23968 this._selectedCell = i;
23969 domClass.add(this._cells[i].node, this.cellClass + "Selected");
23970 break;
23971 }
23972 }
23973 }
23974
23975 // record new value, or null if no matching cell
23976 this._set("value", this._selectedCell >= 0 ? value : null);
23977
23978 if(priorityChange || priorityChange === undefined){
23979 this.onChange(value);
23980 }
23981 },
23982
23983 onChange: function(/*===== value =====*/){
23984 // summary:
23985 // Callback when a cell is selected.
23986 // value: String
23987 // Value corresponding to cell.
23988 },
23989
23990 _navigateByKey: function(increment, typeCount){
23991 // summary:
23992 // This is the callback for typematic.
23993 // It changes the focus and the highlighed cell.
23994 // increment:
23995 // How much the key is navigated.
23996 // typeCount:
23997 // How many times typematic has fired.
23998 // tags:
23999 // private
24000
24001 // typecount == -1 means the key is released.
24002 if(typeCount == -1){ return; }
24003
24004 var newFocusIndex = this._currentFocus.idx + increment;
24005 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
24006 var focusNode = this._cells[newFocusIndex].node;
24007 this._setCurrent(focusNode);
24008
24009 // Actually focus the node, for the benefit of screen readers.
24010 // Use defer because IE doesn't like changing focus inside of an event handler
24011 this.defer(lang.hitch(focus, "focus", focusNode));
24012 }
24013 },
24014
24015 _getDye: function(/*DomNode*/ cell){
24016 // summary:
24017 // Get JS object for given cell DOMNode
24018
24019 return this._cells[cell.idx].dye;
24020 }
24021 });
24022
24023 /*=====
24024 declare("dijit.Dye",
24025 null,
24026 {
24027 // summary:
24028 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
24029
24030 constructor: function(alias, row, col){
24031 // summary:
24032 // Initialize according to value or alias like "white"
24033 // alias: String
24034 },
24035
24036 getValue: function(){
24037 // summary:
24038 // Return "value" of cell; meaning of "value" varies by subclass.
24039 // description:
24040 // For example color hex value, emoticon ascii value etc, entity hex value.
24041 },
24042
24043 fillCell: function(cell, blankGif){
24044 // summary:
24045 // Add cell DOMNode inner structure
24046 // cell: DomNode
24047 // The surrounding cell
24048 // blankGif: String
24049 // URL for blank cell image
24050 }
24051 }
24052 );
24053 =====*/
24054
24055 });
24056
24057 },
24058 'dijit/form/ValidationTextBox':function(){
24059 require({cache:{
24060 '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"}});
24061 define("dijit/form/ValidationTextBox", [
24062 "dojo/_base/declare", // declare
24063 "dojo/_base/kernel", // kernel.deprecated
24064 "dojo/i18n", // i18n.getLocalization
24065 "./TextBox",
24066 "../Tooltip",
24067 "dojo/text!./templates/ValidationTextBox.html",
24068 "dojo/i18n!./nls/validate"
24069 ], function(declare, kernel, i18n, TextBox, Tooltip, template){
24070
24071 // module:
24072 // dijit/form/ValidationTextBox
24073
24074
24075 /*=====
24076 var __Constraints = {
24077 // locale: String
24078 // locale used for validation, picks up value from this widget's lang attribute
24079 // _flags_: anything
24080 // various flags passed to pattern function
24081 };
24082 =====*/
24083
24084 var ValidationTextBox;
24085 return ValidationTextBox = declare("dijit.form.ValidationTextBox", TextBox, {
24086 // summary:
24087 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
24088
24089 templateString: template,
24090
24091 // required: Boolean
24092 // User is required to enter data into this field.
24093 required: false,
24094
24095 // promptMessage: String
24096 // If defined, display this hint string immediately on focus to the textbox, if empty.
24097 // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
24098 // Think of this like a tooltip that tells the user what to do, not an error message
24099 // that tells the user what they've done wrong.
24100 //
24101 // Message disappears when user starts typing.
24102 promptMessage: "",
24103
24104 // invalidMessage: String
24105 // The message to display if value is invalid.
24106 // The translated string value is read from the message file by default.
24107 // Set to "" to use the promptMessage instead.
24108 invalidMessage: "$_unset_$",
24109
24110 // missingMessage: String
24111 // The message to display if value is empty and the field is required.
24112 // The translated string value is read from the message file by default.
24113 // Set to "" to use the invalidMessage instead.
24114 missingMessage: "$_unset_$",
24115
24116 // message: String
24117 // Currently error/prompt message.
24118 // When using the default tooltip implementation, this will only be
24119 // displayed when the field is focused.
24120 message: "",
24121
24122 // constraints: __Constraints
24123 // user-defined object needed to pass parameters to the validator functions
24124 constraints: {},
24125
24126 // pattern: [extension protected] String|Function(constraints) returning a string.
24127 // This defines the regular expression used to validate the input.
24128 // Do not add leading ^ or $ characters since the widget adds these.
24129 // A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
24130 // set('pattern', String|Function).
24131 pattern: ".*",
24132
24133 // regExp: Deprecated [extension protected] String. Use "pattern" instead.
24134 regExp: "",
24135
24136 regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
24137 // summary:
24138 // Deprecated. Use set('pattern', Function) instead.
24139 },
24140
24141 // state: [readonly] String
24142 // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
24143 state: "",
24144
24145 // tooltipPosition: String[]
24146 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
24147 tooltipPosition: [],
24148
24149 _deprecateRegExp: function(attr, value){
24150 if(value != ValidationTextBox.prototype[attr]){
24151 kernel.deprecated("ValidationTextBox id="+this.id+", set('" + attr + "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0");
24152 this.set('pattern', value);
24153 }
24154 },
24155 _setRegExpGenAttr: function(/*Function*/ newFcn){
24156 this._deprecateRegExp("regExpGen", newFcn);
24157 this.regExpGen = this._getPatternAttr; // backward compat with this.regExpGen(this.constraints)
24158 },
24159 _setRegExpAttr: function(/*String*/ value){
24160 this._deprecateRegExp("regExp", value);
24161 },
24162
24163 _setValueAttr: function(){
24164 // summary:
24165 // Hook so set('value', ...) works.
24166 this.inherited(arguments);
24167 this.validate(this.focused);
24168 },
24169
24170 validator: function(/*anything*/ value, /*__Constraints*/ constraints){
24171 // summary:
24172 // Overridable function used to validate the text input against the regular expression.
24173 // tags:
24174 // protected
24175 return (new RegExp("^(?:" + this._getPatternAttr(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
24176 (!this.required || !this._isEmpty(value)) &&
24177 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
24178 },
24179
24180 _isValidSubset: function(){
24181 // summary:
24182 // Returns true if the value is either already valid or could be made valid by appending characters.
24183 // This is used for validation while the user [may be] still typing.
24184 return this.textbox.value.search(this._partialre) == 0;
24185 },
24186
24187 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
24188 // summary:
24189 // Tests if value is valid.
24190 // Can override with your own routine in a subclass.
24191 // tags:
24192 // protected
24193 return this.validator(this.textbox.value, this.constraints);
24194 },
24195
24196 _isEmpty: function(value){
24197 // summary:
24198 // Checks for whitespace
24199 return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
24200 },
24201
24202 getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24203 // summary:
24204 // Return an error message to show if appropriate
24205 // tags:
24206 // protected
24207 var invalid = this.invalidMessage == "$_unset_$" ? this.messages.invalidMessage :
24208 !this.invalidMessage ? this.promptMessage : this.invalidMessage;
24209 var missing = this.missingMessage == "$_unset_$" ? this.messages.missingMessage :
24210 !this.missingMessage ? invalid : this.missingMessage;
24211 return (this.required && this._isEmpty(this.textbox.value)) ? missing : invalid; // String
24212 },
24213
24214 getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24215 // summary:
24216 // Return a hint message to show when widget is first focused
24217 // tags:
24218 // protected
24219 return this.promptMessage; // String
24220 },
24221
24222 _maskValidSubsetError: true,
24223 validate: function(/*Boolean*/ isFocused){
24224 // summary:
24225 // Called by oninit, onblur, and onkeypress.
24226 // description:
24227 // Show missing or invalid messages if appropriate, and highlight textbox field.
24228 // tags:
24229 // protected
24230 var message = "";
24231 var isValid = this.disabled || this.isValid(isFocused);
24232 if(isValid){ this._maskValidSubsetError = true; }
24233 var isEmpty = this._isEmpty(this.textbox.value);
24234 var isValidSubset = !isValid && isFocused && this._isValidSubset();
24235 this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && (this._maskValidSubsetError || (isValidSubset && !this._hasBeenBlurred && isFocused))) ? "Incomplete" : "Error"));
24236 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
24237
24238 if(this.state == "Error"){
24239 this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
24240 message = this.getErrorMessage(isFocused);
24241 }else if(this.state == "Incomplete"){
24242 message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
24243 this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
24244 }else if(isEmpty){
24245 message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
24246 }
24247 this.set("message", message);
24248
24249 return isValid;
24250 },
24251
24252 displayMessage: function(/*String*/ message){
24253 // summary:
24254 // Overridable method to display validation errors/hints.
24255 // By default uses a tooltip.
24256 // tags:
24257 // extension
24258 if(message && this.focused){
24259 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
24260 }else{
24261 Tooltip.hide(this.domNode);
24262 }
24263 },
24264
24265 _refreshState: function(){
24266 // Overrides TextBox._refreshState()
24267 if(this._created){
24268 this.validate(this.focused);
24269 }
24270 this.inherited(arguments);
24271 },
24272
24273 //////////// INITIALIZATION METHODS ///////////////////////////////////////
24274
24275 constructor: function(params /*===== , srcNodeRef =====*/){
24276 // summary:
24277 // Create the widget.
24278 // params: Object|null
24279 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
24280 // and functions, typically callbacks like onClick.
24281 // The hash can contain any of the widget's properties, excluding read-only properties.
24282 // srcNodeRef: DOMNode|String?
24283 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
24284
24285 this.constraints = {};
24286 this.baseClass += ' dijitValidationTextBox';
24287 },
24288
24289 startup: function(){
24290 this.inherited(arguments);
24291 this._refreshState(); // after all _set* methods have run
24292 },
24293
24294 _setConstraintsAttr: function(/*__Constraints*/ constraints){
24295 if(!constraints.locale && this.lang){
24296 constraints.locale = this.lang;
24297 }
24298 this._set("constraints", constraints);
24299 this._refreshState();
24300 },
24301
24302 _setPatternAttr: function(/*String|Function*/ pattern){
24303 this._set("pattern", pattern); // don't set on INPUT to avoid native HTML5 validation
24304 },
24305
24306 _getPatternAttr: function(/*__Constraints*/ constraints){
24307 // summary:
24308 // Hook to get the current regExp and to compute the partial validation RE.
24309 var p = this.pattern;
24310 var type = (typeof p).toLowerCase();
24311 if(type == "function"){
24312 p = this.pattern(constraints || this.constraints);
24313 }
24314 if(p != this._lastRegExp){
24315 var partialre = "";
24316 this._lastRegExp = p;
24317 // parse the regexp and produce a new regexp that matches valid subsets
24318 // if the regexp is .* then there's no use in matching subsets since everything is valid
24319 if(p != ".*"){
24320 p.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
24321 function(re){
24322 switch(re.charAt(0)){
24323 case '{':
24324 case '+':
24325 case '?':
24326 case '*':
24327 case '^':
24328 case '$':
24329 case '|':
24330 case '(':
24331 partialre += re;
24332 break;
24333 case ")":
24334 partialre += "|$)";
24335 break;
24336 default:
24337 partialre += "(?:"+re+"|$)";
24338 break;
24339 }
24340 });
24341 }
24342 try{ // this is needed for now since the above regexp parsing needs more test verification
24343 "".search(partialre);
24344 }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
24345 partialre = this.pattern;
24346 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.pattern);
24347 } // should never be here unless the original RE is bad or the parsing is bad
24348 this._partialre = "^(?:" + partialre + ")$";
24349 }
24350 return p;
24351 },
24352
24353 postMixInProperties: function(){
24354 this.inherited(arguments);
24355 this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
24356 this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
24357 },
24358
24359 _setDisabledAttr: function(/*Boolean*/ value){
24360 this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
24361 this._refreshState();
24362 },
24363
24364 _setRequiredAttr: function(/*Boolean*/ value){
24365 this._set("required", value);
24366 this.focusNode.setAttribute("aria-required", value);
24367 this._refreshState();
24368 },
24369
24370 _setMessageAttr: function(/*String*/ message){
24371 this._set("message", message);
24372 this.displayMessage(message);
24373 },
24374
24375 reset:function(){
24376 // Overrides dijit/form/TextBox.reset() by also
24377 // hiding errors about partial matches
24378 this._maskValidSubsetError = true;
24379 this.inherited(arguments);
24380 },
24381
24382 _onBlur: function(){
24383 // the message still exists but for back-compat, and to erase the tooltip
24384 // (if the message is being displayed as a tooltip), call displayMessage('')
24385 this.displayMessage('');
24386
24387 this.inherited(arguments);
24388 }
24389 });
24390 });
24391
24392 },
24393 'dijit/_base/typematic':function(){
24394 define("dijit/_base/typematic", ["../typematic"], function(){
24395
24396 /*=====
24397 return {
24398 // summary:
24399 // Deprecated, for back-compat, just loads top level module
24400 };
24401 =====*/
24402
24403 });
24404
24405 },
24406 'dijit/layout/BorderContainer':function(){
24407 define("dijit/layout/BorderContainer", [
24408 "dojo/_base/array", // array.filter array.forEach array.map
24409 "dojo/cookie", // cookie
24410 "dojo/_base/declare", // declare
24411 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
24412 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
24413 "dojo/dom-geometry", // domGeometry.marginBox
24414 "dojo/dom-style", // domStyle.style
24415 "dojo/_base/event", // event.stop
24416 "dojo/keys",
24417 "dojo/_base/lang", // lang.getObject lang.hitch
24418 "dojo/on",
24419 "dojo/touch",
24420 "../_WidgetBase",
24421 "../_Widget",
24422 "../_TemplatedMixin",
24423 "./_LayoutWidget",
24424 "./utils" // layoutUtils.layoutChildren
24425 ], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch,
24426 _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
24427
24428 // module:
24429 // dijit/layout/BorderContainer
24430
24431 var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ],
24432 {
24433 // summary:
24434 // A draggable spacer between two items in a `dijit/layout/BorderContainer`.
24435 // description:
24436 // This is instantiated by `dijit/layout/BorderContainer`. Users should not
24437 // create it directly.
24438 // tags:
24439 // private
24440
24441 /*=====
24442 // container: [const] dijit/layout/BorderContainer
24443 // Pointer to the parent BorderContainer
24444 container: null,
24445
24446 // child: [const] dijit/layout/_LayoutWidget
24447 // Pointer to the pane associated with this splitter
24448 child: null,
24449
24450 // region: [const] String
24451 // Region of pane associated with this splitter.
24452 // "top", "bottom", "left", "right".
24453 region: null,
24454 =====*/
24455
24456 // live: [const] Boolean
24457 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
24458 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
24459 live: true,
24460
24461 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>',
24462
24463 constructor: function(){
24464 this._handlers = [];
24465 },
24466
24467 postMixInProperties: function(){
24468 this.inherited(arguments);
24469
24470 this.horizontal = /top|bottom/.test(this.region);
24471 this._factor = /top|left/.test(this.region) ? 1 : -1;
24472 this._cookieName = this.container.id + "_" + this.region;
24473 },
24474
24475 buildRendering: function(){
24476 this.inherited(arguments);
24477
24478 domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
24479
24480 if(this.container.persist){
24481 // restore old size
24482 var persistSize = cookie(this._cookieName);
24483 if(persistSize){
24484 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
24485 }
24486 }
24487 },
24488
24489 _computeMaxSize: function(){
24490 // summary:
24491 // Return the maximum size that my corresponding pane can be set to
24492
24493 var dim = this.horizontal ? 'h' : 'w',
24494 childSize = domGeometry.getMarginBox(this.child.domNode)[dim],
24495 center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
24496 spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0
24497
24498 return Math.min(this.child.maxSize, childSize + spaceAvailable);
24499 },
24500
24501 _startDrag: function(e){
24502 if(!this.cover){
24503 this.cover = domConstruct.place("<div class=dijitSplitterCover></div>", this.child.domNode, "after");
24504 }
24505 domClass.add(this.cover, "dijitSplitterCoverActive");
24506
24507 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
24508 if(this.fake){ domConstruct.destroy(this.fake); }
24509 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
24510 // create fake splitter to display at old position while we drag
24511 (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
24512 domClass.add(this.domNode, "dijitSplitterShadow");
24513 domConstruct.place(this.fake, this.domNode, "after");
24514 }
24515 domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
24516 if(this.fake){
24517 domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
24518 }
24519
24520 //Performance: load data info local vars for onmousevent function closure
24521 var factor = this._factor,
24522 isHorizontal = this.horizontal,
24523 axis = isHorizontal ? "pageY" : "pageX",
24524 pageStart = e[axis],
24525 splitterStyle = this.domNode.style,
24526 dim = isHorizontal ? 'h' : 'w',
24527 childStart = domGeometry.getMarginBox(this.child.domNode)[dim],
24528 max = this._computeMaxSize(),
24529 min = this.child.minSize || 20,
24530 region = this.region,
24531 splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
24532 splitterStart = parseInt(splitterStyle[splitterAttr], 10),
24533 resize = this._resize,
24534 layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id),
24535 de = this.ownerDocument;
24536
24537 this._handlers = this._handlers.concat([
24538 on(de, touch.move, this._drag = function(e, forceResize){
24539 var delta = e[axis] - pageStart,
24540 childSize = factor * delta + childStart,
24541 boundChildSize = Math.max(Math.min(childSize, max), min);
24542
24543 if(resize || forceResize){
24544 layoutFunc(boundChildSize);
24545 }
24546 // TODO: setting style directly (usually) sets content box size, need to set margin box size
24547 splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
24548 }),
24549 on(de, "dragstart", event.stop),
24550 on(this.ownerDocumentBody, "selectstart", event.stop),
24551 on(de, touch.release, lang.hitch(this, "_stopDrag"))
24552 ]);
24553 event.stop(e);
24554 },
24555
24556 _onMouse: function(e){
24557 // summary:
24558 // Handler for onmouseenter / onmouseleave events
24559 var o = (e.type == "mouseover" || e.type == "mouseenter");
24560 domClass.toggle(this.domNode, "dijitSplitterHover", o);
24561 domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
24562 },
24563
24564 _stopDrag: function(e){
24565 try{
24566 if(this.cover){
24567 domClass.remove(this.cover, "dijitSplitterCoverActive");
24568 }
24569 if(this.fake){ domConstruct.destroy(this.fake); }
24570 domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter"
24571 + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
24572 this._drag(e); //TODO: redundant with onmousemove?
24573 this._drag(e, true);
24574 }finally{
24575 this._cleanupHandlers();
24576 delete this._drag;
24577 }
24578
24579 if(this.container.persist){
24580 cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
24581 }
24582 },
24583
24584 _cleanupHandlers: function(){
24585 var h;
24586 while(h = this._handlers.pop()){ h.remove(); }
24587 },
24588
24589 _onKeyPress: function(/*Event*/ e){
24590 // should we apply typematic to this?
24591 this._resize = true;
24592 var horizontal = this.horizontal;
24593 var tick = 1;
24594 switch(e.charOrCode){
24595 case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW:
24596 tick *= -1;
24597 // break;
24598 case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW:
24599 break;
24600 default:
24601 // this.inherited(arguments);
24602 return;
24603 }
24604 var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
24605 this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
24606 event.stop(e);
24607 },
24608
24609 destroy: function(){
24610 this._cleanupHandlers();
24611 delete this.child;
24612 delete this.container;
24613 delete this.cover;
24614 delete this.fake;
24615 this.inherited(arguments);
24616 }
24617 });
24618
24619 var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin],
24620 {
24621 // summary:
24622 // Just a spacer div to separate side pane from center pane.
24623 // Basically a trick to lookup the gutter/splitter width from the theme.
24624 // description:
24625 // Instantiated by `dijit/layout/BorderContainer`. Users should not
24626 // create directly.
24627 // tags:
24628 // private
24629
24630 templateString: '<div class="dijitGutter" role="presentation"></div>',
24631
24632 postMixInProperties: function(){
24633 this.inherited(arguments);
24634 this.horizontal = /top|bottom/.test(this.region);
24635 },
24636
24637 buildRendering: function(){
24638 this.inherited(arguments);
24639 domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
24640 }
24641 });
24642
24643 var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, {
24644 // summary:
24645 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
24646 // description:
24647 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
24648 // that contains a child widget marked region="center" and optionally children widgets marked
24649 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
24650 // Children along the edges will be laid out according to width or height dimensions and may
24651 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
24652 // space is designated for the center region.
24653 //
24654 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
24655 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
24656 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
24657 // "left" and "right" except that they will be reversed in right-to-left environments.
24658 //
24659 // For complex layouts, multiple children can be specified for a single region. In this case, the
24660 // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
24661 // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
24662 // instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
24663 //
24664 // See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on
24665 // children of a `BorderContainer`.
24666 // example:
24667 // | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
24668 // | style="width: 400px; height: 300px;">
24669 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div>
24670 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
24671 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div>
24672 // | </div>
24673
24674 // design: String
24675 // Which design is used for the layout:
24676 //
24677 // - "headline" (default) where the top and bottom extend the full width of the container
24678 // - "sidebar" where the left and right sides extend from top to bottom.
24679 design: "headline",
24680
24681 // gutters: [const] Boolean
24682 // Give each pane a border and margin.
24683 // Margin determined by domNode.paddingLeft.
24684 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
24685 gutters: true,
24686
24687 // liveSplitters: [const] Boolean
24688 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
24689 liveSplitters: true,
24690
24691 // persist: Boolean
24692 // Save splitter positions in a cookie.
24693 persist: false,
24694
24695 baseClass: "dijitBorderContainer",
24696
24697 // _splitterClass: Function||String
24698 // Optional hook to override the default Splitter widget used by BorderContainer
24699 _splitterClass: _Splitter,
24700
24701 postMixInProperties: function(){
24702 // change class name to indicate that BorderContainer is being used purely for
24703 // layout (like LayoutContainer) rather than for pretty formatting.
24704 if(!this.gutters){
24705 this.baseClass += "NoGutter";
24706 }
24707 this.inherited(arguments);
24708 },
24709
24710 startup: function(){
24711 if(this._started){ return; }
24712 array.forEach(this.getChildren(), this._setupChild, this);
24713 this.inherited(arguments);
24714 },
24715
24716 _setupChild: function(/*dijit/_WidgetBase*/ child){
24717 // Override _LayoutWidget._setupChild().
24718
24719 var region = child.region;
24720 if(region){
24721 this.inherited(arguments);
24722
24723 domClass.add(child.domNode, this.baseClass+"Pane");
24724
24725 var ltr = this.isLeftToRight();
24726 if(region == "leading"){ region = ltr ? "left" : "right"; }
24727 if(region == "trailing"){ region = ltr ? "right" : "left"; }
24728
24729 // Create draggable splitter for resizing pane,
24730 // or alternately if splitter=false but BorderContainer.gutters=true then
24731 // insert dummy div just for spacing
24732 if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
24733 var _Splitter = child.splitter ? this._splitterClass : _Gutter;
24734 if(lang.isString(_Splitter)){
24735 _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
24736 }
24737 var splitter = new _Splitter({
24738 id: child.id + "_splitter",
24739 container: this,
24740 child: child,
24741 region: region,
24742 live: this.liveSplitters
24743 });
24744 splitter.isSplitter = true;
24745 child._splitterWidget = splitter;
24746
24747 domConstruct.place(splitter.domNode, child.domNode, "after");
24748
24749 // Splitters aren't added as Contained children, so we need to call startup explicitly
24750 splitter.startup();
24751 }
24752 child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
24753 }
24754 },
24755
24756 layout: function(){
24757 // Implement _LayoutWidget.layout() virtual method.
24758 this._layoutChildren();
24759 },
24760
24761 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
24762 // Override _LayoutWidget.addChild().
24763 this.inherited(arguments);
24764 if(this._started){
24765 this.layout(); //OPT
24766 }
24767 },
24768
24769 removeChild: function(/*dijit/_WidgetBase*/ child){
24770 // Override _LayoutWidget.removeChild().
24771
24772 var region = child.region;
24773 var splitter = child._splitterWidget;
24774 if(splitter){
24775 splitter.destroy();
24776 delete child._splitterWidget;
24777 }
24778 this.inherited(arguments);
24779
24780 if(this._started){
24781 this._layoutChildren();
24782 }
24783 // Clean up whatever style changes we made to the child pane.
24784 // Unclear how height and width should be handled.
24785 domClass.remove(child.domNode, this.baseClass+"Pane");
24786 domStyle.set(child.domNode, {
24787 top: "auto",
24788 bottom: "auto",
24789 left: "auto",
24790 right: "auto",
24791 position: "static"
24792 });
24793 domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
24794 },
24795
24796 getChildren: function(){
24797 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
24798 return array.filter(this.inherited(arguments), function(widget){
24799 return !widget.isSplitter;
24800 });
24801 },
24802
24803 // TODO: remove in 2.0
24804 getSplitter: function(/*String*/region){
24805 // summary:
24806 // Returns the widget responsible for rendering the splitter associated with region
24807 // tags:
24808 // deprecated
24809 return array.filter(this.getChildren(), function(child){
24810 return child.region == region;
24811 })[0]._splitterWidget;
24812 },
24813
24814 resize: function(newSize, currentSize){
24815 // Overrides _LayoutWidget.resize().
24816
24817 // resetting potential padding to 0px to provide support for 100% width/height + padding
24818 // TODO: this hack doesn't respect the box model and is a temporary fix
24819 if(!this.cs || !this.pe){
24820 var node = this.domNode;
24821 this.cs = domStyle.getComputedStyle(node);
24822 this.pe = domGeometry.getPadExtents(node, this.cs);
24823 this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight);
24824 this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom);
24825
24826 domStyle.set(node, "padding", "0px");
24827 }
24828
24829 this.inherited(arguments);
24830 },
24831
24832 _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
24833 // summary:
24834 // This is the main routine for setting size/position of each child.
24835 // description:
24836 // With no arguments, measures the height of top/bottom panes, the width
24837 // of left/right panes, and then sizes all panes accordingly.
24838 //
24839 // With changedRegion specified (as "left", "top", "bottom", or "right"),
24840 // it changes that region's width/height to changedRegionSize and
24841 // then resizes other regions that were affected.
24842 // changedChildId:
24843 // Id of the child which should be resized because splitter was dragged.
24844 // changedChildSize:
24845 // The new width/height (in pixels) to make specified child
24846
24847 if(!this._borderBox || !this._borderBox.h){
24848 // We are currently hidden, or we haven't been sized by our parent yet.
24849 // Abort. Someone will resize us later.
24850 return;
24851 }
24852
24853 // Generate list of wrappers of my children in the order that I want layoutChildren()
24854 // to process them (i.e. from the outside to the inside)
24855 var wrappers = array.map(this.getChildren(), function(child, idx){
24856 return {
24857 pane: child,
24858 weight: [
24859 child.region == "center" ? Infinity : 0,
24860 child.layoutPriority,
24861 (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
24862 idx
24863 ]
24864 };
24865 }, this);
24866 wrappers.sort(function(a, b){
24867 var aw = a.weight, bw = b.weight;
24868 for(var i=0; i<aw.length; i++){
24869 if(aw[i] != bw[i]){
24870 return aw[i] - bw[i];
24871 }
24872 }
24873 return 0;
24874 });
24875
24876 // Make new list, combining the externally specified children with splitters and gutters
24877 var childrenAndSplitters = [];
24878 array.forEach(wrappers, function(wrapper){
24879 var pane = wrapper.pane;
24880 childrenAndSplitters.push(pane);
24881 if(pane._splitterWidget){
24882 childrenAndSplitters.push(pane._splitterWidget);
24883 }
24884 });
24885
24886 // Compute the box in which to lay out my children
24887 var dim = {
24888 l: this.pe.l,
24889 t: this.pe.t,
24890 w: this._borderBox.w - this.pe.w,
24891 h: this._borderBox.h - this.pe.h
24892 };
24893
24894 // Layout the children, possibly changing size due to a splitter drag
24895 layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
24896 changedChildId, changedChildSize);
24897 },
24898
24899 destroyRecursive: function(){
24900 // Destroy splitters first, while getChildren() still works
24901 array.forEach(this.getChildren(), function(child){
24902 var splitter = child._splitterWidget;
24903 if(splitter){
24904 splitter.destroy();
24905 }
24906 delete child._splitterWidget;
24907 });
24908
24909 // Then destroy the real children, and myself
24910 this.inherited(arguments);
24911 }
24912 });
24913
24914 BorderContainer.ChildWidgetProperties = {
24915 // summary:
24916 // These properties can be specified for the children of a BorderContainer.
24917
24918 // region: [const] String
24919 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
24920 // See the `dijit/layout/BorderContainer` description for details.
24921 region: '',
24922
24923 // layoutPriority: [const] Number
24924 // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
24925 // between children with a lower layoutPriority.
24926 layoutPriority: 0,
24927
24928 // splitter: [const] Boolean
24929 // Parameter for children where region != "center".
24930 // If true, enables user to resize the widget by putting a draggable splitter between
24931 // this widget and the region=center widget.
24932 splitter: false,
24933
24934 // minSize: [const] Number
24935 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
24936 minSize: 0,
24937
24938 // maxSize: [const] Number
24939 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
24940 maxSize: Infinity
24941 };
24942
24943 // Since any widget can be specified as a LayoutContainer child, mix it
24944 // into the base widget class. (This is a hack, but it's effective.)
24945 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
24946 lang.extend(_WidgetBase, /*===== {} || =====*/ BorderContainer.ChildWidgetProperties);
24947
24948 // For monkey patching
24949 BorderContainer._Splitter = _Splitter;
24950 BorderContainer._Gutter = _Gutter;
24951
24952 return BorderContainer;
24953 });
24954
24955 },
24956 'dijit/_base':function(){
24957 define("dijit/_base", [
24958 "./main",
24959 "./a11y", // used to be in dijit/_base/manager
24960 "./WidgetSet", // used to be in dijit/_base/manager
24961 "./_base/focus",
24962 "./_base/manager",
24963 "./_base/place",
24964 "./_base/popup",
24965 "./_base/scroll",
24966 "./_base/sniff",
24967 "./_base/typematic",
24968 "./_base/wai",
24969 "./_base/window"
24970 ], function(dijit){
24971
24972 // module:
24973 // dijit/_base
24974
24975 /*=====
24976 return {
24977 // summary:
24978 // Includes all the modules in dijit/_base
24979 };
24980 =====*/
24981
24982 return dijit._base;
24983 });
24984
24985 },
24986 'dojo/window':function(){
24987 define("dojo/window", ["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
24988 function(lang, has, baseWindow, dom, geom, style){
24989
24990 // module:
24991 // dojo/window
24992
24993 var window = {
24994 // summary:
24995 // TODOC
24996
24997 getBox: function(/*Document?*/ doc){
24998 // summary:
24999 // Returns the dimensions and scroll position of the viewable area of a browser window
25000
25001 doc = doc || baseWindow.doc;
25002
25003 var
25004 scrollRoot = (doc.compatMode == 'BackCompat') ? baseWindow.body(doc) : doc.documentElement,
25005 // get scroll position
25006 scroll = geom.docScroll(doc), // scrollRoot.scrollTop/Left should work
25007 w, h;
25008
25009 if(has("touch")){ // if(scrollbars not supported)
25010 var uiWindow = window.get(doc); // use UI window, not dojo.global window
25011 // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
25012 w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated
25013 h = uiWindow.innerHeight || scrollRoot.clientHeight;
25014 }else{
25015 // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
25016 // uiWindow.innerWidth/Height includes the scrollbar and cannot be used
25017 w = scrollRoot.clientWidth;
25018 h = scrollRoot.clientHeight;
25019 }
25020 return {
25021 l: scroll.x,
25022 t: scroll.y,
25023 w: w,
25024 h: h
25025 };
25026 },
25027
25028 get: function(/*Document*/ doc){
25029 // summary:
25030 // Get window object associated with document doc.
25031 // doc:
25032 // The document to get the associated window for.
25033
25034 // In some IE versions (at least 6.0), document.parentWindow does not return a
25035 // reference to the real window object (maybe a copy), so we must fix it as well
25036 // We use IE specific execScript to attach the real window reference to
25037 // document._parentWindow for later use
25038 if(has("ie") && window !== document.parentWindow){
25039 /*
25040 In IE 6, only the variable "window" can be used to connect events (others
25041 may be only copies).
25042 */
25043 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
25044 //to prevent memory leak, unset it after use
25045 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
25046 var win = doc._parentWindow;
25047 doc._parentWindow = null;
25048 return win; // Window
25049 }
25050
25051 return doc.parentWindow || doc.defaultView; // Window
25052 },
25053
25054 scrollIntoView: function(/*DomNode*/ node, /*Object?*/ pos){
25055 // summary:
25056 // Scroll the passed node into view, if it is not already.
25057
25058 // don't rely on node.scrollIntoView working just because the function is there
25059
25060 try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
25061 node = dom.byId(node);
25062 var doc = node.ownerDocument || baseWindow.doc, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined?
25063 body = baseWindow.body(doc),
25064 html = doc.documentElement || body.parentNode,
25065 isIE = has("ie"), isWK = has("webkit");
25066 // if an untested browser, then use the native method
25067 if((!(has("mozilla") || isIE || isWK || has("opera")) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
25068 node.scrollIntoView(false); // short-circuit to native if possible
25069 return;
25070 }
25071 var backCompat = doc.compatMode == 'BackCompat',
25072 clientAreaRoot = (isIE >= 9 && "frameElement" in node.ownerDocument.parentWindow)
25073 ? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body)
25074 : (backCompat ? body : html),
25075 scrollRoot = isWK ? body : clientAreaRoot,
25076 rootWidth = clientAreaRoot.clientWidth,
25077 rootHeight = clientAreaRoot.clientHeight,
25078 rtl = !geom.isBodyLtr(doc),
25079 nodePos = pos || geom.position(node),
25080 el = node.parentNode,
25081 isFixed = function(el){
25082 return ((isIE <= 6 || (isIE && backCompat))? false : (style.get(el, 'position').toLowerCase() == "fixed"));
25083 };
25084 if(isFixed(node)){ return; } // nothing to do
25085
25086 while(el){
25087 if(el == body){ el = scrollRoot; }
25088 var elPos = geom.position(el),
25089 fixedPos = isFixed(el);
25090
25091 if(el == scrollRoot){
25092 elPos.w = rootWidth; elPos.h = rootHeight;
25093 if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
25094 if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
25095 if(elPos.y < 0 || !isIE){ elPos.y = 0; }
25096 }else{
25097 var pb = geom.getPadBorderExtents(el);
25098 elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
25099 var clientSize = el.clientWidth,
25100 scrollBarSize = elPos.w - clientSize;
25101 if(clientSize > 0 && scrollBarSize > 0){
25102 elPos.w = clientSize;
25103 elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0;
25104 }
25105 clientSize = el.clientHeight;
25106 scrollBarSize = elPos.h - clientSize;
25107 if(clientSize > 0 && scrollBarSize > 0){
25108 elPos.h = clientSize;
25109 }
25110 }
25111 if(fixedPos){ // bounded by viewport, not parents
25112 if(elPos.y < 0){
25113 elPos.h += elPos.y; elPos.y = 0;
25114 }
25115 if(elPos.x < 0){
25116 elPos.w += elPos.x; elPos.x = 0;
25117 }
25118 if(elPos.y + elPos.h > rootHeight){
25119 elPos.h = rootHeight - elPos.y;
25120 }
25121 if(elPos.x + elPos.w > rootWidth){
25122 elPos.w = rootWidth - elPos.x;
25123 }
25124 }
25125 // calculate overflow in all 4 directions
25126 var l = nodePos.x - elPos.x, // beyond left: < 0
25127 t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
25128 r = l + nodePos.w - elPos.w, // beyond right: > 0
25129 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
25130 if(r * l > 0){
25131 var s = Math[l < 0? "max" : "min"](l, r);
25132 if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
25133 nodePos.x += el.scrollLeft;
25134 el.scrollLeft += s;
25135 nodePos.x -= el.scrollLeft;
25136 }
25137 if(bot * t > 0){
25138 nodePos.y += el.scrollTop;
25139 el.scrollTop += Math[t < 0? "max" : "min"](t, bot);
25140 nodePos.y -= el.scrollTop;
25141 }
25142 el = (el != scrollRoot) && !fixedPos && el.parentNode;
25143 }
25144 }catch(error){
25145 console.error('scrollIntoView: ' + error);
25146 node.scrollIntoView(false);
25147 }
25148 }
25149 };
25150
25151 1 && lang.setObject("dojo.window", window);
25152
25153 return window;
25154 });
25155
25156 },
25157 'dojo/number':function(){
25158 define("dojo/number", [/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
25159 function(/*===== declare, =====*/ lang, i18n, nlsNumber, dstring, dregexp){
25160
25161 // module:
25162 // dojo/number
25163
25164 var number = {
25165 // summary:
25166 // localized formatting and parsing routines for Number
25167 };
25168 lang.setObject("dojo.number", number);
25169
25170 /*=====
25171 number.__FormatOptions = declare(null, {
25172 // pattern: String?
25173 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25174 // with this string. Default value is based on locale. Overriding this property will defeat
25175 // localization. Literal characters in patterns are not supported.
25176 // type: String?
25177 // choose a format type based on the locale from the following:
25178 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25179 // places: Number?
25180 // fixed number of decimal places to show. This overrides any
25181 // information in the provided pattern.
25182 // round: Number?
25183 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25184 // means do not round.
25185 // locale: String?
25186 // override the locale used to determine formatting rules
25187 // fractional: Boolean?
25188 // If false, show no decimal places, overriding places and pattern settings.
25189 });
25190 =====*/
25191
25192 number.format = function(/*Number*/ value, /*number.__FormatOptions?*/ options){
25193 // summary:
25194 // Format a Number as a String, using locale-specific settings
25195 // description:
25196 // Create a string from a Number using a known localized pattern.
25197 // Formatting patterns appropriate to the locale are chosen from the
25198 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
25199 // delimiters.
25200 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
25201 // value:
25202 // the number to be formatted
25203
25204 options = lang.mixin({}, options || {});
25205 var locale = i18n.normalizeLocale(options.locale),
25206 bundle = i18n.getLocalization("dojo.cldr", "number", locale);
25207 options.customs = bundle;
25208 var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
25209 if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
25210 return number._applyPattern(value, pattern, options); // String
25211 };
25212
25213 //number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
25214 number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
25215
25216 number._applyPattern = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatOptions?*/ options){
25217 // summary:
25218 // Apply pattern to format value as a string using options. Gives no
25219 // consideration to local customs.
25220 // value:
25221 // the number to be formatted.
25222 // pattern:
25223 // a pattern string as described by
25224 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25225 // options: number.__FormatOptions?
25226 // _applyPattern is usually called via `dojo/number.format()` which
25227 // populates an extra property in the options parameter, "customs".
25228 // The customs object specifies group and decimal parameters if set.
25229
25230 //TODO: support escapes
25231 options = options || {};
25232 var group = options.customs.group,
25233 decimal = options.customs.decimal,
25234 patternList = pattern.split(';'),
25235 positivePattern = patternList[0];
25236 pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
25237
25238 //TODO: only test against unescaped
25239 if(pattern.indexOf('%') != -1){
25240 value *= 100;
25241 }else if(pattern.indexOf('\u2030') != -1){
25242 value *= 1000; // per mille
25243 }else if(pattern.indexOf('\u00a4') != -1){
25244 group = options.customs.currencyGroup || group;//mixins instead?
25245 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
25246 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
25247 var prop = ["symbol", "currency", "displayName"][match.length-1];
25248 return options[prop] || options.currency || "";
25249 });
25250 }else if(pattern.indexOf('E') != -1){
25251 throw new Error("exponential notation not supported");
25252 }
25253
25254 //TODO: support @ sig figs?
25255 var numberPatternRE = number._numberPatternRE;
25256 var numberPattern = positivePattern.match(numberPatternRE);
25257 if(!numberPattern){
25258 throw new Error("unable to find a number expression in pattern: "+pattern);
25259 }
25260 if(options.fractional === false){ options.places = 0; }
25261 return pattern.replace(numberPatternRE,
25262 number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
25263 };
25264
25265 number.round = function(/*Number*/ value, /*Number?*/ places, /*Number?*/ increment){
25266 // summary:
25267 // Rounds to the nearest value with the given number of decimal places, away from zero
25268 // description:
25269 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
25270 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
25271 // fractional increments also, such as the nearest quarter.
25272 // NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround.
25273 // value:
25274 // The number to round
25275 // places:
25276 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
25277 // Must be non-negative.
25278 // increment:
25279 // Rounds next place to nearest value of increment/10. 10 by default.
25280 // example:
25281 // | >>> number.round(-0.5)
25282 // | -1
25283 // | >>> number.round(162.295, 2)
25284 // | 162.29 // note floating point error. Should be 162.3
25285 // | >>> number.round(10.71, 0, 2.5)
25286 // | 10.75
25287 var factor = 10 / (increment || 10);
25288 return (factor * +value).toFixed(places) / factor; // Number
25289 };
25290
25291 if((0.9).toFixed() == 0){
25292 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
25293 // is just after the rounding place and is >=5
25294 var round = number.round;
25295 number.round = function(v, p, m){
25296 var d = Math.pow(10, -p || 0), a = Math.abs(v);
25297 if(!v || a >= d){
25298 d = 0;
25299 }else{
25300 a /= d;
25301 if(a < 0.5 || a >= 0.95){
25302 d = 0;
25303 }
25304 }
25305 return round(v, p, m) + (v > 0 ? d : -d);
25306 };
25307
25308 // Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above.
25309 /*===== number.round = round; =====*/
25310 }
25311
25312 /*=====
25313 number.__FormatAbsoluteOptions = declare(null, {
25314 // decimal: String?
25315 // the decimal separator
25316 // group: String?
25317 // the group separator
25318 // places: Number|String?
25319 // number of decimal places. the range "n,m" will format to m places.
25320 // round: Number?
25321 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25322 // means don't round.
25323 });
25324 =====*/
25325
25326 number._formatAbsolute = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatAbsoluteOptions?*/ options){
25327 // summary:
25328 // Apply numeric pattern to absolute value using options. Gives no
25329 // consideration to local customs.
25330 // value:
25331 // the number to be formatted, ignores sign
25332 // pattern:
25333 // the number portion of a pattern (e.g. `#,##0.00`)
25334 options = options || {};
25335 if(options.places === true){options.places=0;}
25336 if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
25337
25338 var patternParts = pattern.split("."),
25339 comma = typeof options.places == "string" && options.places.indexOf(","),
25340 maxPlaces = options.places;
25341 if(comma){
25342 maxPlaces = options.places.substring(comma + 1);
25343 }else if(!(maxPlaces >= 0)){
25344 maxPlaces = (patternParts[1] || []).length;
25345 }
25346 if(!(options.round < 0)){
25347 value = number.round(value, maxPlaces, options.round);
25348 }
25349
25350 var valueParts = String(Math.abs(value)).split("."),
25351 fractional = valueParts[1] || "";
25352 if(patternParts[1] || options.places){
25353 if(comma){
25354 options.places = options.places.substring(0, comma);
25355 }
25356 // Pad fractional with trailing zeros
25357 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
25358 if(pad > fractional.length){
25359 valueParts[1] = dstring.pad(fractional, pad, '0', true);
25360 }
25361
25362 // Truncate fractional
25363 if(maxPlaces < fractional.length){
25364 valueParts[1] = fractional.substr(0, maxPlaces);
25365 }
25366 }else{
25367 if(valueParts[1]){ valueParts.pop(); }
25368 }
25369
25370 // Pad whole with leading zeros
25371 var patternDigits = patternParts[0].replace(',', '');
25372 pad = patternDigits.indexOf("0");
25373 if(pad != -1){
25374 pad = patternDigits.length - pad;
25375 if(pad > valueParts[0].length){
25376 valueParts[0] = dstring.pad(valueParts[0], pad);
25377 }
25378
25379 // Truncate whole
25380 if(patternDigits.indexOf("#") == -1){
25381 valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
25382 }
25383 }
25384
25385 // Add group separators
25386 var index = patternParts[0].lastIndexOf(','),
25387 groupSize, groupSize2;
25388 if(index != -1){
25389 groupSize = patternParts[0].length - index - 1;
25390 var remainder = patternParts[0].substr(0, index);
25391 index = remainder.lastIndexOf(',');
25392 if(index != -1){
25393 groupSize2 = remainder.length - index - 1;
25394 }
25395 }
25396 var pieces = [];
25397 for(var whole = valueParts[0]; whole;){
25398 var off = whole.length - groupSize;
25399 pieces.push((off > 0) ? whole.substr(off) : whole);
25400 whole = (off > 0) ? whole.slice(0, off) : "";
25401 if(groupSize2){
25402 groupSize = groupSize2;
25403 delete groupSize2;
25404 }
25405 }
25406 valueParts[0] = pieces.reverse().join(options.group || ",");
25407
25408 return valueParts.join(options.decimal || ".");
25409 };
25410
25411 /*=====
25412 number.__RegexpOptions = declare(null, {
25413 // pattern: String?
25414 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25415 // with this string. Default value is based on locale. Overriding this property will defeat
25416 // localization.
25417 // type: String?
25418 // choose a format type based on the locale from the following:
25419 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25420 // locale: String?
25421 // override the locale used to determine formatting rules
25422 // strict: Boolean?
25423 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25424 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25425 // places: Number|String?
25426 // number of decimal places to accept: Infinity, a positive number, or
25427 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
25428 });
25429 =====*/
25430 number.regexp = function(/*number.__RegexpOptions?*/ options){
25431 // summary:
25432 // Builds the regular needed to parse a number
25433 // description:
25434 // Returns regular expression with positive and negative match, group
25435 // and decimal separators
25436 return number._parseInfo(options).regexp; // String
25437 };
25438
25439 number._parseInfo = function(/*Object?*/ options){
25440 options = options || {};
25441 var locale = i18n.normalizeLocale(options.locale),
25442 bundle = i18n.getLocalization("dojo.cldr", "number", locale),
25443 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
25444 //TODO: memoize?
25445 group = bundle.group,
25446 decimal = bundle.decimal,
25447 factor = 1;
25448
25449 if(pattern.indexOf('%') != -1){
25450 factor /= 100;
25451 }else if(pattern.indexOf('\u2030') != -1){
25452 factor /= 1000; // per mille
25453 }else{
25454 var isCurrency = pattern.indexOf('\u00a4') != -1;
25455 if(isCurrency){
25456 group = bundle.currencyGroup || group;
25457 decimal = bundle.currencyDecimal || decimal;
25458 }
25459 }
25460
25461 //TODO: handle quoted escapes
25462 var patternList = pattern.split(';');
25463 if(patternList.length == 1){
25464 patternList.push("-" + patternList[0]);
25465 }
25466
25467 var re = dregexp.buildGroupRE(patternList, function(pattern){
25468 pattern = "(?:"+dregexp.escapeString(pattern, '.')+")";
25469 return pattern.replace(number._numberPatternRE, function(format){
25470 var flags = {
25471 signed: false,
25472 separator: options.strict ? group : [group,""],
25473 fractional: options.fractional,
25474 decimal: decimal,
25475 exponent: false
25476 },
25477
25478 parts = format.split('.'),
25479 places = options.places;
25480
25481 // special condition for percent (factor != 1)
25482 // allow decimal places even if not specified in pattern
25483 if(parts.length == 1 && factor != 1){
25484 parts[1] = "###";
25485 }
25486 if(parts.length == 1 || places === 0){
25487 flags.fractional = false;
25488 }else{
25489 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
25490 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
25491 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
25492 flags.places = places;
25493 }
25494 var groups = parts[0].split(',');
25495 if(groups.length > 1){
25496 flags.groupSize = groups.pop().length;
25497 if(groups.length > 1){
25498 flags.groupSize2 = groups.pop().length;
25499 }
25500 }
25501 return "("+number._realNumberRegexp(flags)+")";
25502 });
25503 }, true);
25504
25505 if(isCurrency){
25506 // substitute the currency symbol for the placeholder in the pattern
25507 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
25508 var prop = ["symbol", "currency", "displayName"][target.length-1],
25509 symbol = dregexp.escapeString(options[prop] || options.currency || "");
25510 before = before ? "[\\s\\xa0]" : "";
25511 after = after ? "[\\s\\xa0]" : "";
25512 if(!options.strict){
25513 if(before){before += "*";}
25514 if(after){after += "*";}
25515 return "(?:"+before+symbol+after+")?";
25516 }
25517 return before+symbol+after;
25518 });
25519 }
25520
25521 //TODO: substitute localized sign/percent/permille/etc.?
25522
25523 // normalize whitespace and return
25524 return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
25525 };
25526
25527 /*=====
25528 number.__ParseOptions = declare(null, {
25529 // pattern: String?
25530 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25531 // with this string. Default value is based on locale. Overriding this property will defeat
25532 // localization. Literal characters in patterns are not supported.
25533 // type: String?
25534 // choose a format type based on the locale from the following:
25535 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25536 // locale: String?
25537 // override the locale used to determine formatting rules
25538 // strict: Boolean?
25539 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25540 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25541 // fractional: Boolean|Array?
25542 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
25543 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
25544 });
25545 =====*/
25546 number.parse = function(/*String*/ expression, /*number.__ParseOptions?*/ options){
25547 // summary:
25548 // Convert a properly formatted string to a primitive Number, using
25549 // locale-specific settings.
25550 // description:
25551 // Create a Number from a string using a known localized pattern.
25552 // Formatting patterns are chosen appropriate to the locale
25553 // and follow the syntax described by
25554 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25555 // Note that literal characters in patterns are not supported.
25556 // expression:
25557 // A string representation of a Number
25558 var info = number._parseInfo(options),
25559 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
25560 if(!results){
25561 return NaN; //NaN
25562 }
25563 var absoluteMatch = results[1]; // match for the positive expression
25564 if(!results[1]){
25565 if(!results[2]){
25566 return NaN; //NaN
25567 }
25568 // matched the negative pattern
25569 absoluteMatch =results[2];
25570 info.factor *= -1;
25571 }
25572
25573 // Transform it to something Javascript can parse as a number. Normalize
25574 // decimal point and strip out group separators or alternate forms of whitespace
25575 absoluteMatch = absoluteMatch.
25576 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
25577 replace(info.decimal, ".");
25578 // Adjust for negative sign, percent, etc. as necessary
25579 return absoluteMatch * info.factor; //Number
25580 };
25581
25582 /*=====
25583 number.__RealNumberRegexpFlags = declare(null, {
25584 // places: Number?
25585 // The integer number of decimal places or a range given as "n,m". If
25586 // not given, the decimal part is optional and the number of places is
25587 // unlimited.
25588 // decimal: String?
25589 // A string for the character used as the decimal point. Default
25590 // is ".".
25591 // fractional: Boolean|Array?
25592 // Whether decimal places are used. Can be true, false, or [true,
25593 // false]. Default is [true, false] which means optional.
25594 // exponent: Boolean|Array?
25595 // Express in exponential notation. Can be true, false, or [true,
25596 // false]. Default is [true, false], (i.e. will match if the
25597 // exponential part is present are not).
25598 // eSigned: Boolean|Array?
25599 // The leading plus-or-minus sign on the exponent. Can be true,
25600 // false, or [true, false]. Default is [true, false], (i.e. will
25601 // match if it is signed or unsigned). flags in regexp.integer can be
25602 // applied.
25603 });
25604 =====*/
25605
25606 number._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags){
25607 // summary:
25608 // Builds a regular expression to match a real number in exponential
25609 // notation
25610
25611 // assign default values to missing parameters
25612 flags = flags || {};
25613 //TODO: use mixin instead?
25614 if(!("places" in flags)){ flags.places = Infinity; }
25615 if(typeof flags.decimal != "string"){ flags.decimal = "."; }
25616 if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
25617 if(!("exponent" in flags)){ flags.exponent = [true, false]; }
25618 if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
25619
25620 var integerRE = number._integerRegexp(flags),
25621 decimalRE = dregexp.buildGroupRE(flags.fractional,
25622 function(q){
25623 var re = "";
25624 if(q && (flags.places!==0)){
25625 re = "\\" + flags.decimal;
25626 if(flags.places == Infinity){
25627 re = "(?:" + re + "\\d+)?";
25628 }else{
25629 re += "\\d{" + flags.places + "}";
25630 }
25631 }
25632 return re;
25633 },
25634 true
25635 );
25636
25637 var exponentRE = dregexp.buildGroupRE(flags.exponent,
25638 function(q){
25639 if(q){ return "([eE]" + number._integerRegexp({ signed: flags.eSigned}) + ")"; }
25640 return "";
25641 }
25642 );
25643
25644 var realRE = integerRE + decimalRE;
25645 // allow for decimals without integers, e.g. .25
25646 if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
25647 return realRE + exponentRE; // String
25648 };
25649
25650 /*=====
25651 number.__IntegerRegexpFlags = declare(null, {
25652 // signed: Boolean?
25653 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
25654 // Default is `[true, false]`, (i.e. will match if it is signed
25655 // or unsigned).
25656 // separator: String?
25657 // The character used as the thousands separator. Default is no
25658 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
25659 // makes ',' optional.
25660 // groupSize: Number?
25661 // group size between separators
25662 // groupSize2: Number?
25663 // second grouping, where separators 2..n have a different interval than the first separator (for India)
25664 });
25665 =====*/
25666
25667 number._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags){
25668 // summary:
25669 // Builds a regular expression that matches an integer
25670
25671 // assign default values to missing parameters
25672 flags = flags || {};
25673 if(!("signed" in flags)){ flags.signed = [true, false]; }
25674 if(!("separator" in flags)){
25675 flags.separator = "";
25676 }else if(!("groupSize" in flags)){
25677 flags.groupSize = 3;
25678 }
25679
25680 var signRE = dregexp.buildGroupRE(flags.signed,
25681 function(q){ return q ? "[-+]" : ""; },
25682 true
25683 );
25684
25685 var numberRE = dregexp.buildGroupRE(flags.separator,
25686 function(sep){
25687 if(!sep){
25688 return "(?:\\d+)";
25689 }
25690
25691 sep = dregexp.escapeString(sep);
25692 if(sep == " "){ sep = "\\s"; }
25693 else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
25694
25695 var grp = flags.groupSize, grp2 = flags.groupSize2;
25696 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
25697 if(grp2){
25698 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
25699 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
25700 }
25701 return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
25702 },
25703 true
25704 );
25705
25706 return signRE + numberRE; // String
25707 };
25708
25709 return number;
25710 });
25711
25712 },
25713 'dijit/_FocusMixin':function(){
25714 define("dijit/_FocusMixin", [
25715 "./focus",
25716 "./_WidgetBase",
25717 "dojo/_base/declare", // declare
25718 "dojo/_base/lang" // lang.extend
25719 ], function(focus, _WidgetBase, declare, lang){
25720
25721 // module:
25722 // dijit/_FocusMixin
25723
25724 // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
25725 // to be last in the inheritance chain, so mixin to _WidgetBase.
25726 lang.extend(_WidgetBase, {
25727 // focused: [readonly] Boolean
25728 // This widget or a widget it contains has focus, or is "active" because
25729 // it was recently clicked.
25730 focused: false,
25731
25732 onFocus: function(){
25733 // summary:
25734 // Called when the widget becomes "active" because
25735 // it or a widget inside of it either has focus, or has recently
25736 // been clicked.
25737 // tags:
25738 // callback
25739 },
25740
25741 onBlur: function(){
25742 // summary:
25743 // Called when the widget stops being "active" because
25744 // focus moved to something outside of it, or the user
25745 // clicked somewhere outside of it, or the widget was
25746 // hidden.
25747 // tags:
25748 // callback
25749 },
25750
25751 _onFocus: function(){
25752 // summary:
25753 // This is where widgets do processing for when they are active,
25754 // such as changing CSS classes. See onFocus() for more details.
25755 // tags:
25756 // protected
25757 this.onFocus();
25758 },
25759
25760 _onBlur: function(){
25761 // summary:
25762 // This is where widgets do processing for when they stop being active,
25763 // such as changing CSS classes. See onBlur() for more details.
25764 // tags:
25765 // protected
25766 this.onBlur();
25767 }
25768 });
25769
25770 return declare("dijit._FocusMixin", null, {
25771 // summary:
25772 // Mixin to widget to provide _onFocus() and _onBlur() methods that
25773 // fire when a widget or its descendants get/lose focus
25774
25775 // flag that I want _onFocus()/_onBlur() notifications from focus manager
25776 _focusManager: focus
25777 });
25778
25779 });
25780
25781 },
25782 'dojo/data/util/filter':function(){
25783 define("dojo/data/util/filter", ["../../_base/lang"], function(lang){
25784 // module:
25785 // dojo/data/util/filter
25786 // summary:
25787 // TODOC
25788
25789 var filter = {};
25790 lang.setObject("dojo.data.util.filter", filter);
25791
25792 filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
25793 // summary:
25794 // Helper function to convert a simple pattern to a regular expression for matching.
25795 // description:
25796 // Returns a regular expression object that conforms to the defined conversion rules.
25797 // For example:
25798 //
25799 // - ca* -> /^ca.*$/
25800 // - *ca* -> /^.*ca.*$/
25801 // - *c\*a* -> /^.*c\*a.*$/
25802 // - *c\*a?* -> /^.*c\*a..*$/
25803 //
25804 // and so on.
25805 // pattern: string
25806 // A simple matching pattern to convert that follows basic rules:
25807 //
25808 // - * Means match anything, so ca* means match anything starting with ca
25809 // - ? Means match single character. So, b?b will match to bob and bab, and so on.
25810 // - \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
25811 //
25812 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
25813 // represented by \\ to be treated as an ordinary \ character instead of an escape.
25814 // ignoreCase:
25815 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
25816 // By default, it is assumed case sensitive.
25817
25818 var rxp = "^";
25819 var c = null;
25820 for(var i = 0; i < pattern.length; i++){
25821 c = pattern.charAt(i);
25822 switch(c){
25823 case '\\':
25824 rxp += c;
25825 i++;
25826 rxp += pattern.charAt(i);
25827 break;
25828 case '*':
25829 rxp += ".*"; break;
25830 case '?':
25831 rxp += "."; break;
25832 case '$':
25833 case '^':
25834 case '/':
25835 case '+':
25836 case '.':
25837 case '|':
25838 case '(':
25839 case ')':
25840 case '{':
25841 case '}':
25842 case '[':
25843 case ']':
25844 rxp += "\\"; //fallthrough
25845 default:
25846 rxp += c;
25847 }
25848 }
25849 rxp += "$";
25850 if(ignoreCase){
25851 return new RegExp(rxp,"mi"); //RegExp
25852 }else{
25853 return new RegExp(rxp,"m"); //RegExp
25854 }
25855
25856 };
25857
25858 return filter;
25859 });
25860
25861 },
25862 'dijit/_WidgetsInTemplateMixin':function(){
25863 define("dijit/_WidgetsInTemplateMixin", [
25864 "dojo/_base/array", // array.forEach
25865 "dojo/_base/declare", // declare
25866 "dojo/parser" // parser.parse
25867 ], function(array, declare, parser){
25868
25869 // module:
25870 // dijit/_WidgetsInTemplateMixin
25871
25872 return declare("dijit._WidgetsInTemplateMixin", null, {
25873 // summary:
25874 // Mixin to supplement _TemplatedMixin when template contains widgets
25875
25876 // _earlyTemplatedStartup: Boolean
25877 // A fallback to preserve the 1.0 - 1.3 behavior of children in
25878 // templates having their startup called before the parent widget
25879 // fires postCreate. Defaults to 'false', causing child widgets to
25880 // have their .startup() called immediately before a parent widget
25881 // .startup(), but always after the parent .postCreate(). Set to
25882 // 'true' to re-enable to previous, arguably broken, behavior.
25883 _earlyTemplatedStartup: false,
25884
25885 // widgetsInTemplate: [protected] Boolean
25886 // Should we parse the template to find widgets that might be
25887 // declared in markup inside it? (Remove for 2.0 and assume true)
25888 widgetsInTemplate: true,
25889
25890 _beforeFillContent: function(){
25891 if(this.widgetsInTemplate){
25892 // Before copying over content, instantiate widgets in template
25893 var node = this.domNode;
25894
25895 var cw = (this._startupWidgets = parser.parse(node, {
25896 noStart: !this._earlyTemplatedStartup,
25897 template: true,
25898 inherited: {dir: this.dir, lang: this.lang, textDir: this.textDir},
25899 propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
25900 scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
25901 }));
25902
25903 if(!cw.isFulfilled()){
25904 throw new Error(this.declaredClass + ": parser returned unfilled promise (probably waiting for module auto-load), " +
25905 "unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.");
25906 }
25907
25908 // _WidgetBase::destroy() will destroy any supporting widgets under this.domNode.
25909 // If we wanted to, we could call this.own() on anything in this._startupWidgets that was moved outside
25910 // of this.domNode (like Dialog, which is moved to <body>).
25911
25912 this._attachTemplateNodes(cw, function(n,p){
25913 return n[p];
25914 });
25915 }
25916 },
25917
25918 startup: function(){
25919 array.forEach(this._startupWidgets, function(w){
25920 if(w && !w._started && w.startup){
25921 w.startup();
25922 }
25923 });
25924 this.inherited(arguments);
25925 }
25926 });
25927 });
25928
25929 },
25930 'dojo/fx/Toggler':function(){
25931 define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
25932 function(lang, declare, baseFx, connectUtil){
25933 // module:
25934 // dojo/fx/Toggler
25935
25936 return declare("dojo.fx.Toggler", null, {
25937 // summary:
25938 // A simple `dojo.Animation` toggler API.
25939 // description:
25940 // class constructor for an animation toggler. It accepts a packed
25941 // set of arguments about what type of animation to use in each
25942 // direction, duration, etc. All available members are mixed into
25943 // these animations from the constructor (for example, `node`,
25944 // `showDuration`, `hideDuration`).
25945 // example:
25946 // | var t = new dojo/fx/Toggler({
25947 // | node: "nodeId",
25948 // | showDuration: 500,
25949 // | // hideDuration will default to "200"
25950 // | showFunc: dojo/fx/wipeIn,
25951 // | // hideFunc will default to "fadeOut"
25952 // | });
25953 // | t.show(100); // delay showing for 100ms
25954 // | // ...time passes...
25955 // | t.hide();
25956
25957 // node: DomNode
25958 // the node to target for the showing and hiding animations
25959 node: null,
25960
25961 // showFunc: Function
25962 // The function that returns the `dojo.Animation` to show the node
25963 showFunc: baseFx.fadeIn,
25964
25965 // hideFunc: Function
25966 // The function that returns the `dojo.Animation` to hide the node
25967 hideFunc: baseFx.fadeOut,
25968
25969 // showDuration:
25970 // Time in milliseconds to run the show Animation
25971 showDuration: 200,
25972
25973 // hideDuration:
25974 // Time in milliseconds to run the hide Animation
25975 hideDuration: 200,
25976
25977 // FIXME: need a policy for where the toggler should "be" the next
25978 // time show/hide are called if we're stopped somewhere in the
25979 // middle.
25980 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
25981 // each animation individually.
25982 // FIXME: also would be nice to have events from the animations exposed/bridged
25983
25984 /*=====
25985 _showArgs: null,
25986 _showAnim: null,
25987
25988 _hideArgs: null,
25989 _hideAnim: null,
25990
25991 _isShowing: false,
25992 _isHiding: false,
25993 =====*/
25994
25995 constructor: function(args){
25996 var _t = this;
25997
25998 lang.mixin(_t, args);
25999 _t.node = args.node;
26000 _t._showArgs = lang.mixin({}, args);
26001 _t._showArgs.node = _t.node;
26002 _t._showArgs.duration = _t.showDuration;
26003 _t.showAnim = _t.showFunc(_t._showArgs);
26004
26005 _t._hideArgs = lang.mixin({}, args);
26006 _t._hideArgs.node = _t.node;
26007 _t._hideArgs.duration = _t.hideDuration;
26008 _t.hideAnim = _t.hideFunc(_t._hideArgs);
26009
26010 connectUtil.connect(_t.showAnim, "beforeBegin", lang.hitch(_t.hideAnim, "stop", true));
26011 connectUtil.connect(_t.hideAnim, "beforeBegin", lang.hitch(_t.showAnim, "stop", true));
26012 },
26013
26014 show: function(delay){
26015 // summary:
26016 // Toggle the node to showing
26017 // delay: Integer?
26018 // Amount of time to stall playing the show animation
26019 return this.showAnim.play(delay || 0);
26020 },
26021
26022 hide: function(delay){
26023 // summary:
26024 // Toggle the node to hidden
26025 // delay: Integer?
26026 // Amount of time to stall playing the hide animation
26027 return this.hideAnim.play(delay || 0);
26028 }
26029 });
26030
26031 });
26032
26033 },
26034 'dijit/form/FilteringSelect':function(){
26035 define("dijit/form/FilteringSelect", [
26036 "dojo/data/util/filter", // filter.patternToRegExp
26037 "dojo/_base/declare", // declare
26038 "dojo/_base/lang", // lang.mixin
26039 "dojo/when",
26040 "./MappedTextBox",
26041 "./ComboBoxMixin"
26042 ], function(filter, declare, lang, when, MappedTextBox, ComboBoxMixin){
26043
26044 // module:
26045 // dijit/form/FilteringSelect
26046
26047 return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
26048 // summary:
26049 // An enhanced version of the HTML SELECT tag, populated dynamically
26050 //
26051 // description:
26052 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
26053 // very nicely with very large data sets because it can load and page data as needed.
26054 // It also resembles ComboBox, but does not allow values outside of the provided ones.
26055 // If OPTION tags are used as the data provider via markup, then the
26056 // OPTION tag's child text node is used as the displayed value when selected
26057 // while the OPTION tag's value attribute is used as the widget value on form submit.
26058 // To set the default value when using OPTION tags, specify the selected
26059 // attribute on 1 of the child OPTION tags.
26060 //
26061 // Similar features:
26062 //
26063 // - There is a drop down list of possible values.
26064 // - You can only enter a value from the drop down list. (You can't
26065 // enter an arbitrary value.)
26066 // - The value submitted with the form is the hidden value (ex: CA),
26067 // not the displayed value a.k.a. label (ex: California)
26068 //
26069 // Enhancements over plain HTML version:
26070 //
26071 // - If you type in some text then it will filter down the list of
26072 // possible values in the drop down list.
26073 // - List can be specified either as a static list or via a javascript
26074 // function (that can get the list from a server)
26075
26076 // required: Boolean
26077 // True (default) if user is required to enter a value into this field.
26078 required: true,
26079
26080 _lastDisplayedValue: "",
26081
26082 _isValidSubset: function(){
26083 return this._opened;
26084 },
26085
26086 isValid: function(){
26087 // Overrides ValidationTextBox.isValid()
26088 return !!this.item || (!this.required && this.get('displayedValue') == ""); // #5974
26089 },
26090
26091 _refreshState: function(){
26092 if(!this.searchTimer){ // state will be refreshed after results are returned
26093 this.inherited(arguments);
26094 }
26095 },
26096
26097 _callbackSetLabel: function(
26098 /*Array*/ result,
26099 /*Object*/ query,
26100 /*Object*/ options,
26101 /*Boolean?*/ priorityChange){
26102 // summary:
26103 // Callback from dojo.store after lookup of user entered value finishes
26104
26105 // setValue does a synchronous lookup,
26106 // so it calls _callbackSetLabel directly,
26107 // and so does not pass dataObject
26108 // still need to test against _lastQuery in case it came too late
26109 if((query && query[this.searchAttr] !== this._lastQuery) || (!query && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
26110 return;
26111 }
26112 if(!result.length){
26113 //#3268: don't modify display value on bad input
26114 //#3285: change CSS to indicate error
26115 this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
26116 }else{
26117 this.set('item', result[0], priorityChange);
26118 }
26119 },
26120
26121 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
26122 // Callback when a data store query completes.
26123 // Overrides ComboBox._openResultList()
26124
26125 // #3285: tap into search callback to see if user's query resembles a match
26126 if(query[this.searchAttr] !== this._lastQuery){
26127 return;
26128 }
26129 this.inherited(arguments);
26130
26131 if(this.item === undefined){ // item == undefined for keyboard search
26132 // If the search returned no items that means that the user typed
26133 // in something invalid (and they can't make it valid by typing more characters),
26134 // so flag the FilteringSelect as being in an invalid state
26135 this.validate(true);
26136 }
26137 },
26138
26139 _getValueAttr: function(){
26140 // summary:
26141 // Hook for get('value') to work.
26142
26143 // don't get the textbox value but rather the previously set hidden value.
26144 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
26145 return this.valueNode.value;
26146 },
26147
26148 _getValueField: function(){
26149 // Overrides ComboBox._getValueField()
26150 return "value";
26151 },
26152
26153 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
26154 // summary:
26155 // Hook so set('value', value) works.
26156 // description:
26157 // Sets the value of the select.
26158 // Also sets the label to the corresponding value by reverse lookup.
26159 if(!this._onChangeActive){ priorityChange = null; }
26160
26161 if(item === undefined){
26162 if(value === null || value === ''){
26163 value = '';
26164 if(!lang.isString(displayedValue)){
26165 this._setDisplayedValueAttr(displayedValue||'', priorityChange);
26166 return;
26167 }
26168 }
26169
26170 var self = this;
26171 this._lastQuery = value;
26172 when(this.store.get(value), function(item){
26173 self._callbackSetLabel(item? [item] : [], undefined, undefined, priorityChange);
26174 });
26175 }else{
26176 this.valueNode.value = value;
26177 this.inherited(arguments);
26178 }
26179 },
26180
26181 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
26182 // summary:
26183 // Set the displayed valued in the input box, and the hidden value
26184 // that gets submitted, based on a dojo.data store item.
26185 // description:
26186 // Users shouldn't call this function; they should be calling
26187 // set('item', value)
26188 // tags:
26189 // private
26190 this.inherited(arguments);
26191 this._lastDisplayedValue = this.textbox.value;
26192 },
26193
26194 _getDisplayQueryString: function(/*String*/ text){
26195 return text.replace(/([\\\*\?])/g, "\\$1");
26196 },
26197
26198 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
26199 // summary:
26200 // Hook so set('displayedValue', label) works.
26201 // description:
26202 // Sets textbox to display label. Also performs reverse lookup
26203 // to set the hidden value. label should corresponding to item.searchAttr.
26204
26205 if(label == null){ label = ''; }
26206
26207 // This is called at initialization along with every custom setter.
26208 // Usually (or always?) the call can be ignored. If it needs to be
26209 // processed then at least make sure that the XHR request doesn't trigger an onChange()
26210 // event, even if it returns after creation has finished
26211 if(!this._created){
26212 if(!("displayedValue" in this.params)){
26213 return;
26214 }
26215 priorityChange = false;
26216 }
26217
26218 // Do a reverse lookup to map the specified displayedValue to the hidden value.
26219 // Note that if there's a custom labelFunc() this code
26220 if(this.store){
26221 this.closeDropDown();
26222 var query = lang.clone(this.query); // #6196: populate query with user-specifics
26223
26224 // Generate query
26225 var qs = this._getDisplayQueryString(label), q;
26226 if(this.store._oldAPI){
26227 // remove this branch for 2.0
26228 q = qs;
26229 }else{
26230 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
26231 // but with a toString() method to help dojo/store/JsonRest.
26232 // Search string like "Co*" converted to regex like /^Co.*$/i.
26233 q = filter.patternToRegExp(qs, this.ignoreCase);
26234 q.toString = function(){ return qs; };
26235 }
26236 this._lastQuery = query[this.searchAttr] = q;
26237
26238 // If the label is not valid, the callback will never set it,
26239 // so the last valid value will get the warning textbox. Set the
26240 // textbox value now so that the impending warning will make
26241 // sense to the user
26242 this.textbox.value = label;
26243 this._lastDisplayedValue = label;
26244 this._set("displayedValue", label); // for watch("displayedValue") notification
26245 var _this = this;
26246 var options = {
26247 ignoreCase: this.ignoreCase,
26248 deep: true
26249 };
26250 lang.mixin(options, this.fetchProperties);
26251 this._fetchHandle = this.store.query(query, options);
26252 when(this._fetchHandle, function(result){
26253 _this._fetchHandle = null;
26254 _this._callbackSetLabel(result || [], query, options, priorityChange);
26255 }, function(err){
26256 _this._fetchHandle = null;
26257 if(!_this._cancelingQuery){ // don't treat canceled query as an error
26258 console.error('dijit.form.FilteringSelect: ' + err.toString());
26259 }
26260 });
26261 }
26262 },
26263
26264 undo: function(){
26265 this.set('displayedValue', this._lastDisplayedValue);
26266 }
26267 });
26268 });
26269
26270 },
26271 'dojo/data/util/sorter':function(){
26272 define("dojo/data/util/sorter", ["../../_base/lang"], function(lang){
26273 // module:
26274 // dojo/data/util/sorter
26275 // summary:
26276 // TODOC
26277
26278 var sorter = {};
26279 lang.setObject("dojo.data.util.sorter", sorter);
26280
26281 sorter.basicComparator = function( /*anything*/ a,
26282 /*anything*/ b){
26283 // summary:
26284 // Basic comparison function that compares if an item is greater or less than another item
26285 // description:
26286 // returns 1 if a > b, -1 if a < b, 0 if equal.
26287 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
26288 // And compared to each other, null is equivalent to undefined.
26289
26290 //null is a problematic compare, so if null, we set to undefined.
26291 //Makes the check logic simple, compact, and consistent
26292 //And (null == undefined) === true, so the check later against null
26293 //works for undefined and is less bytes.
26294 var r = -1;
26295 if(a === null){
26296 a = undefined;
26297 }
26298 if(b === null){
26299 b = undefined;
26300 }
26301 if(a == b){
26302 r = 0;
26303 }else if(a > b || a == null){
26304 r = 1;
26305 }
26306 return r; //int {-1,0,1}
26307 };
26308
26309 sorter.createSortFunction = function( /* attributes[] */sortSpec, /*dojo/data/api/Read*/ store){
26310 // summary:
26311 // Helper function to generate the sorting function based off the list of sort attributes.
26312 // description:
26313 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
26314 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
26315 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
26316 // Returns the sorting function for this particular list of attributes and sorting directions.
26317 // sortSpec:
26318 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
26319 // The objects should be formatted as follows:
26320 // | {
26321 // | attribute: "attributeName-string" || attribute,
26322 // | descending: true|false; // Default is false.
26323 // | }
26324 // store:
26325 // The datastore object to look up item values from.
26326
26327 var sortFunctions=[];
26328
26329 function createSortFunction(attr, dir, comp, s){
26330 //Passing in comp and s (comparator and store), makes this
26331 //function much faster.
26332 return function(itemA, itemB){
26333 var a = s.getValue(itemA, attr);
26334 var b = s.getValue(itemB, attr);
26335 return dir * comp(a,b); //int
26336 };
26337 }
26338 var sortAttribute;
26339 var map = store.comparatorMap;
26340 var bc = sorter.basicComparator;
26341 for(var i = 0; i < sortSpec.length; i++){
26342 sortAttribute = sortSpec[i];
26343 var attr = sortAttribute.attribute;
26344 if(attr){
26345 var dir = (sortAttribute.descending) ? -1 : 1;
26346 var comp = bc;
26347 if(map){
26348 if(typeof attr !== "string" && ("toString" in attr)){
26349 attr = attr.toString();
26350 }
26351 comp = map[attr] || bc;
26352 }
26353 sortFunctions.push(createSortFunction(attr,
26354 dir, comp, store));
26355 }
26356 }
26357 return function(rowA, rowB){
26358 var i=0;
26359 while(i < sortFunctions.length){
26360 var ret = sortFunctions[i++](rowA, rowB);
26361 if(ret !== 0){
26362 return ret;//int
26363 }
26364 }
26365 return 0; //int
26366 }; // Function
26367 };
26368
26369 return sorter;
26370 });
26371
26372 },
26373 'dijit/form/_ButtonMixin':function(){
26374 define("dijit/form/_ButtonMixin", [
26375 "dojo/_base/declare", // declare
26376 "dojo/dom", // dom.setSelectable
26377 "dojo/_base/event", // event.stop
26378 "../registry" // registry.byNode
26379 ], function(declare, dom, event, registry){
26380
26381 // module:
26382 // dijit/form/_ButtonMixin
26383
26384 return declare("dijit.form._ButtonMixin", null, {
26385 // summary:
26386 // A mixin to add a thin standard API wrapper to a normal HTML button
26387 // description:
26388 // A label should always be specified (through innerHTML) or the label attribute.
26389 //
26390 // Attach points:
26391 //
26392 // - focusNode (required): this node receives focus
26393 // - valueNode (optional): this node's value gets submitted with FORM elements
26394 // - containerNode (optional): this node gets the innerHTML assignment for label
26395 // example:
26396 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
26397 // example:
26398 // | var button1 = new Button({label: "hello world", onClick: foo});
26399 // | dojo.body().appendChild(button1.domNode);
26400
26401 // label: HTML String
26402 // Content to display in button.
26403 label: "",
26404
26405 // type: [const] String
26406 // Type of button (submit, reset, button, checkbox, radio)
26407 type: "button",
26408
26409 _onClick: function(/*Event*/ e){
26410 // summary:
26411 // Internal function to handle click actions
26412 if(this.disabled){
26413 event.stop(e);
26414 return false;
26415 }
26416 var preventDefault = this.onClick(e) === false; // user click actions
26417 if(!preventDefault && this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a non-form widget needs to be signalled
26418 for(var node=this.domNode; node.parentNode; node=node.parentNode){
26419 var widget=registry.byNode(node);
26420 if(widget && typeof widget._onSubmit == "function"){
26421 widget._onSubmit(e);
26422 preventDefault = true;
26423 break;
26424 }
26425 }
26426 }
26427 if(preventDefault){
26428 e.preventDefault();
26429 }
26430 return !preventDefault;
26431 },
26432
26433 postCreate: function(){
26434 this.inherited(arguments);
26435 dom.setSelectable(this.focusNode, false);
26436 },
26437
26438 onClick: function(/*Event*/ /*===== e =====*/){
26439 // summary:
26440 // Callback for when button is clicked.
26441 // If type="submit", return true to perform submit, or false to cancel it.
26442 // type:
26443 // callback
26444 return true; // Boolean
26445 },
26446
26447 _setLabelAttr: function(/*String*/ content){
26448 // summary:
26449 // Hook for set('label', ...) to work.
26450 // description:
26451 // Set the label (text) of the button; takes an HTML string.
26452 this._set("label", content);
26453 (this.containerNode||this.focusNode).innerHTML = content;
26454 }
26455 });
26456
26457 });
26458
26459 },
26460 'dojo/colors':function(){
26461 define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil){
26462 // module:
26463 // dojo/colors
26464
26465 /*=====
26466 return {
26467 // summary:
26468 // Color utilities, extending Base dojo.Color
26469 };
26470 =====*/
26471
26472 var ColorExt = {};
26473 lang.setObject("dojo.colors", ColorExt);
26474
26475 //TODO: this module appears to break naming conventions
26476
26477 // this is a standard conversion prescribed by the CSS3 Color Module
26478 var hue2rgb = function(m1, m2, h){
26479 if(h < 0){ ++h; }
26480 if(h > 1){ --h; }
26481 var h6 = 6 * h;
26482 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
26483 if(2 * h < 1){ return m2; }
26484 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
26485 return m1;
26486 };
26487 // Override base Color.fromRgb with the impl in this module
26488 dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo/_base/Color?*/ obj){
26489 // summary:
26490 // get rgb(a) array from css-style color declarations
26491 // description:
26492 // this function can handle all 4 CSS3 Color Module formats: rgb,
26493 // rgba, hsl, hsla, including rgb(a) with percentage values.
26494 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
26495 if(m){
26496 var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
26497 if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
26498 var r = c[0];
26499 if(r.charAt(r.length - 1) == "%"){
26500 // 3 rgb percentage values
26501 a = ArrayUtil.map(c, function(x){
26502 return parseFloat(x) * 2.56;
26503 });
26504 if(l == 4){ a[3] = c[3]; }
26505 return Color.fromArray(a, obj); // dojo/_base/Color
26506 }
26507 return Color.fromArray(c, obj); // dojo/_base/Color
26508 }
26509 if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
26510 // normalize hsl values
26511 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
26512 S = parseFloat(c[1]) / 100,
26513 L = parseFloat(c[2]) / 100,
26514 // calculate rgb according to the algorithm
26515 // recommended by the CSS3 Color Module
26516 m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
26517 m1 = 2 * L - m2;
26518 a = [
26519 hue2rgb(m1, m2, H + 1 / 3) * 256,
26520 hue2rgb(m1, m2, H) * 256,
26521 hue2rgb(m1, m2, H - 1 / 3) * 256,
26522 1
26523 ];
26524 if(l == 4){ a[3] = c[3]; }
26525 return Color.fromArray(a, obj); // dojo/_base/Color
26526 }
26527 }
26528 return null; // dojo/_base/Color
26529 };
26530
26531 var confine = function(c, low, high){
26532 // summary:
26533 // sanitize a color component by making sure it is a number,
26534 // and clamping it to valid values
26535 c = Number(c);
26536 return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
26537 };
26538
26539 Color.prototype.sanitize = function(){
26540 // summary:
26541 // makes sure that the object has correct attributes
26542 var t = this;
26543 t.r = Math.round(confine(t.r, 0, 255));
26544 t.g = Math.round(confine(t.g, 0, 255));
26545 t.b = Math.round(confine(t.b, 0, 255));
26546 t.a = confine(t.a, 0, 1);
26547 return this; // dojo/_base/Color
26548 };
26549
26550 ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){
26551 // summary:
26552 // creates a greyscale color with an optional alpha
26553 return Color.fromArray([g, g, g, a]); // dojo/_base/Color
26554 };
26555
26556 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
26557 lang.mixin(Color.named, {
26558 "aliceblue": [240,248,255],
26559 "antiquewhite": [250,235,215],
26560 "aquamarine": [127,255,212],
26561 "azure": [240,255,255],
26562 "beige": [245,245,220],
26563 "bisque": [255,228,196],
26564 "blanchedalmond": [255,235,205],
26565 "blueviolet": [138,43,226],
26566 "brown": [165,42,42],
26567 "burlywood": [222,184,135],
26568 "cadetblue": [95,158,160],
26569 "chartreuse": [127,255,0],
26570 "chocolate": [210,105,30],
26571 "coral": [255,127,80],
26572 "cornflowerblue": [100,149,237],
26573 "cornsilk": [255,248,220],
26574 "crimson": [220,20,60],
26575 "cyan": [0,255,255],
26576 "darkblue": [0,0,139],
26577 "darkcyan": [0,139,139],
26578 "darkgoldenrod": [184,134,11],
26579 "darkgray": [169,169,169],
26580 "darkgreen": [0,100,0],
26581 "darkgrey": [169,169,169],
26582 "darkkhaki": [189,183,107],
26583 "darkmagenta": [139,0,139],
26584 "darkolivegreen": [85,107,47],
26585 "darkorange": [255,140,0],
26586 "darkorchid": [153,50,204],
26587 "darkred": [139,0,0],
26588 "darksalmon": [233,150,122],
26589 "darkseagreen": [143,188,143],
26590 "darkslateblue": [72,61,139],
26591 "darkslategray": [47,79,79],
26592 "darkslategrey": [47,79,79],
26593 "darkturquoise": [0,206,209],
26594 "darkviolet": [148,0,211],
26595 "deeppink": [255,20,147],
26596 "deepskyblue": [0,191,255],
26597 "dimgray": [105,105,105],
26598 "dimgrey": [105,105,105],
26599 "dodgerblue": [30,144,255],
26600 "firebrick": [178,34,34],
26601 "floralwhite": [255,250,240],
26602 "forestgreen": [34,139,34],
26603 "gainsboro": [220,220,220],
26604 "ghostwhite": [248,248,255],
26605 "gold": [255,215,0],
26606 "goldenrod": [218,165,32],
26607 "greenyellow": [173,255,47],
26608 "grey": [128,128,128],
26609 "honeydew": [240,255,240],
26610 "hotpink": [255,105,180],
26611 "indianred": [205,92,92],
26612 "indigo": [75,0,130],
26613 "ivory": [255,255,240],
26614 "khaki": [240,230,140],
26615 "lavender": [230,230,250],
26616 "lavenderblush": [255,240,245],
26617 "lawngreen": [124,252,0],
26618 "lemonchiffon": [255,250,205],
26619 "lightblue": [173,216,230],
26620 "lightcoral": [240,128,128],
26621 "lightcyan": [224,255,255],
26622 "lightgoldenrodyellow": [250,250,210],
26623 "lightgray": [211,211,211],
26624 "lightgreen": [144,238,144],
26625 "lightgrey": [211,211,211],
26626 "lightpink": [255,182,193],
26627 "lightsalmon": [255,160,122],
26628 "lightseagreen": [32,178,170],
26629 "lightskyblue": [135,206,250],
26630 "lightslategray": [119,136,153],
26631 "lightslategrey": [119,136,153],
26632 "lightsteelblue": [176,196,222],
26633 "lightyellow": [255,255,224],
26634 "limegreen": [50,205,50],
26635 "linen": [250,240,230],
26636 "magenta": [255,0,255],
26637 "mediumaquamarine": [102,205,170],
26638 "mediumblue": [0,0,205],
26639 "mediumorchid": [186,85,211],
26640 "mediumpurple": [147,112,219],
26641 "mediumseagreen": [60,179,113],
26642 "mediumslateblue": [123,104,238],
26643 "mediumspringgreen": [0,250,154],
26644 "mediumturquoise": [72,209,204],
26645 "mediumvioletred": [199,21,133],
26646 "midnightblue": [25,25,112],
26647 "mintcream": [245,255,250],
26648 "mistyrose": [255,228,225],
26649 "moccasin": [255,228,181],
26650 "navajowhite": [255,222,173],
26651 "oldlace": [253,245,230],
26652 "olivedrab": [107,142,35],
26653 "orange": [255,165,0],
26654 "orangered": [255,69,0],
26655 "orchid": [218,112,214],
26656 "palegoldenrod": [238,232,170],
26657 "palegreen": [152,251,152],
26658 "paleturquoise": [175,238,238],
26659 "palevioletred": [219,112,147],
26660 "papayawhip": [255,239,213],
26661 "peachpuff": [255,218,185],
26662 "peru": [205,133,63],
26663 "pink": [255,192,203],
26664 "plum": [221,160,221],
26665 "powderblue": [176,224,230],
26666 "rosybrown": [188,143,143],
26667 "royalblue": [65,105,225],
26668 "saddlebrown": [139,69,19],
26669 "salmon": [250,128,114],
26670 "sandybrown": [244,164,96],
26671 "seagreen": [46,139,87],
26672 "seashell": [255,245,238],
26673 "sienna": [160,82,45],
26674 "skyblue": [135,206,235],
26675 "slateblue": [106,90,205],
26676 "slategray": [112,128,144],
26677 "slategrey": [112,128,144],
26678 "snow": [255,250,250],
26679 "springgreen": [0,255,127],
26680 "steelblue": [70,130,180],
26681 "tan": [210,180,140],
26682 "thistle": [216,191,216],
26683 "tomato": [255,99,71],
26684 "turquoise": [64,224,208],
26685 "violet": [238,130,238],
26686 "wheat": [245,222,179],
26687 "whitesmoke": [245,245,245],
26688 "yellowgreen": [154,205,50]
26689 });
26690
26691 return Color; // TODO: return ColorExt, not Color
26692 });
26693
26694 },
26695 'dijit/registry':function(){
26696 define("dijit/registry", [
26697 "dojo/_base/array", // array.forEach array.map
26698 "dojo/sniff", // has("ie")
26699 "dojo/_base/unload", // unload.addOnWindowUnload
26700 "dojo/_base/window", // win.body
26701 "./main" // dijit._scopeName
26702 ], function(array, has, unload, win, dijit){
26703
26704 // module:
26705 // dijit/registry
26706
26707 var _widgetTypeCtr = {}, hash = {};
26708
26709 var registry = {
26710 // summary:
26711 // Registry of existing widget on page, plus some utility methods.
26712
26713 // length: Number
26714 // Number of registered widgets
26715 length: 0,
26716
26717 add: function(widget){
26718 // summary:
26719 // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
26720 // widget: dijit/_WidgetBase
26721 // Any dijit/_WidgetBase subclass.
26722 if(hash[widget.id]){
26723 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
26724 }
26725 hash[widget.id] = widget;
26726 this.length++;
26727 },
26728
26729 remove: function(/*String*/ id){
26730 // summary:
26731 // Remove a widget from the registry. Does not destroy the widget; simply
26732 // removes the reference.
26733 if(hash[id]){
26734 delete hash[id];
26735 this.length--;
26736 }
26737 },
26738
26739 byId: function(/*String|Widget*/ id){
26740 // summary:
26741 // Find a widget by it's id.
26742 // If passed a widget then just returns the widget.
26743 return typeof id == "string" ? hash[id] : id; // dijit/_WidgetBase
26744 },
26745
26746 byNode: function(/*DOMNode*/ node){
26747 // summary:
26748 // Returns the widget corresponding to the given DOMNode
26749 return hash[node.getAttribute("widgetId")]; // dijit/_WidgetBase
26750 },
26751
26752 toArray: function(){
26753 // summary:
26754 // Convert registry into a true Array
26755 //
26756 // example:
26757 // Work with the widget .domNodes in a real Array
26758 // | array.map(registry.toArray(), function(w){ return w.domNode; });
26759
26760 var ar = [];
26761 for(var id in hash){
26762 ar.push(hash[id]);
26763 }
26764 return ar; // dijit/_WidgetBase[]
26765 },
26766
26767 getUniqueId: function(/*String*/widgetType){
26768 // summary:
26769 // Generates a unique id for a given widgetType
26770
26771 var id;
26772 do{
26773 id = widgetType + "_" +
26774 (widgetType in _widgetTypeCtr ?
26775 ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
26776 }while(hash[id]);
26777 return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
26778 },
26779
26780 findWidgets: function(root, skipNode){
26781 // summary:
26782 // Search subtree under root returning widgets found.
26783 // Doesn't search for nested widgets (ie, widgets inside other widgets).
26784 // root: DOMNode
26785 // Node to search under.
26786 // skipNode: DOMNode
26787 // If specified, don't search beneath this node (usually containerNode).
26788
26789 var outAry = [];
26790
26791 function getChildrenHelper(root){
26792 for(var node = root.firstChild; node; node = node.nextSibling){
26793 if(node.nodeType == 1){
26794 var widgetId = node.getAttribute("widgetId");
26795 if(widgetId){
26796 var widget = hash[widgetId];
26797 if(widget){ // may be null on page w/multiple dojo's loaded
26798 outAry.push(widget);
26799 }
26800 }else if(node !== skipNode){
26801 getChildrenHelper(node);
26802 }
26803 }
26804 }
26805 }
26806
26807 getChildrenHelper(root);
26808 return outAry;
26809 },
26810
26811 _destroyAll: function(){
26812 // summary:
26813 // Code to destroy all widgets and do other cleanup on page unload
26814
26815 // Clean up focus manager lingering references to widgets and nodes
26816 dijit._curFocus = null;
26817 dijit._prevFocus = null;
26818 dijit._activeStack = [];
26819
26820 // Destroy all the widgets, top down
26821 array.forEach(registry.findWidgets(win.body()), function(widget){
26822 // Avoid double destroy of widgets like Menu that are attached to <body>
26823 // even though they are logically children of other widgets.
26824 if(!widget._destroyed){
26825 if(widget.destroyRecursive){
26826 widget.destroyRecursive();
26827 }else if(widget.destroy){
26828 widget.destroy();
26829 }
26830 }
26831 });
26832 },
26833
26834 getEnclosingWidget: function(/*DOMNode*/ node){
26835 // summary:
26836 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
26837 // the node is not contained within the DOM tree of any widget
26838 while(node){
26839 var id = node.nodeType == 1 && node.getAttribute("widgetId");
26840 if(id){
26841 return hash[id];
26842 }
26843 node = node.parentNode;
26844 }
26845 return null;
26846 },
26847
26848 // In case someone needs to access hash.
26849 // Actually, this is accessed from WidgetSet back-compatibility code
26850 _hash: hash
26851 };
26852
26853 dijit.registry = registry;
26854
26855 return registry;
26856 });
26857
26858 },
26859 'dijit/tree/_dndContainer':function(){
26860 define("dijit/tree/_dndContainer", [
26861 "dojo/aspect", // aspect.after
26862 "dojo/_base/declare", // declare
26863 "dojo/dom-class", // domClass.add domClass.remove domClass.replace
26864 "dojo/_base/event", // event.stop
26865 "dojo/_base/lang", // lang.mixin lang.hitch
26866 "dojo/on",
26867 "dojo/touch"
26868 ], function(aspect, declare,domClass, event, lang, on, touch){
26869
26870 // module:
26871 // dijit/tree/_dndContainer
26872
26873 /*=====
26874 var __Args = {
26875 // summary:
26876 // A dict of parameters for Tree source configuration.
26877 // isSource: Boolean?
26878 // Can be used as a DnD source. Defaults to true.
26879 // accept: String[]
26880 // List of accepted types (text strings) for a target; defaults to
26881 // ["text", "treeNode"]
26882 // copyOnly: Boolean?
26883 // Copy items, if true, use a state of Ctrl key otherwise,
26884 // dragThreshold: Number
26885 // The move delay in pixels before detecting a drag; 0 by default
26886 // betweenThreshold: Integer
26887 // Distance from upper/lower edge of node to allow drop to reorder nodes
26888 };
26889 =====*/
26890
26891 return declare("dijit.tree._dndContainer", null, {
26892
26893 // summary:
26894 // This is a base class for `dijit/tree/_dndSelector`, and isn't meant to be used directly.
26895 // It's modeled after `dojo/dnd/Container`.
26896 // tags:
26897 // protected
26898
26899 /*=====
26900 // current: DomNode
26901 // The currently hovered TreeNode.rowNode (which is the DOM node
26902 // associated w/a given node in the tree, excluding it's descendants)
26903 current: null,
26904 =====*/
26905
26906 constructor: function(tree, params){
26907 // summary:
26908 // A constructor of the Container
26909 // tree: Node
26910 // Node or node's id to build the container on
26911 // params: __Args
26912 // A dict of parameters, which gets mixed into the object
26913 // tags:
26914 // private
26915 this.tree = tree;
26916 this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
26917 lang.mixin(this, params);
26918
26919 // class-specific variables
26920 this.current = null; // current TreeNode's DOM node
26921
26922 // states
26923 this.containerState = "";
26924 domClass.add(this.node, "dojoDndContainer");
26925
26926 // set up events
26927 this.events = [
26928 // Mouse (or touch) enter/leave on Tree itself
26929 on(this.node, touch.enter, lang.hitch(this, "onOverEvent")),
26930 on(this.node, touch.leave, lang.hitch(this, "onOutEvent")),
26931
26932 // switching between TreeNodes
26933 aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true),
26934 aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true),
26935
26936 // cancel text selection and text dragging
26937 on(this.node, "dragstart", lang.hitch(event, "stop")),
26938 on(this.node, "selectstart", lang.hitch(event, "stop"))
26939 ];
26940 },
26941
26942 destroy: function(){
26943 // summary:
26944 // Prepares this object to be garbage-collected
26945
26946 var h;
26947 while(h = this.events.pop()){ h.remove(); }
26948
26949 // this.clearItems();
26950 this.node = this.parent = null;
26951 },
26952
26953 // mouse events
26954 onMouseOver: function(widget /*===== , evt =====*/){
26955 // summary:
26956 // Called when mouse is moved over a TreeNode
26957 // widget: TreeNode
26958 // evt: Event
26959 // tags:
26960 // protected
26961 this.current = widget;
26962 },
26963
26964 onMouseOut: function(/*===== widget, evt =====*/){
26965 // summary:
26966 // Called when mouse is moved away from a TreeNode
26967 // widget: TreeNode
26968 // evt: Event
26969 // tags:
26970 // protected
26971 this.current = null;
26972 },
26973
26974 _changeState: function(type, newState){
26975 // summary:
26976 // Changes a named state to new state value
26977 // type: String
26978 // A name of the state to change
26979 // newState: String
26980 // new state
26981 var prefix = "dojoDnd" + type;
26982 var state = type.toLowerCase() + "State";
26983 //domClass.replace(this.node, prefix + newState, prefix + this[state]);
26984 domClass.replace(this.node, prefix + newState, prefix + this[state]);
26985 this[state] = newState;
26986 },
26987
26988 _addItemClass: function(node, type){
26989 // summary:
26990 // Adds a class with prefix "dojoDndItem"
26991 // node: Node
26992 // A node
26993 // type: String
26994 // A variable suffix for a class name
26995 domClass.add(node, "dojoDndItem" + type);
26996 },
26997
26998 _removeItemClass: function(node, type){
26999 // summary:
27000 // Removes a class with prefix "dojoDndItem"
27001 // node: Node
27002 // A node
27003 // type: String
27004 // A variable suffix for a class name
27005 domClass.remove(node, "dojoDndItem" + type);
27006 },
27007
27008 onOverEvent: function(){
27009 // summary:
27010 // This function is called once, when mouse is over our container
27011 // tags:
27012 // protected
27013 this._changeState("Container", "Over");
27014 },
27015
27016 onOutEvent: function(){
27017 // summary:
27018 // This function is called once, when mouse is out of our container
27019 // tags:
27020 // protected
27021 this._changeState("Container", "");
27022 }
27023 });
27024 });
27025
27026 },
27027 '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",
27028 'dijit/_base/wai':function(){
27029 define("dijit/_base/wai", [
27030 "dojo/dom-attr", // domAttr.attr
27031 "dojo/_base/lang", // lang.mixin
27032 "../main", // export symbols to dijit
27033 "../hccss" // not using this module directly, but loading it sets CSS flag on <html>
27034 ], function(domAttr, lang, dijit){
27035
27036 // module:
27037 // dijit/_base/wai
27038
27039 var exports = {
27040 // summary:
27041 // Deprecated methods for setting/getting wai roles and states.
27042 // New code should call setAttribute()/getAttribute() directly.
27043 //
27044 // Also loads hccss to apply dj_a11y class to root node if machine is in high-contrast mode.
27045
27046 hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
27047 // summary:
27048 // Determines if an element has a particular role.
27049 // returns:
27050 // True if elem has the specific role attribute and false if not.
27051 // For backwards compatibility if role parameter not provided,
27052 // returns true if has a role
27053 var waiRole = this.getWaiRole(elem);
27054 return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
27055 },
27056
27057 getWaiRole: function(/*Element*/ elem){
27058 // summary:
27059 // Gets the role for an element (which should be a wai role).
27060 // returns:
27061 // The role of elem or an empty string if elem
27062 // does not have a role.
27063 return lang.trim((domAttr.get(elem, "role") || "").replace("wairole:",""));
27064 },
27065
27066 setWaiRole: function(/*Element*/ elem, /*String*/ role){
27067 // summary:
27068 // Sets the role on an element.
27069 // description:
27070 // Replace existing role attribute with new role.
27071
27072 domAttr.set(elem, "role", role);
27073 },
27074
27075 removeWaiRole: function(/*Element*/ elem, /*String*/ role){
27076 // summary:
27077 // Removes the specified role from an element.
27078 // Removes role attribute if no specific role provided (for backwards compat.)
27079
27080 var roleValue = domAttr.get(elem, "role");
27081 if(!roleValue){ return; }
27082 if(role){
27083 var t = lang.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
27084 domAttr.set(elem, "role", t);
27085 }else{
27086 elem.removeAttribute("role");
27087 }
27088 },
27089
27090 hasWaiState: function(/*Element*/ elem, /*String*/ state){
27091 // summary:
27092 // Determines if an element has a given state.
27093 // description:
27094 // Checks for an attribute called "aria-"+state.
27095 // returns:
27096 // true if elem has a value for the given state and
27097 // false if it does not.
27098
27099 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
27100 },
27101
27102 getWaiState: function(/*Element*/ elem, /*String*/ state){
27103 // summary:
27104 // Gets the value of a state on an element.
27105 // description:
27106 // Checks for an attribute called "aria-"+state.
27107 // returns:
27108 // The value of the requested state on elem
27109 // or an empty string if elem has no value for state.
27110
27111 return elem.getAttribute("aria-"+state) || "";
27112 },
27113
27114 setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
27115 // summary:
27116 // Sets a state on an element.
27117 // description:
27118 // Sets an attribute called "aria-"+state.
27119
27120 elem.setAttribute("aria-"+state, value);
27121 },
27122
27123 removeWaiState: function(/*Element*/ elem, /*String*/ state){
27124 // summary:
27125 // Removes a state from an element.
27126 // description:
27127 // Sets an attribute called "aria-"+state.
27128
27129 elem.removeAttribute("aria-"+state);
27130 }
27131 };
27132
27133 lang.mixin(dijit, exports);
27134
27135 /*===== return exports; =====*/
27136 return dijit; // for back compat :-(
27137 });
27138
27139 },
27140 'dijit/form/_FormSelectWidget':function(){
27141 define("dijit/form/_FormSelectWidget", [
27142 "dojo/_base/array", // array.filter array.forEach array.map array.some
27143 "dojo/_base/Deferred",
27144 "dojo/aspect", // aspect.after
27145 "dojo/data/util/sorter", // util.sorter.createSortFunction
27146 "dojo/_base/declare", // declare
27147 "dojo/dom", // dom.setSelectable
27148 "dojo/dom-class", // domClass.toggle
27149 "dojo/_base/kernel", // _scopeName
27150 "dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
27151 "dojo/query", // query
27152 "dojo/when",
27153 "dojo/store/util/QueryResults",
27154 "./_FormValueWidget"
27155 ], function(array, Deferred, aspect, sorter, declare, dom, domClass, kernel, lang, query, when,
27156 QueryResults, _FormValueWidget){
27157
27158 // module:
27159 // dijit/form/_FormSelectWidget
27160
27161 /*=====
27162 var __SelectOption = {
27163 // value: String
27164 // The value of the option. Setting to empty (or missing) will
27165 // place a separator at that location
27166 // label: String
27167 // The label for our option. It can contain html tags.
27168 // selected: Boolean
27169 // Whether or not we are a selected option
27170 // disabled: Boolean
27171 // Whether or not this specific option is disabled
27172 };
27173 =====*/
27174
27175 var _FormSelectWidget = declare("dijit.form._FormSelectWidget", _FormValueWidget, {
27176 // summary:
27177 // Extends _FormValueWidget in order to provide "select-specific"
27178 // values - i.e., those values that are unique to `<select>` elements.
27179 // This also provides the mechanism for reading the elements from
27180 // a store, if desired.
27181
27182 // multiple: [const] Boolean
27183 // Whether or not we are multi-valued
27184 multiple: false,
27185
27186 // options: __SelectOption[]
27187 // The set of options for our select item. Roughly corresponds to
27188 // the html `<option>` tag.
27189 options: null,
27190
27191 // store: dojo/store/api/Store
27192 // A store to use for getting our list of options - rather than reading them
27193 // from the `<option>` html tags. Should support getIdentity().
27194 // For back-compat store can also be a dojo/data/api/Identity.
27195 store: null,
27196
27197 // query: object
27198 // A query to use when fetching items from our store
27199 query: null,
27200
27201 // queryOptions: object
27202 // Query options to use when fetching from the store
27203 queryOptions: null,
27204
27205 // labelAttr: String?
27206 // The entries in the drop down list come from this attribute in the dojo.store items.
27207 // If ``store`` is set, labelAttr must be set too, unless store is an old-style
27208 // dojo.data store rather than a new dojo/store.
27209 labelAttr: "",
27210
27211 // onFetch: Function
27212 // A callback to do with an onFetch - but before any items are actually
27213 // iterated over (i.e. to filter even further what you want to add)
27214 onFetch: null,
27215
27216 // sortByLabel: Boolean
27217 // Flag to sort the options returned from a store by the label of
27218 // the store.
27219 sortByLabel: true,
27220
27221
27222 // loadChildrenOnOpen: Boolean
27223 // By default loadChildren is called when the items are fetched from the
27224 // store. This property allows delaying loadChildren (and the creation
27225 // of the options/menuitems) until the user clicks the button to open the
27226 // dropdown.
27227 loadChildrenOnOpen: false,
27228
27229 // onLoadDeferred: [readonly] dojo.Deferred
27230 // This is the `dojo.Deferred` returned by setStore().
27231 // Calling onLoadDeferred.then() registers your
27232 // callback to be called only once, when the prior setStore completes.
27233 onLoadDeferred: null,
27234
27235 getOptions: function(/*anything*/ valueOrIdx){
27236 // summary:
27237 // Returns a given option (or options).
27238 // valueOrIdx:
27239 // If passed in as a string, that string is used to look up the option
27240 // in the array of options - based on the value property.
27241 // (See dijit/form/_FormSelectWidget.__SelectOption).
27242 //
27243 // If passed in a number, then the option with the given index (0-based)
27244 // within this select will be returned.
27245 //
27246 // If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
27247 // returned if and only if it exists within this select.
27248 //
27249 // If passed an array, then an array will be returned with each element
27250 // in the array being looked up.
27251 //
27252 // If not passed a value, then all options will be returned
27253 //
27254 // returns:
27255 // The option corresponding with the given value or index. null
27256 // is returned if any of the following are true:
27257 //
27258 // - A string value is passed in which doesn't exist
27259 // - An index is passed in which is outside the bounds of the array of options
27260 // - A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
27261
27262 // NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
27263 // if the value property matches - NOT if the exact option exists
27264 // NOTE: if passing in an array, null elements will be placed in the returned
27265 // array when a value is not found.
27266 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
27267
27268 if(lookupValue === undefined){
27269 return opts; // __SelectOption[]
27270 }
27271 if(lang.isArray(lookupValue)){
27272 return array.map(lookupValue, "return this.getOptions(item);", this); // __SelectOption[]
27273 }
27274 if(lang.isObject(valueOrIdx)){
27275 // We were passed an option - so see if it's in our array (directly),
27276 // and if it's not, try and find it by value.
27277 if(!array.some(this.options, function(o, idx){
27278 if(o === lookupValue ||
27279 (o.value && o.value === lookupValue.value)){
27280 lookupValue = idx;
27281 return true;
27282 }
27283 return false;
27284 })){
27285 lookupValue = -1;
27286 }
27287 }
27288 if(typeof lookupValue == "string"){
27289 for(var i=0; i<l; i++){
27290 if(opts[i].value === lookupValue){
27291 lookupValue = i;
27292 break;
27293 }
27294 }
27295 }
27296 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
27297 return this.options[lookupValue]; // __SelectOption
27298 }
27299 return null; // null
27300 },
27301
27302 addOption: function(/*__SelectOption|__SelectOption[]*/ option){
27303 // summary:
27304 // Adds an option or options to the end of the select. If value
27305 // of the option is empty or missing, a separator is created instead.
27306 // Passing in an array of options will yield slightly better performance
27307 // since the children are only loaded once.
27308 if(!lang.isArray(option)){ option = [option]; }
27309 array.forEach(option, function(i){
27310 if(i && lang.isObject(i)){
27311 this.options.push(i);
27312 }
27313 }, this);
27314 this._loadChildren();
27315 },
27316
27317 removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx){
27318 // summary:
27319 // Removes the given option or options. You can remove by string
27320 // (in which case the value is removed), number (in which case the
27321 // index in the options array is removed), or select option (in
27322 // which case, the select option with a matching value is removed).
27323 // You can also pass in an array of those values for a slightly
27324 // better performance since the children are only loaded once.
27325 if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
27326 var oldOpts = this.getOptions(valueOrIdx);
27327 array.forEach(oldOpts, function(i){
27328 // We can get null back in our array - if our option was not found. In
27329 // that case, we don't want to blow up...
27330 if(i){
27331 this.options = array.filter(this.options, function(node){
27332 return (node.value !== i.value || node.label !== i.label);
27333 });
27334 this._removeOptionItem(i);
27335 }
27336 }, this);
27337 this._loadChildren();
27338 },
27339
27340 updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption){
27341 // summary:
27342 // Updates the values of the given option. The option to update
27343 // is matched based on the value of the entered option. Passing
27344 // in an array of new options will yield better performance since
27345 // the children will only be loaded once.
27346 if(!lang.isArray(newOption)){ newOption = [newOption]; }
27347 array.forEach(newOption, function(i){
27348 var oldOpt = this.getOptions(i), k;
27349 if(oldOpt){
27350 for(k in i){ oldOpt[k] = i[k]; }
27351 }
27352 }, this);
27353 this._loadChildren();
27354 },
27355
27356 setStore: function(store,
27357 selectedValue,
27358 fetchArgs){
27359 // summary:
27360 // Sets the store you would like to use with this select widget.
27361 // The selected value is the value of the new store to set. This
27362 // function returns the original store, in case you want to reuse
27363 // it or something.
27364 // store: dojo/store/api/Store
27365 // The dojo.store you would like to use - it MUST implement getIdentity()
27366 // and MAY implement observe().
27367 // For backwards-compatibility this can also be a data.data store, in which case
27368 // it MUST implement dojo/data/api/Identity,
27369 // and MAY implement dojo/data/api/Notification.
27370 // selectedValue: anything?
27371 // The value that this widget should set itself to *after* the store
27372 // has been loaded
27373 // fetchArgs: Object?
27374 // Hash of parameters to set filter on store, etc.
27375 //
27376 // - query: new value for Select.query,
27377 // - queryOptions: new value for Select.queryOptions,
27378 // - onFetch: callback function for each item in data (Deprecated)
27379 var oStore = this.store;
27380 fetchArgs = fetchArgs || {};
27381
27382 if(oStore !== store){
27383 // Our store has changed, so cancel any listeners on old store (remove for 2.0)
27384 var h;
27385 while((h = this._notifyConnections.pop())){ h.remove(); }
27386
27387 // For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
27388 if(!store.get){
27389 lang.mixin(store, {
27390 _oldAPI: true,
27391 get: function(id){
27392 // summary:
27393 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
27394 // Like dojo.store.DataStore.get() except returns native item.
27395 var deferred = new Deferred();
27396 this.fetchItemByIdentity({
27397 identity: id,
27398 onItem: function(object){
27399 deferred.resolve(object);
27400 },
27401 onError: function(error){
27402 deferred.reject(error);
27403 }
27404 });
27405 return deferred.promise;
27406 },
27407 query: function(query, options){
27408 // summary:
27409 // Queries the store for objects. Like dojo/store/DataStore.query()
27410 // except returned Deferred contains array of native items.
27411 var deferred = new Deferred(function(){ if(fetchHandle.abort){ fetchHandle.abort(); } } );
27412 deferred.total = new Deferred();
27413 var fetchHandle = this.fetch(lang.mixin({
27414 query: query,
27415 onBegin: function(count){
27416 deferred.total.resolve(count);
27417 },
27418 onComplete: function(results){
27419 deferred.resolve(results);
27420 },
27421 onError: function(error){
27422 deferred.reject(error);
27423 }
27424 }, options));
27425 return new QueryResults(deferred);
27426 }
27427 });
27428
27429 if(store.getFeatures()["dojo.data.api.Notification"]){
27430 this._notifyConnections = [
27431 aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
27432 aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
27433 aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
27434 ];
27435 }
27436 }
27437 this._set("store", store); // Our store has changed, so update our notifications
27438 }
27439
27440 // Remove existing options (if there are any)
27441 if(this.options && this.options.length){
27442 this.removeOption(this.options);
27443 }
27444
27445 // Cancel listener for updates to old store
27446 if(this._queryRes && this._queryRes.close){
27447 this._queryRes.close();
27448 }
27449
27450 // If user has specified new query and query options along with this new store, then use them.
27451 if(fetchArgs.query){
27452 this._set("query", fetchArgs.query);
27453 this._set("queryOptions", fetchArgs.queryOptions);
27454 }
27455
27456 // Add our new options
27457 if(store){
27458 this._loadingStore = true;
27459 this.onLoadDeferred = new Deferred();
27460
27461 // Run query
27462 // Save result in this._queryRes so we can cancel the listeners we register below
27463 this._queryRes = store.query(this.query, this.queryOptions);
27464 when(this._queryRes, lang.hitch(this, function(items){
27465
27466 if(this.sortByLabel && !fetchArgs.sort && items.length){
27467 if(items[0].getValue){
27468 // Old dojo.data API to access items, remove for 2.0
27469 items.sort(sorter.createSortFunction([{
27470 attribute: store.getLabelAttributes(items[0])[0]
27471 }], store));
27472 }else{
27473 var labelAttr = this.labelAttr;
27474 items.sort(function(a, b){
27475 return a[labelAttr] > b[labelAttr] ? 1 : b[labelAttr] > a[labelAttr] ? -1 : 0;
27476 });
27477 }
27478 }
27479
27480 if(fetchArgs.onFetch){
27481 items = fetchArgs.onFetch.call(this, items, fetchArgs);
27482 }
27483
27484 // TODO: Add these guys as a batch, instead of separately
27485 array.forEach(items, function(i){
27486 this._addOptionForItem(i);
27487 }, this);
27488
27489 // Register listener for store updates
27490 if(this._queryRes.observe){
27491 this._queryRes.observe(lang.hitch(this, function(object, deletedFrom, insertedInto){
27492 if(deletedFrom == insertedInto){
27493 this._onSetItem(object);
27494 }else{
27495 if(deletedFrom != -1){
27496 this._onDeleteItem(object);
27497 }
27498 if(insertedInto != -1){
27499 this._onNewItem(object);
27500 }
27501 }
27502 }), true);
27503 }
27504
27505 // Set our value (which might be undefined), and then tweak
27506 // it to send a change event with the real value
27507 this._loadingStore = false;
27508 this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
27509 delete this._pendingValue;
27510
27511 if(!this.loadChildrenOnOpen){
27512 this._loadChildren();
27513 }else{
27514 this._pseudoLoadChildren(items);
27515 }
27516 this.onLoadDeferred.resolve(true);
27517 this.onSetStore();
27518 }), function(err){
27519 console.error('dijit.form.Select: ' + err.toString());
27520 this.onLoadDeferred.reject(err);
27521 });
27522 }
27523 return oStore; // dojo/data/api/Identity
27524 },
27525
27526 // TODO: implement set() and watch() for store and query, although not sure how to handle
27527 // setting them individually rather than together (as in setStore() above)
27528
27529 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
27530 // summary:
27531 // set the value of the widget.
27532 // If a string is passed, then we set our value from looking it up.
27533 if(!this._onChangeActive){ priorityChange = null; }
27534 if(this._loadingStore){
27535 // Our store is loading - so save our value, and we'll set it when
27536 // we're done
27537 this._pendingValue = newValue;
27538 return;
27539 }
27540 var opts = this.getOptions() || [];
27541 if(!lang.isArray(newValue)){
27542 newValue = [newValue];
27543 }
27544 array.forEach(newValue, function(i, idx){
27545 if(!lang.isObject(i)){
27546 i = i + "";
27547 }
27548 if(typeof i === "string"){
27549 newValue[idx] = array.filter(opts, function(node){
27550 return node.value === i;
27551 })[0] || {value: "", label: ""};
27552 }
27553 }, this);
27554
27555 // Make sure some sane default is set
27556 newValue = array.filter(newValue, function(i){ return i && i.value; });
27557 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
27558 newValue[0] = opts[0];
27559 }
27560 array.forEach(opts, function(i){
27561 i.selected = array.some(newValue, function(v){ return v.value === i.value; });
27562 });
27563 var val = array.map(newValue, function(i){ return i.value; }),
27564 disp = array.map(newValue, function(i){ return i.label; });
27565
27566 if(typeof val == "undefined" || typeof val[0] == "undefined"){ return; } // not fully initialized yet or a failed value lookup
27567 this._setDisplay(this.multiple ? disp : disp[0]);
27568 this.inherited(arguments, [ this.multiple ? val : val[0], priorityChange ]);
27569 this._updateSelection();
27570 },
27571
27572 _getDisplayedValueAttr: function(){
27573 // summary:
27574 // returns the displayed value of the widget
27575 var val = this.get("value");
27576 if(!lang.isArray(val)){
27577 val = [val];
27578 }
27579 var ret = array.map(this.getOptions(val), function(v){
27580 if(v && "label" in v){
27581 return v.label;
27582 }else if(v){
27583 return v.value;
27584 }
27585 return null;
27586 }, this);
27587 return this.multiple ? ret : ret[0];
27588 },
27589
27590 _loadChildren: function(){
27591 // summary:
27592 // Loads the children represented by this widget's options.
27593 // reset the menu to make it populatable on the next click
27594 if(this._loadingStore){ return; }
27595 array.forEach(this._getChildren(), function(child){
27596 child.destroyRecursive();
27597 });
27598 // Add each menu item
27599 array.forEach(this.options, this._addOptionItem, this);
27600
27601 // Update states
27602 this._updateSelection();
27603 },
27604
27605 _updateSelection: function(){
27606 // summary:
27607 // Sets the "selected" class on the item for styling purposes
27608 this._set("value", this._getValueFromOpts());
27609 var val = this.value;
27610 if(!lang.isArray(val)){
27611 val = [val];
27612 }
27613 if(val && val[0]){
27614 array.forEach(this._getChildren(), function(child){
27615 var isSelected = array.some(val, function(v){
27616 return child.option && (v === child.option.value);
27617 });
27618 domClass.toggle(child.domNode, this.baseClass.replace(/\s+|$/g, "SelectedOption "), isSelected);
27619 child.domNode.setAttribute("aria-selected", isSelected ? "true" : "false");
27620 }, this);
27621 }
27622 },
27623
27624 _getValueFromOpts: function(){
27625 // summary:
27626 // Returns the value of the widget by reading the options for
27627 // the selected flag
27628 var opts = this.getOptions() || [];
27629 if(!this.multiple && opts.length){
27630 // Mirror what a select does - choose the first one
27631 var opt = array.filter(opts, function(i){
27632 return i.selected;
27633 })[0];
27634 if(opt && opt.value){
27635 return opt.value;
27636 }else{
27637 opts[0].selected = true;
27638 return opts[0].value;
27639 }
27640 }else if(this.multiple){
27641 // Set value to be the sum of all selected
27642 return array.map(array.filter(opts, function(i){
27643 return i.selected;
27644 }), function(i){
27645 return i.value;
27646 }) || [];
27647 }
27648 return "";
27649 },
27650
27651 // Internal functions to call when we have store notifications come in
27652 _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
27653 if(!parentInfo || !parentInfo.parent){
27654 // Only add it if we are top-level
27655 this._addOptionForItem(item);
27656 }
27657 },
27658 _onDeleteItem: function(/*item*/ item){
27659 var store = this.store;
27660 this.removeOption(store.getIdentity(item));
27661 },
27662 _onSetItem: function(/*item*/ item){
27663 this.updateOption(this._getOptionObjForItem(item));
27664 },
27665
27666 _getOptionObjForItem: function(item){
27667 // summary:
27668 // Returns an option object based off the given item. The "value"
27669 // of the option item will be the identity of the item, the "label"
27670 // of the option will be the label of the item.
27671
27672 // remove getLabel() call for 2.0 (it's to support the old dojo.data API)
27673 var store = this.store,
27674 label = (this.labelAttr && this.labelAttr in item) ? item[this.labelAttr] : store.getLabel(item),
27675 value = (label ? store.getIdentity(item) : null);
27676 return {value: value, label: label, item: item}; // __SelectOption
27677 },
27678
27679 _addOptionForItem: function(/*item*/ item){
27680 // summary:
27681 // Creates (and adds) the option for the given item
27682 var store = this.store;
27683 if(store.isItemLoaded && !store.isItemLoaded(item)){
27684 // We are not loaded - so let's load it and add later.
27685 // Remove for 2.0 (it's the old dojo.data API)
27686 store.loadItem({item: item, onItem: function(i){
27687 this._addOptionForItem(i);
27688 },
27689 scope: this});
27690 return;
27691 }
27692 var newOpt = this._getOptionObjForItem(item);
27693 this.addOption(newOpt);
27694 },
27695
27696 constructor: function(params /*===== , srcNodeRef =====*/){
27697 // summary:
27698 // Create the widget.
27699 // params: Object|null
27700 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
27701 // and functions, typically callbacks like onClick.
27702 // The hash can contain any of the widget's properties, excluding read-only properties.
27703 // srcNodeRef: DOMNode|String?
27704 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
27705
27706 // Saves off our value, if we have an initial one set so we
27707 // can use it if we have a store as well (see startup())
27708 this._oValue = (params || {}).value || null;
27709 this._notifyConnections = []; // remove for 2.0
27710 },
27711
27712 buildRendering: function(){
27713 this.inherited(arguments);
27714 dom.setSelectable(this.focusNode, false);
27715 },
27716
27717 _fillContent: function(){
27718 // summary:
27719 // Loads our options and sets up our dropdown correctly. We
27720 // don't want any content, so we don't call any inherit chain
27721 // function.
27722 if(!this.options){
27723 this.options =
27724 this.srcNodeRef
27725 ? query("> *", this.srcNodeRef).map(
27726 function(node){
27727 if(node.getAttribute("type") === "separator"){
27728 return { value: "", label: "", selected: false, disabled: false };
27729 }
27730 return {
27731 value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
27732 label: String(node.innerHTML),
27733 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
27734 // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
27735 // decide before 1.6
27736 selected: node.getAttribute("selected") || false,
27737 disabled: node.getAttribute("disabled") || false
27738 };
27739 },
27740 this)
27741 : [];
27742 }
27743 if(!this.value){
27744 this._set("value", this._getValueFromOpts());
27745 }else if(this.multiple && typeof this.value == "string"){
27746 this._set("value", this.value.split(","));
27747 }
27748 },
27749
27750 postCreate: function(){
27751 // summary:
27752 // sets up our event handling that we need for functioning
27753 // as a select
27754 this.inherited(arguments);
27755
27756 // Make our event connections for updating state
27757 this.connect(this, "onChange", "_updateSelection");
27758
27759 // moved from startup
27760 // Connects in our store, if we have one defined
27761 var store = this.store;
27762 if(store && (store.getIdentity || store.getFeatures()["dojo.data.api.Identity"])){
27763 // Temporarily set our store to null so that it will get set
27764 // and connected appropriately
27765 this.store = null;
27766 this.setStore(store, this._oValue);
27767 }
27768 },
27769
27770 startup: function(){
27771 // summary:
27772 this._loadChildren();
27773 this.inherited(arguments);
27774 },
27775
27776 destroy: function(){
27777 // summary:
27778 // Clean up our connections
27779
27780 var h;
27781 while((h = this._notifyConnections.pop())){ h.remove(); }
27782
27783 // Cancel listener for store updates
27784 if(this._queryRes && this._queryRes.close){
27785 this._queryRes.close();
27786 }
27787
27788 this.inherited(arguments);
27789 },
27790
27791 _addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
27792 // summary:
27793 // User-overridable function which, for the given option, adds an
27794 // item to the select. If the option doesn't have a value, then a
27795 // separator is added in that place. Make sure to store the option
27796 // in the created option widget.
27797 },
27798
27799 _removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
27800 // summary:
27801 // User-overridable function which, for the given option, removes
27802 // its item from the select.
27803 },
27804
27805 _setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
27806 // summary:
27807 // Overridable function which will set the display for the
27808 // widget. newDisplay is either a string (in the case of
27809 // single selects) or array of strings (in the case of multi-selects)
27810 },
27811
27812 _getChildren: function(){
27813 // summary:
27814 // Overridable function to return the children that this widget contains.
27815 return [];
27816 },
27817
27818 _getSelectedOptionsAttr: function(){
27819 // summary:
27820 // hooks into this.attr to provide a mechanism for getting the
27821 // option items for the current value of the widget.
27822 return this.getOptions(this.get("value"));
27823 },
27824
27825 _pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
27826 // summary:
27827 // a function that will "fake" loading children, if needed, and
27828 // if we have set to not load children until the widget opens.
27829 // items:
27830 // An array of items that will be loaded, when needed
27831 },
27832
27833 onSetStore: function(){
27834 // summary:
27835 // a function that can be connected to in order to receive a
27836 // notification that the store has finished loading and all options
27837 // from that store are available
27838 }
27839 });
27840
27841 /*=====
27842 _FormSelectWidget.__SelectOption = __SelectOption;
27843 =====*/
27844
27845 return _FormSelectWidget;
27846
27847 });
27848
27849 },
27850 'dijit/form/Select':function(){
27851 require({cache:{
27852 '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"}});
27853 define("dijit/form/Select", [
27854 "dojo/_base/array", // array.forEach
27855 "dojo/_base/declare", // declare
27856 "dojo/dom-attr", // domAttr.set
27857 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
27858 "dojo/dom-geometry", // domGeometry.setMarginBox
27859 "dojo/_base/event", // event.stop
27860 "dojo/i18n", // i18n.getLocalization
27861 "dojo/_base/lang", // lang.hitch
27862 "dojo/sniff", // has("ie")
27863 "./_FormSelectWidget",
27864 "../_HasDropDown",
27865 "../Menu",
27866 "../MenuItem",
27867 "../MenuSeparator",
27868 "../Tooltip",
27869 "dojo/text!./templates/Select.html",
27870 "dojo/i18n!./nls/validate"
27871 ], function(array, declare, domAttr, domClass, domGeometry, event, i18n, lang, has,
27872 _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
27873
27874 // module:
27875 // dijit/form/Select
27876
27877
27878 var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
27879 // summary:
27880 // An internally-used menu for dropdown that allows us a vertical scrollbar
27881
27882 // Override Menu.autoFocus setting so that opening a Select highlights the current value.
27883 autoFocus: true,
27884
27885 buildRendering: function(){
27886 // summary:
27887 // Stub in our own changes, so that our domNode is not a table
27888 // otherwise, we won't respond correctly to heights/overflows
27889 this.inherited(arguments);
27890 var o = (this.menuTableNode = this.domNode);
27891 var n = (this.domNode = this.ownerDocument.createElement("div"));
27892 n.style.cssText = "overflow-x: hidden; overflow-y: scroll";
27893 if(o.parentNode){
27894 o.parentNode.replaceChild(n, o);
27895 }
27896 domClass.remove(o, "dijitMenuTable");
27897 n.className = o.className + " dijitSelectMenu";
27898 o.className = "dijitReset dijitMenuTable";
27899 o.setAttribute("role", "listbox");
27900 n.setAttribute("role", "presentation");
27901 n.appendChild(o);
27902 },
27903
27904 postCreate: function(){
27905 // summary:
27906 // stop mousemove from selecting text on IE to be consistent with other browsers
27907
27908 this.inherited(arguments);
27909
27910 this.connect(this.domNode, "onselectstart", event.stop);
27911 },
27912
27913
27914 focus: function(){
27915 // summary:
27916 // Overridden so that the previously selected value will be focused instead of only the first item
27917 var found = false,
27918 val = this.parentWidget.value;
27919 if(lang.isArray(val)){
27920 val = val[val.length-1];
27921 }
27922 if(val){ // if focus selected
27923 array.forEach(this.parentWidget._getChildren(), function(child){
27924 if(child.option && (val === child.option.value)){ // find menu item widget with this value
27925 found = true;
27926 this.focusChild(child, false); // focus previous selection
27927 }
27928 }, this);
27929 }
27930 if(!found){
27931 this.inherited(arguments); // focus first item by default
27932 }
27933 },
27934
27935 resize: function(/*Object*/ mb){
27936 // summary:
27937 // Overridden so that we are able to handle resizing our
27938 // internal widget. Note that this is not a "full" resize
27939 // implementation - it only works correctly if you pass it a
27940 // marginBox.
27941 //
27942 // mb: Object
27943 // The margin box to set this dropdown to.
27944 if(mb){
27945 domGeometry.setMarginBox(this.domNode, mb);
27946 if("w" in mb){
27947 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
27948 // 100% is safer than a pixel value because there may be a scroll bar with
27949 // browser/OS specific width.
27950 this.menuTableNode.style.width = "100%";
27951 }
27952 }
27953 }
27954 });
27955
27956 var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
27957 // summary:
27958 // This is a "styleable" select box - it is basically a DropDownButton which
27959 // can take a `<select>` as its input.
27960
27961 baseClass: "dijitSelect dijitValidationTextBox",
27962
27963 templateString: template,
27964
27965 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
27966
27967 // required: Boolean
27968 // Can be true or false, default is false.
27969 required: false,
27970
27971 // state: [readonly] String
27972 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
27973 state: "",
27974
27975 // message: String
27976 // Currently displayed error/prompt message
27977 message: "",
27978
27979 // tooltipPosition: String[]
27980 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
27981 tooltipPosition: [],
27982
27983 // emptyLabel: string
27984 // What to display in an "empty" dropdown
27985 emptyLabel: "&#160;", // &nbsp;
27986
27987 // _isLoaded: Boolean
27988 // Whether or not we have been loaded
27989 _isLoaded: false,
27990
27991 // _childrenLoaded: Boolean
27992 // Whether or not our children have been loaded
27993 _childrenLoaded: false,
27994
27995 _fillContent: function(){
27996 // summary:
27997 // Set the value to be the first, or the selected index
27998 this.inherited(arguments);
27999 // set value from selected option
28000 if(this.options.length && !this.value && this.srcNodeRef){
28001 var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
28002 this.value = this.options[si >= 0 ? si : 0].value;
28003 }
28004 // Create the dropDown widget
28005 this.dropDown = new _SelectMenu({ id: this.id + "_menu", parentWidget: this });
28006 domClass.add(this.dropDown.domNode, this.baseClass.replace(/\s+|$/g, "Menu "));
28007 },
28008
28009 _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){
28010 // summary:
28011 // For the given option, return the menu item that should be
28012 // used to display it. This can be overridden as needed
28013 if(!option.value && !option.label){
28014 // We are a separator (no label set for it)
28015 return new MenuSeparator({ownerDocument: this.ownerDocument});
28016 }else{
28017 // Just a regular menu option
28018 var click = lang.hitch(this, "_setValueAttr", option);
28019 var item = new MenuItem({
28020 option: option,
28021 label: option.label || this.emptyLabel,
28022 onClick: click,
28023 ownerDocument: this.ownerDocument,
28024 dir: this.dir,
28025 disabled: option.disabled || false
28026 });
28027 item.focusNode.setAttribute("role", "option");
28028 return item;
28029 }
28030 },
28031
28032 _addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option){
28033 // summary:
28034 // For the given option, add an option to our dropdown.
28035 // If the option doesn't have a value, then a separator is added
28036 // in that place.
28037 if(this.dropDown){
28038 this.dropDown.addChild(this._getMenuItemForOption(option));
28039 }
28040 },
28041
28042 _getChildren: function(){
28043 if(!this.dropDown){
28044 return [];
28045 }
28046 return this.dropDown.getChildren();
28047 },
28048
28049 _loadChildren: function(/*Boolean*/ loadMenuItems){
28050 // summary:
28051 // Resets the menu and the length attribute of the button - and
28052 // ensures that the label is appropriately set.
28053 // loadMenuItems: Boolean
28054 // actually loads the child menu items - we only do this when we are
28055 // populating for showing the dropdown.
28056
28057 if(loadMenuItems === true){
28058 // this.inherited destroys this.dropDown's child widgets (MenuItems).
28059 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
28060 // issues later in _setSelected). (see #10296)
28061 if(this.dropDown){
28062 delete this.dropDown.focusedChild;
28063 }
28064 if(this.options.length){
28065 this.inherited(arguments);
28066 }else{
28067 // Drop down menu is blank but add one blank entry just so something appears on the screen
28068 // to let users know that they are no choices (mimicing native select behavior)
28069 array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
28070 var item = new MenuItem({
28071 ownerDocument: this.ownerDocument,
28072 label: this.emptyLabel
28073 });
28074 this.dropDown.addChild(item);
28075 }
28076 }else{
28077 this._updateSelection();
28078 }
28079
28080 this._isLoaded = false;
28081 this._childrenLoaded = true;
28082
28083 if(!this._loadingStore){
28084 // Don't call this if we are loading - since we will handle it later
28085 this._setValueAttr(this.value, false);
28086 }
28087 },
28088
28089 _refreshState: function(){
28090 if(this._started){
28091 this.validate(this.focused);
28092 }
28093 },
28094
28095 startup: function(){
28096 this.inherited(arguments);
28097 this._refreshState(); // after all _set* methods have run
28098 },
28099
28100 _setValueAttr: function(value){
28101 this.inherited(arguments);
28102 domAttr.set(this.valueNode, "value", this.get("value"));
28103 this._refreshState(); // to update this.state
28104 },
28105
28106 _setDisabledAttr: function(/*Boolean*/ value){
28107 this.inherited(arguments);
28108 this._refreshState(); // to update this.state
28109 },
28110
28111 _setRequiredAttr: function(/*Boolean*/ value){
28112 this._set("required", value);
28113 this.focusNode.setAttribute("aria-required", value);
28114 this._refreshState(); // to update this.state
28115 },
28116
28117 _setOptionsAttr: function(/*Array*/ options){
28118 this._isLoaded = false;
28119 this._set('options', options);
28120 },
28121
28122 _setDisplay: function(/*String*/ newDisplay){
28123 // summary:
28124 // sets the display for the given value (or values)
28125 var lbl = newDisplay || this.emptyLabel;
28126 this.containerNode.innerHTML = '<span role="option" class="dijitReset dijitInline ' + this.baseClass.replace(/\s+|$/g, "Label ")+'">' + lbl + '</span>';
28127 },
28128
28129 validate: function(/*Boolean*/ isFocused){
28130 // summary:
28131 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
28132 // description:
28133 // Show missing or invalid messages if appropriate, and highlight textbox field.
28134 // Used when a select is initially set to no value and the user is required to
28135 // set the value.
28136
28137 var isValid = this.disabled || this.isValid(isFocused);
28138 this._set("state", isValid ? "" : (this._hasBeenBlurred ? "Error" : "Incomplete"));
28139 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
28140 var message = isValid ? "" : this._missingMsg;
28141 if(message && this.focused && this._hasBeenBlurred){
28142 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
28143 }else{
28144 Tooltip.hide(this.domNode);
28145 }
28146 this._set("message", message);
28147 return isValid;
28148 },
28149
28150 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
28151 // summary:
28152 // Whether or not this is a valid value. The only way a Select
28153 // can be invalid is when it's required but nothing is selected.
28154 return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
28155 },
28156
28157 reset: function(){
28158 // summary:
28159 // Overridden so that the state will be cleared.
28160 this.inherited(arguments);
28161 Tooltip.hide(this.domNode);
28162 this._refreshState(); // to update this.state
28163 },
28164
28165 postMixInProperties: function(){
28166 // summary:
28167 // set the missing message
28168 this.inherited(arguments);
28169 this._missingMsg = i18n.getLocalization("dijit.form", "validate", this.lang).missingMessage;
28170 },
28171
28172 postCreate: function(){
28173 // summary:
28174 // stop mousemove from selecting text on IE to be consistent with other browsers
28175
28176 this.inherited(arguments);
28177
28178 this.connect(this.domNode, "onselectstart", event.stop);
28179 this.domNode.setAttribute("aria-expanded", "false");
28180
28181 if(has("ie") < 9){
28182 // IE INPUT tag fontFamily has to be set directly using STYLE
28183 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
28184 this.defer(function(){
28185 try{
28186 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
28187 if(s){
28188 var ff = s.fontFamily;
28189 if(ff){
28190 var inputs = this.domNode.getElementsByTagName("INPUT");
28191 if(inputs){
28192 for(var i=0; i < inputs.length; i++){
28193 inputs[i].style.fontFamily = ff;
28194 }
28195 }
28196 }
28197 }
28198 }catch(e){/*when used in a Dialog, and this is called before the dialog is
28199 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
28200 });
28201 }
28202 },
28203
28204 _setStyleAttr: function(/*String||Object*/ value){
28205 this.inherited(arguments);
28206 domClass.toggle(this.domNode, this.baseClass.replace(/\s+|$/g, "FixedWidth "), !!this.domNode.style.width);
28207 },
28208
28209 isLoaded: function(){
28210 return this._isLoaded;
28211 },
28212
28213 loadDropDown: function(/*Function*/ loadCallback){
28214 // summary:
28215 // populates the menu
28216 this._loadChildren(true);
28217 this._isLoaded = true;
28218 loadCallback();
28219 },
28220
28221 closeDropDown: function(){
28222 // overriding _HasDropDown.closeDropDown()
28223 this.inherited(arguments);
28224
28225 if(this.dropDown && this.dropDown.menuTableNode){
28226 // Erase possible width: 100% setting from _SelectMenu.resize().
28227 // Leaving it would interfere with the next openDropDown() call, which
28228 // queries the natural size of the drop down.
28229 this.dropDown.menuTableNode.style.width = "";
28230 }
28231 },
28232
28233 destroy: function(preserveDom){
28234 if(this.dropDown && !this.dropDown._destroyed){
28235 this.dropDown.destroyRecursive(preserveDom);
28236 delete this.dropDown;
28237 }
28238 this.inherited(arguments);
28239 },
28240
28241 _onFocus: function(){
28242 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
28243 this.inherited(arguments);
28244 },
28245
28246 _onBlur: function(){
28247 Tooltip.hide(this.domNode);
28248 this.inherited(arguments);
28249 this.validate(false);
28250 }
28251 });
28252
28253 Select._Menu = _SelectMenu; // for monkey patching
28254
28255 return Select;
28256 });
28257
28258 },
28259 'dojo/store/util/QueryResults':function(){
28260 define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
28261 ], function(array, lang, Deferred){
28262
28263 // module:
28264 // dojo/store/util/QueryResults
28265
28266 var QueryResults = function(results){
28267 // summary:
28268 // A function that wraps the results of a store query with additional
28269 // methods.
28270 // description:
28271 // QueryResults is a basic wrapper that allows for array-like iteration
28272 // over any kind of returned data from a query. While the simplest store
28273 // will return a plain array of data, other stores may return deferreds or
28274 // promises; this wrapper makes sure that *all* results can be treated
28275 // the same.
28276 //
28277 // Additional methods include `forEach`, `filter` and `map`.
28278 // results: Array|dojo/promise/Promise
28279 // The result set as an array, or a promise for an array.
28280 // returns:
28281 // An array-like object that can be used for iterating over.
28282 // example:
28283 // Query a store and iterate over the results.
28284 //
28285 // | store.query({ prime: true }).forEach(function(item){
28286 // | // do something
28287 // | });
28288
28289 if(!results){
28290 return results;
28291 }
28292 // if it is a promise it may be frozen
28293 if(results.then){
28294 results = lang.delegate(results);
28295 }
28296 function addIterativeMethod(method){
28297 if(!results[method]){
28298 results[method] = function(){
28299 var args = arguments;
28300 return Deferred.when(results, function(results){
28301 Array.prototype.unshift.call(args, results);
28302 return QueryResults(array[method].apply(array, args));
28303 });
28304 };
28305 }
28306 }
28307 addIterativeMethod("forEach");
28308 addIterativeMethod("filter");
28309 addIterativeMethod("map");
28310 if(!results.total){
28311 results.total = Deferred.when(results, function(results){
28312 return results.length;
28313 });
28314 }
28315 return results; // Object
28316 };
28317
28318 lang.setObject("dojo.store.util.QueryResults", QueryResults);
28319
28320 return QueryResults;
28321
28322 });
28323
28324 },
28325 'dijit/form/_ListBase':function(){
28326 define("dijit/form/_ListBase", [
28327 "dojo/_base/declare", // declare
28328 "dojo/on",
28329 "dojo/window" // winUtils.scrollIntoView
28330 ], function(declare, on, winUtils){
28331
28332 // module:
28333 // dijit/form/_ListBase
28334
28335 return declare( "dijit.form._ListBase", null, {
28336 // summary:
28337 // Focus-less menu to handle UI events consistently
28338 // Abstract methods that must be defined externally:
28339 //
28340 // - onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
28341 // - onDeselect: cancels onSelect
28342 // tags:
28343 // private
28344
28345 // selected: DOMNode
28346 // currently selected node
28347 selected: null,
28348
28349 _listConnect: function(/*String|Function*/ eventType, /*String*/ callbackFuncName){
28350 // summary:
28351 // Connects 'containerNode' to specified method of this object
28352 // and automatically registers for 'disconnect' on widget destroy.
28353 // description:
28354 // Provide widget-specific analog to 'connect'.
28355 // The callback function is called with the normal event object,
28356 // but also a second parameter is passed that indicates which list item
28357 // actually received the event.
28358 // returns:
28359 // A handle that can be passed to `disconnect` in order to disconnect
28360 // before the widget is destroyed.
28361 // tags:
28362 // private
28363
28364 var self = this;
28365 return self.own(on(self.containerNode,
28366 on.selector(
28367 function(eventTarget, selector, target){
28368 return eventTarget.parentNode == target;
28369 },
28370 eventType
28371 ),
28372 function(evt){
28373 evt.preventDefault();
28374 self[callbackFuncName](evt, this);
28375 }
28376 ));
28377 },
28378
28379 selectFirstNode: function(){
28380 // summary:
28381 // Select the first displayed item in the list.
28382 var first = this.containerNode.firstChild;
28383 while(first && first.style.display == "none"){
28384 first = first.nextSibling;
28385 }
28386 this._setSelectedAttr(first);
28387 },
28388
28389 selectLastNode: function(){
28390 // summary:
28391 // Select the last displayed item in the list
28392 var last = this.containerNode.lastChild;
28393 while(last && last.style.display == "none"){
28394 last = last.previousSibling;
28395 }
28396 this._setSelectedAttr(last);
28397 },
28398
28399 selectNextNode: function(){
28400 // summary:
28401 // Select the item just below the current selection.
28402 // If nothing selected, select first node.
28403 var selectedNode = this.selected;
28404 if(!selectedNode){
28405 this.selectFirstNode();
28406 }else{
28407 var next = selectedNode.nextSibling;
28408 while(next && next.style.display == "none"){
28409 next = next.nextSibling;
28410 }
28411 if(!next){
28412 this.selectFirstNode();
28413 }else{
28414 this._setSelectedAttr(next);
28415 }
28416 }
28417 },
28418
28419 selectPreviousNode: function(){
28420 // summary:
28421 // Select the item just above the current selection.
28422 // If nothing selected, select last node (if
28423 // you select Previous and try to keep scrolling up the list).
28424 var selectedNode = this.selected;
28425 if(!selectedNode){
28426 this.selectLastNode();
28427 }else{
28428 var prev = selectedNode.previousSibling;
28429 while(prev && prev.style.display == "none"){
28430 prev = prev.previousSibling;
28431 }
28432 if(!prev){
28433 this.selectLastNode();
28434 }else{
28435 this._setSelectedAttr(prev);
28436 }
28437 }
28438 },
28439
28440 _setSelectedAttr: function(/*DomNode*/ node){
28441 // summary:
28442 // Does the actual select.
28443 if(this.selected != node){
28444 var selectedNode = this.selected;
28445 if(selectedNode){
28446 this.onDeselect(selectedNode);
28447 this.selected = null;
28448 }
28449 if(node){
28450 this.selected = node;
28451 winUtils.scrollIntoView(node);
28452 this.onSelect(node);
28453 }
28454 }else if(node){
28455 this.onSelect(node);
28456 }
28457 }
28458 });
28459
28460 });
28461
28462 },
28463 'dijit/form/_FormWidget':function(){
28464 define("dijit/form/_FormWidget", [
28465 "dojo/_base/declare", // declare
28466 "dojo/has", // has("dijit-legacy-requires")
28467 "dojo/_base/kernel", // kernel.deprecated
28468 "dojo/ready",
28469 "../_Widget",
28470 "../_CssStateMixin",
28471 "../_TemplatedMixin",
28472 "./_FormWidgetMixin"
28473 ], function(declare, has, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){
28474
28475
28476 // module:
28477 // dijit/form/_FormWidget
28478
28479 // Back compat w/1.6, remove for 2.0
28480 if(has("dijit-legacy-requires")){
28481 ready(0, function(){
28482 var requires = ["dijit/form/_FormValueWidget"];
28483 require(requires); // use indirection so modules not rolled into a build
28484 });
28485 }
28486
28487 return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
28488 // summary:
28489 // Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
28490 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
28491 //
28492 // description:
28493 // Represents a single HTML element.
28494 // All these widgets should have these attributes just like native HTML input elements.
28495 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
28496 //
28497 // They also share some common methods.
28498
28499 setDisabled: function(/*Boolean*/ disabled){
28500 // summary:
28501 // Deprecated. Use set('disabled', ...) instead.
28502 kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
28503 this.set('disabled', disabled);
28504 },
28505
28506 setValue: function(/*String*/ value){
28507 // summary:
28508 // Deprecated. Use set('value', ...) instead.
28509 kernel.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
28510 this.set('value', value);
28511 },
28512
28513 getValue: function(){
28514 // summary:
28515 // Deprecated. Use get('value') instead.
28516 kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
28517 return this.get('value');
28518 },
28519
28520 postMixInProperties: function(){
28521 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
28522 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
28523 // Regarding escaping, see heading "Attribute values" in
28524 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
28525 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/"/g, "&quot;") + '"') : '';
28526 this.inherited(arguments);
28527 },
28528
28529 // Override automatic assigning type --> focusNode, it causes exception on IE.
28530 // Instead, type must be specified as ${type} in the template, as part of the original DOM
28531 _setTypeAttr: null
28532 });
28533
28534 });
28535
28536 },
28537 'dojo/DeferredList':function(){
28538 define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
28539 // module:
28540 // dojo/DeferredList
28541
28542
28543 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
28544 // summary:
28545 // Deprecated, use dojo/promise/all instead.
28546 // Provides event handling for a group of Deferred objects.
28547 // description:
28548 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
28549 // this new deferred will typically have its callback fired when all of the deferreds in
28550 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
28551 // fireOnOneErrback, will fire before all the deferreds as appropriate
28552 // list:
28553 // The list of deferreds to be synchronizied with this DeferredList
28554 // fireOnOneCallback:
28555 // Will cause the DeferredLists callback to be fired as soon as any
28556 // of the deferreds in its list have been fired instead of waiting until
28557 // the entire list has finished
28558 // fireonOneErrback:
28559 // Will cause the errback to fire upon any of the deferreds errback
28560 // canceller:
28561 // A deferred canceller function, see dojo.Deferred
28562 var resultList = [];
28563 Deferred.call(this);
28564 var self = this;
28565 if(list.length === 0 && !fireOnOneCallback){
28566 this.resolve([0, []]);
28567 }
28568 var finished = 0;
28569 darray.forEach(list, function(item, i){
28570 item.then(function(result){
28571 if(fireOnOneCallback){
28572 self.resolve([i, result]);
28573 }else{
28574 addResult(true, result);
28575 }
28576 },function(error){
28577 if(fireOnOneErrback){
28578 self.reject(error);
28579 }else{
28580 addResult(false, error);
28581 }
28582 if(consumeErrors){
28583 return null;
28584 }
28585 throw error;
28586 });
28587 function addResult(succeeded, result){
28588 resultList[i] = [succeeded, result];
28589 finished++;
28590 if(finished === list.length){
28591 self.resolve(resultList);
28592 }
28593
28594 }
28595 });
28596 };
28597 dojo.DeferredList.prototype = new Deferred();
28598
28599 dojo.DeferredList.prototype.gatherResults = function(deferredList){
28600 // summary:
28601 // Gathers the results of the deferreds for packaging
28602 // as the parameters to the Deferred Lists' callback
28603 // deferredList: dojo/DeferredList
28604 // The deferred list from which this function gathers results.
28605 // returns: dojo/DeferredList
28606 // The newly created deferred list which packs results as
28607 // parameters to its callback.
28608
28609 var d = new dojo.DeferredList(deferredList, false, true, false);
28610 d.addCallback(function(results){
28611 var ret = [];
28612 darray.forEach(results, function(result){
28613 ret.push(result[1]);
28614 });
28615 return ret;
28616 });
28617 return d;
28618 };
28619
28620 return dojo.DeferredList;
28621 });
28622
28623 },
28624 'dojo/dnd/common':function(){
28625 define("dojo/dnd/common", ["../_base/connect", "../_base/kernel", "../_base/lang", "../dom"],
28626 function(connect, kernel, lang, dom){
28627
28628 // module:
28629 // dojo/dnd/common
28630
28631 var exports = lang.getObject("dojo.dnd", true);
28632 /*=====
28633 // TODO: for 2.0, replace line above with this code.
28634 var exports = {
28635 // summary:
28636 // TODOC
28637 };
28638 =====*/
28639
28640 exports.getCopyKeyState = connect.isCopyKey;
28641
28642 exports._uniqueId = 0;
28643 exports.getUniqueId = function(){
28644 // summary:
28645 // returns a unique string for use with any DOM element
28646 var id;
28647 do{
28648 id = kernel._scopeName + "Unique" + (++exports._uniqueId);
28649 }while(dom.byId(id));
28650 return id;
28651 };
28652
28653 exports._empty = {};
28654
28655 exports.isFormElement = function(/*Event*/ e){
28656 // summary:
28657 // returns true if user clicked on a form element
28658 var t = e.target;
28659 if(t.nodeType == 3 /*TEXT_NODE*/){
28660 t = t.parentNode;
28661 }
28662 return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
28663 };
28664
28665 return exports;
28666 });
28667
28668 },
28669 'dijit/Viewport':function(){
28670 define("dijit/Viewport", [
28671 "dojo/Evented",
28672 "dojo/on",
28673 "dojo/ready",
28674 "dojo/sniff",
28675 "dojo/_base/window", // global
28676 "dojo/window" // getBox()
28677 ], function(Evented, on, ready, has, win, winUtils){
28678
28679 // module:
28680 // dijit/Viewport
28681
28682 /*=====
28683 return {
28684 // summary:
28685 // Utility singleton to watch for viewport resizes, avoiding duplicate notifications
28686 // which can lead to infinite loops.
28687 // description:
28688 // Usage: Viewport.on("resize", myCallback).
28689 //
28690 // myCallback() is called without arguments in case it's _WidgetBase.resize(),
28691 // which would interpret the argument as the size to make the widget.
28692 };
28693 =====*/
28694
28695 var Viewport = new Evented();
28696
28697 ready(200, function(){
28698 var oldBox = winUtils.getBox();
28699 Viewport._rlh = on(win.global, "resize", function(){
28700 var newBox = winUtils.getBox();
28701 if(oldBox.h == newBox.h && oldBox.w == newBox.w){ return; }
28702 oldBox = newBox;
28703 Viewport.emit("resize");
28704 });
28705
28706 // Also catch zoom changes on IE8, since they don't naturally generate resize events
28707 if(has("ie") == 8){
28708 var deviceXDPI = screen.deviceXDPI;
28709 setInterval(function(){
28710 if(screen.deviceXDPI != deviceXDPI){
28711 deviceXDPI = screen.deviceXDPI;
28712 Viewport.emit("resize");
28713 }
28714 }, 500);
28715 }
28716 });
28717
28718 return Viewport;
28719 });
28720
28721 },
28722 'dijit/_base/place':function(){
28723 define("dijit/_base/place", [
28724 "dojo/_base/array", // array.forEach
28725 "dojo/_base/lang", // lang.isArray, lang.mixin
28726 "dojo/window", // windowUtils.getBox
28727 "../place",
28728 "../main" // export to dijit namespace
28729 ], function(array, lang, windowUtils, place, dijit){
28730
28731 // module:
28732 // dijit/_base/place
28733
28734
28735 var exports = {
28736 // summary:
28737 // Deprecated back compatibility module, new code should use dijit/place directly instead of using this module.
28738 };
28739
28740 exports.getViewport = function(){
28741 // summary:
28742 // Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
28743 // New code should use windowUtils.getBox()
28744
28745 return windowUtils.getBox();
28746 };
28747
28748 exports.placeOnScreen = place.at;
28749
28750 exports.placeOnScreenAroundElement = function(node, aroundNode, aroundCorners, layoutNode){
28751 // summary:
28752 // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
28753 // for the "around" argument and finds a proper processor to place a node.
28754 // Deprecated, new code should use dijit/place.around() instead.
28755
28756 // Convert old style {"BL": "TL", "BR": "TR"} type argument
28757 // to style needed by dijit.place code:
28758 // [
28759 // {aroundCorner: "BL", corner: "TL" },
28760 // {aroundCorner: "BR", corner: "TR" }
28761 // ]
28762 var positions;
28763 if(lang.isArray(aroundCorners)){
28764 positions = aroundCorners;
28765 }else{
28766 positions = [];
28767 for(var key in aroundCorners){
28768 positions.push({aroundCorner: key, corner: aroundCorners[key]});
28769 }
28770 }
28771
28772 return place.around(node, aroundNode, positions, true, layoutNode);
28773 };
28774
28775 exports.placeOnScreenAroundNode = exports.placeOnScreenAroundElement;
28776 /*=====
28777 exports.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
28778 // summary:
28779 // Position node adjacent or kitty-corner to aroundNode
28780 // such that it's fully visible in viewport.
28781 // Deprecated, new code should use dijit/place.around() instead.
28782 };
28783 =====*/
28784
28785 exports.placeOnScreenAroundRectangle = exports.placeOnScreenAroundElement;
28786 /*=====
28787 exports.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, layoutNode){
28788 // summary:
28789 // Like dijit.placeOnScreenAroundNode(), except that the "around"
28790 // parameter is an arbitrary rectangle on the screen (x, y, width, height)
28791 // instead of a dom node.
28792 // Deprecated, new code should use dijit/place.around() instead.
28793 };
28794 =====*/
28795
28796 exports.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
28797 // summary:
28798 // Deprecated method, unneeded when using dijit/place directly.
28799 // Transforms the passed array of preferred positions into a format suitable for
28800 // passing as the aroundCorners argument to dijit/place.placeOnScreenAroundElement.
28801 // position: String[]
28802 // This variable controls the position of the drop down.
28803 // It's an array of strings with the following values:
28804 //
28805 // - before: places drop down to the left of the target node/widget, or to the right in
28806 // the case of RTL scripts like Hebrew and Arabic
28807 // - after: places drop down to the right of the target node/widget, or to the left in
28808 // the case of RTL scripts like Hebrew and Arabic
28809 // - above: drop down goes above target node
28810 // - below: drop down goes below target node
28811 //
28812 // The list is positions is tried, in order, until a position is found where the drop down fits
28813 // within the viewport.
28814 // leftToRight: Boolean
28815 // Whether the popup will be displaying in leftToRight mode.
28816
28817 var align = {};
28818 array.forEach(position, function(pos){
28819 var ltr = leftToRight;
28820 switch(pos){
28821 case "after":
28822 align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
28823 break;
28824 case "before":
28825 align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
28826 break;
28827 case "below-alt":
28828 ltr = !ltr;
28829 // fall through
28830 case "below":
28831 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
28832 align[ltr ? "BL" : "BR"] = ltr ? "TL" : "TR";
28833 align[ltr ? "BR" : "BL"] = ltr ? "TR" : "TL";
28834 break;
28835 case "above-alt":
28836 ltr = !ltr;
28837 // fall through
28838 case "above":
28839 default:
28840 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
28841 align[ltr ? "TL" : "TR"] = ltr ? "BL" : "BR";
28842 align[ltr ? "TR" : "TL"] = ltr ? "BR" : "BL";
28843 break;
28844 }
28845 });
28846 return align;
28847 };
28848
28849 lang.mixin(dijit, exports);
28850
28851 /*===== return exports; =====*/
28852 return dijit; // for back compat :-(
28853 });
28854
28855 },
28856 'dijit/MenuSeparator':function(){
28857 require({cache:{
28858 '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>"}});
28859 define("dijit/MenuSeparator", [
28860 "dojo/_base/declare", // declare
28861 "dojo/dom", // dom.setSelectable
28862 "./_WidgetBase",
28863 "./_TemplatedMixin",
28864 "./_Contained",
28865 "dojo/text!./templates/MenuSeparator.html"
28866 ], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
28867
28868 // module:
28869 // dijit/MenuSeparator
28870
28871 return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
28872 // summary:
28873 // A line between two menu items
28874
28875 templateString: template,
28876
28877 buildRendering: function(){
28878 this.inherited(arguments);
28879 dom.setSelectable(this.domNode, false);
28880 },
28881
28882 isFocusable: function(){
28883 // summary:
28884 // Override to always return false
28885 // tags:
28886 // protected
28887
28888 return false; // Boolean
28889 }
28890 });
28891 });
28892
28893 },
28894 'dijit/form/_ComboBoxMenu':function(){
28895 define("dijit/form/_ComboBoxMenu", [
28896 "dojo/_base/declare", // declare
28897 "dojo/dom-class", // domClass.add domClass.remove
28898 "dojo/dom-style", // domStyle.get
28899 "dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
28900 "../_WidgetBase",
28901 "../_TemplatedMixin",
28902 "./_ComboBoxMenuMixin",
28903 "./_ListMouseMixin"
28904 ], function(declare, domClass, domStyle, keys,
28905 _WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
28906
28907
28908 // module:
28909 // dijit/form/_ComboBoxMenu
28910
28911 return declare("dijit.form._ComboBoxMenu",[_WidgetBase, _TemplatedMixin, _ListMouseMixin, _ComboBoxMenuMixin], {
28912 // summary:
28913 // Focus-less menu for internal use in `dijit/form/ComboBox`
28914 // Abstract methods that must be defined externally:
28915 //
28916 // - onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
28917 // - onPage: next(1) or previous(-1) button pressed
28918 // tags:
28919 // private
28920
28921 templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;' role='listbox'>"
28922 +"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
28923 +"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
28924 +"</div>",
28925
28926 baseClass: "dijitComboBoxMenu",
28927
28928 postCreate: function(){
28929 this.inherited(arguments);
28930 if(!this.isLeftToRight()){
28931 domClass.add(this.previousButton, "dijitMenuItemRtl");
28932 domClass.add(this.nextButton, "dijitMenuItemRtl");
28933 }
28934 },
28935
28936 _createMenuItem: function(){
28937 // note: not using domConstruct.create() because need to specify document
28938 var item = this.ownerDocument.createElement("div");
28939 item.className = "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl");
28940 item.setAttribute("role", "option");
28941 return item;
28942 },
28943
28944 onHover: function(/*DomNode*/ node){
28945 // summary:
28946 // Add hover CSS
28947 domClass.add(node, "dijitMenuItemHover");
28948 },
28949
28950 onUnhover: function(/*DomNode*/ node){
28951 // summary:
28952 // Remove hover CSS
28953 domClass.remove(node, "dijitMenuItemHover");
28954 },
28955
28956 onSelect: function(/*DomNode*/ node){
28957 // summary:
28958 // Add selected CSS
28959 domClass.add(node, "dijitMenuItemSelected");
28960 },
28961
28962 onDeselect: function(/*DomNode*/ node){
28963 // summary:
28964 // Remove selected CSS
28965 domClass.remove(node, "dijitMenuItemSelected");
28966 },
28967
28968 _page: function(/*Boolean*/ up){
28969 // summary:
28970 // Handles page-up and page-down keypresses
28971
28972 var scrollamount = 0;
28973 var oldscroll = this.domNode.scrollTop;
28974 var height = domStyle.get(this.domNode, "height");
28975 // if no item is highlighted, highlight the first option
28976 if(!this.getHighlightedOption()){
28977 this.selectNextNode();
28978 }
28979 while(scrollamount<height){
28980 var highlighted_option = this.getHighlightedOption();
28981 if(up){
28982 // stop at option 1
28983 if(!highlighted_option.previousSibling ||
28984 highlighted_option.previousSibling.style.display == "none"){
28985 break;
28986 }
28987 this.selectPreviousNode();
28988 }else{
28989 // stop at last option
28990 if(!highlighted_option.nextSibling ||
28991 highlighted_option.nextSibling.style.display == "none"){
28992 break;
28993 }
28994 this.selectNextNode();
28995 }
28996 // going backwards
28997 var newscroll = this.domNode.scrollTop;
28998 scrollamount += (newscroll-oldscroll)*(up ? -1:1);
28999 oldscroll = newscroll;
29000 }
29001 },
29002
29003 handleKey: function(evt){
29004 // summary:
29005 // Handle keystroke event forwarded from ComboBox, returning false if it's
29006 // a keystroke I recognize and process, true otherwise.
29007 switch(evt.keyCode){
29008 case keys.DOWN_ARROW:
29009 this.selectNextNode();
29010 return false;
29011 case keys.PAGE_DOWN:
29012 this._page(false);
29013 return false;
29014 case keys.UP_ARROW:
29015 this.selectPreviousNode();
29016 return false;
29017 case keys.PAGE_UP:
29018 this._page(true);
29019 return false;
29020 default:
29021 return true;
29022 }
29023 }
29024 });
29025 });
29026
29027 },
29028 '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>",
29029 'dijit/Dialog':function(){
29030 require({cache:{
29031 '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"}});
29032 define("dijit/Dialog", [
29033 "require",
29034 "dojo/_base/array", // array.forEach array.indexOf array.map
29035 "dojo/_base/connect", // connect._keypress
29036 "dojo/_base/declare", // declare
29037 "dojo/_base/Deferred", // Deferred
29038 "dojo/dom", // dom.isDescendant
29039 "dojo/dom-class", // domClass.add domClass.contains
29040 "dojo/dom-geometry", // domGeometry.position
29041 "dojo/dom-style", // domStyle.set
29042 "dojo/_base/event", // event.stop
29043 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
29044 "dojo/i18n", // i18n.getLocalization
29045 "dojo/keys",
29046 "dojo/_base/lang", // lang.mixin lang.hitch
29047 "dojo/on",
29048 "dojo/ready",
29049 "dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
29050 "dojo/window", // winUtils.getBox, winUtils.get
29051 "dojo/dnd/Moveable", // Moveable
29052 "dojo/dnd/TimedMoveable", // TimedMoveable
29053 "./focus",
29054 "./_base/manager", // manager.defaultDuration
29055 "./_Widget",
29056 "./_TemplatedMixin",
29057 "./_CssStateMixin",
29058 "./form/_FormMixin",
29059 "./_DialogMixin",
29060 "./DialogUnderlay",
29061 "./layout/ContentPane",
29062 "dojo/text!./templates/Dialog.html",
29063 "./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
29064 "dojo/i18n!./nls/common"
29065 ], function(require, array, connect, declare, Deferred,
29066 dom, domClass, domGeometry, domStyle, event, fx, i18n, keys, lang, on, ready, has, winUtils,
29067 Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
29068 DialogUnderlay, ContentPane, template, dijit){
29069
29070 // module:
29071 // dijit/Dialog
29072
29073 /*=====
29074 dijit._underlay = function(kwArgs){
29075 // summary:
29076 // A shared instance of a `dijit.DialogUnderlay`
29077 //
29078 // description:
29079 // A shared instance of a `dijit.DialogUnderlay` created and
29080 // used by `dijit.Dialog`, though never created until some Dialog
29081 // or subclass thereof is shown.
29082 };
29083 =====*/
29084
29085 var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
29086 templateString: template,
29087
29088 baseClass: "dijitDialog",
29089
29090 cssStateNodes: {
29091 closeButtonNode: "dijitDialogCloseIcon"
29092 },
29093
29094 // Map widget attributes to DOMNode attributes.
29095 _setTitleAttr: [
29096 { node: "titleNode", type: "innerHTML" },
29097 { node: "titleBar", type: "attribute" }
29098 ],
29099
29100 // open: [readonly] Boolean
29101 // True if Dialog is currently displayed on screen.
29102 open: false,
29103
29104 // duration: Integer
29105 // The time in milliseconds it takes the dialog to fade in and out
29106 duration: manager.defaultDuration,
29107
29108 // refocus: Boolean
29109 // A Toggle to modify the default focus behavior of a Dialog, which
29110 // is to re-focus the element which had focus before being opened.
29111 // False will disable refocusing. Default: true
29112 refocus: true,
29113
29114 // autofocus: Boolean
29115 // A Toggle to modify the default focus behavior of a Dialog, which
29116 // is to focus on the first dialog element after opening the dialog.
29117 // False will disable autofocusing. Default: true
29118 autofocus: true,
29119
29120 // _firstFocusItem: [private readonly] DomNode
29121 // The pointer to the first focusable node in the dialog.
29122 // Set by `dijit/_DialogMixin._getFocusItems()`.
29123 _firstFocusItem: null,
29124
29125 // _lastFocusItem: [private readonly] DomNode
29126 // The pointer to which node has focus prior to our dialog.
29127 // Set by `dijit/_DialogMixin._getFocusItems()`.
29128 _lastFocusItem: null,
29129
29130 // doLayout: [protected] Boolean
29131 // Don't change this parameter from the default value.
29132 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
29133 // is never a child of a layout container, nor can you specify the size of
29134 // Dialog in order to control the size of an inner widget.
29135 doLayout: false,
29136
29137 // draggable: Boolean
29138 // Toggles the moveable aspect of the Dialog. If true, Dialog
29139 // can be dragged by it's title. If false it will remain centered
29140 // in the viewport.
29141 draggable: true,
29142
29143 _setDraggableAttr: function(/*Boolean*/ val){
29144 // Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
29145 // as that prevents text select on modern browsers (#14452)
29146 this._set("draggable", val);
29147 },
29148
29149 // aria-describedby: String
29150 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
29151 // be the id of the container element of text that describes the dialog purpose (usually
29152 // the first text in the dialog).
29153 // | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
29154 // | <div id="intro">Introductory text</div>
29155 // | <div>rest of dialog contents</div>
29156 // | </div>
29157 "aria-describedby": "",
29158
29159 // maxRatio: Number
29160 // Maximum size to allow the dialog to expand to, relative to viewport size
29161 maxRatio: 0.9,
29162
29163 postMixInProperties: function(){
29164 var _nlsResources = i18n.getLocalization("dijit", "common");
29165 lang.mixin(this, _nlsResources);
29166 this.inherited(arguments);
29167 },
29168
29169 postCreate: function(){
29170 domStyle.set(this.domNode, {
29171 display: "none",
29172 position:"absolute"
29173 });
29174 this.ownerDocumentBody.appendChild(this.domNode);
29175
29176 this.inherited(arguments);
29177
29178 this.connect(this, "onExecute", "hide");
29179 this.connect(this, "onCancel", "hide");
29180 this._modalconnects = [];
29181 },
29182
29183 onLoad: function(){
29184 // summary:
29185 // Called when data has been loaded from an href.
29186 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
29187 // but should *not* be overridden.
29188 // tags:
29189 // callback
29190
29191 // when href is specified we need to reposition the dialog after the data is loaded
29192 // and find the focusable elements
29193 this._position();
29194 if(this.autofocus && DialogLevelManager.isTop(this)){
29195 this._getFocusItems(this.domNode);
29196 focus.focus(this._firstFocusItem);
29197 }
29198 this.inherited(arguments);
29199 },
29200
29201 _onBlur: function(by){
29202 this.inherited(arguments);
29203
29204 // If focus was accidentally removed from the dialog, such as if the user clicked a blank
29205 // area of the screen, or clicked the browser's address bar and then tabbed into the page,
29206 // then refocus. Won't do anything if focus was removed because the Dialog was closed, or
29207 // because a new Dialog popped up on top of the old one.
29208 var refocus = lang.hitch(this, function(){
29209 if(this.open && !this._destroyed && DialogLevelManager.isTop(this)){
29210 this._getFocusItems(this.domNode);
29211 focus.focus(this._firstFocusItem);
29212 }
29213 });
29214 if(by == "mouse"){
29215 // wait for mouse up, and then refocus dialog; otherwise doesn't work
29216 on.once(this.ownerDocument, "mouseup", refocus);
29217 }else{
29218 refocus();
29219 }
29220 },
29221
29222 _endDrag: function(){
29223 // summary:
29224 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
29225 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
29226 var nodePosition = domGeometry.position(this.domNode),
29227 viewport = winUtils.getBox(this.ownerDocument);
29228 nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
29229 nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
29230 this._relativePosition = nodePosition;
29231 this._position();
29232 },
29233
29234 _setup: function(){
29235 // summary:
29236 // Stuff we need to do before showing the Dialog for the first
29237 // time (but we defer it until right beforehand, for
29238 // performance reasons).
29239 // tags:
29240 // private
29241
29242 var node = this.domNode;
29243
29244 if(this.titleBar && this.draggable){
29245 this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
29246 : Moveable)(node, { handle: this.titleBar });
29247 this.connect(this._moveable, "onMoveStop", "_endDrag");
29248 }else{
29249 domClass.add(node,"dijitDialogFixed");
29250 }
29251
29252 this.underlayAttrs = {
29253 dialogId: this.id,
29254 "class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" "),
29255 ownerDocument: this.ownerDocument
29256 };
29257 },
29258
29259 _size: function(){
29260 // summary:
29261 // If necessary, shrink dialog contents so dialog fits in viewport
29262 // tags:
29263 // private
29264
29265 this._checkIfSingleChild();
29266
29267 // If we resized the dialog contents earlier, reset them back to original size, so
29268 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
29269 // Need to do this before the domGeometry.position(this.domNode) call below.
29270 if(this._singleChild){
29271 if(typeof this._singleChildOriginalStyle != "undefined"){
29272 this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
29273 delete this._singleChildOriginalStyle;
29274 }
29275 }else{
29276 domStyle.set(this.containerNode, {
29277 width:"auto",
29278 height:"auto"
29279 });
29280 }
29281
29282 var bb = domGeometry.position(this.domNode);
29283
29284 // Get viewport size but then reduce it by a bit; Dialog should always have some space around it
29285 // to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
29286 var viewport = winUtils.getBox(this.ownerDocument);
29287 viewport.w *= this.maxRatio;
29288 viewport.h *= this.maxRatio;
29289
29290 if(bb.w >= viewport.w || bb.h >= viewport.h){
29291 // Reduce size of dialog contents so that dialog fits in viewport
29292
29293 var containerSize = domGeometry.position(this.containerNode),
29294 w = Math.min(bb.w, viewport.w) - (bb.w - containerSize.w),
29295 h = Math.min(bb.h, viewport.h) - (bb.h - containerSize.h);
29296
29297 if(this._singleChild && this._singleChild.resize){
29298 if(typeof this._singleChildOriginalStyle == "undefined"){
29299 this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
29300 }
29301 this._singleChild.resize({w: w, h: h});
29302 }else{
29303 domStyle.set(this.containerNode, {
29304 width: w + "px",
29305 height: h + "px",
29306 overflow: "auto",
29307 position: "relative" // workaround IE bug moving scrollbar or dragging dialog
29308 });
29309 }
29310 }else{
29311 if(this._singleChild && this._singleChild.resize){
29312 this._singleChild.resize();
29313 }
29314 }
29315 },
29316
29317 _position: function(){
29318 // summary:
29319 // Position modal dialog in the viewport. If no relative offset
29320 // in the viewport has been determined (by dragging, for instance),
29321 // center the node. Otherwise, use the Dialog's stored relative offset,
29322 // and position the node to top: left: values based on the viewport.
29323 if(!domClass.contains(this.ownerDocumentBody, "dojoMove")){ // don't do anything if called during auto-scroll
29324 var node = this.domNode,
29325 viewport = winUtils.getBox(this.ownerDocument),
29326 p = this._relativePosition,
29327 bb = p ? null : domGeometry.position(node),
29328 l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
29329 t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
29330 ;
29331 domStyle.set(node,{
29332 left: l + "px",
29333 top: t + "px"
29334 });
29335 }
29336 },
29337
29338 _onKey: function(/*Event*/ evt){
29339 // summary:
29340 // Handles the keyboard events for accessibility reasons
29341 // tags:
29342 // private
29343
29344 if(evt.charOrCode){
29345 var node = evt.target;
29346 if(evt.charOrCode === keys.TAB){
29347 this._getFocusItems(this.domNode);
29348 }
29349 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
29350 // see if we are shift-tabbing from first focusable item on dialog
29351 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
29352 if(!singleFocusItem){
29353 focus.focus(this._lastFocusItem); // send focus to last item in dialog
29354 }
29355 event.stop(evt);
29356 }else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
29357 if(!singleFocusItem){
29358 focus.focus(this._firstFocusItem); // send focus to first item in dialog
29359 }
29360 event.stop(evt);
29361 }else{
29362 // see if the key is for the dialog
29363 while(node){
29364 if(node == this.domNode || domClass.contains(node, "dijitPopup")){
29365 if(evt.charOrCode == keys.ESCAPE){
29366 this.onCancel();
29367 }else{
29368 return; // just let it go
29369 }
29370 }
29371 node = node.parentNode;
29372 }
29373 // this key is for the disabled document window
29374 if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y
29375 event.stop(evt);
29376 // opera won't tab to a div
29377 }else if(!has("opera")){
29378 try{
29379 this._firstFocusItem.focus();
29380 }catch(e){ /*squelch*/ }
29381 }
29382 }
29383 }
29384 },
29385
29386 show: function(){
29387 // summary:
29388 // Display the dialog
29389 // returns: dojo/_base/Deferred
29390 // Deferred object that resolves when the display animation is complete
29391
29392 if(this.open){ return; }
29393
29394 if(!this._started){
29395 this.startup();
29396 }
29397
29398 // first time we show the dialog, there's some initialization stuff to do
29399 if(!this._alreadyInitialized){
29400 this._setup();
29401 this._alreadyInitialized=true;
29402 }
29403
29404 if(this._fadeOutDeferred){
29405 this._fadeOutDeferred.cancel();
29406 }
29407
29408 // Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
29409 var win = winUtils.get(this.ownerDocument);
29410 this._modalconnects.push(on(win, "scroll", lang.hitch(this, "resize")));
29411
29412 this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
29413
29414 domStyle.set(this.domNode, {
29415 opacity:0,
29416 display:""
29417 });
29418
29419 this._set("open", true);
29420 this._onShow(); // lazy load trigger
29421
29422 this._size();
29423 this._position();
29424
29425 // fade-in Animation object, setup below
29426 var fadeIn;
29427
29428 this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
29429 fadeIn.stop();
29430 delete this._fadeInDeferred;
29431 }));
29432
29433 fadeIn = fx.fadeIn({
29434 node: this.domNode,
29435 duration: this.duration,
29436 beforeBegin: lang.hitch(this, function(){
29437 DialogLevelManager.show(this, this.underlayAttrs);
29438 }),
29439 onEnd: lang.hitch(this, function(){
29440 if(this.autofocus && DialogLevelManager.isTop(this)){
29441 // find focusable items each time dialog is shown since if dialog contains a widget the
29442 // first focusable items can change
29443 this._getFocusItems(this.domNode);
29444 focus.focus(this._firstFocusItem);
29445 }
29446 this._fadeInDeferred.resolve(true);
29447 delete this._fadeInDeferred;
29448 })
29449 }).play();
29450
29451 return this._fadeInDeferred;
29452 },
29453
29454 hide: function(){
29455 // summary:
29456 // Hide the dialog
29457 // returns: dojo/_base/Deferred
29458 // Deferred object that resolves when the hide animation is complete
29459
29460 // If we haven't been initialized yet then we aren't showing and we can just return.
29461 // Likewise if we are already hidden, or are currently fading out.
29462 if(!this._alreadyInitialized || !this.open){
29463 return;
29464 }
29465 if(this._fadeInDeferred){
29466 this._fadeInDeferred.cancel();
29467 }
29468
29469 // fade-in Animation object, setup below
29470 var fadeOut;
29471
29472 this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
29473 fadeOut.stop();
29474 delete this._fadeOutDeferred;
29475 }));
29476 // fire onHide when the promise resolves.
29477 this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
29478
29479 fadeOut = fx.fadeOut({
29480 node: this.domNode,
29481 duration: this.duration,
29482 onEnd: lang.hitch(this, function(){
29483 this.domNode.style.display = "none";
29484 DialogLevelManager.hide(this);
29485 this._fadeOutDeferred.resolve(true);
29486 delete this._fadeOutDeferred;
29487 })
29488 }).play();
29489
29490 if(this._scrollConnected){
29491 this._scrollConnected = false;
29492 }
29493 var h;
29494 while(h = this._modalconnects.pop()){
29495 h.remove();
29496 }
29497
29498 if(this._relativePosition){
29499 delete this._relativePosition;
29500 }
29501 this._set("open", false);
29502
29503 return this._fadeOutDeferred;
29504 },
29505
29506 resize: function(){
29507 // summary:
29508 // Called when viewport scrolled or size changed. Position the Dialog and the underlay.
29509 // tags:
29510 // private
29511 if(this.domNode.style.display != "none"){
29512 if(DialogUnderlay._singleton){ // avoid race condition during show()
29513 DialogUnderlay._singleton.layout();
29514 }
29515 this._position();
29516 this._size();
29517 }
29518 },
29519
29520 destroy: function(){
29521 if(this._fadeInDeferred){
29522 this._fadeInDeferred.cancel();
29523 }
29524 if(this._fadeOutDeferred){
29525 this._fadeOutDeferred.cancel();
29526 }
29527 if(this._moveable){
29528 this._moveable.destroy();
29529 }
29530 var h;
29531 while(h = this._modalconnects.pop()){
29532 h.remove();
29533 }
29534
29535 DialogLevelManager.hide(this);
29536
29537 this.inherited(arguments);
29538 }
29539 });
29540
29541 var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {
29542 // summary:
29543 // A modal dialog Widget.
29544 // description:
29545 // Pops up a modal dialog window, blocking access to the screen
29546 // and also graying out the screen Dialog is extended from
29547 // ContentPane so it supports all the same parameters (href, etc.).
29548 // example:
29549 // | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
29550 // example:
29551 // | var foo = new Dialog({ title: "test dialog", content: "test content" };
29552 // | foo.placeAt(win.body());
29553 // | foo.startup();
29554 });
29555 Dialog._DialogBase = _DialogBase; // for monkey patching and dojox/widget/DialogSimple
29556
29557 var DialogLevelManager = Dialog._DialogLevelManager = {
29558 // summary:
29559 // Controls the various active "levels" on the page, starting with the
29560 // stuff initially visible on the page (at z-index 0), and then having an entry for
29561 // each Dialog shown.
29562
29563 _beginZIndex: 950,
29564
29565 show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs){
29566 // summary:
29567 // Call right before fade-in animation for new dialog.
29568 // Saves current focus, displays/adjusts underlay for new dialog,
29569 // and sets the z-index of the dialog itself.
29570 //
29571 // New dialog will be displayed on top of all currently displayed dialogs.
29572 //
29573 // Caller is responsible for setting focus in new dialog after the fade-in
29574 // animation completes.
29575
29576 // Save current focus
29577 ds[ds.length-1].focus = focus.curNode;
29578
29579 // Display the underlay, or if already displayed then adjust for this new dialog
29580 // TODO: one underlay per document (based on dialog.ownerDocument)
29581 var underlay = DialogUnderlay._singleton;
29582 if(!underlay || underlay._destroyed){
29583 underlay = dijit._underlay = DialogUnderlay._singleton = new DialogUnderlay(underlayAttrs);
29584 }else{
29585 underlay.set(dialog.underlayAttrs);
29586 }
29587
29588 // Set z-index a bit above previous dialog
29589 var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
29590 if(ds.length == 1){ // first dialog
29591 underlay.show();
29592 }
29593 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', zIndex - 1);
29594
29595 // Dialog
29596 domStyle.set(dialog.domNode, 'zIndex', zIndex);
29597
29598 ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
29599 },
29600
29601 hide: function(/*dijit/_WidgetBase*/ dialog){
29602 // summary:
29603 // Called when the specified dialog is hidden/destroyed, after the fade-out
29604 // animation ends, in order to reset page focus, fix the underlay, etc.
29605 // If the specified dialog isn't open then does nothing.
29606 //
29607 // Caller is responsible for either setting display:none on the dialog domNode,
29608 // or calling dijit/popup.hide(), or removing it from the page DOM.
29609
29610 if(ds[ds.length-1].dialog == dialog){
29611 // Removing the top (or only) dialog in the stack, return focus
29612 // to previous dialog
29613
29614 ds.pop();
29615
29616 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
29617
29618 // Adjust underlay, unless the underlay widget has already been destroyed
29619 // because we are being called during page unload (when all widgets are destroyed)
29620 if(!DialogUnderlay._singleton._destroyed){
29621 if(ds.length == 1){
29622 // Returning to original page. Hide the underlay.
29623 DialogUnderlay._singleton.hide();
29624 }else{
29625 // Popping back to previous dialog, adjust underlay.
29626 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
29627 DialogUnderlay._singleton.set(pd.underlayAttrs);
29628 }
29629 }
29630
29631 // Adjust focus
29632 if(dialog.refocus){
29633 // If we are returning control to a previous dialog but for some reason
29634 // that dialog didn't have a focused field, set focus to first focusable item.
29635 // This situation could happen if two dialogs appeared at nearly the same time,
29636 // since a dialog doesn't set it's focus until the fade-in is finished.
29637 var focus = pd.focus;
29638 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
29639 pd.dialog._getFocusItems(pd.dialog.domNode);
29640 focus = pd.dialog._firstFocusItem;
29641 }
29642
29643 if(focus){
29644 // Refocus the button that spawned the Dialog. This will fail in corner cases including
29645 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
29646 // before this code runs. (#15058)
29647 try{
29648 focus.focus();
29649 }catch(e){}
29650 }
29651 }
29652 }else{
29653 // Removing a dialog out of order (#9944, #10705).
29654 // Don't need to mess with underlay or z-index or anything.
29655 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
29656 if(idx != -1){
29657 ds.splice(idx, 1);
29658 }
29659 }
29660 },
29661
29662 isTop: function(/*dijit/_WidgetBase*/ dialog){
29663 // summary:
29664 // Returns true if specified Dialog is the top in the task
29665 return ds[ds.length-1].dialog == dialog;
29666 }
29667 };
29668
29669 // Stack representing the various active "levels" on the page, starting with the
29670 // stuff initially visible on the page (at z-index 0), and then having an entry for
29671 // each Dialog shown.
29672 // Each element in stack has form {
29673 // dialog: dialogWidget,
29674 // focus: returnFromGetFocus(),
29675 // underlayAttrs: attributes to set on underlay (when this widget is active)
29676 // }
29677 var ds = Dialog._dialogStack = [
29678 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
29679 ];
29680
29681 // Back compat w/1.6, remove for 2.0
29682 if(has("dijit-legacy-requires")){
29683 ready(0, function(){
29684 var requires = ["dijit/TooltipDialog"];
29685 require(requires); // use indirection so modules not rolled into a build
29686 });
29687 }
29688
29689 return Dialog;
29690 });
29691
29692 },
29693 'dijit/_base/focus':function(){
29694 define("dijit/_base/focus", [
29695 "dojo/_base/array", // array.forEach
29696 "dojo/dom", // dom.isDescendant
29697 "dojo/_base/lang", // lang.isArray
29698 "dojo/topic", // publish
29699 "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
29700 "../focus",
29701 "../main" // for exporting symbols to dijit
29702 ], function(array, dom, lang, topic, win, focus, dijit){
29703
29704 // module:
29705 // dijit/_base/focus
29706
29707 var exports = {
29708 // summary:
29709 // Deprecated module to monitor currently focused node and stack of currently focused widgets.
29710 // New code should access dijit/focus directly.
29711
29712 // _curFocus: DomNode
29713 // Currently focused item on screen
29714 _curFocus: null,
29715
29716 // _prevFocus: DomNode
29717 // Previously focused item on screen
29718 _prevFocus: null,
29719
29720 isCollapsed: function(){
29721 // summary:
29722 // Returns true if there is no text selected
29723 return dijit.getBookmark().isCollapsed;
29724 },
29725
29726 getBookmark: function(){
29727 // summary:
29728 // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
29729 var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
29730
29731 if(win.global.getSelection){
29732 //W3C Range API for selections.
29733 sel = win.global.getSelection();
29734 if(sel){
29735 if(sel.isCollapsed){
29736 tg = cf? cf.tagName : "";
29737 if(tg){
29738 //Create a fake rangelike item to restore selections.
29739 tg = tg.toLowerCase();
29740 if(tg == "textarea" ||
29741 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
29742 sel = {
29743 start: cf.selectionStart,
29744 end: cf.selectionEnd,
29745 node: cf,
29746 pRange: true
29747 };
29748 return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
29749 }
29750 }
29751 bm = {isCollapsed:true};
29752 if(sel.rangeCount){
29753 bm.mark = sel.getRangeAt(0).cloneRange();
29754 }
29755 }else{
29756 rg = sel.getRangeAt(0);
29757 bm = {isCollapsed: false, mark: rg.cloneRange()};
29758 }
29759 }
29760 }else if(sel){
29761 // If the current focus was a input of some sort and no selection, don't bother saving
29762 // a native bookmark. This is because it causes issues with dialog/page selection restore.
29763 // So, we need to create psuedo bookmarks to work with.
29764 tg = cf ? cf.tagName : "";
29765 tg = tg.toLowerCase();
29766 if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
29767 if(sel.type && sel.type.toLowerCase() == "none"){
29768 return {
29769 isCollapsed: true,
29770 mark: null
29771 }
29772 }else{
29773 rg = sel.createRange();
29774 return {
29775 isCollapsed: rg.text && rg.text.length?false:true,
29776 mark: {
29777 range: rg,
29778 pRange: true
29779 }
29780 };
29781 }
29782 }
29783 bm = {};
29784
29785 //'IE' way for selections.
29786 try{
29787 // createRange() throws exception when dojo in iframe
29788 //and nothing selected, see #9632
29789 rg = sel.createRange();
29790 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
29791 }catch(e){
29792 bm.isCollapsed = true;
29793 return bm;
29794 }
29795 if(sel.type.toUpperCase() == 'CONTROL'){
29796 if(rg.length){
29797 bm.mark=[];
29798 var i=0,len=rg.length;
29799 while(i<len){
29800 bm.mark.push(rg.item(i++));
29801 }
29802 }else{
29803 bm.isCollapsed = true;
29804 bm.mark = null;
29805 }
29806 }else{
29807 bm.mark = rg.getBookmark();
29808 }
29809 }else{
29810 console.warn("No idea how to store the current selection for this browser!");
29811 }
29812 return bm; // Object
29813 },
29814
29815 moveToBookmark: function(/*Object*/ bookmark){
29816 // summary:
29817 // Moves current selection to a bookmark
29818 // bookmark:
29819 // This should be a returned object from dijit.getBookmark()
29820
29821 var _doc = win.doc,
29822 mark = bookmark.mark;
29823 if(mark){
29824 if(win.global.getSelection){
29825 //W3C Rangi API (FF, WebKit, Opera, etc)
29826 var sel = win.global.getSelection();
29827 if(sel && sel.removeAllRanges){
29828 if(mark.pRange){
29829 var n = mark.node;
29830 n.selectionStart = mark.start;
29831 n.selectionEnd = mark.end;
29832 }else{
29833 sel.removeAllRanges();
29834 sel.addRange(mark);
29835 }
29836 }else{
29837 console.warn("No idea how to restore selection for this browser!");
29838 }
29839 }else if(_doc.selection && mark){
29840 //'IE' way.
29841 var rg;
29842 if(mark.pRange){
29843 rg = mark.range;
29844 }else if(lang.isArray(mark)){
29845 rg = _doc.body.createControlRange();
29846 //rg.addElement does not have call/apply method, so can not call it directly
29847 //rg is not available in "range.addElement(item)", so can't use that either
29848 array.forEach(mark, function(n){
29849 rg.addElement(n);
29850 });
29851 }else{
29852 rg = _doc.body.createTextRange();
29853 rg.moveToBookmark(mark);
29854 }
29855 rg.select();
29856 }
29857 }
29858 },
29859
29860 getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
29861 // summary:
29862 // Called as getFocus(), this returns an Object showing the current focus
29863 // and selected text.
29864 //
29865 // Called as getFocus(widget), where widget is a (widget representing) a button
29866 // that was just pressed, it returns where focus was before that button
29867 // was pressed. (Pressing the button may have either shifted focus to the button,
29868 // or removed focus altogether.) In this case the selected text is not returned,
29869 // since it can't be accurately determined.
29870 //
29871 // menu: dijit/_WidgetBase|{domNode: DomNode} structure
29872 // The button that was just pressed. If focus has disappeared or moved
29873 // to this button, returns the previous focus. In this case the bookmark
29874 // information is already lost, and null is returned.
29875 //
29876 // openedForWindow:
29877 // iframe in which menu was opened
29878 //
29879 // returns:
29880 // A handle to restore focus/selection, to be passed to `dijit.focus`
29881 var node = !focus.curNode || (menu && dom.isDescendant(focus.curNode, menu.domNode)) ? dijit._prevFocus : focus.curNode;
29882 return {
29883 node: node,
29884 bookmark: node && (node == focus.curNode) && win.withGlobal(openedForWindow || win.global, dijit.getBookmark),
29885 openedForWindow: openedForWindow
29886 }; // Object
29887 },
29888
29889 // _activeStack: dijit/_WidgetBase[]
29890 // List of currently active widgets (focused widget and it's ancestors)
29891 _activeStack: [],
29892
29893 registerIframe: function(/*DomNode*/ iframe){
29894 // summary:
29895 // Registers listeners on the specified iframe so that any click
29896 // or focus event on that iframe (or anything in it) is reported
29897 // as a focus/click event on the `<iframe>` itself.
29898 // description:
29899 // Currently only used by editor.
29900 // returns:
29901 // Handle to pass to unregisterIframe()
29902 return focus.registerIframe(iframe);
29903 },
29904
29905 unregisterIframe: function(/*Object*/ handle){
29906 // summary:
29907 // Unregisters listeners on the specified iframe created by registerIframe.
29908 // After calling be sure to delete or null out the handle itself.
29909 // handle:
29910 // Handle returned by registerIframe()
29911
29912 handle && handle.remove();
29913 },
29914
29915 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
29916 // summary:
29917 // Registers listeners on the specified window (either the main
29918 // window or an iframe's window) to detect when the user has clicked somewhere
29919 // or focused somewhere.
29920 // description:
29921 // Users should call registerIframe() instead of this method.
29922 // targetWindow:
29923 // If specified this is the window associated with the iframe,
29924 // i.e. iframe.contentWindow.
29925 // effectiveNode:
29926 // If specified, report any focus events inside targetWindow as
29927 // an event on effectiveNode, rather than on evt.target.
29928 // returns:
29929 // Handle to pass to unregisterWin()
29930
29931 return focus.registerWin(targetWindow, effectiveNode);
29932 },
29933
29934 unregisterWin: function(/*Handle*/ handle){
29935 // summary:
29936 // Unregisters listeners on the specified window (either the main
29937 // window or an iframe's window) according to handle returned from registerWin().
29938 // After calling be sure to delete or null out the handle itself.
29939
29940 handle && handle.remove();
29941 }
29942 };
29943
29944 // Override focus singleton's focus function so that dijit.focus()
29945 // has backwards compatible behavior of restoring selection (although
29946 // probably no one is using that).
29947 focus.focus = function(/*Object|DomNode */ handle){
29948 // summary:
29949 // Sets the focused node and the selection according to argument.
29950 // To set focus to an iframe's content, pass in the iframe itself.
29951 // handle:
29952 // object returned by get(), or a DomNode
29953
29954 if(!handle){ return; }
29955
29956 var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
29957 bookmark = handle.bookmark,
29958 openedForWindow = handle.openedForWindow,
29959 collapsed = bookmark ? bookmark.isCollapsed : false;
29960
29961 // Set the focus
29962 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
29963 // but we need to set focus to iframe.contentWindow
29964 if(node){
29965 var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
29966 if(focusNode && focusNode.focus){
29967 try{
29968 // Gecko throws sometimes if setting focus is impossible,
29969 // node not displayed or something like that
29970 focusNode.focus();
29971 }catch(e){/*quiet*/}
29972 }
29973 focus._onFocusNode(node);
29974 }
29975
29976 // set the selection
29977 // do not need to restore if current selection is not empty
29978 // (use keyboard to select a menu item) or if previous selection was collapsed
29979 // as it may cause focus shift (Esp in IE).
29980 if(bookmark && win.withGlobal(openedForWindow || win.global, dijit.isCollapsed) && !collapsed){
29981 if(openedForWindow){
29982 openedForWindow.focus();
29983 }
29984 try{
29985 win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
29986 }catch(e2){
29987 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
29988 }
29989 }
29990 };
29991
29992 // For back compatibility, monitor changes to focused node and active widget stack,
29993 // publishing events and copying changes from focus manager variables into dijit (top level) variables
29994 focus.watch("curNode", function(name, oldVal, newVal){
29995 dijit._curFocus = newVal;
29996 dijit._prevFocus = oldVal;
29997 if(newVal){
29998 topic.publish("focusNode", newVal); // publish
29999 }
30000 });
30001 focus.watch("activeStack", function(name, oldVal, newVal){
30002 dijit._activeStack = newVal;
30003 });
30004
30005 focus.on("widget-blur", function(widget, by){
30006 topic.publish("widgetBlur", widget, by); // publish
30007 });
30008 focus.on("widget-focus", function(widget, by){
30009 topic.publish("widgetFocus", widget, by); // publish
30010 });
30011
30012 lang.mixin(dijit, exports);
30013
30014 /*===== return exports; =====*/
30015 return dijit; // for back compat :-(
30016 });
30017
30018 },
30019 'dijit/tree/dndSource':function(){
30020 define("dijit/tree/dndSource", [
30021 "dojo/_base/array", // array.forEach array.indexOf array.map
30022 "dojo/_base/connect", // isCopyKey
30023 "dojo/_base/declare", // declare
30024 "dojo/dom-class", // domClass.add
30025 "dojo/dom-geometry", // domGeometry.position
30026 "dojo/_base/lang", // lang.mixin lang.hitch
30027 "dojo/on", // subscribe
30028 "dojo/touch",
30029 "dojo/topic",
30030 "dojo/dnd/Manager", // DNDManager.manager
30031 "./_dndSelector"
30032 ], function(array, connect, declare, domClass, domGeometry, lang, on, touch, topic, DNDManager, _dndSelector){
30033
30034 // module:
30035 // dijit/tree/dndSource
30036 // summary:
30037 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30038
30039 /*=====
30040 var __Item = {
30041 // summary:
30042 // New item to be added to the Tree, like:
30043 // id: Anything
30044 id: "",
30045 // name: String
30046 name: ""
30047 };
30048 =====*/
30049
30050 var dndSource = declare("dijit.tree.dndSource", _dndSelector, {
30051 // summary:
30052 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30053
30054 // isSource: Boolean
30055 // Can be used as a DnD source.
30056 isSource: true,
30057
30058 // accept: String[]
30059 // List of accepted types (text strings) for the Tree; defaults to
30060 // ["text"]
30061 accept: ["text", "treeNode"],
30062
30063 // copyOnly: [private] Boolean
30064 // Copy items, if true, use a state of Ctrl key otherwise
30065 copyOnly: false,
30066
30067 // dragThreshold: Number
30068 // The move delay in pixels before detecting a drag; 5 by default
30069 dragThreshold: 5,
30070
30071 // betweenThreshold: Integer
30072 // Distance from upper/lower edge of node to allow drop to reorder nodes
30073 betweenThreshold: 0,
30074
30075 // Flag used by Avatar.js to signal to generate text node when dragging
30076 generateText: true,
30077
30078 constructor: function(/*dijit/Tree*/ tree, /*dijit/tree/dndSource*/ params){
30079 // summary:
30080 // a constructor of the Tree DnD Source
30081 // tags:
30082 // private
30083 if(!params){ params = {}; }
30084 lang.mixin(this, params);
30085 var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
30086 this.accept = null;
30087 if(type.length){
30088 this.accept = {};
30089 for(var i = 0; i < type.length; ++i){
30090 this.accept[type[i]] = 1;
30091 }
30092 }
30093
30094 // class-specific variables
30095 this.isDragging = false;
30096 this.mouseDown = false;
30097 this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode
30098 this.targetBox = null; // coordinates of this.targetAnchor
30099 this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
30100 this._lastX = 0;
30101 this._lastY = 0;
30102
30103 // states
30104 this.sourceState = "";
30105 if(this.isSource){
30106 domClass.add(this.node, "dojoDndSource");
30107 }
30108 this.targetState = "";
30109 if(this.accept){
30110 domClass.add(this.node, "dojoDndTarget");
30111 }
30112
30113 // set up events
30114 this.topics = [
30115 topic.subscribe("/dnd/source/over", lang.hitch(this, "onDndSourceOver")),
30116 topic.subscribe("/dnd/start", lang.hitch(this, "onDndStart")),
30117 topic.subscribe("/dnd/drop", lang.hitch(this, "onDndDrop")),
30118 topic.subscribe("/dnd/cancel", lang.hitch(this, "onDndCancel"))
30119 ];
30120 },
30121
30122 // methods
30123 checkAcceptance: function(/*===== source, nodes =====*/){
30124 // summary:
30125 // Checks if the target can accept nodes from this source
30126 // source: dijit/tree/dndSource
30127 // The source which provides items
30128 // nodes: DOMNode[]
30129 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
30130 // source is a dijit/Tree.
30131 // tags:
30132 // extension
30133 return true; // Boolean
30134 },
30135
30136 copyState: function(keyPressed){
30137 // summary:
30138 // Returns true, if we need to copy items, false to move.
30139 // It is separated to be overwritten dynamically, if needed.
30140 // keyPressed: Boolean
30141 // The "copy" control key was pressed
30142 // tags:
30143 // protected
30144 return this.copyOnly || keyPressed; // Boolean
30145 },
30146 destroy: function(){
30147 // summary:
30148 // Prepares the object to be garbage-collected.
30149 this.inherited(arguments);
30150 var h;
30151 while(h = this.topics.pop()){ h.remove(); }
30152 this.targetAnchor = null;
30153 },
30154
30155 _onDragMouse: function(e, firstTime){
30156 // summary:
30157 // Helper method for processing onmousemove/onmouseover events while drag is in progress.
30158 // Keeps track of current drop target.
30159 // e: Event
30160 // The mousemove event.
30161 // firstTime: Boolean?
30162 // If this flag is set, this is the first mouse move event of the drag, so call m.canDrop() etc.
30163 // even if newTarget == null because the user quickly dragged a node in the Tree to a position
30164 // over Tree.containerNode but not over any TreeNode (#7971)
30165
30166 var m = DNDManager.manager(),
30167 oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over
30168 newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over
30169 oldDropPosition = this.dropPosition; // the previous drop position (over/before/after)
30170
30171 // calculate if user is indicating to drop the dragged node before, after, or over
30172 // (i.e., to become a child of) the target node
30173 var newDropPosition = "Over";
30174 if(newTarget && this.betweenThreshold > 0){
30175 // If mouse is over a new TreeNode, then get new TreeNode's position and size
30176 if(!this.targetBox || oldTarget != newTarget){
30177 this.targetBox = domGeometry.position(newTarget.rowNode, true);
30178 }
30179 if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
30180 newDropPosition = "Before";
30181 }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
30182 newDropPosition = "After";
30183 }
30184 }
30185
30186 if(firstTime || newTarget != oldTarget || newDropPosition != oldDropPosition){
30187 if(oldTarget){
30188 this._removeItemClass(oldTarget.rowNode, oldDropPosition);
30189 }
30190 if(newTarget){
30191 this._addItemClass(newTarget.rowNode, newDropPosition);
30192 }
30193
30194 // Check if it's ok to drop the dragged node on/before/after the target node.
30195 if(!newTarget){
30196 m.canDrop(false);
30197 }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
30198 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
30199 m.canDrop(false);
30200 }else{
30201 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
30202 var sameId = false;
30203 if(m.source == this){
30204 for(var dragId in this.selection){
30205 var dragNode = this.selection[dragId];
30206 if(dragNode.item === newTarget.item){
30207 sameId = true;
30208 break;
30209 }
30210 }
30211 }
30212 if(sameId){
30213 m.canDrop(false);
30214 }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
30215 && !this._isParentChildDrop(m.source, newTarget.rowNode)){
30216 m.canDrop(true);
30217 }else{
30218 m.canDrop(false);
30219 }
30220 }
30221
30222 this.targetAnchor = newTarget;
30223 this.dropPosition = newDropPosition;
30224 }
30225 },
30226
30227 onMouseMove: function(e){
30228 // summary:
30229 // Called for any onmousemove/ontouchmove events over the Tree
30230 // e: Event
30231 // onmousemouse/ontouchmove event
30232 // tags:
30233 // private
30234 if(this.isDragging && this.targetState == "Disabled"){ return; }
30235 this.inherited(arguments);
30236 var m = DNDManager.manager();
30237 if(this.isDragging){
30238 this._onDragMouse(e);
30239 }else{
30240 if(this.mouseDown && this.isSource &&
30241 (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
30242 var nodes = this.getSelectedTreeNodes();
30243 if(nodes.length){
30244 if(nodes.length > 1){
30245 //filter out all selected items which has one of their ancestor selected as well
30246 var seen = this.selection, i = 0, r = [], n, p;
30247 nextitem: while((n = nodes[i++])){
30248 for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
30249 if(seen[p.id]){ //parent is already selected, skip this node
30250 continue nextitem;
30251 }
30252 }
30253 //this node does not have any ancestors selected, add it
30254 r.push(n);
30255 }
30256 nodes = r;
30257 }
30258 nodes = array.map(nodes, function(n){return n.domNode});
30259 m.startDrag(this, nodes, this.copyState(connect.isCopyKey(e)));
30260 this._onDragMouse(e, true); // because this may be the only mousemove event we get before the drop
30261 }
30262 }
30263 }
30264 },
30265
30266 onMouseDown: function(e){
30267 // summary:
30268 // Event processor for onmousedown/ontouchstart
30269 // e: Event
30270 // onmousedown/ontouchend event
30271 // tags:
30272 // private
30273 this.mouseDown = true;
30274 this.mouseButton = e.button;
30275 this._lastX = e.pageX;
30276 this._lastY = e.pageY;
30277 this.inherited(arguments);
30278 },
30279
30280 onMouseUp: function(e){
30281 // summary:
30282 // Event processor for onmouseup/ontouchend
30283 // e: Event
30284 // onmouseup/ontouchend event
30285 // tags:
30286 // private
30287 if(this.mouseDown){
30288 this.mouseDown = false;
30289 this.inherited(arguments);
30290 }
30291 },
30292
30293 onMouseOut: function(){
30294 // summary:
30295 // Event processor for when mouse is moved away from a TreeNode
30296 // tags:
30297 // private
30298 this.inherited(arguments);
30299 this._unmarkTargetAnchor();
30300 },
30301
30302 checkItemAcceptance: function(/*===== target, source, position =====*/){
30303 // summary:
30304 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
30305 // description:
30306 // In the base case, this is called to check if target can become a child of source.
30307 // When betweenThreshold is set, position="before" or "after" means that we
30308 // are asking if the source node can be dropped before/after the target node.
30309 // target: DOMNode
30310 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
30311 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
30312 // source: dijit/tree/dndSource
30313 // The (set of) nodes we are dropping
30314 // position: String
30315 // "over", "before", or "after"
30316 // tags:
30317 // extension
30318 return true;
30319 },
30320
30321 // topic event processors
30322 onDndSourceOver: function(source){
30323 // summary:
30324 // Topic event processor for /dnd/source/over, called when detected a current source.
30325 // source: Object
30326 // The dijit/tree/dndSource / dojo/dnd/Source which has the mouse over it
30327 // tags:
30328 // private
30329 if(this != source){
30330 this.mouseDown = false;
30331 this._unmarkTargetAnchor();
30332 }else if(this.isDragging){
30333 var m = DNDManager.manager();
30334 m.canDrop(false);
30335 }
30336 },
30337 onDndStart: function(source, nodes, copy){
30338 // summary:
30339 // Topic event processor for /dnd/start, called to initiate the DnD operation
30340 // source: Object
30341 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30342 // nodes: DomNode[]
30343 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30344 // copy: Boolean
30345 // Copy items, if true, move items otherwise
30346 // tags:
30347 // private
30348
30349 if(this.isSource){
30350 this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
30351 }
30352 var accepted = this.checkAcceptance(source, nodes);
30353
30354 this._changeState("Target", accepted ? "" : "Disabled");
30355
30356 if(this == source){
30357 DNDManager.manager().overSource(this);
30358 }
30359
30360 this.isDragging = true;
30361 },
30362
30363 itemCreator: function(nodes /*===== , target, source =====*/){
30364 // summary:
30365 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
30366 // dropped onto the tree. Developer must override this method to enable
30367 // dropping from external sources onto this Tree, unless the Tree.model's items
30368 // happen to look like {id: 123, name: "Apple" } with no other attributes.
30369 // description:
30370 // For each node in nodes[], which came from source, create a hash of name/value
30371 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
30372 // nodes: DomNode[]
30373 // target: DomNode
30374 // source: dojo/dnd/Source
30375 // returns: __Item[]
30376 // Array of name/value hashes for each new item to be added to the Tree
30377 // tags:
30378 // extension
30379
30380 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
30381 // make signature itemCreator(sourceItem, node, target) (or similar).
30382
30383 return array.map(nodes, function(node){
30384 return {
30385 "id": node.id,
30386 "name": node.textContent || node.innerText || ""
30387 };
30388 }); // Object[]
30389 },
30390
30391 onDndDrop: function(source, nodes, copy){
30392 // summary:
30393 // Topic event processor for /dnd/drop, called to finish the DnD operation.
30394 // description:
30395 // Updates data store items according to where node was dragged from and dropped
30396 // to. The tree will then respond to those data store updates and redraw itself.
30397 // source: Object
30398 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30399 // nodes: DomNode[]
30400 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30401 // copy: Boolean
30402 // Copy items, if true, move items otherwise
30403 // tags:
30404 // protected
30405 if(this.containerState == "Over"){
30406 var tree = this.tree,
30407 model = tree.model,
30408 target = this.targetAnchor;
30409
30410 this.isDragging = false;
30411
30412 // Compute the new parent item
30413 var newParentItem;
30414 var insertIndex;
30415 var before; // drop source before (aka previous sibling) of target
30416 newParentItem = (target && target.item) || tree.item;
30417 if(this.dropPosition == "Before" || this.dropPosition == "After"){
30418 // TODO: if there is no parent item then disallow the drop.
30419 // Actually this should be checked during onMouseMove too, to make the drag icon red.
30420 newParentItem = (target.getParent() && target.getParent().item) || tree.item;
30421 // Compute the insert index for reordering
30422 insertIndex = target.getIndexInParent();
30423 if(this.dropPosition == "After"){
30424 insertIndex = target.getIndexInParent() + 1;
30425 before = target.getNextSibling() && target.getNextSibling().item;
30426 }else{
30427 before = target.item;
30428 }
30429 }else{
30430 newParentItem = (target && target.item) || tree.item;
30431 }
30432
30433 // If necessary, use this variable to hold array of hashes to pass to model.newItem()
30434 // (one entry in the array for each dragged node).
30435 var newItemsParams;
30436
30437 array.forEach(nodes, function(node, idx){
30438 // dojo/dnd/Item representing the thing being dropped.
30439 // Don't confuse the use of item here (meaning a DnD item) with the
30440 // uses below where item means dojo.data item.
30441 var sourceItem = source.getItem(node.id);
30442
30443 // Information that's available if the source is another Tree
30444 // (possibly but not necessarily this tree, possibly but not
30445 // necessarily the same model as this Tree)
30446 if(array.indexOf(sourceItem.type, "treeNode") != -1){
30447 var childTreeNode = sourceItem.data,
30448 childItem = childTreeNode.item,
30449 oldParentItem = childTreeNode.getParent().item;
30450 }
30451
30452 if(source == this){
30453 // This is a node from my own tree, and we are moving it, not copying.
30454 // Remove item from old parent's children attribute.
30455 // TODO: dijit/tree/dndSelector should implement deleteSelectedNodes()
30456 // and this code should go there.
30457
30458 if(typeof insertIndex == "number"){
30459 if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
30460 insertIndex -= 1;
30461 }
30462 }
30463 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
30464 }else if(model.isItem(childItem)){
30465 // Item from same model
30466 // (maybe we should only do this branch if the source is a tree?)
30467 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
30468 }else{
30469 // Get the hash to pass to model.newItem(). A single call to
30470 // itemCreator() returns an array of hashes, one for each drag source node.
30471 if(!newItemsParams){
30472 newItemsParams = this.itemCreator(nodes, target.rowNode, source);
30473 }
30474
30475 // Create new item in the tree, based on the drag source.
30476 model.newItem(newItemsParams[idx], newParentItem, insertIndex, before);
30477 }
30478 }, this);
30479
30480 // Expand the target node (if it's currently collapsed) so the user can see
30481 // where their node was dropped. In particular since that node is still selected.
30482 this.tree._expandNode(target);
30483 }
30484 this.onDndCancel();
30485 },
30486
30487 onDndCancel: function(){
30488 // summary:
30489 // Topic event processor for /dnd/cancel, called to cancel the DnD operation
30490 // tags:
30491 // private
30492 this._unmarkTargetAnchor();
30493 this.isDragging = false;
30494 this.mouseDown = false;
30495 delete this.mouseButton;
30496 this._changeState("Source", "");
30497 this._changeState("Target", "");
30498 },
30499
30500 // When focus moves in/out of the entire Tree
30501 onOverEvent: function(){
30502 // summary:
30503 // This method is called when mouse is moved over our container (like onmouseenter)
30504 // tags:
30505 // private
30506 this.inherited(arguments);
30507 DNDManager.manager().overSource(this);
30508 },
30509 onOutEvent: function(){
30510 // summary:
30511 // This method is called when mouse is moved out of our container (like onmouseleave)
30512 // tags:
30513 // private
30514 this._unmarkTargetAnchor();
30515 var m = DNDManager.manager();
30516 if(this.isDragging){
30517 m.canDrop(false);
30518 }
30519 m.outSource(this);
30520
30521 this.inherited(arguments);
30522 },
30523
30524 _isParentChildDrop: function(source, targetRow){
30525 // summary:
30526 // Checks whether the dragged items are parent rows in the tree which are being
30527 // dragged into their own children.
30528 //
30529 // source:
30530 // The DragSource object.
30531 //
30532 // targetRow:
30533 // The tree row onto which the dragged nodes are being dropped.
30534 //
30535 // tags:
30536 // private
30537
30538 // If the dragged object is not coming from the tree this widget belongs to,
30539 // it cannot be invalid.
30540 if(!source.tree || source.tree != this.tree){
30541 return false;
30542 }
30543
30544
30545 var root = source.tree.domNode;
30546 var ids = source.selection;
30547
30548 var node = targetRow.parentNode;
30549
30550 // Iterate up the DOM hierarchy from the target drop row,
30551 // checking of any of the dragged nodes have the same ID.
30552 while(node != root && !ids[node.id]){
30553 node = node.parentNode;
30554 }
30555
30556 return node.id && ids[node.id];
30557 },
30558
30559 _unmarkTargetAnchor: function(){
30560 // summary:
30561 // Removes hover class of the current target anchor
30562 // tags:
30563 // private
30564 if(!this.targetAnchor){ return; }
30565 this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
30566 this.targetAnchor = null;
30567 this.targetBox = null;
30568 this.dropPosition = null;
30569 },
30570
30571 _markDndStatus: function(copy){
30572 // summary:
30573 // Changes source's state based on "copy" status
30574 this._changeState("Source", copy ? "Copied" : "Moved");
30575 }
30576 });
30577
30578 /*=====
30579 dndSource.__Item = __Item;
30580 =====*/
30581
30582 return dndSource;
30583 });
30584
30585 },
30586 'dijit/a11y':function(){
30587 define("dijit/a11y", [
30588 "dojo/_base/array", // array.forEach array.map
30589 "dojo/_base/config", // defaultDuration
30590 "dojo/_base/declare", // declare
30591 "dojo/dom", // dom.byId
30592 "dojo/dom-attr", // domAttr.attr domAttr.has
30593 "dojo/dom-style", // style.style
30594 "dojo/sniff", // has("ie")
30595 "./main" // for exporting methods to dijit namespace
30596 ], function(array, config, declare, dom, domAttr, domStyle, has, dijit){
30597
30598 // module:
30599 // dijit/a11y
30600
30601 var shown = (dijit._isElementShown = function(/*Element*/ elem){
30602 var s = domStyle.get(elem);
30603 return (s.visibility != "hidden")
30604 && (s.visibility != "collapsed")
30605 && (s.display != "none")
30606 && (domAttr.get(elem, "type") != "hidden");
30607 });
30608
30609 dijit.hasDefaultTabStop = function(/*Element*/ elem){
30610 // summary:
30611 // Tests if element is tab-navigable even without an explicit tabIndex setting
30612
30613 // No explicit tabIndex setting, need to investigate node type
30614 switch(elem.nodeName.toLowerCase()){
30615 case "a":
30616 // An <a> w/out a tabindex is only navigable if it has an href
30617 return domAttr.has(elem, "href");
30618 case "area":
30619 case "button":
30620 case "input":
30621 case "object":
30622 case "select":
30623 case "textarea":
30624 // These are navigable by default
30625 return true;
30626 case "iframe":
30627 // If it's an editor <iframe> then it's tab navigable.
30628 var body;
30629 try{
30630 // non-IE
30631 var contentDocument = elem.contentDocument;
30632 if("designMode" in contentDocument && contentDocument.designMode == "on"){
30633 return true;
30634 }
30635 body = contentDocument.body;
30636 }catch(e1){
30637 // contentWindow.document isn't accessible within IE7/8
30638 // if the iframe.src points to a foreign url and this
30639 // page contains an element, that could get focus
30640 try{
30641 body = elem.contentWindow.document.body;
30642 }catch(e2){
30643 return false;
30644 }
30645 }
30646 return body && (body.contentEditable == 'true' ||
30647 (body.firstChild && body.firstChild.contentEditable == 'true'));
30648 default:
30649 return elem.contentEditable == 'true';
30650 }
30651 };
30652
30653 var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
30654 // summary:
30655 // Tests if an element is tab-navigable
30656
30657 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
30658 if(domAttr.get(elem, "disabled")){
30659 return false;
30660 }else if(domAttr.has(elem, "tabIndex")){
30661 // Explicit tab index setting
30662 return domAttr.get(elem, "tabIndex") >= 0; // boolean
30663 }else{
30664 // No explicit tabIndex setting, so depends on node type
30665 return dijit.hasDefaultTabStop(elem);
30666 }
30667 });
30668
30669 dijit._getTabNavigable = function(/*DOMNode*/ root){
30670 // summary:
30671 // Finds descendants of the specified root node.
30672 // description:
30673 // Finds the following descendants of the specified root node:
30674 //
30675 // - the first tab-navigable element in document order
30676 // without a tabIndex or with tabIndex="0"
30677 // - the last tab-navigable element in document order
30678 // without a tabIndex or with tabIndex="0"
30679 // - the first element in document order with the lowest
30680 // positive tabIndex value
30681 // - the last element in document order with the highest
30682 // positive tabIndex value
30683 var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
30684
30685 function radioName(node){
30686 // If this element is part of a radio button group, return the name for that group.
30687 return node && node.tagName.toLowerCase() == "input" &&
30688 node.type && node.type.toLowerCase() == "radio" &&
30689 node.name && node.name.toLowerCase();
30690 }
30691
30692 var walkTree = function(/*DOMNode*/ parent){
30693 for(var child = parent.firstChild; child; child = child.nextSibling){
30694 // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
30695 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
30696 if(child.nodeType != 1 || (has("ie") <= 9 && child.scopeName !== "HTML") || !shown(child)){
30697 continue;
30698 }
30699
30700 if(isTabNavigable(child)){
30701 var tabindex = +domAttr.get(child, "tabIndex"); // + to convert string --> number
30702 if(!domAttr.has(child, "tabIndex") || tabindex == 0){
30703 if(!first){
30704 first = child;
30705 }
30706 last = child;
30707 }else if(tabindex > 0){
30708 if(!lowest || tabindex < lowestTabindex){
30709 lowestTabindex = tabindex;
30710 lowest = child;
30711 }
30712 if(!highest || tabindex >= highestTabindex){
30713 highestTabindex = tabindex;
30714 highest = child;
30715 }
30716 }
30717 var rn = radioName(child);
30718 if(domAttr.get(child, "checked") && rn){
30719 radioSelected[rn] = child;
30720 }
30721 }
30722 if(child.nodeName.toUpperCase() != 'SELECT'){
30723 walkTree(child);
30724 }
30725 }
30726 };
30727 if(shown(root)){
30728 walkTree(root);
30729 }
30730 function rs(node){
30731 // substitute checked radio button for unchecked one, if there is a checked one with the same name.
30732 return radioSelected[radioName(node)] || node;
30733 }
30734
30735 return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
30736 };
30737 dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
30738 // summary:
30739 // Finds the descendant of the specified root node
30740 // that is first in the tabbing order
30741 var elems = dijit._getTabNavigable(dom.byId(root, doc));
30742 return elems.lowest ? elems.lowest : elems.first; // DomNode
30743 };
30744
30745 dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
30746 // summary:
30747 // Finds the descendant of the specified root node
30748 // that is last in the tabbing order
30749 var elems = dijit._getTabNavigable(dom.byId(root, doc));
30750 return elems.last ? elems.last : elems.highest; // DomNode
30751 };
30752
30753 return {
30754 // summary:
30755 // Accessibility utility functions (keyboard, tab stops, etc.)
30756
30757 hasDefaultTabStop: dijit.hasDefaultTabStop,
30758 isTabNavigable: dijit.isTabNavigable,
30759 _getTabNavigable: dijit._getTabNavigable,
30760 getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
30761 getLastInTabbingOrder: dijit.getLastInTabbingOrder
30762 };
30763 });
30764
30765 },
30766 'dijit/form/_ToggleButtonMixin':function(){
30767 define("dijit/form/_ToggleButtonMixin", [
30768 "dojo/_base/declare", // declare
30769 "dojo/dom-attr" // domAttr.set
30770 ], function(declare, domAttr){
30771
30772 // module:
30773 // dijit/form/_ToggleButtonMixin
30774
30775 return declare("dijit.form._ToggleButtonMixin", null, {
30776 // summary:
30777 // A mixin to provide functionality to allow a button that can be in two states (checked or not).
30778
30779 // checked: Boolean
30780 // Corresponds to the native HTML `<input>` element's attribute.
30781 // In markup, specified as "checked='checked'" or just "checked".
30782 // True if the button is depressed, or the checkbox is checked,
30783 // or the radio button is selected, etc.
30784 checked: false,
30785
30786 // aria-pressed for toggle buttons, and aria-checked for checkboxes
30787 _aria_attr: "aria-pressed",
30788
30789 _onClick: function(/*Event*/ evt){
30790 var original = this.checked;
30791 this._set('checked', !original); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
30792 var ret = this.inherited(arguments); // the user could reset the value here
30793 this.set('checked', ret ? this.checked : original); // officially set the toggled or user value, or reset it back
30794 return ret;
30795 },
30796
30797 _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
30798 this._set("checked", value);
30799 var node = this.focusNode || this.domNode;
30800 domAttr.set(node, "checked", !!value); // "mixed" -> true
30801 if(value){
30802 node.setAttribute("checked", "");
30803 }else{
30804 node.removeAttribute("checked");
30805 }
30806 node.setAttribute(this._aria_attr, String(value)); // aria values should be strings
30807 this._handleOnChange(value, priorityChange);
30808 },
30809
30810 reset: function(){
30811 // summary:
30812 // Reset the widget's value to what it was at initialization time
30813
30814 this._hasBeenBlurred = false;
30815
30816 // set checked state to original setting
30817 this.set('checked', this.params.checked || false);
30818 }
30819 });
30820
30821 });
30822
30823 },
30824 'dijit/_Widget':function(){
30825 define("dijit/_Widget", [
30826 "dojo/aspect", // aspect.around
30827 "dojo/_base/config", // config.isDebug
30828 "dojo/_base/connect", // connect.connect
30829 "dojo/_base/declare", // declare
30830 "dojo/has",
30831 "dojo/_base/kernel", // kernel.deprecated
30832 "dojo/_base/lang", // lang.hitch
30833 "dojo/query",
30834 "dojo/ready",
30835 "./registry", // registry.byNode
30836 "./_WidgetBase",
30837 "./_OnDijitClickMixin",
30838 "./_FocusMixin",
30839 "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
30840 "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
30841 ], function(aspect, config, connect, declare, has, kernel, lang, query, ready,
30842 registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
30843
30844
30845 // module:
30846 // dijit/_Widget
30847
30848
30849 function connectToDomNode(){
30850 // summary:
30851 // If user connects to a widget method === this function, then they will
30852 // instead actually be connecting the equivalent event on this.domNode
30853 }
30854
30855 // Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
30856 function aroundAdvice(originalConnect){
30857 return function(obj, event, scope, method){
30858 if(obj && typeof event == "string" && obj[event] == connectToDomNode){
30859 return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
30860 }
30861 return originalConnect.apply(connect, arguments);
30862 };
30863 }
30864 aspect.around(connect, "connect", aroundAdvice);
30865 if(kernel.connect){
30866 aspect.around(kernel, "connect", aroundAdvice);
30867 }
30868
30869 var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
30870 // summary:
30871 // Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
30872 // description:
30873 // Old Base class for Dijit widgets.
30874 //
30875 // Extends _WidgetBase, adding support for:
30876 //
30877 // - declaratively/programatically specifying widget initialization parameters like
30878 // onMouseMove="foo" that call foo when this.domNode gets a mousemove event
30879 // - ondijitclick:
30880 // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
30881 // - focus related functions:
30882 // In particular, the onFocus()/onBlur() callbacks. Driven internally by
30883 // dijit/_base/focus.js.
30884 // - deprecated methods
30885 // - onShow(), onHide(), onClose()
30886 //
30887 // Also, by loading code in dijit/_base, turns on:
30888 //
30889 // - browser sniffing (putting browser class like `dj_ie` on `<html>` node)
30890 // - high contrast mode sniffing (add `dijit_a11y` class to `<body>` if machine is in high contrast mode)
30891
30892
30893 ////////////////// DEFERRED CONNECTS ///////////////////
30894
30895 onClick: connectToDomNode,
30896 /*=====
30897 onClick: function(event){
30898 // summary:
30899 // Connect to this function to receive notifications of mouse click events.
30900 // event:
30901 // mouse Event
30902 // tags:
30903 // callback
30904 },
30905 =====*/
30906 onDblClick: connectToDomNode,
30907 /*=====
30908 onDblClick: function(event){
30909 // summary:
30910 // Connect to this function to receive notifications of mouse double click events.
30911 // event:
30912 // mouse Event
30913 // tags:
30914 // callback
30915 },
30916 =====*/
30917 onKeyDown: connectToDomNode,
30918 /*=====
30919 onKeyDown: function(event){
30920 // summary:
30921 // Connect to this function to receive notifications of keys being pressed down.
30922 // event:
30923 // key Event
30924 // tags:
30925 // callback
30926 },
30927 =====*/
30928 onKeyPress: connectToDomNode,
30929 /*=====
30930 onKeyPress: function(event){
30931 // summary:
30932 // Connect to this function to receive notifications of printable keys being typed.
30933 // event:
30934 // key Event
30935 // tags:
30936 // callback
30937 },
30938 =====*/
30939 onKeyUp: connectToDomNode,
30940 /*=====
30941 onKeyUp: function(event){
30942 // summary:
30943 // Connect to this function to receive notifications of keys being released.
30944 // event:
30945 // key Event
30946 // tags:
30947 // callback
30948 },
30949 =====*/
30950 onMouseDown: connectToDomNode,
30951 /*=====
30952 onMouseDown: function(event){
30953 // summary:
30954 // Connect to this function to receive notifications of when the mouse button is pressed down.
30955 // event:
30956 // mouse Event
30957 // tags:
30958 // callback
30959 },
30960 =====*/
30961 onMouseMove: connectToDomNode,
30962 /*=====
30963 onMouseMove: function(event){
30964 // summary:
30965 // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
30966 // event:
30967 // mouse Event
30968 // tags:
30969 // callback
30970 },
30971 =====*/
30972 onMouseOut: connectToDomNode,
30973 /*=====
30974 onMouseOut: function(event){
30975 // summary:
30976 // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
30977 // event:
30978 // mouse Event
30979 // tags:
30980 // callback
30981 },
30982 =====*/
30983 onMouseOver: connectToDomNode,
30984 /*=====
30985 onMouseOver: function(event){
30986 // summary:
30987 // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
30988 // event:
30989 // mouse Event
30990 // tags:
30991 // callback
30992 },
30993 =====*/
30994 onMouseLeave: connectToDomNode,
30995 /*=====
30996 onMouseLeave: function(event){
30997 // summary:
30998 // Connect to this function to receive notifications of when the mouse moves off of this widget.
30999 // event:
31000 // mouse Event
31001 // tags:
31002 // callback
31003 },
31004 =====*/
31005 onMouseEnter: connectToDomNode,
31006 /*=====
31007 onMouseEnter: function(event){
31008 // summary:
31009 // Connect to this function to receive notifications of when the mouse moves onto this widget.
31010 // event:
31011 // mouse Event
31012 // tags:
31013 // callback
31014 },
31015 =====*/
31016 onMouseUp: connectToDomNode,
31017 /*=====
31018 onMouseUp: function(event){
31019 // summary:
31020 // Connect to this function to receive notifications of when the mouse button is released.
31021 // event:
31022 // mouse Event
31023 // tags:
31024 // callback
31025 },
31026 =====*/
31027
31028 constructor: function(params /*===== ,srcNodeRef =====*/){
31029 // summary:
31030 // Create the widget.
31031 // params: Object|null
31032 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
31033 // and functions, typically callbacks like onClick.
31034 // The hash can contain any of the widget's properties, excluding read-only properties.
31035 // srcNodeRef: DOMNode|String?
31036 // If a srcNodeRef (DOM node) is specified:
31037 //
31038 // - use srcNodeRef.innerHTML as my contents
31039 // - if this is a behavioral widget then apply behavior to that srcNodeRef
31040 // - otherwise, replace srcNodeRef with my generated DOM tree
31041
31042 // extract parameters like onMouseMove that should connect directly to this.domNode
31043 this._toConnect = {};
31044 for(var name in params){
31045 if(this[name] === connectToDomNode){
31046 this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
31047 delete params[name];
31048 }
31049 }
31050 },
31051
31052 postCreate: function(){
31053 this.inherited(arguments);
31054
31055 // perform connection from this.domNode to user specified handlers (ex: onMouseMove)
31056 for(var name in this._toConnect){
31057 this.on(name, this._toConnect[name]);
31058 }
31059 delete this._toConnect;
31060 },
31061
31062 on: function(/*String|Function*/ type, /*Function*/ func){
31063 if(this[this._onMap(type)] === connectToDomNode){
31064 // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
31065 // normalization of onkeypress/onkeydown to behave like firefox, etc.
31066 // Also, need to specify context as "this" rather than the default context of the DOMNode
31067 // Remove in 2.0.
31068 return connect.connect(this.domNode, type.toLowerCase(), this, func);
31069 }
31070 return this.inherited(arguments);
31071 },
31072
31073 _setFocusedAttr: function(val){
31074 // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
31075 // (but since it's a private variable we aren't required to keep supporting it).
31076 this._focused = val;
31077 this._set("focused", val);
31078 },
31079
31080 ////////////////// DEPRECATED METHODS ///////////////////
31081
31082 setAttribute: function(/*String*/ attr, /*anything*/ value){
31083 // summary:
31084 // Deprecated. Use set() instead.
31085 // tags:
31086 // deprecated
31087 kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
31088 this.set(attr, value);
31089 },
31090
31091 attr: function(/*String|Object*/name, /*Object?*/value){
31092 // summary:
31093 // Set or get properties on a widget instance.
31094 // name:
31095 // The property to get or set. If an object is passed here and not
31096 // a string, its keys are used as names of attributes to be set
31097 // and the value of the object as values to set in the widget.
31098 // value:
31099 // Optional. If provided, attr() operates as a setter. If omitted,
31100 // the current value of the named property is returned.
31101 // description:
31102 // This method is deprecated, use get() or set() directly.
31103
31104 // Print deprecation warning but only once per calling function
31105 if(config.isDebug){
31106 var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
31107 caller = (arguments.callee.caller || "unknown caller").toString();
31108 if(!alreadyCalledHash[caller]){
31109 kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
31110 caller, "", "2.0");
31111 alreadyCalledHash[caller] = true;
31112 }
31113 }
31114
31115 var args = arguments.length;
31116 if(args >= 2 || typeof name === "object"){ // setter
31117 return this.set.apply(this, arguments);
31118 }else{ // getter
31119 return this.get(name);
31120 }
31121 },
31122
31123 getDescendants: function(){
31124 // summary:
31125 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
31126 // This method should generally be avoided as it returns widgets declared in templates, which are
31127 // supposed to be internal/hidden, but it's left here for back-compat reasons.
31128
31129 kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
31130 return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit/_WidgetBase[]
31131 },
31132
31133 ////////////////// MISCELLANEOUS METHODS ///////////////////
31134
31135 _onShow: function(){
31136 // summary:
31137 // Internal method called when this widget is made visible.
31138 // See `onShow` for details.
31139 this.onShow();
31140 },
31141
31142 onShow: function(){
31143 // summary:
31144 // Called when this widget becomes the selected pane in a
31145 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31146 // `dijit/layout/AccordionContainer`, etc.
31147 //
31148 // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31149 // tags:
31150 // callback
31151 },
31152
31153 onHide: function(){
31154 // summary:
31155 // Called when another widget becomes the selected pane in a
31156 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31157 // `dijit/layout/AccordionContainer`, etc.
31158 //
31159 // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31160 // tags:
31161 // callback
31162 },
31163
31164 onClose: function(){
31165 // summary:
31166 // Called when this widget is being displayed as a popup (ex: a Calendar popped
31167 // up from a DateTextBox), and it is hidden.
31168 // This is called from the dijit.popup code, and should not be called directly.
31169 //
31170 // Also used as a parameter for children of `dijit/layout/StackContainer` or subclasses.
31171 // Callback if a user tries to close the child. Child will be closed if this function returns true.
31172 // tags:
31173 // extension
31174
31175 return true; // Boolean
31176 }
31177 });
31178
31179 // For back-compat, remove in 2.0.
31180 if(has("dijit-legacy-requires")){
31181 ready(0, function(){
31182 var requires = ["dijit/_base"];
31183 require(requires); // use indirection so modules not rolled into a build
31184 });
31185 }
31186 return _Widget;
31187 });
31188
31189 },
31190 'dojo/touch':function(){
31191 define("dojo/touch", ["./_base/kernel", "./aspect", "./dom", "./on", "./has", "./mouse", "./ready", "./_base/window"],
31192 function(dojo, aspect, dom, on, has, mouse, ready, win){
31193
31194 // module:
31195 // dojo/touch
31196
31197 var hasTouch = has("touch");
31198
31199 // TODO: get iOS version from dojo/sniff after #15827 is fixed
31200 var ios4 = false;
31201 if(has("ios")){
31202 var ua = navigator.userAgent;
31203 var v = ua.match(/OS ([\d_]+)/) ? RegExp.$1 : "1";
31204 var os = parseFloat(v.replace(/_/, '.').replace(/_/g, ''));
31205 ios4 = os < 5;
31206 }
31207
31208 var touchmove, hoveredNode;
31209
31210 if(hasTouch){
31211 ready(function(){
31212 // Keep track of currently hovered node
31213 hoveredNode = win.body(); // currently hovered node
31214
31215 win.doc.addEventListener("touchstart", function(evt){
31216 // Precede touchstart event with touch.over event. DnD depends on this.
31217 // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
31218 // and to ensure this code runs even if the listener on the node does event.stop().
31219 var oldNode = hoveredNode;
31220 hoveredNode = evt.target;
31221 on.emit(oldNode, "dojotouchout", {
31222 target: oldNode,
31223 relatedTarget: hoveredNode,
31224 bubbles: true
31225 });
31226 on.emit(hoveredNode, "dojotouchover", {
31227 target: hoveredNode,
31228 relatedTarget: oldNode,
31229 bubbles: true
31230 });
31231 }, true);
31232
31233 // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
31234 on(win.doc, "touchmove", function(evt){
31235 var newNode = win.doc.elementFromPoint(
31236 evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
31237 evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
31238 );
31239 if(newNode && hoveredNode !== newNode){
31240 // touch out on the old node
31241 on.emit(hoveredNode, "dojotouchout", {
31242 target: hoveredNode,
31243 relatedTarget: newNode,
31244 bubbles: true
31245 });
31246
31247 // touchover on the new node
31248 on.emit(newNode, "dojotouchover", {
31249 target: newNode,
31250 relatedTarget: hoveredNode,
31251 bubbles: true
31252 });
31253
31254 hoveredNode = newNode;
31255 }
31256 });
31257 });
31258
31259 // Define synthetic touch.move event that unlike the native touchmove, fires for the node the finger is
31260 // currently dragging over rather than the node where the touch started.
31261 touchmove = function(node, listener){
31262 return on(win.doc, "touchmove", function(evt){
31263 if(node === win.doc || dom.isDescendant(hoveredNode, node)){
31264 evt.target = hoveredNode;
31265 listener.call(this, evt);
31266 }
31267 });
31268 };
31269 }
31270
31271
31272 function _handle(type){
31273 // type: String
31274 // press | move | release | cancel
31275
31276 return function(node, listener){//called by on(), see dojo.on
31277 return on(node, type, listener);
31278 };
31279 }
31280
31281 //device neutral events - touch.press|move|release|cancel/over/out
31282 var touch = {
31283 press: _handle(hasTouch ? "touchstart": "mousedown"),
31284 move: hasTouch ? touchmove :_handle("mousemove"),
31285 release: _handle(hasTouch ? "touchend": "mouseup"),
31286 cancel: hasTouch ? _handle("touchcancel") : mouse.leave,
31287 over: _handle(hasTouch ? "dojotouchover": "mouseover"),
31288 out: _handle(hasTouch ? "dojotouchout": "mouseout"),
31289 enter: mouse._eventHandler(hasTouch ? "dojotouchover" : "mouseover"),
31290 leave: mouse._eventHandler(hasTouch ? "dojotouchout" : "mouseout")
31291 };
31292 /*=====
31293 touch = {
31294 // summary:
31295 // This module provides unified touch event handlers by exporting
31296 // press, move, release and cancel which can also run well on desktop.
31297 // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
31298 //
31299 // example:
31300 // Used with dojo.on
31301 // | define(["dojo/on", "dojo/touch"], function(on, touch){
31302 // | on(node, touch.press, function(e){});
31303 // | on(node, touch.move, function(e){});
31304 // | on(node, touch.release, function(e){});
31305 // | on(node, touch.cancel, function(e){});
31306 // example:
31307 // Used with touch.* directly
31308 // | touch.press(node, function(e){});
31309 // | touch.move(node, function(e){});
31310 // | touch.release(node, function(e){});
31311 // | touch.cancel(node, function(e){});
31312
31313 press: function(node, listener){
31314 // summary:
31315 // Register a listener to 'touchstart'|'mousedown' for the given node
31316 // node: Dom
31317 // Target node to listen to
31318 // listener: Function
31319 // Callback function
31320 // returns:
31321 // A handle which will be used to remove the listener by handle.remove()
31322 },
31323 move: function(node, listener){
31324 // summary:
31325 // Register a listener to 'touchmove'|'mousemove' for the given node
31326 // node: Dom
31327 // Target node to listen to
31328 // listener: Function
31329 // Callback function
31330 // returns:
31331 // A handle which will be used to remove the listener by handle.remove()
31332 },
31333 release: function(node, listener){
31334 // summary:
31335 // Register a listener to 'touchend'|'mouseup' for the given node
31336 // node: Dom
31337 // Target node to listen to
31338 // listener: Function
31339 // Callback function
31340 // returns:
31341 // A handle which will be used to remove the listener by handle.remove()
31342 },
31343 cancel: function(node, listener){
31344 // summary:
31345 // Register a listener to 'touchcancel'|'mouseleave' for the given node
31346 // node: Dom
31347 // Target node to listen to
31348 // listener: Function
31349 // Callback function
31350 // returns:
31351 // A handle which will be used to remove the listener by handle.remove()
31352 },
31353 over: function(node, listener){
31354 // summary:
31355 // Register a listener to 'mouseover' or touch equivalent for the given node
31356 // node: Dom
31357 // Target node to listen to
31358 // listener: Function
31359 // Callback function
31360 // returns:
31361 // A handle which will be used to remove the listener by handle.remove()
31362 },
31363 out: function(node, listener){
31364 // summary:
31365 // Register a listener to 'mouseout' or touch equivalent for the given node
31366 // node: Dom
31367 // Target node to listen to
31368 // listener: Function
31369 // Callback function
31370 // returns:
31371 // A handle which will be used to remove the listener by handle.remove()
31372 },
31373 enter: function(node, listener){
31374 // summary:
31375 // Register a listener to mouse.enter or touch equivalent for the given node
31376 // node: Dom
31377 // Target node to listen to
31378 // listener: Function
31379 // Callback function
31380 // returns:
31381 // A handle which will be used to remove the listener by handle.remove()
31382 },
31383 leave: function(node, listener){
31384 // summary:
31385 // Register a listener to mouse.leave or touch equivalent for the given node
31386 // node: Dom
31387 // Target node to listen to
31388 // listener: Function
31389 // Callback function
31390 // returns:
31391 // A handle which will be used to remove the listener by handle.remove()
31392 }
31393 };
31394 =====*/
31395
31396 1 && (dojo.touch = touch);
31397
31398 return touch;
31399 });
31400
31401 },
31402 '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",
31403 'dojo/fx':function(){
31404 define("dojo/fx", [
31405 "./_base/lang",
31406 "./Evented",
31407 "./_base/kernel",
31408 "./_base/array",
31409 "./_base/connect",
31410 "./_base/fx",
31411 "./dom",
31412 "./dom-style",
31413 "./dom-geometry",
31414 "./ready",
31415 "require" // for context sensitive loading of Toggler
31416 ], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require){
31417
31418 // module:
31419 // dojo/fx
31420
31421 // For back-compat, remove in 2.0.
31422 if(!dojo.isAsync){
31423 ready(0, function(){
31424 var requires = ["./fx/Toggler"];
31425 require(requires); // use indirection so modules not rolled into a build
31426 });
31427 }
31428
31429 var coreFx = dojo.fx = {
31430 // summary:
31431 // Effects library on top of Base animations
31432 };
31433
31434 var _baseObj = {
31435 _fire: function(evt, args){
31436 if(this[evt]){
31437 this[evt].apply(this, args||[]);
31438 }
31439 return this;
31440 }
31441 };
31442
31443 var _chain = function(animations){
31444 this._index = -1;
31445 this._animations = animations||[];
31446 this._current = this._onAnimateCtx = this._onEndCtx = null;
31447
31448 this.duration = 0;
31449 arrayUtil.forEach(this._animations, function(a){
31450 this.duration += a.duration;
31451 if(a.delay){ this.duration += a.delay; }
31452 }, this);
31453 };
31454 _chain.prototype = new Evented();
31455 lang.extend(_chain, {
31456 _onAnimate: function(){
31457 this._fire("onAnimate", arguments);
31458 },
31459 _onEnd: function(){
31460 connect.disconnect(this._onAnimateCtx);
31461 connect.disconnect(this._onEndCtx);
31462 this._onAnimateCtx = this._onEndCtx = null;
31463 if(this._index + 1 == this._animations.length){
31464 this._fire("onEnd");
31465 }else{
31466 // switch animations
31467 this._current = this._animations[++this._index];
31468 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
31469 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
31470 this._current.play(0, true);
31471 }
31472 },
31473 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
31474 if(!this._current){ this._current = this._animations[this._index = 0]; }
31475 if(!gotoStart && this._current.status() == "playing"){ return this; }
31476 var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
31477 this._fire("beforeBegin");
31478 }),
31479 onBegin = connect.connect(this._current, "onBegin", this, function(arg){
31480 this._fire("onBegin", arguments);
31481 }),
31482 onPlay = connect.connect(this._current, "onPlay", this, function(arg){
31483 this._fire("onPlay", arguments);
31484 connect.disconnect(beforeBegin);
31485 connect.disconnect(onBegin);
31486 connect.disconnect(onPlay);
31487 });
31488 if(this._onAnimateCtx){
31489 connect.disconnect(this._onAnimateCtx);
31490 }
31491 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
31492 if(this._onEndCtx){
31493 connect.disconnect(this._onEndCtx);
31494 }
31495 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
31496 this._current.play.apply(this._current, arguments);
31497 return this;
31498 },
31499 pause: function(){
31500 if(this._current){
31501 var e = connect.connect(this._current, "onPause", this, function(arg){
31502 this._fire("onPause", arguments);
31503 connect.disconnect(e);
31504 });
31505 this._current.pause();
31506 }
31507 return this;
31508 },
31509 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
31510 this.pause();
31511 var offset = this.duration * percent;
31512 this._current = null;
31513 arrayUtil.some(this._animations, function(a){
31514 if(a.duration <= offset){
31515 this._current = a;
31516 return true;
31517 }
31518 offset -= a.duration;
31519 return false;
31520 });
31521 if(this._current){
31522 this._current.gotoPercent(offset / this._current.duration, andPlay);
31523 }
31524 return this;
31525 },
31526 stop: function(/*boolean?*/ gotoEnd){
31527 if(this._current){
31528 if(gotoEnd){
31529 for(; this._index + 1 < this._animations.length; ++this._index){
31530 this._animations[this._index].stop(true);
31531 }
31532 this._current = this._animations[this._index];
31533 }
31534 var e = connect.connect(this._current, "onStop", this, function(arg){
31535 this._fire("onStop", arguments);
31536 connect.disconnect(e);
31537 });
31538 this._current.stop();
31539 }
31540 return this;
31541 },
31542 status: function(){
31543 return this._current ? this._current.status() : "stopped";
31544 },
31545 destroy: function(){
31546 if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
31547 if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
31548 }
31549 });
31550 lang.extend(_chain, _baseObj);
31551
31552 coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){
31553 // summary:
31554 // Chain a list of `dojo.Animation`s to run in sequence
31555 //
31556 // description:
31557 // Return a `dojo.Animation` which will play all passed
31558 // `dojo.Animation` instances in sequence, firing its own
31559 // synthesized events simulating a single animation. (eg:
31560 // onEnd of this animation means the end of the chain,
31561 // not the individual animations within)
31562 //
31563 // example:
31564 // Once `node` is faded out, fade in `otherNode`
31565 // | fx.chain([
31566 // | dojo.fadeIn({ node:node }),
31567 // | dojo.fadeOut({ node:otherNode })
31568 // | ]).play();
31569 //
31570 return new _chain(animations); // dojo/_base/fx.Animation
31571 };
31572
31573 var _combine = function(animations){
31574 this._animations = animations||[];
31575 this._connects = [];
31576 this._finished = 0;
31577
31578 this.duration = 0;
31579 arrayUtil.forEach(animations, function(a){
31580 var duration = a.duration;
31581 if(a.delay){ duration += a.delay; }
31582 if(this.duration < duration){ this.duration = duration; }
31583 this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
31584 }, this);
31585
31586 this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
31587 var self = this;
31588 arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
31589 function(evt){
31590 self._connects.push(connect.connect(self._pseudoAnimation, evt,
31591 function(){ self._fire(evt, arguments); }
31592 ));
31593 }
31594 );
31595 };
31596 lang.extend(_combine, {
31597 _doAction: function(action, args){
31598 arrayUtil.forEach(this._animations, function(a){
31599 a[action].apply(a, args);
31600 });
31601 return this;
31602 },
31603 _onEnd: function(){
31604 if(++this._finished > this._animations.length){
31605 this._fire("onEnd");
31606 }
31607 },
31608 _call: function(action, args){
31609 var t = this._pseudoAnimation;
31610 t[action].apply(t, args);
31611 },
31612 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
31613 this._finished = 0;
31614 this._doAction("play", arguments);
31615 this._call("play", arguments);
31616 return this;
31617 },
31618 pause: function(){
31619 this._doAction("pause", arguments);
31620 this._call("pause", arguments);
31621 return this;
31622 },
31623 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
31624 var ms = this.duration * percent;
31625 arrayUtil.forEach(this._animations, function(a){
31626 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
31627 });
31628 this._call("gotoPercent", arguments);
31629 return this;
31630 },
31631 stop: function(/*boolean?*/ gotoEnd){
31632 this._doAction("stop", arguments);
31633 this._call("stop", arguments);
31634 return this;
31635 },
31636 status: function(){
31637 return this._pseudoAnimation.status();
31638 },
31639 destroy: function(){
31640 arrayUtil.forEach(this._connects, connect.disconnect);
31641 }
31642 });
31643 lang.extend(_combine, _baseObj);
31644
31645 coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){
31646 // summary:
31647 // Combine a list of `dojo.Animation`s to run in parallel
31648 //
31649 // description:
31650 // Combine an array of `dojo.Animation`s to run in parallel,
31651 // providing a new `dojo.Animation` instance encompasing each
31652 // animation, firing standard animation events.
31653 //
31654 // example:
31655 // Fade out `node` while fading in `otherNode` simultaneously
31656 // | fx.combine([
31657 // | dojo.fadeIn({ node:node }),
31658 // | dojo.fadeOut({ node:otherNode })
31659 // | ]).play();
31660 //
31661 // example:
31662 // When the longest animation ends, execute a function:
31663 // | var anim = fx.combine([
31664 // | dojo.fadeIn({ node: n, duration:700 }),
31665 // | dojo.fadeOut({ node: otherNode, duration: 300 })
31666 // | ]);
31667 // | dojo.connect(anim, "onEnd", function(){
31668 // | // overall animation is done.
31669 // | });
31670 // | anim.play(); // play the animation
31671 //
31672 return new _combine(animations); // dojo/_base/fx.Animation
31673 };
31674
31675 coreFx.wipeIn = function(/*Object*/ args){
31676 // summary:
31677 // Expand a node to it's natural height.
31678 //
31679 // description:
31680 // Returns an animation that will expand the
31681 // node defined in 'args' object from it's current height to
31682 // it's natural height (with no scrollbar).
31683 // Node must have no margin/border/padding.
31684 //
31685 // args: Object
31686 // A hash-map of standard `dojo.Animation` constructor properties
31687 // (such as easing: node: duration: and so on)
31688 //
31689 // example:
31690 // | fx.wipeIn({
31691 // | node:"someId"
31692 // | }).play()
31693 var node = args.node = dom.byId(args.node), s = node.style, o;
31694
31695 var anim = baseFx.animateProperty(lang.mixin({
31696 properties: {
31697 height: {
31698 // wrapped in functions so we wait till the last second to query (in case value has changed)
31699 start: function(){
31700 // start at current [computed] height, but use 1px rather than 0
31701 // because 0 causes IE to display the whole panel
31702 o = s.overflow;
31703 s.overflow = "hidden";
31704 if(s.visibility == "hidden" || s.display == "none"){
31705 s.height = "1px";
31706 s.display = "";
31707 s.visibility = "";
31708 return 1;
31709 }else{
31710 var height = domStyle.get(node, "height");
31711 return Math.max(height, 1);
31712 }
31713 },
31714 end: function(){
31715 return node.scrollHeight;
31716 }
31717 }
31718 }
31719 }, args));
31720
31721 var fini = function(){
31722 s.height = "auto";
31723 s.overflow = o;
31724 };
31725 connect.connect(anim, "onStop", fini);
31726 connect.connect(anim, "onEnd", fini);
31727
31728 return anim; // dojo/_base/fx.Animation
31729 };
31730
31731 coreFx.wipeOut = function(/*Object*/ args){
31732 // summary:
31733 // Shrink a node to nothing and hide it.
31734 //
31735 // description:
31736 // Returns an animation that will shrink node defined in "args"
31737 // from it's current height to 1px, and then hide it.
31738 //
31739 // args: Object
31740 // A hash-map of standard `dojo.Animation` constructor properties
31741 // (such as easing: node: duration: and so on)
31742 //
31743 // example:
31744 // | fx.wipeOut({ node:"someId" }).play()
31745
31746 var node = args.node = dom.byId(args.node), s = node.style, o;
31747
31748 var anim = baseFx.animateProperty(lang.mixin({
31749 properties: {
31750 height: {
31751 end: 1 // 0 causes IE to display the whole panel
31752 }
31753 }
31754 }, args));
31755
31756 connect.connect(anim, "beforeBegin", function(){
31757 o = s.overflow;
31758 s.overflow = "hidden";
31759 s.display = "";
31760 });
31761 var fini = function(){
31762 s.overflow = o;
31763 s.height = "auto";
31764 s.display = "none";
31765 };
31766 connect.connect(anim, "onStop", fini);
31767 connect.connect(anim, "onEnd", fini);
31768
31769 return anim; // dojo/_base/fx.Animation
31770 };
31771
31772 coreFx.slideTo = function(/*Object*/ args){
31773 // summary:
31774 // Slide a node to a new top/left position
31775 //
31776 // description:
31777 // Returns an animation that will slide "node"
31778 // defined in args Object from its current position to
31779 // the position defined by (args.left, args.top).
31780 //
31781 // args: Object
31782 // A hash-map of standard `dojo.Animation` constructor properties
31783 // (such as easing: node: duration: and so on). Special args members
31784 // are `top` and `left`, which indicate the new position to slide to.
31785 //
31786 // example:
31787 // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
31788
31789 var node = args.node = dom.byId(args.node),
31790 top = null, left = null;
31791
31792 var init = (function(n){
31793 return function(){
31794 var cs = domStyle.getComputedStyle(n);
31795 var pos = cs.position;
31796 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
31797 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
31798 if(pos != 'absolute' && pos != 'relative'){
31799 var ret = geom.position(n, true);
31800 top = ret.y;
31801 left = ret.x;
31802 n.style.position="absolute";
31803 n.style.top=top+"px";
31804 n.style.left=left+"px";
31805 }
31806 };
31807 })(node);
31808 init();
31809
31810 var anim = baseFx.animateProperty(lang.mixin({
31811 properties: {
31812 top: args.top || 0,
31813 left: args.left || 0
31814 }
31815 }, args));
31816 connect.connect(anim, "beforeBegin", anim, init);
31817
31818 return anim; // dojo/_base/fx.Animation
31819 };
31820
31821 return coreFx;
31822 });
31823
31824 },
31825 'dijit/_DialogMixin':function(){
31826 define("dijit/_DialogMixin", [
31827 "dojo/_base/declare", // declare
31828 "./a11y" // _getTabNavigable
31829 ], function(declare, a11y){
31830
31831 // module:
31832 // dijit/_DialogMixin
31833
31834 return declare("dijit._DialogMixin", null, {
31835 // summary:
31836 // This provides functions useful to Dialog and TooltipDialog
31837
31838 execute: function(/*Object*/ /*===== formContents =====*/){
31839 // summary:
31840 // Callback when the user hits the submit button.
31841 // Override this method to handle Dialog execution.
31842 // description:
31843 // After the user has pressed the submit button, the Dialog
31844 // first calls onExecute() to notify the container to hide the
31845 // dialog and restore focus to wherever it used to be.
31846 //
31847 // *Then* this method is called.
31848 // type:
31849 // callback
31850 },
31851
31852 onCancel: function(){
31853 // summary:
31854 // Called when user has pressed the Dialog's cancel button, to notify container.
31855 // description:
31856 // Developer shouldn't override or connect to this method;
31857 // it's a private communication device between the TooltipDialog
31858 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
31859 // type:
31860 // protected
31861 },
31862
31863 onExecute: function(){
31864 // summary:
31865 // Called when user has pressed the dialog's OK button, to notify container.
31866 // description:
31867 // Developer shouldn't override or connect to this method;
31868 // it's a private communication device between the TooltipDialog
31869 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
31870 // type:
31871 // protected
31872 },
31873
31874 _onSubmit: function(){
31875 // summary:
31876 // Callback when user hits submit button
31877 // type:
31878 // protected
31879 this.onExecute(); // notify container that we are about to execute
31880 this.execute(this.get('value'));
31881 },
31882
31883 _getFocusItems: function(){
31884 // summary:
31885 // Finds focusable items in dialog,
31886 // and sets this._firstFocusItem and this._lastFocusItem
31887 // tags:
31888 // protected
31889
31890 var elems = a11y._getTabNavigable(this.containerNode);
31891 this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
31892 this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
31893 }
31894 });
31895 });
31896
31897 },
31898 'dijit/Tree':function(){
31899 require({cache:{
31900 '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",
31901 '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"}});
31902 define("dijit/Tree", [
31903 "dojo/_base/array", // array.filter array.forEach array.map
31904 "dojo/_base/connect", // connect.isCopyKey()
31905 "dojo/cookie", // cookie
31906 "dojo/_base/declare", // declare
31907 "dojo/Deferred", // Deferred
31908 "dojo/DeferredList", // DeferredList
31909 "dojo/dom", // dom.isDescendant
31910 "dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
31911 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
31912 "dojo/dom-style",// domStyle.set
31913 "dojo/_base/event", // event.stop
31914 "dojo/errors/create", // createError
31915 "dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
31916 "dojo/_base/kernel", // kernel.deprecated
31917 "dojo/keys", // arrows etc.
31918 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
31919 "dojo/on", // on(), on.selector()
31920 "dojo/topic",
31921 "dojo/touch",
31922 "dojo/when",
31923 "./focus",
31924 "./registry", // registry.byNode(), registry.getEnclosingWidget()
31925 "./_base/manager", // manager.defaultDuration
31926 "./_Widget",
31927 "./_TemplatedMixin",
31928 "./_Container",
31929 "./_Contained",
31930 "./_CssStateMixin",
31931 "dojo/text!./templates/TreeNode.html",
31932 "dojo/text!./templates/Tree.html",
31933 "./tree/TreeStoreModel",
31934 "./tree/ForestStoreModel",
31935 "./tree/_dndSelector"
31936 ], function(array, connect, cookie, declare, Deferred, DeferredList,
31937 dom, domClass, domGeometry, domStyle, event, createError, fxUtils, kernel, keys, lang, on, topic, touch, when,
31938 focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
31939 treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
31940
31941 // module:
31942 // dijit/Tree
31943
31944 // Back-compat shim
31945 Deferred = declare(Deferred, {
31946 addCallback: function(callback){ this.then(callback); },
31947 addErrback: function(errback){ this.then(null, errback); }
31948 });
31949
31950 var TreeNode = declare(
31951 "dijit._TreeNode",
31952 [_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
31953 {
31954 // summary:
31955 // Single node within a tree. This class is used internally
31956 // by Tree and should not be accessed directly.
31957 // tags:
31958 // private
31959
31960 // item: [const] Item
31961 // the dojo.data entry this tree represents
31962 item: null,
31963
31964 // isTreeNode: [protected] Boolean
31965 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
31966 // should not be accessed directly.
31967 isTreeNode: true,
31968
31969 // label: String
31970 // Text of this tree node
31971 label: "",
31972 _setLabelAttr: {node: "labelNode", type: "innerText"},
31973
31974 // isExpandable: [private] Boolean
31975 // This node has children, so show the expando node (+ sign)
31976 isExpandable: null,
31977
31978 // isExpanded: [readonly] Boolean
31979 // This node is currently expanded (ie, opened)
31980 isExpanded: false,
31981
31982 // state: [private] String
31983 // Dynamic loading-related stuff.
31984 // When an empty folder node appears, it is "UNCHECKED" first,
31985 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
31986 state: "UNCHECKED",
31987
31988 templateString: treeNodeTemplate,
31989
31990 baseClass: "dijitTreeNode",
31991
31992 // For hover effect for tree node, and focus effect for label
31993 cssStateNodes: {
31994 rowNode: "dijitTreeRow"
31995 },
31996
31997 // Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
31998 _setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
31999
32000 buildRendering: function(){
32001 this.inherited(arguments);
32002
32003 // set expand icon for leaf
32004 this._setExpando();
32005
32006 // set icon and label class based on item
32007 this._updateItemClasses(this.item);
32008
32009 if(this.isExpandable){
32010 this.labelNode.setAttribute("aria-expanded", this.isExpanded);
32011 }
32012
32013 //aria-selected should be false on all selectable elements.
32014 this.setSelected(false);
32015 },
32016
32017 _setIndentAttr: function(indent){
32018 // summary:
32019 // Tell this node how many levels it should be indented
32020 // description:
32021 // 0 for top level nodes, 1 for their children, 2 for their
32022 // grandchildren, etc.
32023
32024 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
32025 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
32026
32027 domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px"); // TODOC: what is this for???
32028 domStyle.set(this.indentNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
32029
32030 array.forEach(this.getChildren(), function(child){
32031 child.set("indent", indent+1);
32032 });
32033
32034 this._set("indent", indent);
32035 },
32036
32037 markProcessing: function(){
32038 // summary:
32039 // Visually denote that tree is loading data, etc.
32040 // tags:
32041 // private
32042 this.state = "LOADING";
32043 this._setExpando(true);
32044 },
32045
32046 unmarkProcessing: function(){
32047 // summary:
32048 // Clear markup from markProcessing() call
32049 // tags:
32050 // private
32051 this._setExpando(false);
32052 },
32053
32054 _updateItemClasses: function(item){
32055 // summary:
32056 // Set appropriate CSS classes for icon and label dom node
32057 // (used to allow for item updates to change respective CSS)
32058 // tags:
32059 // private
32060 var tree = this.tree, model = tree.model;
32061 if(tree._v10Compat && item === model.root){
32062 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
32063 item = null;
32064 }
32065 this._applyClassAndStyle(item, "icon", "Icon");
32066 this._applyClassAndStyle(item, "label", "Label");
32067 this._applyClassAndStyle(item, "row", "Row");
32068
32069 this.tree._startPaint(true); // signifies paint started and finished (synchronously)
32070 },
32071
32072 _applyClassAndStyle: function(item, lower, upper){
32073 // summary:
32074 // Set the appropriate CSS classes and styles for labels, icons and rows.
32075 //
32076 // item:
32077 // The data item.
32078 //
32079 // lower:
32080 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
32081 //
32082 // upper:
32083 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
32084 //
32085 // tags:
32086 // private
32087
32088 var clsName = "_" + lower + "Class";
32089 var nodeName = lower + "Node";
32090 var oldCls = this[clsName];
32091
32092 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
32093 domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
32094
32095 domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
32096 },
32097
32098 _updateLayout: function(){
32099 // summary:
32100 // Set appropriate CSS classes for this.domNode
32101 // tags:
32102 // private
32103 var parent = this.getParent();
32104 if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
32105 /* if we are hiding the root node then make every first level child look like a root node */
32106 domClass.add(this.domNode, "dijitTreeIsRoot");
32107 }else{
32108 domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
32109 }
32110 },
32111
32112 _setExpando: function(/*Boolean*/ processing){
32113 // summary:
32114 // Set the right image for the expando node
32115 // tags:
32116 // private
32117
32118 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
32119 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
32120 _a11yStates = ["*","-","+","*"],
32121 idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
32122
32123 // apply the appropriate class to the expando node
32124 domClass.replace(this.expandoNode, styles[idx], styles);
32125
32126 // provide a non-image based indicator for images-off mode
32127 this.expandoNodeText.innerHTML = _a11yStates[idx];
32128
32129 },
32130
32131 expand: function(){
32132 // summary:
32133 // Show my children
32134 // returns:
32135 // Deferred that fires when expansion is complete
32136
32137 // If there's already an expand in progress or we are already expanded, just return
32138 if(this._expandDeferred){
32139 return this._expandDeferred; // dojo/_base/Deferred
32140 }
32141
32142 // cancel in progress collapse operation
32143 if(this._collapseDeferred){
32144 this._collapseDeferred.cancel();
32145 delete this._collapseDeferred;
32146 }
32147
32148 // All the state information for when a node is expanded, maybe this should be
32149 // set when the animation completes instead
32150 this.isExpanded = true;
32151 this.labelNode.setAttribute("aria-expanded", "true");
32152 if(this.tree.showRoot || this !== this.tree.rootNode){
32153 this.containerNode.setAttribute("role", "group");
32154 }
32155 domClass.add(this.contentNode,'dijitTreeContentExpanded');
32156 this._setExpando();
32157 this._updateItemClasses(this.item);
32158
32159 if(this == this.tree.rootNode && this.tree.showRoot){
32160 this.tree.domNode.setAttribute("aria-expanded", "true");
32161 }
32162
32163 var def,
32164 wipeIn = fxUtils.wipeIn({
32165 node: this.containerNode,
32166 duration: manager.defaultDuration,
32167 onEnd: function(){
32168 def.resolve(true);
32169 }
32170 });
32171
32172 // Deferred that fires when expand is complete
32173 def = (this._expandDeferred = new Deferred(function(){
32174 // Canceller
32175 wipeIn.stop();
32176 }));
32177
32178 wipeIn.play();
32179
32180 return def; // dojo/_base/Deferred
32181 },
32182
32183 collapse: function(){
32184 // summary:
32185 // Collapse this node (if it's expanded)
32186
32187 if(this._collapseDeferred){
32188 // Node is already collapsed, or there's a collapse in progress, just return that Deferred
32189 return this._collapseDeferred;
32190 }
32191
32192 // cancel in progress expand operation
32193 if(this._expandDeferred){
32194 this._expandDeferred.cancel();
32195 delete this._expandDeferred;
32196 }
32197
32198 this.isExpanded = false;
32199 this.labelNode.setAttribute("aria-expanded", "false");
32200 if(this == this.tree.rootNode && this.tree.showRoot){
32201 this.tree.domNode.setAttribute("aria-expanded", "false");
32202 }
32203 domClass.remove(this.contentNode,'dijitTreeContentExpanded');
32204 this._setExpando();
32205 this._updateItemClasses(this.item);
32206
32207 var def,
32208 wipeOut = fxUtils.wipeOut({
32209 node: this.containerNode,
32210 duration: manager.defaultDuration,
32211 onEnd: function(){
32212 def.resolve(true);
32213 }
32214 });
32215
32216 // Deferred that fires when expand is complete
32217 def = (this._collapseDeferred = new Deferred(function(){
32218 // Canceller
32219 wipeOut.stop();
32220 }));
32221
32222 wipeOut.play();
32223
32224 return def; // dojo/_base/Deferred
32225 },
32226
32227 // indent: Integer
32228 // Levels from this node to the root node
32229 indent: 0,
32230
32231 setChildItems: function(/* Object[] */ items){
32232 // summary:
32233 // Sets the child items of this node, removing/adding nodes
32234 // from current children to match specified items[] array.
32235 // Also, if this.persist == true, expands any children that were previously
32236 // opened.
32237 // returns:
32238 // Deferred object that fires after all previously opened children
32239 // have been expanded again (or fires instantly if there are no such children).
32240
32241 var tree = this.tree,
32242 model = tree.model,
32243 defs = []; // list of deferreds that need to fire before I am complete
32244
32245
32246 // Orphan all my existing children.
32247 // If items contains some of the same items as before then we will reattach them.
32248 // Don't call this.removeChild() because that will collapse the tree etc.
32249 var oldChildren = this.getChildren();
32250 array.forEach(oldChildren, function(child){
32251 _Container.prototype.removeChild.call(this, child);
32252 }, this);
32253
32254 // All the old children of this TreeNode are subject for destruction if
32255 // 1) they aren't listed in the new children array (items)
32256 // 2) they aren't immediately adopted by another node (DnD)
32257 this.defer(function(){
32258 array.forEach(oldChildren, function(node){
32259 if(!node._destroyed && !node.getParent()){
32260 // If node is in selection then remove it.
32261 tree.dndController.removeTreeNode(node);
32262
32263 // Deregister mapping from item id --> this node
32264 var id = model.getIdentity(node.item),
32265 ary = tree._itemNodesMap[id];
32266 if(ary.length == 1){
32267 delete tree._itemNodesMap[id];
32268 }else{
32269 var index = array.indexOf(ary, node);
32270 if(index != -1){
32271 ary.splice(index, 1);
32272 }
32273 }
32274
32275 // And finally we can destroy the node
32276 node.destroyRecursive();
32277 }
32278 });
32279 });
32280
32281 this.state = "LOADED";
32282
32283 if(items && items.length > 0){
32284 this.isExpandable = true;
32285
32286 // Create _TreeNode widget for each specified tree node, unless one already
32287 // exists and isn't being used (presumably it's from a DnD move and was recently
32288 // released
32289 array.forEach(items, function(item){ // MARKER: REUSE NODE
32290 var id = model.getIdentity(item),
32291 existingNodes = tree._itemNodesMap[id],
32292 node;
32293 if(existingNodes){
32294 for(var i=0;i<existingNodes.length;i++){
32295 if(existingNodes[i] && !existingNodes[i].getParent()){
32296 node = existingNodes[i];
32297 node.set('indent', this.indent+1);
32298 break;
32299 }
32300 }
32301 }
32302 if(!node){
32303 node = this.tree._createTreeNode({
32304 item: item,
32305 tree: tree,
32306 isExpandable: model.mayHaveChildren(item),
32307 label: tree.getLabel(item),
32308 tooltip: tree.getTooltip(item),
32309 ownerDocument: tree.ownerDocument,
32310 dir: tree.dir,
32311 lang: tree.lang,
32312 textDir: tree.textDir,
32313 indent: this.indent + 1
32314 });
32315 if(existingNodes){
32316 existingNodes.push(node);
32317 }else{
32318 tree._itemNodesMap[id] = [node];
32319 }
32320 }
32321 this.addChild(node);
32322
32323 // If node was previously opened then open it again now (this may trigger
32324 // more data store accesses, recursively)
32325 if(this.tree.autoExpand || this.tree._state(node)){
32326 defs.push(tree._expandNode(node));
32327 }
32328 }, this);
32329
32330 // note that updateLayout() needs to be called on each child after
32331 // _all_ the children exist
32332 array.forEach(this.getChildren(), function(child){
32333 child._updateLayout();
32334 });
32335 }else{
32336 this.isExpandable=false;
32337 }
32338
32339 if(this._setExpando){
32340 // change expando to/from dot or + icon, as appropriate
32341 this._setExpando(false);
32342 }
32343
32344 // Set leaf icon or folder icon, as appropriate
32345 this._updateItemClasses(this.item);
32346
32347 // On initial tree show, make the selected TreeNode as either the root node of the tree,
32348 // or the first child, if the root node is hidden
32349 if(this == tree.rootNode){
32350 var fc = this.tree.showRoot ? this : this.getChildren()[0];
32351 if(fc){
32352 fc.setFocusable(true);
32353 tree.lastFocused = fc;
32354 }else{
32355 // fallback: no nodes in tree so focus on Tree <div> itself
32356 tree.domNode.setAttribute("tabIndex", "0");
32357 }
32358 }
32359
32360 var def = new DeferredList(defs);
32361 this.tree._startPaint(def); // to reset TreeNode widths after an item is added/removed from the Tree
32362 return def; // dojo/_base/Deferred
32363 },
32364
32365 getTreePath: function(){
32366 var node = this;
32367 var path = [];
32368 while(node && node !== this.tree.rootNode){
32369 path.unshift(node.item);
32370 node = node.getParent();
32371 }
32372 path.unshift(this.tree.rootNode.item);
32373
32374 return path;
32375 },
32376
32377 getIdentity: function(){
32378 return this.tree.model.getIdentity(this.item);
32379 },
32380
32381 removeChild: function(/* treeNode */ node){
32382 this.inherited(arguments);
32383
32384 var children = this.getChildren();
32385 if(children.length == 0){
32386 this.isExpandable = false;
32387 this.collapse();
32388 }
32389
32390 array.forEach(children, function(child){
32391 child._updateLayout();
32392 });
32393 },
32394
32395 makeExpandable: function(){
32396 // summary:
32397 // if this node wasn't already showing the expando node,
32398 // turn it into one and call _setExpando()
32399
32400 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
32401
32402 this.isExpandable = true;
32403 this._setExpando(false);
32404 },
32405
32406 setSelected: function(/*Boolean*/ selected){
32407 // summary:
32408 // A Tree has a (single) currently selected node.
32409 // Mark that this node is/isn't that currently selected node.
32410 // description:
32411 // In particular, setting a node as selected involves setting tabIndex
32412 // so that when user tabs to the tree, focus will go to that node (only).
32413 this.labelNode.setAttribute("aria-selected", selected ? "true" : "false");
32414 domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
32415 },
32416
32417 setFocusable: function(/*Boolean*/ selected){
32418 // summary:
32419 // A Tree has a (single) node that's focusable.
32420 // Mark that this node is/isn't that currently focsuable node.
32421 // description:
32422 // In particular, setting a node as selected involves setting tabIndex
32423 // so that when user tabs to the tree, focus will go to that node (only).
32424
32425 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
32426 },
32427
32428
32429 _setTextDirAttr: function(textDir){
32430 if(textDir &&((this.textDir != textDir) || !this._created)){
32431 this._set("textDir", textDir);
32432 this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
32433 array.forEach(this.getChildren(), function(childNode){
32434 childNode.set("textDir", textDir);
32435 }, this);
32436 }
32437 }
32438 });
32439
32440 var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
32441 // summary:
32442 // This widget displays hierarchical data from a store.
32443
32444 // store: [deprecated] String|dojo/data/Store
32445 // Deprecated. Use "model" parameter instead.
32446 // The store to get data to display in the tree.
32447 store: null,
32448
32449 // model: dijit/tree/model
32450 // Interface to read tree data, get notifications of changes to tree data,
32451 // and for handling drop operations (i.e drag and drop onto the tree)
32452 model: null,
32453
32454 // query: [deprecated] anything
32455 // Deprecated. User should specify query to the model directly instead.
32456 // Specifies datastore query to return the root item or top items for the tree.
32457 query: null,
32458
32459 // label: [deprecated] String
32460 // Deprecated. Use dijit/tree/ForestStoreModel directly instead.
32461 // Used in conjunction with query parameter.
32462 // If a query is specified (rather than a root node id), and a label is also specified,
32463 // then a fake root node is created and displayed, with this label.
32464 label: "",
32465
32466 // showRoot: [const] Boolean
32467 // Should the root node be displayed, or hidden?
32468 showRoot: true,
32469
32470 // childrenAttr: [deprecated] String[]
32471 // Deprecated. This information should be specified in the model.
32472 // One ore more attributes that holds children of a tree node
32473 childrenAttr: ["children"],
32474
32475 // paths: String[][] or Item[][]
32476 // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
32477 // Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
32478 // returns a Deferred to indicate when the set is complete.
32479 paths: [],
32480
32481 // path: String[] or Item[]
32482 // Backward compatible singular variant of paths.
32483 path: [],
32484
32485 // selectedItems: [readonly] Item[]
32486 // The currently selected items in this tree.
32487 // This property can only be set (via set('selectedItems', ...)) when that item is already
32488 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
32489 // Should generally use `paths` attribute to set the selected items instead.
32490 selectedItems: null,
32491
32492 // selectedItem: [readonly] Item
32493 // Backward compatible singular variant of selectedItems.
32494 selectedItem: null,
32495
32496 // openOnClick: Boolean
32497 // If true, clicking a folder node's label will open it, rather than calling onClick()
32498 openOnClick: false,
32499
32500 // openOnDblClick: Boolean
32501 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
32502 openOnDblClick: false,
32503
32504 templateString: treeTemplate,
32505
32506 // persist: Boolean
32507 // Enables/disables use of cookies for state saving.
32508 persist: true,
32509
32510 // autoExpand: Boolean
32511 // Fully expand the tree on load. Overrides `persist`.
32512 autoExpand: false,
32513
32514 // dndController: [protected] Function|String
32515 // Class to use as as the dnd controller. Specifying this class enables DnD.
32516 // Generally you should specify this as dijit/tree/dndSource.
32517 // Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
32518 dndController: _dndSelector,
32519
32520 // parameters to pull off of the tree and pass on to the dndController as its params
32521 dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
32522
32523 //declare the above items so they can be pulled from the tree's markup
32524
32525 // onDndDrop: [protected] Function
32526 // Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
32527 // Generally this doesn't need to be set.
32528 onDndDrop: null,
32529
32530 itemCreator: null,
32531 /*=====
32532 itemCreator: function(nodes, target, source){
32533 // summary:
32534 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
32535 // dropped onto the tree. Developer must override this method to enable
32536 // dropping from external sources onto this Tree, unless the Tree.model's items
32537 // happen to look like {id: 123, name: "Apple" } with no other attributes.
32538 //
32539 // For each node in nodes[], which came from source, create a hash of name/value
32540 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
32541 // nodes: DomNode[]
32542 // The DOMNodes dragged from the source container
32543 // target: DomNode
32544 // The target TreeNode.rowNode
32545 // source: dojo/dnd/Source
32546 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
32547 // returns: Object[]
32548 // Array of name/value hashes for each new item to be added to the Tree, like:
32549 // | [
32550 // | { id: 123, label: "apple", foo: "bar" },
32551 // | { id: 456, label: "pear", zaz: "bam" }
32552 // | ]
32553 // tags:
32554 // extension
32555 return [{}];
32556 },
32557 =====*/
32558
32559 // onDndCancel: [protected] Function
32560 // Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
32561 // Generally this doesn't need to be set.
32562 onDndCancel: null,
32563
32564 /*=====
32565 checkAcceptance: function(source, nodes){
32566 // summary:
32567 // Checks if the Tree itself can accept nodes from this source
32568 // source: dijit/tree/dndSource
32569 // The source which provides items
32570 // nodes: DOMNode[]
32571 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
32572 // source is a dijit/Tree.
32573 // tags:
32574 // extension
32575 return true; // Boolean
32576 },
32577 =====*/
32578 checkAcceptance: null,
32579
32580 /*=====
32581 checkItemAcceptance: function(target, source, position){
32582 // summary:
32583 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
32584 // description:
32585 // In the base case, this is called to check if target can become a child of source.
32586 // When betweenThreshold is set, position="before" or "after" means that we
32587 // are asking if the source node can be dropped before/after the target node.
32588 // target: DOMNode
32589 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
32590 // Use registry.getEnclosingWidget(target) to get the TreeNode.
32591 // source: dijit/tree/dndSource
32592 // The (set of) nodes we are dropping
32593 // position: String
32594 // "over", "before", or "after"
32595 // tags:
32596 // extension
32597 return true; // Boolean
32598 },
32599 =====*/
32600 checkItemAcceptance: null,
32601
32602 // dragThreshold: Integer
32603 // Number of pixels mouse moves before it's considered the start of a drag operation
32604 dragThreshold: 5,
32605
32606 // betweenThreshold: Integer
32607 // Set to a positive value to allow drag and drop "between" nodes.
32608 //
32609 // If during DnD mouse is over a (target) node but less than betweenThreshold
32610 // pixels from the bottom edge, dropping the the dragged node will make it
32611 // the next sibling of the target node, rather than the child.
32612 //
32613 // Similarly, if mouse is over a target node but less that betweenThreshold
32614 // pixels from the top edge, dropping the dragged node will make it
32615 // the target node's previous sibling rather than the target node's child.
32616 betweenThreshold: 0,
32617
32618 // _nodePixelIndent: Integer
32619 // Number of pixels to indent tree nodes (relative to parent node).
32620 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
32621 // and calling resize() or startup() on tree after it's in the DOM.
32622 _nodePixelIndent: 19,
32623
32624 _publish: function(/*String*/ topicName, /*Object*/ message){
32625 // summary:
32626 // Publish a message for this widget/topic
32627 topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
32628 },
32629
32630 postMixInProperties: function(){
32631 this.tree = this;
32632
32633 if(this.autoExpand){
32634 // There's little point in saving opened/closed state of nodes for a Tree
32635 // that initially opens all it's nodes.
32636 this.persist = false;
32637 }
32638
32639 this._itemNodesMap = {};
32640
32641 if(!this.cookieName && this.id){
32642 this.cookieName = this.id + "SaveStateCookie";
32643 }
32644
32645 // Deferred that fires when all the children have loaded.
32646 this.expandChildrenDeferred = new Deferred();
32647
32648 // Deferred that fires when all pending operations complete.
32649 this.pendingCommandsDeferred = this.expandChildrenDeferred;
32650
32651 this.inherited(arguments);
32652 },
32653
32654 postCreate: function(){
32655 this._initState();
32656
32657 // Catch events on TreeNodes
32658 var self = this;
32659 this.own(
32660 on(this.domNode, on.selector(".dijitTreeNode", touch.enter), function(evt){
32661 self._onNodeMouseEnter(registry.byNode(this), evt);
32662 }),
32663 on(this.domNode, on.selector(".dijitTreeNode", touch.leave), function(evt){
32664 self._onNodeMouseLeave(registry.byNode(this), evt);
32665 }),
32666 on(this.domNode, on.selector(".dijitTreeNode", "click"), function(evt){
32667 self._onClick(registry.byNode(this), evt);
32668 }),
32669 on(this.domNode, on.selector(".dijitTreeNode", "dblclick"), function(evt){
32670 self._onDblClick(registry.byNode(this), evt);
32671 }),
32672 on(this.domNode, on.selector(".dijitTreeNode", "keypress"), function(evt){
32673 self._onKeyPress(registry.byNode(this), evt);
32674 }),
32675 on(this.domNode, on.selector(".dijitTreeNode", "keydown"), function(evt){
32676 self._onKeyDown(registry.byNode(this), evt);
32677 }),
32678 on(this.domNode, on.selector(".dijitTreeRow", "focusin"), function(evt){
32679 self._onNodeFocus(registry.getEnclosingWidget(this), evt);
32680 })
32681 );
32682
32683 // Create glue between store and Tree, if not specified directly by user
32684 if(!this.model){
32685 this._store2model();
32686 }
32687
32688 // monitor changes to items
32689 this.connect(this.model, "onChange", "_onItemChange");
32690 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
32691 this.connect(this.model, "onDelete", "_onItemDelete");
32692
32693 this.inherited(arguments);
32694
32695 if(this.dndController){
32696 if(lang.isString(this.dndController)){
32697 this.dndController = lang.getObject(this.dndController);
32698 }
32699 var params={};
32700 for(var i=0; i<this.dndParams.length;i++){
32701 if(this[this.dndParams[i]]){
32702 params[this.dndParams[i]] = this[this.dndParams[i]];
32703 }
32704 }
32705 this.dndController = new this.dndController(this, params);
32706 }
32707
32708 this._load();
32709
32710 // If no path was specified to the constructor, use path saved in cookie
32711 if(!this.params.path && !this.params.paths && this.persist){
32712 this.set("paths", this.dndController._getSavedPaths());
32713 }
32714
32715 // onLoadDeferred should fire when all commands that are part of initialization have completed.
32716 // It will include all the set("paths", ...) commands that happen during initialization.
32717 this.onLoadDeferred = this.pendingCommandsDeferred;
32718
32719 this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
32720 },
32721
32722 _store2model: function(){
32723 // summary:
32724 // User specified a store&query rather than model, so create model from store/query
32725 this._v10Compat = true;
32726 kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
32727
32728 var modelParams = {
32729 id: this.id + "_ForestStoreModel",
32730 store: this.store,
32731 query: this.query,
32732 childrenAttrs: this.childrenAttr
32733 };
32734
32735 // Only override the model's mayHaveChildren() method if the user has specified an override
32736 if(this.params.mayHaveChildren){
32737 modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
32738 }
32739
32740 if(this.params.getItemChildren){
32741 modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
32742 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
32743 });
32744 }
32745 this.model = new ForestStoreModel(modelParams);
32746
32747 // For backwards compatibility, the visibility of the root node is controlled by
32748 // whether or not the user has specified a label
32749 this.showRoot = Boolean(this.label);
32750 },
32751
32752 onLoad: function(){
32753 // summary:
32754 // Called when tree finishes loading and expanding.
32755 // description:
32756 // If persist == true the loading may encompass many levels of fetches
32757 // from the data store, each asynchronous. Waits for all to finish.
32758 // tags:
32759 // callback
32760 },
32761
32762 _load: function(){
32763 // summary:
32764 // Initial load of the tree.
32765 // Load root node (possibly hidden) and it's children.
32766 this.model.getRoot(
32767 lang.hitch(this, function(item){
32768 var rn = (this.rootNode = this.tree._createTreeNode({
32769 item: item,
32770 tree: this,
32771 isExpandable: true,
32772 label: this.label || this.getLabel(item),
32773 textDir: this.textDir,
32774 indent: this.showRoot ? 0 : -1
32775 }));
32776
32777 if(!this.showRoot){
32778 rn.rowNode.style.display="none";
32779 // if root is not visible, move tree role to the invisible
32780 // root node's containerNode, see #12135
32781 this.domNode.setAttribute("role", "presentation");
32782 this.domNode.removeAttribute("aria-expanded");
32783 this.domNode.removeAttribute("aria-multiselectable");
32784
32785 rn.labelNode.setAttribute("role", "presentation");
32786 rn.containerNode.setAttribute("role", "tree");
32787 rn.containerNode.setAttribute("aria-expanded","true");
32788 rn.containerNode.setAttribute("aria-multiselectable", !this.dndController.singular);
32789 }else{
32790 this.domNode.setAttribute("aria-multiselectable", !this.dndController.singular);
32791 }
32792
32793 this.domNode.appendChild(rn.domNode);
32794 var identity = this.model.getIdentity(item);
32795 if(this._itemNodesMap[identity]){
32796 this._itemNodesMap[identity].push(rn);
32797 }else{
32798 this._itemNodesMap[identity] = [rn];
32799 }
32800
32801 rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
32802
32803 // Load top level children, and if persist==true, all nodes that were previously opened
32804 this._expandNode(rn).then(lang.hitch(this, function(){
32805 // Then, select the nodes that were selected last time, or
32806 // the ones specified by params.paths[].
32807
32808 this.expandChildrenDeferred.resolve(true);
32809 }));
32810 }),
32811 lang.hitch(this, function(err){
32812 console.error(this, ": error loading root: ", err);
32813 })
32814 );
32815 },
32816
32817 getNodesByItem: function(/*Item or id*/ item){
32818 // summary:
32819 // Returns all tree nodes that refer to an item
32820 // returns:
32821 // Array of tree nodes that refer to passed item
32822
32823 if(!item){ return []; }
32824 var identity = lang.isString(item) ? item : this.model.getIdentity(item);
32825 // return a copy so widget don't get messed up by changes to returned array
32826 return [].concat(this._itemNodesMap[identity]);
32827 },
32828
32829 _setSelectedItemAttr: function(/*Item or id*/ item){
32830 this.set('selectedItems', [item]);
32831 },
32832
32833 _setSelectedItemsAttr: function(/*Items or ids*/ items){
32834 // summary:
32835 // Select tree nodes related to passed items.
32836 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
32837 // behavior is undefined. Use set('paths', ...) instead.
32838 var tree = this;
32839 return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then( lang.hitch(this, function(){
32840 var identities = array.map(items, function(item){
32841 return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
32842 });
32843 var nodes = [];
32844 array.forEach(identities, function(id){
32845 nodes = nodes.concat(tree._itemNodesMap[id] || []);
32846 });
32847 this.set('selectedNodes', nodes);
32848 }));
32849 },
32850
32851 _setPathAttr: function(/*Item[]|String[]*/ path){
32852 // summary:
32853 // Singular variant of _setPathsAttr
32854 if(path.length){
32855 return this.set("paths", [path]);
32856 }else{
32857 // Empty list is interpreted as "select nothing"
32858 return this.set("paths", []);
32859 }
32860 },
32861
32862 _setPathsAttr: function(/*Item[][]|String[][]*/ paths){
32863 // summary:
32864 // Select the tree nodes identified by passed paths.
32865 // paths:
32866 // Array of arrays of items or item id's
32867 // returns:
32868 // Deferred to indicate when the set is complete
32869
32870 var tree = this;
32871
32872 // Let any previous set("path", ...) commands complete before this one starts.
32873 return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then(function(){
32874 // We may need to wait for some nodes to expand, so setting
32875 // each path will involve a Deferred. We bring those deferreds
32876 // together with a DeferredList.
32877 return new DeferredList(array.map(paths, function(path){
32878 var d = new Deferred();
32879
32880 // normalize path to use identity
32881 path = array.map(path, function(item){
32882 return lang.isString(item) ? item : tree.model.getIdentity(item);
32883 });
32884
32885 if(path.length){
32886 // Wait for the tree to load, if it hasn't already.
32887 selectPath(path, [tree.rootNode], d);
32888 }else{
32889 d.reject(new Tree.PathError("Empty path"));
32890 }
32891 return d;
32892 }));
32893 }).then(setNodes);
32894
32895 function selectPath(path, nodes, def){
32896 // Traverse path; the next path component should be among "nodes".
32897 var nextPath = path.shift();
32898 var nextNode = array.filter(nodes, function(node){
32899 return node.getIdentity() == nextPath;
32900 })[0];
32901 if(!!nextNode){
32902 if(path.length){
32903 tree._expandNode(nextNode).then(function(){ selectPath(path, nextNode.getChildren(), def); });
32904 }else{
32905 // Successfully reached the end of this path
32906 def.resolve(nextNode);
32907 }
32908 }else{
32909 def.reject(new Tree.PathError("Could not expand path at " + nextPath));
32910 }
32911 }
32912
32913 function setNodes(newNodes){
32914 // After all expansion is finished, set the selection to
32915 // the set of nodes successfully found.
32916 tree.set("selectedNodes", array.map(
32917 array.filter(newNodes,function(x){return x[0];}),
32918 function(x){return x[1];}));
32919 }
32920 },
32921
32922 _setSelectedNodeAttr: function(node){
32923 this.set('selectedNodes', [node]);
32924 },
32925 _setSelectedNodesAttr: function(nodes){
32926 // summary:
32927 // Marks the specified TreeNodes as selected.
32928 // nodes: TreeNode[]
32929 // TreeNodes to mark.
32930 this.dndController.setSelection(nodes);
32931 },
32932
32933
32934 expandAll: function(){
32935 // summary:
32936 // Expand all nodes in the tree
32937 // returns:
32938 // Deferred that fires when all nodes have expanded
32939
32940 var _this = this;
32941
32942 function expand(node){
32943 var def = new dojo.Deferred();
32944
32945 // Expand the node
32946 _this._expandNode(node).then(function(){
32947 // When node has expanded, call expand() recursively on each non-leaf child
32948 var childBranches = array.filter(node.getChildren() || [], function(node){
32949 return node.isExpandable;
32950 }),
32951 defs = array.map(childBranches, expand);
32952
32953 // And when all those recursive calls finish, signal that I'm finished
32954 new dojo.DeferredList(defs).then(function(){
32955 def.resolve(true);
32956 });
32957 });
32958
32959 return def;
32960 }
32961
32962 return expand(this.rootNode);
32963 },
32964
32965 collapseAll: function(){
32966 // summary:
32967 // Collapse all nodes in the tree
32968 // returns:
32969 // Deferred that fires when all nodes have collapsed
32970
32971 var _this = this;
32972
32973 function collapse(node){
32974 var def = new dojo.Deferred();
32975 def.label = "collapseAllDeferred";
32976
32977 // Collapse children first
32978 var childBranches = array.filter(node.getChildren() || [], function(node){
32979 return node.isExpandable;
32980 }),
32981 defs = array.map(childBranches, collapse);
32982
32983 // And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
32984 // in which case collapseAll() is finished
32985 new dojo.DeferredList(defs).then(function(){
32986 if(!node.isExpanded || (node == _this.rootNode && !_this.showRoot)){
32987 def.resolve(true);
32988 }else{
32989 _this._collapseNode(node).then(function(){
32990 // When node has collapsed, signal that call is finished
32991 def.resolve(true);
32992 });
32993 }
32994 });
32995
32996
32997 return def;
32998 }
32999
33000 return collapse(this.rootNode);
33001 },
33002
33003 ////////////// Data store related functions //////////////////////
33004 // These just get passed to the model; they are here for back-compat
33005
33006 mayHaveChildren: function(/*dojo/data/Item*/ /*===== item =====*/){
33007 // summary:
33008 // Deprecated. This should be specified on the model itself.
33009 //
33010 // Overridable function to tell if an item has or may have children.
33011 // Controls whether or not +/- expando icon is shown.
33012 // (For efficiency reasons we may not want to check if an element actually
33013 // has children until user clicks the expando node)
33014 // tags:
33015 // deprecated
33016 },
33017
33018 getItemChildren: function(/*===== parentItem, onComplete =====*/){
33019 // summary:
33020 // Deprecated. This should be specified on the model itself.
33021 //
33022 // Overridable function that return array of child items of given parent item,
33023 // or if parentItem==null then return top items in tree
33024 // tags:
33025 // deprecated
33026 },
33027
33028 ///////////////////////////////////////////////////////
33029 // Functions for converting an item to a TreeNode
33030 getLabel: function(/*dojo/data/Item*/ item){
33031 // summary:
33032 // Overridable function to get the label for a tree node (given the item)
33033 // tags:
33034 // extension
33035 return this.model.getLabel(item); // String
33036 },
33037
33038 getIconClass: function(/*dojo/data/Item*/ item, /*Boolean*/ opened){
33039 // summary:
33040 // Overridable function to return CSS class name to display icon
33041 // tags:
33042 // extension
33043 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
33044 },
33045
33046 getLabelClass: function(/*===== item, opened =====*/){
33047 // summary:
33048 // Overridable function to return CSS class name to display label
33049 // item: dojo/data/Item
33050 // opened: Boolean
33051 // returns: String
33052 // CSS class name
33053 // tags:
33054 // extension
33055 },
33056
33057 getRowClass: function(/*===== item, opened =====*/){
33058 // summary:
33059 // Overridable function to return CSS class name to display row
33060 // item: dojo/data/Item
33061 // opened: Boolean
33062 // returns: String
33063 // CSS class name
33064 // tags:
33065 // extension
33066 },
33067
33068 getIconStyle: function(/*===== item, opened =====*/){
33069 // summary:
33070 // Overridable function to return CSS styles to display icon
33071 // item: dojo/data/Item
33072 // opened: Boolean
33073 // returns: Object
33074 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
33075 // tags:
33076 // extension
33077 },
33078
33079 getLabelStyle: function(/*===== item, opened =====*/){
33080 // summary:
33081 // Overridable function to return CSS styles to display label
33082 // item: dojo/data/Item
33083 // opened: Boolean
33084 // returns:
33085 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
33086 // tags:
33087 // extension
33088 },
33089
33090 getRowStyle: function(/*===== item, opened =====*/){
33091 // summary:
33092 // Overridable function to return CSS styles to display row
33093 // item: dojo/data/Item
33094 // opened: Boolean
33095 // returns:
33096 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
33097 // tags:
33098 // extension
33099 },
33100
33101 getTooltip: function(/*dojo/data/Item*/ /*===== item =====*/){
33102 // summary:
33103 // Overridable function to get the tooltip for a tree node (given the item)
33104 // tags:
33105 // extension
33106 return ""; // String
33107 },
33108
33109 /////////// Keyboard and Mouse handlers ////////////////////
33110
33111 _onKeyPress: function(/*TreeNode*/ treeNode, /*Event*/ e){
33112 // summary:
33113 // Handles keystrokes for printable keys, doing search navigation
33114
33115 if(e.charCode <= 32){
33116 // Avoid duplicate events on firefox (this is an arrow key that will be handled by keydown handler)
33117 return;
33118 }
33119
33120 if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
33121 var c = String.fromCharCode(e.charCode);
33122 this._onLetterKeyNav( { node: treeNode, key: c.toLowerCase() } );
33123 event.stop(e);
33124 }
33125 },
33126
33127 _onKeyDown: function(/*TreeNode*/ treeNode, /*Event*/ e){
33128 // summary:
33129 // Handles arrow, space, and enter keys
33130
33131 var key = e.keyCode;
33132
33133 var map = this._keyHandlerMap;
33134 if(!map){
33135 // Setup table mapping keys to events.
33136 // On WebKit based browsers, the combination ctrl-enter does not get passed through. To allow accessible
33137 // multi-select on those browsers, the space key is also used for selection.
33138 // Therefore, also allow space key for keyboard "click" operation.
33139 map = {};
33140 map[keys.ENTER] = map[keys.SPACE] = map[" "] = "_onEnterKey";
33141 map[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW] = "_onLeftArrow";
33142 map[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW] = "_onRightArrow";
33143 map[keys.UP_ARROW] = "_onUpArrow";
33144 map[keys.DOWN_ARROW] = "_onDownArrow";
33145 map[keys.HOME] = "_onHomeKey";
33146 map[keys.END] = "_onEndKey";
33147 this._keyHandlerMap = map;
33148 }
33149
33150 if(this._keyHandlerMap[key]){
33151 // clear record of recent printables (being saved for multi-char letter navigation),
33152 // because "a", down-arrow, "b" shouldn't search for "ab"
33153 if(this._curSearch){
33154 this._curSearch.timer.remove();
33155 delete this._curSearch;
33156 }
33157
33158 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
33159 event.stop(e);
33160 }
33161 },
33162
33163 _onEnterKey: function(/*Object*/ message){
33164 this._publish("execute", { item: message.item, node: message.node } );
33165 this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
33166 this.onClick(message.item, message.node, message.evt);
33167 },
33168
33169 _onDownArrow: function(/*Object*/ message){
33170 // summary:
33171 // down arrow pressed; get next visible node, set focus there
33172 var node = this._getNextNode(message.node);
33173 if(node && node.isTreeNode){
33174 this.focusNode(node);
33175 }
33176 },
33177
33178 _onUpArrow: function(/*Object*/ message){
33179 // summary:
33180 // Up arrow pressed; move to previous visible node
33181
33182 var node = message.node;
33183
33184 // if younger siblings
33185 var previousSibling = node.getPreviousSibling();
33186 if(previousSibling){
33187 node = previousSibling;
33188 // if the previous node is expanded, dive in deep
33189 while(node.isExpandable && node.isExpanded && node.hasChildren()){
33190 // move to the last child
33191 var children = node.getChildren();
33192 node = children[children.length-1];
33193 }
33194 }else{
33195 // if this is the first child, return the parent
33196 // unless the parent is the root of a tree with a hidden root
33197 var parent = node.getParent();
33198 if(!(!this.showRoot && parent === this.rootNode)){
33199 node = parent;
33200 }
33201 }
33202
33203 if(node && node.isTreeNode){
33204 this.focusNode(node);
33205 }
33206 },
33207
33208 _onRightArrow: function(/*Object*/ message){
33209 // summary:
33210 // Right arrow pressed; go to child node
33211 var node = message.node;
33212
33213 // if not expanded, expand, else move to 1st child
33214 if(node.isExpandable && !node.isExpanded){
33215 this._expandNode(node);
33216 }else if(node.hasChildren()){
33217 node = node.getChildren()[0];
33218 if(node && node.isTreeNode){
33219 this.focusNode(node);
33220 }
33221 }
33222 },
33223
33224 _onLeftArrow: function(/*Object*/ message){
33225 // summary:
33226 // Left arrow pressed.
33227 // If not collapsed, collapse, else move to parent.
33228
33229 var node = message.node;
33230
33231 if(node.isExpandable && node.isExpanded){
33232 this._collapseNode(node);
33233 }else{
33234 var parent = node.getParent();
33235 if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
33236 this.focusNode(parent);
33237 }
33238 }
33239 },
33240
33241 _onHomeKey: function(){
33242 // summary:
33243 // Home key pressed; get first visible node, and set focus there
33244 var node = this._getRootOrFirstNode();
33245 if(node){
33246 this.focusNode(node);
33247 }
33248 },
33249
33250 _onEndKey: function(){
33251 // summary:
33252 // End key pressed; go to last visible node.
33253
33254 var node = this.rootNode;
33255 while(node.isExpanded){
33256 var c = node.getChildren();
33257 node = c[c.length - 1];
33258 }
33259
33260 if(node && node.isTreeNode){
33261 this.focusNode(node);
33262 }
33263 },
33264
33265 // multiCharSearchDuration: Number
33266 // If multiple characters are typed where each keystroke happens within
33267 // multiCharSearchDuration of the previous keystroke,
33268 // search for nodes matching all the keystrokes.
33269 //
33270 // For example, typing "ab" will search for entries starting with
33271 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
33272 multiCharSearchDuration: 250,
33273
33274 _onLetterKeyNav: function(message){
33275 // summary:
33276 // Called when user presses a prinatable key; search for node starting with recently typed letters.
33277 // message: Object
33278 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
33279
33280 // Branch depending on whether this key starts a new search, or modifies an existing search
33281 var cs = this._curSearch;
33282 if(cs){
33283 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
33284 // 'b', so we want to search for nodes starting w/"ab".
33285 cs.pattern = cs.pattern + message.key;
33286 cs.timer.remove();
33287 }else{
33288 // We are starting a new search
33289 cs = this._curSearch = {
33290 pattern: message.key,
33291 startNode: message.node
33292 };
33293 }
33294
33295 // set/reset timer to forget recent keystrokes
33296 cs.timer = this.defer(function(){
33297 delete this._curSearch;
33298 }, this.multiCharSearchDuration);
33299
33300 // Navigate to TreeNode matching keystrokes [entered so far].
33301 var node = cs.startNode;
33302 do{
33303 node = this._getNextNode(node);
33304 //check for last node, jump to first node if necessary
33305 if(!node){
33306 node = this._getRootOrFirstNode();
33307 }
33308 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
33309 if(node && node.isTreeNode){
33310 // no need to set focus if back where we started
33311 if(node !== cs.startNode){
33312 this.focusNode(node);
33313 }
33314 }
33315 },
33316
33317 isExpandoNode: function(node, widget){
33318 // summary:
33319 // check whether a dom node is the expandoNode for a particular TreeNode widget
33320 return dom.isDescendant(node, widget.expandoNode) || dom.isDescendant(node, widget.expandoNodeText);
33321 },
33322
33323 _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
33324 // summary:
33325 // Translates click events into commands for the controller to process
33326
33327 var domElement = e.target,
33328 isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
33329
33330 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
33331 // expando node was clicked, or label of a folder node was clicked; open it
33332 if(nodeWidget.isExpandable){
33333 this._onExpandoClick({node:nodeWidget});
33334 }
33335 }else{
33336 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
33337 this.onClick(nodeWidget.item, nodeWidget, e);
33338 this.focusNode(nodeWidget);
33339 }
33340 event.stop(e);
33341 },
33342 _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
33343 // summary:
33344 // Translates double-click events into commands for the controller to process
33345
33346 var domElement = e.target,
33347 isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
33348
33349 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
33350 // expando node was clicked, or label of a folder node was clicked; open it
33351 if(nodeWidget.isExpandable){
33352 this._onExpandoClick({node:nodeWidget});
33353 }
33354 }else{
33355 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
33356 this.onDblClick(nodeWidget.item, nodeWidget, e);
33357 this.focusNode(nodeWidget);
33358 }
33359 event.stop(e);
33360 },
33361
33362 _onExpandoClick: function(/*Object*/ message){
33363 // summary:
33364 // User clicked the +/- icon; expand or collapse my children.
33365 var node = message.node;
33366
33367 // If we are collapsing, we might be hiding the currently focused node.
33368 // Also, clicking the expando node might have erased focus from the current node.
33369 // For simplicity's sake just focus on the node with the expando.
33370 this.focusNode(node);
33371
33372 if(node.isExpanded){
33373 this._collapseNode(node);
33374 }else{
33375 this._expandNode(node);
33376 }
33377 },
33378
33379 onClick: function(/*===== item, node, evt =====*/){
33380 // summary:
33381 // Callback when a tree node is clicked
33382 // item: Object
33383 // Object from the dojo/store corresponding to this TreeNode
33384 // node: TreeNode
33385 // The TreeNode itself
33386 // evt: Event
33387 // The event
33388 // tags:
33389 // callback
33390 },
33391 onDblClick: function(/*===== item, node, evt =====*/){
33392 // summary:
33393 // Callback when a tree node is double-clicked
33394 // item: Object
33395 // Object from the dojo/store corresponding to this TreeNode
33396 // node: TreeNode
33397 // The TreeNode itself
33398 // evt: Event
33399 // The event
33400 // tags:
33401 // callback
33402 },
33403 onOpen: function(/*===== item, node =====*/){
33404 // summary:
33405 // Callback when a node is opened
33406 // item: dojo/data/Item
33407 // node: TreeNode
33408 // tags:
33409 // callback
33410 },
33411 onClose: function(/*===== item, node =====*/){
33412 // summary:
33413 // Callback when a node is closed
33414 // item: Object
33415 // Object from the dojo/store corresponding to this TreeNode
33416 // node: TreeNode
33417 // The TreeNode itself
33418 // tags:
33419 // callback
33420 },
33421
33422 _getNextNode: function(node){
33423 // summary:
33424 // Get next visible node
33425
33426 if(node.isExpandable && node.isExpanded && node.hasChildren()){
33427 // if this is an expanded node, get the first child
33428 return node.getChildren()[0]; // TreeNode
33429 }else{
33430 // find a parent node with a sibling
33431 while(node && node.isTreeNode){
33432 var returnNode = node.getNextSibling();
33433 if(returnNode){
33434 return returnNode; // TreeNode
33435 }
33436 node = node.getParent();
33437 }
33438 return null;
33439 }
33440 },
33441
33442 _getRootOrFirstNode: function(){
33443 // summary:
33444 // Get first visible node
33445 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
33446 },
33447
33448 _collapseNode: function(/*TreeNode*/ node){
33449 // summary:
33450 // Called when the user has requested to collapse the node
33451 // returns:
33452 // Deferred that fires when the node is closed
33453
33454 if(node._expandNodeDeferred){
33455 delete node._expandNodeDeferred;
33456 }
33457
33458 if(node.state == "LOADING"){
33459 // ignore clicks while we are in the process of loading data
33460 return;
33461 }
33462
33463 if(node.isExpanded){
33464 var ret = node.collapse();
33465
33466 this.onClose(node.item, node);
33467 this._state(node, false);
33468
33469 this._startPaint(ret); // after this finishes, need to reset widths of TreeNodes
33470
33471 return ret;
33472 }
33473 },
33474
33475 _expandNode: function(/*TreeNode*/ node){
33476 // summary:
33477 // Called when the user has requested to expand the node
33478 // returns:
33479 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
33480 // that were previously opened too
33481
33482 // Signal that this call is complete
33483 var def = new Deferred();
33484
33485 if(node._expandNodeDeferred){
33486 // there's already an expand in progress, or completed, so just return
33487 return node._expandNodeDeferred; // dojo/_base/Deferred
33488 }
33489
33490 var model = this.model,
33491 item = node.item,
33492 _this = this;
33493
33494 // Load data if it's not already loaded
33495 if(!node._loadDeferred){
33496 // need to load all the children before expanding
33497 node.markProcessing();
33498
33499 // Setup deferred to signal when the load and expand are finished.
33500 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
33501 node._loadDeferred = new Deferred();
33502
33503 // Get the children
33504 model.getChildren(
33505 item,
33506 function(items){
33507 node.unmarkProcessing();
33508
33509 // Display the children and also start expanding any children that were previously expanded
33510 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
33511 node.setChildItems(items).then(function(){
33512 node._loadDeferred.resolve(items);
33513 });
33514 },
33515 function(err){
33516 console.error(_this, ": error loading " + node.label + " children: ", err);
33517 node._loadDeferred.reject(err);
33518 }
33519 );
33520 }
33521
33522 // Expand the node after data has loaded
33523 node._loadDeferred.then(lang.hitch(this, function(){
33524 node.expand().then(function(){
33525 def.resolve(true); // signal that this _expandNode() call is complete
33526 });
33527
33528 // seems like these should be inside of then(), but left here for back-compat about
33529 // when this.isOpen flag gets set (ie, at the beginning of the animation)
33530 this.onOpen(node.item, node);
33531 this._state(node, true);
33532 }));
33533
33534 this._startPaint(def); // after this finishes, need to reset widths of TreeNodes
33535
33536 return def; // dojo/_base/Deferred
33537 },
33538
33539 ////////////////// Miscellaneous functions ////////////////
33540
33541 focusNode: function(/* _tree.Node */ node){
33542 // summary:
33543 // Focus on the specified node (which must be visible)
33544 // tags:
33545 // protected
33546
33547 // set focus so that the label will be voiced using screen readers
33548 focus.focus(node.labelNode);
33549 },
33550
33551 _onNodeFocus: function(/*dijit/_WidgetBase*/ node){
33552 // summary:
33553 // Called when a TreeNode gets focus, either by user clicking
33554 // it, or programatically by arrow key handling code.
33555 // description:
33556 // It marks that the current node is the selected one, and the previously
33557 // selected node no longer is.
33558
33559 if(node && node != this.lastFocused){
33560 if(this.lastFocused && !this.lastFocused._destroyed){
33561 // mark that the previously focsable node is no longer focusable
33562 this.lastFocused.setFocusable(false);
33563 }
33564
33565 // mark that the new node is the currently selected one
33566 node.setFocusable(true);
33567 this.lastFocused = node;
33568 }
33569 },
33570
33571 _onNodeMouseEnter: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
33572 // summary:
33573 // Called when mouse is over a node (onmouseenter event),
33574 // this is monitored by the DND code
33575 },
33576
33577 _onNodeMouseLeave: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
33578 // summary:
33579 // Called when mouse leaves a node (onmouseleave event),
33580 // this is monitored by the DND code
33581 },
33582
33583 //////////////// Events from the model //////////////////////////
33584
33585 _onItemChange: function(/*Item*/ item){
33586 // summary:
33587 // Processes notification of a change to an item's scalar values like label
33588 var model = this.model,
33589 identity = model.getIdentity(item),
33590 nodes = this._itemNodesMap[identity];
33591
33592 if(nodes){
33593 var label = this.getLabel(item),
33594 tooltip = this.getTooltip(item);
33595 array.forEach(nodes, function(node){
33596 node.set({
33597 item: item, // theoretically could be new JS Object representing same item
33598 label: label,
33599 tooltip: tooltip
33600 });
33601 node._updateItemClasses(item);
33602 });
33603 }
33604 },
33605
33606 _onItemChildrenChange: function(/*dojo/data/Item*/ parent, /*dojo/data/Item[]*/ newChildrenList){
33607 // summary:
33608 // Processes notification of a change to an item's children
33609 var model = this.model,
33610 identity = model.getIdentity(parent),
33611 parentNodes = this._itemNodesMap[identity];
33612
33613 if(parentNodes){
33614 array.forEach(parentNodes,function(parentNode){
33615 parentNode.setChildItems(newChildrenList);
33616 });
33617 }
33618 },
33619
33620 _onItemDelete: function(/*Item*/ item){
33621 // summary:
33622 // Processes notification of a deletion of an item.
33623 // Not called from new dojo.store interface but there's cleanup code in setChildItems() instead.
33624
33625 var model = this.model,
33626 identity = model.getIdentity(item),
33627 nodes = this._itemNodesMap[identity];
33628
33629 if(nodes){
33630 array.forEach(nodes,function(node){
33631 // Remove node from set of selected nodes (if it's selected)
33632 this.dndController.removeTreeNode(node);
33633
33634 var parent = node.getParent();
33635 if(parent){
33636 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
33637 parent.removeChild(node);
33638 }
33639 node.destroyRecursive();
33640 }, this);
33641 delete this._itemNodesMap[identity];
33642 }
33643 },
33644
33645 /////////////// Miscellaneous funcs
33646
33647 _initState: function(){
33648 // summary:
33649 // Load in which nodes should be opened automatically
33650 this._openedNodes = {};
33651 if(this.persist && this.cookieName){
33652 var oreo = cookie(this.cookieName);
33653 if(oreo){
33654 array.forEach(oreo.split(','), function(item){
33655 this._openedNodes[item] = true;
33656 }, this);
33657 }
33658 }
33659 },
33660 _state: function(node, expanded){
33661 // summary:
33662 // Query or set expanded state for an node
33663 if(!this.persist){
33664 return false;
33665 }
33666 var path = array.map(node.getTreePath(), function(item){
33667 return this.model.getIdentity(item);
33668 }, this).join("/");
33669 if(arguments.length === 1){
33670 return this._openedNodes[path];
33671 }else{
33672 if(expanded){
33673 this._openedNodes[path] = true;
33674 }else{
33675 delete this._openedNodes[path];
33676 }
33677 if(this.persist && this.cookieName){
33678 var ary = [];
33679 for(var id in this._openedNodes){
33680 ary.push(id);
33681 }
33682 cookie(this.cookieName, ary.join(","), {expires:365});
33683 }
33684 }
33685 },
33686
33687 destroy: function(){
33688 if(this._curSearch){
33689 this._curSearch.timer.remove();
33690 delete this._curSearch;
33691 }
33692 if(this.rootNode){
33693 this.rootNode.destroyRecursive();
33694 }
33695 if(this.dndController && !lang.isString(this.dndController)){
33696 this.dndController.destroy();
33697 }
33698 this.rootNode = null;
33699 this.inherited(arguments);
33700 },
33701
33702 destroyRecursive: function(){
33703 // A tree is treated as a leaf, not as a node with children (like a grid),
33704 // but defining destroyRecursive for back-compat.
33705 this.destroy();
33706 },
33707
33708 resize: function(changeSize){
33709 if(changeSize){
33710 domGeometry.setMarginBox(this.domNode, changeSize);
33711 }
33712
33713 // The main JS sizing involved w/tree is the indentation, which is specified
33714 // in CSS and read in through this dummy indentDetector node (tree must be
33715 // visible and attached to the DOM to read this).
33716 // If the Tree is hidden domGeometry.position(this.tree.indentDetector).w will return 0, in which case just
33717 // keep the default value.
33718 this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w || this._nodePixelIndent;
33719
33720 // resize() may be called before this.rootNode is created, so wait until it's available
33721 this.expandChildrenDeferred.then(lang.hitch(this, function(){
33722 // If tree has already loaded, then reset indent for all the nodes
33723 this.rootNode.set('indent', this.showRoot ? 0 : -1);
33724
33725 // Also, adjust widths of all rows to match width of Tree
33726 this._adjustWidths();
33727 }));
33728 },
33729
33730 _outstandingPaintOperations: 0,
33731 _startPaint: function(/*Promise|Boolean*/ p){
33732 // summary:
33733 // Called at the start of an operation that will change what's displayed.
33734 // p:
33735 // Promise that tells when the operation will complete. Alternately, if it's just a Boolean, it signifies
33736 // that the operation was synchronous, and already completed.
33737
33738 this._outstandingPaintOperations++;
33739 if(this._adjustWidthsTimer){
33740 this._adjustWidthsTimer.remove();
33741 delete this._adjustWidthsTimer;
33742 }
33743
33744 var oc = lang.hitch(this, function(){
33745 this._outstandingPaintOperations--;
33746
33747 if(this._outstandingPaintOperations <= 0 && !this._adjustWidthsTimer && this._started){
33748 // Use defer() to avoid a width adjustment when another operation will immediately follow,
33749 // such as a sequence of opening a node, then it's children, then it's grandchildren, etc.
33750 this._adjustWidthsTimer = this.defer("_adjustWidths");
33751 }
33752 });
33753 when(p, oc, oc);
33754 },
33755
33756 _adjustWidths: function(){
33757 // summary:
33758 // Get width of widest TreeNode, or the width of the Tree itself, whichever is greater,
33759 // and then set all TreeNodes to that width, so that selection/hover highlighting
33760 // extends to the edge of the Tree (#13141)
33761
33762 if(this._adjustWidthsTimer){
33763 this._adjustWidthsTimer.remove();
33764 delete this._adjustWidthsTimer;
33765 }
33766
33767 var maxWidth = 0,
33768 nodes = [];
33769 function collect(/*TreeNode*/ parent){
33770 var node = parent.rowNode;
33771 node.style.width = "auto"; // erase setting from previous run
33772 maxWidth = Math.max(maxWidth, node.clientWidth);
33773 nodes.push(node);
33774 if(parent.isExpanded){
33775 array.forEach(parent.getChildren(), collect);
33776 }
33777 }
33778 collect(this.rootNode);
33779 maxWidth = Math.max(maxWidth, domGeometry.getContentBox(this.domNode).w); // do after node.style.width="auto"
33780 array.forEach(nodes, function(node){
33781 node.style.width = maxWidth + "px"; // assumes no horizontal padding, border, or margin on rowNode
33782 });
33783 },
33784
33785 _createTreeNode: function(/*Object*/ args){
33786 // summary:
33787 // creates a TreeNode
33788 // description:
33789 // Developers can override this method to define their own TreeNode class;
33790 // However it will probably be removed in a future release in favor of a way
33791 // of just specifying a widget for the label, rather than one that contains
33792 // the children too.
33793 return new TreeNode(args);
33794 },
33795
33796 _setTextDirAttr: function(textDir){
33797 if(textDir && this.textDir!= textDir){
33798 this._set("textDir",textDir);
33799 this.rootNode.set("textDir", textDir);
33800 }
33801 }
33802 });
33803
33804 Tree.PathError = createError("TreePathError");
33805 Tree._TreeNode = TreeNode; // for monkey patching or creating subclasses of TreeNode
33806
33807 return Tree;
33808 });
33809
33810 },
33811 'dijit/form/_FormValueWidget':function(){
33812 define("dijit/form/_FormValueWidget", [
33813 "dojo/_base/declare", // declare
33814 "dojo/sniff", // has("ie")
33815 "./_FormWidget",
33816 "./_FormValueMixin"
33817 ], function(declare, has, _FormWidget, _FormValueMixin){
33818
33819 // module:
33820 // dijit/form/_FormValueWidget
33821
33822 return declare("dijit.form._FormValueWidget", [_FormWidget, _FormValueMixin],
33823 {
33824 // summary:
33825 // Base class for widgets corresponding to native HTML elements such as `<input>` or `<select>`
33826 // that have user changeable values.
33827 // description:
33828 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) `<input>` element,
33829 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
33830 // works as expected.
33831
33832 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
33833 // directly in the template as read by the parser in order to function. IE is known to specifically
33834 // require the 'name' attribute at element creation time. See #8484, #8660.
33835
33836 _layoutHackIE7: function(){
33837 // summary:
33838 // Work around table sizing bugs on IE7 by forcing redraw
33839
33840 if(has("ie") == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
33841 var domNode = this.domNode;
33842 var parent = domNode.parentNode;
33843 var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
33844 var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
33845 var _this = this;
33846 while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
33847 (function ping(){
33848 var disconnectHandle = _this.connect(parent, "onscroll",
33849 function(){
33850 _this.disconnect(disconnectHandle); // only call once
33851 pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
33852 _this.defer(function(){ pingNode.style.filter = origFilter; }); // restore custom filter, if any
33853 }
33854 );
33855 })();
33856 parent = parent.parentNode;
33857 }
33858 }
33859 }
33860 });
33861
33862 });
33863
33864 },
33865 '*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"]']);}
33866 }});
33867 define("dojo/tt-rss-layer", [], 1);