2 'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">✓</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\"> </td>\n</tr>\n",
3 'dijit/form/TextBox':function(){
5 'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
6 define("dijit/form/TextBox", [
7 "dojo/_base/declare", // declare
8 "dojo/dom-construct", // domConstruct.create
9 "dojo/dom-style", // domStyle.getComputedStyle
10 "dojo/_base/kernel", // kernel.deprecated
11 "dojo/_base/lang", // lang.hitch
12 "dojo/sniff", // has("ie") has("mozilla")
15 "dojo/text!./templates/TextBox.html",
16 "../main" // to export dijit._setSelectionRange, remove in 2.0
17 ], function(declare
, domConstruct
, domStyle
, kernel
, lang
, has
,
18 _FormValueWidget
, _TextBoxMixin
, template
, dijit
){
23 var TextBox
= declare("dijit.form.TextBox", [_FormValueWidget
, _TextBoxMixin
], {
25 // A base class for textbox form inputs
27 templateString
: template
,
28 _singleNodeTemplate
: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
30 _buttonInputDisabled
: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
32 baseClass
: "dijitTextBox",
34 postMixInProperties: function(){
35 var type
= this.type
.toLowerCase();
36 if(this.templateString
&& this.templateString
.toLowerCase() == "input" || ((type
== "hidden" || type
== "file") && this.templateString
== this.constructor.prototype.templateString
)){
37 this.templateString
= this._singleNodeTemplate
;
39 this.inherited(arguments
);
42 postCreate: function(){
43 this.inherited(arguments
);
46 // IE INPUT tag fontFamily has to be set directly using STYLE
47 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
48 this.defer(function(){
50 var s
= domStyle
.getComputedStyle(this.domNode
); // can throw an exception if widget is immediately destroyed
52 var ff
= s
.fontFamily
;
54 var inputs
= this.domNode
.getElementsByTagName("INPUT");
56 for(var i
=0; i
< inputs
.length
; i
++){
57 inputs
[i
].style
.fontFamily
= ff
;
62 }catch(e
){/*when used in a Dialog, and this is called before the dialog is
63 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
68 _onInput: function(e
){
69 this.inherited(arguments
);
70 if(this.intermediateChanges
){ // _TextBoxMixin uses onInput
71 // allow the key to post to the widget input box
72 this.defer(function(){ this._handleOnChange(this.get('value'), false); });
76 _setPlaceHolderAttr: function(v
){
77 this._set("placeHolder", v
);
79 this._attachPoints
.push('_phspan');
80 // dijitInputField class gives placeHolder same padding as the input field
81 // parent node already has dijitInputField class but it doesn't affect this <span>
82 // since it's position: absolute.
83 this._phspan
= domConstruct
.create('span',{ onmousedown:function(e
){ e
.preventDefault(); }, className
:'dijitPlaceHolder dijitInputField'},this.textbox
,'after');
85 this._phspan
.innerHTML
="";
86 this._phspan
.appendChild(this._phspan
.ownerDocument
.createTextNode(v
));
87 this._updatePlaceHolder();
90 _updatePlaceHolder: function(){
92 this._phspan
.style
.display
=(this.placeHolder
&&!this.focused
&&!this.textbox
.value
)?"":"none";
96 _setValueAttr: function(value
, /*Boolean?*/ priorityChange
, /*String?*/ formattedValue
){
97 this.inherited(arguments
);
98 this._updatePlaceHolder();
101 getDisplayedValue: function(){
103 // Deprecated. Use get('displayedValue') instead.
106 kernel
.deprecated(this.declaredClass
+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
107 return this.get('displayedValue');
110 setDisplayedValue: function(/*String*/ value
){
112 // Deprecated. Use set('displayedValue', ...) instead.
115 kernel
.deprecated(this.declaredClass
+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
116 this.set('displayedValue', value
);
119 _onBlur: function(e
){
120 if(this.disabled
){ return; }
121 this.inherited(arguments
);
122 this._updatePlaceHolder();
125 if(this.selectOnClick
){
126 // clear selection so that the next mouse click doesn't reselect
127 this.textbox
.selectionStart
= this.textbox
.selectionEnd
= undefined;
132 _onFocus: function(/*String*/ by
){
133 if(this.disabled
|| this.readOnly
){ return; }
134 this.inherited(arguments
);
135 this._updatePlaceHolder();
140 TextBox
.prototype._isTextSelected = function(){
141 var range
= this.ownerDocument
.selection
.createRange();
142 var parent
= range
.parentElement();
143 return parent
== this.textbox
&& range
.text
.length
> 0;
146 // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
147 dijit
._setSelectionRange
= _TextBoxMixin
._setSelectionRange = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
148 if(element
.createTextRange
){
149 var r
= element
.createTextRange();
151 r
.moveStart("character", -99999); // move to 0
152 r
.moveStart("character", start
); // delta from 0 is the correct position
153 r
.moveEnd("character", stop
-start
);
163 'dijit/_base/scroll':function(){
164 define("dijit/_base/scroll", [
165 "dojo/window", // windowUtils.scrollIntoView
166 "../main" // export symbol to dijit
167 ], function(windowUtils
, dijit
){
169 // dijit/_base/scroll
174 // Back compatibility module, new code should use windowUtils directly instead of using this module.
178 dijit
.scrollIntoView = function(/*DomNode*/ node
, /*Object?*/ pos
){
180 // Scroll the passed node into view, if it is not already.
181 // Deprecated, use `windowUtils.scrollIntoView` instead.
183 windowUtils
.scrollIntoView(node
, pos
);
188 'dijit/_TemplatedMixin':function(){
189 define("dijit/_TemplatedMixin", [
190 "dojo/_base/lang", // lang.getObject
193 "dojo/string", // string.substitute string.trim
194 "dojo/cache", // dojo.cache
195 "dojo/_base/array", // array.forEach
196 "dojo/_base/declare", // declare
197 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
198 "dojo/sniff", // has("ie")
199 "dojo/_base/unload" // unload.addOnWindowUnload
200 ], function(lang
, touch
, _WidgetBase
, string
, cache
, array
, declare
, domConstruct
, has
, unload
) {
203 // dijit/_TemplatedMixin
205 var _TemplatedMixin
= declare("dijit._TemplatedMixin", null, {
207 // Mixin for widgets that are instantiated from a template
209 // templateString: [protected] String
210 // A string that represents the widget template.
211 // Use in conjunction with dojo.cache() to load from a file.
212 templateString
: null,
214 // templatePath: [protected deprecated] String
215 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
216 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
219 // skipNodeCache: [protected] Boolean
220 // If using a cached widget template nodes poses issues for a
221 // particular widget class, it can set this property to ensure
222 // that its template is always re-built from a string
223 _skipNodeCache
: false,
225 // _earlyTemplatedStartup: Boolean
226 // A fallback to preserve the 1.0 - 1.3 behavior of children in
227 // templates having their startup called before the parent widget
228 // fires postCreate. Defaults to 'false', causing child widgets to
229 // have their .startup() called immediately before a parent widget
230 // .startup(), but always after the parent .postCreate(). Set to
231 // 'true' to re-enable to previous, arguably broken, behavior.
232 _earlyTemplatedStartup
: false,
235 // _attachPoints: [private] String[]
236 // List of widget attribute names associated with data-dojo-attach-point=... in the
237 // template, ex: ["containerNode", "labelNode"]
240 // _attachEvents: [private] Handle[]
241 // List of connections associated with data-dojo-attach-event=... in the
246 constructor: function(/*===== params, srcNodeRef =====*/){
248 // Create the widget.
249 // params: Object|null
250 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
251 // and functions, typically callbacks like onClick.
252 // The hash can contain any of the widget's properties, excluding read-only properties.
253 // srcNodeRef: DOMNode|String?
254 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
256 this._attachPoints
= [];
257 this._attachEvents
= [];
260 _stringRepl: function(tmpl
){
262 // Does substitution of ${foo} type properties in template string
265 var className
= this.declaredClass
, _this
= this;
266 // Cache contains a string because we need to do property replacement
267 // do the property replacement
268 return string
.substitute(tmpl
, this, function(value
, key
){
269 if(key
.charAt(0) == '!'){ value
= lang
.getObject(key
.substr(1), false, _this
); }
270 if(typeof value
== "undefined"){ throw new Error(className
+" template:"+key
); } // a debugging aide
271 if(value
== null){ return ""; }
273 // Substitution keys beginning with ! will skip the transform step,
274 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
275 return key
.charAt(0) == "!" ? value
:
276 // Safer substitution, see heading "Attribute values" in
277 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
278 value
.toString().replace(/"/g,"""); //TODO
: add
&
? use encodeXML method
?
282 buildRendering: function(){
284 // Construct the UI for this widget from a template, setting this.domNode.
288 if(!this.templateString
){
289 this.templateString
= cache(this.templatePath
, {sanitize
: true});
292 // Lookup cached version of template, and download to cache if it
293 // isn't there already. Returns either a DomNode or a string, depending on
294 // whether or not the template contains ${foo} replacement parameters.
295 var cached
= _TemplatedMixin
.getCachedTemplate(this.templateString
, this._skipNodeCache
, this.ownerDocument
);
298 if(lang
.isString(cached
)){
299 node
= domConstruct
.toDom(this._stringRepl(cached
), this.ownerDocument
);
300 if(node
.nodeType
!= 1){
301 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
302 throw new Error("Invalid template: " + cached
);
305 // if it's a node, all we have to do is clone it
306 node
= cached
.cloneNode(true);
311 // Call down to _Widget.buildRendering() to get base classes assigned
312 // TODO: change the baseClass assignment to _setBaseClassAttr
313 this.inherited(arguments
);
315 // recurse through the node, looking for, and attaching to, our
316 // attachment points and events, which should be defined on the template node.
317 this._attachTemplateNodes(node
, function(n
,p
){ return n
.getAttribute(p
); });
319 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
321 this._fillContent(this.srcNodeRef
);
324 _beforeFillContent: function(){
327 _fillContent: function(/*DomNode*/ source
){
329 // Relocate source contents to templated container node.
330 // this.containerNode must be able to receive children, or exceptions will be thrown.
333 var dest
= this.containerNode
;
335 while(source
.hasChildNodes()){
336 dest
.appendChild(source
.firstChild
);
341 _attachTemplateNodes: function(rootNode
, getAttrFunc
){
343 // Iterate through the template and attach functions and nodes accordingly.
344 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
345 // etc. for those widgets.
347 // Map widget properties and functions to the handlers specified in
348 // the dom node and it's descendants. This function iterates over all
349 // nodes and looks for these properties:
351 // - dojoAttachPoint/data-dojo-attach-point
352 // - dojoAttachEvent/data-dojo-attach-event
353 // rootNode: DomNode|Widget[]
354 // the node to search for properties. All children will be searched.
355 // getAttrFunc: Function
356 // a function which will be used to obtain property for a given
361 var nodes
= lang
.isArray(rootNode
) ? rootNode
: (rootNode
.all
|| rootNode
.getElementsByTagName("*"));
362 var x
= lang
.isArray(rootNode
) ? 0 : -1;
363 for(; x
< 0 || nodes
[x
]; x
++){ // don't access nodes.length on IE, see #14346
364 var baseNode
= (x
== -1) ? rootNode
: nodes
[x
];
365 if(this.widgetsInTemplate
&& (getAttrFunc(baseNode
, "dojoType") || getAttrFunc(baseNode
, "data-dojo-type"))){
368 // Process data-dojo-attach-point
369 var attachPoint
= getAttrFunc(baseNode
, "dojoAttachPoint") || getAttrFunc(baseNode
, "data-dojo-attach-point");
371 var point
, points
= attachPoint
.split(/\s*,\s*/);
372 while((point
= points
.shift())){
373 if(lang
.isArray(this[point
])){
374 this[point
].push(baseNode
);
376 this[point
]=baseNode
;
378 this._attachPoints
.push(point
);
382 // Process data-dojo-attach-event
383 var attachEvent
= getAttrFunc(baseNode
, "dojoAttachEvent") || getAttrFunc(baseNode
, "data-dojo-attach-event");
385 // NOTE: we want to support attributes that have the form
386 // "domEvent: nativeEvent; ..."
387 var event
, events
= attachEvent
.split(/\s*,\s*/);
388 var trim
= lang
.trim
;
389 while((event
= events
.shift())){
392 if(event
.indexOf(":") != -1){
393 // oh, if only JS had tuple assignment
394 var funcNameArr
= event
.split(":");
395 event
= trim(funcNameArr
[0]);
396 thisFunc
= trim(funcNameArr
[1]);
403 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
404 this._attachEvents
.push(this.connect(baseNode
, touch
[event
] || event
, thisFunc
));
411 destroyRendering: function(){
412 // Delete all attach points to prevent IE6 memory leaks.
413 array
.forEach(this._attachPoints
, function(point
){
416 this._attachPoints
= [];
418 // And same for event handlers
419 array
.forEach(this._attachEvents
, this.disconnect
, this);
420 this._attachEvents
= [];
422 this.inherited(arguments
);
426 // key is templateString; object is either string or DOM tree
427 _TemplatedMixin
._templateCache
= {};
429 _TemplatedMixin
.getCachedTemplate = function(templateString
, alwaysUseString
, doc
){
431 // Static method to get a template based on the templatePath or
432 // templateString key
433 // templateString: String
435 // alwaysUseString: Boolean
436 // Don't cache the DOM tree for this template, even if it doesn't have any variables
438 // The target document. Defaults to document global if unspecified.
440 // Either string (if there are ${} variables that need to be replaced) or just
441 // a DOM tree (if the node can be cloned directly)
443 // is it already cached?
444 var tmplts
= _TemplatedMixin
._templateCache
;
445 var key
= templateString
;
446 var cached
= tmplts
[key
];
449 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
450 // current document, then use the current cached value
451 if(!cached
.ownerDocument
|| cached
.ownerDocument
== (doc
|| document
)){
452 // string or node of the same document
455 }catch(e
){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
456 domConstruct
.destroy(cached
);
459 templateString
= string
.trim(templateString
);
461 if(alwaysUseString
|| templateString
.match(/\$\{([^\}]+)\}/g)){
462 // there are variables in the template so all we can do is cache the string
463 return (tmplts
[key
] = templateString
); //String
465 // there are no variables in the template so we can cache the DOM tree
466 var node
= domConstruct
.toDom(templateString
, doc
);
467 if(node
.nodeType
!= 1){
468 throw new Error("Invalid template: " + templateString
);
470 return (tmplts
[key
] = node
); //Node
475 unload
.addOnWindowUnload(function(){
476 var cache
= _TemplatedMixin
._templateCache
;
477 for(var key
in cache
){
478 var value
= cache
[key
];
479 if(typeof value
== "object"){ // value is either a string or a DOM node template
480 domConstruct
.destroy(value
);
487 // These arguments can be specified for widgets which are used in templates.
488 // Since any widget can be specified as sub widgets in template, mix it
489 // into the base widget class. (This is a hack, but it's effective.).
490 // Remove for 2.0. Also, hide from API doc parser.
491 lang
.extend(_WidgetBase
, /*===== {} || =====*/ {
496 return _TemplatedMixin
;
500 'dijit/_CssStateMixin':function(){
501 define("dijit/_CssStateMixin", [
502 "dojo/_base/array", // array.forEach array.map
503 "dojo/_base/declare", // declare
504 "dojo/dom", // dom.isDescendant()
505 "dojo/dom-class", // domClass.toggle
507 "dojo/_base/lang", // lang.hitch
510 "dojo/_base/window", // win.body
512 ], function(array
, declare
, dom
, domClass
, has
, lang
, on
, ready
, win
, registry
){
515 // dijit/_CssStateMixin
517 var CssStateMixin
= declare("dijit._CssStateMixin", [], {
519 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
520 // state changes, and also higher-level state changes such becoming disabled or selected.
523 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
524 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
525 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
526 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
528 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
530 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
531 // within the widget).
533 // cssStateNodes: [protected] Object
534 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
536 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
537 // (like "dijitUpArrowButton"). Example:
539 // | "upArrowButton": "dijitUpArrowButton",
540 // | "downArrowButton": "dijitDownArrowButton"
542 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
546 // hovering: [readonly] Boolean
547 // True if cursor is over this widget
550 // active: [readonly] Boolean
551 // True if mouse was pressed while over this widget, and hasn't been released yet
554 _applyAttributes: function(){
555 // This code would typically be in postCreate(), but putting in _applyAttributes() for
556 // performance: so the class changes happen before DOM is inserted into the document.
557 // Change back to postCreate() in 2.0. See #11635.
559 this.inherited(arguments
);
561 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
562 array
.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr
){
563 this.watch(attr
, lang
.hitch(this, "_setStateClass"));
566 // Track hover and active mouse events on widget root node, plus possibly on subnodes
567 for(var ap
in this.cssStateNodes
){
568 this._trackMouseState(this[ap
], this.cssStateNodes
[ap
]);
570 this._trackMouseState(this.domNode
, this.baseClass
);
572 // Set state initially; there's probably no hover/active/focus state but widget might be
573 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
574 this._setStateClass();
577 _cssMouseEvent: function(/*Event*/ event
){
579 // Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
580 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
585 this._set("hovering", true);
586 this._set("active", this._mouseDown
);
589 this._set("hovering", false);
590 this._set("active", false);
594 this._set("active", true);
598 this._set("active", false);
604 _setStateClass: function(){
606 // Update the visual state of the widget by setting the css classes on this.domNode
607 // (or this.stateNode if defined) by combining this.baseClass with
608 // various suffixes that represent the current widget state(s).
611 // In the case where a widget has multiple
612 // states, it sets the class based on all possible
613 // combinations. For example, an invalid form widget that is being hovered
614 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
616 // The widget may have one or more of the following states, determined
617 // by this.state, this.checked, this.valid, and this.selected:
619 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
620 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
621 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
622 // - Selected - ex: currently selected tab will have this.selected==true
624 // In addition, it may have one or more of the following states,
625 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
627 // - Disabled - if the widget is disabled
628 // - Active - if the mouse (or space/enter key?) is being pressed down
629 // - Focused - if the widget has focus
630 // - Hover - if the mouse is over the widget
632 // Compute new set of classes
633 var newStateClasses
= this.baseClass
.split(" ");
635 function multiply(modifier
){
636 newStateClasses
= newStateClasses
.concat(array
.map(newStateClasses
, function(c
){ return c
+modifier
; }), "dijit"+modifier
);
639 if(!this.isLeftToRight()){
640 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
644 var checkedState
= this.checked
== "mixed" ? "Mixed" : (this.checked
? "Checked" : "");
646 multiply(checkedState
);
649 multiply(this.state
);
652 multiply("Selected");
659 multiply("Disabled");
660 }else if(this.readOnly
){
661 multiply("ReadOnly");
665 }else if(this.hovering
){
674 // Remove old state classes and add new ones.
675 // For performance concerns we only write into domNode.className once.
676 var tn
= this.stateNode
|| this.domNode
,
677 classHash
= {}; // set of all classes (state and otherwise) for node
679 array
.forEach(tn
.className
.split(" "), function(c
){ classHash
[c
] = true; });
681 if("_stateClasses" in this){
682 array
.forEach(this._stateClasses
, function(c
){ delete classHash
[c
]; });
685 array
.forEach(newStateClasses
, function(c
){ classHash
[c
] = true; });
688 for(var c
in classHash
){
691 tn
.className
= newClasses
.join(" ");
693 this._stateClasses
= newStateClasses
;
696 _subnodeCssMouseEvent: function(node
, clazz
, evt
){
698 // Handler for hover/active mouse event on widget's subnode
699 if(this.disabled
|| this.readOnly
){
702 function hover(isHovering
){
703 domClass
.toggle(node
, clazz
+"Hover", isHovering
);
705 function active(isActive
){
706 domClass
.toggle(node
, clazz
+"Active", isActive
);
708 function focused(isFocused
){
709 domClass
.toggle(node
, clazz
+"Focused", isFocused
);
738 _trackMouseState: function(/*DomNode*/ node
, /*String*/ clazz
){
740 // Track mouse/focus events on specified node and set CSS class on that node to indicate
741 // current state. Usually not called directly, but via cssStateNodes attribute.
743 // Given class=foo, will set the following CSS class on the node
745 // - fooActive: if the user is currently pressing down the mouse button while over the node
746 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
747 // - fooFocus: if the node is focused
749 // Note that it won't set any classes if the widget is disabled.
751 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
752 // is handled specially and automatically just by mixing in this class.
754 // CSS class name (ex: dijitSliderUpArrow)
756 // Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
757 // when node is hovered/active
758 node
._cssState
= clazz
;
763 // Document level listener to catch hover etc. events on widget root nodes and subnodes.
764 // Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
765 // have been hovered or unhovered (try test_Accordion.html)
766 function handler(evt
){
767 // Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
768 // to avoid processing mouseout events moving from a widget's domNode to a descendant node;
769 // such events shouldn't be interpreted as a mouseleave on the widget.
770 if(!dom
.isDescendant(evt
.relatedTarget
, evt
.target
)){
771 for(var node
= evt
.target
; node
&& node
!= evt
.relatedTarget
; node
= node
.parentNode
){
772 // Process any nodes with _cssState property. They are generally widget root nodes,
773 // but could also be sub-nodes within a widget
775 var widget
= registry
.getEnclosingWidget(node
);
777 if(node
== widget
.domNode
){
778 // event on the widget's root node
779 widget
._cssMouseEvent(evt
);
781 // event on widget's sub-node
782 widget
._subnodeCssMouseEvent(node
, node
._cssState
, evt
);
789 function ieHandler(evt
){
790 evt
.target
= evt
.srcElement
;
794 // Use addEventListener() (and attachEvent() on IE) to catch the relevant events even if other handlers
795 // (on individual nodes) call evt.stopPropagation() or event.stopEvent().
796 // Currently typematic.js is doing that, not sure why.
797 // Don't monitor mouseover/mouseout on mobile because iOS generates "phantom" mouseover/mouseout events when
798 // drag-scrolling, at the point in the viewport where the drag originated. Test the Tree in api viewer.
799 var body
= win
.body(),
800 types
= (has("touch") ? [] : ["mouseover", "mouseout"]).concat(["mousedown", "touchstart", "mouseup", "touchend"]);
801 array
.forEach(types
, function(type
){
802 if(body
.addEventListener
){
803 body
.addEventListener(type
, handler
, true); // W3C
805 body
.attachEvent("on"+type
, ieHandler
); // IE
809 // Track focus events on widget sub-nodes that have been registered via _trackMouseState().
810 // However, don't track focus events on the widget root nodes, because focus is tracked via the
811 // focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
812 // nodes or a subwidget's node or a popup node, etc.)
813 // Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
814 on(body
, "focusin, focusout", function(evt
){
815 var node
= evt
.target
;
816 if(node
._cssState
&& !node
.getAttribute("widgetId")){
817 var widget
= registry
.getEnclosingWidget(node
);
818 widget
._subnodeCssMouseEvent(node
, node
._cssState
, evt
);
823 return CssStateMixin
;
827 'dijit/layout/ScrollingTabController':function(){
829 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</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\">◀</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\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
830 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>"}});
831 define("dijit/layout/ScrollingTabController", [
832 "dojo/_base/array", // array.forEach
833 "dojo/_base/declare", // declare
834 "dojo/dom-class", // domClass.add domClass.contains
835 "dojo/dom-geometry", // domGeometry.contentBox
836 "dojo/dom-style", // domStyle.style
837 "dojo/_base/fx", // Animation
838 "dojo/_base/lang", // lang.hitch
840 "dojo/query", // query
841 "dojo/sniff", // has("ie"), has("webkit"), has("quirks")
842 "../registry", // registry.byId()
843 "dojo/text!./templates/ScrollingTabController.html",
844 "dojo/text!./templates/_ScrollingTabControllerButton.html",
846 "./utils", // marginBox2contextBox, layoutChildren
847 "../_WidgetsInTemplateMixin",
852 "dojo/NodeList-dom" // NodeList.style
853 ], function(array
, declare
, domClass
, domGeometry
, domStyle
, fx
, lang
, on
, query
, has
,
854 registry
, tabControllerTemplate
, buttonTemplate
, TabController
, layoutUtils
, _WidgetsInTemplateMixin
,
855 Menu
, MenuItem
, Button
, _HasDropDown
){
858 // dijit/layout/ScrollingTabController
861 var ScrollingTabController
= declare("dijit.layout.ScrollingTabController", [TabController
, _WidgetsInTemplateMixin
], {
863 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
864 // all fitting on a single row.
865 // Works only for horizontal tabs (either above or below the content, not to the left
870 baseClass
: "dijitTabController dijitScrollingTabController",
872 templateString
: tabControllerTemplate
,
874 // useMenu: [const] Boolean
875 // True if a menu should be used to select tabs when they are too
876 // wide to fit the TabContainer, false otherwise.
879 // useSlider: [const] Boolean
880 // True if a slider should be used to select tabs when they are too
881 // wide to fit the TabContainer, false otherwise.
884 // tabStripClass: [const] String
885 // The css class to apply to the tab strip, if it is visible.
888 widgetsInTemplate
: true,
890 // _minScroll: Number
891 // The distance in pixels from the edge of the tab strip which,
892 // if a scroll animation is less than, forces the scroll to
893 // go all the way to the left/right.
896 // Override default behavior mapping class to DOMNode
897 _setClassAttr
: { node
: "containerNode", type
: "class" },
899 buildRendering: function(){
900 this.inherited(arguments
);
901 var n
= this.domNode
;
903 this.scrollNode
= this.tablistWrapper
;
906 if(!this.tabStripClass
){
907 this.tabStripClass
= "dijitTabContainer" +
908 this.tabPosition
.charAt(0).toUpperCase() +
909 this.tabPosition
.substr(1).replace(/-.*/, "") +
911 domClass
.add(n
, "tabStrip-disabled")
914 domClass
.add(this.tablistWrapper
, this.tabStripClass
);
917 onStartup: function(){
918 this.inherited(arguments
);
920 // TabController is hidden until it finishes drawing, to give
921 // a less visually jumpy instantiation. When it's finished, set visibility to ""
922 // to that the tabs are hidden/shown depending on the container's visibility setting.
923 domStyle
.set(this.domNode
, "visibility", "");
924 this._postStartup
= true;
926 // changes to the tab button label or iconClass will have changed the width of the
927 // buttons, so do a resize
928 this.own(on(this.containerNode
, "attrmodified-label, attrmodified-iconclass", lang
.hitch(this, function(evt
){
930 this.resize(this._dim
);
935 onAddChild: function(page
, insertIndex
){
936 this.inherited(arguments
);
938 // Increment the width of the wrapper when a tab is added
939 // This makes sure that the buttons never wrap.
940 // The value 200 is chosen as it should be bigger than most
941 // Tab button widths.
942 domStyle
.set(this.containerNode
, "width",
943 (domStyle
.get(this.containerNode
, "width") + 200) + "px");
946 onRemoveChild: function(page
, insertIndex
){
947 // null out _selectedTab because we are about to delete that dom node
948 var button
= this.pane2button
[page
.id
];
949 if(this._selectedTab
=== button
.domNode
){
950 this._selectedTab
= null;
953 this.inherited(arguments
);
956 _initButtons: function(){
958 // Creates the buttons used to scroll to view tabs that
959 // may not be visible if the TabContainer is too narrow.
961 // Make a list of the buttons to display when the tab labels become
962 // wider than the TabContainer, and hide the other buttons.
963 // Also gets the total width of the displayed buttons.
965 this._buttons
= query("> .tabStripButton", this.domNode
).filter(function(btn
){
966 if((this.useMenu
&& btn
== this._menuBtn
.domNode
) ||
967 (this.useSlider
&& (btn
== this._rightBtn
.domNode
|| btn
== this._leftBtn
.domNode
))){
968 this._btnWidth
+= domGeometry
.getMarginSize(btn
).w
;
971 domStyle
.set(btn
, "display", "none");
977 _getTabsWidth: function(){
978 var children
= this.getChildren();
980 var leftTab
= children
[this.isLeftToRight() ? 0 : children
.length
- 1].domNode
,
981 rightTab
= children
[this.isLeftToRight() ? children
.length
- 1 : 0].domNode
;
982 return rightTab
.offsetLeft
+ rightTab
.offsetWidth
- leftTab
.offsetLeft
;
988 _enableBtn: function(width
){
990 // Determines if the tabs are wider than the width of the TabContainer, and
991 // thus that we need to display left/right/menu navigation buttons.
992 var tabsWidth
= this._getTabsWidth();
993 width
= width
|| domStyle
.get(this.scrollNode
, "width");
994 return tabsWidth
> 0 && width
< tabsWidth
;
997 resize: function(dim
){
999 // Hides or displays the buttons used to scroll the tab list and launch the menu
1000 // that selects tabs.
1002 // Save the dimensions to be used when a child is renamed.
1005 // Set my height to be my natural height (tall enough for one row of tab labels),
1006 // and my content-box width based on margin-box width specified in dim parameter.
1007 // But first reset scrollNode.height in case it was set by layoutChildren() call
1008 // in a previous run of this method.
1009 this.scrollNode
.style
.height
= "auto";
1010 var cb
= this._contentBox
= layoutUtils
.marginBox2contentBox(this.domNode
, {h
: 0, w
: dim
.w
});
1011 cb
.h
= this.scrollNode
.offsetHeight
;
1012 domGeometry
.setContentSize(this.domNode
, cb
);
1014 // Show/hide the left/right/menu navigation buttons depending on whether or not they
1016 var enable
= this._enableBtn(this._contentBox
.w
);
1017 this._buttons
.style("display", enable
? "" : "none");
1019 // Position and size the navigation buttons and the tablist
1020 this._leftBtn
.layoutAlign
= "left";
1021 this._rightBtn
.layoutAlign
= "right";
1022 this._menuBtn
.layoutAlign
= this.isLeftToRight() ? "right" : "left";
1023 layoutUtils
.layoutChildren(this.domNode
, this._contentBox
,
1024 [this._menuBtn
, this._leftBtn
, this._rightBtn
, {domNode
: this.scrollNode
, layoutAlign
: "client"}]);
1026 // set proper scroll so that selected tab is visible
1027 if(this._selectedTab
){
1028 if(this._anim
&& this._anim
.status() == "playing"){
1031 this.scrollNode
.scrollLeft
= this._convertToScrollLeft(this._getScrollForSelectedTab());
1034 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
1035 this._setButtonClass(this._getScroll());
1037 this._postResize
= true;
1039 // Return my size so layoutChildren() can use it.
1040 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
1041 return {h
: this._contentBox
.h
, w
: dim
.w
};
1044 _getScroll: function(){
1046 // Returns the current scroll of the tabs where 0 means
1047 // "scrolled all the way to the left" and some positive number, based on #
1048 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
1049 return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode
.scrollLeft
:
1050 domStyle
.get(this.containerNode
, "width") - domStyle
.get(this.scrollNode
, "width")
1051 + (has("ie") >= 8 ? -1 : 1) * this.scrollNode
.scrollLeft
;
1054 _convertToScrollLeft: function(val
){
1056 // Given a scroll value where 0 means "scrolled all the way to the left"
1057 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
1058 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
1059 // to achieve that scroll.
1061 // This method is to adjust for RTL funniness in various browsers and versions.
1062 if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
1065 var maxScroll
= domStyle
.get(this.containerNode
, "width") - domStyle
.get(this.scrollNode
, "width");
1066 return (has("ie") >= 8 ? -1 : 1) * (val
- maxScroll
);
1070 onSelectChild: function(/*dijit/_WidgetBase*/ page){
1072 // Smoothly scrolls to a tab when it is selected.
1074 var tab = this.pane2button[page.id];
1075 if(!tab || !page){return;}
1077 var node = tab.domNode;
1079 // Save the selection
1080 if(node != this._selectedTab){
1081 this._selectedTab = node;
1083 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
1084 if(this._postResize){
1085 var sl = this._getScroll();
1087 if(sl > node.offsetLeft ||
1088 sl + domStyle.get(this.scrollNode, "width") <
1089 node.offsetLeft + domStyle.get(node, "width")){
1090 this.createSmoothScroll().play();
1095 this.inherited(arguments);
1098 _getScrollBounds: function(){
1100 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
1101 // tabs (respectively)
1102 var children = this.getChildren(),
1103 scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
1104 containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
1105 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
1106 tabsWidth = this._getTabsWidth();
1108 if(children.length && tabsWidth > scrollNodeWidth){
1109 // Scrolling should happen
1111 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
1112 max: this.isLeftToRight() ?
1113 (children[children.length-1].domNode.offsetLeft + children[children.length-1].domNode.offsetWidth) - scrollNodeWidth :
1117 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
1118 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
1120 min: onlyScrollPosition,
1121 max: onlyScrollPosition
1126 _getScrollForSelectedTab: function(){
1128 // Returns the scroll value setting so that the selected tab
1129 // will appear in the center
1130 var w = this.scrollNode,
1131 n = this._selectedTab,
1132 scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
1133 scrollBounds = this._getScrollBounds();
1135 // TODO: scroll minimal amount (to either right or left) so that
1136 // selected tab is fully visible, and just return if it's already visible?
1137 var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
1138 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
1141 // If scrolling close to the left side or right side, scroll
1142 // all the way to the left or right. See this._minScroll.
1143 // (But need to make sure that doesn't scroll the tab out of view...)
1147 createSmoothScroll: function(x){
1149 // Creates a dojo._Animation object that smoothly scrolls the tab list
1150 // either to a fixed horizontal pixel value, or to the selected tab.
1152 // If an number argument is passed to the function, that horizontal
1153 // pixel position is scrolled to. Otherwise the currently selected
1154 // tab is scrolled to.
1156 // An optional pixel value to scroll to, indicating distance from left.
1158 // Calculate position to scroll to
1159 if(arguments.length > 0){
1160 // position specified by caller, just make sure it's within bounds
1161 var scrollBounds = this._getScrollBounds();
1162 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
1164 // scroll to center the current tab
1165 x = this._getScrollForSelectedTab();
1168 if(this._anim && this._anim.status() == "playing"){
1173 w = this.scrollNode,
1174 anim = new fx.Animation({
1175 beforeBegin: function(){
1176 if(this.curve){ delete this.curve; }
1177 var oldS = w.scrollLeft,
1178 newS = self._convertToScrollLeft(x);
1179 anim.curve = new fx._Line(oldS, newS);
1181 onAnimate: function(val){
1187 // Disable/enable left/right buttons according to new scroll position
1188 this._setButtonClass(x);
1190 return anim; // dojo/_base/fx/Animation
1193 _getBtnNode: function(/*Event*/ e
){
1195 // Gets a button DOM node from a mouse click event.
1197 // The mouse click event.
1199 while(n
&& !domClass
.contains(n
, "tabStripButton")){
1205 doSlideRight: function(/*Event*/ e
){
1207 // Scrolls the menu to the right.
1209 // The mouse click event.
1210 this.doSlide(1, this._getBtnNode(e
));
1213 doSlideLeft: function(/*Event*/ e
){
1215 // Scrolls the menu to the left.
1217 // The mouse click event.
1218 this.doSlide(-1,this._getBtnNode(e
));
1221 doSlide: function(/*Number*/ direction
, /*DomNode*/ node
){
1223 // Scrolls the tab list to the left or right by 75% of the widget width.
1225 // If the direction is 1, the widget scrolls to the right, if it is -1,
1226 // it scrolls to the left.
1228 if(node
&& domClass
.contains(node
, "dijitTabDisabled")){return;}
1230 var sWidth
= domStyle
.get(this.scrollNode
, "width");
1231 var d
= (sWidth
* 0.75) * direction
;
1233 var to
= this._getScroll() + d
;
1235 this._setButtonClass(to
);
1237 this.createSmoothScroll(to
).play();
1240 _setButtonClass: function(/*Number*/ scroll
){
1242 // Disables the left scroll button if the tabs are scrolled all the way to the left,
1243 // or the right scroll button in the opposite case.
1245 // amount of horizontal scroll
1247 var scrollBounds
= this._getScrollBounds();
1248 this._leftBtn
.set("disabled", scroll
<= scrollBounds
.min
);
1249 this._rightBtn
.set("disabled", scroll
>= scrollBounds
.max
);
1254 var ScrollingTabControllerButtonMixin
= declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
1255 baseClass
: "dijitTab tabStripButton",
1257 templateString
: buttonTemplate
,
1259 // Override inherited tabIndex: 0 from dijit/form/Button, because user shouldn't be
1260 // able to tab to the left/right/menu buttons
1263 // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
1264 // either (this override avoids focus() call in FormWidget.js)
1265 isFocusable: function(){ return false; }
1268 // Class used in template
1269 declare("dijit.layout._ScrollingTabControllerButton",
1270 [Button
, ScrollingTabControllerButtonMixin
]);
1272 // Class used in template
1274 "dijit.layout._ScrollingTabControllerMenuButton",
1275 [Button
, _HasDropDown
, ScrollingTabControllerButtonMixin
],
1277 // id of the TabContainer itself
1280 // -1 so user can't tab into the button, but so that button can still be focused programatically.
1281 // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
1284 isLoaded: function(){
1285 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
1289 loadDropDown: function(callback
){
1290 this.dropDown
= new Menu({
1291 id
: this.containerId
+ "_menu",
1292 ownerDocument
: this.ownerDocument
,
1295 textDir
: this.textDir
1297 var container
= registry
.byId(this.containerId
);
1298 array
.forEach(container
.getChildren(), function(page
){
1299 var menuItem
= new MenuItem({
1300 id
: page
.id
+ "_stcMi",
1302 iconClass
: page
.iconClass
,
1303 disabled
: page
.disabled
,
1304 ownerDocument
: this.ownerDocument
,
1307 textDir
: page
.textDir
,
1308 onClick: function(){
1309 container
.selectChild(page
);
1312 this.dropDown
.addChild(menuItem
);
1317 closeDropDown: function(/*Boolean*/ focus
){
1318 this.inherited(arguments
);
1320 this.dropDown
.destroyRecursive();
1321 delete this.dropDown
;
1326 return ScrollingTabController
;
1330 'url:dijit/form/templates/ComboButton.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" data-dojo-attach-point=\"buttonNode\" data-dojo-attach-event=\"ondijitclick:_onClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" data-dojo-attach-point=\"containerNode\" role=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdata-dojo-attach-point=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdata-dojo-attach-event=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">▼</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" data-dojo-attach-point=\"valueNode\" role=\"presentation\"\n\t\t/></td></tr></tbody\n></table>\n",
1331 'dijit/DialogUnderlay':function(){
1332 define("dijit/DialogUnderlay", [
1333 "dojo/_base/declare", // declare
1334 "dojo/dom-attr", // domAttr.set
1335 "dojo/window", // winUtils.getBox
1337 "./_TemplatedMixin",
1338 "./BackgroundIframe"
1339 ], function(declare
, domAttr
, winUtils
, _Widget
, _TemplatedMixin
, BackgroundIframe
){
1342 // dijit/DialogUnderlay
1344 return declare("dijit.DialogUnderlay", [_Widget
, _TemplatedMixin
], {
1346 // The component that blocks the screen behind a `dijit.Dialog`
1349 // A component used to block input behind a `dijit.Dialog`. Only a single
1350 // instance of this widget is created by `dijit.Dialog`, and saved as
1351 // a reference to be shared between all Dialogs as `dijit._underlay`
1353 // The underlay itself can be styled based on and id:
1354 // | #myDialog_underlay { background-color:red; }
1356 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
1357 // suffixed with _underlay.
1359 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
1360 // Inner div has opacity specified in CSS file.
1361 templateString
: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
1363 // Parameters on creation or updatable later
1366 // Id of the dialog.... DialogUnderlay's id is based on this id
1370 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
1373 _setDialogIdAttr: function(id
){
1374 domAttr
.set(this.node
, "id", id
+ "_underlay");
1375 this._set("dialogId", id
);
1378 _setClassAttr: function(clazz
){
1379 this.node
.className
= "dijitDialogUnderlay " + clazz
;
1380 this._set("class", clazz
);
1383 postCreate: function(){
1385 // Append the underlay to the body
1386 this.ownerDocumentBody
.appendChild(this.domNode
);
1391 // Sets the background to the size of the viewport
1394 // Sets the background to the size of the viewport (rather than the size
1395 // of the document) since we need to cover the whole browser window, even
1396 // if the document is only a few lines long.
1400 var is
= this.node
.style
,
1401 os
= this.domNode
.style
;
1403 // hide the background temporarily, so that the background itself isn't
1404 // causing scrollbars to appear (might happen when user shrinks browser
1405 // window and then we are called to resize)
1406 os
.display
= "none";
1408 // then resize and show
1409 var viewport
= winUtils
.getBox(this.ownerDocument
);
1410 os
.top
= viewport
.t
+ "px";
1411 os
.left
= viewport
.l
+ "px";
1412 is
.width
= viewport
.w
+ "px";
1413 is
.height
= viewport
.h
+ "px";
1414 os
.display
= "block";
1419 // Show the dialog underlay
1420 this.domNode
.style
.display
= "block";
1422 this.bgIframe
= new BackgroundIframe(this.domNode
);
1427 // Hides the dialog underlay
1428 this.bgIframe
.destroy();
1429 delete this.bgIframe
;
1430 this.domNode
.style
.display
= "none";
1436 'dijit/place':function(){
1437 define("dijit/place", [
1438 "dojo/_base/array", // array.forEach array.map array.some
1439 "dojo/dom-geometry", // domGeometry.position
1440 "dojo/dom-style", // domStyle.getComputedStyle
1441 "dojo/_base/kernel", // kernel.deprecated
1442 "dojo/_base/window", // win.body
1443 "dojo/window", // winUtils.getBox
1444 "./main" // dijit (defining dijit.place to match API doc)
1445 ], function(array
, domGeometry
, domStyle
, kernel
, win
, winUtils
, dijit
){
1451 function _place(/*DomNode*/ node
, choices
, layoutNode
, aroundNodeCoords
){
1453 // Given a list of spots to put node, put it at the first spot where it fits,
1454 // of if it doesn't fit anywhere then the place with the least overflow
1456 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1457 // Above example says to put the top-left corner of the node at (10,20)
1458 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
1459 // for things like tooltip, they are displayed differently (and have different dimensions)
1460 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1461 // It also passes in the available size for the popup, which is useful for tooltips to
1462 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
1463 // how much the popup had to be modified to fit into the available space. This is used to determine
1464 // what the best placement is.
1465 // aroundNodeCoords: Object
1466 // Size of aroundNode, ex: {w: 200, h: 50}
1468 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1469 // viewport over document
1470 var view
= winUtils
.getBox(node
.ownerDocument
);
1472 // This won't work if the node is inside a <div style="position: relative">,
1473 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
1474 // and also it might get cutoff)
1475 if(!node
.parentNode
|| String(node
.parentNode
.tagName
).toLowerCase() != "body"){
1476 win
.body(node
.ownerDocument
).appendChild(node
);
1480 array
.some(choices
, function(choice
){
1481 var corner
= choice
.corner
;
1482 var pos
= choice
.pos
;
1485 // calculate amount of space available given specified position of node
1486 var spaceAvailable
= {
1488 'L': view
.l
+ view
.w
- pos
.x
,
1489 'R': pos
.x
- view
.l
,
1491 }[corner
.charAt(1)],
1493 'T': view
.t
+ view
.h
- pos
.y
,
1494 'B': pos
.y
- view
.t
,
1499 // Clear left/right position settings set earlier so they don't interfere with calculations,
1500 // specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
1502 s
.left
= s
.right
= "auto";
1504 // configure node to be displayed in given position relative to button
1505 // (need to do this in order to get an accurate size for the node, because
1506 // a tooltip's size changes based on position, due to triangle)
1508 var res
= layoutNode(node
, choice
.aroundCorner
, corner
, spaceAvailable
, aroundNodeCoords
);
1509 overflow
= typeof res
== "undefined" ? 0 : res
;
1513 var style
= node
.style
;
1514 var oldDisplay
= style
.display
;
1515 var oldVis
= style
.visibility
;
1516 if(style
.display
== "none"){
1517 style
.visibility
= "hidden";
1520 var bb
= domGeometry
.position(node
);
1521 style
.display
= oldDisplay
;
1522 style
.visibility
= oldVis
;
1524 // coordinates and size of node with specified corner placed at pos,
1525 // and clipped by viewport
1530 'M': Math
.max(view
.l
, Math
.min(view
.l
+ view
.w
, pos
.x
+ (bb
.w
>> 1)) - bb
.w
) // M orientation is more flexible
1531 }[corner
.charAt(1)],
1535 'M': Math
.max(view
.t
, Math
.min(view
.t
+ view
.h
, pos
.y
+ (bb
.h
>> 1)) - bb
.h
)
1536 }[corner
.charAt(0)],
1537 startX
= Math
.max(view
.l
, startXpos
),
1538 startY
= Math
.max(view
.t
, startYpos
),
1539 endX
= Math
.min(view
.l
+ view
.w
, startXpos
+ bb
.w
),
1540 endY
= Math
.min(view
.t
+ view
.h
, startYpos
+ bb
.h
),
1541 width
= endX
- startX
,
1542 height
= endY
- startY
;
1544 overflow
+= (bb
.w
- width
) + (bb
.h
- height
);
1546 if(best
== null || overflow
< best
.overflow
){
1549 aroundCorner
: choice
.aroundCorner
,
1555 spaceAvailable
: spaceAvailable
1562 // In case the best position is not the last one we checked, need to call
1563 // layoutNode() again.
1564 if(best
.overflow
&& layoutNode
){
1565 layoutNode(node
, best
.aroundCorner
, best
.corner
, best
.spaceAvailable
, aroundNodeCoords
);
1568 // And then position the node. Do this last, after the layoutNode() above
1569 // has sized the node, due to browser quirks when the viewport is scrolled
1570 // (specifically that a Tooltip will shrink to fit as though the window was
1571 // scrolled to the left).
1573 // In RTL mode, set style.right rather than style.left so in the common case,
1574 // window resizes move the popup along with the aroundNode.
1575 var l
= domGeometry
.isBodyLtr(node
.ownerDocument
),
1577 s
.top
= best
.y
+ "px";
1578 s
[l
? "left" : "right"] = (l
? best
.x
: view
.w
- best
.x
- best
.w
) + "px";
1579 s
[l
? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
1586 // Code to place a DOMNode relative to another DOMNode.
1587 // Load using require(["dijit/place"], function(place){ ... }).
1589 at: function(node
, pos
, corners
, padding
){
1591 // Positions one of the node's corners at specified position
1592 // such that node is fully visible in viewport.
1594 // NOTE: node is assumed to be absolutely or relatively positioned.
1596 // The node to position
1597 // pos: dijit/place.__Position
1598 // Object like {x: 10, y: 20}
1599 // corners: String[]
1600 // Array of Strings representing order to try corners in, like ["TR", "BL"].
1601 // Possible values are:
1603 // - "BL" - bottom left
1604 // - "BR" - bottom right
1605 // - "TL" - top left
1606 // - "TR" - top right
1607 // padding: dijit/place.__Position?
1608 // optional param to set padding, to put some buffer around the element you want to position.
1610 // Try to place node's top right corner at (10,20).
1611 // If that makes node go (partially) off screen, then try placing
1612 // bottom left corner at (10,20).
1613 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
1614 var choices
= array
.map(corners
, function(corner
){
1615 var c
= { corner
: corner
, pos
: {x
:pos
.x
,y
:pos
.y
} };
1617 c
.pos
.x
+= corner
.charAt(1) == 'L' ? padding
.x
: -padding
.x
;
1618 c
.pos
.y
+= corner
.charAt(0) == 'T' ? padding
.y
: -padding
.y
;
1623 return _place(node
, choices
);
1628 /*DomNode|dijit/place.__Rectangle*/ anchor,
1629 /*String[]*/ positions
,
1630 /*Boolean*/ leftToRight
,
1631 /*Function?*/ layoutNode
){
1634 // Position node adjacent or kitty-corner to anchor
1635 // such that it's fully visible in viewport.
1637 // Place node such that corner of node touches a corner of
1638 // aroundNode, and that node is fully visible.
1640 // Either a DOMNode or a rectangle (object with x, y, width, height).
1642 // Ordered list of positions to try matching up.
1644 // - before: places drop down to the left of the anchor node/widget, or to the right in the case
1645 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1646 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1647 // - after: places drop down to the right of the anchor node/widget, or to the left in the case
1648 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1649 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1650 // - before-centered: centers drop down to the left of the anchor node/widget, or to the right
1651 // in the case of RTL scripts like Hebrew and Arabic
1652 // - after-centered: centers drop down to the right of the anchor node/widget, or to the left
1653 // in the case of RTL scripts like Hebrew and Arabic
1654 // - above-centered: drop down is centered above anchor node
1655 // - above: drop down goes above anchor node, left sides aligned
1656 // - above-alt: drop down goes above anchor node, right sides aligned
1657 // - below-centered: drop down is centered above anchor node
1658 // - below: drop down goes below anchor node
1659 // - below-alt: drop down goes below anchor node, right sides aligned
1660 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1661 // For things like tooltip, they are displayed differently (and have different dimensions)
1662 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1664 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
1665 // positions slightly.
1667 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
1668 // This will try to position node such that node's top-left corner is at the same position
1669 // as the bottom left corner of the aroundNode (ie, put node below
1670 // aroundNode, with left edges aligned). If that fails it will try to put
1671 // the bottom-right corner of node where the top right corner of aroundNode is
1672 // (ie, put node above aroundNode, with right edges aligned)
1675 // if around is a DOMNode (or DOMNode id), convert to coordinates
1676 var aroundNodePos
= (typeof anchor
== "string" || "offsetWidth" in anchor
)
1677 ? domGeometry
.position(anchor
, true)
1680 // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
1681 if(anchor
.parentNode
){
1682 // ignore nodes between position:relative and position:absolute
1683 var sawPosAbsolute
= domStyle
.getComputedStyle(anchor
).position
== "absolute";
1684 var parent
= anchor
.parentNode
;
1685 while(parent
&& parent
.nodeType
== 1 && parent
.nodeName
!= "BODY"){ //ignoring the body will help performance
1686 var parentPos
= domGeometry
.position(parent
, true),
1687 pcs
= domStyle
.getComputedStyle(parent
);
1688 if(/relative|absolute/.test(pcs
.position
)){
1689 sawPosAbsolute
= false;
1691 if(!sawPosAbsolute
&& /hidden|auto|scroll/.test(pcs
.overflow
)){
1692 var bottomYCoord
= Math
.min(aroundNodePos
.y
+ aroundNodePos
.h
, parentPos
.y
+ parentPos
.h
);
1693 var rightXCoord
= Math
.min(aroundNodePos
.x
+ aroundNodePos
.w
, parentPos
.x
+ parentPos
.w
);
1694 aroundNodePos
.x
= Math
.max(aroundNodePos
.x
, parentPos
.x
);
1695 aroundNodePos
.y
= Math
.max(aroundNodePos
.y
, parentPos
.y
);
1696 aroundNodePos
.h
= bottomYCoord
- aroundNodePos
.y
;
1697 aroundNodePos
.w
= rightXCoord
- aroundNodePos
.x
;
1699 if(pcs
.position
== "absolute"){
1700 sawPosAbsolute
= true;
1702 parent
= parent
.parentNode
;
1706 var x
= aroundNodePos
.x
,
1707 y
= aroundNodePos
.y
,
1708 width
= "w" in aroundNodePos
? aroundNodePos
.w
: (aroundNodePos
.w
= aroundNodePos
.width
),
1709 height
= "h" in aroundNodePos
? aroundNodePos
.h
: (kernel
.deprecated("place.around: dijit/place.__Rectangle: { x:"+x
+", y:"+y
+", height:"+aroundNodePos
.height
+", width:"+width
+" } has been deprecated. Please use { x:"+x
+", y:"+y
+", h:"+aroundNodePos
.height
+", w:"+width
+" }", "", "2.0"), aroundNodePos
.h
= aroundNodePos
.height
);
1711 // Convert positions arguments into choices argument for _place()
1713 function push(aroundCorner
, corner
){
1715 aroundCorner
: aroundCorner
,
1721 'M': x
+ (width
>> 1)
1722 }[aroundCorner
.charAt(1)],
1726 'M': y
+ (height
>> 1)
1727 }[aroundCorner
.charAt(0)]
1731 array
.forEach(positions
, function(pos
){
1732 var ltr
= leftToRight
;
1734 case "above-centered":
1737 case "below-centered":
1740 case "after-centered":
1743 case "before-centered":
1744 push(ltr
? "ML" : "MR", ltr
? "MR" : "ML");
1750 push(ltr
? "TL" : "TR", ltr
? "TR" : "TL");
1751 push(ltr
? "BL" : "BR", ltr
? "BR" : "BL");
1757 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1758 push(ltr
? "BL" : "BR", ltr
? "TL" : "TR");
1759 push(ltr
? "BR" : "BL", ltr
? "TR" : "TL");
1765 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1766 push(ltr
? "TL" : "TR", ltr
? "BL" : "BR");
1767 push(ltr
? "TR" : "TL", ltr
? "BR" : "BL");
1770 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
1771 // Not meant to be used directly.
1772 push(pos
.aroundCorner
, pos
.corner
);
1776 var position
= _place(node
, choices
, layoutNode
, {w
: width
, h
: height
});
1777 position
.aroundNodePos
= aroundNodePos
;
1784 place.__Position = {
1786 // horizontal coordinate in pixels, relative to document body
1788 // vertical coordinate in pixels, relative to document body
1790 place.__Rectangle = {
1792 // horizontal offset in pixels, relative to document body
1794 // vertical offset in pixels, relative to document body
1796 // width in pixels. Can also be specified as "width" for backwards-compatibility.
1798 // height in pixels. Can also be specified as "height" for backwards-compatibility.
1802 return dijit
.place
= place
; // setting dijit.place for back-compat, remove for 2.0
1806 'dijit/_HasDropDown':function(){
1807 define("dijit/_HasDropDown", [
1808 "dojo/_base/declare", // declare
1809 "dojo/_base/Deferred",
1810 "dojo/_base/event", // event.stop
1811 "dojo/dom", // dom.isDescendant
1812 "dojo/dom-attr", // domAttr.set
1813 "dojo/dom-class", // domClass.add domClass.contains domClass.remove
1814 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
1815 "dojo/dom-style", // domStyle.set
1816 "dojo/has", // has("touch")
1817 "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
1818 "dojo/_base/lang", // lang.hitch lang.isFunction
1820 "dojo/window", // winUtils.getBox
1821 "./registry", // registry.byNode()
1825 ], function(declare
, Deferred
, event
,dom
, domAttr
, domClass
, domGeometry
, domStyle
, has
, keys
, lang
, on
,
1826 winUtils
, registry
, focus
, popup
, _FocusMixin
){
1830 // dijit/_HasDropDown
1832 return declare("dijit._HasDropDown", _FocusMixin
, {
1834 // Mixin for widgets that need drop down ability.
1836 // _buttonNode: [protected] DomNode
1837 // The button/icon/node to click to display the drop down.
1838 // Can be set via a data-dojo-attach-point assignment.
1839 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
1842 // _arrowWrapperNode: [protected] DomNode
1843 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
1844 // on where the drop down is set to be positioned.
1845 // Can be set via a data-dojo-attach-point assignment.
1846 // If missing, then _buttonNode will be used.
1847 _arrowWrapperNode
: null,
1849 // _popupStateNode: [protected] DomNode
1850 // The node to set the popupActive class on.
1851 // Can be set via a data-dojo-attach-point assignment.
1852 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
1853 _popupStateNode
: null,
1855 // _aroundNode: [protected] DomNode
1856 // The node to display the popup around.
1857 // Can be set via a data-dojo-attach-point assignment.
1858 // If missing, then domNode will be used.
1861 // dropDown: [protected] Widget
1862 // The widget to display as a popup. This widget *must* be
1863 // defined before the startup function is called.
1866 // autoWidth: [protected] Boolean
1867 // Set to true to make the drop down at least as wide as this
1868 // widget. Set to false if the drop down should just be its
1872 // forceWidth: [protected] Boolean
1873 // Set to true to make the drop down exactly as wide as this
1874 // widget. Overrides autoWidth.
1877 // maxHeight: [protected] Integer
1878 // The max height for our dropdown.
1879 // Any dropdown taller than this will have scrollbars.
1880 // Set to 0 for no max height, or -1 to limit height to available space in viewport
1883 // dropDownPosition: [const] String[]
1884 // This variable controls the position of the drop down.
1885 // It's an array of strings with the following values:
1887 // - before: places drop down to the left of the target node/widget, or to the right in
1888 // the case of RTL scripts like Hebrew and Arabic
1889 // - after: places drop down to the right of the target node/widget, or to the left in
1890 // the case of RTL scripts like Hebrew and Arabic
1891 // - above: drop down goes above target node
1892 // - below: drop down goes below target node
1894 // The list is positions is tried, in order, until a position is found where the drop down fits
1895 // within the viewport.
1897 dropDownPosition
: ["below","above"],
1899 // _stopClickEvents: Boolean
1900 // When set to false, the click events will not be stopped, in
1901 // case you want to use them in your subclass
1902 _stopClickEvents
: true,
1904 _onDropDownMouseDown: function(/*Event*/ e
){
1906 // Callback when the user mousedown's on the arrow icon
1907 if(this.disabled
|| this.readOnly
){ return; }
1909 // Prevent default to stop things like text selection, but don't stop propagation, so that:
1910 // 1. TimeTextBox etc. can focus the <input> on mousedown
1911 // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
1912 // 3. user defined onMouseDown handler fires
1915 this._docHandler
= this.connect(this.ownerDocument
, "mouseup", "_onDropDownMouseUp");
1917 this.toggleDropDown();
1920 _onDropDownMouseUp: function(/*Event?*/ e
){
1922 // Callback when the user lifts their mouse after mouse down on the arrow icon.
1923 // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
1924 // drop down widget. If the event is missing, then we are not
1927 // This is useful for the common mouse movement pattern
1928 // with native browser `<select>` nodes:
1930 // 1. mouse down on the select node (probably on the arrow)
1931 // 2. move mouse to a menu item while holding down the mouse button
1932 // 3. mouse up. this selects the menu item as though the user had clicked it.
1933 if(e
&& this._docHandler
){
1934 this.disconnect(this._docHandler
);
1936 var dropDown
= this.dropDown
, overMenu
= false;
1938 if(e
&& this._opened
){
1939 // This code deals with the corner-case when the drop down covers the original widget,
1940 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
1941 // Find out if our target is somewhere in our dropdown widget,
1942 // but not over our _buttonNode (the clickable node)
1943 var c
= domGeometry
.position(this._buttonNode
, true);
1944 if(!(e
.pageX
>= c
.x
&& e
.pageX
<= c
.x
+ c
.w
) ||
1945 !(e
.pageY
>= c
.y
&& e
.pageY
<= c
.y
+ c
.h
)){
1947 while(t
&& !overMenu
){
1948 if(domClass
.contains(t
, "dijitPopup")){
1956 if(dropDown
.onItemClick
){
1958 while(t
&& !(menuItem
= registry
.byNode(t
))){
1961 if(menuItem
&& menuItem
.onClick
&& menuItem
.getParent
){
1962 menuItem
.getParent().onItemClick(menuItem
, e
);
1970 if(dropDown
.focus
&& dropDown
.autoFocus
!== false){
1971 // Focus the dropdown widget - do it on a delay so that we
1972 // don't steal back focus from the dropdown.
1973 this._focusDropDownTimer
= this.defer(function(){
1975 delete this._focusDropDownTimer
;
1979 // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
1980 // defer() needed to make it work on IE (test DateTextBox)
1981 this.defer("focus");
1985 this._justGotMouseUp
= true;
1986 this.defer(function(){
1987 this._justGotMouseUp
= false;
1992 _onDropDownClick: function(/*Event*/ e
){
1993 if(has("touch") && !this._justGotMouseUp
){
1994 // If there was no preceding mousedown/mouseup (like on android), then simulate them to
1995 // toggle the drop down.
1997 // The if(has("touch") is necessary since IE and desktop safari get spurious onclick events
1998 // when there are nested tables (specifically, clicking on a table that holds a dijit/form/Select,
1999 // but not on the Select itself, causes an onclick event on the Select)
2000 this._onDropDownMouseDown(e
);
2001 this._onDropDownMouseUp(e
);
2004 // The drop down was already opened on mousedown/keydown; just need to call stopEvent().
2005 if(this._stopClickEvents
){
2010 buildRendering: function(){
2011 this.inherited(arguments
);
2013 this._buttonNode
= this._buttonNode
|| this.focusNode
|| this.domNode
;
2014 this._popupStateNode
= this._popupStateNode
|| this.focusNode
|| this._buttonNode
;
2016 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
2017 // based on where drop down will normally appear
2019 "after" : this.isLeftToRight() ? "Right" : "Left",
2020 "before" : this.isLeftToRight() ? "Left" : "Right",
2025 }[this.dropDownPosition
[0]] || this.dropDownPosition
[0] || "Down";
2026 domClass
.add(this._arrowWrapperNode
|| this._buttonNode
, "dijit" + defaultPos
+ "ArrowButton");
2029 postCreate: function(){
2031 // set up nodes and connect our mouse and keyboard events
2033 this.inherited(arguments
);
2035 var keyboardEventNode
= this.focusNode
|| this.domNode
;
2037 on(this._buttonNode
, "mousedown", lang
.hitch(this, "_onDropDownMouseDown")),
2038 on(this._buttonNode
, "click", lang
.hitch(this, "_onDropDownClick")),
2039 on(keyboardEventNode
, "keydown", lang
.hitch(this, "_onKey")),
2040 on(keyboardEventNode
, "keyup", lang
.hitch(this, "_onKeyUp"))
2044 destroy: function(){
2046 // Destroy the drop down, unless it's already been destroyed. This can happen because
2047 // the drop down is a direct child of <body> even though it's logically my child.
2048 if(!this.dropDown
._destroyed
){
2049 this.dropDown
.destroyRecursive();
2051 delete this.dropDown
;
2053 this.inherited(arguments
);
2056 _onKey: function(/*Event*/ e
){
2058 // Callback when the user presses a key while focused on the button node
2060 if(this.disabled
|| this.readOnly
){ return; }
2061 var d
= this.dropDown
, target
= e
.target
;
2062 if(d
&& this._opened
&& d
.handleKey
){
2063 if(d
.handleKey(e
) === false){
2064 /* false return code means that the drop down handled the key */
2069 if(d
&& this._opened
&& e
.keyCode
== keys
.ESCAPE
){
2070 this.closeDropDown();
2072 }else if(!this._opened
&&
2073 (e
.keyCode
== keys
.DOWN_ARROW
||
2074 ( (e
.keyCode
== keys
.ENTER
|| e
.keyCode
== keys
.SPACE
) &&
2075 //ignore enter and space if the event is for a text input
2076 ((target
.tagName
|| "").toLowerCase() !== 'input' ||
2077 (target
.type
&& target
.type
.toLowerCase() !== 'text'))))){
2078 // Toggle the drop down, but wait until keyup so that the drop down doesn't
2079 // get a stray keyup event, or in the case of key-repeat (because user held
2080 // down key for too long), stray keydown events
2081 this._toggleOnKeyUp
= true;
2086 _onKeyUp: function(){
2087 if(this._toggleOnKeyUp
){
2088 delete this._toggleOnKeyUp
;
2089 this.toggleDropDown();
2090 var d
= this.dropDown
; // drop down may not exist until toggleDropDown() call
2092 this.defer(lang
.hitch(d
, "focus"), 1);
2097 _onBlur: function(){
2099 // Called magically when focus has shifted away from this widget and it's dropdown
2101 // Don't focus on button if the user has explicitly focused on something else (happens
2102 // when user clicks another control causing the current popup to close)..
2103 // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
2104 // it when you display:none a node with focus.
2105 var focusMe
= focus
.curNode
&& this.dropDown
&& dom
.isDescendant(focus
.curNode
, this.dropDown
.domNode
);
2107 this.closeDropDown(focusMe
);
2109 this.inherited(arguments
);
2112 isLoaded: function(){
2114 // Returns true if the dropdown exists and it's data is loaded. This can
2115 // be overridden in order to force a call to loadDropDown().
2122 loadDropDown: function(/*Function*/ loadCallback
){
2124 // Creates the drop down if it doesn't exist, loads the data
2125 // if there's an href and it hasn't been loaded yet, and then calls
2126 // the given callback.
2130 // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
2134 loadAndOpenDropDown: function(){
2136 // Creates the drop down if it doesn't exist, loads the data
2137 // if there's an href and it hasn't been loaded yet, and
2138 // then opens the drop down. This is basically a callback when the
2139 // user presses the down arrow button to open the drop down.
2140 // returns: Deferred
2141 // Deferred for the drop down widget that
2142 // fires when drop down is created and loaded
2145 var d
= new Deferred(),
2146 afterLoad
= lang
.hitch(this, function(){
2147 this.openDropDown();
2148 d
.resolve(this.dropDown
);
2150 if(!this.isLoaded()){
2151 this.loadDropDown(afterLoad
);
2158 toggleDropDown: function(){
2160 // Callback when the user presses the down arrow button or presses
2161 // the down arrow key to open/close the drop down.
2162 // Toggle the drop-down widget; if it is up, close it, if not, open it
2166 if(this.disabled
|| this.readOnly
){ return; }
2168 this.loadAndOpenDropDown();
2170 this.closeDropDown();
2174 openDropDown: function(){
2176 // Opens the dropdown for this widget. To be called only when this.dropDown
2177 // has been created and is ready to display (ie, it's data is loaded).
2179 // return value of dijit/popup.open()
2183 var dropDown
= this.dropDown
,
2184 ddNode
= dropDown
.domNode
,
2185 aroundNode
= this._aroundNode
|| this.domNode
,
2188 // Prepare our popup's height and honor maxHeight if it exists.
2190 // TODO: isn't maxHeight dependent on the return value from dijit/popup.open(),
2191 // ie, dependent on how much space is available (BK)
2193 if(!this._preparedNode
){
2194 this._preparedNode
= true;
2195 // Check if we have explicitly set width and height on the dropdown widget dom node
2196 if(ddNode
.style
.width
){
2197 this._explicitDDWidth
= true;
2199 if(ddNode
.style
.height
){
2200 this._explicitDDHeight
= true;
2204 // Code for resizing dropdown (height limitation, or increasing width to match my width)
2205 if(this.maxHeight
|| this.forceWidth
|| this.autoWidth
){
2208 visibility
: "hidden"
2210 if(!this._explicitDDWidth
){
2213 if(!this._explicitDDHeight
){
2214 myStyle
.height
= "";
2216 domStyle
.set(ddNode
, myStyle
);
2218 // Figure out maximum height allowed (if there is a height restriction)
2219 var maxHeight
= this.maxHeight
;
2220 if(maxHeight
== -1){
2221 // limit height to space available in viewport either above or below my domNode
2222 // (whichever side has more room)
2223 var viewport
= winUtils
.getBox(this.ownerDocument
),
2224 position
= domGeometry
.position(aroundNode
, false);
2225 maxHeight
= Math
.floor(Math
.max(position
.y
, viewport
.h
- (position
.y
+ position
.h
)));
2228 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
2229 // so we call startup() and also get the size
2230 popup
.moveOffScreen(dropDown
);
2232 if(dropDown
.startup
&& !dropDown
._started
){
2233 dropDown
.startup(); // this has to be done after being added to the DOM
2235 // Get size of drop down, and determine if vertical scroll bar needed. If no scroll bar needed,
2236 // use overflow:visible rather than overflow:hidden so off-by-one errors don't hide drop down border.
2237 var mb
= domGeometry
.getMarginSize(ddNode
);
2238 var overHeight
= (maxHeight
&& mb
.h
> maxHeight
);
2239 domStyle
.set(ddNode
, {
2240 overflowX
: "visible",
2241 overflowY
: overHeight
? "auto" : "visible"
2246 mb
.w
+= 16; // room for vertical scrollbar
2252 // Adjust dropdown width to match or be larger than my width
2253 if(this.forceWidth
){
2254 mb
.w
= aroundNode
.offsetWidth
;
2255 }else if(this.autoWidth
){
2256 mb
.w
= Math
.max(mb
.w
, aroundNode
.offsetWidth
);
2261 // And finally, resize the dropdown to calculated height and width
2262 if(lang
.isFunction(dropDown
.resize
)){
2263 dropDown
.resize(mb
);
2265 domGeometry
.setMarginBox(ddNode
, mb
);
2269 var retVal
= popup
.open({
2273 orient
: this.dropDownPosition
,
2274 onExecute: function(){
2275 self
.closeDropDown(true);
2277 onCancel: function(){
2278 self
.closeDropDown(true);
2280 onClose: function(){
2281 domAttr
.set(self
._popupStateNode
, "popupActive", false);
2282 domClass
.remove(self
._popupStateNode
, "dijitHasDropDownOpen");
2283 self
._set("_opened", false); // use set() because _CssStateMixin is watching
2286 domAttr
.set(this._popupStateNode
, "popupActive", "true");
2287 domClass
.add(this._popupStateNode
, "dijitHasDropDownOpen");
2288 this._set("_opened", true); // use set() because _CssStateMixin is watching
2289 this.domNode
.setAttribute("aria-expanded", "true");
2294 closeDropDown: function(/*Boolean*/ focus
){
2296 // Closes the drop down on this widget
2298 // If true, refocuses the button widget
2302 if(this._focusDropDownTimer
){
2303 this._focusDropDownTimer
.remove();
2304 delete this._focusDropDownTimer
;
2307 this.domNode
.setAttribute("aria-expanded", "false");
2308 if(focus
){ this.focus(); }
2309 popup
.close(this.dropDown
);
2310 this._opened
= false;
2318 'dijit/tree/TreeStoreModel':function(){
2319 define("dijit/tree/TreeStoreModel", [
2320 "dojo/_base/array", // array.filter array.forEach array.indexOf array.some
2321 "dojo/aspect", // aspect.after
2322 "dojo/_base/declare", // declare
2323 "dojo/_base/lang" // lang.hitch
2324 ], function(array
, aspect
, declare
, lang
){
2327 // dijit/tree/TreeStoreModel
2329 return declare("dijit.tree.TreeStoreModel", null, {
2331 // Implements dijit/Tree/model connecting to a dojo.data store with a single
2332 // root item. Any methods passed into the constructor will override
2333 // the ones defined here.
2335 // store: dojo/data/api/Read
2339 // childrenAttrs: String[]
2340 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
2341 childrenAttrs
: ["children"],
2343 // newItemIdAttr: String
2344 // Name of attribute in the Object passed to newItem() that specifies the id.
2346 // If newItemIdAttr is set then it's used when newItem() is called to see if an
2347 // item with the same id already exists, and if so just links to the old item
2348 // (so that the old item ends up with two parents).
2350 // Setting this to null or "" will make every drop create a new item.
2351 newItemIdAttr
: "id",
2353 // labelAttr: String
2354 // If specified, get label for tree node from this attribute, rather
2355 // than by calling store.getLabel()
2358 // root: [readonly] dojo/data/Item
2359 // Pointer to the root item (read only, not a parameter)
2363 // Specifies datastore query to return the root item for the tree.
2364 // Must only return a single item. Alternately can just pass in pointer
2370 // deferItemLoadingUntilExpand: Boolean
2371 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
2372 // until they are expanded. This allows for lazying loading where only one
2373 // loadItem (and generally one network call, consequently) per expansion
2374 // (rather than one for each child).
2375 // This relies on partial loading of the children items; each children item of a
2376 // fully loaded item should contain the label and info about having children.
2377 deferItemLoadingUntilExpand
: false,
2379 constructor: function(/* Object */ args
){
2381 // Passed the arguments listed above (store, etc)
2385 lang
.mixin(this, args
);
2389 var store
= this.store
;
2390 if(!store
.getFeatures()['dojo.data.api.Identity']){
2391 throw new Error("dijit.tree.TreeStoreModel: store must support dojo.data.Identity");
2394 // if the store supports Notification, subscribe to the notification events
2395 if(store
.getFeatures()['dojo.data.api.Notification']){
2396 this.connects
= this.connects
.concat([
2397 aspect
.after(store
, "onNew", lang
.hitch(this, "onNewItem"), true),
2398 aspect
.after(store
, "onDelete", lang
.hitch(this, "onDeleteItem"), true),
2399 aspect
.after(store
, "onSet", lang
.hitch(this, "onSetItem"), true)
2404 destroy: function(){
2406 while(h
= this.connects
.pop()){ h
.remove(); }
2407 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
2410 // =======================================================================
2411 // Methods for traversing hierarchy
2413 getRoot: function(onItem
, onError
){
2415 // Calls onItem with the root item for the tree, possibly a fabricated item.
2416 // Calls onError on error.
2422 onComplete
: lang
.hitch(this, function(items
){
2423 if(items
.length
!= 1){
2424 throw new Error("dijit.tree.TreeStoreModel: root query returned " + items
.length
+
2425 " items, but must return exactly one");
2427 this.root
= items
[0];
2435 mayHaveChildren: function(/*dojo/data/Item*/ item
){
2437 // Tells if an item has or may have children. Implementing logic here
2438 // avoids showing +/- expando icon for nodes that we know don't have children.
2439 // (For efficiency reasons we may not want to check if an element actually
2440 // has children until user clicks the expando node)
2441 return array
.some(this.childrenAttrs
, function(attr
){
2442 return this.store
.hasAttribute(item
, attr
);
2446 getChildren: function(/*dojo/data/Item*/ parentItem
, /*function(items)*/ onComplete
, /*function*/ onError
){
2448 // Calls onComplete() with array of child items of given parent item, all loaded.
2450 var store
= this.store
;
2451 if(!store
.isItemLoaded(parentItem
)){
2452 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
2453 // mode, so we will load it and just return the children (without loading each
2455 var getChildren
= lang
.hitch(this, arguments
.callee
);
2458 onItem: function(parentItem
){
2459 getChildren(parentItem
, onComplete
, onError
);
2465 // get children of specified item
2466 var childItems
= [];
2467 for(var i
=0; i
<this.childrenAttrs
.length
; i
++){
2468 var vals
= store
.getValues(parentItem
, this.childrenAttrs
[i
]);
2469 childItems
= childItems
.concat(vals
);
2472 // count how many items need to be loaded
2474 if(!this.deferItemLoadingUntilExpand
){
2475 array
.forEach(childItems
, function(item
){ if(!store
.isItemLoaded(item
)){ _waitCount
++; } });
2478 if(_waitCount
== 0){
2479 // all items are already loaded (or we aren't loading them). proceed...
2480 onComplete(childItems
);
2482 // still waiting for some or all of the items to load
2483 array
.forEach(childItems
, function(item
, idx
){
2484 if(!store
.isItemLoaded(item
)){
2487 onItem: function(item
){
2488 childItems
[idx
] = item
;
2489 if(--_waitCount
== 0){
2490 // all nodes have been loaded, send them to the tree
2491 onComplete(childItems
);
2501 // =======================================================================
2504 isItem: function(/* anything */ something
){
2505 return this.store
.isItem(something
); // Boolean
2508 fetchItemByIdentity: function(/* object */ keywordArgs
){
2509 this.store
.fetchItemByIdentity(keywordArgs
);
2512 getIdentity: function(/* item */ item
){
2513 return this.store
.getIdentity(item
); // Object
2516 getLabel: function(/*dojo/data/Item*/ item
){
2518 // Get the label for an item
2520 return this.store
.getValue(item
,this.labelAttr
); // String
2522 return this.store
.getLabel(item
); // String
2526 // =======================================================================
2529 newItem: function(/* dijit/tree/dndSource.__Item */ args
, /*dojo/data/api/Item*/ parent, /*int?*/ insertIndex
){
2531 // Creates a new item. See `dojo/data/api/Write` for details on args.
2532 // Used in drag & drop when item from external source dropped onto tree.
2534 // Developers will need to override this method if new items get added
2535 // to parents with multiple children attributes, in order to define which
2536 // children attribute points to the new item.
2538 var pInfo
= {parent
: parent
, attribute
: this.childrenAttrs
[0]}, LnewItem
;
2540 if(this.newItemIdAttr
&& args
[this.newItemIdAttr
]){
2541 // Maybe there's already a corresponding item in the store; if so, reuse it.
2542 this.fetchItemByIdentity({identity
: args
[this.newItemIdAttr
], scope
: this, onItem: function(item
){
2544 // There's already a matching item in store, use it
2545 this.pasteItem(item
, null, parent
, true, insertIndex
);
2547 // Create new item in the tree, based on the drag source.
2548 LnewItem
=this.store
.newItem(args
, pInfo
);
2549 if(LnewItem
&& (insertIndex
!=undefined)){
2550 // Move new item to desired position
2551 this.pasteItem(LnewItem
, parent
, parent
, false, insertIndex
);
2556 // [as far as we know] there is no id so we must assume this is a new item
2557 LnewItem
=this.store
.newItem(args
, pInfo
);
2558 if(LnewItem
&& (insertIndex
!=undefined)){
2559 // Move new item to desired position
2560 this.pasteItem(LnewItem
, parent
, parent
, false, insertIndex
);
2565 pasteItem: function(/*Item*/ childItem
, /*Item*/ oldParentItem
, /*Item*/ newParentItem
, /*Boolean*/ bCopy
, /*int?*/ insertIndex
){
2567 // Move or copy an item from one parent item to another.
2568 // Used in drag & drop
2569 var store
= this.store
,
2570 parentAttr
= this.childrenAttrs
[0]; // name of "children" attr in parent item
2572 // remove child from source item, and record the attribute that child occurred in
2574 array
.forEach(this.childrenAttrs
, function(attr
){
2575 if(store
.containsValue(oldParentItem
, attr
, childItem
)){
2577 var values
= array
.filter(store
.getValues(oldParentItem
, attr
), function(x
){
2578 return x
!= childItem
;
2580 store
.setValues(oldParentItem
, attr
, values
);
2587 // modify target item's children attribute to include this item
2589 if(typeof insertIndex
== "number"){
2590 // call slice() to avoid modifying the original array, confusing the data store
2591 var childItems
= store
.getValues(newParentItem
, parentAttr
).slice();
2592 childItems
.splice(insertIndex
, 0, childItem
);
2593 store
.setValues(newParentItem
, parentAttr
, childItems
);
2595 store
.setValues(newParentItem
, parentAttr
,
2596 store
.getValues(newParentItem
, parentAttr
).concat(childItem
));
2601 // =======================================================================
2604 onChange: function(/*dojo/data/Item*/ /*===== item =====*/){
2606 // Callback whenever an item has changed, so that Tree
2607 // can update the label, icon, etc. Note that changes
2608 // to an item's children or parent(s) will trigger an
2609 // onChildrenChange() so you can ignore those changes here.
2614 onChildrenChange: function(/*===== parent, newChildrenList =====*/){
2616 // Callback to do notifications about new, updated, or deleted items.
2617 // parent: dojo/data/Item
2618 // newChildrenList: dojo/data/Item[]
2623 onDelete: function(/*dojo/data/Item*/ /*===== item =====*/){
2625 // Callback when an item has been deleted.
2627 // Note that there will also be an onChildrenChange() callback for the parent
2633 // =======================================================================
2634 // Events from data store
2636 onNewItem: function(/* dojo/data/Item */ item
, /* Object */ parentInfo
){
2638 // Handler for when new items appear in the store, either from a drop operation
2639 // or some other way. Updates the tree view (if necessary).
2641 // If the new item is a child of an existing item,
2642 // calls onChildrenChange() with the new list of children
2643 // for that existing item.
2648 // We only care about the new item if it has a parent that corresponds to a TreeNode
2649 // we are currently displaying
2654 // Call onChildrenChange() on parent (ie, existing) item with new list of children
2655 // In the common case, the new list of children is simply parentInfo.newValue or
2656 // [ parentInfo.newValue ], although if items in the store has multiple
2657 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
2658 // so call getChildren() to be sure to get right answer.
2659 this.getChildren(parentInfo
.item
, lang
.hitch(this, function(children
){
2660 this.onChildrenChange(parentInfo
.item
, children
);
2664 onDeleteItem: function(/*Object*/ item
){
2666 // Handler for delete notifications from underlying store
2667 this.onDelete(item
);
2670 onSetItem: function(item
, attribute
/*===== , oldValue, newValue =====*/){
2672 // Updates the tree view according to changes in the data store.
2674 // Handles updates to an item's children by calling onChildrenChange(), and
2675 // other updates to an item by calling onChange().
2677 // See `onNewItem` for more details on handling updates to an item's children.
2679 // attribute: attribute-name-string
2680 // oldValue: Object|Array
2681 // newValue: Object|Array
2685 if(array
.indexOf(this.childrenAttrs
, attribute
) != -1){
2686 // item's children list changed
2687 this.getChildren(item
, lang
.hitch(this, function(children
){
2688 // See comments in onNewItem() about calling getChildren()
2689 this.onChildrenChange(item
, children
);
2692 // item's label/icon/etc. changed.
2693 this.onChange(item
);
2700 'dijit/_MenuBase':function(){
2701 define("dijit/_MenuBase", [
2702 "dojo/_base/array", // array.indexOf
2703 "dojo/_base/declare", // declare
2704 "dojo/dom", // dom.isDescendant domClass.replace
2706 "dojo/dom-class", // domClass.replace
2707 "dojo/_base/lang", // lang.hitch
2708 "dojo/mouse", // mouse.enter, mouse.leave
2715 "./_KeyNavContainer",
2717 ], function(array
, declare
, dom
, domAttr
, domClass
, lang
, mouse
, on
, winUtils
,
2718 a11yclick
, pm
, registry
, _Widget
, _KeyNavContainer
, _TemplatedMixin
){
2724 return declare("dijit._MenuBase",
2725 [_Widget
, _TemplatedMixin
, _KeyNavContainer
],
2728 // Base class for Menu and MenuBar
2730 // parentMenu: [readonly] Widget
2731 // pointer to menu that displayed me
2734 // popupDelay: Integer
2735 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
2738 // autoFocus: Boolean
2739 // A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
2740 // or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
2743 childSelector: function(/*DOMNode*/ node
){
2745 // Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
2746 // like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
2750 var widget
= registry
.byNode(node
);
2751 return node
.parentNode
== this.containerNode
&& widget
&& widget
.focus
;
2754 postCreate: function(){
2756 matches
= typeof this.childSelector
== "string" ? this.childSelector
: lang
.hitch(this, "childSelector");
2758 on(this.containerNode
, on
.selector(matches
, mouse
.enter
), function(){
2759 self
.onItemHover(registry
.byNode(this));
2761 on(this.containerNode
, on
.selector(matches
, mouse
.leave
), function(){
2762 self
.onItemUnhover(registry
.byNode(this));
2764 on(this.containerNode
, on
.selector(matches
, a11yclick
), function(evt
){
2765 self
.onItemClick(registry
.byNode(this), evt
);
2766 evt
.stopPropagation();
2767 evt
.preventDefault();
2770 this.inherited(arguments
);
2773 onExecute: function(){
2775 // Attach point for notification about when a menu item has been executed.
2776 // This is an internal mechanism used for Menus to signal to their parent to
2777 // close them, because they are about to execute the onClick handler. In
2778 // general developers should not attach to or override this method.
2783 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
2785 // Attach point for notification about when the user cancels the current menu
2786 // This is an internal mechanism used for Menus to signal to their parent to
2787 // close them. In general developers should not attach to or override this method.
2792 _moveToPopup: function(/*Event*/ evt
){
2794 // This handles the right arrow key (left arrow key on RTL systems),
2795 // which will either open a submenu, or move to the next item in the
2800 if(this.focusedChild
&& this.focusedChild
.popup
&& !this.focusedChild
.disabled
){
2801 this.onItemClick(this.focusedChild
, evt
);
2803 var topMenu
= this._getTopMenu();
2804 if(topMenu
&& topMenu
._isMenuBar
){
2805 topMenu
.focusNext();
2810 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
2812 // This handler is called when the mouse moves over the popup.
2816 // if the mouse hovers over a menu popup that is in pending-close state,
2817 // then stop the close operation.
2818 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
2819 if(this.currentPopup
&& this.currentPopup
._pendingClose_timer
){
2820 var parentMenu
= this.currentPopup
.parentMenu
;
2821 // highlight the parent menu item pointing to this popup
2822 if(parentMenu
.focusedChild
){
2823 parentMenu
.focusedChild
._setSelected(false);
2825 parentMenu
.focusedChild
= this.currentPopup
.from_item
;
2826 parentMenu
.focusedChild
._setSelected(true);
2827 // cancel the pending close
2828 this._stopPendingCloseTimer(this.currentPopup
);
2832 onItemHover: function(/*MenuItem*/ item
){
2834 // Called when cursor is over a MenuItem.
2838 // Don't do anything unless user has "activated" the menu by:
2840 // 2) opening it from a parent menu (which automatically focuses it)
2842 this.focusChild(item
);
2843 if(this.focusedChild
.popup
&& !this.focusedChild
.disabled
&& !this.hover_timer
){
2844 this.hover_timer
= this.defer("_openPopup", this.popupDelay
);
2847 // if the user is mixing mouse and keyboard navigation,
2848 // then the menu may not be active but a menu item has focus,
2849 // but it's not the item that the mouse just hovered over.
2850 // To avoid both keyboard and mouse selections, use the latest.
2851 if(this.focusedChild
){
2852 this.focusChild(item
);
2854 this._hoveredChild
= item
;
2856 item
._set("hovering", true);
2859 _onChildBlur: function(item
){
2861 // Called when a child MenuItem becomes inactive because focus
2862 // has been removed from the MenuItem *and* it's descendant menus.
2865 this._stopPopupTimer();
2866 item
._setSelected(false);
2867 // Close all popups that are open and descendants of this menu
2868 var itemPopup
= item
.popup
;
2870 this._stopPendingCloseTimer(itemPopup
);
2871 itemPopup
._pendingClose_timer
= this.defer(function(){
2872 itemPopup
._pendingClose_timer
= null;
2873 if(itemPopup
.parentMenu
){
2874 itemPopup
.parentMenu
.currentPopup
= null;
2876 pm
.close(itemPopup
); // this calls onClose
2877 }, this.popupDelay
);
2881 onItemUnhover: function(/*MenuItem*/ item
){
2883 // Callback fires when mouse exits a MenuItem
2888 this._stopPopupTimer();
2890 if(this._hoveredChild
== item
){ this._hoveredChild
= null; }
2892 item
._set("hovering", false);
2895 _stopPopupTimer: function(){
2897 // Cancels the popup timer because the user has stop hovering
2898 // on the MenuItem, etc.
2901 if(this.hover_timer
){
2902 this.hover_timer
= this.hover_timer
.remove();
2906 _stopPendingCloseTimer: function(/*dijit/_WidgetBase*/ popup){
2908 // Cancels the pending-close timer because the close has been preempted
2911 if(popup._pendingClose_timer){
2912 popup._pendingClose_timer = popup._pendingClose_timer.remove();
2916 _stopFocusTimer: function(){
2918 // Cancels the pending-focus timer because the menu was closed before focus occured
2921 if(this._focus_timer){
2922 this._focus_timer = this._focus_timer.remove();
2926 _getTopMenu: function(){
2928 // Returns the top menu in this chain of Menus
2931 for(var top=this; top.parentMenu; top=top.parentMenu);
2935 onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt
){
2937 // Handle clicks on an item.
2941 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
2942 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu
2946 this.focusChild(item
);
2948 if(item
.disabled
){ return false; }
2951 this._openPopup(evt
.type
== "keypress");
2953 // before calling user defined handler, close hierarchy of menus
2954 // and restore focus to place it was when menu was opened
2957 // user defined handler for click
2958 item
._onClick
? item
._onClick(evt
) : item
.onClick(evt
);
2962 _openPopup: function(/*Boolean*/ focus
){
2964 // Open the popup to the side of/underneath the current menu item, and optionally focus first item
2968 this._stopPopupTimer();
2969 var from_item
= this.focusedChild
;
2970 if(!from_item
){ return; } // the focused child lost focus since the timer was started
2971 var popup
= from_item
.popup
;
2972 if(!popup
.isShowingNow
){
2973 if(this.currentPopup
){
2974 this._stopPendingCloseTimer(this.currentPopup
);
2975 pm
.close(this.currentPopup
);
2977 popup
.parentMenu
= this;
2978 popup
.from_item
= from_item
; // helps finding the parent item that should be focused for this popup
2983 around
: from_item
.domNode
,
2984 orient
: this._orient
|| ["after", "before"],
2985 onCancel: function(){ // called when the child menu is canceled
2986 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
2987 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
2988 self
.focusChild(from_item
); // put focus back on my node
2989 self
._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
2990 from_item
._setSelected(true); // oops, _cleanUp() deselected the item
2991 self
.focusedChild
= from_item
; // and unset focusedChild
2993 onExecute
: lang
.hitch(this, "_cleanUp")
2996 this.currentPopup
= popup
;
2997 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
2998 popup
.connect(popup
.domNode
, "onmouseenter", lang
.hitch(self
, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
3001 if(focus
&& popup
.focus
){
3002 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar), then focus the popup.
3003 // If the cursor happens to collide with the popup, it will generate an onmouseover event
3004 // even though the mouse wasn't moved. Use defer() to call popup.focus so that
3005 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
3006 popup
._focus_timer
= this.defer(lang
.hitch(popup
, function(){
3007 this._focus_timer
= null;
3013 _markActive: function(){
3015 // Mark this menu's state as active.
3016 // Called when this Menu gets focus from:
3018 // 1. clicking it (mouse or via space/arrow key)
3019 // 2. being opened by a parent menu.
3021 // This is not called just from mouse hover.
3022 // Focusing a menu via TAB does NOT automatically set isActive
3023 // since TAB is a navigation operation and not a selection one.
3024 // For Windows apps, pressing the ALT key focuses the menubar
3025 // menus (similar to TAB navigation) but the menu is not active
3026 // (ie no dropdown) until an item is clicked.
3027 this.isActive
= true;
3028 domClass
.replace(this.domNode
, "dijitMenuActive", "dijitMenuPassive");
3031 onOpen: function(/*Event*/ /*===== e =====*/){
3033 // Callback when this menu is opened.
3034 // This is called by the popup manager as notification that the menu
3039 this.isShowingNow
= true;
3043 _markInactive: function(){
3045 // Mark this menu's state as inactive.
3046 this.isActive
= false; // don't do this in _onBlur since the state is pending-close until we get here
3047 domClass
.replace(this.domNode
, "dijitMenuPassive", "dijitMenuActive");
3050 onClose: function(){
3052 // Callback when this menu is closed.
3053 // This is called by the popup manager as notification that the menu
3058 this._stopFocusTimer();
3059 this._markInactive();
3060 this.isShowingNow
= false;
3061 this.parentMenu
= null;
3064 _closeChild: function(){
3066 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
3069 this._stopPopupTimer();
3071 if(this.currentPopup
){
3072 // If focus is on a descendant MenuItem then move focus to me,
3073 // because IE doesn't like it when you display:none a node with focus,
3074 // and also so keyboard users don't lose control.
3075 // Likely, immediately after a user defined onClick handler will move focus somewhere
3076 // else, like a Dialog.
3077 if(array
.indexOf(this._focusManager
.activeStack
, this.id
) >= 0){
3078 domAttr
.set(this.focusedChild
.focusNode
, "tabIndex", this.tabIndex
);
3079 this.focusedChild
.focusNode
.focus();
3081 // Close all popups that are open and descendants of this menu
3082 pm
.close(this.currentPopup
);
3083 this.currentPopup
= null;
3086 if(this.focusedChild
){ // unhighlight the focused item
3087 this.focusedChild
._setSelected(false);
3088 this.onItemUnhover(this.focusedChild
);
3089 this.focusedChild
= null;
3093 _onItemFocus: function(/*MenuItem*/ item
){
3095 // Called when child of this Menu gets focus from:
3098 // 2. tabbing into it
3099 // 3. being opened by a parent menu.
3101 // This is not called just from mouse hover.
3102 if(this._hoveredChild
&& this._hoveredChild
!= item
){
3103 this.onItemUnhover(this._hoveredChild
); // any previous mouse movement is trumped by focus selection
3107 _onBlur: function(){
3109 // Called when focus is moved away from this Menu and it's submenus.
3113 this.inherited(arguments
);
3116 _cleanUp: function(){
3118 // Called when the user is done with this menu. Closes hierarchy of menus.
3122 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
3123 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu doesn't call onClose
3124 this._markInactive();
3132 'dijit/focus':function(){
3133 define("dijit/focus", [
3135 "dojo/_base/declare", // declare
3136 "dojo/dom", // domAttr.get dom.isDescendant
3137 "dojo/dom-attr", // domAttr.get dom.isDescendant
3138 "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
3140 "dojo/_base/lang", // lang.hitch
3143 "dojo/sniff", // has("ie")
3145 "dojo/_base/unload", // unload.addOnWindowUnload
3146 "dojo/_base/window", // win.body
3147 "dojo/window", // winUtils.get
3148 "./a11y", // a11y.isTabNavigable
3149 "./registry", // registry.byId
3150 "./main" // to set dijit.focus
3151 ], function(aspect
, declare
, dom
, domAttr
, domConstruct
, Evented
, lang
, on
, ready
, has
, Stateful
, unload
, win
, winUtils
,
3152 a11y
, registry
, dijit
){
3157 var FocusManager
= declare([Stateful
, Evented
], {
3159 // Tracks the currently focused node, and which widgets are currently "active".
3160 // Access via require(["dijit/focus"], function(focus){ ... }).
3162 // A widget is considered active if it or a descendant widget has focus,
3163 // or if a non-focusable node of this widget or a descendant was recently clicked.
3165 // Call focus.watch("curNode", callback) to track the current focused DOMNode,
3166 // or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
3168 // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
3169 // when widgets become active/inactive
3171 // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
3174 // Currently focused item on screen
3177 // activeStack: dijit/_WidgetBase[]
3178 // List of currently active widgets (focused widget and it's ancestors)
3181 constructor: function(){
3182 // Don't leave curNode/prevNode pointing to bogus elements
3183 var check
= lang
.hitch(this, function(node
){
3184 if(dom
.isDescendant(this.curNode
, node
)){
3185 this.set("curNode", null);
3187 if(dom
.isDescendant(this.prevNode
, node
)){
3188 this.set("prevNode", null);
3191 aspect
.before(domConstruct
, "empty", check
);
3192 aspect
.before(domConstruct
, "destroy", check
);
3195 registerIframe: function(/*DomNode*/ iframe
){
3197 // Registers listeners on the specified iframe so that any click
3198 // or focus event on that iframe (or anything in it) is reported
3199 // as a focus/click event on the `<iframe>` itself.
3201 // Currently only used by editor.
3203 // Handle with remove() method to deregister.
3204 return this.registerWin(iframe
.contentWindow
, iframe
);
3207 registerWin: function(/*Window?*/targetWindow
, /*DomNode?*/ effectiveNode
){
3209 // Registers listeners on the specified window (either the main
3210 // window or an iframe's window) to detect when the user has clicked somewhere
3211 // or focused somewhere.
3213 // Users should call registerIframe() instead of this method.
3215 // If specified this is the window associated with the iframe,
3216 // i.e. iframe.contentWindow.
3218 // If specified, report any focus events inside targetWindow as
3219 // an event on effectiveNode, rather than on evt.target.
3221 // Handle with remove() method to deregister.
3223 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
3226 var mousedownListener = function(evt
){
3227 _this
._justMouseDowned
= true;
3228 setTimeout(function(){ _this
._justMouseDowned
= false; }, 0);
3230 // workaround weird IE bug where the click is on an orphaned node
3231 // (first time clicking a Select/DropDownButton inside a TooltipDialog)
3232 if(has("ie") && evt
&& evt
.srcElement
&& evt
.srcElement
.parentNode
== null){
3236 _this
._onTouchNode(effectiveNode
|| evt
.target
|| evt
.srcElement
, "mouse");
3239 // Listen for blur and focus events on targetWindow's document.
3240 // Using attachEvent()/addEventListener() rather than on() to try to catch mouseDown events even
3241 // if other code calls evt.stopPropagation(). But rethink for 2.0 since that doesn't work for attachEvent(),
3242 // which watches events at the bubbling phase rather than capturing phase, like addEventListener(..., false).
3243 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
3244 // (at least for FF) the focus event doesn't fire on <html> or <body>.
3245 var doc
= has("ie") ? targetWindow
.document
.documentElement
: targetWindow
.document
;
3248 targetWindow
.document
.body
.attachEvent('onmousedown', mousedownListener
);
3249 var focusinListener = function(evt
){
3250 // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
3251 // ignore those events
3252 var tag
= evt
.srcElement
.tagName
.toLowerCase();
3253 if(tag
== "#document" || tag
== "body"){ return; }
3255 // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
3256 // probably just ignore such an event as it will be handled by onmousedown handler above, but
3257 // leaving the code for now.
3258 if(a11y
.isTabNavigable(evt
.srcElement
)){
3259 _this
._onFocusNode(effectiveNode
|| evt
.srcElement
);
3261 _this
._onTouchNode(effectiveNode
|| evt
.srcElement
);
3264 doc
.attachEvent('onfocusin', focusinListener
);
3265 var focusoutListener = function(evt
){
3266 _this
._onBlurNode(effectiveNode
|| evt
.srcElement
);
3268 doc
.attachEvent('onfocusout', focusoutListener
);
3272 targetWindow
.document
.detachEvent('onmousedown', mousedownListener
);
3273 doc
.detachEvent('onfocusin', focusinListener
);
3274 doc
.detachEvent('onfocusout', focusoutListener
);
3275 doc
= null; // prevent memory leak (apparent circular reference via closure)
3279 doc
.body
.addEventListener('mousedown', mousedownListener
, true);
3280 doc
.body
.addEventListener('touchstart', mousedownListener
, true);
3281 var focusListener = function(evt
){
3282 _this
._onFocusNode(effectiveNode
|| evt
.target
);
3284 doc
.addEventListener('focus', focusListener
, true);
3285 var blurListener = function(evt
){
3286 _this
._onBlurNode(effectiveNode
|| evt
.target
);
3288 doc
.addEventListener('blur', blurListener
, true);
3292 doc
.body
.removeEventListener('mousedown', mousedownListener
, true);
3293 doc
.body
.removeEventListener('touchstart', mousedownListener
, true);
3294 doc
.removeEventListener('focus', focusListener
, true);
3295 doc
.removeEventListener('blur', blurListener
, true);
3296 doc
= null; // prevent memory leak (apparent circular reference via closure)
3303 _onBlurNode: function(/*DomNode*/ node
){
3305 // Called when focus leaves a node.
3306 // Usually ignored, _unless_ it *isn't* followed by touching another node,
3307 // which indicates that we tabbed off the last field on the page,
3308 // in which case every widget is marked inactive
3310 // If the blur event isn't followed by a focus event, it means the user clicked on something unfocusable,
3312 if(this._clearFocusTimer
){
3313 clearTimeout(this._clearFocusTimer
);
3315 this._clearFocusTimer
= setTimeout(lang
.hitch(this, function(){
3316 this.set("prevNode", this.curNode
);
3317 this.set("curNode", null);
3320 if(this._justMouseDowned
){
3321 // the mouse down caused a new widget to be marked as active; this blur event
3322 // is coming late, so ignore it.
3326 // If the blur event isn't followed by a focus or touch event then mark all widgets as inactive.
3327 if(this._clearActiveWidgetsTimer
){
3328 clearTimeout(this._clearActiveWidgetsTimer
);
3330 this._clearActiveWidgetsTimer
= setTimeout(lang
.hitch(this, function(){
3331 delete this._clearActiveWidgetsTimer
;
3336 _onTouchNode: function(/*DomNode*/ node
, /*String*/ by
){
3338 // Callback when node is focused or mouse-downed
3340 // The node that was touched.
3342 // "mouse" if the focus/touch was caused by a mouse down event
3344 // ignore the recent blurNode event
3345 if(this._clearActiveWidgetsTimer
){
3346 clearTimeout(this._clearActiveWidgetsTimer
);
3347 delete this._clearActiveWidgetsTimer
;
3350 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
3354 var popupParent
= domAttr
.get(node
, "dijitPopupParent");
3356 node
=registry
.byId(popupParent
).domNode
;
3357 }else if(node
.tagName
&& node
.tagName
.toLowerCase() == "body"){
3358 // is this the root of the document or just the root of an iframe?
3359 if(node
=== win
.body()){
3360 // node is the root of the main document
3363 // otherwise, find the iframe this node refers to (can't access it via parentNode,
3364 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
3365 node
=winUtils
.get(node
.ownerDocument
).frameElement
;
3367 // if this node is the root node of a widget, then add widget id to stack,
3368 // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
3369 // to support MenuItem)
3370 var id
= node
.getAttribute
&& node
.getAttribute("widgetId"),
3371 widget
= id
&& registry
.byId(id
);
3372 if(widget
&& !(by
== "mouse" && widget
.get("disabled"))){
3373 newStack
.unshift(id
);
3375 node
=node
.parentNode
;
3378 }catch(e
){ /* squelch */ }
3380 this._setStack(newStack
, by
);
3383 _onFocusNode: function(/*DomNode*/ node
){
3385 // Callback when node is focused
3391 if(node
.nodeType
== 9){
3392 // Ignore focus events on the document itself. This is here so that
3393 // (for example) clicking the up/down arrows of a spinner
3394 // (which don't get focus) won't cause that widget to blur. (FF issue)
3398 // There was probably a blur event right before this event, but since we have a new focus, don't
3399 // do anything with the blur
3400 if(this._clearFocusTimer
){
3401 clearTimeout(this._clearFocusTimer
);
3402 delete this._clearFocusTimer
;
3405 this._onTouchNode(node
);
3407 if(node
== this.curNode
){ return; }
3408 this.set("prevNode", this.curNode
);
3409 this.set("curNode", node
);
3412 _setStack: function(/*String[]*/ newStack
, /*String*/ by
){
3414 // The stack of active widgets has changed. Send out appropriate events and records new stack.
3416 // array of widget id's, starting from the top (outermost) widget
3418 // "mouse" if the focus/touch was caused by a mouse down event
3420 var oldStack
= this.activeStack
;
3421 this.set("activeStack", newStack
);
3423 // compare old stack to new stack to see how many elements they have in common
3424 for(var nCommon
=0; nCommon
<Math
.min(oldStack
.length
, newStack
.length
); nCommon
++){
3425 if(oldStack
[nCommon
] != newStack
[nCommon
]){
3431 // for all elements that have gone out of focus, set focused=false
3432 for(var i
=oldStack
.length
-1; i
>=nCommon
; i
--){
3433 widget
= registry
.byId(oldStack
[i
]);
3435 widget
._hasBeenBlurred
= true; // TODO: used by form widgets, should be moved there
3436 widget
.set("focused", false);
3437 if(widget
._focusManager
== this){
3440 this.emit("widget-blur", widget
, by
);
3444 // for all element that have come into focus, set focused=true
3445 for(i
=nCommon
; i
<newStack
.length
; i
++){
3446 widget
= registry
.byId(newStack
[i
]);
3448 widget
.set("focused", true);
3449 if(widget
._focusManager
== this){
3450 widget
._onFocus(by
);
3452 this.emit("widget-focus", widget
, by
);
3457 focus: function(node
){
3459 // Focus the specified node, suppressing errors if they occur
3461 try{ node
.focus(); }catch(e
){/*quiet*/}
3466 var singleton
= new FocusManager();
3468 // register top window and all the iframes it contains
3470 var handle
= singleton
.registerWin(winUtils
.get(win
.doc
));
3472 unload
.addOnWindowUnload(function(){
3473 if(handle
){ // because this gets called twice when doh.robot is running
3481 // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
3482 // as a function to set focus. Remove for 2.0.
3483 dijit
.focus = function(node
){
3484 singleton
.focus(node
); // indirection here allows dijit/_base/focus.js to override behavior
3486 for(var attr
in singleton
){
3487 if(!/^_/.test(attr
)){
3488 dijit
.focus
[attr
] = typeof singleton
[attr
] == "function" ? lang
.hitch(singleton
, attr
) : singleton
[attr
];
3491 singleton
.watch(function(attr
, oldVal
, newVal
){
3492 dijit
.focus
[attr
] = newVal
;
3499 'dojo/i18n':function(){
3500 define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json", "module"],
3501 function(dojo
, require
, has
, array
, config
, lang
, xhr
, json
, module
){
3506 has
.add("dojo-preload-i18n-Api",
3507 // if true, define the preload localizations machinery
3511 1 || has
.add("dojo-v1x-i18n-Api",
3512 // if true, define the v1.x i18n functions
3517 thisModule
= dojo
.i18n
=
3520 // This module implements the dojo/i18n! plugin and the v1.6- i18n API
3522 // We choose to include our own plugin to leverage functionality already contained in dojo
3523 // and thereby reduce the size of the plugin compared to various loader implementations. Also, this
3524 // allows foreign AMD loaders to be used without their plugins.
3528 // regexp for reconstructing the master bundle name from parts of the regexp match
3529 // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
3530 // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
3531 // nlsRe.exec("foo/bar/baz/nls/foo") gives:
3532 // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
3533 // so, if match[5] is blank, it means this is the top bundle definition.
3534 // courtesy of http://requirejs.org
3535 /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
3537 getAvailableLocales = function(
3544 // return a vector of module ids containing all available locales with respect to the target locale
3545 // For example, assuming:
3547 // - the root bundle indicates specific bundles for "fr" and "fr-ca",
3548 // - bundlePath is "myPackage/nls"
3549 // - bundleName is "myBundle"
3551 // Then a locale argument of "fr-ca" would return
3553 // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
3555 // Notice that bundles are returned least-specific to most-specific, starting with the root.
3557 // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
3558 // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
3560 for(var result
= [bundlePath
+ bundleName
], localeParts
= locale
.split("-"), current
= "", i
= 0; i
<localeParts
.length
; i
++){
3561 current
+= (current
? "-" : "") + localeParts
[i
];
3562 if(!root
|| root
[current
]){
3563 result
.push(bundlePath
+ current
+ "/" + bundleName
);
3571 getBundleName = function(moduleName
, bundleName
, locale
){
3572 locale
= locale
? locale
.toLowerCase() : dojo
.locale
;
3573 moduleName
= moduleName
.replace(/\./g, "/");
3574 bundleName
= bundleName
.replace(/\./g, "/");
3575 return (/root/i.test(locale
)) ?
3576 (moduleName
+ "/nls/" + bundleName
) :
3577 (moduleName
+ "/nls/" + locale
+ "/" + bundleName
);
3580 getL10nName
= dojo
.getL10nName = function(moduleName
, bundleName
, locale
){
3581 return moduleName
= module
.id
+ "!" + getBundleName(moduleName
, bundleName
, locale
);
3584 doLoad = function(require
, bundlePathAndName
, bundlePath
, bundleName
, locale
, load
){
3586 // get the root bundle which instructs which other bundles are required to construct the localized bundle
3587 require([bundlePathAndName
], function(root
){
3588 var current
= lang
.clone(root
.root
),
3589 availableLocales
= getAvailableLocales(!root
._v1x
&& root
, locale
, bundlePath
, bundleName
);
3590 require(availableLocales
, function(){
3591 for (var i
= 1; i
<availableLocales
.length
; i
++){
3592 current
= lang
.mixin(lang
.clone(current
), arguments
[i
]);
3594 // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
3595 var target
= bundlePathAndName
+ "/" + locale
;
3596 cache
[target
] = current
;
3602 normalize = function(id
, toAbsMid
){
3604 // id may be relative.
3605 // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and
3606 // therefore never looks like a relative
3607 return /^\./.test(id
) ? toAbsMid(id
) : id
;
3610 getLocalesToLoad = function(targetLocale
){
3611 var list
= config
.extraLocale
|| [];
3612 list
= lang
.isArray(list
) ? list
: [list
];
3613 list
.push(targetLocale
);
3617 load = function(id
, require
, load
){
3619 // id is in one of the following formats
3621 // 1. <path>/nls/<bundle>
3622 // => load the bundle, localized to config.locale; load all bundles localized to
3623 // config.extraLocale (if any); return the loaded bundle localized to config.locale.
3625 // 2. <path>/nls/<locale>/<bundle>
3626 // => load then return the bundle localized to <locale>
3628 // 3. *preload*<path>/nls/<module>*<JSON array of available locales>
3629 // => for config.locale and all config.extraLocale, load all bundles found
3630 // in the best-matching bundle rollup. A value of 1 is returned, which
3631 // is meaningless other than to say the plugin is executing the requested
3634 // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
3635 // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder.
3637 // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
3638 // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
3640 // <path>/nls/<bundle>/<locale>
3642 // will hold the value. Similarly, then plugin will publish this value to the loader by
3644 // define("<path>/nls/<bundle>/<locale>", <bundle-value>);
3646 // Given this algorithm, other machinery can provide fast load paths be preplacing
3647 // values in the plugin's cache, which is public. When a load is demanded the
3648 // cache is inspected before starting any loading. Explicitly placing values in the plugin
3649 // cache is an advanced/experimental feature that should not be needed; use at your own risk.
3651 // For the normal AMD algorithm, the root bundle is loaded first, which instructs the
3652 // plugin what additional localized bundles are required for a particular locale. These
3653 // additional locales are loaded and a mix of the root and each progressively-specific
3654 // locale is returned. For example:
3656 // 1. The client demands "dojo/i18n!some/path/nls/someBundle
3658 // 2. The loader demands load(some/path/nls/someBundle)
3660 // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
3662 // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
3663 // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
3664 // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
3666 // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
3669 // mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
3670 // require("some/path/nls/ab/someBundle")),
3671 // require("some/path/nls/ab-cd-ef/someBundle"));
3673 // This value is inserted into the cache and published to the loader at the
3674 // key/module-id some/path/nls/someBundle/ab-cd-ef.
3676 // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
3677 // (further preload requests will be serviced) until all ongoing preloading has completed.
3679 // The preload signature instructs the plugin that a special rollup module is available that contains
3680 // one or more flattened, localized bundles. The JSON array of available locales indicates which locales
3681 // are available. Here is an example:
3683 // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
3685 // This indicates the following rollup modules are available:
3687 // some/path/nls/someModule_ROOT
3688 // some/path/nls/someModule_ab
3689 // some/path/nls/someModule_ab-cd-ef
3691 // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
3692 // For example, assume someModule contained the bundles some/bundle/path/someBundle and
3693 // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows:
3696 // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
3697 // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
3700 // E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
3702 // require(["some/path/nls/someModule_ab"], function(rollup){
3703 // for(var p in rollup){
3704 // var id = p + "/ab",
3705 // cache[id] = rollup[p];
3706 // define(id, rollup[p]);
3710 // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
3711 // load accordingly.
3713 // The builder will write such rollups for every layer if a non-empty localeList profile property is
3714 // provided. Further, the builder will include the following cache entry in the cache associated with
3717 // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
3719 // The *now special cache module instructs the loader to apply the provided function to context-require
3720 // with respect to the particular layer being defined. This causes the plugin to hold all normal service
3721 // requests until all preloading is complete.
3723 // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
3724 // where the target locale has a single segment and a layer depends on a single bundle:
3726 // Without Preloads:
3728 // 1. Layer loads root bundle.
3729 // 2. bundle is demanded; plugin loads single localized bundle.
3733 // 1. Layer causes preloading of target bundle.
3734 // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
3736 // In each case a single transaction is required to load the target bundle. In cases where multiple bundles
3737 // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
3738 // the normal path requires an additional transaction for each additional bundle/locale-segment. However all
3739 // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
3740 // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
3742 if(has("dojo-preload-i18n-Api")){
3743 var split
= id
.split("*"),
3744 preloadDemand
= split
[1] == "preload";
3747 // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
3748 // who knows what over-aggressive human optimizers may attempt
3750 preloadL10n(split
[2], json
.parse(split
[3]), 1, require
);
3752 // don't stall the loader!
3755 if(preloadDemand
|| waitForPreloads(id
, require
, load
)){
3760 var match
= nlsRe
.exec(id
),
3761 bundlePath
= match
[1] + "/",
3762 bundleName
= match
[5] || match
[4],
3763 bundlePathAndName
= bundlePath
+ bundleName
,
3764 localeSpecified
= (match
[5] && match
[4]),
3765 targetLocale
= localeSpecified
|| dojo
.locale
,
3766 loadTarget
= bundlePathAndName
+ "/" + targetLocale
,
3767 loadList
= localeSpecified
? [targetLocale
] : getLocalesToLoad(targetLocale
),
3768 remaining
= loadList
.length
,
3769 finish = function(){
3771 load(lang
.delegate(cache
[loadTarget
]));
3774 array
.forEach(loadList
, function(locale
){
3775 var target
= bundlePathAndName
+ "/" + locale
;
3776 if(has("dojo-preload-i18n-Api")){
3777 checkForLegacyModules(target
);
3780 doLoad(require
, bundlePathAndName
, bundlePath
, bundleName
, locale
, finish
);
3787 if(has("dojo-unit-tests")){
3788 var unitTests
= thisModule
.unitTests
= [];
3791 if(has("dojo-preload-i18n-Api") || 1 ){
3792 var normalizeLocale
= thisModule
.normalizeLocale = function(locale
){
3793 var result
= locale
? locale
.toLowerCase() : dojo
.locale
;
3794 return result
== "root" ? "ROOT" : result
;
3797 isXd = function(mid
, contextRequire
){
3799 contextRequire
.isXdUrl(require
.toUrl(mid
+ ".js")) :
3805 preloadWaitQueue
= [],
3807 preloadL10n
= thisModule
._preloadLocalizations = function(/*String*/bundlePrefix
, /*Array*/localesGenerated
, /*boolean?*/ guaranteedAmdFormat
, /*function?*/ contextRequire
){
3809 // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any)
3811 // Only called by built layer files. The entire locale hierarchy is loaded. For example,
3812 // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
3813 // in that the v1.6- would only load ab-cd...which was *always* flattened.
3815 // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
3816 // and the extra possible extra transaction.
3818 // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function
3819 // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which
3820 // itself may have been mapped.
3821 contextRequire
= contextRequire
|| require
;
3823 function doRequire(mid
, callback
){
3824 if(isXd(mid
, contextRequire
) || guaranteedAmdFormat
){
3825 contextRequire([mid
], callback
);
3827 syncRequire([mid
], callback
, contextRequire
);
3831 function forEachLocale(locale
, func
){
3832 // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
3833 var parts
= locale
.split("-");
3834 while(parts
.length
){
3835 if(func(parts
.join("-"))){
3843 function preload(locale
){
3844 locale
= normalizeLocale(locale
);
3845 forEachLocale(locale
, function(loc
){
3846 if(array
.indexOf(localesGenerated
, loc
)>=0){
3847 var mid
= bundlePrefix
.replace(/\./g, "/")+"_"+loc
;
3849 doRequire(mid
, function(rollup
){
3850 for(var p
in rollup
){
3851 cache
[require
.toAbsMid(p
) + "/" + loc
] = rollup
[p
];
3854 while(!preloading
&& preloadWaitQueue
.length
){
3855 load
.apply(null, preloadWaitQueue
.shift());
3865 array
.forEach(dojo
.config
.extraLocale
, preload
);
3868 waitForPreloads = function(id
, require
, load
){
3870 preloadWaitQueue
.push([id
, require
, load
]);
3875 checkForLegacyModules = function()
3880 // this code path assumes the dojo loader and won't work with a standard AMD loader
3883 // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
3885 "__bundle", // the bundle to evalutate
3886 "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
3887 "__mid", // the mid that __bundle is intended to define
3891 // 1 => the bundle was an AMD bundle
3892 // a legacy bundle object that is the value of __mid
3893 // instance of Error => could not figure out how to evaluate bundle
3895 // used to detect when __bundle calls define
3896 "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;},"
3897 + " require = function(){define.called = 1;};"
3900 + "define.called = 0;"
3902 + "if(define.called==1)"
3903 // bundle called define; therefore signal it's an AMD bundle
3904 + "return __amdValue;"
3906 + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
3907 // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
3908 + "return __checkForLegacyModules;"
3911 // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
3912 // either way, re-eval *after* surrounding with parentheses
3915 + "return eval('('+__bundle+')');"
3921 syncRequire = function(deps
, callback
, require
){
3923 array
.forEach(deps
, function(mid
){
3924 var url
= require
.toUrl(mid
+ ".js");
3926 function load(text
){
3927 var result
= evalBundle(text
, checkForLegacyModules
, mid
, amdValue
);
3928 if(result
===amdValue
){
3929 // the bundle was an AMD module; re-inject it through the normal AMD path
3930 // we gotta do this since it could be an anonymous module and simply evaluating
3931 // the text here won't provide the loader with the context to know what
3932 // module is being defined()'d. With browser caching, this should be free; further
3933 // this entire code path can be circumvented by using the AMD format to begin with
3934 results
.push(cache
[url
] = amdValue
.result
);
3936 if(result
instanceof Error
){
3937 console
.error("failed to evaluate i18n bundle; url=" + url
, result
);
3940 // nls/<locale>/<bundle-name> indicates not the root.
3941 results
.push(cache
[url
] = (/nls\/[^\/]+\/[^\/]+$/.test(url
) ? result
: {root
:result
, _v1x
:1}));
3946 results
.push(cache
[url
]);
3948 var bundle
= require
.syncLoadNls(mid
);
3949 // don't need to check for legacy since syncLoadNls returns a module if the module
3950 // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
3951 // from getLocalization --> load, then load will have called checkForLegacyModules() before
3952 // calling syncRequire; if syncRequire is called from preloadLocalizations, then we
3953 // don't care about checkForLegacyModules() because that will be done when a particular
3954 // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
3955 // because cached modules are always v1.7+ built modules.
3957 results
.push(bundle
);
3961 require
.getText(url
, true, load
);
3963 results
.push(cache
[url
] = {});
3971 results
.push(cache
[url
] = {});
3978 callback
&& callback
.apply(null, results
);
3981 checkForLegacyModules = function(target
){
3982 // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
3983 for(var result
, names
= target
.split("/"), object
= dojo
.global
[names
[0]], i
= 1; object
&& i
<names
.length
-1; object
= object
[names
[i
++]]){}
3985 result
= object
[names
[i
]];
3987 // fallback for incorrect bundle build of 1.6
3988 result
= object
[names
[i
].replace(/-/g
,"_")];
3991 cache
[target
] = result
;
3997 thisModule
.getLocalization = function(moduleName
, bundleName
, locale
){
3999 l10nName
= getBundleName(moduleName
, bundleName
, locale
);
4003 // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module.
4004 // Since this legacy function does not have the concept of a reference module, resolve with respect to this
4005 // dojo/i18n module, which, itself may have been mapped.
4006 (!isXd(l10nName
, require
) ? function(deps
, callback
){ syncRequire(deps
, callback
, require
); } : require
),
4008 function(result_
){ result
= result_
; }
4013 if(has("dojo-unit-tests")){
4014 unitTests
.push(function(doh
){
4015 doh
.register("tests.i18n.unit", function(t
){
4018 check
= evalBundle("{prop:1}", checkForLegacyModules
, "nonsense", amdValue
);
4019 t
.is({prop
:1}, check
); t
.is(undefined, check
[1]);
4021 check
= evalBundle("({prop:1})", checkForLegacyModules
, "nonsense", amdValue
);
4022 t
.is({prop
:1}, check
); t
.is(undefined, check
[1]);
4024 check
= evalBundle("{'prop-x':1}", checkForLegacyModules
, "nonsense", amdValue
);
4025 t
.is({'prop-x':1}, check
); t
.is(undefined, check
[1]);
4027 check
= evalBundle("({'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4028 t
.is({'prop-x':1}, check
); t
.is(undefined, check
[1]);
4030 check
= evalBundle("define({'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4031 t
.is(amdValue
, check
); t
.is({'prop-x':1}, amdValue
.result
);
4033 check
= evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4034 t
.is(amdValue
, check
); t
.is({'prop-x':1}, amdValue
.result
);
4036 check
= evalBundle("this is total nonsense and should throw an error", checkForLegacyModules
, "nonsense", amdValue
);
4037 t
.is(check
instanceof Error
, true);
4043 return lang
.mixin(thisModule
, {
4045 normalize
:normalize
,
4052 'dijit/hccss':function(){
4053 define("dijit/hccss", ["dojo/dom-class", "dojo/hccss", "dojo/ready", "dojo/_base/window"], function(domClass
, has
, ready
, win
){
4061 // Test if computer is in high contrast mode, and sets `dijit_a11y` flag on `<body>` if it is.
4062 // Deprecated, use ``dojo/hccss`` instead.
4066 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
4067 // change this module to depend on dojo/domReady!
4068 ready(90, function(){
4069 if(has("highcontrast")){
4070 domClass
.add(win
.body(), "dijit_a11y");
4078 'dijit/tree/ForestStoreModel':function(){
4079 define("dijit/tree/ForestStoreModel", [
4080 "dojo/_base/array", // array.indexOf array.some
4081 "dojo/_base/declare", // declare
4082 "dojo/_base/kernel", // global
4083 "dojo/_base/lang", // lang.hitch
4085 ], function(array
, declare
, kernel
, lang
, TreeStoreModel
){
4088 // dijit/tree/ForestStoreModel
4090 return declare("dijit.tree.ForestStoreModel", TreeStoreModel
, {
4092 // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4093 // a.k.a. a store that has multiple "top level" items.
4096 // Use this class to wrap a dojo.data store, making all the items matching the specified query
4097 // appear as children of a fabricated "root item". If no query is specified then all the
4098 // items returned by fetch() on the underlying store become children of the root item.
4099 // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
4101 // When using this class the developer must override a number of methods according to their app and
4110 // Parameters to constructor
4113 // ID of fabricated root item
4116 // rootLabel: String
4117 // Label of fabricated root item
4121 // Specifies the set of children of the root item.
4123 // | {type:'continent'}
4126 // End of parameters to constructor
4128 constructor: function(params
){
4130 // Sets up variables, etc.
4134 // Make dummy root item
4139 label
: params
.rootLabel
,
4140 children
: params
.rootChildren
// optional param
4144 // =======================================================================
4145 // Methods for traversing hierarchy
4147 mayHaveChildren: function(/*dojo/data/Item*/ item
){
4149 // Tells if an item has or may have children. Implementing logic here
4150 // avoids showing +/- expando icon for nodes that we know don't have children.
4151 // (For efficiency reasons we may not want to check if an element actually
4152 // has children until user clicks the expando node)
4155 return item
=== this.root
|| this.inherited(arguments
);
4158 getChildren: function(/*dojo/data/Item*/ parentItem
, /*function(items)*/ callback
, /*function*/ onError
){
4160 // Calls onComplete() with array of child items of given parent item, all loaded.
4161 if(parentItem
=== this.root
){
4162 if(this.root
.children
){
4163 // already loaded, just return
4164 callback(this.root
.children
);
4168 onComplete
: lang
.hitch(this, function(items
){
4169 this.root
.children
= items
;
4176 this.inherited(arguments
);
4180 // =======================================================================
4183 isItem: function(/* anything */ something
){
4184 return (something
=== this.root
) ? true : this.inherited(arguments
);
4187 fetchItemByIdentity: function(/* object */ keywordArgs
){
4188 if(keywordArgs
.identity
== this.root
.id
){
4189 var scope
= keywordArgs
.scope
|| kernel
.global
;
4190 if(keywordArgs
.onItem
){
4191 keywordArgs
.onItem
.call(scope
, this.root
);
4194 this.inherited(arguments
);
4198 getIdentity: function(/* item */ item
){
4199 return (item
=== this.root
) ? this.root
.id
: this.inherited(arguments
);
4202 getLabel: function(/* item */ item
){
4203 return (item
=== this.root
) ? this.root
.label
: this.inherited(arguments
);
4206 // =======================================================================
4209 newItem: function(/* dijit/tree/dndSource.__Item */ args
, /*Item*/ parent
, /*int?*/ insertIndex
){
4211 // Creates a new item. See dojo/data/api/Write for details on args.
4212 // Used in drag & drop when item from external source dropped onto tree.
4213 if(parent
=== this.root
){
4214 this.onNewRootItem(args
);
4215 return this.store
.newItem(args
);
4217 return this.inherited(arguments
);
4221 onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
4223 // User can override this method to modify a new element that's being
4224 // added to the root of the tree, for example to add a flag like root=true
4227 pasteItem: function(/*Item*/ childItem
, /*Item*/ oldParentItem
, /*Item*/ newParentItem
, /*Boolean*/ bCopy
, /*int?*/ insertIndex
){
4229 // Move or copy an item from one parent item to another.
4230 // Used in drag & drop
4231 if(oldParentItem
=== this.root
){
4233 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
4234 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4235 // that this element is no longer a child of the root node
4236 this.onLeaveRoot(childItem
);
4239 this.inherited(arguments
, [childItem
,
4240 oldParentItem
=== this.root
? null : oldParentItem
,
4241 newParentItem
=== this.root
? null : newParentItem
,
4245 if(newParentItem
=== this.root
){
4246 // It's onAddToRoot()'s responsibility to modify the item so it matches
4247 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4248 // that this element is now a child of the root node
4249 this.onAddToRoot(childItem
);
4253 // =======================================================================
4254 // Handling for top level children
4256 onAddToRoot: function(/* item */ item
){
4258 // Called when item added to root of tree; user must override this method
4259 // to modify the item so that it matches the query for top level items
4261 // | store.setValue(item, "root", true);
4264 console
.log(this, ": item ", item
, " added to root");
4267 onLeaveRoot: function(/* item */ item
){
4269 // Called when item removed from root of tree; user must override this method
4270 // to modify the item so it doesn't match the query for top level items
4272 // | store.unsetAttribute(item, "root");
4275 console
.log(this, ": item ", item
, " removed from root");
4278 // =======================================================================
4279 // Events from data store
4281 _requeryTop: function(){
4282 // reruns the query for the children of the root node,
4283 // sending out an onSet notification if those children have changed
4284 var oldChildren
= this.root
.children
|| [];
4287 onComplete
: lang
.hitch(this, function(newChildren
){
4288 this.root
.children
= newChildren
;
4290 // If the list of children or the order of children has changed...
4291 if(oldChildren
.length
!= newChildren
.length
||
4292 array
.some(oldChildren
, function(item
, idx
){ return newChildren
[idx
] != item
;})){
4293 this.onChildrenChange(this.root
, newChildren
);
4299 onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo
){
4301 // Handler for when new items appear in the store. Developers should override this
4302 // method to be more efficient based on their app/data.
4304 // Note that the default implementation requeries the top level items every time
4305 // a new item is created, since any new item could be a top level item (even in
4306 // addition to being a child of another item, since items can have multiple parents).
4308 // If developers can detect which items are possible top level items (based on the item and the
4309 // parentInfo parameters), they should override this method to only call _requeryTop() for top
4310 // level items. Often all top level items have parentInfo==null, but
4311 // that will depend on which store you use and what your data is like.
4316 this.inherited(arguments
);
4319 onDeleteItem: function(/*Object*/ item
){
4321 // Handler for delete notifications from underlying store
4323 // check if this was a child of root, and if so send notification that root's children
4325 if(array
.indexOf(this.root
.children
, item
) != -1){
4329 this.inherited(arguments
);
4332 onSetItem: function(/* item */ item
,
4333 /* attribute-name-string */ attribute
,
4334 /* Object|Array */ oldValue
,
4335 /* Object|Array */ newValue
){
4337 // Updates the tree view according to changes to an item in the data store.
4338 // Developers should override this method to be more efficient based on their app/data.
4340 // Handles updates to an item's children by calling onChildrenChange(), and
4341 // other updates to an item by calling onChange().
4343 // Also, any change to any item re-executes the query for the tree's top-level items,
4344 // since this modified item may have started/stopped matching the query for top level items.
4346 // If possible, developers should override this function to only call _requeryTop() when
4347 // the change to the item has caused it to stop/start being a top level item in the tree.
4352 this.inherited(arguments
);
4360 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n",
4361 'dijit/form/_ComboBoxMenuMixin':function(){
4362 define("dijit/form/_ComboBoxMenuMixin", [
4363 "dojo/_base/array", // array.forEach
4364 "dojo/_base/declare", // declare
4365 "dojo/dom-attr", // domAttr.set
4366 "dojo/i18n", // i18n.getLocalization
4367 "dojo/i18n!./nls/ComboBox"
4368 ], function(array
, declare
, domAttr
, i18n
){
4371 // dijit/form/_ComboBoxMenuMixin
4373 return declare( "dijit.form._ComboBoxMenuMixin", null, {
4375 // Focus-less menu for internal use in `dijit/form/ComboBox`
4379 // _messages: Object
4380 // Holds "next" and "previous" text for paging buttons on drop down
4383 postMixInProperties: function(){
4384 this.inherited(arguments
);
4385 this._messages
= i18n
.getLocalization("dijit.form", "ComboBox", this.lang
);
4388 buildRendering: function(){
4389 this.inherited(arguments
);
4391 // fill in template with i18n messages
4392 this.previousButton
.innerHTML
= this._messages
["previousMessage"];
4393 this.nextButton
.innerHTML
= this._messages
["nextMessage"];
4396 _setValueAttr: function(/*Object*/ value
){
4398 this.onChange(value
);
4401 onClick: function(/*DomNode*/ node
){
4402 if(node
== this.previousButton
){
4403 this._setSelectedAttr(null);
4405 }else if(node
== this.nextButton
){
4406 this._setSelectedAttr(null);
4409 this.onChange(node
);
4414 onChange: function(/*Number*/ /*===== direction =====*/){
4416 // Notifies ComboBox/FilteringSelect that user selected an option.
4421 onPage: function(/*Number*/ /*===== direction =====*/){
4423 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
4428 onClose: function(){
4430 // Callback from dijit.popup code to this widget, notifying it that it closed
4433 this._setSelectedAttr(null);
4436 _createOption: function(/*Object*/ item
, labelFunc
){
4438 // Creates an option to appear on the popup menu subclassed by
4439 // `dijit/form/FilteringSelect`.
4441 var menuitem
= this._createMenuItem();
4442 var labelObject
= labelFunc(item
);
4443 if(labelObject
.html
){
4444 menuitem
.innerHTML
= labelObject
.label
;
4446 menuitem
.appendChild(
4447 menuitem
.ownerDocument
.createTextNode(labelObject
.label
)
4450 // #3250: in blank options, assign a normal height
4451 if(menuitem
.innerHTML
== ""){
4452 menuitem
.innerHTML
= " "; //
4455 // update menuitem.dir if BidiSupport was required
4456 this.applyTextDir(menuitem
, (menuitem
.innerText
|| menuitem
.textContent
|| ""));
4461 createOptions: function(results
, options
, labelFunc
){
4463 // Fills in the items in the drop down list
4467 // The options to the query function of the store
4470 // Function to produce a label in the drop down list from a dojo.data item
4472 this.items
= results
;
4474 // display "Previous . . ." button
4475 this.previousButton
.style
.display
= (options
.start
== 0) ? "none" : "";
4476 domAttr
.set(this.previousButton
, "id", this.id
+ "_prev");
4477 // create options using _createOption function defined by parent
4478 // ComboBox (or FilteringSelect) class
4480 // iterate over cache nondestructively
4481 array
.forEach(results
, function(item
, i
){
4482 var menuitem
= this._createOption(item
, labelFunc
);
4483 menuitem
.setAttribute("item", i
); // index to this.items; use indirection to avoid mem leak
4484 domAttr
.set(menuitem
, "id", this.id
+ i
);
4485 this.nextButton
.parentNode
.insertBefore(menuitem
, this.nextButton
);
4487 // display "Next . . ." button
4488 var displayMore
= false;
4489 // Try to determine if we should show 'more'...
4490 if(results
.total
&& !results
.total
.then
&& results
.total
!= -1){
4491 if((options
.start
+ options
.count
) < results
.total
){
4493 }else if((options
.start
+ options
.count
) > results
.total
&& options
.count
== results
.length
){
4494 // Weird return from a data store, where a start + count > maxOptions
4495 // implies maxOptions isn't really valid and we have to go into faking it.
4496 // And more or less assume more if count == results.length
4499 }else if(options
.count
== results
.length
){
4500 //Don't know the size, so we do the best we can based off count alone.
4501 //So, if we have an exact match to count, assume more.
4505 this.nextButton
.style
.display
= displayMore
? "" : "none";
4506 domAttr
.set(this.nextButton
,"id", this.id
+ "_next");
4509 clearResultList: function(){
4511 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
4512 var container
= this.containerNode
;
4513 while(container
.childNodes
.length
> 2){
4514 container
.removeChild(container
.childNodes
[container
.childNodes
.length
-2]);
4516 this._setSelectedAttr(null);
4519 highlightFirstOption: function(){
4521 // Highlight the first real item in the list (not Previous Choices).
4522 this.selectFirstNode();
4525 highlightLastOption: function(){
4527 // Highlight the last real item in the list (not More Choices).
4528 this.selectLastNode();
4531 selectFirstNode: function(){
4532 this.inherited(arguments
);
4533 if(this.getHighlightedOption() == this.previousButton
){
4534 this.selectNextNode();
4538 selectLastNode: function(){
4539 this.inherited(arguments
);
4540 if(this.getHighlightedOption() == this.nextButton
){
4541 this.selectPreviousNode();
4545 getHighlightedOption: function(){
4546 return this.selected
;
4553 'dijit/form/_SearchMixin':function(){
4554 define("dijit/form/_SearchMixin", [
4555 "dojo/data/util/filter", // patternToRegExp
4556 "dojo/_base/declare", // declare
4557 "dojo/_base/event", // event.stop
4558 "dojo/keys", // keys
4559 "dojo/_base/lang", // lang.clone lang.hitch
4560 "dojo/query", // query
4561 "dojo/sniff", // has("ie")
4562 "dojo/string", // string.substitute
4564 "../registry" // registry.byId
4565 ], function(filter
, declare
, event
, keys
, lang
, query
, has
, string
, when
, registry
){
4568 // dijit/form/_SearchMixin
4571 return declare("dijit.form._SearchMixin", null, {
4573 // A mixin that implements the base functionality to search a store based upon user-entered text such as
4574 // with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
4578 // pageSize: Integer
4579 // Argument to data provider.
4580 // Specifies maximum number of search results to return per query
4583 // store: [const] dojo/store/api/Store
4584 // Reference to data provider object used by this ComboBox.
4585 // The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
4588 // fetchProperties: Object
4589 // Mixin to the store's fetch.
4590 // For example, to set the sort order of the ComboBox menu, pass:
4591 // | { sort: [{attribute:"name",descending: true}] }
4592 // To override the default queryOptions so that deep=false, do:
4593 // | { queryOptions: {ignoreCase: true, deep: false} }
4597 // A query that can be passed to `store` to initially filter the items.
4598 // ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
4601 // searchDelay: Integer
4602 // Delay in milliseconds between when user types something and we start
4603 // searching based on that value
4606 // searchAttr: String
4607 // Search for items in the data store where this attribute (in the item)
4608 // matches what the user typed
4611 // queryExpr: String
4612 // This specifies what query is sent to the data store,
4613 // based on what the user has typed. Changing this expression will modify
4614 // whether the results are only exact matches, a "starting with" match,
4616 // dojo.data query expression pattern.
4617 // `${0}` will be substituted for the user text.
4618 // `*` is used for wildcards.
4619 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
4622 // ignoreCase: Boolean
4623 // Set true if the query should ignore case when matching possible items
4626 _abortQuery: function(){
4627 // stop in-progress query
4628 if(this.searchTimer
){
4629 this.searchTimer
= this.searchTimer
.remove();
4631 if(this._queryDeferHandle
){
4632 this._queryDeferHandle
= this._queryDeferHandle
.remove();
4634 if(this._fetchHandle
){
4635 if(this._fetchHandle
.abort
){
4636 this._cancelingQuery
= true;
4637 this._fetchHandle
.abort();
4638 this._cancelingQuery
= false;
4640 if(this._fetchHandle
.cancel
){
4641 this._cancelingQuery
= true;
4642 this._fetchHandle
.cancel();
4643 this._cancelingQuery
= false;
4645 this._fetchHandle
= null;
4649 _processInput: function(/*Event*/ evt
){
4651 // Handles input (keyboard/paste) events
4652 if(this.disabled
|| this.readOnly
){ return; }
4653 var key
= evt
.charOrCode
;
4655 // except for cutting/pasting case - ctrl + x/v
4656 if(evt
.altKey
|| ((evt
.ctrlKey
|| evt
.metaKey
) && (key
!= 'x' && key
!= 'v')) || key
== keys
.SHIFT
){
4657 return; // throw out weird key combinations and spurious events
4660 var doSearch
= false;
4661 this._prev_key_backspace
= false;
4665 case keys
.BACKSPACE
:
4666 this._prev_key_backspace
= true;
4667 this._maskValidSubsetError
= true;
4672 // Non char keys (F1-F12 etc..) shouldn't start a search..
4673 // Ascii characters and IME input (Chinese, Japanese etc.) should.
4674 //IME input produces keycode == 229.
4675 doSearch
= typeof key
== 'string' || key
== 229;
4678 // need to wait a tad before start search so that the event
4679 // bubbles through DOM and we have value visible
4683 this.searchTimer
= this.defer("_startSearchFromInput", 1);
4688 onSearch: function(/*===== results, query, options =====*/){
4690 // Callback when a search completes.
4693 // An array of items from the originating _SearchMixin's store.
4696 // A copy of the originating _SearchMixin's query property.
4699 // The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
4705 _startSearchFromInput: function(){
4706 this._startSearch(this.focusNode
.value
.replace(/([\\\*\?])/g, "\\$1"));
4709 _startSearch: function(/*String*/ text
){
4711 // Starts a search for elements matching text (text=="" means to return all items),
4712 // and calls onSearch(...) when the search completes, to display the results.
4717 // Setup parameters to be passed to store.query().
4718 // Create a new query to prevent accidentally querying for a hidden
4719 // value from FilteringSelect's keyField
4720 query
= lang
.clone(this.query
), // #5970
4723 count
: this.pageSize
,
4724 queryOptions
: { // remove for 2.0
4725 ignoreCase
: this.ignoreCase
,
4729 qs
= string
.substitute(this.queryExpr
, [text
]),
4731 startQuery = function(){
4732 var resPromise
= _this
._fetchHandle
= _this
.store
.query(query
, options
);
4733 if(_this
.disabled
|| _this
.readOnly
|| (q
!== _this
._lastQuery
)){
4735 } // avoid getting unwanted notify
4736 when(resPromise
, function(res
){
4737 _this
._fetchHandle
= null;
4738 if(!_this
.disabled
&& !_this
.readOnly
&& (q
=== _this
._lastQuery
)){ // avoid getting unwanted notify
4739 when(resPromise
.total
, function(total
){
4741 var pageSize
= _this
.pageSize
;
4742 if(isNaN(pageSize
) || pageSize
> res
.total
){ pageSize
= res
.total
; }
4743 // Setup method to fetching the next page of results
4744 res
.nextPage = function(direction
){
4745 // tell callback the direction of the paging so the screen
4746 // reader knows which menu option to shout
4747 options
.direction
= direction
= direction
!== false;
4748 options
.count
= pageSize
;
4750 options
.start
+= res
.length
;
4751 if(options
.start
>= res
.total
){
4755 options
.start
-= pageSize
;
4756 if(options
.start
< 0){
4757 options
.count
= Math
.max(pageSize
+ options
.start
, 0);
4761 if(options
.count
<= 0){
4763 _this
.onSearch(res
, query
, options
);
4768 _this
.onSearch(res
, query
, options
);
4772 _this
._fetchHandle
= null;
4773 if(!_this
._cancelingQuery
){ // don't treat canceled query as an error
4774 console
.error(_this
.declaredClass
+ ' ' + err
.toString());
4779 lang
.mixin(options
, this.fetchProperties
);
4782 if(this.store
._oldAPI
){
4783 // remove this branch for 2.0
4786 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
4787 // but with a toString() method to help dojo/store/JsonRest.
4788 // Search string like "Co*" converted to regex like /^Co.*$/i.
4789 q
= filter
.patternToRegExp(qs
, this.ignoreCase
);
4790 q
.toString = function(){ return qs
; };
4793 // set _lastQuery, *then* start the timeout
4794 // otherwise, if the user types and the last query returns before the timeout,
4795 // _lastQuery won't be set and their input gets rewritten
4796 this._lastQuery
= query
[this.searchAttr
] = q
;
4797 this._queryDeferHandle
= this.defer(startQuery
, this.searchDelay
);
4800 //////////// INITIALIZATION METHODS ///////////////////////////////////////
4802 constructor: function(){
4804 this.fetchProperties
={};
4807 postMixInProperties: function(){
4809 var list
= this.list
;
4811 this.store
= registry
.byId(list
);
4814 this.inherited(arguments
);
4820 'dojo/parser':function(){
4822 "dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window",
4823 "./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"],
4824 function(require
, dojo
, dlang
, darray
, config
, dhtml
, dwindow
, _Url
, djson
, aspect
, dates
, Deferred
, has
, query
, don
, ready
){
4829 new Date("X"); // workaround for #11279, new Date("") == NaN
4832 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
4833 // If BorderContainer is loaded after _Widget's parameter list has been cached,
4834 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
4836 aspect
.after(dlang
, "extend", function(){
4840 function getNameMap(ctor
){
4842 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
4843 var map
= ctor
._nameCaseMap
, proto
= ctor
.prototype;
4845 // Create the map if it's undefined.
4846 // Refresh the map if a superclass was possibly extended with new methods since the map was created.
4847 if(!map
|| map
._extendCnt
< extendCnt
){
4848 map
= ctor
._nameCaseMap
= {};
4849 for(var name
in proto
){
4850 if(name
.charAt(0) === "_"){ continue; } // skip internal properties
4851 map
[name
.toLowerCase()] = name
;
4853 map
._extendCnt
= extendCnt
;
4858 // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor.
4861 function getCtor(/*String[]*/ types
){
4863 // Retrieves a constructor. If the types array contains more than one class/MID then the
4864 // subsequent classes will be mixed into the first class and a unique constructor will be
4865 // returned for that array.
4867 var ts
= types
.join();
4870 for(var i
= 0, l
= types
.length
; i
< l
; i
++){
4872 // TODO: Consider swapping getObject and require in the future
4873 mixins
[mixins
.length
] = (_ctorMap
[t
] = _ctorMap
[t
] || (dlang
.getObject(t
) || (~t
.indexOf('/') && require(t
))));
4875 var ctor
= mixins
.shift();
4876 _ctorMap
[ts
] = mixins
.length
? (ctor
.createSubclass
? ctor
.createSubclass(mixins
) : ctor
.extend
.apply(ctor
, mixins
)) : ctor
;
4879 return _ctorMap
[ts
];
4884 // The Dom/Widget parsing package
4886 _clearCache: function(){
4888 // Clear cached data. Used mainly for benchmarking.
4893 _functionFromScript: function(script
, attrData
){
4895 // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>`
4898 // The `<script>` DOMNode
4900 // For HTML5 compliance, searches for attrData + "args" (typically
4901 // "data-dojo-args") instead of "args"
4904 argsStr
= (script
.getAttribute(attrData
+ "args") || script
.getAttribute("args")),
4905 withStr
= script
.getAttribute("with");
4907 // Convert any arguments supplied in script tag into an array to be passed to the
4908 var fnArgs
= (argsStr
|| "").split(/\s*,\s*/);
4910 if(withStr
&& withStr
.length
){
4911 darray
.forEach(withStr
.split(/\s*,\s*/), function(part
){
4912 preamble
+= "with("+part
+"){";
4917 return new Function(fnArgs
, preamble
+ script
.innerHTML
+ suffix
);
4920 instantiate: function(nodes
, mixin
, options
){
4922 // Takes array of nodes, and turns them into class instances and
4923 // potentially calls a startup method to allow them to connect with
4926 // Array of DOM nodes
4928 // An object that will be mixed in with each node in the array.
4929 // Values in the mixin will override values in the node, if they
4932 // An object used to hold kwArgs for instantiation.
4933 // See parse.options argument for details.
4935 mixin
= mixin
|| {};
4936 options
= options
|| {};
4938 var dojoType
= (options
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
4939 attrData
= "data-" + (options
.scope
|| dojo
._scopeName
) + "-",// typically "data-dojo-"
4940 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
4941 dataDojoMixins
= attrData
+ "mixins"; // typically "data-dojo-mixins"
4944 darray
.forEach(nodes
, function(node
){
4945 var type
= dojoType
in mixin
? mixin
[dojoType
] : node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
);
4947 var mixinsValue
= node
.getAttribute(dataDojoMixins
),
4948 types
= mixinsValue
? [type
].concat(mixinsValue
.split(/\s*,\s*/)) : [type
];
4957 // Instantiate the nodes and return the objects
4958 return this._instantiate(list
, mixin
, options
);
4961 _instantiate: function(nodes
, mixin
, options
){
4963 // Takes array of objects representing nodes, and turns them into class instances and
4964 // potentially calls a startup method to allow them to connect with
4967 // Array of objects like
4969 // | ctor: Function (may be null)
4970 // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified)
4972 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
4973 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
4976 // An object that will be mixed in with each node in the array.
4977 // Values in the mixin will override values in the node, if they
4980 // An options object used to hold kwArgs for instantiation.
4981 // See parse.options argument for details.
4983 // Call widget constructors
4984 var thelist
= darray
.map(nodes
, function(obj
){
4985 var ctor
= obj
.ctor
|| getCtor(obj
.types
);
4986 // If we still haven't resolved a ctor, it is fatal now
4988 throw new Error("Unable to resolve constructor for: '" + obj
.types
.join() + "'");
4990 return this.construct(ctor
, obj
.node
, mixin
, options
, obj
.scripts
, obj
.inherited
);
4993 // Call startup on each top level instance if it makes sense (as for
4994 // widgets). Parent widgets will recursively call startup on their
4995 // (non-top level) children
4996 if(!mixin
._started
&& !options
.noStart
){
4997 darray
.forEach(thelist
, function(instance
){
4998 if(typeof instance
.startup
=== "function" && !instance
._started
){
5007 construct: function(ctor
, node
, mixin
, options
, scripts
, inherited
){
5009 // Calls new ctor(params, node), where params is the hash of parameters specified on the node,
5010 // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget.
5012 // Widget constructor.
5014 // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor.
5016 // Attributes in this object will be passed as parameters to ctor,
5017 // overriding attributes specified on the node.
5019 // An options object used to hold kwArgs for instantiation. See parse.options argument for details.
5020 // scripts: DomNode[]?
5021 // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node.
5022 // inherited: Object?
5023 // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited.
5025 var proto
= ctor
&& ctor
.prototype;
5026 options
= options
|| {};
5028 // Setup hash to hold parameter settings for this widget. Start with the parameter
5029 // settings inherited from ancestors ("dir" and "lang").
5030 // Inherited setting may later be overridden by explicit settings on node itself.
5033 if(options
.defaults
){
5034 // settings for the document itself (or whatever subtree is being parsed)
5035 dlang
.mixin(params
, options
.defaults
);
5038 // settings from dir=rtl or lang=... on a node above this node
5039 dlang
.mixin(params
, inherited
);
5042 // Get list of attributes explicitly listed in the markup
5044 if(has("dom-attributes-explicit")){
5045 // Standard path to get list of user specified attributes
5046 attributes
= node
.attributes
;
5047 }else if(has("dom-attributes-specified-flag")){
5048 // Special processing needed for IE8, to skip a few faux values in attributes[]
5049 attributes
= darray
.filter(node
.attributes
, function(a
){ return a
.specified
;});
5051 // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes
5052 var clone
= /^input$|^img$/i.test(node
.nodeName
) ? node
: node
.cloneNode(false),
5053 attrs
= clone
.outerHTML
.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, "");
5055 attributes
= darray
.map(attrs
.split(/\s+/), function(name
){
5056 var lcName
= name
.toLowerCase();
5059 // getAttribute() doesn't work for button.value, returns innerHTML of button.
5060 // but getAttributeNode().value doesn't work for the form.encType or li.value
5061 value
: (node
.nodeName
== "LI" && name
== "value") || lcName
== "enctype" ?
5062 node
.getAttribute(lcName
) : node
.getAttributeNode(lcName
).value
5067 // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params)
5068 // TODO: remove scope for 2.0
5069 var scope
= options
.scope
|| dojo
._scopeName
,
5070 attrData
= "data-" + scope
+ "-", // typically "data-dojo-"
5072 if(scope
!== "dojo"){
5073 hash
[attrData
+ "props"] = "data-dojo-props";
5074 hash
[attrData
+ "type"] = "data-dojo-type";
5075 hash
[attrData
+ "mixins"] = "data-dojo-mixins";
5076 hash
[scope
+ "type"] = "dojoType";
5077 hash
[attrData
+ "id"] = "data-dojo-id";
5080 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
5081 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
5082 var i
=0, item
, funcAttrs
=[], jsname
, extra
;
5083 while(item
= attributes
[i
++]){
5084 var name
= item
.name
,
5085 lcName
= name
.toLowerCase(),
5088 switch(hash
[lcName
] || lcName
){
5089 // Already processed, just ignore
5090 case "data-dojo-type":
5092 case "data-dojo-mixins":
5095 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
5096 case "data-dojo-props":
5100 // data-dojo-id or jsId. TODO: drop jsId in 2.0
5101 case "data-dojo-id":
5106 // For the benefit of _Templated
5107 case "data-dojo-attach-point":
5108 case "dojoattachpoint":
5109 params
.dojoAttachPoint
= value
;
5111 case "data-dojo-attach-event":
5112 case "dojoattachevent":
5113 params
.dojoAttachEvent
= value
;
5116 // Special parameter handling needed for IE
5118 params
["class"] = node
.className
;
5121 params
["style"] = node
.style
&& node
.style
.cssText
;
5124 // Normal attribute, ex: value="123"
5126 // Find attribute in widget corresponding to specified name.
5127 // May involve case conversion, ex: onclick --> onClick
5128 if(!(name
in proto
)){
5129 var map
= getNameMap(ctor
);
5130 name
= map
[lcName
] || name
;
5133 // Set params[name] to value, doing type conversion
5135 switch(typeof proto
[name
]){
5137 params
[name
] = value
;
5140 params
[name
] = value
.length
? Number(value
) : NaN
;
5143 // for checked/disabled value might be "" or "checked". interpret as true.
5144 params
[name
] = value
.toLowerCase() != "false";
5147 if(value
=== "" || value
.search(/[^\w\.]+/i) != -1){
5148 // The user has specified some text for a function like "return x+5"
5149 params
[name
] = new Function(value
);
5151 // The user has specified the name of a global function like "myOnClick"
5152 // or a single word function "return"
5153 params
[name
] = dlang
.getObject(value
, false) || new Function(value
);
5155 funcAttrs
.push(name
); // prevent "double connect", see #15026
5158 var pVal
= proto
[name
];
5160 (pVal
&& "length" in pVal
) ? (value
? value
.split(/\s*,\s*/) : []) : // array
5161 (pVal
instanceof Date
) ?
5162 (value
== "" ? new Date("") : // the NaN of dates
5163 value
== "now" ? new Date() : // current date
5164 dates
.fromISOString(value
)) :
5165 (pVal
instanceof _Url
) ? (dojo
.baseUrl
+ value
) :
5166 djson
.fromJson(value
);
5169 params
[name
] = value
;
5174 // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026.
5175 // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though).
5176 for(var j
=0; j
<funcAttrs
.length
; j
++){
5177 var lcfname
= funcAttrs
[j
].toLowerCase();
5178 node
.removeAttribute(lcfname
);
5179 node
[lcfname
] = null;
5182 // Mix things found in data-dojo-props into the params, overriding any direct settings
5185 extra
= djson
.fromJson
.call(options
.propsThis
, "{" + extra
+ "}");
5186 dlang
.mixin(params
, extra
);
5188 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
5189 throw new Error(e
.toString() + " in data-dojo-props='" + extra
+ "'");
5193 // Any parameters specified in "mixin" override everything else.
5194 dlang
.mixin(params
, mixin
);
5196 // Get <script> nodes associated with this widget, if they weren't specified explicitly
5198 scripts
= (ctor
&& (ctor
._noScript
|| proto
._noScript
) ? [] : query("> script[type^='dojo/']", node
));
5201 // Process <script type="dojo/*"> script tags
5202 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
5203 // the widget on instantiation.
5204 // <script type="dojo/method"> tags (with no event) are executed after instantiation
5205 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
5206 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
5207 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
5208 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
5209 var aspects
= [], // aspects to connect after instantiation
5210 calls
= [], // functions to call after instantiation
5211 watches
= [], // functions to watch after instantiation
5212 ons
= []; // functions to on after instantiation
5215 for(i
=0; i
<scripts
.length
; i
++){
5216 var script
= scripts
[i
];
5217 node
.removeChild(script
);
5218 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
5219 var event
= (script
.getAttribute(attrData
+ "event") || script
.getAttribute("event")),
5220 prop
= script
.getAttribute(attrData
+ "prop"),
5221 method
= script
.getAttribute(attrData
+ "method"),
5222 advice
= script
.getAttribute(attrData
+ "advice"),
5223 scriptType
= script
.getAttribute("type"),
5224 nf
= this._functionFromScript(script
, attrData
);
5226 if(scriptType
== "dojo/connect"){
5227 aspects
.push({ method
: event
, func
: nf
});
5228 }else if(scriptType
== "dojo/on"){
5229 ons
.push({ event
: event
, func
: nf
});
5233 }else if(scriptType
== "dojo/aspect"){
5234 aspects
.push({ method
: method
, advice
: advice
, func
: nf
});
5235 }else if(scriptType
== "dojo/watch"){
5236 watches
.push({ prop
: prop
, func
: nf
});
5243 // create the instance
5244 var markupFactory
= ctor
.markupFactory
|| proto
.markupFactory
;
5245 var instance
= markupFactory
? markupFactory(params
, node
, ctor
) : new ctor(params
, node
);
5247 // map it to the JS namespace if that makes sense
5249 dlang
.setObject(jsname
, instance
);
5252 // process connections and startup functions
5253 for(i
=0; i
<aspects
.length
; i
++){
5254 aspect
[aspects
[i
].advice
|| "after"](instance
, aspects
[i
].method
, dlang
.hitch(instance
, aspects
[i
].func
), true);
5256 for(i
=0; i
<calls
.length
; i
++){
5257 calls
[i
].call(instance
);
5259 for(i
=0; i
<watches
.length
; i
++){
5260 instance
.watch(watches
[i
].prop
, watches
[i
].func
);
5262 for(i
=0; i
<ons
.length
; i
++){
5263 don(instance
, ons
[i
].event
, ons
[i
].func
);
5269 scan: function(root
, options
){
5271 // Scan a DOM tree and return an array of objects representing the DOMNodes
5272 // that need to be turned into widgets.
5274 // Search specified node (or document root node) recursively for class instances
5275 // and return an array of objects that represent potential widgets to be
5276 // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where
5277 // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name
5278 // like "dijit/form/Button". If the MID is not currently available, scan will
5279 // attempt to require() in the module.
5281 // See parser.parse() for details of markup.
5283 // A default starting root node from which to start the parsing. Can be
5284 // omitted, defaulting to the entire document. If omitted, the `options`
5285 // object can be passed in this place. If the `options` object has a
5286 // `rootNode` member, that is used.
5288 // a kwArgs options object, see parse() for details
5291 // A promise that is resolved with the nodes that have been parsed.
5293 var list
= [], // Output List
5294 mids
= [], // An array of modules that are not yet loaded
5295 midsHash
= {}; // Used to keep the mids array unique
5297 var dojoType
= (options
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
5298 attrData
= "data-" + (options
.scope
|| dojo
._scopeName
) + "-", // typically "data-dojo-"
5299 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
5300 dataDojoTextDir
= attrData
+ "textdir", // typically "data-dojo-textdir"
5301 dataDojoMixins
= attrData
+ "mixins"; // typically "data-dojo-mixins"
5303 // Info on DOMNode currently being processed
5304 var node
= root
.firstChild
;
5306 // Info on parent of DOMNode currently being processed
5307 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
5308 // - parent: pointer to identical structure for my parent (or null if no parent)
5309 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
5310 var inherited
= options
.inherited
;
5312 function findAncestorAttr(node
, attr
){
5313 return (node
.getAttribute
&& node
.getAttribute(attr
)) ||
5314 (node
.parentNode
&& findAncestorAttr(node
.parentNode
, attr
));
5317 dir
: findAncestorAttr(root
, "dir"),
5318 lang
: findAncestorAttr(root
, "lang"),
5319 textDir
: findAncestorAttr(root
, dataDojoTextDir
)
5321 for(var key
in inherited
){
5322 if(!inherited
[key
]){ delete inherited
[key
]; }
5326 // Metadata about parent node
5328 inherited
: inherited
5331 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
5334 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
5337 function getEffective(parent
){
5339 // Get effective dir, lang, textDir settings for specified obj
5340 // (matching "parent" object structure above), and do caching.
5341 // Take care not to return null entries.
5342 if(!parent
.inherited
){
5343 parent
.inherited
= {};
5344 var node
= parent
.node
,
5345 grandparent
= getEffective(parent
.parent
);
5347 dir
: node
.getAttribute("dir") || grandparent
.dir
,
5348 lang
: node
.getAttribute("lang") || grandparent
.lang
,
5349 textDir
: node
.getAttribute(dataDojoTextDir
) || grandparent
.textDir
5351 for(var key
in inherited
){
5353 parent
.inherited
[key
] = inherited
[key
];
5357 return parent
.inherited
;
5360 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
5363 // Finished this level, continue to parent's next sibling
5364 if(!parent
|| !parent
.node
){
5367 node
= parent
.node
.nextSibling
;
5368 scriptsOnly
= false;
5369 parent
= parent
.parent
;
5370 scripts
= parent
.scripts
;
5374 if(node
.nodeType
!= 1){
5375 // Text or comment node, skip to next sibling
5376 node
= node
.nextSibling
;
5380 if(scripts
&& node
.nodeName
.toLowerCase() == "script"){
5381 // Save <script type="dojo/..."> for parent, then continue to next sibling
5382 type
= node
.getAttribute("type");
5383 if(type
&& /^dojo\/\w/i.test(type
)){
5386 node
= node
.nextSibling
;
5390 // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't
5391 // continue further analysis of the node and will continue to the next sibling
5392 node
= node
.nextSibling
;
5396 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
5397 // TODO: Remove dojoType in 2.0
5398 var type
= node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
);
5400 // Short circuit for leaf nodes containing nothing [but text]
5401 var firstChild
= node
.firstChild
;
5402 if(!type
&& (!firstChild
|| (firstChild
.nodeType
== 3 && !firstChild
.nextSibling
))){
5403 node
= node
.nextSibling
;
5407 // Meta data about current node
5412 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate.
5413 var mixinsValue
= node
.getAttribute(dataDojoMixins
),
5414 types
= mixinsValue
? [type
].concat(mixinsValue
.split(/\s*,\s*/)) : [type
];
5416 // Note: won't find classes declared via dojo/Declaration or any modules that haven't been
5417 // loaded yet so use try/catch to avoid throw from require()
5419 ctor
= getCtor(types
);
5422 // If the constructor was not found, check to see if it has modules that can be loaded
5424 darray
.forEach(types
, function(t
){
5425 if(~t
.indexOf('/') && !midsHash
[t
]){
5426 // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it.
5428 mids
[mids
.length
] = t
;
5433 var childScripts
= ctor
&& !ctor
.prototype._noScript
? [] : null; // <script> nodes that are parent's children
5435 // Setup meta data about this widget node, and save it to list of nodes to instantiate
5441 scripts
: childScripts
5443 current
.inherited
= getEffective(current
); // dir & lang settings for current node, explicit or inherited
5446 // Meta data about this non-widget node
5454 // Recurse, collecting <script type="dojo/..."> children, and also looking for
5455 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
5456 // When finished with children, go to my next sibling.
5458 scripts
= childScripts
;
5459 scriptsOnly
= ctor
&& ctor
.prototype.stopParser
&& !(options
.template
);
5463 var d
= new Deferred();
5465 // If there are modules to load then require them in
5467 // Warn that there are modules being auto-required
5468 if(has("dojo-debug-messages")){
5469 console
.warn("WARNING: Modules being Auto-Required: " + mids
.join(", "));
5471 require(mids
, function(){
5472 // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't
5473 // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to
5474 // auto-require of a module like ContentPane. Assumes list is in DFS order.
5475 d
.resolve(darray
.filter(list
, function(widget
){
5477 // Attempt to find the constructor again. Still won't find classes defined via
5478 // dijit/Declaration so need to try/catch.
5480 widget
.ctor
= getCtor(widget
.types
);
5484 // Get the parent widget
5485 var parent
= widget
.parent
;
5486 while(parent
&& !parent
.types
){
5487 parent
= parent
.parent
;
5490 // Return false if this node should be skipped due to stopParser on an ancestor.
5491 // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before
5492 // trying to compute widget.instantiate.
5493 var proto
= widget
.ctor
&& widget
.ctor
.prototype;
5494 widget
.instantiateChildren
= !(proto
&& proto
.stopParser
&& !(options
.template
));
5495 widget
.instantiate
= !parent
|| (parent
.instantiate
&& parent
.instantiateChildren
);
5496 return widget
.instantiate
;
5500 // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for
5501 // efficiency, to avoid running the require() and the callback code above.
5505 // Return the promise
5509 _require: function(/*DOMNode*/ script
){
5511 // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node,
5512 // calls require() to load the specified modules and (asynchronously) assign them to the specified global
5513 // variables, and returns a Promise for when that operation completes.
5515 // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
5517 var hash
= djson
.fromJson("{" + script
.innerHTML
+ "}"),
5522 for(var name
in hash
){
5524 mids
.push(hash
[name
]);
5527 require(mids
, function(){
5528 for(var i
=0; i
<vars
.length
; i
++){
5529 dlang
.setObject(vars
[i
], arguments
[i
]);
5531 d
.resolve(arguments
);
5537 _scanAmd: function(root
){
5539 // Scans the DOM for any declarative requires and returns their values.
5541 // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the
5542 // specified modules and (asynchronously) assign them to the specified global variables,
5543 // and returns a Promise for when those operations complete.
5545 // The node to base the scan from.
5547 // Promise that resolves when all the <script type=dojo/require> nodes have finished loading.
5548 var deferred
= new Deferred(),
5549 promise
= deferred
.promise
;
5550 deferred
.resolve(true);
5553 query("script[type='dojo/require']", root
).forEach(function(node
){
5554 // Fire off require() call for specified modules. Chain this require to fire after
5555 // any previous requires complete, so that layers can be loaded before individual module require()'s fire.
5556 promise
= promise
.then(function(){ return self
._require(node
); });
5558 // Remove from DOM so it isn't seen again
5559 node
.parentNode
.removeChild(node
);
5565 parse: function(rootNode
, options
){
5567 // Scan the DOM for class instances, and instantiate them.
5569 // Search specified node (or root node) recursively for class instances,
5570 // and instantiate them. Searches for either data-dojo-type="Class" or
5571 // dojoType="Class" where "Class" is a a fully qualified class name,
5572 // like `dijit/form/Button`
5574 // Using `data-dojo-type`:
5575 // Attributes using can be mixed into the parameters used to instantiate the
5576 // Class by using a `data-dojo-props` attribute on the node being converted.
5577 // `data-dojo-props` should be a string attribute to be converted from JSON.
5579 // Using `dojoType`:
5580 // Attributes are read from the original domNode and converted to appropriate
5581 // types by looking up the Class prototype values. This is the default behavior
5582 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
5583 // go away in Dojo 2.0.
5584 // rootNode: DomNode?
5585 // A default starting root node from which to start the parsing. Can be
5586 // omitted, defaulting to the entire document. If omitted, the `options`
5587 // object can be passed in this place. If the `options` object has a
5588 // `rootNode` member, that is used.
5590 // A hash of options.
5592 // - noStart: Boolean?:
5593 // when set will prevent the parser from calling .startup()
5594 // when locating the nodes.
5595 // - rootNode: DomNode?:
5596 // identical to the function's `rootNode` argument, though
5597 // allowed to be passed in via this `options object.
5598 // - template: Boolean:
5599 // If true, ignores ContentPane's stopParser flag and parses contents inside of
5600 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
5601 // nested inside the ContentPane to work.
5602 // - inherited: Object:
5603 // Hash possibly containing dir and lang settings to be applied to
5604 // parsed widgets, unless there's another setting on a sub-node that overrides
5606 // Root for attribute names to search for. If scopeName is dojo,
5607 // will search for data-dojo-type (or dojoType). For backwards compatibility
5608 // reasons defaults to dojo._scopeName (which is "dojo" except when
5609 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
5610 // - propsThis: Object:
5611 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
5612 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
5614 // Returns a blended object that is an array of the instantiated objects, but also can include
5615 // a promise that is resolved with the instantiated objects. This is done for backwards
5616 // compatibility. If the parser auto-requires modules, it will always behave in a promise
5617 // fashion and `parser.parse().then(function(instances){...})` should be used.
5619 // Parse all widgets on a page:
5620 // | parser.parse();
5622 // Parse all classes within the node with id="foo"
5623 // | parser.parse(dojo.byId('foo'));
5625 // Parse all classes in a page, but do not call .startup() on any
5627 // | parser.parse({ noStart: true })
5629 // Parse all classes in a node, but do not call .startup()
5630 // | parser.parse(someNode, { noStart:true });
5632 // | parser.parse({ noStart:true, rootNode: someNode });
5634 // determine the root node and options based on the passed arguments.
5636 if(!options
&& rootNode
&& rootNode
.rootNode
){
5638 root
= options
.rootNode
;
5639 }else if(rootNode
&& dlang
.isObject(rootNode
) && !("nodeType" in rootNode
)){
5644 root
= root
? dhtml
.byId(root
) : dwindow
.body();
5646 options
= options
|| {};
5648 var mixin
= options
.template
? { template
: true } : {},
5652 // First scan for any <script type=dojo/require> nodes, and execute.
5653 // Then scan for all nodes with data-dojo-type, and load any unloaded modules.
5654 // Then build the object instances. Add instances to already existing (but empty) instances[] array,
5655 // which may already have been returned to caller. Also, use otherwise to collect and throw any errors
5656 // that occur during the parse().
5658 this._scanAmd(root
, options
).then(function(){
5659 return self
.scan(root
, options
);
5660 }).then(function(parsedNodes
){
5661 return instances
= instances
.concat(self
._instantiate(parsedNodes
, mixin
, options
));
5662 }).otherwise(function(e
){
5663 // TODO Modify to follow better pattern for promise error managment when available
5664 console
.error("dojo/parser::parse() error", e
);
5668 // Blend the array with the promise
5669 dlang
.mixin(instances
, p
);
5675 dojo
.parser
= parser
;
5678 // Register the parser callback. It should be the first callback
5679 // after the a11y test.
5680 if(config
.parseOnLoad
){
5681 ready(100, parser
, "parse");
5688 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">▼</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n",
5689 'dojo/dnd/Manager':function(){
5690 define("dojo/dnd/Manager", [
5691 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window",
5692 "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch",
5693 "./common", "./autoscroll", "./Avatar"
5694 ], function(array
, declare
, event
, lang
, win
, domClass
, Evented
, has
, keys
, on
, topic
, touch
,
5695 dnd
, autoscroll
, Avatar
){
5700 var Manager
= declare("dojo.dnd.Manager", [Evented
], {
5702 // the manager of DnD operations (usually a singleton)
5703 constructor: function(){
5709 this.canDropFlag
= false;
5713 // avatar's offset from the mouse
5714 OFFSET_X
: has("touch") ? 0 : 16,
5715 OFFSET_Y
: has("touch") ? -64 : 16,
5718 overSource: function(source
){
5720 // called when a source detected a mouse-over condition
5724 this.target
= (source
&& source
.targetState
!= "Disabled") ? source
: null;
5725 this.canDropFlag
= Boolean(this.target
);
5726 this.avatar
.update();
5728 topic
.publish("/dnd/source/over", source
);
5730 outSource: function(source
){
5732 // called when a source detected a mouse-out condition
5736 if(this.target
== source
){
5738 this.canDropFlag
= false;
5739 this.avatar
.update();
5740 topic
.publish("/dnd/source/over", null);
5743 topic
.publish("/dnd/source/over", null);
5746 startDrag: function(source
, nodes
, copy
){
5748 // called to initiate the DnD operation
5750 // the source which provides items
5752 // the list of transferred items
5754 // copy items, if true, move items otherwise
5756 // Tell autoscroll that a drag is starting
5757 autoscroll
.autoScrollStart(win
.doc
);
5759 this.source
= source
;
5761 this.copy
= Boolean(copy
); // normalizing to true boolean
5762 this.avatar
= this.makeAvatar();
5763 win
.body().appendChild(this.avatar
.node
);
5764 topic
.publish("/dnd/start", source
, nodes
, this.copy
);
5766 on(win
.doc
, touch
.move, lang
.hitch(this, "onMouseMove")),
5767 on(win
.doc
, touch
.release
, lang
.hitch(this, "onMouseUp")),
5768 on(win
.doc
, "keydown", lang
.hitch(this, "onKeyDown")),
5769 on(win
.doc
, "keyup", lang
.hitch(this, "onKeyUp")),
5770 // cancel text selection and text dragging
5771 on(win
.doc
, "dragstart", event
.stop
),
5772 on(win
.body(), "selectstart", event
.stop
)
5774 var c
= "dojoDnd" + (copy
? "Copy" : "Move");
5775 domClass
.add(win
.body(), c
);
5777 canDrop: function(flag
){
5779 // called to notify if the current target can accept items
5780 var canDropFlag
= Boolean(this.target
&& flag
);
5781 if(this.canDropFlag
!= canDropFlag
){
5782 this.canDropFlag
= canDropFlag
;
5783 this.avatar
.update();
5786 stopDrag: function(){
5788 // stop the DnD in progress
5789 domClass
.remove(win
.body(), ["dojoDndCopy", "dojoDndMove"]);
5790 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
5792 this.avatar
.destroy();
5794 this.source
= this.target
= null;
5797 makeAvatar: function(){
5799 // makes the avatar; it is separate to be overwritten dynamically, if needed
5800 return new Avatar(this);
5802 updateAvatar: function(){
5804 // updates the avatar; it is separate to be overwritten dynamically, if needed
5805 this.avatar
.update();
5808 // mouse event processors
5809 onMouseMove: function(e
){
5811 // event processor for onmousemove
5814 var a
= this.avatar
;
5816 autoscroll
.autoScrollNodes(e
);
5817 //autoscroll.autoScroll(e);
5818 var s
= a
.node
.style
;
5819 s
.left
= (e
.pageX
+ this.OFFSET_X
) + "px";
5820 s
.top
= (e
.pageY
+ this.OFFSET_Y
) + "px";
5821 var copy
= Boolean(this.source
.copyState(dnd
.getCopyKeyState(e
)));
5822 if(this.copy
!= copy
){
5823 this._setCopyStatus(copy
);
5827 // Prevent page from scrolling so that user can drag instead.
5831 onMouseUp: function(e
){
5833 // event processor for onmouseup
5837 if(this.target
&& this.canDropFlag
){
5838 var copy
= Boolean(this.source
.copyState(dnd
.getCopyKeyState(e
)));
5839 topic
.publish("/dnd/drop/before", this.source
, this.nodes
, copy
, this.target
, e
);
5840 topic
.publish("/dnd/drop", this.source
, this.nodes
, copy
, this.target
, e
);
5842 topic
.publish("/dnd/cancel");
5848 // keyboard event processors
5849 onKeyDown: function(e
){
5851 // event processor for onkeydown:
5852 // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
5858 var copy
= Boolean(this.source
.copyState(true));
5859 if(this.copy
!= copy
){
5860 this._setCopyStatus(copy
);
5864 topic
.publish("/dnd/cancel");
5870 onKeyUp: function(e
){
5872 // event processor for onkeyup, watching for CTRL for copy/move status
5875 if(this.avatar
&& e
.keyCode
== keys
.CTRL
){
5876 var copy
= Boolean(this.source
.copyState(false));
5877 if(this.copy
!= copy
){
5878 this._setCopyStatus(copy
);
5884 _setCopyStatus: function(copy
){
5886 // changes the copy status
5890 this.source
._markDndStatus(this.copy
);
5891 this.updateAvatar();
5892 domClass
.replace(win
.body(),
5893 "dojoDnd" + (this.copy
? "Copy" : "Move"),
5894 "dojoDnd" + (this.copy
? "Move" : "Copy"));
5899 // The manager singleton variable. Can be overwritten if needed.
5900 dnd
._manager
= null;
5902 Manager
.manager
= dnd
.manager = function(){
5904 // Returns the current DnD manager. Creates one if it is not created yet.
5906 dnd
._manager
= new Manager();
5908 return dnd
._manager
; // Object
5915 'dijit/form/ToggleButton':function(){
5916 define("dijit/form/ToggleButton", [
5917 "dojo/_base/declare", // declare
5918 "dojo/_base/kernel", // kernel.deprecated
5920 "./_ToggleButtonMixin"
5921 ], function(declare
, kernel
, Button
, _ToggleButtonMixin
){
5924 // dijit/form/ToggleButton
5927 return declare("dijit.form.ToggleButton", [Button
, _ToggleButtonMixin
], {
5929 // A templated button widget that can be in two states (checked or not).
5930 // Can be base class for things like tabs or checkbox or radio buttons.
5932 baseClass
: "dijitToggleButton",
5934 setChecked: function(/*Boolean*/ checked
){
5936 // Deprecated. Use set('checked', true/false) instead.
5937 kernel
.deprecated("setChecked("+checked
+") is deprecated. Use set('checked',"+checked
+") instead.", "", "2.0");
5938 this.set('checked', checked
);
5944 'dojo/date/stamp':function(){
5945 define("dojo/date/stamp", ["../_base/lang", "../_base/array"], function(lang
, array
){
5954 lang
.setObject("dojo.date.stamp", stamp
);
5956 // Methods to convert dates to or from a wire (string) format using well-known conventions
5958 stamp
.fromISOString = function(/*String*/ formattedString
, /*Number?*/ defaultTime
){
5960 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
5963 // Accepts a string formatted according to a profile of ISO8601 as defined by
5964 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
5965 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
5966 // The following combinations are valid:
5972 // - times only, with an optional time zone appended
5976 // - and "datetimes" which could be any combination of the above
5978 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
5979 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
5980 // input may return null. Arguments which are out of bounds will be handled
5981 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
5982 // Only years between 100 and 9999 are supported.
5984 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
5986 // Used for defaults for fields omitted in the formattedString.
5987 // Uses 1970-01-01T00:00:00.0Z by default.
5989 if(!stamp
._isoRegExp
){
5991 //TODO: could be more restrictive and check for 00-59, etc.
5992 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
5995 var match
= stamp
._isoRegExp
.exec(formattedString
),
6000 if(match
[1]){match
[1]--;} // Javascript Date months are 0-based
6001 if(match
[6]){match
[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
6004 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
6005 defaultTime
= new Date(defaultTime
);
6006 array
.forEach(array
.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop
){
6007 return defaultTime
["get" + prop
]();
6008 }), function(value
, index
){
6009 match
[index
] = match
[index
] || value
;
6012 result
= new Date(match
[0]||1970, match
[1]||0, match
[2]||1, match
[3]||0, match
[4]||0, match
[5]||0, match
[6]||0); //TODO: UTC defaults
6014 result
.setFullYear(match
[0] || 1970);
6018 zoneSign
= match
[7] && match
[7].charAt(0);
6019 if(zoneSign
!= 'Z'){
6020 offset
= ((match
[8] || 0) * 60) + (Number(match
[9]) || 0);
6021 if(zoneSign
!= '-'){ offset
*= -1; }
6024 offset
-= result
.getTimezoneOffset();
6027 result
.setTime(result
.getTime() + offset
* 60000);
6031 return result
; // Date or null
6037 // "date" or "time" for partial formatting of the Date object.
6038 // Both date and time will be formatted by default.
6040 // if true, UTC/GMT is used for a timezone
6041 // milliseconds: Boolean
6042 // if true, output milliseconds
6046 stamp
.toISOString = function(/*Date*/ dateObject
, /*__Options?*/ options
){
6048 // Format a Date object as a string according a subset of the ISO-8601 standard
6051 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
6052 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
6053 // Does not check bounds. Only years between 100 and 9999 are supported.
6058 var _ = function(n
){ return (n
< 10) ? "0" + n
: n
; };
6059 options
= options
|| {};
6060 var formattedDate
= [],
6061 getter
= options
.zulu
? "getUTC" : "get",
6063 if(options
.selector
!= "time"){
6064 var year
= dateObject
[getter
+"FullYear"]();
6065 date
= ["0000".substr((year
+"").length
)+year
, _(dateObject
[getter
+"Month"]()+1), _(dateObject
[getter
+"Date"]())].join('-');
6067 formattedDate
.push(date
);
6068 if(options
.selector
!= "date"){
6069 var time
= [_(dateObject
[getter
+"Hours"]()), _(dateObject
[getter
+"Minutes"]()), _(dateObject
[getter
+"Seconds"]())].join(':');
6070 var millis
= dateObject
[getter
+"Milliseconds"]();
6071 if(options
.milliseconds
){
6072 time
+= "."+ (millis
< 100 ? "0" : "") + _(millis
);
6076 }else if(options
.selector
!= "time"){
6077 var timezoneOffset
= dateObject
.getTimezoneOffset();
6078 var absOffset
= Math
.abs(timezoneOffset
);
6079 time
+= (timezoneOffset
> 0 ? "-" : "+") +
6080 _(Math
.floor(absOffset
/60)) + ":" + _(absOffset
%60);
6082 formattedDate
.push(time
);
6084 return formattedDate
.join('T'); // String
6091 'dojo/Stateful':function(){
6092 define("dojo/Stateful", ["./_base/declare", "./_base/lang", "./_base/array", "dojo/when"], function(declare
, lang
, array
, when
){
6096 return declare("dojo.Stateful", null, {
6098 // Base class for objects that provide named properties with optional getter/setter
6099 // control and the ability to watch for property changes
6101 // The class also provides the functionality to auto-magically manage getters
6102 // and setters for object attributes/properties.
6104 // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
6105 // the xxx is a name of the attribute to handle. So an attribute of "foo"
6106 // would have a custom getter of _fooGetter and a custom setter of _fooSetter.
6109 // | var obj = new dojo.Stateful();
6110 // | obj.watch("foo", function(){
6111 // | console.log("foo changed to " + this.get("foo"));
6113 // | obj.set("foo","bar");
6115 // _attrPairNames: Hash
6116 // Used across all instances a hash to cache attribute names and their getter
6117 // and setter names.
6120 _getAttrNames: function(name
){
6122 // Helper function for get() and set().
6123 // Caches attribute name values so we don't do the string ops every time.
6127 var apn
= this._attrPairNames
;
6128 if(apn
[name
]){ return apn
[name
]; }
6129 return (apn
[name
] = {
6130 s
: "_" + name
+ "Setter",
6131 g
: "_" + name
+ "Getter"
6135 postscript: function(/*Object?*/ params
){
6136 // Automatic setting of params during construction
6137 if (params
){ this.set(params
); }
6140 _get: function(name
, names
){
6142 // Private function that does a get based off a hash of names
6144 // Hash of names of custom attributes
6145 return typeof this[names
.g
] === "function" ? this[names
.g
]() : this[name
];
6147 get: function(/*String*/name
){
6149 // Get a property on a Stateful instance.
6151 // The property to get.
6153 // The property value on this Stateful instance.
6155 // Get a named property on a Stateful object. The property may
6156 // potentially be retrieved via a getter method in subclasses. In the base class
6157 // this just retrieves the object's property.
6159 // | stateful = new dojo.Stateful({foo: 3});
6160 // | stateful.get("foo") // returns 3
6161 // | stateful.foo // returns 3
6163 return this._get(name
, this._getAttrNames(name
)); //Any
6165 set: function(/*String*/name
, /*Object*/value
){
6167 // Set a property on a Stateful instance
6169 // The property to set.
6171 // The value to set in the property.
6173 // The function returns this dojo.Stateful instance.
6175 // Sets named properties on a stateful object and notifies any watchers of
6176 // the property. A programmatic setter may be defined in subclasses.
6178 // | stateful = new dojo.Stateful();
6179 // | stateful.watch(function(name, oldValue, value){
6180 // | // this will be called on the set below
6182 // | stateful.set(foo, 5);
6184 // set() may also be called with a hash of name/value pairs, ex:
6189 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
6191 // If an object is used, iterate through object
6192 if(typeof name
=== "object"){
6194 if(name
.hasOwnProperty(x
) && x
!="_watchCallbacks"){
6195 this.set(x
, name
[x
]);
6201 var names
= this._getAttrNames(name
),
6202 oldValue
= this._get(name
, names
),
6203 setter
= this[names
.s
],
6205 if(typeof setter
=== "function"){
6206 // use the explicit setter
6207 result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
6209 // no setter so set attribute directly
6212 if(this._watchCallbacks
){
6214 // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
6215 when(result
, function(){
6216 self
._watchCallbacks(name
, oldValue
, value
);
6219 return this; // dojo/Stateful
6221 _changeAttrValue: function(name
, value
){
6223 // Internal helper for directly changing an attribute value.
6226 // The property to set.
6228 // The value to set in the property.
6231 // Directly change the value of an attribute on an object, bypassing any
6232 // accessor setter. Also handles the calling of watch and emitting events.
6233 // It is designed to be used by descendent class when there are two values
6234 // of attributes that are linked, but calling .set() is not appropriate.
6236 var oldValue
= this.get(name
);
6238 if(this._watchCallbacks
){
6239 this._watchCallbacks(name
, oldValue
, value
);
6241 return this; // dojo/Stateful
6243 watch: function(/*String?*/name
, /*Function*/callback
){
6245 // Watches a property for changes
6247 // Indicates the property to watch. This is optional (the callback may be the
6248 // only parameter), and if omitted, all the properties will be watched
6250 // An object handle for the watch. The unwatch method of this object
6251 // can be used to discontinue watching this property:
6252 // | var watchHandle = obj.watch("foo", callback);
6253 // | watchHandle.unwatch(); // callback won't be called now
6255 // The function to execute when the property changes. This will be called after
6256 // the property has been changed. The callback will be called with the |this|
6257 // set to the instance, the first argument as the name of the property, the
6258 // second argument as the old value and the third argument as the new value.
6260 var callbacks
= this._watchCallbacks
;
6263 callbacks
= this._watchCallbacks = function(name
, oldValue
, value
, ignoreCatchall
){
6264 var notify = function(propertyCallbacks
){
6265 if(propertyCallbacks
){
6266 propertyCallbacks
= propertyCallbacks
.slice();
6267 for(var i
= 0, l
= propertyCallbacks
.length
; i
< l
; i
++){
6268 propertyCallbacks
[i
].call(self
, name
, oldValue
, value
);
6272 notify(callbacks
['_' + name
]);
6273 if(!ignoreCatchall
){
6274 notify(callbacks
["*"]); // the catch-all
6276 }; // we use a function instead of an object so it will be ignored by JSON conversion
6278 if(!callback
&& typeof name
=== "function"){
6282 // prepend with dash to prevent name conflicts with function (like "name" property)
6285 var propertyCallbacks
= callbacks
[name
];
6286 if(typeof propertyCallbacks
!== "object"){
6287 propertyCallbacks
= callbacks
[name
] = [];
6289 propertyCallbacks
.push(callback
);
6291 // TODO: Remove unwatch in 2.0
6293 handle
.unwatch
= handle
.remove = function(){
6294 var index
= array
.indexOf(propertyCallbacks
, callback
);
6296 propertyCallbacks
.splice(index
, 1);
6299 return handle
; //Object
6307 'dijit/layout/AccordionContainer':function(){
6309 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
6310 define("dijit/layout/AccordionContainer", [
6312 "dojo/_base/array", // array.forEach array.map
6313 "dojo/_base/declare", // declare
6314 "dojo/_base/event", // event.stop
6315 "dojo/_base/fx", // fx.Animation
6316 "dojo/dom", // dom.setSelectable
6317 "dojo/dom-attr", // domAttr.attr
6318 "dojo/dom-class", // domClass.remove
6319 "dojo/dom-construct", // domConstruct.place
6320 "dojo/dom-geometry",
6321 "dojo/keys", // keys
6322 "dojo/_base/lang", // lang.getObject lang.hitch
6323 "dojo/sniff", // has("ie") has("dijit-legacy-requires")
6324 "dojo/topic", // publish
6325 "../focus", // focus.focus()
6326 "../_base/manager", // manager.defaultDuration
6330 "../_TemplatedMixin",
6331 "../_CssStateMixin",
6334 "dojo/text!./templates/AccordionButton.html"
6335 ], function(require
, array
, declare
, event
, fx
, dom
, domAttr
, domClass
, domConstruct
, domGeometry
,
6336 keys
, lang
, has
, topic
, focus
, manager
, ready
,
6337 _Widget
, _Container
, _TemplatedMixin
, _CssStateMixin
, StackContainer
, ContentPane
, template
){
6340 // dijit/layout/AccordionContainer
6345 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
6346 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
6348 // The resulting markup will look like:
6350 // <div class=dijitAccordionContainer>
6351 // <div class=dijitAccordionInnerContainer> (one pane)
6352 // <div class=dijitAccordionTitle> (title bar) ... </div>
6353 // <div class=dijtAccordionChildWrapper> (content pane) </div>
6357 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
6358 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
6359 // which on claro has a 1px border plus a 2px bottom margin.
6361 // During animation there are two dijtAccordionChildWrapper's shown, so we need
6362 // to compensate for that.
6365 var AccordionButton
= declare("dijit.layout._AccordionButton", [_Widget
, _TemplatedMixin
, _CssStateMixin
], {
6367 // The title bar to click to open up an accordion pane.
6368 // Internal widget used by AccordionContainer.
6372 templateString
: template
,
6375 // Title of the pane
6377 _setLabelAttr
: {node
: "titleTextNode", type
: "innerHTML" },
6380 // Tooltip that appears on hover
6382 _setTitleAttr
: {node
: "titleTextNode", type
: "attribute", attribute
: "title"},
6384 // iconClassAttr: String
6385 // CSS class for icon to left of label
6387 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
6389 baseClass
: "dijitAccordionTitle",
6391 getParent: function(){
6393 // Returns the AccordionContainer parent.
6399 buildRendering: function(){
6400 this.inherited(arguments
);
6401 var titleTextNodeId
= this.id
.replace(' ','_');
6402 domAttr
.set(this.titleTextNode
, "id", titleTextNodeId
+"_title");
6403 this.focusNode
.setAttribute("aria-labelledby", domAttr
.get(this.titleTextNode
, "id"));
6404 dom
.setSelectable(this.domNode
, false);
6407 getTitleHeight: function(){
6409 // Returns the height of the title dom node.
6410 return domGeometry
.getMarginSize(this.domNode
).h
; // Integer
6413 // TODO: maybe the parent should set these methods directly rather than forcing the code
6414 // into the button widget?
6415 _onTitleClick: function(){
6417 // Callback when someone clicks my title.
6418 var parent
= this.getParent();
6419 parent
.selectChild(this.contentWidget
, true);
6420 focus
.focus(this.focusNode
);
6423 _onTitleKeyPress: function(/*Event*/ evt
){
6424 return this.getParent()._onKeyPress(evt
, this.contentWidget
);
6427 _setSelectedAttr: function(/*Boolean*/ isSelected
){
6428 this._set("selected", isSelected
);
6429 this.focusNode
.setAttribute("aria-expanded", isSelected
? "true" : "false");
6430 this.focusNode
.setAttribute("aria-selected", isSelected
? "true" : "false");
6431 this.focusNode
.setAttribute("tabIndex", isSelected
? "0" : "-1");
6435 var AccordionInnerContainer
= declare("dijit.layout._AccordionInnerContainer", [_Widget
, _CssStateMixin
], {
6437 // Internal widget placed as direct child of AccordionContainer.containerNode.
6438 // When other widgets are added as children to an AccordionContainer they are wrapped in
6442 // buttonWidget: Function|String
6443 // Class to use to instantiate title
6444 // (Wish we didn't have a separate widget for just the title but maintaining it
6445 // for backwards compatibility, is it worth it?)
6450 // contentWidget: dijit/_WidgetBase
6451 // Pointer to the real child widget
6452 contentWidget: null,
6455 baseClass
: "dijitAccordionInnerContainer",
6457 // tell nested layout widget that we will take care of sizing
6458 isLayoutContainer
: true,
6460 buildRendering: function(){
6461 // Builds a template like:
6462 // <div class=dijitAccordionInnerContainer>
6464 // <div class=dijitAccordionChildWrapper>
6469 // Create wrapper div, placed where the child is now
6470 this.domNode
= domConstruct
.place("<div class='" + this.baseClass
+
6471 "' role='presentation'>", this.contentWidget
.domNode
, "after");
6473 // wrapper div's first child is the button widget (ie, the title bar)
6474 var child
= this.contentWidget
,
6475 cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
6476 this.button
= child
._buttonWidget
= (new cls({
6477 contentWidget
: child
,
6479 title
: child
.tooltip
,
6482 textDir
: child
.textDir
,
6483 iconClass
: child
.iconClass
,
6484 id
: child
.id
+ "_button",
6486 })).placeAt(this.domNode
);
6488 // and then the actual content widget (changing it from prior-sibling to last-child),
6489 // wrapped by a <div class=dijitAccordionChildWrapper>
6490 this.containerNode
= domConstruct
.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode
);
6491 domConstruct
.place(this.contentWidget
.domNode
, this.containerNode
);
6494 postCreate: function(){
6495 this.inherited(arguments
);
6497 // Map changes in content widget's title etc. to changes in the button
6498 var button
= this.button
;
6499 this._contentWidgetWatches
= [
6500 this.contentWidget
.watch('title', lang
.hitch(this, function(name
, oldValue
, newValue
){
6501 button
.set("label", newValue
);
6503 this.contentWidget
.watch('tooltip', lang
.hitch(this, function(name
, oldValue
, newValue
){
6504 button
.set("title", newValue
);
6506 this.contentWidget
.watch('iconClass', lang
.hitch(this, function(name
, oldValue
, newValue
){
6507 button
.set("iconClass", newValue
);
6512 _setSelectedAttr: function(/*Boolean*/ isSelected
){
6513 this._set("selected", isSelected
);
6514 this.button
.set("selected", isSelected
);
6516 var cw
= this.contentWidget
;
6517 if(cw
.onSelected
){ cw
.onSelected(); }
6521 startup: function(){
6522 // Called by _Container.addChild()
6523 this.contentWidget
.startup();
6526 destroy: function(){
6527 this.button
.destroyRecursive();
6529 array
.forEach(this._contentWidgetWatches
|| [], function(w
){ w
.unwatch(); });
6531 delete this.contentWidget
._buttonWidget
;
6532 delete this.contentWidget
._wrapperWidget
;
6534 this.inherited(arguments
);
6537 destroyDescendants: function(/*Boolean*/ preserveDom
){
6538 // since getChildren isn't working for me, have to code this manually
6539 this.contentWidget
.destroyRecursive(preserveDom
);
6543 var AccordionContainer
= declare("dijit.layout.AccordionContainer", StackContainer
, {
6545 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
6546 // and switching between panes is visualized by sliding the other panes up/down.
6548 // | <div data-dojo-type="dijit/layout/AccordionContainer">
6549 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
6551 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
6552 // | <p>This is some text</p>
6556 // duration: Integer
6557 // Amount of time (in ms) it takes to slide panes
6558 duration
: manager
.defaultDuration
,
6560 // buttonWidget: [const] String
6561 // The name of the widget used to display the title of each pane
6562 buttonWidget
: AccordionButton
,
6565 // _verticalSpace: Number
6566 // Pixels of space available for the open pane
6567 // (my content box size minus the cumulative size of all the title bars)
6570 baseClass
: "dijitAccordionContainer",
6572 buildRendering: function(){
6573 this.inherited(arguments
);
6574 this.domNode
.style
.overflow
= "hidden"; // TODO: put this in dijit.css
6575 this.domNode
.setAttribute("role", "tablist"); // TODO: put this in template
6578 startup: function(){
6579 if(this._started
){ return; }
6580 this.inherited(arguments
);
6581 if(this.selectedChildWidget
){
6582 this.selectedChildWidget
._wrapperWidget
.set("selected", true);
6587 // Implement _LayoutWidget.layout() virtual method.
6588 // Set the height of the open pane based on what room remains.
6590 var openPane
= this.selectedChildWidget
;
6592 if(!openPane
){ return;}
6594 // space taken up by title, plus wrapper div (with border/margin) for open pane
6595 var wrapperDomNode
= openPane
._wrapperWidget
.domNode
,
6596 wrapperDomNodeMargin
= domGeometry
.getMarginExtents(wrapperDomNode
),
6597 wrapperDomNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperDomNode
),
6598 wrapperContainerNode
= openPane
._wrapperWidget
.containerNode
,
6599 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
6600 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
6601 mySize
= this._contentBox
;
6603 // get cumulative height of all the unselected title bars
6604 var totalCollapsedHeight
= 0;
6605 array
.forEach(this.getChildren(), function(child
){
6606 if(child
!= openPane
){
6607 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
6608 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
6609 // margin below the bottom pane (even though we don't want one).
6610 totalCollapsedHeight
+= domGeometry
.getMarginSize(child
._wrapperWidget
.domNode
).h
;
6613 this._verticalSpace
= mySize
.h
- totalCollapsedHeight
- wrapperDomNodeMargin
.h
6614 - wrapperDomNodePadBorder
.h
- wrapperContainerNodeMargin
.h
- wrapperContainerNodePadBorder
.h
6615 - openPane
._buttonWidget
.getTitleHeight();
6617 // Memo size to make displayed child
6618 this._containerContentBox
= {
6619 h
: this._verticalSpace
,
6620 w
: this._contentBox
.w
- wrapperDomNodeMargin
.w
- wrapperDomNodePadBorder
.w
6621 - wrapperContainerNodeMargin
.w
- wrapperContainerNodePadBorder
.w
6625 openPane
.resize(this._containerContentBox
);
6629 _setupChild: function(child
){
6630 // Overrides _LayoutWidget._setupChild().
6631 // Put wrapper widget around the child widget, showing title
6633 child
._wrapperWidget
= AccordionInnerContainer({
6634 contentWidget
: child
,
6635 buttonWidget
: this.buttonWidget
,
6636 id
: child
.id
+ "_wrapper",
6639 textDir
: child
.textDir
,
6643 this.inherited(arguments
);
6646 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
6647 // Overrides _LayoutWidget.addChild().
6649 // Adding a child to a started Accordion is complicated because children have
6650 // wrapper widgets. Default code path (calling this.inherited()) would add
6651 // the new child inside another child's wrapper.
6653 // First add in child as a direct child of this AccordionContainer
6654 var refNode
= this.containerNode
;
6655 if(insertIndex
&& typeof insertIndex
== "number"){
6656 var children
= _Widget
.prototype.getChildren
.call(this); // get wrapper panes
6657 if(children
&& children
.length
>= insertIndex
){
6658 refNode
= children
[insertIndex
-1].domNode
;
6659 insertIndex
= "after";
6662 domConstruct
.place(child
.domNode
, refNode
, insertIndex
);
6664 if(!child
._started
){
6668 // Then stick the wrapper widget around the child widget
6669 this._setupChild(child
);
6671 // Code below copied from StackContainer
6672 topic
.publish(this.id
+"-addChild", child
, insertIndex
); // publish
6674 if(!this.selectedChildWidget
){
6675 this.selectChild(child
);
6678 // We haven't been started yet so just add in the child widget directly,
6679 // and the wrapper will be created on startup()
6680 this.inherited(arguments
);
6684 removeChild: function(child
){
6685 // Overrides _LayoutWidget.removeChild().
6687 // Destroy wrapper widget first, before StackContainer.getChildren() call.
6688 // Replace wrapper widget with true child widget (ContentPane etc.).
6689 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
6690 if(child
._wrapperWidget
){
6691 domConstruct
.place(child
.domNode
, child
._wrapperWidget
.domNode
, "after");
6692 child
._wrapperWidget
.destroy();
6693 delete child
._wrapperWidget
;
6696 domClass
.remove(child
.domNode
, "dijitHidden");
6698 this.inherited(arguments
);
6701 getChildren: function(){
6702 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
6703 return array
.map(this.inherited(arguments
), function(child
){
6704 return child
.declaredClass
== "dijit.layout._AccordionInnerContainer" ? child
.contentWidget
: child
;
6708 destroy: function(){
6709 if(this._animation
){
6710 this._animation
.stop();
6712 array
.forEach(this.getChildren(), function(child
){
6713 // If AccordionContainer has been started, then each child has a wrapper widget which
6714 // also needs to be destroyed.
6715 if(child
._wrapperWidget
){
6716 child
._wrapperWidget
.destroy();
6718 child
.destroyRecursive();
6721 this.inherited(arguments
);
6724 _showChild: function(child
){
6725 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6726 child
._wrapperWidget
.containerNode
.style
.display
="block";
6727 return this.inherited(arguments
);
6730 _hideChild: function(child
){
6731 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6732 child
._wrapperWidget
.containerNode
.style
.display
="none";
6733 this.inherited(arguments
);
6736 _transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate
){
6737 // Overrides StackContainer._transition() to provide sliding of title bars etc.
6740 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
6744 if(this._animation
){
6745 // there's an in-progress animation. speedily end it so we can do the newly requested one
6746 this._animation
.stop(true);
6747 delete this._animation
;
6753 newWidget
._wrapperWidget
.set("selected", true);
6755 var d
= this._showChild(newWidget
); // prepare widget to be slid in
6757 // Size the new widget, in case this is the first time it's being shown,
6758 // or I have been resized since the last time it was shown.
6759 // Note that page must be visible for resizing to work.
6760 if(this.doLayout
&& newWidget
.resize
){
6761 newWidget
.resize(this._containerContentBox
);
6766 oldWidget
._wrapperWidget
.set("selected", false);
6768 this._hideChild(oldWidget
);
6773 var newContents
= newWidget
._wrapperWidget
.containerNode
,
6774 oldContents
= oldWidget
._wrapperWidget
.containerNode
;
6776 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
6777 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
6778 // Have to compensate for that by immediately shrinking the pane being closed.
6779 var wrapperContainerNode
= newWidget
._wrapperWidget
.containerNode
,
6780 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
6781 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
6782 animationHeightOverhead
= wrapperContainerNodeMargin
.h
+ wrapperContainerNodePadBorder
.h
;
6784 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
) + "px";
6786 this._animation
= new fx
.Animation({
6788 duration
: this.duration
,
6789 curve
: [1, this._verticalSpace
- animationHeightOverhead
- 1],
6790 onAnimate: function(value
){
6791 value
= Math
.floor(value
); // avoid fractional values
6792 newContents
.style
.height
= value
+ "px";
6793 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
- value
) + "px";
6796 delete self
._animation
;
6797 newContents
.style
.height
= "auto";
6798 oldWidget
._wrapperWidget
.containerNode
.style
.display
= "none";
6799 oldContents
.style
.height
= "auto";
6800 self
._hideChild(oldWidget
);
6803 this._animation
.onStop
= this._animation
.onEnd
;
6804 this._animation
.play();
6807 return d
; // If child has an href, promise that fires when the widget has finished loading
6810 // note: we are treating the container as controller here
6811 _onKeyPress: function(/*Event*/ e
, /*dijit/_WidgetBase*/ fromTitle){
6813 // Handle keypress events
6815 // This is called from a handler on AccordionContainer.domNode
6816 // (setup in StackContainer), and is also called directly from
6817 // the click handler for accordion labels
6818 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
6821 var c = e.charOrCode;
6822 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
6823 (e.ctrlKey && c == keys.PAGE_UP)){
6824 this._adjacent(false)._buttonWidget._onTitleClick();
6826 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
6827 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
6828 this._adjacent(true)._buttonWidget._onTitleClick();
6834 // Back compat w/1.6, remove for 2.0
6835 if(has("dijit-legacy-requires")){
6836 ready(0, function(){
6837 var requires = ["dijit/layout/AccordionPane"];
6838 require(requires); // use indirection so modules not rolled into a build
6842 // For monkey patching
6843 AccordionContainer._InnerContainer = AccordionInnerContainer;
6844 AccordionContainer._Button = AccordionButton;
6846 return AccordionContainer;
6850 'dijit/form/ComboButton':function(){
6852 'url:dijit/form/templates/ComboButton.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tcellspacing='0' cellpadding='0' role=\"presentation\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonNode\" data-dojo-attach-point=\"buttonNode\" data-dojo-attach-event=\"ondijitclick:_onClick,onkeypress:_onButtonKeyPress\"\n\t\t><div id=\"${id}_button\" class=\"dijitReset dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><div class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitInline dijitButtonText\" id=\"${id}_label\" data-dojo-attach-point=\"containerNode\" role=\"presentation\"></div\n\t\t></div\n\t\t></td\n\t\t><td id=\"${id}_arrow\" class='dijitReset dijitRight dijitButtonNode dijitArrowButton'\n\t\t\tdata-dojo-attach-point=\"_popupStateNode,focusNode,_buttonNode\"\n\t\t\tdata-dojo-attach-event=\"onkeypress:_onArrowKeyPress\"\n\t\t\ttitle=\"${optionsTitle}\"\n\t\t\trole=\"button\" aria-haspopup=\"true\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">▼</div\n\t\t></td\n\t\t><td style=\"display:none !important;\"\n\t\t\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" data-dojo-attach-point=\"valueNode\" role=\"presentation\"\n\t\t/></td></tr></tbody\n></table>\n"}});
6853 define("dijit/form/ComboButton", [
6854 "dojo/_base/declare", // declare
6855 "dojo/_base/event", // event.stop
6856 "dojo/keys", // keys
6857 "../focus", // focus.focus()
6859 "dojo/text!./templates/ComboButton.html"
6860 ], function(declare, event, keys, focus, DropDownButton, template){
6863 // dijit/form/ComboButton
6865 return declare("dijit.form.ComboButton", DropDownButton, {
6867 // A combination button and drop-down button.
6868 // Users can click one side to "press" the button, or click an arrow
6869 // icon to display the drop down.
6872 // | <button data-dojo-type="dijit/form/ComboButton" onClick="...">
6873 // | <span>Hello world</span>
6874 // | <div data-dojo-type="dijit/Menu">...</div>
6878 // | var button1 = new ComboButton({label: "hello world", onClick: foo, dropDown: "myMenu"});
6879 // | dojo.body().appendChild(button1.domNode);
6882 templateString: template,
6884 // Map widget attributes to DOMNode attributes.
6885 _setIdAttr: "", // override _FormWidgetMixin which puts id on the focusNode
6886 _setTabIndexAttr: ["focusNode", "titleNode"],
6887 _setTitleAttr: "titleNode",
6889 // optionsTitle: String
6890 // Text that describes the options menu (accessibility)
6893 baseClass: "dijitComboButton",
6895 // Set classes like dijitButtonContentsHover or dijitArrowButtonActive depending on
6896 // mouse action over specified node
6898 "buttonNode": "dijitButtonNode",
6899 "titleNode": "dijitButtonContents",
6900 "_popupStateNode": "dijitDownArrowButton"
6905 _onButtonKeyPress: function(/*Event*/ evt
){
6907 // Handler for right arrow key when focus is on left part of button
6908 if(evt
.charOrCode
== keys
[this.isLeftToRight() ? "RIGHT_ARROW" : "LEFT_ARROW"]){
6909 focus
.focus(this._popupStateNode
);
6914 _onArrowKeyPress: function(/*Event*/ evt
){
6916 // Handler for left arrow key when focus is on right part of button
6917 if(evt
.charOrCode
== keys
[this.isLeftToRight() ? "LEFT_ARROW" : "RIGHT_ARROW"]){
6918 focus
.focus(this.titleNode
);
6923 focus: function(/*String*/ position
){
6925 // Focuses this widget to according to position, if specified,
6926 // otherwise on arrow node
6930 focus
.focus(position
== "start" ? this.titleNode
: this._popupStateNode
);
6938 'dijit/form/_AutoCompleterMixin':function(){
6939 define("dijit/form/_AutoCompleterMixin", [
6940 "dojo/data/util/filter", // patternToRegExp
6941 "dojo/_base/declare", // declare
6942 "dojo/dom-attr", // domAttr.get
6943 "dojo/_base/event", // event.stop
6945 "dojo/_base/lang", // lang.clone lang.hitch
6946 "dojo/query", // query
6947 "dojo/regexp", // regexp.escapeString
6948 "dojo/sniff", // has("ie")
6949 "dojo/string", // string.substitute
6951 "../registry", // registry.byId
6952 "./_TextBoxMixin", // defines _TextBoxMixin.selectInputText
6954 ], function(filter
, declare
, domAttr
, event
, keys
, lang
, query
, regexp
, has
, string
,
6955 DataList
, registry
, _TextBoxMixin
, SearchMixin
){
6958 // dijit/form/_AutoCompleterMixin
6960 return declare("dijit.form._AutoCompleterMixin", SearchMixin
, {
6962 // A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect`
6964 // All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`.
6969 // This is the item returned by the dojo/store/api/Store implementation that
6970 // provides the data for this ComboBox, it's the currently selected item.
6973 // autoComplete: Boolean
6974 // If user types in a partial string, and then tab out of the `<input>` box,
6975 // automatically copy the first entry displayed in the drop down list to
6976 // the `<input>` field
6979 // highlightMatch: String
6980 // One of: "first", "all" or "none".
6982 // If the ComboBox/FilteringSelect opens with the search results and the searched
6983 // string can be found, it will be highlighted. If set to "all"
6984 // then will probably want to change `queryExpr` parameter to '*${0}*'
6986 // Highlighting is only performed when `labelType` is "text", so as to not
6987 // interfere with any HTML markup an HTML label might contain.
6988 highlightMatch
: "first",
6990 // labelAttr: String?
6991 // The entries in the drop down list come from this attribute in the
6993 // If not specified, the searchAttr attribute is used instead.
6996 // labelType: String
6997 // Specifies how to interpret the labelAttr in the data store items.
6998 // Can be "html" or "text".
7001 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
7004 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
7005 _stopClickEvents
: false,
7007 _getCaretPos: function(/*DomNode*/ element
){
7008 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
7010 if(typeof(element
.selectionStart
) == "number"){
7011 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
7012 pos
= element
.selectionStart
;
7013 }else if(has("ie")){
7014 // in the case of a mouse click in a popup being handled,
7015 // then the win.doc.selection is not the textarea, but the popup
7016 // var r = win.doc.selection.createRange();
7017 // hack to get IE 6 to play nice. What a POS browser.
7018 var tr
= element
.ownerDocument
.selection
.createRange().duplicate();
7019 var ntr
= element
.createTextRange();
7020 tr
.move("character",0);
7021 ntr
.move("character",0);
7023 // If control doesn't have focus, you get an exception.
7024 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
7025 // There appears to be no workaround for this - googled for quite a while.
7026 ntr
.setEndPoint("EndToEnd", tr
);
7027 pos
= String(ntr
.text
).replace(/\r/g,"").length
;
7029 // If focus has shifted, 0 is fine for caret pos.
7035 _setCaretPos: function(/*DomNode*/ element
, /*Number*/ location
){
7036 location
= parseInt(location
);
7037 _TextBoxMixin
.selectInputText(element
, location
, location
);
7040 _setDisabledAttr: function(/*Boolean*/ value
){
7041 // Additional code to set disabled state of ComboBox node.
7042 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
7043 this.inherited(arguments
);
7044 this.domNode
.setAttribute("aria-disabled", value
? "true" : "false");
7047 _onKey: function(/*Event*/ evt
){
7049 // Handles keyboard events
7051 if(evt
.charCode
>= 32){ return; } // alphanumeric reserved for searching
7053 var key
= evt
.charCode
|| evt
.keyCode
;
7055 // except for cutting/pasting case - ctrl + x/v
7056 if(key
== keys
.ALT
|| key
== keys
.CTRL
|| key
== keys
.META
|| key
== keys
.SHIFT
){
7057 return; // throw out spurious events
7060 var pw
= this.dropDown
;
7061 var highlighted
= null;
7064 // _HasDropDown will do some of the work:
7066 // 1. when drop down is not yet shown:
7067 // - if user presses the down arrow key, call loadDropDown()
7068 // 2. when drop down is already displayed:
7069 // - on ESC key, call closeDropDown()
7070 // - otherwise, call dropDown.handleKey() to process the keystroke
7071 this.inherited(arguments
);
7073 if(evt
.altKey
|| evt
.ctrlKey
|| evt
.metaKey
){ return; } // don't process keys with modifiers - but we want shift+TAB
7076 highlighted
= pw
.getHighlightedOption();
7079 case keys
.PAGE_DOWN
:
7080 case keys
.DOWN_ARROW
:
7083 // Keystroke caused ComboBox_menu to move to a different item.
7084 // Copy new item to <input> box.
7086 this._announceOption(highlighted
);
7092 // prevent submitting form if user presses enter. Also
7093 // prevent accepting the value if either Next or Previous
7096 // only stop event on prev/next
7097 if(highlighted
== pw
.nextButton
){
7098 this._nextSearch(1);
7099 event
.stop(evt
); // prevent submit
7101 }else if(highlighted
== pw
.previousButton
){
7102 this._nextSearch(-1);
7103 event
.stop(evt
); // prevent submit
7106 event
.stop(evt
); // prevent submit if ENTER was to choose an item
7108 // Update 'value' (ex: KY) according to currently displayed text
7109 this._setBlurValue(); // set value if needed
7110 this._setCaretPos(this.focusNode
, this.focusNode
.value
.length
); // move cursor to end and cancel highlighting
7115 var newvalue
= this.get('displayedValue');
7116 // if the user had More Choices selected fall into the
7119 newvalue
== pw
._messages
["previousMessage"] ||
7120 newvalue
== pw
._messages
["nextMessage"])
7125 this._selectOption(highlighted
);
7131 this._lastQuery
= null; // in case results come back later
7132 this.closeDropDown();
7138 _autoCompleteText: function(/*String*/ text
){
7140 // Fill in the textbox with the first item from the drop down
7141 // list, and highlight the characters that were
7142 // auto-completed. For example, if user typed "CA" and the
7143 // drop down list appeared, the textbox would be changed to
7144 // "California" and "ifornia" would be highlighted.
7146 var fn
= this.focusNode
;
7148 // IE7: clear selection so next highlight works all the time
7149 _TextBoxMixin
.selectInputText(fn
, fn
.value
.length
);
7150 // does text autoComplete the value in the textbox?
7151 var caseFilter
= this.ignoreCase
? 'toLowerCase' : 'substr';
7152 if(text
[caseFilter
](0).indexOf(this.focusNode
.value
[caseFilter
](0)) == 0){
7153 var cpos
= this.autoComplete
? this._getCaretPos(fn
) : fn
.value
.length
;
7154 // only try to extend if we added the last character at the end of the input
7155 if((cpos
+1) > fn
.value
.length
){
7156 // only add to input node as we would overwrite Capitalisation of chars
7157 // actually, that is ok
7158 fn
.value
= text
;//.substr(cpos);
7159 // visually highlight the autocompleted characters
7160 _TextBoxMixin
.selectInputText(fn
, cpos
);
7163 // text does not autoComplete; replace the whole value and highlight
7165 _TextBoxMixin
.selectInputText(fn
);
7169 _openResultList: function(/*Object*/ results
, /*Object*/ query
, /*Object*/ options
){
7171 // Callback when a search completes.
7173 // 1. generates drop-down list and calls _showResultList() to display it
7174 // 2. if this result list is from user pressing "more choices"/"previous choices"
7175 // then tell screen reader to announce new option
7176 var wasSelected
= this.dropDown
.getHighlightedOption();
7177 this.dropDown
.clearResultList();
7178 if(!results
.length
&& options
.start
== 0){ // if no results and not just the previous choices button
7179 this.closeDropDown();
7182 this._nextSearch
= this.dropDown
.onPage
= lang
.hitch(this, function(direction
){
7183 results
.nextPage(direction
!== -1);
7187 // Fill in the textbox with the first item from the drop down list,
7188 // and highlight the characters that were auto-completed. For
7189 // example, if user typed "CA" and the drop down list appeared, the
7190 // textbox would be changed to "California" and "ifornia" would be
7193 this.dropDown
.createOptions(
7196 lang
.hitch(this, "_getMenuLabelFromItem")
7199 // show our list (only if we have content, else nothing)
7200 this._showResultList();
7203 // tell the screen reader that the paging callback finished by
7204 // shouting the next choice
7205 if("direction" in options
){
7206 if(options
.direction
){
7207 this.dropDown
.highlightFirstOption();
7208 }else if(!options
.direction
){
7209 this.dropDown
.highlightLastOption();
7212 this._announceOption(this.dropDown
.getHighlightedOption());
7214 }else if(this.autoComplete
&& !this._prev_key_backspace
7215 // when the user clicks the arrow button to show the full list,
7216 // startSearch looks for "*".
7217 // it does not make sense to autocomplete
7218 // if they are just previewing the options available.
7219 && !/^[*]+$/.test(query
[this.searchAttr
].toString())){
7220 this._announceOption(this.dropDown
.containerNode
.firstChild
.nextSibling
); // 1st real item
7224 _showResultList: function(){
7226 // Display the drop down if not already displayed, or if it is displayed, then
7227 // reposition it if necessary (reposition may be necessary if drop down's height changed).
7228 this.closeDropDown(true);
7229 this.openDropDown();
7230 this.domNode
.setAttribute("aria-expanded", "true");
7233 loadDropDown: function(/*Function*/ /*===== callback =====*/){
7234 // Overrides _HasDropDown.loadDropDown().
7235 // This is called when user has pressed button icon or pressed the down arrow key
7236 // to open the drop down.
7237 this._startSearchAll();
7240 isLoaded: function(){
7241 // signal to _HasDropDown that it needs to call loadDropDown() to load the
7242 // drop down asynchronously before displaying it
7246 closeDropDown: function(){
7247 // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
7248 // This method is the callback when the user types ESC or clicking
7249 // the button icon while the drop down is open. It's also called by other code.
7252 this.inherited(arguments
);
7253 this.domNode
.setAttribute("aria-expanded", "false");
7254 this.focusNode
.removeAttribute("aria-activedescendant");
7258 _setBlurValue: function(){
7259 // if the user clicks away from the textbox OR tabs away, set the
7260 // value to the textbox value
7262 // if value is now more choices or previous choices, revert
7264 var newvalue
= this.get('displayedValue');
7265 var pw
= this.dropDown
;
7267 newvalue
== pw
._messages
["previousMessage"] ||
7268 newvalue
== pw
._messages
["nextMessage"]
7271 this._setValueAttr(this._lastValueReported
, true);
7272 }else if(typeof this.item
== "undefined"){
7273 // Update 'value' (ex: KY) according to currently displayed text
7275 this.set('displayedValue', newvalue
);
7277 if(this.value
!= this._lastValueReported
){
7278 this._handleOnChange(this.value
, true);
7280 this._refreshState();
7284 _setItemAttr: function(/*item*/ item
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
){
7286 // Set the displayed valued in the input box, and the hidden value
7287 // that gets submitted, based on a dojo.data store item.
7289 // Users shouldn't call this function; they should be calling
7290 // set('item', value)
7295 if(!displayedValue
){
7296 displayedValue
= this.store
._oldAPI
? // remove getValue() for 2.0 (old dojo.data API)
7297 this.store
.getValue(item
, this.searchAttr
) : item
[this.searchAttr
];
7299 value
= this._getValueField() != this.searchAttr
? this.store
.getIdentity(item
) : displayedValue
;
7301 this.set('value', value
, priorityChange
, displayedValue
, item
);
7304 _announceOption: function(/*Node*/ node
){
7306 // a11y code that puts the highlighted option in the textbox.
7307 // This way screen readers will know what is happening in the
7313 // pull the text value from the item attached to the DOM node
7315 if(node
== this.dropDown
.nextButton
||
7316 node
== this.dropDown
.previousButton
){
7317 newValue
= node
.innerHTML
;
7318 this.item
= undefined;
7321 var item
= this.dropDown
.items
[node
.getAttribute("item")];
7322 newValue
= (this.store
._oldAPI
? // remove getValue() for 2.0 (old dojo.data API)
7323 this.store
.getValue(item
, this.searchAttr
) : item
[this.searchAttr
]).toString();
7324 this.set('item', item
, false, newValue
);
7326 // get the text that the user manually entered (cut off autocompleted text)
7327 this.focusNode
.value
= this.focusNode
.value
.substring(0, this._lastInput
.length
);
7328 // set up ARIA activedescendant
7329 this.focusNode
.setAttribute("aria-activedescendant", domAttr
.get(node
, "id"));
7330 // autocomplete the rest of the option to announce change
7331 this._autoCompleteText(newValue
);
7334 _selectOption: function(/*DomNode*/ target
){
7336 // Menu callback function, called when an item in the menu is selected.
7337 this.closeDropDown();
7339 this._announceOption(target
);
7341 this._setCaretPos(this.focusNode
, this.focusNode
.value
.length
);
7342 this._handleOnChange(this.value
, true);
7345 _startSearchAll: function(){
7346 this._startSearch('');
7349 _startSearchFromInput: function(){
7350 this.item
= undefined; // undefined means item needs to be set
7351 this.inherited(arguments
);
7354 _startSearch: function(/*String*/ key
){
7356 // Starts a search for elements matching key (key=="" means to return all items),
7357 // and calls _openResultList() when the search completes, to display the results.
7359 var popupId
= this.id
+ "_popup",
7360 dropDownConstructor
= lang
.isString(this.dropDownClass
) ?
7361 lang
.getObject(this.dropDownClass
, false) : this.dropDownClass
;
7362 this.dropDown
= new dropDownConstructor({
7363 onChange
: lang
.hitch(this, this._selectOption
),
7366 textDir
: this.textDir
7368 this.focusNode
.removeAttribute("aria-activedescendant");
7369 this.textbox
.setAttribute("aria-owns",popupId
); // associate popup with textbox
7371 this._lastInput
= key
; // Store exactly what was entered by the user.
7372 this.inherited(arguments
);
7375 _getValueField: function(){
7377 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
7378 // Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value.
7379 return this.searchAttr
;
7382 //////////// INITIALIZATION METHODS ///////////////////////////////////////
7384 postMixInProperties: function(){
7385 this.inherited(arguments
);
7387 var srcNodeRef
= this.srcNodeRef
;
7388 // if user didn't specify store, then assume there are option tags
7389 this.store
= new DataList({}, srcNodeRef
);
7391 // if there is no value set and there is an option list, set
7392 // the value to the first value to be consistent with native Select
7393 // Firefox and Safari set value
7394 // IE6 and Opera set selectedIndex, which is automatically set
7395 // by the selected attribute of an option tag
7396 // IE6 does not set value, Opera sets value = selectedIndex
7397 if(!("value" in this.params
)){
7398 var item
= (this.item
= this.store
.fetchSelectedItem());
7400 var valueField
= this._getValueField();
7401 // remove getValue() for 2.0 (old dojo.data API)
7402 this.value
= this.store
._oldAPI
? this.store
.getValue(item
, valueField
) : item
[valueField
];
7408 postCreate: function(){
7410 // Subclasses must call this method from their postCreate() methods
7414 // find any associated label element and add to ComboBox node.
7415 var label
=query('label[for="'+this.id
+'"]');
7417 if(!label
[0].id
){ label
[0].id
= this.id
+ "_label"; }
7418 this.domNode
.setAttribute("aria-labelledby", label
[0].id
);
7421 this.inherited(arguments
);
7422 this.connect(this, "onSearch", "_openResultList");
7425 _getMenuLabelFromItem: function(/*Item*/ item
){
7426 var label
= this.labelFunc(item
, this.store
),
7427 labelType
= this.labelType
;
7428 // If labelType is not "text" we don't want to screw any markup ot whatever.
7429 if(this.highlightMatch
!= "none" && this.labelType
== "text" && this._lastInput
){
7430 label
= this.doHighlight(label
, this._lastInput
);
7433 return {html
: labelType
== "html", label
: label
};
7436 doHighlight: function(/*String*/ label
, /*String*/ find
){
7438 // Highlights the string entered by the user in the menu. By default this
7439 // highlights the first occurrence found. Override this method
7440 // to implement your custom highlighting.
7445 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
7446 modifiers
= (this.ignoreCase
? "i" : "") + (this.highlightMatch
== "all" ? "g" : ""),
7447 i
= this.queryExpr
.indexOf("${0}");
7448 find
= regexp
.escapeString(find
); // escape regexp special chars
7449 //If < appears in label, and user presses t, we don't want to highlight the t in the escaped "<"
7450 //first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which
7451 //should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the
7452 //HTML highlight markup.
7453 return this._escapeHtml(label
.replace(
7454 new RegExp((i
== 0 ? "^" : "") + "("+ find
+")" + (i
== (this.queryExpr
.length
- 4) ? "$" : ""), modifiers
),
7455 '\uFFFF$1\uFFFF')).replace(
7456 /\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>'
7457 ); // returns String, (almost) valid HTML (entities encoded)
7460 _escapeHtml: function(/*String*/ str
){
7461 // TODO Should become dojo.html.entities(), when exists use instead
7463 // Adds escape sequences for special characters in XML: `&<>"'`
7464 str
= String(str
).replace(/&/gm, "&").replace(/</gm
, "<")
7465 .replace(/>/gm, ">").replace(/"/gm, ""
;"); //balance"
7466 return str
; // string
7470 // Overrides the _FormWidget.reset().
7471 // Additionally reset the .item (to clean up).
7473 this.inherited(arguments
);
7476 labelFunc: function(item
, store
){
7478 // Computes the label to display based on the dojo.data store item.
7480 // The item from the store
7481 // store: dojo/store/api/Store
7484 // The label that the ComboBox should display
7488 // Use toString() because XMLStore returns an XMLItem whereas this
7489 // method is expected to return a String (#9354).
7490 // Remove getValue() for 2.0 (old dojo.data API)
7491 return (store
._oldAPI
? store
.getValue(item
, this.labelAttr
|| this.searchAttr
) :
7492 item
[this.labelAttr
|| this.searchAttr
]).toString(); // String
7495 _setValueAttr: function(/*String*/ value
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
, /*item?*/ item
){
7497 // Hook so set('value', value) works.
7499 // Sets the value of the select.
7500 this._set("item", item
||null); // value not looked up in store
7501 if(value
== null /* or undefined */){ value
= ''; } // null translates to blank
7502 this.inherited(arguments
);
7504 _setTextDirAttr: function(/*String*/ textDir
){
7506 // Setter for textDir, needed for the dropDown's textDir update.
7508 // Users shouldn't call this function; they should be calling
7509 // set('textDir', value)
7512 this.inherited(arguments
);
7513 // update the drop down also (_ComboBoxMenuMixin)
7515 this.dropDown
._set("textDir", textDir
);
7522 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n",
7523 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>",
7524 'dijit/form/MappedTextBox':function(){
7525 define("dijit/form/MappedTextBox", [
7526 "dojo/_base/declare", // declare
7527 "dojo/dom-construct", // domConstruct.place
7528 "./ValidationTextBox"
7529 ], function(declare
, domConstruct
, ValidationTextBox
){
7532 // dijit/form/MappedTextBox
7534 return declare("dijit.form.MappedTextBox", ValidationTextBox
, {
7536 // A dijit/form/ValidationTextBox subclass which provides a base class for widgets that have
7537 // a visible formatted display value, and a serializable
7538 // value in a hidden input field which is actually sent to the server.
7540 // The visible display may
7541 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
7542 // input field which uses the `name` attribute declared by the original widget. That value sent
7543 // to the server is defined by the dijit/form/MappedTextBox.serialize() method and is typically
7548 postMixInProperties: function(){
7549 this.inherited(arguments
);
7551 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
7552 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
7553 this.nameAttrSetting
= "";
7556 // Override default behavior to assign name to focusNode
7559 serialize: function(val
/*=====, options =====*/){
7561 // Overridable function used to convert the get('value') result to a canonical
7562 // (non-localized) string. For example, will print dates in ISO format, and
7563 // numbers the same way as they are represented in javascript.
7567 // protected extension
7568 return val
.toString
? val
.toString() : ""; // String
7571 toString: function(){
7573 // Returns widget as a printable string using the widget's value
7576 var val
= this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
7577 return val
!= null ? (typeof val
== "string" ? val
: this.serialize(val
, this.constraints
)) : ""; // String
7580 validate: function(){
7581 // Overrides `dijit/form/TextBox.validate`
7582 this.valueNode
.value
= this.toString();
7583 return this.inherited(arguments
);
7586 buildRendering: function(){
7587 // Overrides `dijit/_TemplatedMixin/buildRendering`
7589 this.inherited(arguments
);
7591 // Create a hidden <input> node with the serialized value used for submit
7592 // (as opposed to the displayed value).
7593 // Passing in name as markup rather than calling domConstruct.create() with an attrs argument
7594 // to make query(input[name=...]) work on IE. (see #8660)
7595 this.valueNode
= domConstruct
.place("<input type='hidden'" + (this.name
? ' name="' + this.name
.replace(/"/g, """) + '"' : "") + "/>", this.textbox, "after
");
7599 // Overrides `dijit/form/ValidationTextBox.reset` to
7600 // reset the hidden textbox value to ''
7601 this.valueNode.value = '';
7602 this.inherited(arguments);
7608 'dijit/form/ComboBoxMixin':function(){
7610 'url:dijit/form/templates/DropDownBox.html':"<div
class=\"dijit dijitReset dijitInline dijitLeft
\"\n\tid=\"widget_${id}
\"\n\trole=\"combobox
\"\n\t><div
class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo
-attach
-point
=\"_buttonNode
, _popupStateNode
\" role
=\"presentation
\"\n\t\t><input
class=\"dijitReset dijitInputField dijitArrowButtonInner
\" value
=\"▼ \" 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=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"}});
7611 define("dijit
/form/ComboBoxMixin
", [
7612 "dojo
/_base/declare", // declare
7613 "dojo
/_base/Deferred
",
7614 "dojo
/_base/kernel
", // kernel.deprecated
7615 "dojo
/_base/lang", // lang.mixin
7616 "dojo
/store/util/QueryResults
",
7617 "./_AutoCompleterMixin
",
7620 "dojo
/text!./templates
/DropDownBox
.html
"
7621 ], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
7625 // dijit/form/ComboBoxMixin
7627 return declare("dijit
.form
.ComboBoxMixin
", [_HasDropDown, _AutoCompleterMixin], {
7629 // Provides main functionality of ComboBox widget
7631 // dropDownClass: [protected extension] Function String
7632 // Dropdown widget class used to select a date/time.
7633 // Subclasses should specify this.
7634 dropDownClass: _ComboBoxMenu,
7636 // hasDownArrow: Boolean
7637 // Set this textbox to have a down arrow button, to display the drop down list.
7638 // Defaults to true.
7641 templateString: template,
7643 baseClass: "dijitTextBox dijitComboBox
",
7646 // store: [const] dojo/store/api/Store|dojo/data/api/Read
7647 // Reference to data provider object used by this ComboBox.
7649 // Should be dojo/store/api/Store, but dojo/data/api/Read supported
7650 // for backwards compatibility.
7654 // Set classes like dijitDownArrowButtonHover depending on
7655 // mouse action over button node
7657 "_buttonNode
": "dijitDownArrowButton
"
7660 _setHasDownArrowAttr: function(/*Boolean*/ val){
7661 this._set("hasDownArrow
", val);
7662 this._buttonNode.style.display = val ? "" : "none
";
7665 _showResultList: function(){
7667 this.displayMessage("");
7668 this.inherited(arguments);
7671 _setStoreAttr: function(store){
7672 // For backwards-compatibility, accept dojo.data store in addition to dojo/store/api/Store. Remove in 2.0.
7678 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
7679 // Like dojo/store/DataStore.get() except returns native item.
7680 var deferred = new Deferred();
7681 this.fetchItemByIdentity({
7683 onItem: function(object){
7684 deferred.resolve(object);
7686 onError: function(error){
7687 deferred.reject(error);
7690 return deferred.promise;
7692 query: function(query, options){
7694 // Queries the store for objects. Like dojo/store/DataStore.query()
7695 // except returned Deferred contains array of native items.
7696 var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
7697 deferred.total = new Deferred();
7698 var fetchHandle = this.fetch(lang.mixin({
7700 onBegin: function(count){
7701 deferred.total.resolve(count);
7703 onComplete: function(results){
7704 deferred.resolve(results);
7706 onError: function(error){
7707 deferred.reject(error);
7710 return QueryResults(deferred);
7714 this._set("store
", store);
7717 postMixInProperties: function(){
7718 // Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
7719 // Unfortunately, without special code, it ends up executing second.
7720 var store = this.params.store || this.store;
7722 this._setStoreAttr(store);
7725 this.inherited(arguments);
7727 // User may try to access this.store.getValue() etc. in a custom labelFunc() function.
7728 // It's not available with the new data store for handling inline <option> tags, so add it.
7729 if(!this.params.store && !this.store._oldAPI){
7730 var clazz = this.declaredClass;
7731 lang.mixin(this.store, {
7732 getValue: function(item, attr){
7733 kernel.deprecated(clazz + ".store
.getValue(item
, attr
) is deprecated
for builtin store
. Use item
.attr directly
", "", "2.0");
7736 getLabel: function(item){
7737 kernel.deprecated(clazz + ".store
.getLabel(item
) is deprecated
for builtin store
. Use item
.label directly
", "", "2.0");
7740 fetch: function(args){
7741 kernel.deprecated(clazz + ".store
.fetch() is deprecated
for builtin store
.", "Use store
.query()", "2.0");
7742 var shim = ["dojo
/data/ObjectStore
"]; // indirection so it doesn't get rolled into a build
7743 require(shim, lang.hitch(this, function(ObjectStore){
7744 new ObjectStore({objectStore: this}).fetch(args);
7754 'dijit/form/_TextBoxMixin':function(){
7755 define("dijit
/form/_TextBoxMixin
", [
7756 "dojo
/_base/array", // array.forEach
7757 "dojo
/_base/declare", // declare
7758 "dojo
/dom", // dom
.byId
7759 "dojo/_base/event", // event.stop
7760 "dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
7761 "dojo/_base/lang", // lang.mixin
7763 "../main" // for exporting dijit._setSelectionRange, dijit.selectInputText
7764 ], function(array
, declare
, dom
, event
, keys
, lang
, on
, dijit
){
7767 // dijit/form/_TextBoxMixin
7769 var _TextBoxMixin
= declare("dijit.form._TextBoxMixin", null, {
7771 // A mixin for textbox form input widgets
7774 // Removes leading and trailing whitespace if true. Default is false.
7777 // uppercase: Boolean
7778 // Converts all characters to uppercase if true. Default is false.
7781 // lowercase: Boolean
7782 // Converts all characters to lowercase if true. Default is false.
7785 // propercase: Boolean
7786 // Converts the first character of each word to uppercase if true.
7789 // maxLength: String
7790 // HTML INPUT tag maxLength declaration.
7793 // selectOnClick: [const] Boolean
7794 // If true, all text will be selected when focused with mouse
7795 selectOnClick
: false,
7797 // placeHolder: String
7798 // Defines a hint to help users fill out the input field (as defined in HTML 5).
7799 // This should only contain plain text (no html markup).
7802 _getValueAttr: function(){
7804 // Hook so get('value') works as we like.
7806 // For `dijit/form/TextBox` this basically returns the value of the `<input>`.
7808 // For `dijit/form/MappedTextBox` subclasses, which have both
7809 // a "displayed value" and a separate "submit value",
7810 // This treats the "displayed value" as the master value, computing the
7811 // submit value from it via this.parse().
7812 return this.parse(this.get('displayedValue'), this.constraints
);
7815 _setValueAttr: function(value
, /*Boolean?*/ priorityChange
, /*String?*/ formattedValue
){
7817 // Hook so set('value', ...) works.
7820 // Sets the value of the widget to "value" which can be of
7821 // any type as determined by the widget.
7824 // The visual element value is also set to a corresponding,
7825 // but not necessarily the same, value.
7828 // If specified, used to set the visual element value,
7829 // otherwise a computed visual value is used.
7832 // If true, an onChange event is fired immediately instead of
7833 // waiting for the next blur event.
7836 if(value
!== undefined){
7837 // TODO: this is calling filter() on both the display value and the actual value.
7838 // I added a comment to the filter() definition about this, but it should be changed.
7839 filteredValue
= this.filter(value
);
7840 if(typeof formattedValue
!= "string"){
7841 if(filteredValue
!== null && ((typeof filteredValue
!= "number") || !isNaN(filteredValue
))){
7842 formattedValue
= this.filter(this.format(filteredValue
, this.constraints
));
7843 }else{ formattedValue
= ''; }
7846 if(formattedValue
!= null /* and !undefined */ && ((typeof formattedValue
) != "number" || !isNaN(formattedValue
)) && this.textbox
.value
!= formattedValue
){
7847 this.textbox
.value
= formattedValue
;
7848 this._set("displayedValue", this.get("displayedValue"));
7851 if(this.textDir
== "auto"){
7852 this.applyTextDir(this.focusNode
, formattedValue
);
7855 this.inherited(arguments
, [filteredValue
, priorityChange
]);
7858 // displayedValue: String
7859 // For subclasses like ComboBox where the displayed value
7860 // (ex: Kentucky) and the serialized value (ex: KY) are different,
7861 // this represents the displayed value.
7863 // Setting 'displayedValue' through set('displayedValue', ...)
7864 // updates 'value', and vice-versa. Otherwise 'value' is updated
7865 // from 'displayedValue' periodically, like onBlur etc.
7867 // TODO: move declaration to MappedTextBox?
7868 // Problem is that ComboBox references displayedValue,
7869 // for benefit of FilteringSelect.
7872 _getDisplayedValueAttr: function(){
7874 // Hook so get('displayedValue') works.
7876 // Returns the displayed value (what the user sees on the screen),
7877 // after filtering (ie, trimming spaces etc.).
7879 // For some subclasses of TextBox (like ComboBox), the displayed value
7880 // is different from the serialized value that's actually
7881 // sent to the server (see `dijit/form/ValidationTextBox.serialize()`)
7883 // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
7885 // TODO: this isn't really the displayed value when the user is typing
7886 return this.filter(this.textbox
.value
);
7889 _setDisplayedValueAttr: function(/*String*/ value
){
7891 // Hook so set('displayedValue', ...) works.
7893 // Sets the value of the visual element to the string "value".
7894 // The widget value is also set to a corresponding,
7895 // but not necessarily the same, value.
7897 if(value
== null /* or undefined */){ value
= '' }
7898 else if(typeof value
!= "string"){ value
= String(value
) }
7900 this.textbox
.value
= value
;
7902 // sets the serialized value to something corresponding to specified displayedValue
7903 // (if possible), and also updates the textbox.value, for example converting "123"
7905 this._setValueAttr(this.get('value'), undefined);
7907 this._set("displayedValue", this.get('displayedValue'));
7910 if(this.textDir
== "auto"){
7911 this.applyTextDir(this.focusNode
, value
);
7915 format: function(value
/*=====, constraints =====*/){
7917 // Replaceable function to convert a value to a properly formatted string.
7919 // constraints: Object
7921 // protected extension
7922 return value
== null /* or undefined */ ? "" : (value
.toString
? value
.toString() : value
);
7925 parse: function(value
/*=====, constraints =====*/){
7927 // Replaceable function to convert a formatted string to a value
7929 // constraints: Object
7931 // protected extension
7933 return value
; // String
7936 _refreshState: function(){
7938 // After the user types some characters, etc., this method is
7939 // called to check the field for validity etc. The base method
7940 // in `dijit/form/TextBox` does nothing, but subclasses override.
7946 onInput: function(event){
7948 // Connect to this function to receive notifications of various user data-input events.
7949 // Return false to cancel the event and prevent it from being processed.
7951 // keydown | keypress | cut | paste | input
7956 onInput: function(){},
7958 __skipInputEvent
: false,
7959 _onInput: function(/*Event*/ evt
){
7961 // Called AFTER the input event has happened
7963 // set text direction according to textDir that was defined in creation
7964 if(this.textDir
== "auto"){
7965 this.applyTextDir(this.focusNode
, this.focusNode
.value
);
7968 this._processInput(evt
);
7971 _processInput: function(/*Event*/ evt
){
7973 // Default action handler for user input events
7975 this._refreshState();
7977 // In case someone is watch()'ing for changes to displayedValue
7978 this._set("displayedValue", this.get("displayedValue"));
7981 postCreate: function(){
7982 // setting the value here is needed since value="" in the template causes "undefined"
7983 // and setting in the DOM (instead of the JS object) helps with form reset actions
7984 this.textbox
.setAttribute("value", this.textbox
.value
); // DOM and JS values should be the same
7986 this.inherited(arguments
);
7988 // normalize input events to reduce spurious event processing
7989 // onkeydown: do not forward modifier keys
7990 // set charOrCode to numeric keycode
7991 // onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
7992 // onpaste & oncut: set charOrCode to 229 (IME)
7993 // oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
7994 var handleEvent = function(e
){
7996 if(e
.type
== "keydown"){
7997 charOrCode
= e
.keyCode
;
7998 switch(charOrCode
){ // ignore state keys
8003 case keys
.CAPS_LOCK
:
8005 case keys
.SCROLL_LOCK
:
8008 if(!e
.ctrlKey
&& !e
.metaKey
&& !e
.altKey
){ // no modifiers
8009 switch(charOrCode
){ // ignore location keys
8020 case keys
.NUMPAD_MULTIPLY
:
8021 case keys
.NUMPAD_PLUS
:
8022 case keys
.NUMPAD_ENTER
:
8023 case keys
.NUMPAD_MINUS
:
8024 case keys
.NUMPAD_PERIOD
:
8025 case keys
.NUMPAD_DIVIDE
:
8028 if((charOrCode
>= 65 && charOrCode
<= 90) || (charOrCode
>= 48 && charOrCode
<= 57) || charOrCode
== keys
.SPACE
){
8029 return; // keypress will handle simple non-modified printable keys
8033 if(keys
[i
] === e
.keyCode
){
8038 if(!named
){ return; } // only allow named ones through
8041 charOrCode
= e
.charCode
>= 32 ? String
.fromCharCode(e
.charCode
) : e
.charCode
;
8043 charOrCode
= (e
.keyCode
>= 65 && e
.keyCode
<= 90) || (e
.keyCode
>= 48 && e
.keyCode
<= 57) || e
.keyCode
== keys
.SPACE
? String
.fromCharCode(e
.keyCode
) : e
.keyCode
;
8046 charOrCode
= 229; // IME
8048 if(e
.type
== "keypress"){
8049 if(typeof charOrCode
!= "string"){ return; }
8050 if((charOrCode
>= 'a' && charOrCode
<= 'z') || (charOrCode
>= 'A' && charOrCode
<= 'Z') || (charOrCode
>= '0' && charOrCode
<= '9') || (charOrCode
=== ' ')){
8051 if(e
.ctrlKey
|| e
.metaKey
|| e
.altKey
){ return; } // can only be stopped reliably in keydown
8054 if(e
.type
== "input"){
8055 if(this.__skipInputEvent
){ // duplicate event
8056 this.__skipInputEvent
= false;
8060 this.__skipInputEvent
= true;
8062 // create fake event to set charOrCode and to know if preventDefault() was called
8063 var faux
= { faux
: true }, attr
;
8065 if(attr
!= "layerX" && attr
!= "layerY"){ // prevent WebKit warnings
8067 if(typeof v
!= "function" && typeof v
!= "undefined"){ faux
[attr
] = v
; }
8071 charOrCode
: charOrCode
,
8072 _wasConsumed
: false,
8073 preventDefault: function(){
8074 faux
._wasConsumed
= true;
8077 stopPropagation: function(){ e
.stopPropagation(); }
8079 // give web page author a chance to consume the event
8080 //console.log(faux.type + ', charOrCode = (' + (typeof charOrCode) + ') ' + charOrCode + ', ctrl ' + !!faux.ctrlKey + ', alt ' + !!faux.altKey + ', meta ' + !!faux.metaKey + ', shift ' + !!faux.shiftKey);
8081 if(this.onInput(faux
) === false){ // return false means stop
8082 faux
.preventDefault();
8083 faux
.stopPropagation();
8085 if(faux
._wasConsumed
){ return; } // if preventDefault was called
8086 this.defer(function(){ this._onInput(faux
); }); // widget notification after key has posted
8088 this.own(on(this.textbox
, "keydown, keypress, paste, cut, input, compositionend", lang
.hitch(this, handleEvent
)));
8091 _blankValue
: '', // if the textbox is blank, what value should be reported
8092 filter: function(val
){
8094 // Auto-corrections (such as trimming) that are applied to textbox
8095 // value on blur or form submit.
8097 // For MappedTextBox subclasses, this is called twice
8099 // - once with the display value
8100 // - once the value as set/returned by set('value', ...)
8102 // and get('value'), ex: a Number for NumberTextBox.
8104 // In the latter case it does corrections like converting null to NaN. In
8105 // the former case the NumberTextBox.filter() method calls this.inherited()
8106 // to execute standard trimming code in TextBox.filter().
8108 // TODO: break this into two methods in 2.0
8111 // protected extension
8112 if(val
=== null){ return this._blankValue
; }
8113 if(typeof val
!= "string"){ return val
; }
8115 val
= lang
.trim(val
);
8118 val
= val
.toUpperCase();
8121 val
= val
.toLowerCase();
8123 if(this.propercase
){
8124 val
= val
.replace(/[^\s]+/g, function(word
){
8125 return word
.substring(0,1).toUpperCase() + word
.substring(1);
8131 _setBlurValue: function(){
8132 this._setValueAttr(this.get('value'), true);
8135 _onBlur: function(e
){
8136 if(this.disabled
){ return; }
8137 this._setBlurValue();
8138 this.inherited(arguments
);
8141 _isTextSelected: function(){
8142 return this.textbox
.selectionStart
!= this.textbox
.selectionEnd
;
8145 _onFocus: function(/*String*/ by
){
8146 if(this.disabled
|| this.readOnly
){ return; }
8148 // Select all text on focus via click if nothing already selected.
8149 // Since mouse-up will clear the selection, need to defer selection until after mouse-up.
8150 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
8151 if(this.selectOnClick
&& by
== "mouse"){
8152 this._selectOnClickHandle
= this.connect(this.domNode
, "onmouseup", function(){
8153 // Only select all text on first click; otherwise users would have no way to clear
8155 this.disconnect(this._selectOnClickHandle
);
8156 this._selectOnClickHandle
= null;
8158 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
8159 // and if not, then select all the text
8160 if(!this._isTextSelected()){
8161 _TextBoxMixin
.selectInputText(this.textbox
);
8164 // in case the mouseup never comes
8165 this.defer(function(){
8166 if(this._selectOnClickHandle
){
8167 this.disconnect(this._selectOnClickHandle
);
8168 this._selectOnClickHandle
= null;
8170 }, 500); // if mouseup not received soon, then treat it as some gesture
8172 // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
8173 // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
8174 this.inherited(arguments
);
8176 this._refreshState();
8180 // Overrides `dijit/_FormWidget/reset()`.
8181 // Additionally resets the displayed textbox value to ''
8182 this.textbox
.value
= '';
8183 this.inherited(arguments
);
8186 _setTextDirAttr: function(/*String*/ textDir
){
8188 // Setter for textDir.
8190 // Users shouldn't call this function; they should be calling
8191 // set('textDir', value)
8195 // only if new textDir is different from the old one
8196 // and on widgets creation.
8198 || this.textDir
!= textDir
){
8199 this._set("textDir", textDir
);
8200 // so the change of the textDir will take place immediately.
8201 this.applyTextDir(this.focusNode
, this.focusNode
.value
);
8207 _TextBoxMixin
._setSelectionRange
= dijit
._setSelectionRange = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
8208 if(element
.setSelectionRange
){
8209 element
.setSelectionRange(start
, stop
);
8213 _TextBoxMixin
.selectInputText
= dijit
.selectInputText = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
8215 // Select text in the input element argument, from start (default 0), to stop (default end).
8217 // TODO: use functions in _editor/selection.js?
8218 element
= dom
.byId(element
);
8219 if(isNaN(start
)){ start
= 0; }
8220 if(isNaN(stop
)){ stop
= element
.value
? element
.value
.length
: 0; }
8223 _TextBoxMixin
._setSelectionRange(element
, start
, stop
);
8224 }catch(e
){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
8227 return _TextBoxMixin
;
8231 'dijit/form/SimpleTextarea':function(){
8232 define("dijit/form/SimpleTextarea", [
8233 "dojo/_base/declare", // declare
8234 "dojo/dom-class", // domClass.add
8235 "dojo/sniff", // has("ie") has("opera")
8237 ], function(declare
, domClass
, has
, TextBox
){
8240 // dijit/form/SimpleTextarea
8243 return declare("dijit.form.SimpleTextarea", TextBox
, {
8245 // A simple textarea that degrades, and responds to
8246 // minimal LayoutContainer usage, and works with dijit/form/Form.
8247 // Doesn't automatically size according to input, like Textarea.
8250 // | <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
8253 // | new SimpleTextarea({ rows:20, cols:30 }, "foo");
8255 baseClass
: "dijitTextBox dijitTextArea",
8258 // The number of rows of text.
8262 // The number of characters per line.
8265 templateString
: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
8267 postMixInProperties: function(){
8268 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
8269 // TODO: parser will handle this in 2.0
8270 if(!this.value
&& this.srcNodeRef
){
8271 this.value
= this.srcNodeRef
.value
;
8273 this.inherited(arguments
);
8276 buildRendering: function(){
8277 this.inherited(arguments
);
8278 if(has("ie") && this.cols
){ // attribute selectors is not supported in IE6
8279 domClass
.add(this.textbox
, "dijitTextAreaCols");
8283 filter: function(/*String*/ value
){
8284 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
8285 // as \r\n instead of just \n
8287 value
= value
.replace(/\r/g,"");
8289 return this.inherited(arguments
);
8292 _onInput: function(/*Event?*/ e
){
8293 // Override TextBox._onInput() to enforce maxLength restriction
8295 var maxLength
= parseInt(this.maxLength
);
8296 var value
= this.textbox
.value
.replace(/\r/g,'');
8297 var overflow
= value
.length
- maxLength
;
8299 var textarea
= this.textbox
;
8300 if(textarea
.selectionStart
){
8301 var pos
= textarea
.selectionStart
;
8304 cr
= (this.textbox
.value
.substring(0,pos
).match(/\r/g) || []).length
;
8306 this.textbox
.value
= value
.substring(0,pos
-overflow
-cr
)+value
.substring(pos
-cr
);
8307 textarea
.setSelectionRange(pos
-overflow
, pos
-overflow
);
8308 }else if(this.ownerDocument
.selection
){ //IE
8310 var range
= this.ownerDocument
.selection
.createRange();
8311 // delete overflow characters
8312 range
.moveStart("character", -overflow
);
8319 this.inherited(arguments
);
8326 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n",
8327 'dijit/_base/window':function(){
8328 define("dijit/_base/window", [
8329 "dojo/window", // windowUtils.get
8330 "../main" // export symbol to dijit
8331 ], function(windowUtils
, dijit
){
8333 // dijit/_base/window
8338 // Back compatibility module, new code should use windowUtils directly instead of using this module.
8342 dijit
.getDocumentWindow = function(doc
){
8343 return windowUtils
.get(doc
);
8348 'dijit/PopupMenuItem':function(){
8349 define("dijit/PopupMenuItem", [
8350 "dojo/_base/declare", // declare
8351 "dojo/dom-style", // domStyle.set
8352 "dojo/query", // query
8353 "./registry", // registry.byNode
8356 ], function(declare
, domStyle
, query
, registry
, MenuItem
){
8359 // dijit/PopupMenuItem
8361 return declare("dijit.PopupMenuItem", MenuItem
, {
8363 // An item in a Menu that spawn a drop down (usually a drop down menu)
8365 _fillContent: function(){
8367 // When Menu is declared in markup, this code gets the menu label and
8368 // the popup widget from the srcNodeRef.
8370 // srcNodeRefinnerHTML contains both the menu item text and a popup widget
8371 // The first part holds the menu item text and the second part is the popup
8373 // | <div data-dojo-type="dijit/PopupMenuItem">
8374 // | <span>pick me</span>
8375 // | <popup> ... </popup>
8380 if(this.srcNodeRef
){
8381 var nodes
= query("*", this.srcNodeRef
);
8382 this.inherited(arguments
, [nodes
[0]]);
8384 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
8385 this.dropDownContainer
= this.srcNodeRef
;
8389 startup: function(){
8390 if(this._started
){ return; }
8391 this.inherited(arguments
);
8393 // we didn't copy the dropdown widget from the this.srcNodeRef, so it's in no-man's
8394 // land now. move it to win.doc.body.
8396 var node
= query("[widgetId]", this.dropDownContainer
)[0];
8397 this.popup
= registry
.byNode(node
);
8399 this.ownerDocumentBody
.appendChild(this.popup
.domNode
);
8400 this.popup
.startup();
8402 this.popup
.domNode
.style
.display
="none";
8403 if(this.arrowWrapper
){
8404 domStyle
.set(this.arrowWrapper
, "visibility", "");
8406 this.focusNode
.setAttribute("aria-haspopup", "true");
8409 destroyDescendants: function(/*Boolean*/ preserveDom
){
8411 // Destroy the popup, unless it's already been destroyed. This can happen because
8412 // the popup is a direct child of <body> even though it's logically my child.
8413 if(!this.popup
._destroyed
){
8414 this.popup
.destroyRecursive(preserveDom
);
8418 this.inherited(arguments
);
8424 'dojo/hccss':function(){
8425 define("dojo/hccss", [
8426 "require", // require.toUrl
8427 "./_base/config", // config.blankGif
8428 "./dom-class", // domClass.add
8429 "./dom-style", // domStyle.getComputedStyle
8432 "./_base/window" // win.body
8433 ], function(require
, config
, domClass
, domStyle
, has
, ready
, win
){
8441 // Test if computer is in high contrast mode (i.e. if browser is not displaying background images).
8442 // Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode.
8443 // Returns `has()` method;
8447 // Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!.
8448 has
.add("highcontrast", function(){
8449 // note: if multiple documents, doesn't matter which one we use
8450 var div
= win
.doc
.createElement("div");
8451 div
.style
.cssText
= "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" +
8452 "background-image: url(" + (config
.blankGif
|| require
.toUrl("./resources/blank.gif")) + ");";
8453 win
.body().appendChild(div
);
8455 var cs
= domStyle
.getComputedStyle(div
),
8456 bkImg
= cs
.backgroundImage
,
8457 hc
= (cs
.borderTopColor
== cs
.borderRightColor
) ||
8458 (bkImg
&& (bkImg
== "none" || bkImg
== "url(invalid-url:)" ));
8461 div
.outerHTML
= ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
8463 win
.body().removeChild(div
);
8469 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
8470 // change this module to depend on dojo/domReady!
8471 ready(90, function(){
8472 if(has("highcontrast")){
8473 domClass
.add(win
.body(), "dj_a11y");
8481 'dijit/form/RadioButton':function(){
8482 define("dijit/form/RadioButton", [
8483 "dojo/_base/declare", // declare
8485 "./_RadioButtonMixin"
8486 ], function(declare
, CheckBox
, _RadioButtonMixin
){
8489 // dijit/form/RadioButton
8491 return declare("dijit.form.RadioButton", [CheckBox
, _RadioButtonMixin
], {
8493 // Same as an HTML radio, but with fancy styling.
8495 baseClass
: "dijitRadio"
8500 'dijit/main':function(){
8501 define("dijit/main", [
8510 // The dijit package main module.
8511 // Deprecated. Users should access individual modules (ex: dijit/registry) directly.
8519 'dijit/_OnDijitClickMixin':function(){
8520 define("dijit/_OnDijitClickMixin", [
8522 "dojo/_base/array", // array.forEach
8523 "dojo/keys", // keys.ENTER keys.SPACE
8524 "dojo/_base/declare", // declare
8525 "dojo/has", // has("dom-addeventlistener")
8526 "dojo/_base/unload", // unload.addOnWindowUnload
8527 "dojo/_base/window", // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
8529 ], function(on
, array
, keys
, declare
, has
, unload
, win
, a11yclick
){
8532 // dijit/_OnDijitClickMixin
8534 var ret
= declare("dijit._OnDijitClickMixin", null, {
8536 /*Object|null*/ obj
,
8537 /*String|Function*/ event
,
8538 /*String|Function*/ method
){
8540 // Connects specified obj/event to specified method of this object
8541 // and registers for disconnect() on widget destroy.
8543 // Provide widget-specific analog to connect.connect, except with the
8544 // implicit use of this widget as the target object.
8545 // This version of connect also provides a special "ondijitclick"
8546 // event which triggers on a click or space or enter keyup.
8547 // Events connected with `this.connect` are disconnected upon
8550 // A handle that can be passed to `disconnect` in order to disconnect before
8551 // the widget is destroyed.
8553 // | var btn = new Button();
8554 // | // when foo.bar() is called, call the listener we're going to
8555 // | // provide in the scope of btn
8556 // | btn.connect(foo, "bar", function(){
8557 // | console.debug(this.toString());
8562 return this.inherited(arguments
, [obj
, event
== "ondijitclick" ? a11yclick
: event
, method
]);
8566 ret
.a11yclick
= a11yclick
; // back compat
8572 'dijit/InlineEditBox':function(){
8574 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
8575 define("dijit/InlineEditBox", [
8577 "dojo/_base/array", // array.forEach
8578 "dojo/_base/declare", // declare
8579 "dojo/dom-attr", // domAttr.set domAttr.get
8580 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
8581 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
8582 "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
8583 "dojo/_base/event", // event.stop
8584 "dojo/i18n", // i18n.getLocalization
8585 "dojo/_base/kernel", // kernel.deprecated
8586 "dojo/keys", // keys.ENTER keys.ESCAPE
8587 "dojo/_base/lang", // lang.getObject
8588 "dojo/sniff", // has("ie")
8592 "./_TemplatedMixin",
8593 "./_WidgetsInTemplateMixin",
8596 "./form/_TextBoxMixin",
8598 "dojo/text!./templates/InlineEditBox.html",
8599 "dojo/i18n!./nls/common"
8600 ], function(require
, array
, declare
, domAttr
, domClass
, domConstruct
, domStyle
, event
, i18n
, kernel
, keys
, lang
, has
, when
,
8601 fm
, _Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
, _Container
, Button
, _TextBoxMixin
, TextBox
, template
){
8604 // dijit/InlineEditBox
8606 var InlineEditor
= declare("dijit._InlineEditor", [_Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
], {
8608 // Internal widget used by InlineEditBox, displayed when in editing mode
8609 // to display the editor and maybe save/cancel buttons. Calling code should
8610 // connect to save/cancel methods to detect when editing is finished
8612 // Has mainly the same parameters as InlineEditBox, plus these values:
8615 // Set of CSS attributes of display node, to replicate in editor
8618 // Value as an HTML string or plain text string, depending on renderAsHTML flag
8620 templateString
: template
,
8622 postMixInProperties: function(){
8623 this.inherited(arguments
);
8624 this.messages
= i18n
.getLocalization("dijit", "common", this.lang
);
8625 array
.forEach(["buttonSave", "buttonCancel"], function(prop
){
8627 this[prop
] = this.messages
[prop
];
8632 buildRendering: function(){
8633 this.inherited(arguments
);
8635 // Create edit widget in place in the template
8636 // TODO: remove getObject() for 2.0
8637 var Cls
= typeof this.editor
== "string" ? (lang
.getObject(this.editor
) || require(this.editor
)) : this.editor
;
8639 // Copy the style from the source
8640 // Don't copy ALL properties though, just the necessary/applicable ones.
8641 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
8642 // is a relative value like 200%, rather than an absolute value like 24px, and
8643 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
8644 var srcStyle
= this.sourceStyle
,
8645 editStyle
= "line-height:" + srcStyle
.lineHeight
+ ";",
8646 destStyle
= domStyle
.getComputedStyle(this.domNode
);
8647 array
.forEach(["Weight", "Family", "Size", "Style"], function(prop
){
8648 var textStyle
= srcStyle
["font" + prop
],
8649 wrapperStyle
= destStyle
["font" + prop
];
8650 if(wrapperStyle
!= textStyle
){
8651 editStyle
+= "font-" + prop
+ ":" + srcStyle
["font" + prop
] + ";";
8654 array
.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop
){
8655 this.domNode
.style
[prop
] = srcStyle
[prop
];
8657 var width
= this.inlineEditBox
.width
;
8658 if(width
== "100%"){
8660 editStyle
+= "width:100%;";
8661 this.domNode
.style
.display
= "block";
8663 // inline-block mode
8664 editStyle
+= "width:" + (width
+ (Number(width
) == width
? "px" : "")) + ";";
8666 var editorParams
= lang
.delegate(this.inlineEditBox
.editorParams
, {
8670 textDir
: this.textDir
8672 editorParams
[ "displayedValue" in Cls
.prototype ? "displayedValue" : "value"] = this.value
;
8673 this.editWidget
= new Cls(editorParams
, this.editorPlaceholder
);
8675 if(this.inlineEditBox
.autoSave
){
8676 // Remove the save/cancel buttons since saving is done by simply tabbing away or
8677 // selecting a value from the drop down list
8678 domConstruct
.destroy(this.buttonContainer
);
8682 postCreate: function(){
8683 this.inherited(arguments
);
8685 var ew
= this.editWidget
;
8687 if(this.inlineEditBox
.autoSave
){
8688 // Selecting a value from a drop down list causes an onChange event and then we save
8689 this.connect(ew
, "onChange", "_onChange");
8691 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
8692 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
8693 // so this is the only way we can see the key press event.
8694 this.connect(ew
, "onKeyPress", "_onKeyPress");
8696 // If possible, enable/disable save button based on whether the user has changed the value
8697 if("intermediateChanges" in ew
){
8698 ew
.set("intermediateChanges", true);
8699 this.connect(ew
, "onChange", "_onIntermediateChange");
8700 this.saveButton
.set("disabled", true);
8705 startup: function(){
8706 this.editWidget
.startup();
8707 this.inherited(arguments
);
8710 _onIntermediateChange: function(/*===== val =====*/){
8712 // Called for editor widgets that support the intermediateChanges=true flag as a way
8713 // to detect when to enable/disabled the save button
8714 this.saveButton
.set("disabled", (this.getValue() == this._resetValue
) || !this.enableSave());
8717 destroy: function(){
8718 this.editWidget
.destroy(true); // let the parent wrapper widget clean up the DOM
8719 this.inherited(arguments
);
8722 getValue: function(){
8724 // Return the [display] value of the edit widget
8725 var ew
= this.editWidget
;
8726 return String(ew
.get("displayedValue" in ew
? "displayedValue" : "value"));
8729 _onKeyPress: function(e
){
8731 // Handler for keypress in the edit box in autoSave mode.
8733 // For autoSave widgets, if Esc/Enter, call cancel/save.
8737 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
){
8738 if(e
.altKey
|| e
.ctrlKey
){
8741 // If Enter/Esc pressed, treat as save/cancel.
8742 if(e
.charOrCode
== keys
.ESCAPE
){
8744 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
8745 }else if(e
.charOrCode
== keys
.ENTER
&& e
.target
.tagName
== "INPUT"){
8747 this._onChange(); // fire _onBlur and then save
8750 // _onBlur will handle TAB automatically by allowing
8751 // the TAB to change focus before we mess with the DOM: #6227
8752 // Expounding by request:
8753 // The current focus is on the edit widget input field.
8754 // save() will hide and destroy this widget.
8755 // We want the focus to jump from the currently hidden
8756 // displayNode, but since it's hidden, it's impossible to
8757 // unhide it, focus it, and then have the browser focus
8758 // away from it to the next focusable element since each
8759 // of these events is asynchronous and the focus-to-next-element
8760 // is already queued.
8761 // So we allow the browser time to unqueue the move-focus event
8762 // before we do all the hide/show stuff.
8766 _onBlur: function(){
8768 // Called when focus moves outside the editor
8772 this.inherited(arguments
);
8773 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
){
8774 if(this.getValue() == this._resetValue
){
8776 }else if(this.enableSave()){
8782 _onChange: function(){
8784 // Called when the underlying widget fires an onChange event,
8785 // such as when the user selects a value from the drop down list of a ComboBox,
8786 // which means that the user has finished entering the value and we should save.
8790 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
&& this.enableSave()){
8791 fm
.focus(this.inlineEditBox
.displayNode
); // fires _onBlur which will save the formatted value
8795 enableSave: function(){
8797 // User overridable function returning a Boolean to indicate
8798 // if the Save button should be enabled or not - usually due to invalid conditions
8801 return this.editWidget
.isValid
? this.editWidget
.isValid() : true;
8806 // Focus the edit widget.
8810 this.editWidget
.focus();
8812 if(this.editWidget
.focusNode
){
8813 // IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
8814 fm
._onFocusNode(this.editWidget
.focusNode
);
8816 if(this.editWidget
.focusNode
.tagName
== "INPUT"){
8817 this.defer(function(){
8818 _TextBoxMixin
.selectInputText(this.editWidget
.focusNode
);
8826 var InlineEditBox
= declare("dijit.InlineEditBox", _Widget
, {
8828 // An element with in-line edit capabilities
8831 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
8832 // when you click it, an editor shows up in place of the original
8833 // text. Optionally, Save and Cancel button are displayed below the edit widget.
8834 // When Save is clicked, the text is pulled from the edit
8835 // widget and redisplayed and the edit widget is again hidden.
8836 // By default a plain Textarea widget is used as the editor (or for
8837 // inline values a TextBox), but you can specify an editor such as
8838 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
8839 // An edit widget must support the following API to be used:
8841 // - displayedValue or value as initialization parameter,
8842 // and available through set('displayedValue') / set('value')
8844 // - DOM-node focusNode = node containing editable text
8846 // editing: [readonly] Boolean
8847 // Is the node currently in edit mode?
8850 // autoSave: Boolean
8851 // Changing the value automatically saves it; don't have to push save button
8852 // (and save button isn't even displayed)
8855 // buttonSave: String
8856 // Save button label
8859 // buttonCancel: String
8860 // Cancel button label
8863 // renderAsHtml: Boolean
8864 // Set this to true if the specified Editor's value should be interpreted as HTML
8865 // rather than plain text (ex: `dijit.Editor`)
8866 renderAsHtml
: false,
8868 // editor: String|Function
8869 // MID (ex: "dijit/form/TextBox") or constructor for editor widget
8872 // editorWrapper: String|Function
8873 // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
8875 editorWrapper
: InlineEditor
,
8877 // editorParams: Object
8878 // Set of parameters for editor, like {required: true}
8881 // disabled: Boolean
8882 // If true, clicking the InlineEditBox to edit it will have no effect.
8885 onChange: function(/*===== value =====*/){
8887 // Set this handler to be notified of changes to value.
8892 onCancel: function(){
8894 // Set this handler to be notified when editing is cancelled.
8900 // Width of editor. By default it's width=100% (ie, block mode).
8904 // The display value of the widget in read-only mode
8907 // noValueIndicator: [const] String
8908 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
8909 noValueIndicator
: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
8910 "<span style='font-family: wingdings; text-decoration: underline;'>    ✍    </span>" :
8911 "<span style='text-decoration: underline;'>    ✍    </span>", //   ==
8913 constructor: function(/*===== params, srcNodeRef =====*/){
8915 // Create the widget.
8916 // params: Object|null
8917 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
8918 // and functions, typically callbacks like onClick.
8919 // The hash can contain any of the widget's properties, excluding read-only properties.
8920 // srcNodeRef: DOMNode|String?
8921 // If a srcNodeRef (DOM node) is specified:
8923 // - use srcNodeRef.innerHTML as my value
8924 // - replace srcNodeRef with my generated DOM tree
8926 this.editorParams
= {};
8929 postMixInProperties: function(){
8930 this.inherited(arguments
);
8932 // save pointer to original source node, since Widget nulls-out srcNodeRef
8933 this.displayNode
= this.srcNodeRef
;
8935 // connect handlers to the display node
8937 ondijitclick
: "_onClick",
8938 onmouseover
: "_onMouseOver",
8939 onmouseout
: "_onMouseOut",
8940 onfocus
: "_onMouseOver",
8941 onblur
: "_onMouseOut"
8943 for(var name
in events
){
8944 this.connect(this.displayNode
, name
, events
[name
]);
8946 this.displayNode
.setAttribute("role", "button");
8947 if(!this.displayNode
.getAttribute("tabIndex")){
8948 this.displayNode
.setAttribute("tabIndex", 0);
8951 if(!this.value
&& !("value" in this.params
)){ // "" is a good value if specified directly so check params){
8952 this.value
= lang
.trim(this.renderAsHtml
? this.displayNode
.innerHTML
:
8953 (this.displayNode
.innerText
|| this.displayNode
.textContent
|| ""));
8956 this.displayNode
.innerHTML
= this.noValueIndicator
;
8959 domClass
.add(this.displayNode
, 'dijitInlineEditBoxDisplayMode');
8962 setDisabled: function(/*Boolean*/ disabled
){
8964 // Deprecated. Use set('disabled', ...) instead.
8967 kernel
.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
8968 this.set('disabled', disabled
);
8971 _setDisabledAttr: function(/*Boolean*/ disabled
){
8973 // Hook to make set("disabled", ...) work.
8974 // Set disabled state of widget.
8975 this.domNode
.setAttribute("aria-disabled", disabled
? "true" : "false");
8977 this.displayNode
.removeAttribute("tabIndex");
8979 this.displayNode
.setAttribute("tabIndex", 0);
8981 domClass
.toggle(this.displayNode
, "dijitInlineEditBoxDisplayModeDisabled", disabled
);
8982 this._set("disabled", disabled
);
8985 _onMouseOver: function(){
8987 // Handler for onmouseover and onfocus event.
8991 domClass
.add(this.displayNode
, "dijitInlineEditBoxDisplayModeHover");
8995 _onMouseOut: function(){
8997 // Handler for onmouseout and onblur event.
9000 domClass
.remove(this.displayNode
, "dijitInlineEditBoxDisplayModeHover");
9003 _onClick: function(/*Event*/ e
){
9005 // Handler for onclick event.
9016 // Since FF gets upset if you move a node while in an event handler for that node...
9022 // Display the editor widget in place of the original (read only) markup.
9026 if(this.disabled
|| this.editing
){
9029 this._set('editing', true);
9031 // save some display node values that can be restored later
9032 this._savedTabIndex
= domAttr
.get(this.displayNode
, "tabIndex") || "0";
9034 if(this.wrapperWidget
){
9035 var ew
= this.wrapperWidget
.editWidget
;
9036 ew
.set("displayedValue" in ew
? "displayedValue" : "value", this.value
);
9038 // Placeholder for edit widget
9039 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
9040 // when Calendar dropdown appears, which happens automatically on focus.
9041 var placeholder
= domConstruct
.create("span", null, this.domNode
, "before");
9043 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
9044 var Ewc
= typeof this.editorWrapper
== "string" ? lang
.getObject(this.editorWrapper
) : this.editorWrapper
;
9045 this.wrapperWidget
= new Ewc({
9047 buttonSave
: this.buttonSave
,
9048 buttonCancel
: this.buttonCancel
,
9051 tabIndex
: this._savedTabIndex
,
9052 editor
: this.editor
,
9053 inlineEditBox
: this,
9054 sourceStyle
: domStyle
.getComputedStyle(this.displayNode
),
9055 save
: lang
.hitch(this, "save"),
9056 cancel
: lang
.hitch(this, "cancel"),
9057 textDir
: this.textDir
9059 if(!this.wrapperWidget
._started
){
9060 this.wrapperWidget
.startup();
9066 var ww
= this.wrapperWidget
;
9068 // to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
9069 // and then when it's finished rendering, we switch from display mode to editor
9070 // position: absolute releases screen space allocated to the display node
9071 // opacity:0 is the same as visibility: hidden but is still focusable
9072 // visibility: hidden removes focus outline
9074 domClass
.add(this.displayNode
, "dijitOffScreen");
9075 domClass
.remove(ww
.domNode
, "dijitOffScreen");
9076 domStyle
.set(ww
.domNode
, { visibility
: "visible" });
9077 domAttr
.set(this.displayNode
, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
9079 // After edit widget has finished initializing (in particular need to wait for dijit.Editor),
9080 // or immediately if there is no onLoadDeferred Deferred,
9081 // replace the display widget with edit widget, leaving them both displayed for a brief time so that
9082 // focus can be shifted without incident.
9083 when(ww
.editWidget
.onLoadDeferred
, lang
.hitch(ww
, function(){
9084 this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
9085 this.focus(); // both nodes are showing, so we can switch focus safely
9086 this._resetValue
= this.getValue();
9091 _onBlur: function(){
9093 // Called when focus moves outside the InlineEditBox.
9094 // Performs garbage collection.
9098 this.inherited(arguments
);
9100 /* causes IE focus problems, see TooltipDialog_a11y.html...
9101 this.defer(function(){
9102 if(this.wrapperWidget){
9103 this.wrapperWidget.destroy();
9104 delete this.wrapperWidget;
9111 destroy: function(){
9112 if(this.wrapperWidget
&& !this.wrapperWidget
._destroyed
){
9113 this.wrapperWidget
.destroy();
9114 delete this.wrapperWidget
;
9116 this.inherited(arguments
);
9119 _showText: function(/*Boolean*/ focus
){
9121 // Revert to display mode, and optionally focus on display node
9125 var ww
= this.wrapperWidget
;
9126 domStyle
.set(ww
.domNode
, { visibility
: "hidden" }); // hide the editor from mouse/keyboard events
9127 domClass
.add(ww
.domNode
, "dijitOffScreen");
9128 domClass
.remove(this.displayNode
, "dijitOffScreen");
9129 domAttr
.set(this.displayNode
, "tabIndex", this._savedTabIndex
);
9131 fm
.focus(this.displayNode
);
9135 save: function(/*Boolean*/ focus
){
9137 // Save the contents of the editor and revert to display mode.
9139 // Focus on the display mode text
9143 if(this.disabled
|| !this.editing
){
9146 this._set('editing', false);
9148 var ww
= this.wrapperWidget
;
9149 var value
= ww
.getValue();
9150 this.set('value', value
); // display changed, formatted value
9152 this._showText(focus
); // set focus as needed
9155 setValue: function(/*String*/ val
){
9157 // Deprecated. Use set('value', ...) instead.
9160 kernel
.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
9161 return this.set("value", val
);
9164 _setValueAttr: function(/*String*/ val
){
9166 // Hook to make set("value", ...) work.
9167 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
9169 val
= lang
.trim(val
);
9170 var renderVal
= this.renderAsHtml
? val
: val
.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, ""
;").replace(/\n/g, "<br
>");
9171 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
9172 this._set("value
", val);
9175 // tell the world that we have changed
9176 this.defer(function(){
9178 }); // defer prevents browser freeze for long-running event handlers
9180 // contextual (auto) text direction depends on the text value
9181 if(this.textDir == "auto
"){
9182 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9186 getValue: function(){
9188 // Deprecated. Use get('value') instead.
9191 kernel.deprecated("dijit
.InlineEditBox
.getValue() is deprecated
. Use
get('value') instead
.", "", "2.0");
9192 return this.get("value
");
9195 cancel: function(/*Boolean*/ focus){
9197 // Revert to display mode, discarding any changes made in the editor
9201 if(this.disabled || !this.editing){
9204 this._set('editing', false);
9206 // tell the world that we have no changes
9207 this.defer("onCancel
"); // defer prevents browser freeze for long-running event handlers
9209 this._showText(focus);
9212 _setTextDirAttr: function(/*String*/ textDir){
9214 // Setter for textDir.
9216 // Users shouldn't call this function; they should be calling
9217 // set('textDir', value)
9220 if(!this._created || this.textDir != textDir){
9221 this._set("textDir
", textDir);
9222 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9223 this.displayNode.align = this.dir == "rtl
" ? "right
" : "left
"; //fix the text alignment
9228 InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
9230 return InlineEditBox;
9233 'dojo/selector/acme':function(){
9234 define("dojo
/selector/acme", [
9235 "../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window
"
9236 ], function(dom, has, array, lang, win){
9239 // dojo/selector/acme
9242 acme architectural overview:
9244 acme is a relatively full-featured CSS3 query library. It is
9245 designed to take any valid CSS3 selector and return the nodes matching
9246 the selector. To do this quickly, it processes queries in several
9247 steps, applying caching where profitable.
9249 The steps (roughly in reverse order of the way they appear in the code):
9250 1.) check to see if we already have a "query dispatcher
"
9251 - if so, use that with the given parameterization. Skip to step 4.
9252 2.) attempt to determine which branch to dispatch the query to:
9253 - JS (optimized DOM iteration)
9254 - native (FF3.1+, Safari 3.1+, IE 8+)
9255 3.) tokenize and convert to executable "query dispatcher
"
9256 - this is where the lion's share of the complexity in the
9257 system lies. In the DOM version, the query dispatcher is
9258 assembled as a chain of "yes
/no
" test functions pertaining to
9259 a section of a simple query statement (".blah
:nth
-child(odd
)"
9260 but not "div div
", which is 2 simple statements). Individual
9261 statement dispatchers are cached (to prevent re-definition)
9262 as are entire dispatch chains (to make re-execution of the
9264 4.) the resulting query dispatcher is called in the passed scope
9265 (by default the top-level document)
9266 - for DOM queries, this results in a recursive, top-down
9267 evaluation of nodes based on each simple query section
9268 - for native implementations, this may mean working around spec
9270 5.) matched nodes are pruned to ensure they are unique (if necessary)
9274 ////////////////////////////////////////////////////////////////////////
9276 ////////////////////////////////////////////////////////////////////////
9278 // if you are extracting acme for use in your own system, you will
9279 // need to provide these methods and properties. No other porting should be
9280 // necessary, save for configuring the system to use a class other than
9281 // dojo/NodeList as the return instance instantiator
9282 var trim = lang.trim;
9283 var each = array.forEach;
9285 var getDoc = function(){ return win.doc; };
9286 // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
9287 var cssCaseBug = (getDoc().compatMode) == "BackCompat
";
9289 ////////////////////////////////////////////////////////////////////////
9291 ////////////////////////////////////////////////////////////////////////
9294 var specials = ">~+";
9296 // global thunk to determine whether we should treat the current query as
9297 // case sensitive or not. This switch is flipped by the query evaluator
9298 // based on the document passed as the context to search.
9299 var caseSensitive = false;
9302 var yesman = function(){ return true; };
9304 ////////////////////////////////////////////////////////////////////////
9306 ////////////////////////////////////////////////////////////////////////
9308 var getQueryParts = function(query){
9310 // state machine for query tokenization
9312 // instead of using a brittle and slow regex-based CSS parser,
9313 // acme implements an AST-style query representation. This
9314 // representation is only generated once per query. For example,
9315 // the same query run multiple times or under different root nodes
9316 // does not re-parse the selector expression but instead uses the
9317 // cached data structure. The state machine implemented here
9318 // terminates on the last " " (space) character and returns an
9319 // ordered array of query component structures (or "parts
"). Each
9320 // part represents an operator or a simple CSS filtering
9321 // expression. The structure for parts is documented in the code
9326 // this code is designed to run fast and compress well. Sacrifices
9327 // to readability and maintainability have been made. Your best
9328 // bet when hacking the tokenizer is to put The Donnas on *really*
9329 // loud (may we recommend their "Spend The Night
" release?) and
9330 // just assume you're gonna make mistakes. Keep the unit tests
9331 // open and run them frequently. Knowing is half the battle ;-)
9332 if(specials.indexOf(query.slice(-1)) >= 0){
9333 // if we end with a ">", "+", or "~", that means we're implicitly
9334 // searching all children, so make it explicit
9337 // if you have not provided a terminator, one will be provided for
9342 var ts = function(/*Integer*/ s, /*Integer*/ e){
9345 // take an index to start a string slice from and an end position
9346 // and return a trimmed copy of that sub-string
9347 return trim(query.slice(s, e));
9350 // the overall data graph of the full query, as represented by queryPart objects
9351 var queryParts = [];
9354 // state keeping vars
9355 var inBrackets = -1, inParens = -1, inMatchFor = -1,
9356 inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
9357 lc = "", cc = "", pStart;
9360 var x = 0, // index in the query
9362 currentPart = null, // data structure representing the entire clause
9363 _cp = null; // the current pseudo or attr matcher
9365 // several temporary variables are assigned to this structure during a
9366 // potential sub-expression match:
9368 // a string representing the current full attribute match in a
9369 // bracket expression
9371 // if there's an operator in a bracket expression, this is
9372 // used to keep track of it
9374 // the internals of parenthetical expression for a pseudo. for
9375 // :nth-child(2n+1), value might be "2n
+1"
9377 var endTag = function(){
9378 // called when the tokenizer hits the end of a particular tag name.
9379 // Re-sets state variables for tag matching and sets up the matcher
9380 // to handle the next type of token (tag or operator).
9382 var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
9383 currentPart[ (specials.indexOf(tv) < 0) ? "tag
" : "oper
" ] = tv;
9388 var endId = function(){
9389 // called when the tokenizer might be at the end of an ID portion of a match
9391 currentPart.id = ts(inId, x).replace(/\\/g, "");
9396 var endClass = function(){
9397 // called when the tokenizer might be at the end of a class name
9398 // match. CSS allows for multiple classes, so we augment the
9399 // current item with another class in its list
9401 currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
9406 var endAll = function(){
9407 // at the end of a simple fragment, so wall off the matches
9413 var endPart = function(){
9416 currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
9418 // hint to the selector engine to tell it whether or not it
9419 // needs to do any iteration. Many simple selectors don't, and
9420 // we can avoid significant construction-time work by advising
9421 // the system to skip them
9422 currentPart.loops = (
9423 currentPart.pseudos.length ||
9424 currentPart.attrs.length ||
9425 currentPart.classes.length );
9427 currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
9430 // otag/tag are hints to suggest to the system whether or not
9431 // it's an operator or a tag. We save a copy of otag since the
9432 // tag name is cast to upper-case in regular HTML matches. The
9433 // system has a global switch to figure out if the current
9434 // expression needs to be case sensitive or not and it will use
9435 // otag or tag accordingly
9436 currentPart.otag = currentPart.tag = (currentPart["oper
"]) ? null : (currentPart.tag || "*");
9438 if(currentPart.tag){
9439 // if we're in a case-insensitive HTML doc, we likely want
9440 // the toUpperCase when matching on element.tagName. If we
9441 // do it here, we can skip the string op per node
9443 currentPart.tag = currentPart.tag.toUpperCase();
9446 // add the part to the list
9447 if(queryParts.length && (queryParts[queryParts.length-1].oper)){
9448 // operators are always infix, so we remove them from the
9449 // list and attach them to the next match. The evaluator is
9450 // responsible for sorting out how to handle them.
9451 currentPart.infixOper = queryParts.pop();
9452 currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
9454 console.debug( "swapping out the infix
",
9455 currentPart.infixOper,
9456 "and attaching it to
",
9460 queryParts.push(currentPart);
9465 // iterate over the query, character by character, building up a
9466 // list of query part objects
9467 for(; lc=cc, cc=query.charAt(x), x < ql; x++){
9468 // cc: the current character in the match
9469 // lc: the last character (if any)
9471 // someone is trying to escape something, so don't try to match any
9472 // fragments. We assume we're inside a literal.
9473 if(lc == "\\"){ continue; }
9474 if(!currentPart){ // a part was just ended or none has yet been created
9475 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
9477 // rules describe full CSS sub-expressions, like:
9479 // .className:first-child
9481 // thinger > div.howdy[type=thinger]
9482 // the indidual components of the previous query would be
9483 // split into 3 parts that would be represented a structure like:
9486 // query: "thinger
",
9490 // query: "div
.howdy
[type
=thinger
]",
9491 // classes: ["howdy
"],
9499 query: null, // the full text of the part's rule
9500 pseudos: [], // CSS supports multiple pseud-class matches in a single rule
9501 attrs: [], // CSS supports multi-attribute match, so we need an array
9502 classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
9503 tag: null, // only one tag...
9504 oper: null, // ...or operator per component. Note that these wind up being exclusive.
9505 id: null, // the id component of a rule
9507 return caseSensitive ? this.otag : this.tag;
9511 // if we don't have a part, we assume we're going to start at
9512 // the beginning of a match, which should be a tag name. This
9513 // might fault a little later on, but we detect that and this
9514 // iteration will still be fine.
9518 // Skip processing all quoted characters.
9519 // If we are inside quoted text then currentQuoteChar stores the character that began the quote,
9520 // thus that character that will end it.
9521 if(currentQuoteChar){
9522 if(cc == currentQuoteChar){
9523 currentQuoteChar = null;
9526 }else if (cc == "'" || cc == '"'){
9527 currentQuoteChar = cc;
9531 if(inBrackets >= 0){
9532 // look for a the close first
9533 if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
9535 // no attribute match was previously begun, so we
9536 // assume this is an attribute existence match in the
9537 // form of [someAttributeName]
9538 _cp.attr = ts(inBrackets+1, x);
9540 // we had an attribute already, so we know that we're
9541 // matching some sort of value, as in [attrName=howdy]
9542 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
9544 var cmf = _cp.matchFor;
9546 // try to strip quotes from the matchFor value. We want
9547 // [attrName=howdy] to match the same
9548 // as [attrName = 'howdy' ]
9549 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
9550 _cp.matchFor = cmf.slice(1, -1);
9553 // remove backslash escapes from an attribute match, since DOM
9554 // querying will get attribute values without backslashes
9556 _cp.matchFor = _cp.matchFor.replace(/\\/g, "");
9559 // end the attribute by adding it to the list of attributes.
9560 currentPart.attrs.push(_cp);
9561 _cp = null; // necessary?
9562 inBrackets = inMatchFor = -1;
9563 }else if(cc == "="){
9564 // if the last char was an operator prefix, make sure we
9565 // record it along with the "=" operator.
9566 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
9567 _cp.type = addToCc+cc;
9568 _cp.attr = ts(inBrackets+1, x-addToCc.length);
9571 // now look for other clause parts
9572 }else if(inParens >= 0){
9573 // if we're in a parenthetical expression, we need to figure
9574 // out if it's attached to a pseudo-selector rule like
9578 _cp.value = ts(inParens+1, x);
9580 inPseudo = inParens = -1;
9582 }else if(cc == "#"){
9583 // start of an ID match
9586 }else if(cc == "."){
9587 // start of a class match
9590 }else if(cc == ":"){
9591 // start of a pseudo-selector match
9594 }else if(cc == "["){
9595 // start of an attribute match.
9598 // provide a new structure for the attribute match to fill-in
9601 attr: null, type: null, matchFor: null
9604 }else if(cc == "("){
9605 // we really only care if we've entered a parenthetical
9606 // expression if we're already inside a pseudo-selector match
9608 // provide a new structure for the pseudo match to fill-in
9610 name: ts(inPseudo+1, x),
9613 currentPart.pseudos.push(_cp);
9618 // if it's a space char and the last char is too, consume the
9619 // current one without doing more work
9629 ////////////////////////////////////////////////////////////////////////
9630 // DOM query infrastructure
9631 ////////////////////////////////////////////////////////////////////////
9633 var agree = function(first, second){
9634 // the basic building block of the yes/no chaining system. agree(f1,
9635 // f2) generates a new function which returns the boolean results of
9636 // both of the passed functions to a single logical-anded result. If
9637 // either are not passed, the other is used exclusively.
9638 if(!first){ return second; }
9639 if(!second){ return first; }
9642 return first.apply(window, arguments) && second.apply(window, arguments);
9646 var getArr = function(i, arr){
9647 // helps us avoid array alloc when we don't need it
9648 var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
9653 var _isElement = function(n){ return (1 == n.nodeType); };
9655 // FIXME: need to coalesce _getAttr with defaultGetter
9657 var _getAttr = function(elem, attr){
9658 if(!elem){ return blank; }
9659 if(attr == "class"){
9660 return elem.className || blank;
9663 return elem.htmlFor || blank;
9665 if(attr == "style
"){
9666 return elem.style.cssText || blank;
9668 return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
9672 "*=": function(attr, value){
9673 return function(elem){
9675 // an E element whose "foo
" attribute value contains
9676 // the substring "bar
"
9677 return (_getAttr(elem, attr).indexOf(value)>=0);
9680 "^=": function(attr, value){
9682 // an E element whose "foo
" attribute value begins exactly
9683 // with the string "bar
"
9684 return function(elem){
9685 return (_getAttr(elem, attr).indexOf(value)==0);
9688 "$=": function(attr, value){
9690 // an E element whose "foo
" attribute value ends exactly
9691 // with the string "bar
"
9692 return function(elem){
9693 var ea = " "+_getAttr(elem, attr);
9694 var lastIndex = ea.lastIndexOf(value);
9695 return lastIndex > -1 && (lastIndex==(ea.length-value.length));
9698 "~=": function(attr, value){
9700 // an E element whose "foo
" attribute value is a list of
9701 // space-separated values, one of which is exactly equal
9704 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
9705 var tval = " "+value+" ";
9706 return function(elem){
9707 var ea = " "+_getAttr(elem, attr)+" ";
9708 return (ea.indexOf(tval)>=0);
9711 "|=": function(attr, value){
9712 // E[hreflang|="en
"]
9713 // an E element whose "hreflang
" attribute has a
9714 // hyphen-separated list of values beginning (from the
9716 var valueDash = value+"-";
9717 return function(elem){
9718 var ea = _getAttr(elem, attr);
9721 (ea.indexOf(valueDash)==0)
9725 "=": function(attr, value){
9726 return function(elem){
9727 return (_getAttr(elem, attr) == value);
9732 // avoid testing for node type if we can. Defining this in the negative
9733 // here to avoid negation in the fast path.
9734 var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
9735 var _ns = !_noNES ? "nextElementSibling
" : "nextSibling
";
9736 var _ps = !_noNES ? "previousElementSibling
" : "previousSibling
";
9737 var _simpleNodeTest = (_noNES ? _isElement : yesman);
9739 var _lookLeft = function(node){
9741 while(node = node[_ps]){
9742 if(_simpleNodeTest(node)){ return false; }
9747 var _lookRight = function(node){
9749 while(node = node[_ns]){
9750 if(_simpleNodeTest(node)){ return false; }
9755 var getNodeIndex = function(node){
9756 var root = node.parentNode;
9757 root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
9759 tret = root.children || root.childNodes,
9760 ci = (node["_i
"]||node.getAttribute("_i
")||-1),
9761 cl = (root["_l
"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l
") : -1));
9763 if(!tret){ return -1; }
9764 var l = tret.length;
9766 // we calculate the parent length as a cheap way to invalidate the
9767 // cache. It's not 100% accurate, but it's much more honest than what
9768 // other libraries do
9769 if( cl == l && ci >= 0 && cl >= 0 ){
9770 // if it's legit, tag and release
9774 // else re-key things
9775 if(has("ie
") && typeof root.setAttribute !== "undefined"){
9776 root.setAttribute("_l
", l);
9781 for(var te = root["firstElementChild
"]||root["firstChild
"]; te; te = te[_ns]){
9782 if(_simpleNodeTest(te)){
9784 te.setAttribute("_i
", ++i);
9790 // shortcutting the return at this step in indexing works
9791 // very well for benchmarking but we avoid it here since
9792 // it leads to potential O(n^2) behavior in sequential
9793 // getNodexIndex operations on a previously un-indexed
9794 // parent. We may revisit this at a later time, but for
9795 // now we just want to get the right answer more often
9804 var isEven = function(elem){
9805 return !((getNodeIndex(elem)) % 2);
9808 var isOdd = function(elem){
9809 return ((getNodeIndex(elem)) % 2);
9813 "checked
": function(name, condition){
9814 return function(elem){
9815 return !!("checked
" in elem ? elem.checked : elem.selected);
9818 "disabled
": function(name, condition){
9819 return function(elem){
9820 return elem.disabled;
9823 "enabled
": function(name, condition){
9824 return function(elem){
9825 return !elem.disabled;
9828 "first
-child
": function(){ return _lookLeft; },
9829 "last
-child
": function(){ return _lookRight; },
9830 "only
-child
": function(name, condition){
9831 return function(node){
9832 return _lookLeft(node) && _lookRight(node);
9835 "empty
": function(name, condition){
9836 return function(elem){
9837 // DomQuery and jQuery get this wrong, oddly enough.
9838 // The CSS 3 selectors spec is pretty explicit about it, too.
9839 var cn = elem.childNodes;
9840 var cnl = elem.childNodes.length;
9841 // if(!cnl){ return true; }
9842 for(var x=cnl-1; x >= 0; x--){
9843 var nt = cn[x].nodeType;
9844 if((nt === 1)||(nt == 3)){ return false; }
9849 "contains
": function(name, condition){
9850 var cz = condition.charAt(0);
9851 if( cz == '"' || cz == "'" ){ //remove quote
9852 condition = condition.slice(1, -1);
9854 return function(elem){
9855 return (elem.innerHTML.indexOf(condition) >= 0);
9858 "not
": function(name, condition){
9859 var p = getQueryParts(condition)[0];
9860 var ignores = { el: 1 };
9864 if(!p.classes.length){
9865 ignores.classes = 1;
9867 var ntf = getSimpleFilterFunc(p, ignores);
9868 return function(elem){
9869 return (!ntf(elem));
9872 "nth
-child
": function(name, condition){
9874 // avoid re-defining function objects if we can
9875 if(condition == "odd
"){
9877 }else if(condition == "even
"){
9880 // FIXME: can we shorten this?
9881 if(condition.indexOf("n
") != -1){
9882 var tparts = condition.split("n
", 2);
9883 var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
9884 var idx = tparts[1] ? pi(tparts[1]) : 0;
9885 var lb = 0, ub = -1;
9888 idx = (idx % pred) && (pred + (idx % pred));
9891 lb = idx - idx % pred;
9897 // idx has to be greater than 0 when pred is negative;
9898 // shall we throw an error here?
9905 return function(elem){
9906 var i = getNodeIndex(elem);
9907 return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
9913 var ncount = pi(condition);
9914 return function(elem){
9915 return (getNodeIndex(elem) == ncount);
9920 var defaultGetter = (has("ie
") < 9 || has("ie
") == 9 && has("quirks
")) ? function(cond){
9921 var clc = cond.toLowerCase();
9922 if(clc == "class"){ cond = "className
"; }
9923 return function(elem){
9924 return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
9927 return function(elem){
9928 return (elem && elem.getAttribute && elem.hasAttribute(cond));
9932 var getSimpleFilterFunc = function(query, ignores){
9933 // generates a node tester function based on the passed query part. The
9934 // query part is one of the structures generated by the query parser
9935 // when it creates the query AST. The "ignores
" object specifies which
9936 // (if any) tests to skip, allowing the system to avoid duplicating
9937 // work where it may have already been taken into account by other
9938 // factors such as how the nodes to test were fetched in the first
9940 if(!query){ return yesman; }
9941 ignores = ignores||{};
9945 if(!("el
" in ignores)){
9946 ff = agree(ff, _isElement);
9949 if(!("tag
" in ignores)){
9950 if(query.tag != "*"){
9951 ff = agree(ff, function(elem){
9952 return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
9957 if(!("classes
" in ignores)){
9958 each(query.classes, function(cname, idx, arr){
9959 // get the class name
9961 var isWildcard = cname.charAt(cname.length-1) == "*";
9963 cname = cname.substr(0, cname.length-1);
9965 // I dislike the regex thing, even if memoized in a cache, but it's VERY short
9966 var re = new RegExp("(?:^|\\s
)" + cname + (isWildcard ? ".*" : "") + "(?:\\s
|$)");
9968 var re = new RegExp("(?:^|\\s
)" + cname + "(?:\\s
|$)");
9969 ff = agree(ff, function(elem){
9970 return re.test(elem.className);
9976 if(!("pseudos
" in ignores)){
9977 each(query.pseudos, function(pseudo){
9978 var pn = pseudo.name;
9980 ff = agree(ff, pseudos[pn](pn, pseudo.value));
9985 if(!("attrs
" in ignores)){
9986 each(query.attrs, function(attr){
9989 // type, attr, matchFor
9990 if(attr.type && attrs[attr.type]){
9991 matcher = attrs[attr.type](a, attr.matchFor);
9993 matcher = defaultGetter(a);
9996 ff = agree(ff, matcher);
10001 if(!("id
" in ignores)){
10003 ff = agree(ff, function(elem){
10004 return (!!elem && (elem.id == query.id));
10010 if(!("default" in ignores)){
10017 var _nextSibling = function(filterFunc){
10018 return function(node, ret, bag){
10019 while(node = node[_ns]){
10020 if(_noNES && (!_isElement(node))){ continue; }
10022 (!bag || _isUnique(node, bag)) &&
10033 var _nextSiblings = function(filterFunc){
10034 return function(root, ret, bag){
10035 var te = root[_ns];
10037 if(_simpleNodeTest(te)){
10038 if(bag && !_isUnique(te, bag)){
10041 if(filterFunc(te)){
10051 // get an array of child *elements*, skipping text and comment nodes
10052 var _childElements = function(filterFunc){
10053 filterFunc = filterFunc||yesman;
10054 return function(root, ret, bag){
10055 // get an array of child elements, skipping text and comment nodes
10056 var te, x = 0, tret = root.children || root.childNodes;
10057 while(te = tret[x++]){
10059 _simpleNodeTest(te) &&
10060 (!bag || _isUnique(te, bag)) &&
10061 (filterFunc(te, x))
10070 // test to see if node is below root
10071 var _isDescendant = function(node, root){
10072 var pn = node.parentNode;
10077 pn = pn.parentNode;
10082 var _getElementsFuncCache = {};
10084 var getElementsFunc = function(query){
10085 var retFunc = _getElementsFuncCache[query.query];
10086 // if we've got a cached dispatcher, just use that
10087 if(retFunc){ return retFunc; }
10088 // else, generate a new on
10091 // this function returns a function that searches for nodes and
10092 // filters them. The search may be specialized by infix operators
10093 // (">", "~", or "+") else it will default to searching all
10094 // descendants (the " " selector). Once a group of children is
10095 // found, a test function is applied to weed out the ones we
10096 // don't want. Many common cases can be fast-pathed. We spend a
10097 // lot of cycles to create a dispatcher that doesn't do more work
10098 // than necessary at any point since, unlike this function, the
10099 // dispatchers will be called every time. The logic of generating
10100 // efficient dispatchers looks like this in pseudo code:
10102 // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
10103 // if infixOperator == " ":
10105 // return def(root):
10106 // return d.byId(id, root);
10109 // return def(root):
10110 // return filter(d.byId(id, root));
10112 // elif cssClass && getElementsByClassName:
10113 // return def(root):
10114 // return filter(root.getElementsByClassName(cssClass));
10117 // return def(root):
10118 // return root.getElementsByTagName(tagName);
10121 // # search by tag name, then filter
10122 // return def(root):
10123 // return filter(root.getElementsByTagName(tagName||"*"));
10125 // elif infixOperator == ">":
10126 // # search direct children
10127 // return def(root):
10128 // return filter(root.children);
10130 // elif infixOperator == "+":
10131 // # search next sibling
10132 // return def(root):
10133 // return filter(root.nextElementSibling);
10135 // elif infixOperator == "~":
10136 // # search rightward siblings
10137 // return def(root):
10138 // return filter(nextSiblings(root));
10140 var io = query.infixOper;
10141 var oper = (io ? io.oper : "");
10142 // the default filter func which tests for all conditions in the query
10143 // part. This is potentially inefficient, so some optimized paths may
10144 // re-define it to test fewer things.
10145 var filterFunc = getSimpleFilterFunc(query, { el: 1 });
10146 var qt = query.tag;
10147 var wildcardTag = ("*" == qt);
10148 var ecs = getDoc()["getElementsByClassName
"];
10151 // if there's no infix operator, then it's a descendant query. ID
10152 // and "elements by
class name
" variants can be accelerated so we
10153 // call them out explicitly:
10155 // testing shows that the overhead of yesman() is acceptable
10156 // and can save us some bytes vs. re-defining the function
10158 filterFunc = (!query.loops && wildcardTag) ?
10160 getSimpleFilterFunc(query, { el: 1, id: 1 });
10162 retFunc = function(root, arr){
10163 var te = dom.byId(query.id, (root.ownerDocument||root));
10164 if(!te || !filterFunc(te)){ return; }
10165 if(9 == root.nodeType){ // if root's a doc, we just return directly
10166 return getArr(te, arr);
10167 }else{ // otherwise check ancestry
10168 if(_isDescendant(te, root)){
10169 return getArr(te, arr);
10175 // isAlien check. Workaround for Prototype.js being totally evil/dumb.
10176 /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
10177 query.classes.length &&
10180 // it's a class-based query and we've got a fast way to run it.
10182 // ignore class and ID filters since we will have handled both
10183 filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
10184 var classesString = query.classes.join(" ");
10185 retFunc = function(root, arr, bag){
10186 var ret = getArr(0, arr), te, x=0;
10187 var tret = root.getElementsByClassName(classesString);
10188 while((te = tret[x++])){
10189 if(filterFunc(te, root) && _isUnique(te, bag)){
10196 }else if(!wildcardTag && !query.loops){
10197 // it's tag only. Fast-path it.
10198 retFunc = function(root, arr, bag){
10199 var ret = getArr(0, arr), te, x=0;
10200 var tag = query.getTag(),
10201 tret = tag ? root.getElementsByTagName(tag) : [];
10202 while((te = tret[x++])){
10203 if(_isUnique(te, bag)){
10210 // the common case:
10211 // a descendant selector without a fast path. By now it's got
10212 // to have a tag selector, even if it's just "*" so we query
10213 // by that and filter
10214 filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
10215 retFunc = function(root, arr, bag){
10216 var ret = getArr(0, arr), te, x=0;
10217 // we use getTag() to avoid case sensitivity issues
10218 var tag = query.getTag(),
10219 tret = tag ? root.getElementsByTagName(tag) : [];
10220 while((te = tret[x++])){
10221 if(filterFunc(te, root) && _isUnique(te, bag)){
10229 // the query is scoped in some way. Instead of querying by tag we
10230 // use some other collection to find candidate nodes
10231 var skipFilters = { el: 1 };
10233 skipFilters.tag = 1;
10235 filterFunc = getSimpleFilterFunc(query, skipFilters);
10237 retFunc = _nextSibling(filterFunc);
10238 }else if("~" == oper){
10239 retFunc = _nextSiblings(filterFunc);
10240 }else if(">" == oper){
10241 retFunc = _childElements(filterFunc);
10244 // cache it and return
10245 return _getElementsFuncCache[query.query] = retFunc;
10248 var filterDown = function(root, queryParts){
10250 // this is the guts of the DOM query system. It takes a list of
10251 // parsed query parts and a root and finds children which match
10252 // the selector represented by the parts
10253 var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
10255 for(var i = 0; i < qpl; i++){
10257 qp = queryParts[i];
10258 x = candidates.length - 1;
10260 // if we have more than one root at this level, provide a new
10261 // hash to use for checking group membership but tell the
10262 // system not to post-filter us since we will already have been
10263 // guaranteed to be unique
10267 var gef = getElementsFunc(qp);
10268 for(var j = 0; (te = candidates[j]); j++){
10269 // for every root, get the elements that match the descendant
10270 // selector, adding them to the "ret
" array and filtering them
10271 // via membership in this level's bag. If there are more query
10272 // parts, then this level's return will be used as the next
10273 // level's candidates
10276 if(!ret.length){ break; }
10282 ////////////////////////////////////////////////////////////////////////
10283 // the query runner
10284 ////////////////////////////////////////////////////////////////////////
10286 // these are the primary caches for full-query results. The query
10287 // dispatcher functions are generated then stored here for hash lookup in
10289 var _queryFuncCacheDOM = {},
10290 _queryFuncCacheQSA = {};
10292 // this is the second level of splitting, from full-length queries (e.g.,
10293 // "div
.foo
.bar
") into simple query expressions (e.g., ["div
.foo
",
10295 var getStepQueryFunc = function(query){
10296 var qparts = getQueryParts(trim(query));
10298 // if it's trivial, avoid iteration and zipping costs
10299 if(qparts.length == 1){
10300 // we optimize this case here to prevent dispatch further down the
10301 // chain, potentially slowing things down. We could more elegantly
10302 // handle this in filterDown(), but it's slower for simple things
10303 // that need to be fast (e.g., "#someId
").
10304 var tef = getElementsFunc(qparts[0]);
10305 return function(root){
10306 var r = tef(root, []);
10307 if(r){ r.nozip = true; }
10312 // otherwise, break it up and return a runner that iterates over the parts recursively
10313 return function(root){
10314 return filterDown(root, qparts);
10319 // * we can't trust QSA for anything but document-rooted queries, so
10320 // caching is split into DOM query evaluators and QSA query evaluators
10321 // * caching query results is dirty and leak-prone (or, at a minimum,
10322 // prone to unbounded growth). Other toolkits may go this route, but
10323 // they totally destroy their own ability to manage their memory
10324 // footprint. If we implement it, it should only ever be with a fixed
10325 // total element reference # limit and an LRU-style algorithm since JS
10326 // has no weakref support. Caching compiled query evaluators is also
10327 // potentially problematic, but even on large documents the size of the
10328 // query evaluators is often < 100 function objects per evaluator (and
10329 // LRU can be applied if it's ever shown to be an issue).
10330 // * since IE's QSA support is currently only for HTML documents and even
10331 // then only in IE 8's "standards mode
", we have to detect our dispatch
10332 // route at query time and keep 2 separate caches. Ugg.
10334 // we need to determine if we think we can run a given query via
10335 // querySelectorAll or if we'll need to fall back on DOM queries to get
10336 // there. We need a lot of information about the environment and the query
10337 // to make the determination (e.g. does it support QSA, does the query in
10338 // question work in the native QSA impl, etc.).
10340 // IE QSA queries may incorrectly include comment nodes, so we throw the
10341 // zipping function into "remove
" comments mode instead of the normal "skip
10342 // it" which every other QSA-clued browser enjoys
10343 var noZip
= has("ie") ? "commentStrip" : "nozip";
10345 var qsa
= "querySelectorAll";
10346 var qsaAvail
= !!getDoc()[qsa
];
10348 //Don't bother with n+3 type of matches, IE complains if we modify those.
10349 var infixSpaceRe
= /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
10350 var infixSpaceFunc = function(match
, pre
, ch
, post
){
10351 return ch
? (pre
? pre
+ " " : "") + ch
+ (post
? " " + post
: "") : /*n+3*/ match
;
10354 //Don't apply the infixSpaceRe to attribute value selectors
10355 var attRe
= /([^[]*)([^\]]*])?/g;
10356 var attFunc = function(match
, nonAtt
, att
){
10357 return nonAtt
.replace(infixSpaceRe
, infixSpaceFunc
) + (att
||"");
10359 var getQueryFunc = function(query
, forceDOM
){
10360 //Normalize query. The CSS3 selectors spec allows for omitting spaces around
10361 //infix operators, >, ~ and +
10362 //Do the work here since detection for spaces is used as a simple "not use QSA"
10364 query
= query
.replace(attRe
, attFunc
);
10367 // if we've got a cached variant and we think we can do it, run it!
10368 var qsaCached
= _queryFuncCacheQSA
[query
];
10369 if(qsaCached
&& !forceDOM
){ return qsaCached
; }
10372 // else if we've got a DOM cached variant, assume that we already know
10373 // all we need to and use it
10374 var domCached
= _queryFuncCacheDOM
[query
];
10375 if(domCached
){ return domCached
; }
10378 // today we're caching DOM and QSA branches separately so we
10379 // recalc useQSA every time. If we had a way to tag root+query
10380 // efficiently, we'd be in good shape to do a global cache.
10382 var qcz
= query
.charAt(0);
10383 var nospace
= (-1 == query
.indexOf(" "));
10385 // byId searches are wicked fast compared to QSA, even when filtering
10387 if( (query
.indexOf("#") >= 0) && (nospace
) ){
10392 qsaAvail
&& (!forceDOM
) &&
10393 // as per CSS 3, we can't currently start w/ combinator:
10394 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
10395 (specials
.indexOf(qcz
) == -1) &&
10396 // IE's QSA impl sucks on pseudos
10397 (!has("ie") || (query
.indexOf(":") == -1)) &&
10399 (!(cssCaseBug
&& (query
.indexOf(".") >= 0))) &&
10402 // need to tighten up browser rules on ":contains" and "|=" to
10403 // figure out which aren't good
10404 // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
10405 // elements, even though according to spec, selected options should
10406 // match :checked. So go nonQSA for it:
10407 // http://bugs.dojotoolkit.org/ticket/5179
10408 (query
.indexOf(":contains") == -1) && (query
.indexOf(":checked") == -1) &&
10409 (query
.indexOf("|=") == -1) // some browsers don't grok it
10413 // if we've got a descendant query (e.g., "> .thinger" instead of
10414 // just ".thinger") in a QSA-able doc, but are passed a child as a
10415 // root, it should be possible to give the item a synthetic ID and
10416 // trivially rewrite the query to the form "#synid > .thinger" to
10417 // use the QSA branch
10421 var tq
= (specials
.indexOf(query
.charAt(query
.length
-1)) >= 0) ?
10422 (query
+ " *") : query
;
10423 return _queryFuncCacheQSA
[query
] = function(root
){
10425 // the QSA system contains an egregious spec bug which
10426 // limits us, effectively, to only running QSA queries over
10427 // entire documents. See:
10428 // http://ejohn.org/blog/thoughts-on-queryselectorall/
10429 // despite this, we can also handle QSA runs on simple
10430 // selectors, but we don't want detection to be expensive
10431 // so we're just checking for the presence of a space char
10432 // right now. Not elegant, but it's cheaper than running
10433 // the query parser when we might not need to
10434 if(!((9 == root
.nodeType
) || nospace
)){ throw ""; }
10435 var r
= root
[qsa
](tq
);
10436 // skip expensive duplication checks and just wrap in a NodeList
10440 // else run the DOM branch on this query, ensuring that we
10441 // default that way in the future
10442 return getQueryFunc(query
, true)(root
);
10447 var parts
= query
.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
10448 return _queryFuncCacheDOM
[query
] = ((parts
.length
< 2) ?
10449 // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
10450 getStepQueryFunc(query
) :
10451 // if it *is* a complex query, break it up into its
10452 // constituent parts and return a dispatcher that will
10453 // merge the parts when run
10455 var pindex
= 0, // avoid array alloc for every invocation
10458 while((tp
= parts
[pindex
++])){
10459 ret
= ret
.concat(getStepQueryFunc(tp
)(root
));
10470 // this function is Moo inspired, but our own impl to deal correctly
10472 var _nodeUID
= has("ie") ? function(node
){
10474 // XML docs don't have uniqueID on their nodes
10475 return (node
.getAttribute("_uid") || node
.setAttribute("_uid", ++_zipIdx
) || _zipIdx
);
10478 return node
.uniqueID
;
10482 return (node
._uid
|| (node
._uid
= ++_zipIdx
));
10485 // determine if a node in is unique in a "bag". In this case we don't want
10486 // to flatten a list of unique items, but rather just tell if the item in
10487 // question is already in the bag. Normally we'd just use hash lookup to do
10488 // this for us but IE's DOM is busted so we can't really count on that. On
10489 // the upside, it gives us a built in unique ID function.
10490 var _isUnique = function(node
, bag
){
10491 if(!bag
){ return 1; }
10492 var id
= _nodeUID(node
);
10493 if(!bag
[id
]){ return bag
[id
] = 1; }
10497 // attempt to efficiently determine if an item in a list is a dupe,
10498 // returning a list of "uniques", hopefully in document order
10499 var _zipIdxName
= "_zipIdx";
10500 var _zip = function(arr
){
10501 if(arr
&& arr
.nozip
){
10505 if(!arr
|| !arr
.length
){ return ret
; }
10509 if(arr
.length
< 2){ return ret
; }
10513 // we have to fork here for IE and XML docs because we can't set
10514 // expandos on their nodes (apparently). *sigh*
10516 if(has("ie") && caseSensitive
){
10517 var szidx
= _zipIdx
+"";
10518 arr
[0].setAttribute(_zipIdxName
, szidx
);
10519 for(x
= 1; te
= arr
[x
]; x
++){
10520 if(arr
[x
].getAttribute(_zipIdxName
) != szidx
){
10523 te
.setAttribute(_zipIdxName
, szidx
);
10525 }else if(has("ie") && arr
.commentStrip
){
10527 for(x
= 1; te
= arr
[x
]; x
++){
10528 if(_isElement(te
)){
10532 }catch(e
){ /* squelch */ }
10534 if(arr
[0]){ arr
[0][_zipIdxName
] = _zipIdx
; }
10535 for(x
= 1; te
= arr
[x
]; x
++){
10536 if(arr
[x
][_zipIdxName
] != _zipIdx
){
10539 te
[_zipIdxName
] = _zipIdx
;
10545 // the main executor
10546 var query = function(/*String*/ query
, /*String|DOMNode?*/ root
){
10548 // Returns nodes which match the given CSS3 selector, searching the
10549 // entire document by default but optionally taking a node to scope
10550 // the search by. Returns an array.
10552 // dojo.query() is the swiss army knife of DOM node manipulation in
10553 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
10554 // "$" function, dojo.query provides robust, high-performance
10555 // CSS-based node selector support with the option of scoping searches
10556 // to a particular sub-tree of a document.
10558 // Supported Selectors:
10559 // --------------------
10561 // acme supports a rich set of CSS3 selectors, including:
10563 // - class selectors (e.g., `.foo`)
10564 // - node type selectors like `span`
10565 // - ` ` descendant selectors
10566 // - `>` child element selectors
10567 // - `#foo` style ID selectors
10568 // - `*` universal selector
10569 // - `~`, the preceded-by sibling selector
10570 // - `+`, the immediately preceded-by sibling selector
10571 // - attribute queries:
10572 // - `[foo]` attribute presence selector
10573 // - `[foo='bar']` attribute value exact match
10574 // - `[foo~='bar']` attribute value list item match
10575 // - `[foo^='bar']` attribute start match
10576 // - `[foo$='bar']` attribute end match
10577 // - `[foo*='bar']` attribute substring match
10578 // - `:first-child`, `:last-child`, and `:only-child` positional selectors
10579 // - `:empty` content emtpy selector
10580 // - `:checked` pseudo selector
10581 // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
10582 // - `:nth-child(even)`, `:nth-child(odd)` positional selectors
10583 // - `:not(...)` negation pseudo selectors
10585 // Any legal combination of these selectors will work with
10586 // `dojo.query()`, including compound selectors ("," delimited).
10587 // Very complex and useful searches can be constructed with this
10588 // palette of selectors and when combined with functions for
10589 // manipulation presented by dojo/NodeList, many types of DOM
10590 // manipulation operations become very straightforward.
10592 // Unsupported Selectors:
10593 // ----------------------
10595 // While dojo.query handles many CSS3 selectors, some fall outside of
10596 // what's reasonable for a programmatic node querying engine to
10597 // handle. Currently unsupported selectors include:
10599 // - namespace-differentiated selectors of any form
10600 // - all `::` pseduo-element selectors
10601 // - certain pseudo-selectors which don't get a lot of day-to-day use:
10602 // - `:root`, `:lang()`, `:target`, `:focus`
10603 // - all visual and state selectors:
10604 // - `:root`, `:active`, `:hover`, `:visited`, `:link`,
10605 // `:enabled`, `:disabled`
10606 // - `:*-of-type` pseudo selectors
10608 // dojo.query and XML Documents:
10609 // -----------------------------
10611 // `dojo.query` (as of dojo 1.2) supports searching XML documents
10612 // in a case-sensitive manner. If an HTML document is served with
10613 // a doctype that forces case-sensitivity (e.g., XHTML 1.1
10614 // Strict), dojo.query() will detect this and "do the right
10615 // thing". Case sensitivity is dependent upon the document being
10616 // searched and not the query used. It is therefore possible to
10617 // use case-sensitive queries on strict sub-documents (iframes,
10618 // etc.) or XML documents while still assuming case-insensitivity
10619 // for a host/root document.
10621 // Non-selector Queries:
10622 // ---------------------
10624 // If something other than a String is passed for the query,
10625 // `dojo.query` will return a new `dojo/NodeList` instance
10626 // constructed from that parameter alone and all further
10627 // processing will stop. This means that if you have a reference
10628 // to a node or NodeList, you can quickly construct a new NodeList
10629 // from the original by calling `dojo.query(node)` or
10630 // `dojo.query(list)`.
10633 // The CSS3 expression to match against. For details on the syntax of
10634 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
10636 // A DOMNode (or node id) to scope the search from. Optional.
10639 // search the entire document for elements with the class "foo":
10640 // | dojo.query(".foo");
10641 // these elements will match:
10642 // | <span class="foo"></span>
10643 // | <span class="foo bar"></span>
10644 // | <p class="thud foo"></p>
10646 // search the entire document for elements with the classes "foo" *and* "bar":
10647 // | dojo.query(".foo.bar");
10648 // these elements will match:
10649 // | <span class="foo bar"></span>
10650 // while these will not:
10651 // | <span class="foo"></span>
10652 // | <p class="thud foo"></p>
10654 // find `<span>` elements which are descendants of paragraphs and
10655 // which have a "highlighted" class:
10656 // | dojo.query("p span.highlighted");
10657 // the innermost span in this fragment matches:
10658 // | <p class="foo">
10660 // | <span class="highlighted foo bar">...</span>
10664 // set an "odd" class on all odd table rows inside of the table
10665 // `#tabular_data`, using the `>` (direct child) selector to avoid
10666 // affecting any nested tables:
10667 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
10669 // remove all elements with the class "error" from the document
10670 // and store them in a list:
10671 // | var errors = dojo.query(".error").orphan();
10673 // add an onclick handler to every submit button in the document
10674 // which causes the form to be sent via Ajax instead:
10675 // | dojo.query("input[type='submit']").onclick(function(e){
10676 // | dojo.stopEvent(e); // prevent sending the form
10677 // | var btn = e.target;
10678 // | dojo.xhrPost({
10679 // | form: btn.form,
10680 // | load: function(data){
10681 // | // replace the form with the response
10682 // | var div = dojo.doc.createElement("div");
10683 // | dojo.place(div, btn.form, "after");
10684 // | div.innerHTML = data;
10685 // | dojo.style(btn.form, "display", "none");
10690 root
= root
|| getDoc();
10692 // throw the big case sensitivity switch
10693 var od
= root
.ownerDocument
|| root
; // root is either Document or a node inside the document
10694 caseSensitive
= (od
.createElement("div").tagName
=== "div");
10697 // adding "true" as the 2nd argument to getQueryFunc is useful for
10698 // testing the DOM branch without worrying about the
10699 // behavior/performance of the QSA branch.
10700 var r
= getQueryFunc(query
)(root
);
10703 // need to investigate this branch WRT #8074 and #8075
10707 return _zip(r
); // dojo/NodeList
10709 query
.filter = function(/*Node[]*/ nodeList
, /*String*/ filter
, /*String|DOMNode?*/ root
){
10711 // function for filtering a NodeList based on a selector, optimized for simple selectors
10712 var tmpNodeList
= [],
10713 parts
= getQueryParts(filter
),
10715 (parts
.length
== 1 && !/[^\w#\.]/.test(filter
)) ?
10716 getSimpleFilterFunc(parts
[0]) :
10718 return array
.indexOf(query(filter
, dom
.byId(root
)), node
) != -1;
10720 for(var x
= 0, te
; te
= nodeList
[x
]; x
++){
10721 if(filterFunc(te
)){ tmpNodeList
.push(te
); }
10723 return tmpNodeList
;
10729 'dojo/dnd/autoscroll':function(){
10730 define("dojo/dnd/autoscroll", ["../_base/lang", "../sniff", "../_base/window", "../dom-geometry", "../dom-style", "../window"],
10731 function(lang
, has
, win
, domGeom
, domStyle
, winUtils
){
10734 // dojo/dnd/autoscroll
10738 // Used by dojo/dnd/Manager to scroll document or internal node when the user
10739 // drags near the edge of the viewport or a scrollable node
10741 lang
.setObject("dojo.dnd.autoscroll", exports
);
10743 exports
.getViewport
= winUtils
.getBox
;
10745 exports
.V_TRIGGER_AUTOSCROLL
= 32;
10746 exports
.H_TRIGGER_AUTOSCROLL
= 32;
10748 exports
.V_AUTOSCROLL_VALUE
= 16;
10749 exports
.H_AUTOSCROLL_VALUE
= 16;
10751 // These are set by autoScrollStart().
10752 // Set to default values in case autoScrollStart() isn't called. (back-compat, remove for 2.0)
10755 maxScrollTop
= Infinity
,
10756 maxScrollLeft
= Infinity
;
10758 exports
.autoScrollStart = function(d
){
10760 // Called at the start of a drag.
10762 // The document of the node being dragged.
10765 viewport
= winUtils
.getBox(doc
);
10767 // Save height/width of document at start of drag, before it gets distorted by a user dragging an avatar past
10768 // the document's edge
10769 var html
= win
.body(doc
).parentNode
;
10770 maxScrollTop
= Math
.max(html
.scrollHeight
- viewport
.h
, 0);
10771 maxScrollLeft
= Math
.max(html
.scrollWidth
- viewport
.w
, 0); // usually 0
10774 exports
.autoScroll = function(e
){
10776 // a handler for mousemove and touchmove events, which scrolls the window, if
10779 // mousemove/touchmove event
10781 // FIXME: needs more docs!
10782 var v
= viewport
|| winUtils
.getBox(doc
), // getBox() call for back-compat, in case autoScrollStart() wasn't called
10783 html
= win
.body(doc
).parentNode
,
10785 if(e
.clientX
< exports
.H_TRIGGER_AUTOSCROLL
){
10786 dx
= -exports
.H_AUTOSCROLL_VALUE
;
10787 }else if(e
.clientX
> v
.w
- exports
.H_TRIGGER_AUTOSCROLL
){
10788 dx
= Math
.min(exports
.H_AUTOSCROLL_VALUE
, maxScrollLeft
- html
.scrollLeft
); // don't scroll past edge of doc
10790 if(e
.clientY
< exports
.V_TRIGGER_AUTOSCROLL
){
10791 dy
= -exports
.V_AUTOSCROLL_VALUE
;
10792 }else if(e
.clientY
> v
.h
- exports
.V_TRIGGER_AUTOSCROLL
){
10793 dy
= Math
.min(exports
.V_AUTOSCROLL_VALUE
, maxScrollTop
- html
.scrollTop
); // don't scroll past edge of doc
10795 window
.scrollBy(dx
, dy
);
10798 exports
._validNodes
= {"div": 1, "p": 1, "td": 1};
10799 exports
._validOverflow
= {"auto": 1, "scroll": 1};
10801 exports
.autoScrollNodes = function(e
){
10803 // a handler for mousemove and touchmove events, which scrolls the first available
10804 // Dom element, it falls back to exports.autoScroll()
10806 // mousemove/touchmove event
10808 // FIXME: needs more docs!
10810 var b
, t
, w
, h
, rx
, ry
, dx
= 0, dy
= 0, oldLeft
, oldTop
;
10812 for(var n
= e
.target
; n
;){
10813 if(n
.nodeType
== 1 && (n
.tagName
.toLowerCase() in exports
._validNodes
)){
10814 var s
= domStyle
.getComputedStyle(n
),
10815 overflow
= (s
.overflow
.toLowerCase() in exports
._validOverflow
),
10816 overflowX
= (s
.overflowX
.toLowerCase() in exports
._validOverflow
),
10817 overflowY
= (s
.overflowY
.toLowerCase() in exports
._validOverflow
);
10818 if(overflow
|| overflowX
|| overflowY
){
10819 b
= domGeom
.getContentBox(n
, s
);
10820 t
= domGeom
.position(n
, true);
10823 if(overflow
|| overflowX
){
10824 w
= Math
.min(exports
.H_TRIGGER_AUTOSCROLL
, b
.w
/ 2);
10825 rx
= e
.pageX
- t
.x
;
10826 if(has("webkit") || has("opera")){
10827 // FIXME: this code should not be here, it should be taken into account
10828 // either by the event fixing code, or the domGeom.position()
10829 // FIXME: this code doesn't work on Opera 9.5 Beta
10830 rx
+= win
.body().scrollLeft
;
10833 if(rx
> 0 && rx
< b
.w
){
10836 }else if(rx
> b
.w
- w
){
10839 oldLeft
= n
.scrollLeft
;
10840 n
.scrollLeft
= n
.scrollLeft
+ dx
;
10844 if(overflow
|| overflowY
){
10845 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
10846 h
= Math
.min(exports
.V_TRIGGER_AUTOSCROLL
, b
.h
/ 2);
10847 ry
= e
.pageY
- t
.y
;
10848 if(has("webkit") || has("opera")){
10849 // FIXME: this code should not be here, it should be taken into account
10850 // either by the event fixing code, or the domGeom.position()
10851 // FIXME: this code doesn't work on Opera 9.5 Beta
10852 ry
+= win
.body().scrollTop
;
10855 if(ry
> 0 && ry
< b
.h
){
10858 }else if(ry
> b
.h
- h
){
10861 oldTop
= n
.scrollTop
;
10862 n
.scrollTop
= n
.scrollTop
+ dy
;
10865 if(dx
|| dy
){ return; }
10873 exports
.autoScroll(e
);
10881 'dijit/form/_RadioButtonMixin':function(){
10882 define("dijit/form/_RadioButtonMixin", [
10883 "dojo/_base/array", // array.forEach
10884 "dojo/_base/declare", // declare
10885 "dojo/dom-attr", // domAttr.set
10886 "dojo/_base/event", // event.stop
10887 "dojo/_base/lang", // lang.hitch
10888 "dojo/query", // query
10889 "../registry" // registry.getEnclosingWidget
10890 ], function(array
, declare
, domAttr
, event
, lang
, query
, registry
){
10893 // dijit/form/_RadioButtonMixin
10895 return declare("dijit.form._RadioButtonMixin", null, {
10897 // Mixin to provide widget functionality for an HTML radio button
10899 // type: [private] String
10900 // type attribute on `<input>` node.
10901 // Users should not change this value.
10904 _getRelatedWidgets: function(){
10905 // Private function needed to help iterate over all radio buttons in a group.
10907 query("input[type=radio]", this.focusNode
.form
|| this.ownerDocument
).forEach( // can't use name= since query doesn't support [] in the name
10908 lang
.hitch(this, function(inputNode
){
10909 if(inputNode
.name
== this.name
&& inputNode
.form
== this.focusNode
.form
){
10910 var widget
= registry
.getEnclosingWidget(inputNode
);
10920 _setCheckedAttr: function(/*Boolean*/ value
){
10921 // If I am being checked then have to deselect currently checked radio button
10922 this.inherited(arguments
);
10923 if(!this._created
){ return; }
10925 array
.forEach(this._getRelatedWidgets(), lang
.hitch(this, function(widget
){
10926 if(widget
!= this && widget
.checked
){
10927 widget
.set('checked', false);
10933 _getSubmitValue: function(/*String*/ value
){
10934 return value
=== null ? "on" : value
;
10937 _onClick: function(/*Event*/ e
){
10938 if(this.checked
|| this.disabled
){ // nothing to do
10942 if(this.readOnly
){ // ignored by some browsers so we have to resync the DOM elements with widget values
10944 array
.forEach(this._getRelatedWidgets(), lang
.hitch(this, function(widget
){
10945 domAttr
.set(this.focusNode
|| this.domNode
, 'checked', widget
.checked
);
10949 return this.inherited(arguments
);
10955 'dojo/data/ItemFileWriteStore':function(){
10956 define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/kernel",
10957 "./ItemFileReadStore", "../date/stamp"
10958 ], function(lang
, declare
, arrayUtil
, jsonUtil
, kernel
, ItemFileReadStore
, dateStamp
){
10961 // dojo/data/ItemFileWriteStore
10963 return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore
, {
10967 constructor: function(/* object */ keywordParameters
){
10968 // keywordParameters:
10969 // The structure of the typeMap object is as follows:
10971 // | type0: function || object,
10972 // | type1: function || object,
10974 // | typeN: function || object
10976 // Where if it is a function, it is assumed to be an object constructor that takes the
10977 // value of _value as the initialization parameters. It is serialized assuming object.toString()
10978 // serialization. If it is an object, then it is assumed
10979 // to be an object of general form:
10981 // | type: function, //constructor.
10982 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
10983 // | serialize: function(object) //The function that converts the object back into the proper file format form.
10986 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
10987 this._features
['dojo.data.api.Write'] = true;
10988 this._features
['dojo.data.api.Notification'] = true;
10990 // For keeping track of changes so that we can implement isDirty and revert
10997 if(!this._datatypeMap
['Date'].serialize
){
10998 this._datatypeMap
['Date'].serialize = function(obj
){
10999 return dateStamp
.toISOString(obj
, {zulu
:true});
11002 //Disable only if explicitly set to false.
11003 if(keywordParameters
&& (keywordParameters
.referenceIntegrity
=== false)){
11004 this.referenceIntegrity
= false;
11007 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
11008 this._saveInProgress
= false;
11011 referenceIntegrity
: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
11013 _assert: function(/* boolean */ condition
){
11015 throw new Error("assertion failed in ItemFileWriteStore");
11019 _getIdentifierAttribute: function(){
11020 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
11021 return this.getFeatures()['dojo.data.api.Identity'];
11025 /* dojo/data/api/Write */
11027 newItem: function(/* Object? */ keywordArgs
, /* Object? */ parentInfo
){
11029 // See dojo/data/api/Write.newItem()
11031 this._assert(!this._saveInProgress
);
11033 if(!this._loadFinished
){
11034 // We need to do this here so that we'll be able to find out what
11035 // identifierAttribute was specified in the data file.
11039 if(typeof keywordArgs
!= "object" && typeof keywordArgs
!= "undefined"){
11040 throw new Error("newItem() was passed something other than an object");
11042 var newIdentity
= null;
11043 var identifierAttribute
= this._getIdentifierAttribute();
11044 if(identifierAttribute
=== Number
){
11045 newIdentity
= this._arrayOfAllItems
.length
;
11047 newIdentity
= keywordArgs
[identifierAttribute
];
11048 if(typeof newIdentity
=== "undefined"){
11049 throw new Error("newItem() was not passed an identity for the new item");
11051 if(lang
.isArray(newIdentity
)){
11052 throw new Error("newItem() was not passed an single-valued identity");
11056 // make sure this identity is not already in use by another item, if identifiers were
11057 // defined in the file. Otherwise it would be the item count,
11058 // which should always be unique in this case.
11059 if(this._itemsByIdentity
){
11060 this._assert(typeof this._itemsByIdentity
[newIdentity
] === "undefined");
11062 this._assert(typeof this._pending
._newItems
[newIdentity
] === "undefined");
11063 this._assert(typeof this._pending
._deletedItems
[newIdentity
] === "undefined");
11066 newItem
[this._storeRefPropName
] = this;
11067 newItem
[this._itemNumPropName
] = this._arrayOfAllItems
.length
;
11068 if(this._itemsByIdentity
){
11069 this._itemsByIdentity
[newIdentity
] = newItem
;
11070 //We have to set the identifier now, otherwise we can't look it
11071 //up at calls to setValueorValues in parentInfo handling.
11072 newItem
[identifierAttribute
] = [newIdentity
];
11074 this._arrayOfAllItems
.push(newItem
);
11076 //We need to construct some data for the onNew call too...
11079 // Now we need to check to see where we want to assign this thingm if any.
11080 if(parentInfo
&& parentInfo
.parent
&& parentInfo
.attribute
){
11082 item
: parentInfo
.parent
,
11083 attribute
: parentInfo
.attribute
,
11084 oldValue
: undefined
11087 //See if it is multi-valued or not and handle appropriately
11088 //Generally, all attributes are multi-valued for this store
11089 //So, we only need to append if there are already values present.
11090 var values
= this.getValues(parentInfo
.parent
, parentInfo
.attribute
);
11091 if(values
&& values
.length
> 0){
11092 var tempValues
= values
.slice(0, values
.length
);
11093 if(values
.length
=== 1){
11094 pInfo
.oldValue
= values
[0];
11096 pInfo
.oldValue
= values
.slice(0, values
.length
);
11098 tempValues
.push(newItem
);
11099 this._setValueOrValues(parentInfo
.parent
, parentInfo
.attribute
, tempValues
, false);
11100 pInfo
.newValue
= this.getValues(parentInfo
.parent
, parentInfo
.attribute
);
11102 this._setValueOrValues(parentInfo
.parent
, parentInfo
.attribute
, newItem
, false);
11103 pInfo
.newValue
= newItem
;
11106 //Toplevel item, add to both top list as well as all list.
11107 newItem
[this._rootItemPropName
]=true;
11108 this._arrayOfTopLevelItems
.push(newItem
);
11111 this._pending
._newItems
[newIdentity
] = newItem
;
11113 //Clone over the properties to the new item
11114 for(var key
in keywordArgs
){
11115 if(key
=== this._storeRefPropName
|| key
=== this._itemNumPropName
){
11116 // Bummer, the user is trying to do something like
11117 // newItem({_S:"foo"}). Unfortunately, our superclass,
11118 // ItemFileReadStore, is already using _S in each of our items
11119 // to hold private info. To avoid a naming collision, we
11120 // need to move all our private info to some other property
11121 // of all the items/objects. So, we need to iterate over all
11122 // the items and do something like:
11123 // item.__S = item._S;
11124 // item._S = undefined;
11125 // But first we have to make sure the new "__S" variable is
11126 // not in use, which means we have to iterate over all the
11127 // items checking for that.
11128 throw new Error("encountered bug in ItemFileWriteStore.newItem");
11130 var value
= keywordArgs
[key
];
11131 if(!lang
.isArray(value
)){
11134 newItem
[key
] = value
;
11135 if(this.referenceIntegrity
){
11136 for(var i
= 0; i
< value
.length
; i
++){
11137 var val
= value
[i
];
11138 if(this.isItem(val
)){
11139 this._addReferenceToMap(val
, newItem
, key
);
11144 this.onNew(newItem
, pInfo
); // dojo/data/api/Notification call
11145 return newItem
; // item
11148 _removeArrayElement: function(/* Array */ array
, /* anything */ element
){
11149 var index
= arrayUtil
.indexOf(array
, element
);
11151 array
.splice(index
, 1);
11157 deleteItem: function(/* dojo/data/api/Item */ item){
11159 // See dojo/data/api/Write.deleteItem()
11160 this._assert(!this._saveInProgress);
11161 this._assertIsItem(item);
11163 // Remove this item from the _arrayOfAllItems, but leave a null value in place
11164 // of the item, so as not to change the length of the array, so that in newItem()
11165 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
11166 var indexInArrayOfAllItems = item[this._itemNumPropName];
11167 var identity = this.getIdentity(item);
11169 //If we have reference integrity on, we need to do reference cleanup for the deleted item
11170 if(this.referenceIntegrity){
11171 //First scan all the attributes of this items for references and clean them up in the map
11172 //As this item is going away, no need to track its references anymore.
11174 //Get the attributes list before we generate the backup so it
11175 //doesn't pollute the attributes list.
11176 var attributes = this.getAttributes(item);
11178 //Backup the map, we'll have to restore it potentially, in a revert.
11179 if(item[this._reverseRefMap]){
11180 item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
11183 //TODO: This causes a reversion problem. This list won't be restored on revert since it is
11184 //attached to the 'value'. item, not ours. Need to back tese up somehow too.
11185 //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
11186 //later. Or just record them and call _addReferenceToMap on them in revert.
11187 arrayUtil.forEach(attributes, function(attribute){
11188 arrayUtil.forEach(this.getValues(item, attribute), function(value){
11189 if(this.isItem(value)){
11190 //We have to back up all the references we had to others so they can be restored on a revert.
11191 if(!item["backupRefs_" + this._reverseRefMap]){
11192 item["backupRefs_" + this._reverseRefMap] = [];
11194 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
11195 this._removeReferenceFromMap(value, item, attribute);
11200 //Next, see if we have references to this item, if we do, we have to clean them up too.
11201 var references = item[this._reverseRefMap];
11203 //Look through all the items noted as references to clean them up.
11204 for(var itemId in references){
11205 var containingItem = null;
11206 if(this._itemsByIdentity){
11207 containingItem = this._itemsByIdentity[itemId];
11209 containingItem = this._arrayOfAllItems[itemId];
11211 //We have a reference to a containing item, now we have to process the
11212 //attributes and clear all references to the item being deleted.
11213 if(containingItem){
11214 for(var attribute in references[itemId]){
11215 var oldValues = this.getValues(containingItem, attribute) || [];
11216 var newValues = arrayUtil.filter(oldValues, function(possibleItem){
11217 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
11219 //Remove the note of the reference to the item and set the values on the modified attribute.
11220 this._removeReferenceFromMap(item, containingItem, attribute);
11221 if(newValues.length < oldValues.length){
11222 this._setValueOrValues(containingItem, attribute, newValues, true);
11230 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
11232 item[this._storeRefPropName] = null;
11233 if(this._itemsByIdentity){
11234 delete this._itemsByIdentity[identity];
11236 this._pending._deletedItems[identity] = item;
11238 //Remove from the toplevel items, if necessary...
11239 if(item[this._rootItemPropName]){
11240 this._removeArrayElement(this._arrayOfTopLevelItems, item);
11242 this.onDelete(item); // dojo/data/api/Notification call
11246 setValue: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* almost anything */ value
){
11248 // See dojo/data/api/Write.set()
11249 return this._setValueOrValues(item
, attribute
, value
, true); // boolean
11252 setValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* array */ values
){
11254 // See dojo/data/api/Write.setValues()
11255 return this._setValueOrValues(item
, attribute
, values
, true); // boolean
11258 unsetAttribute: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
){
11260 // See dojo/data/api/Write.unsetAttribute()
11261 return this._setValueOrValues(item
, attribute
, [], true);
11264 _setValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* anything */ newValueOrValues
, /*boolean?*/ callOnSet
){
11265 this._assert(!this._saveInProgress
);
11267 // Check for valid arguments
11268 this._assertIsItem(item
);
11269 this._assert(lang
.isString(attribute
));
11270 this._assert(typeof newValueOrValues
!== "undefined");
11272 // Make sure the user isn't trying to change the item's identity
11273 var identifierAttribute
= this._getIdentifierAttribute();
11274 if(attribute
== identifierAttribute
){
11275 throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
11278 // To implement the Notification API, we need to make a note of what
11279 // the old attribute value was, so that we can pass that info when
11280 // we call the onSet method.
11281 var oldValueOrValues
= this._getValueOrValues(item
, attribute
);
11283 var identity
= this.getIdentity(item
);
11284 if(!this._pending
._modifiedItems
[identity
]){
11285 // Before we actually change the item, we make a copy of it to
11286 // record the original state, so that we'll be able to revert if
11287 // the revert method gets called. If the item has already been
11288 // modified then there's no need to do this now, since we already
11289 // have a record of the original state.
11290 var copyOfItemState
= {};
11291 for(var key
in item
){
11292 if((key
=== this._storeRefPropName
) || (key
=== this._itemNumPropName
) || (key
=== this._rootItemPropName
)){
11293 copyOfItemState
[key
] = item
[key
];
11294 }else if(key
=== this._reverseRefMap
){
11295 copyOfItemState
[key
] = lang
.clone(item
[key
]);
11297 copyOfItemState
[key
] = item
[key
].slice(0, item
[key
].length
);
11300 // Now mark the item as dirty, and save the copy of the original state
11301 this._pending
._modifiedItems
[identity
] = copyOfItemState
;
11304 // Okay, now we can actually change this attribute on the item
11305 var success
= false;
11307 if(lang
.isArray(newValueOrValues
) && newValueOrValues
.length
=== 0){
11309 // If we were passed an empty array as the value, that counts
11310 // as "unsetting" the attribute, so we need to remove this
11311 // attribute from the item.
11312 success
= delete item
[attribute
];
11313 newValueOrValues
= undefined; // used in the onSet Notification call below
11315 if(this.referenceIntegrity
&& oldValueOrValues
){
11316 var oldValues
= oldValueOrValues
;
11317 if(!lang
.isArray(oldValues
)){
11318 oldValues
= [oldValues
];
11320 for(var i
= 0; i
< oldValues
.length
; i
++){
11321 var value
= oldValues
[i
];
11322 if(this.isItem(value
)){
11323 this._removeReferenceFromMap(value
, item
, attribute
);
11329 if(lang
.isArray(newValueOrValues
)){
11330 // Unfortunately, it's not safe to just do this:
11331 // newValueArray = newValueOrValues;
11332 // Instead, we need to copy the array, which slice() does very nicely.
11333 // This is so that our internal data structure won't
11334 // get corrupted if the user mucks with the values array *after*
11335 // calling setValues().
11336 newValueArray
= newValueOrValues
.slice(0, newValueOrValues
.length
);
11338 newValueArray
= [newValueOrValues
];
11341 //We need to handle reference integrity if this is on.
11342 //In the case of set, we need to see if references were added or removed
11343 //and update the reference tracking map accordingly.
11344 if(this.referenceIntegrity
){
11345 if(oldValueOrValues
){
11346 var oldValues
= oldValueOrValues
;
11347 if(!lang
.isArray(oldValues
)){
11348 oldValues
= [oldValues
];
11350 //Use an associative map to determine what was added/removed from the list.
11351 //Should be O(n) performant. First look at all the old values and make a list of them
11352 //Then for any item not in the old list, we add it. If it was already present, we remove it.
11353 //Then we pass over the map and any references left it it need to be removed (IE, no match in
11354 //the new values list).
11356 arrayUtil
.forEach(oldValues
, function(possibleItem
){
11357 if(this.isItem(possibleItem
)){
11358 var id
= this.getIdentity(possibleItem
);
11359 map
[id
.toString()] = true;
11362 arrayUtil
.forEach(newValueArray
, function(possibleItem
){
11363 if(this.isItem(possibleItem
)){
11364 var id
= this.getIdentity(possibleItem
);
11365 if(map
[id
.toString()]){
11366 delete map
[id
.toString()];
11368 this._addReferenceToMap(possibleItem
, item
, attribute
);
11372 for(var rId
in map
){
11374 if(this._itemsByIdentity
){
11375 removedItem
= this._itemsByIdentity
[rId
];
11377 removedItem
= this._arrayOfAllItems
[rId
];
11379 this._removeReferenceFromMap(removedItem
, item
, attribute
);
11382 //Everything is new (no old values) so we have to just
11383 //insert all the references, if any.
11384 for(var i
= 0; i
< newValueArray
.length
; i
++){
11385 var value
= newValueArray
[i
];
11386 if(this.isItem(value
)){
11387 this._addReferenceToMap(value
, item
, attribute
);
11392 item
[attribute
] = newValueArray
;
11396 // Now we make the dojo/data/api/Notification call
11398 this.onSet(item
, attribute
, oldValueOrValues
, newValueOrValues
);
11400 return success
; // boolean
11403 _addReferenceToMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute
){
11405 // Method to add an reference map entry for an item and attribute.
11407 // Method to add an reference map entry for an item and attribute.
11409 // The item that is referenced.
11411 // The item that holds the new reference to refItem.
11413 // The attribute on parentItem that contains the new reference.
11415 var parentId
= this.getIdentity(parentItem
);
11416 var references
= refItem
[this._reverseRefMap
];
11419 references
= refItem
[this._reverseRefMap
] = {};
11421 var itemRef
= references
[parentId
];
11423 itemRef
= references
[parentId
] = {};
11425 itemRef
[attribute
] = true;
11428 _removeReferenceFromMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute
){
11430 // Method to remove an reference map entry for an item and attribute.
11432 // Method to remove an reference map entry for an item and attribute. This will
11433 // also perform cleanup on the map such that if there are no more references at all to
11434 // the item, its reference object and entry are removed.
11436 // The item that is referenced.
11438 // The item holding a reference to refItem.
11440 // The attribute on parentItem that contains the reference.
11441 var identity
= this.getIdentity(parentItem
);
11442 var references
= refItem
[this._reverseRefMap
];
11445 for(itemId
in references
){
11446 if(itemId
== identity
){
11447 delete references
[itemId
][attribute
];
11448 if(this._isEmpty(references
[itemId
])){
11449 delete references
[itemId
];
11453 if(this._isEmpty(references
)){
11454 delete refItem
[this._reverseRefMap
];
11459 _dumpReferenceMap: function(){
11461 // Function to dump the reverse reference map of all items in the store for debug purposes.
11463 // Function to dump the reverse reference map of all items in the store for debug purposes.
11465 for(i
= 0; i
< this._arrayOfAllItems
.length
; i
++){
11466 var item
= this._arrayOfAllItems
[i
];
11467 if(item
&& item
[this._reverseRefMap
]){
11468 console
.log("Item: [" + this.getIdentity(item
) + "] is referenced by: " + jsonUtil
.toJson(item
[this._reverseRefMap
]));
11473 _getValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
){
11474 var valueOrValues
= undefined;
11475 if(this.hasAttribute(item
, attribute
)){
11476 var valueArray
= this.getValues(item
, attribute
);
11477 if(valueArray
.length
== 1){
11478 valueOrValues
= valueArray
[0];
11480 valueOrValues
= valueArray
;
11483 return valueOrValues
;
11486 _flatten: function(/* anything */ value
){
11487 if(this.isItem(value
)){
11488 // Given an item, return an serializable object that provides a
11489 // reference to the item.
11490 // For example, given kermit:
11491 // var kermit = store.newItem({id:2, name:"Kermit"});
11492 // we want to return
11494 return {_reference
: this.getIdentity(value
)};
11496 if(typeof value
=== "object"){
11497 for(var type
in this._datatypeMap
){
11498 var typeMap
= this._datatypeMap
[type
];
11499 if(lang
.isObject(typeMap
) && !lang
.isFunction(typeMap
)){
11500 if(value
instanceof typeMap
.type
){
11501 if(!typeMap
.serialize
){
11502 throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type
+ "]");
11504 return {_type
: type
, _value
: typeMap
.serialize(value
)};
11506 }else if(value
instanceof typeMap
){
11507 //SImple mapping, therefore, return as a toString serialization.
11508 return {_type
: type
, _value
: value
.toString()};
11516 _getNewFileContentString: function(){
11518 // Generate a string that can be saved to a file.
11519 // The result should look similar to:
11520 // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
11521 var serializableStructure
= {};
11523 var identifierAttribute
= this._getIdentifierAttribute();
11524 if(identifierAttribute
!== Number
){
11525 serializableStructure
.identifier
= identifierAttribute
;
11527 if(this._labelAttr
){
11528 serializableStructure
.label
= this._labelAttr
;
11530 serializableStructure
.items
= [];
11531 for(var i
= 0; i
< this._arrayOfAllItems
.length
; ++i
){
11532 var item
= this._arrayOfAllItems
[i
];
11534 var serializableItem
= {};
11535 for(var key
in item
){
11536 if(key
!== this._storeRefPropName
&& key
!== this._itemNumPropName
&& key
!== this._reverseRefMap
&& key
!== this._rootItemPropName
){
11537 var valueArray
= this.getValues(item
, key
);
11538 if(valueArray
.length
== 1){
11539 serializableItem
[key
] = this._flatten(valueArray
[0]);
11541 var serializableArray
= [];
11542 for(var j
= 0; j
< valueArray
.length
; ++j
){
11543 serializableArray
.push(this._flatten(valueArray
[j
]));
11544 serializableItem
[key
] = serializableArray
;
11549 serializableStructure
.items
.push(serializableItem
);
11552 var prettyPrint
= true;
11553 return jsonUtil
.toJson(serializableStructure
, prettyPrint
);
11556 _isEmpty: function(something
){
11558 // Function to determine if an array or object has no properties or values.
11560 // The array or object to examine.
11562 if(lang
.isObject(something
)){
11564 for(i
in something
){
11568 }else if(lang
.isArray(something
)){
11569 if(something
.length
> 0){
11573 return empty
; //boolean
11576 save: function(/* object */ keywordArgs
){
11578 // See dojo/data/api/Write.save()
11579 this._assert(!this._saveInProgress
);
11581 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
11582 this._saveInProgress
= true;
11585 var saveCompleteCallback = function(){
11592 self
._saveInProgress
= false; // must come after this._pending is cleared, but before any callbacks
11593 if(keywordArgs
&& keywordArgs
.onComplete
){
11594 var scope
= keywordArgs
.scope
|| kernel
.global
;
11595 keywordArgs
.onComplete
.call(scope
);
11598 var saveFailedCallback = function(err
){
11599 self
._saveInProgress
= false;
11600 if(keywordArgs
&& keywordArgs
.onError
){
11601 var scope
= keywordArgs
.scope
|| kernel
.global
;
11602 keywordArgs
.onError
.call(scope
, err
);
11606 if(this._saveEverything
){
11607 var newFileContentString
= this._getNewFileContentString();
11608 this._saveEverything(saveCompleteCallback
, saveFailedCallback
, newFileContentString
);
11610 if(this._saveCustom
){
11611 this._saveCustom(saveCompleteCallback
, saveFailedCallback
);
11613 if(!this._saveEverything
&& !this._saveCustom
){
11614 // Looks like there is no user-defined save-handler function.
11615 // That's fine, it just means the datastore is acting as a "mock-write"
11616 // store -- changes get saved in memory but don't get saved to disk.
11617 saveCompleteCallback();
11621 revert: function(){
11623 // See dojo/data/api/Write.revert()
11624 this._assert(!this._saveInProgress
);
11627 for(identity
in this._pending
._modifiedItems
){
11628 // find the original item and the modified item that replaced it
11629 var copyOfItemState
= this._pending
._modifiedItems
[identity
];
11630 var modifiedItem
= null;
11631 if(this._itemsByIdentity
){
11632 modifiedItem
= this._itemsByIdentity
[identity
];
11634 modifiedItem
= this._arrayOfAllItems
[identity
];
11637 // Restore the original item into a full-fledged item again, we want to try to
11638 // keep the same object instance as if we don't it, causes bugs like #9022.
11639 copyOfItemState
[this._storeRefPropName
] = this;
11640 for(var key
in modifiedItem
){
11641 delete modifiedItem
[key
];
11643 lang
.mixin(modifiedItem
, copyOfItemState
);
11646 for(identity
in this._pending
._deletedItems
){
11647 deletedItem
= this._pending
._deletedItems
[identity
];
11648 deletedItem
[this._storeRefPropName
] = this;
11649 var index
= deletedItem
[this._itemNumPropName
];
11651 //Restore the reverse refererence map, if any.
11652 if(deletedItem
["backup_" + this._reverseRefMap
]){
11653 deletedItem
[this._reverseRefMap
] = deletedItem
["backup_" + this._reverseRefMap
];
11654 delete deletedItem
["backup_" + this._reverseRefMap
];
11656 this._arrayOfAllItems
[index
] = deletedItem
;
11657 if(this._itemsByIdentity
){
11658 this._itemsByIdentity
[identity
] = deletedItem
;
11660 if(deletedItem
[this._rootItemPropName
]){
11661 this._arrayOfTopLevelItems
.push(deletedItem
);
11664 //We have to pass through it again and restore the reference maps after all the
11665 //undeletes have occurred.
11666 for(identity
in this._pending
._deletedItems
){
11667 deletedItem
= this._pending
._deletedItems
[identity
];
11668 if(deletedItem
["backupRefs_" + this._reverseRefMap
]){
11669 arrayUtil
.forEach(deletedItem
["backupRefs_" + this._reverseRefMap
], function(reference
){
11671 if(this._itemsByIdentity
){
11672 refItem
= this._itemsByIdentity
[reference
.id
];
11674 refItem
= this._arrayOfAllItems
[reference
.id
];
11676 this._addReferenceToMap(refItem
, deletedItem
, reference
.attr
);
11678 delete deletedItem
["backupRefs_" + this._reverseRefMap
];
11682 for(identity
in this._pending
._newItems
){
11683 var newItem
= this._pending
._newItems
[identity
];
11684 newItem
[this._storeRefPropName
] = null;
11685 // null out the new item, but don't change the array index so
11686 // so we can keep using _arrayOfAllItems.length.
11687 this._arrayOfAllItems
[newItem
[this._itemNumPropName
]] = null;
11688 if(newItem
[this._rootItemPropName
]){
11689 this._removeArrayElement(this._arrayOfTopLevelItems
, newItem
);
11691 if(this._itemsByIdentity
){
11692 delete this._itemsByIdentity
[identity
];
11701 return true; // boolean
11704 isDirty: function(/* item? */ item
){
11706 // See dojo/data/api/Write.isDirty()
11708 // return true if the item is dirty
11709 var identity
= this.getIdentity(item
);
11710 return new Boolean(this._pending
._newItems
[identity
] ||
11711 this._pending
._modifiedItems
[identity
] ||
11712 this._pending
._deletedItems
[identity
]).valueOf(); // boolean
11714 // return true if the store is dirty -- which means return true
11715 // if there are any new items, dirty items, or modified items
11716 return !this._isEmpty(this._pending
._newItems
) ||
11717 !this._isEmpty(this._pending
._modifiedItems
) ||
11718 !this._isEmpty(this._pending
._deletedItems
); // boolean
11722 /* dojo/data/api/Notification */
11724 onSet: function(/* dojo/data/api/Item */ item,
11725 /*attribute-name-string*/ attribute
,
11726 /*object|array*/ oldValue
,
11727 /*object|array*/ newValue
){
11729 // See dojo/data/api/Notification.onSet()
11731 // No need to do anything. This method is here just so that the
11732 // client code can connect observers to it.
11735 onNew: function(/* dojo/data/api/Item */ newItem, /*object?*/ parentInfo
){
11737 // See dojo/data/api/Notification.onNew()
11739 // No need to do anything. This method is here just so that the
11740 // client code can connect observers to it.
11743 onDelete: function(/* dojo/data/api/Item */ deletedItem){
11745 // See dojo/data/api/Notification.onDelete()
11747 // No need to do anything. This method is here just so that the
11748 // client code can connect observers to it.
11751 close: function(/* object? */ request
){
11753 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11755 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11756 // If the store is still dirty (unsaved changes), then an error will be thrown instead of
11757 // clearing the internal state for reload from the url.
11759 //Clear if not dirty ... or throw an error
11760 if(this.clearOnClose
){
11761 if(!this.isDirty()){
11762 this.inherited(arguments
);
11764 //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
11765 throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
11774 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
11775 'dojo/dnd/TimedMoveable':function(){
11776 define("dojo/dnd/TimedMoveable", ["../_base/declare", "./Moveable" /*=====, "./Mover" =====*/], function(declare, Moveable /*=====, Mover =====*/){
11778 // dojo/dnd/TimedMoveable
11781 var __TimedMoveableArgs = declare([Moveable.__MoveableArgs], {
11783 // delay move by this number of ms,
11784 // accumulating position changes during the timeout
11789 // precalculate long expressions
11790 var oldOnMove
= Moveable
.prototype.onMove
;
11792 return declare("dojo.dnd.TimedMoveable", Moveable
, {
11794 // A specialized version of Moveable to support an FPS throttling.
11795 // This class puts an upper restriction on FPS, which may reduce
11796 // the CPU load. The additional parameter "timeout" regulates
11797 // the delay before actually moving the moveable object.
11799 // object attributes (for markup)
11800 timeout
: 40, // in ms, 40ms corresponds to 25 fps
11802 constructor: function(node
, params
){
11804 // an object that makes a node moveable with a timer
11805 // node: Node||String
11806 // a node (or node's id) to be moved
11807 // params: __TimedMoveableArgs
11808 // object with additional parameters.
11810 // sanitize parameters
11811 if(!params
){ params
= {}; }
11812 if(params
.timeout
&& typeof params
.timeout
== "number" && params
.timeout
>= 0){
11813 this.timeout
= params
.timeout
;
11817 onMoveStop: function(/*Mover*/ mover
){
11820 clearTimeout(mover
._timer
);
11821 // reflect the last received position
11822 oldOnMove
.call(this, mover
, mover
._leftTop
);
11824 Moveable
.prototype.onMoveStop
.apply(this, arguments
);
11826 onMove: function(/*Mover*/ mover
, /*Object*/ leftTop
){
11827 mover
._leftTop
= leftTop
;
11829 var _t
= this; // to avoid using dojo.hitch()
11830 mover
._timer
= setTimeout(function(){
11831 // we don't have any pending requests
11832 mover
._timer
= null;
11833 // reflect the last received position
11834 oldOnMove
.call(_t
, mover
, mover
._leftTop
);
11842 'dojo/NodeList-fx':function(){
11843 define("dojo/NodeList-fx", ["./query", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
11844 function(query
, lang
, connectLib
, baseFx
, coreFx
){
11847 // dojo/NodeList-fx
11852 // Adds dojo.fx animation support to dojo.query() by extending the NodeList class
11853 // with additional FX functions. NodeList is the array-like object used to hold query results.
11857 var NodeList
= query
.NodeList
;
11859 lang
.extend(NodeList
, {
11860 _anim: function(obj
, method
, args
){
11862 var a
= coreFx
.combine(
11863 this.map(function(item
){
11864 var tmpArgs
= { node
: item
};
11865 lang
.mixin(tmpArgs
, args
);
11866 return obj
[method
](tmpArgs
);
11869 return args
.auto
? a
.play() && this : a
; // dojo/_base/fx.Animation|dojo/NodeList
11872 wipeIn: function(args
){
11874 // wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
11877 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11878 // an `auto` parameter.
11880 // returns: dojo/_base/fx.Animation|dojo/NodeList
11881 // A special args member `auto` can be passed to automatically play the animation.
11882 // If args.auto is present, the original dojo/NodeList will be returned for further
11883 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11886 // Fade in all tables with class "blah":
11887 // | dojo.query("table.blah").wipeIn().play();
11890 // Utilizing `auto` to get the NodeList back:
11891 // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
11893 return this._anim(coreFx
, "wipeIn", args
); // dojo/_base/fx.Animation|dojo/NodeList
11896 wipeOut: function(args
){
11898 // wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
11901 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11902 // an `auto` parameter.
11904 // returns: dojo/_base/fx.Animation|dojo/NodeList
11905 // A special args member `auto` can be passed to automatically play the animation.
11906 // If args.auto is present, the original dojo/NodeList will be returned for further
11907 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11910 // Wipe out all tables with class "blah":
11911 // | dojo.query("table.blah").wipeOut().play();
11912 return this._anim(coreFx
, "wipeOut", args
); // dojo/_base/fx.Animation|dojo/NodeList
11915 slideTo: function(args
){
11917 // slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
11920 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11921 // an `auto` parameter.
11923 // returns: dojo/_base/fx.Animation|dojo/NodeList
11924 // A special args member `auto` can be passed to automatically play the animation.
11925 // If args.auto is present, the original dojo/NodeList will be returned for further
11926 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11929 // | Move all tables with class "blah" to 300/300:
11930 // | dojo.query("table.blah").slideTo({
11934 return this._anim(coreFx
, "slideTo", args
); // dojo/_base/fx.Animation|dojo/NodeList
11938 fadeIn: function(args
){
11940 // fade in all elements of this NodeList via `dojo.fadeIn`
11943 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11944 // an `auto` parameter.
11946 // returns: dojo/_base/fx.Animation|dojo/NodeList
11947 // A special args member `auto` can be passed to automatically play the animation.
11948 // If args.auto is present, the original dojo/NodeList will be returned for further
11949 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11952 // Fade in all tables with class "blah":
11953 // | dojo.query("table.blah").fadeIn().play();
11954 return this._anim(baseFx
, "fadeIn", args
); // dojo/_base/fx.Animation|dojo/NodeList
11957 fadeOut: function(args
){
11959 // fade out all elements of this NodeList via `dojo.fadeOut`
11962 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11963 // an `auto` parameter.
11965 // returns: dojo/_base/fx.Animation|dojo/NodeList
11966 // A special args member `auto` can be passed to automatically play the animation.
11967 // If args.auto is present, the original dojo/NodeList will be returned for further
11968 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11971 // Fade out all elements with class "zork":
11972 // | dojo.query(".zork").fadeOut().play();
11974 // Fade them on a delay and do something at the end:
11975 // | var fo = dojo.query(".zork").fadeOut();
11976 // | dojo.connect(fo, "onEnd", function(){ /*...*/ });
11980 // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
11982 return this._anim(baseFx
, "fadeOut", args
); // dojo/_base/fx.Animation|dojo/NodeList
11985 animateProperty: function(args
){
11987 // Animate all elements of this NodeList across the properties specified.
11988 // syntax identical to `dojo.animateProperty`
11991 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11992 // an `auto` parameter.
11994 // returns: dojo/_base/fx.Animation|dojo/NodeList
11995 // A special args member `auto` can be passed to automatically play the animation.
11996 // If args.auto is present, the original dojo/NodeList will be returned for further
11997 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
12000 // | dojo.query(".zork").animateProperty({
12001 // | duration: 500,
12003 // | color: { start: "black", end: "white" },
12004 // | left: { end: 300 }
12009 // | dojo.query(".grue").animateProperty({
12014 // | }).onclick(handler);
12015 return this._anim(baseFx
, "animateProperty", args
); // dojo/_base/fx.Animation|dojo/NodeList
12018 anim: function( /*Object*/ properties
,
12019 /*Integer?*/ duration
,
12020 /*Function?*/ easing
,
12021 /*Function?*/ onEnd
,
12022 /*Integer?*/ delay
){
12024 // Animate one or more CSS properties for all nodes in this list.
12025 // The returned animation object will already be playing when it
12026 // is returned. See the docs for `dojo.anim` for full details.
12027 // properties: Object
12028 // the properties to animate. does NOT support the `auto` parameter like other
12029 // NodeList-fx methods.
12030 // duration: Integer?
12031 // Optional. The time to run the animations for
12032 // easing: Function?
12033 // Optional. The easing function to use.
12034 // onEnd: Function?
12035 // A function to be called when the animation ends
12037 // how long to delay playing the returned animation
12039 // Another way to fade out:
12040 // | dojo.query(".thinger").anim({ opacity: 0 });
12042 // animate all elements with the "thigner" class to a width of 500
12043 // pixels over half a second
12044 // | dojo.query(".thinger").anim({ width: 500 }, 700);
12045 var canim
= coreFx
.combine(
12046 this.map(function(item
){
12047 return baseFx
.animateProperty({
12049 properties
: properties
,
12050 duration
: duration
||350,
12056 connectLib
.connect(canim
, "onEnd", onEnd
);
12058 return canim
.play(delay
||0); // dojo/_base/fx.Animation
12066 'dijit/form/_ListMouseMixin':function(){
12067 define("dijit/form/_ListMouseMixin", [
12068 "dojo/_base/declare", // declare
12073 ], function(declare
, mouse
, on
, touch
, _ListBase
){
12076 // dijit/form/_ListMouseMixin
12078 return declare( "dijit.form._ListMouseMixin", _ListBase
, {
12080 // a Mixin to handle mouse or touch events for a focus-less menu
12081 // Abstract methods that must be defined externally:
12083 // - onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
12087 postCreate: function(){
12088 this.inherited(arguments
);
12090 this.own(on(this.domNode
, touch
.press
, function(evt
){ evt
.preventDefault(); })); // prevent focus shift on list scrollbar press
12092 this._listConnect(touch
.press
, "_onMouseDown");
12093 this._listConnect(touch
.release
, "_onMouseUp");
12094 this._listConnect(mouse
.enter
, "_onMouseOver");
12095 this._listConnect(mouse
.leave
, "_onMouseOut");
12098 _onMouseDown: function(/*Event*/ evt
, /*DomNode*/ target
){
12099 if(this._hoveredNode
){
12100 this.onUnhover(this._hoveredNode
);
12101 this._hoveredNode
= null;
12103 this._isDragging
= true;
12104 this._setSelectedAttr(target
);
12107 _onMouseUp: function(/*Event*/ evt
, /*DomNode*/ target
){
12108 this._isDragging
= false;
12109 var selectedNode
= this.selected
;
12110 var hoveredNode
= this._hoveredNode
;
12111 if(selectedNode
&& target
== selectedNode
){
12112 this.onClick(selectedNode
);
12113 }else if(hoveredNode
&& target
== hoveredNode
){ // drag to select
12114 this._setSelectedAttr(hoveredNode
);
12115 this.onClick(hoveredNode
);
12119 _onMouseOut: function(/*Event*/ evt
, /*DomNode*/ target
){
12120 if(this._hoveredNode
){
12121 this.onUnhover(this._hoveredNode
);
12122 this._hoveredNode
= null;
12124 if(this._isDragging
){
12125 this._cancelDrag
= (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
12129 _onMouseOver: function(/*Event*/ evt
, /*DomNode*/ target
){
12130 if(this._cancelDrag
){
12131 var time
= (new Date()).getTime();
12132 if(time
> this._cancelDrag
){
12133 this._isDragging
= false;
12135 this._cancelDrag
= null;
12137 this._hoveredNode
= target
;
12138 this.onHover(target
);
12139 if(this._isDragging
){
12140 this._setSelectedAttr(target
);
12148 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
12149 'dojo/cookie':function(){
12150 define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo
, regexp
){
12156 var __cookieProps = {
12157 // expires: Date|String|Number?
12158 // If a number, the number of days from today at which the cookie
12159 // will expire. If a date, the date past which the cookie will expire.
12160 // If expires is in the past, the cookie will be deleted.
12161 // If expires is omitted or is 0, the cookie will expire when the browser closes.
12163 // The path to use for the cookie.
12165 // The domain to use for the cookie.
12166 // secure: Boolean?
12167 // Whether to only send the cookie on secure connections
12172 dojo
.cookie = function(/*String*/name
, /*String?*/ value
, /*__cookieProps?*/ props
){
12174 // Get or set a cookie.
12176 // If one argument is passed, returns the value of the cookie
12177 // For two or more arguments, acts as a setter.
12179 // Name of the cookie
12181 // Value for the cookie
12183 // Properties for the cookie
12185 // set a cookie with the JSON-serialized contents of an object which
12186 // will expire 5 days from now:
12187 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12188 // | cookie("configObj", json.stringify(config, {expires: 5 }));
12192 // de-serialize a cookie back into a JavaScript object:
12193 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12194 // | config = json.parse(cookie("configObj"));
12198 // delete a cookie:
12199 // | require(["dojo/cookie"], function(cookie){
12200 // | cookie("configObj", null, {expires: -1});
12202 var c
= document
.cookie
, ret
;
12203 if(arguments
.length
== 1){
12204 var matches
= c
.match(new RegExp("(?:^|; )" + regexp
.escapeString(name
) + "=([^;]*)"));
12205 ret
= matches
? decodeURIComponent(matches
[1]) : undefined;
12207 props
= props
|| {};
12208 // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
12209 var exp
= props
.expires
;
12210 if(typeof exp
== "number"){
12211 var d
= new Date();
12212 d
.setTime(d
.getTime() + exp
*24*60*60*1000);
12213 exp
= props
.expires
= d
;
12215 if(exp
&& exp
.toUTCString
){ props
.expires
= exp
.toUTCString(); }
12217 value
= encodeURIComponent(value
);
12218 var updatedCookie
= name
+ "=" + value
, propName
;
12219 for(propName
in props
){
12220 updatedCookie
+= "; " + propName
;
12221 var propValue
= props
[propName
];
12222 if(propValue
!== true){ updatedCookie
+= "=" + propValue
; }
12224 document
.cookie
= updatedCookie
;
12226 return ret
; // String|undefined
12229 dojo
.cookie
.isSupported = function(){
12231 // Use to determine if the current browser supports cookies or not.
12233 // Returns true if user allows cookies.
12234 // Returns false if user doesn't allow cookies.
12236 if(!("cookieEnabled" in navigator
)){
12237 this("__djCookieTest__", "CookiesAllowed");
12238 navigator
.cookieEnabled
= this("__djCookieTest__") == "CookiesAllowed";
12239 if(navigator
.cookieEnabled
){
12240 this("__djCookieTest__", "", {expires
: -1});
12243 return navigator
.cookieEnabled
;
12246 return dojo
.cookie
;
12250 'dojo/cache':function(){
12251 define("dojo/cache", ["./_base/kernel", "./text"], function(dojo
){
12255 // dojo.cache is defined in dojo/text
12260 'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"▼ \" 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=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n",
12261 'dijit/ProgressBar':function(){
12263 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\"> </span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"}});
12264 define("dijit/ProgressBar", [
12265 "require", // require.toUrl
12266 "dojo/_base/declare", // declare
12267 "dojo/dom-class", // domClass.toggle
12268 "dojo/_base/lang", // lang.mixin
12269 "dojo/number", // number.format
12271 "./_TemplatedMixin",
12272 "dojo/text!./templates/ProgressBar.html"
12273 ], function(require
, declare
, domClass
, lang
, number
, _Widget
, _TemplatedMixin
, template
){
12276 // dijit/ProgressBar
12279 return declare("dijit.ProgressBar", [_Widget
, _TemplatedMixin
], {
12281 // A progress indication widget, showing the amount completed
12282 // (often the percentage completed) of a task.
12285 // | <div data-dojo-type="ProgressBar"
12287 // | value="..." maximum="...">
12290 // progress: [const] String (Percentage or Number)
12291 // Number or percentage indicating amount of task completed.
12292 // Deprecated. Use "value" instead.
12295 // value: String (Percentage or Number)
12296 // Number or percentage indicating amount of task completed.
12297 // With "%": percentage value, 0% <= progress <= 100%, or
12298 // without "%": absolute value, 0 <= progress <= maximum.
12299 // Infinity means that the progress bar is indeterminate.
12302 // maximum: [const] Float
12303 // Max sample number
12306 // places: [const] Number
12307 // Number of places to show in values; 0 by default
12310 // indeterminate: [const] Boolean
12311 // If false: show progress value (number or percentage).
12312 // If true: show that a process is underway but that the amount completed is unknown.
12313 // Deprecated. Use "value" instead.
12314 indeterminate
: false,
12317 // Label on progress bar. Defaults to percentage for determinate progress bar and
12318 // blank for indeterminate progress bar.
12322 // this is the field name (for a form) if set. This needs to be set if you want to use
12323 // this widget in a dijit/form/Form widget (such as dijit/Dialog)
12326 templateString
: template
,
12328 // _indeterminateHighContrastImagePath: [private] URL
12329 // URL to image to use for indeterminate progress bar when display is in high contrast mode
12330 _indeterminateHighContrastImagePath
:
12331 require
.toUrl("./themes/a11y/indeterminate_progress.gif"),
12333 postMixInProperties: function(){
12334 this.inherited(arguments
);
12336 // Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
12337 if(!(this.params
&& "value" in this.params
)){
12338 this.value
= this.indeterminate
? Infinity
: this.progress
;
12342 buildRendering: function(){
12343 this.inherited(arguments
);
12344 this.indeterminateHighContrastImage
.setAttribute("src",
12345 this._indeterminateHighContrastImagePath
.toString());
12349 update: function(/*Object?*/attributes
){
12351 // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
12352 // set("value", ...) rather than calling this method directly.
12354 // May provide progress and/or maximum properties on this parameter;
12355 // see attribute specs for details.
12357 // | myProgressBar.update({'indeterminate': true});
12358 // | myProgressBar.update({'progress': 80});
12359 // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
12363 // TODO: deprecate this method and use set() instead
12365 lang
.mixin(this, attributes
|| {});
12366 var tip
= this.internalProgress
, ap
= this.domNode
;
12368 if(this.indeterminate
){
12369 ap
.removeAttribute("aria-valuenow");
12371 if(String(this.progress
).indexOf("%") != -1){
12372 percent
= Math
.min(parseFloat(this.progress
)/100, 1);
12373 this.progress
= percent
* this.maximum
;
12375 this.progress
= Math
.min(this.progress
, this.maximum
);
12376 percent
= this.maximum
? this.progress
/ this.maximum
: 0;
12378 ap
.setAttribute("aria-valuenow", this.progress
);
12381 // Even indeterminate ProgressBars should have these attributes
12382 ap
.setAttribute("aria-describedby", this.labelNode
.id
);
12383 ap
.setAttribute("aria-valuemin", 0);
12384 ap
.setAttribute("aria-valuemax", this.maximum
);
12386 this.labelNode
.innerHTML
= this.report(percent
);
12388 domClass
.toggle(this.domNode
, "dijitProgressBarIndeterminate", this.indeterminate
);
12389 tip
.style
.width
= (percent
* 100) + "%";
12393 _setValueAttr: function(v
){
12394 this._set("value", v
);
12396 this.update({indeterminate
:true});
12398 this.update({indeterminate
:false, progress
:v
});
12402 _setLabelAttr: function(label
){
12403 this._set("label", label
);
12407 _setIndeterminateAttr: function(indeterminate
){
12408 // Deprecated, use set("value", ...) instead
12409 this.indeterminate
= indeterminate
;
12413 report: function(/*float*/percent
){
12415 // Generates message to show inside progress bar (normally indicating amount of task completed).
12416 // May be overridden.
12420 return this.label
? this.label
:
12421 (this.indeterminate
? " " : number
.format(percent
, { type
: "percent", places
: this.places
, locale
: this.lang
}));
12424 onChange: function(){
12426 // Callback fired when progress updates.
12435 'dijit/_base/popup':function(){
12436 define("dijit/_base/popup", [
12437 "dojo/dom-class", // domClass.contains
12438 "dojo/_base/window",
12440 "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
12441 ], function(domClass
, win
, popup
){
12444 // dijit/_base/popup
12449 // Deprecated. Old module for popups, new code should use dijit/popup directly.
12454 // Hack support for old API passing in node instead of a widget (to various methods)
12455 var origCreateWrapper
= popup
._createWrapper
;
12456 popup
._createWrapper = function(widget
){
12457 if(!widget
.declaredClass
){
12458 // make fake widget to pass to new API
12460 _popupWrapper
: (widget
.parentNode
&& domClass
.contains(widget
.parentNode
, "dijitPopup")) ?
12461 widget
.parentNode
: null,
12463 destroy: function(){},
12464 ownerDocument
: widget
.ownerDocument
,
12465 ownerDocumentBody
: win
.body(widget
.ownerDocument
)
12468 return origCreateWrapper
.call(this, widget
);
12471 // Support old format of orient parameter
12472 var origOpen
= popup
.open
;
12473 popup
.open = function(/*__OpenArgs*/ args
){
12474 // Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
12475 // Don't do conversion for:
12476 // - null parameter (that means to use the default positioning)
12477 // - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
12478 // - new format, ex: ["below", "above"]
12479 // - return value from deprecated dijit.getPopupAroundAlignment() method,
12480 // ex: ["below", "above"]
12481 if(args
.orient
&& typeof args
.orient
!= "string" && !("length" in args
.orient
)){
12483 for(var key
in args
.orient
){
12484 ary
.push({aroundCorner
: key
, corner
: args
.orient
[key
]});
12489 return origOpen
.call(this, args
);
12496 'dijit/ColorPalette':function(){
12498 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
12499 define("dijit/ColorPalette", [
12500 "require", // require.toUrl
12501 "dojo/text!./templates/ColorPalette.html",
12502 "./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
12503 "./_TemplatedMixin",
12505 "./hccss", // has("highcontrast")
12506 "dojo/i18n", // i18n.getLocalization
12507 "dojo/_base/Color", // dojo.Color dojo.Color.named
12508 "dojo/_base/declare", // declare
12509 "dojo/dom-construct", // domConstruct.place
12510 "dojo/string", // string.substitute
12511 "dojo/i18n!dojo/nls/colors", // translations
12512 "dojo/colors" // extend dojo.Color w/names of other colors
12513 ], function(require
, template
, _Widget
, _TemplatedMixin
, _PaletteMixin
, has
, i18n
, Color
,
12514 declare
, domConstruct
, string
){
12517 // dijit/ColorPalette
12519 var ColorPalette
= declare("dijit.ColorPalette", [_Widget
, _TemplatedMixin
, _PaletteMixin
], {
12521 // A keyboard accessible color-picking widget
12523 // Grid showing various colors, so the user can pick a certain color.
12524 // Can be used standalone, or as a popup.
12527 // | <div data-dojo-type="dijit/ColorPalette"></div>
12530 // | var picker = new dijit.ColorPalette({ },srcNode);
12531 // | picker.startup();
12534 // palette: [const] String
12535 // Size of grid, either "7x10" or "3x4".
12538 // _palettes: [protected] Map
12539 // This represents the value of the colors.
12540 // The first level is a hashmap of the different palettes available.
12541 // The next two dimensions represent the columns and rows of colors.
12543 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
12544 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
12545 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
12546 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
12547 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
12548 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
12549 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
12551 "3x4": [["white", "lime", "green", "blue"],
12552 ["silver", "yellow", "fuchsia", "navy"],
12553 ["gray", "red", "purple", "black"]]
12556 // templateString: String
12557 // The template of this widget.
12558 templateString
: template
,
12560 baseClass
: "dijitColorPalette",
12562 _dyeFactory: function(value
, row
, col
, title
){
12563 // Overrides _PaletteMixin._dyeFactory().
12564 return new this._dyeClass(value
, row
, col
, title
);
12567 buildRendering: function(){
12568 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
12570 this.inherited(arguments
);
12572 // Creates customized constructor for dye class (color of a single cell) for
12573 // specified palette and high-contrast vs. normal mode. Used in _getDye().
12574 this._dyeClass
= declare(ColorPalette
._Color
, {
12575 palette
: this.palette
12578 // Creates <img> nodes in each cell of the template.
12579 this._preparePalette(
12580 this._palettes
[this.palette
],
12581 i18n
.getLocalization("dojo", "colors", this.lang
));
12585 ColorPalette
._Color
= declare("dijit._Color", Color
, {
12587 // Object associated with each cell in a ColorPalette palette.
12588 // Implements dijit/Dye.
12590 // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
12591 // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
12592 // for showing the color.
12594 "<span class='dijitInline dijitPaletteImg'>" +
12595 "<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
12598 // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
12599 // but scrolled and clipped to show the correct color only
12601 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
12602 "<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
12605 // _imagePaths: [protected] Map
12606 // This is stores the path to the palette images used for high-contrast mode display
12608 "7x10": require
.toUrl("./themes/a11y/colors7x10.png"),
12609 "3x4": require
.toUrl("./themes/a11y/colors3x4.png")
12612 constructor: function(alias
, row
, col
, title
){
12614 // Constructor for ColorPalette._Color
12616 // English name of the color.
12618 // Vertical position in grid.
12620 // Horizontal position in grid.
12622 // Localized name of the color.
12623 this._title
= title
;
12626 this.setColor(Color
.named
[alias
]);
12629 getValue: function(){
12631 // Note that although dijit._Color is initialized with a value like "white" getValue() always
12632 // returns a hex value
12633 return this.toHex();
12636 fillCell: function(/*DOMNode*/ cell
, /*String*/ blankGif
){
12637 var html
= string
.substitute(has("highcontrast") ? this.hcTemplate
: this.template
, {
12638 // substitution variables for normal mode
12639 color
: this.toHex(),
12640 blankGif
: blankGif
,
12642 title
: this._title
,
12644 // variables used for high contrast mode
12645 image
: this._imagePaths
[this.palette
].toString(),
12646 left
: this._col
* -20 - 5,
12647 top
: this._row
* -20 - 5,
12648 size
: this.palette
== "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
12651 domConstruct
.place(html
, cell
);
12656 return ColorPalette
;
12660 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">●</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
12661 'dojo/_base/url':function(){
12662 define("dojo/_base/url", ["./kernel"], function(dojo
){
12667 ore
= new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
12668 ire
= new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
12673 // resolve uri components relative to each other
12674 for(var i
= 1; i
<_a
.length
; i
++){
12675 if(!_a
[i
]){ continue; }
12677 // Safari doesn't support this.constructor so we have to be explicit
12678 // FIXME: Tracked (and fixed) in Webkit bug 3537.
12679 // http://bugs.webkit.org/show_bug.cgi?id=3537
12680 var relobj
= new _Url(_a
[i
]+""),
12681 uriobj
= new _Url(uri
[0]+"");
12684 relobj
.path
== "" &&
12686 !relobj
.authority
&&
12689 if(relobj
.fragment
!= n
){
12690 uriobj
.fragment
= relobj
.fragment
;
12693 }else if(!relobj
.scheme
){
12694 relobj
.scheme
= uriobj
.scheme
;
12696 if(!relobj
.authority
){
12697 relobj
.authority
= uriobj
.authority
;
12699 if(relobj
.path
.charAt(0) != "/"){
12700 var path
= uriobj
.path
.substring(0,
12701 uriobj
.path
.lastIndexOf("/") + 1) + relobj
.path
;
12703 var segs
= path
.split("/");
12704 for(var j
= 0; j
< segs
.length
; j
++){
12705 if(segs
[j
] == "."){
12706 // flatten "./" references
12707 if(j
== segs
.length
- 1){
12713 }else if(j
> 0 && !(j
== 1 && segs
[0] == "") &&
12714 segs
[j
] == ".." && segs
[j
-1] != ".."){
12715 // flatten "../" references
12716 if(j
== (segs
.length
- 1)){
12720 segs
.splice(j
- 1, 2);
12725 relobj
.path
= segs
.join("/");
12732 uri
.push(relobj
.scheme
, ":");
12734 if(relobj
.authority
){
12735 uri
.push("//", relobj
.authority
);
12737 uri
.push(relobj
.path
);
12739 uri
.push("?", relobj
.query
);
12741 if(relobj
.fragment
){
12742 uri
.push("#", relobj
.fragment
);
12746 this.uri
= uri
.join("");
12748 // break the uri into its main components
12749 var r
= this.uri
.match(ore
);
12751 this.scheme
= r
[2] || (r
[1] ? "" : n
);
12752 this.authority
= r
[4] || (r
[3] ? "" : n
);
12753 this.path
= r
[5]; // can never be undefined
12754 this.query
= r
[7] || (r
[6] ? "" : n
);
12755 this.fragment
= r
[9] || (r
[8] ? "" : n
);
12757 if(this.authority
!= n
){
12758 // server based naming authority
12759 r
= this.authority
.match(ire
);
12761 this.user
= r
[3] || n
;
12762 this.password
= r
[4] || n
;
12763 this.host
= r
[6] || r
[7]; // ipv6 || ipv4
12764 this.port
= r
[9] || n
;
12767 _Url
.prototype.toString = function(){ return this.uri
; };
12769 return dojo
._Url
= _Url
;
12773 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n",
12774 'dojo/text':function(){
12775 define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo
, require
, has
, xhr
){
12781 getText= function(url
, sync
, load
){
12782 xhr("GET", {url
: url
, sync
:!!sync
, load
: load
, headers
: dojo
.config
.textPluginHeaders
|| {}});
12785 // TODOC: only works for dojo AMD loader
12786 if(require
.getText
){
12787 getText
= require
.getText
;
12789 console
.error("dojo/text plugin failed to load because loader does not support getText");
12796 strip= function(text
){
12797 //Strips <?xml ...?> declarations so that external SVG and XML
12798 //documents can be added to a document without worry. Also, if the string
12799 //is an HTML document, only the part inside the body tag is returned.
12801 text
= text
.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
12802 var matches
= text
.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
12816 dojo
.cache = function(/*String||Object*/module
, /*String*/url
, /*String||Object?*/value
){
12818 // A getter and setter for storing the string content associated with the
12819 // module and url arguments.
12821 // If module is a string that contains slashes, then it is interpretted as a fully
12822 // resolved path (typically a result returned by require.toUrl), and url should not be
12823 // provided. This is the preferred signature. If module is a string that does not
12824 // contain slashes, then url must also be provided and module and url are used to
12825 // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
12826 // If value is specified, the cache value for the moduleUrl will be set to
12827 // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
12828 // in its internal cache and return that cached value for the URL. To clear
12829 // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
12830 // the URL contents, only modules on the same domain of the page can use this capability.
12831 // The build system can inline the cache values though, to allow for xdomain hosting.
12832 // module: String||Object
12833 // If a String with slashes, a fully resolved path; if a String without slashes, the
12834 // module name to use for the base part of the URL, similar to module argument
12835 // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
12836 // generates a valid path for the cache item. For example, a dojo._Url object.
12838 // The rest of the path to append to the path derived from the module argument. If
12839 // module is an object, then this second argument should be the "value" argument instead.
12840 // value: String||Object?
12841 // If a String, the value to use in the cache for the module/url combination.
12842 // If an Object, it can have two properties: value and sanitize. The value property
12843 // should be the value to use in the cache, and sanitize can be set to true or false,
12844 // to indicate if XML declarations should be removed from the value and if the HTML
12845 // inside a body tag in the value should be extracted as the real value. The value argument
12846 // or the value property on the value argument are usually only used by the build system
12847 // as it inlines cache content.
12849 // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
12850 // of call is used to avoid an issue with the build system erroneously trying to intern
12851 // this example. To get the build system to intern your dojo.cache calls, use the
12852 // "dojo.cache" style of call):
12853 // | //If template.html contains "<h1>Hello</h1>" that will be
12854 // | //the value for the text variable.
12855 // | var text = dojo["cache"]("my.module", "template.html");
12857 // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
12858 // (the dojo["cache"] style of call is used to avoid an issue with the build system
12859 // erroneously trying to intern this example. To get the build system to intern your
12860 // dojo.cache calls, use the "dojo.cache" style of call):
12861 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12862 // | //text variable will contain just "<h1>Hello</h1>".
12863 // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
12865 // Same example as previous, but demonstrates how an object can be passed in as
12866 // the first argument, then the value argument can then be the second argument.
12867 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12868 // | //text variable will contain just "<h1>Hello</h1>".
12869 // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
12871 // * (string string [value]) => (module, url, value)
12872 // * (object [value]) => (module, value), url defaults to ""
12874 // * if module is an object, then it must be convertable to a string
12875 // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
12876 // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
12878 if(typeof module
=="string"){
12879 if(/\//.test(module
)){
12880 // module is a version 1.7+ resolved path
12884 // module is a version 1.6- argument to dojo.moduleUrl
12885 key
= require
.toUrl(module
.replace(/\./g, "/") + (url
? ("/" + url
) : ""));
12892 val
= (value
!= undefined && typeof value
!= "string") ? value
.value
: value
,
12893 sanitize
= value
&& value
.sanitize
;
12895 if(typeof val
== "string"){
12896 //We have a string, set cache value
12897 theCache
[key
] = val
;
12898 return sanitize
? strip(val
) : val
;
12899 }else if(val
=== null){
12900 //Remove cached value
12901 delete theCache
[key
];
12904 //Allow cache values to be empty strings. If key property does
12905 //not exist, fetch it.
12906 if(!(key
in theCache
)){
12907 getText(key
, true, function(text
){
12908 theCache
[key
]= text
;
12911 return sanitize
? strip(theCache
[key
]) : theCache
[key
];
12917 // This module implements the dojo/text! plugin and the dojo.cache API.
12919 // We choose to include our own plugin to leverage functionality already contained in dojo
12920 // and thereby reduce the size of the plugin compared to various foreign loader implementations.
12921 // Also, this allows foreign AMD loaders to be used without their plugins.
12923 // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
12924 // loader. This feature is outside the scope of the CommonJS plugins specification.
12926 // the dojo/text caches it's own resources because of dojo.cache
12929 normalize: function(id
, toAbsMid
){
12930 // id is something like (path may be relative):
12932 // "path/to/text.html"
12933 // "path/to/text.html!strip"
12934 var parts
= id
.split("!"),
12936 return (/^\./.test(url
) ? toAbsMid(url
) : url
) + (parts
[1] ? "!" + parts
[1] : "");
12939 load: function(id
, require
, load
){
12941 // Path to the resource.
12942 // require: Function
12943 // Object that include the function toUrl with given id returns a valid URL from which to load the text.
12945 // Callback function which will be called, when the loading finished.
12947 // id is something like (path is always absolute):
12949 // "path/to/text.html"
12950 // "path/to/text.html!strip"
12952 parts
= id
.split("!"),
12953 stripFlag
= parts
.length
>1,
12955 url
= require
.toUrl(parts
[0]),
12956 requireCacheUrl
= "url:" + url
,
12958 finish = function(text
){
12959 load(stripFlag
? strip(text
) : text
);
12961 if(absMid
in theCache
){
12962 text
= theCache
[absMid
];
12963 }else if(requireCacheUrl
in require
.cache
){
12964 text
= require
.cache
[requireCacheUrl
];
12965 }else if(url
in theCache
){
12966 text
= theCache
[url
];
12968 if(text
===notFound
){
12970 pending
[url
].push(finish
);
12972 var pendingList
= pending
[url
] = [finish
];
12973 getText(url
, !require
.async
, function(text
){
12974 theCache
[absMid
]= theCache
[url
]= text
;
12975 for(var i
= 0; i
<pendingList
.length
;){
12976 pendingList
[i
++](text
);
12978 delete pending
[url
];
12991 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
12992 'dojo/uacss':function(){
12993 define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./sniff", "./_base/window"],
12994 function(geometry
, lang
, ready
, has
, baseWindow
){
13002 // Applies pre-set CSS classes to the top-level HTML node, based on:
13004 // - browser (ex: dj_ie)
13005 // - browser version (ex: dj_ie6)
13006 // - box model (ex: dj_contentBox)
13007 // - text direction (ex: dijitRtl)
13009 // In addition, browser, browser version, and box model are
13010 // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
13012 // Returns the has() method.
13017 html
= baseWindow
.doc
.documentElement
,
13019 opera
= has("opera"),
13022 boxModel
= geometry
.boxModel
.replace(/-/,''),
13025 "dj_quirks": has("quirks"),
13027 // NOTE: Opera not supported by dijit
13030 "dj_khtml": has("khtml"),
13032 "dj_webkit": has("webkit"),
13033 "dj_safari": has("safari"),
13034 "dj_chrome": has("chrome"),
13036 "dj_gecko": has("mozilla")
13037 }; // no dojo unsupported browsers
13040 classes
["dj_ie"] = true;
13041 classes
["dj_ie" + maj(ie
)] = true;
13042 classes
["dj_iequirks"] = has("quirks");
13045 classes
["dj_ff" + maj(ff
)] = true;
13048 classes
["dj_" + boxModel
] = true;
13050 // apply browser, browser version, and box model class names
13052 for(var clz
in classes
){
13054 classStr
+= clz
+ " ";
13057 html
.className
= lang
.trim(html
.className
+ " " + classStr
);
13059 // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
13060 // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
13061 // priority is 90 to run ahead of parser priority of 100
13062 ready(90, function(){
13063 if(!geometry
.isBodyLtr()){
13064 var rtlClassStr
= "dj_rtl dijitRtl " + classStr
.replace(/ /g
, "-rtl ");
13065 html
.className
= lang
.trim(html
.className
+ " " + rtlClassStr
+ "dj_rtl dijitRtl " + classStr
.replace(/ /g
, "-rtl "));
13072 'dijit/Tooltip':function(){
13074 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}});
13075 define("dijit/Tooltip", [
13076 "dojo/_base/array", // array.forEach array.indexOf array.map
13077 "dojo/_base/declare", // declare
13078 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
13079 "dojo/dom", // dom.byId
13080 "dojo/dom-class", // domClass.add
13081 "dojo/dom-geometry", // domGeometry.position
13082 "dojo/dom-style", // domStyle.set, domStyle.get
13083 "dojo/_base/lang", // lang.hitch lang.isArrayLike
13086 "dojo/sniff", // has("ie")
13087 "./_base/manager", // manager.defaultDuration
13090 "./_TemplatedMixin",
13091 "./BackgroundIframe",
13092 "dojo/text!./templates/Tooltip.html",
13093 "./main" // sets dijit.showTooltip etc. for back-compat
13094 ], function(array
, declare
, fx
, dom
, domClass
, domGeometry
, domStyle
, lang
, mouse
, on
, has
,
13095 manager
, place
, _Widget
, _TemplatedMixin
, BackgroundIframe
, template
, dijit
){
13101 // TODO: Tooltip should really share more positioning code with TooltipDialog, like:
13102 // - the orient() method
13103 // - the connector positioning code in show()
13104 // - the dijitTooltip[Dialog] class
13106 // The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
13107 // with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
13109 var MasterTooltip
= declare("dijit._MasterTooltip", [_Widget
, _TemplatedMixin
], {
13111 // Internal widget that holds the actual tooltip markup,
13112 // which occurs once per page.
13113 // Called by Tooltip widgets which are just containers to hold
13118 // duration: Integer
13119 // Milliseconds to fade in/fade out
13120 duration
: manager
.defaultDuration
,
13122 templateString
: template
,
13124 postCreate: function(){
13125 this.ownerDocumentBody
.appendChild(this.domNode
);
13127 this.bgIframe
= new BackgroundIframe(this.domNode
);
13129 // Setup fade-in and fade-out functions.
13130 this.fadeIn
= fx
.fadeIn({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onShow") });
13131 this.fadeOut
= fx
.fadeOut({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onHide") });
13134 show: function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
13136 // Display tooltip w/specified contents to right of specified node
13137 // (To left if there's no space on the right, or if rtl == true)
13138 // innerHTML: String
13139 // Contents of the tooltip
13140 // aroundNode: DomNode|dijit/place.__Rectangle
13141 // Specifies that tooltip should be next to this node / area
13142 // position: String[]?
13143 // List of positions to try to position tooltip (ex: ["right", "above"])
13145 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13146 // means "rtl"; specifies GUI direction, not text direction.
13147 // textDir: String?
13148 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13151 if(this.aroundNode
&& this.aroundNode
=== aroundNode
&& this.containerNode
.innerHTML
== innerHTML
){
13155 if(this.fadeOut
.status() == "playing"){
13156 // previous tooltip is being hidden; wait until the hide completes then show new one
13157 this._onDeck
=arguments
;
13160 this.containerNode
.innerHTML
=innerHTML
;
13163 this.set("textDir", textDir
);
13166 this.containerNode
.align
= rtl
? "right" : "left"; //fix the text alignment
13168 var pos
= place
.around(this.domNode
, aroundNode
,
13169 position
&& position
.length
? position
: Tooltip
.defaultPosition
, !rtl
, lang
.hitch(this, "orient"));
13171 // Position the tooltip connector for middle alignment.
13172 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
13173 var aroundNodeCoords
= pos
.aroundNodePos
;
13174 if(pos
.corner
.charAt(0) == 'M' && pos
.aroundCorner
.charAt(0) == 'M'){
13175 this.connectorNode
.style
.top
= aroundNodeCoords
.y
+ ((aroundNodeCoords
.h
- this.connectorNode
.offsetHeight
) >> 1) - pos
.y
+ "px";
13176 this.connectorNode
.style
.left
= "";
13177 }else if(pos
.corner
.charAt(1) == 'M' && pos
.aroundCorner
.charAt(1) == 'M'){
13178 this.connectorNode
.style
.left
= aroundNodeCoords
.x
+ ((aroundNodeCoords
.w
- this.connectorNode
.offsetWidth
) >> 1) - pos
.x
+ "px";
13180 // Not *-centered, but just above/below/after/before
13181 this.connectorNode
.style
.left
= "";
13182 this.connectorNode
.style
.top
= "";
13186 domStyle
.set(this.domNode
, "opacity", 0);
13187 this.fadeIn
.play();
13188 this.isShowingNow
= true;
13189 this.aroundNode
= aroundNode
;
13192 orient: function(/*DomNode*/ node
, /*String*/ aroundCorner
, /*String*/ tooltipCorner
, /*Object*/ spaceAvailable
, /*Object*/ aroundNodeCoords
){
13194 // Private function to set CSS for tooltip node based on which position it's in.
13195 // This is called by the dijit popup code. It will also reduce the tooltip's
13196 // width to whatever width is available
13200 this.connectorNode
.style
.top
= ""; //reset to default
13202 var heightAvailable
= spaceAvailable
.h
,
13203 widthAvailable
= spaceAvailable
.w
;
13205 node
.className
= "dijitTooltip " +
13207 "MR-ML": "dijitTooltipRight",
13208 "ML-MR": "dijitTooltipLeft",
13209 "TM-BM": "dijitTooltipAbove",
13210 "BM-TM": "dijitTooltipBelow",
13211 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
13212 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
13213 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
13214 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
13215 "BR-BL": "dijitTooltipRight",
13216 "BL-BR": "dijitTooltipLeft"
13217 }[aroundCorner
+ "-" + tooltipCorner
];
13219 // reset width; it may have been set by orient() on a previous tooltip show()
13220 this.domNode
.style
.width
= "auto";
13222 // Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
13223 // Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
13224 // negative number since that causes an exception on IE.
13225 var size
= domGeometry
.position(this.domNode
);
13226 if(has("ie") == 9){
13227 // workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
13231 var width
= Math
.min((Math
.max(widthAvailable
,1)), size
.w
);
13233 domGeometry
.setMarginBox(this.domNode
, {w
: width
});
13235 // Reposition the tooltip connector.
13236 if(tooltipCorner
.charAt(0) == 'B' && aroundCorner
.charAt(0) == 'B'){
13237 var bb
= domGeometry
.position(node
);
13238 var tooltipConnectorHeight
= this.connectorNode
.offsetHeight
;
13239 if(bb
.h
> heightAvailable
){
13240 // The tooltip starts at the top of the page and will extend past the aroundNode
13241 var aroundNodePlacement
= heightAvailable
- ((aroundNodeCoords
.h
+ tooltipConnectorHeight
) >> 1);
13242 this.connectorNode
.style
.top
= aroundNodePlacement
+ "px";
13243 this.connectorNode
.style
.bottom
= "";
13245 // Align center of connector with center of aroundNode, except don't let bottom
13246 // of connector extend below bottom of tooltip content, or top of connector
13247 // extend past top of tooltip content
13248 this.connectorNode
.style
.bottom
= Math
.min(
13249 Math
.max(aroundNodeCoords
.h
/2 - tooltipConnectorHeight/2, 0),
13250 bb
.h
- tooltipConnectorHeight
) + "px";
13251 this.connectorNode
.style
.top
= "";
13254 // reset the tooltip back to the defaults
13255 this.connectorNode
.style
.top
= "";
13256 this.connectorNode
.style
.bottom
= "";
13259 return Math
.max(0, size
.w
- widthAvailable
);
13262 _onShow: function(){
13264 // Called at end of fade-in operation
13268 // the arrow won't show up on a node w/an opacity filter
13269 this.domNode
.style
.filter
="";
13273 hide: function(aroundNode
){
13275 // Hide the tooltip
13277 if(this._onDeck
&& this._onDeck
[1] == aroundNode
){
13278 // this hide request is for a show() that hasn't even started yet;
13279 // just cancel the pending show()
13281 }else if(this.aroundNode
=== aroundNode
){
13282 // this hide request is for the currently displayed tooltip
13283 this.fadeIn
.stop();
13284 this.isShowingNow
= false;
13285 this.aroundNode
= null;
13286 this.fadeOut
.play();
13288 // just ignore the call, it's for a tooltip that has already been erased
13292 _onHide: function(){
13294 // Called at end of fade-out operation
13298 this.domNode
.style
.cssText
=""; // to position offscreen again
13299 this.containerNode
.innerHTML
="";
13301 // a show request has been queued up; do it now
13302 this.show
.apply(this, this._onDeck
);
13307 _setAutoTextDir: function(/*Object*/node
){
13309 // Resolve "auto" text direction for children nodes
13313 this.applyTextDir(node
, has("ie") ? node
.outerText
: node
.textContent
);
13314 array
.forEach(node
.children
, function(child
){this._setAutoTextDir(child
); }, this);
13317 _setTextDirAttr: function(/*String*/ textDir
){
13319 // Setter for textDir.
13321 // Users shouldn't call this function; they should be calling
13322 // set('textDir', value)
13326 this._set("textDir", textDir
);
13328 if (textDir
== "auto"){
13329 this._setAutoTextDir(this.containerNode
);
13331 this.containerNode
.dir
= this.textDir
;
13336 dijit
.showTooltip = function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
13338 // Static method to display tooltip w/specified contents in specified position.
13339 // See description of dijit/Tooltip.defaultPosition for details on position parameter.
13340 // If position is not specified then dijit/Tooltip.defaultPosition is used.
13341 // innerHTML: String
13342 // Contents of the tooltip
13343 // aroundNode: place.__Rectangle
13344 // Specifies that tooltip should be next to this node / area
13345 // position: String[]?
13346 // List of positions to try to position tooltip (ex: ["right", "above"])
13348 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13349 // means "rtl"; specifies GUI direction, not text direction.
13350 // textDir: String?
13351 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13353 // After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
13354 // Possibly remove this in 2.0. Alternately, get before/after to work.
13356 position
= array
.map(position
, function(val
){
13357 return {after
: "after-centered", before
: "before-centered"}[val
] || val
;
13361 if(!Tooltip
._masterTT
){ dijit
._masterTT
= Tooltip
._masterTT
= new MasterTooltip(); }
13362 return Tooltip
._masterTT
.show(innerHTML
, aroundNode
, position
, rtl
, textDir
);
13365 dijit
.hideTooltip = function(aroundNode
){
13367 // Static method to hide the tooltip displayed via showTooltip()
13368 return Tooltip
._masterTT
&& Tooltip
._masterTT
.hide(aroundNode
);
13371 var Tooltip
= declare("dijit.Tooltip", _Widget
, {
13373 // Pops up a tooltip (a help message) when you hover over a node.
13374 // Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
13377 // Text to display in the tooltip.
13378 // Specified as innerHTML when creating the widget from markup.
13381 // showDelay: Integer
13382 // Number of milliseconds to wait after hovering over/focusing on the object, before
13383 // the tooltip is displayed.
13386 // connectId: String|String[]|DomNode|DomNode[]
13387 // Id of domNode(s) to attach the tooltip to.
13388 // When user hovers over specified dom node(s), the tooltip will appear.
13391 // position: String[]
13392 // See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
13395 // selector: String?
13396 // CSS expression to apply this Tooltip to descendants of connectIds, rather than to
13397 // the nodes specified by connectIds themselves. Useful for applying a Tooltip to
13398 // a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
13399 // Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
13401 // The application must require() an appropriate level of dojo/query to handle the selector.
13404 // TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
13405 // So, change connectId to a "", remove addTarget()/removeTarget(), etc.
13407 _setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId
){
13409 // Connect to specified node(s)
13411 // Remove connections to old nodes (if there are any)
13412 array
.forEach(this._connections
|| [], function(nested
){
13413 array
.forEach(nested
, function(handle
){ handle
.remove(); });
13416 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
13417 this._connectIds
= array
.filter(lang
.isArrayLike(newId
) ? newId
: (newId
? [newId
] : []),
13418 function(id
){ return dom
.byId(id
, this.ownerDocument
); }, this);
13420 // Make connections
13421 this._connections
= array
.map(this._connectIds
, function(id
){
13422 var node
= dom
.byId(id
, this.ownerDocument
),
13423 selector
= this.selector
,
13424 delegatedEvent
= selector
?
13425 function(eventType
){ return on
.selector(selector
, eventType
); } :
13426 function(eventType
){ return eventType
; },
13429 on(node
, delegatedEvent(mouse
.enter
), function(){
13430 self
._onHover(this);
13432 on(node
, delegatedEvent("focusin"), function(){
13433 self
._onHover(this);
13435 on(node
, delegatedEvent(mouse
.leave
), lang
.hitch(self
, "_onUnHover")),
13436 on(node
, delegatedEvent("focusout"), lang
.hitch(self
, "_onUnHover"))
13440 this._set("connectId", newId
);
13443 addTarget: function(/*OomNode|String*/ node
){
13445 // Attach tooltip to specified node if it's not already connected
13447 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13449 var id
= node
.id
|| node
;
13450 if(array
.indexOf(this._connectIds
, id
) == -1){
13451 this.set("connectId", this._connectIds
.concat(id
));
13455 removeTarget: function(/*DomNode|String*/ node
){
13457 // Detach tooltip from specified node
13459 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13461 var id
= node
.id
|| node
, // map from DOMNode back to plain id string
13462 idx
= array
.indexOf(this._connectIds
, id
);
13464 // remove id (modifies original this._connectIds but that's OK in this case)
13465 this._connectIds
.splice(idx
, 1);
13466 this.set("connectId", this._connectIds
);
13470 buildRendering: function(){
13471 this.inherited(arguments
);
13472 domClass
.add(this.domNode
,"dijitTooltipData");
13475 startup: function(){
13476 this.inherited(arguments
);
13478 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
13479 // didn't exist during the widget's initialization, then connect now.
13480 var ids
= this.connectId
;
13481 array
.forEach(lang
.isArrayLike(ids
) ? ids
: [ids
], this.addTarget
, this);
13484 getContent: function(/*DomNode*/ node
){
13486 // User overridable function that return the text to display in the tooltip.
13489 return this.label
|| this.domNode
.innerHTML
;
13492 _onHover: function(/*DomNode*/ target
){
13494 // Despite the name of this method, it actually handles both hover and focus
13495 // events on the target node, setting a timer to show the tooltip.
13498 if(!this._showTimer
){
13499 this._showTimer
= this.defer(function(){ this.open(target
); }, this.showDelay
);
13503 _onUnHover: function(){
13505 // Despite the name of this method, it actually handles both mouseleave and blur
13506 // events on the target node, hiding the tooltip.
13510 if(this._showTimer
){
13511 this._showTimer
.remove();
13512 delete this._showTimer
;
13517 open: function(/*DomNode*/ target
){
13519 // Display the tooltip; usually not called directly.
13523 if(this._showTimer
){
13524 this._showTimer
.remove();
13525 delete this._showTimer
;
13528 var content
= this.getContent(target
);
13532 Tooltip
.show(content
, target
, this.position
, !this.isLeftToRight(), this.textDir
);
13534 this._connectNode
= target
; // _connectNode means "tooltip currently displayed for this node"
13535 this.onShow(target
, this.position
);
13540 // Hide the tooltip or cancel timer for show of tooltip
13544 if(this._connectNode
){
13545 // if tooltip is currently shown
13546 Tooltip
.hide(this._connectNode
);
13547 delete this._connectNode
;
13550 if(this._showTimer
){
13551 // if tooltip is scheduled to be shown (after a brief delay)
13552 this._showTimer
.remove();
13553 delete this._showTimer
;
13557 onShow: function(/*===== target, position =====*/){
13559 // Called when the tooltip is shown
13564 onHide: function(){
13566 // Called when the tooltip is hidden
13571 destroy: function(){
13574 // Remove connections manually since they aren't registered to be removed by _WidgetBase
13575 array
.forEach(this._connections
|| [], function(nested
){
13576 array
.forEach(nested
, function(handle
){ handle
.remove(); });
13579 this.inherited(arguments
);
13583 Tooltip
._MasterTooltip
= MasterTooltip
; // for monkey patching
13584 Tooltip
.show
= dijit
.showTooltip
; // export function through module return value
13585 Tooltip
.hide
= dijit
.hideTooltip
; // export function through module return value
13587 Tooltip
.defaultPosition
= ["after-centered", "before-centered"];
13590 lang.mixin(Tooltip, {
13591 // defaultPosition: String[]
13592 // This variable controls the position of tooltips, if the position is not specified to
13593 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
13594 // possible for `dijit/place.around()`. The recommended values are:
13596 // - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
13597 // in the case of RTL scripts like Hebrew and Arabic
13598 // - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
13599 // in the case of RTL scripts like Hebrew and Arabic
13600 // - above-centered: tooltip is centered above anchor node
13601 // - below-centered: tooltip is centered above anchor node
13603 // The list is positions is tried, in order, until a position is found where the tooltip fits
13604 // within the viewport.
13606 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
13607 // the screen so that there's no room above the target node. Nodes with drop downs, like
13608 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
13609 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
13610 // is only room below (or above) the target node, but not both.
13617 'dojo/string':function(){
13618 define("dojo/string", [
13619 "./_base/kernel", // kernel.global
13621 ], function(kernel
, lang
){
13628 // String utilities for Dojo
13630 lang
.setObject("dojo.string", string
);
13632 string
.rep = function(/*String*/str
, /*Integer*/num
){
13634 // Efficiently replicate a string `n` times.
13636 // the string to replicate
13638 // number of times to replicate the string
13640 if(num
<= 0 || !str
){ return ""; }
13647 if(!(num
>>= 1)){ break; }
13650 return buf
.join(""); // String
13653 string
.pad = function(/*String*/text
, /*Integer*/size
, /*String?*/ch
, /*Boolean?*/end
){
13655 // Pad a string to guarantee that it is at least `size` length by
13656 // filling with the character `ch` at either the start or end of the
13657 // string. Pads at the start, by default.
13659 // the string to pad
13661 // length to provide padding
13663 // character to pad, defaults to '0'
13665 // adds padding at the end if true, otherwise pads at start
13667 // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
13668 // | string.pad("Dojo", 10, "+", true);
13673 var out
= String(text
),
13674 pad
= string
.rep(ch
, Math
.ceil((size
- out
.length
) / ch
.length
));
13675 return end
? out
+ pad
: pad
+ out
; // String
13678 string
.substitute = function( /*String*/ template
,
13679 /*Object|Array*/map
,
13680 /*Function?*/ transform
,
13681 /*Object?*/ thisObject
){
13683 // Performs parameterized substitutions on a string. Throws an
13684 // exception if any parameter is unmatched.
13686 // a string with expressions in the form `${key}` to be replaced or
13687 // `${key:format}` which specifies a format function. keys are case-sensitive.
13689 // hash to search for substitutions
13691 // a function to process all parameters before substitution takes
13692 // place, e.g. mylib.encodeXML
13694 // where to look for optional format function; default to the global
13697 // Substitutes two expressions in a string from an Array or Object
13698 // | // returns "File 'foo.html' is not found in directory '/temp'."
13699 // | // by providing substitution data in an Array
13700 // | string.substitute(
13701 // | "File '${0}' is not found in directory '${1}'.",
13702 // | ["foo.html","/temp"]
13705 // | // also returns "File 'foo.html' is not found in directory '/temp'."
13706 // | // but provides substitution data in an Object structure. Dotted
13707 // | // notation may be used to traverse the structure.
13708 // | string.substitute(
13709 // | "File '${name}' is not found in directory '${info.dir}'.",
13710 // | { name: "foo.html", info: { dir: "/temp" } }
13713 // Use a transform function to modify the values:
13714 // | // returns "file 'foo.html' is not found in directory '/temp'."
13715 // | string.substitute(
13716 // | "${0} is not found in ${1}.",
13717 // | ["foo.html","/temp"],
13718 // | function(str){
13719 // | // try to figure out the type
13720 // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
13721 // | return prefix + " '" + str + "'";
13726 // | // returns "thinger -- howdy"
13727 // | string.substitute(
13728 // | "${0:postfix}", ["thinger"], null, {
13729 // | postfix: function(value, key){
13730 // | return value + " -- howdy";
13735 thisObject
= thisObject
|| kernel
.global
;
13736 transform
= transform
?
13737 lang
.hitch(thisObject
, transform
) : function(v
){ return v
; };
13739 return template
.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
13740 function(match
, key
, format
){
13741 var value
= lang
.getObject(key
, false, map
);
13743 value
= lang
.getObject(format
, false, thisObject
).call(thisObject
, value
, key
);
13745 return transform(value
, key
).toString();
13749 string
.trim
= String
.prototype.trim
?
13750 lang
.trim
: // aliasing to the native function
13752 str
= str
.replace(/^\s+/, '');
13753 for(var i
= str
.length
- 1; i
>= 0; i
--){
13754 if(/\S/.test(str
.charAt(i
))){
13755 str
= str
.substring(0, i
+ 1);
13763 string.trim = function(str){
13765 // Trims whitespace from both sides of the string
13767 // String to be trimmed
13769 // Returns the trimmed string
13771 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
13772 // The short yet performant version of this function is dojo.trim(),
13773 // which is part of Dojo base. Uses String.prototype.trim instead, if available.
13774 return ""; // String
13782 'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>",
13783 'dijit/layout/AccordionPane':function(){
13784 define("dijit/layout/AccordionPane", [
13785 "dojo/_base/declare", // declare
13786 "dojo/_base/kernel", // kernel.deprecated
13788 ], function(declare
, kernel
, ContentPane
){
13791 // dijit/layout/AccordionPane
13793 return declare("dijit.layout.AccordionPane", ContentPane
, {
13795 // Deprecated widget. Use `dijit/layout/ContentPane` instead.
13799 constructor: function(){
13800 kernel
.deprecated("dijit.layout.AccordionPane deprecated, use ContentPane instead", "", "2.0");
13803 onSelected: function(){
13805 // called when this pane is selected
13811 'dijit/dijit':function(){
13812 define("dijit/dijit", [
13817 "./_TemplatedMixin",
13819 "./layout/_LayoutWidget",
13820 "./form/_FormWidget",
13821 "./form/_FormValueWidget"
13822 ], function(dijit
){
13830 // A roll-up for common dijit methods
13831 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
13832 // And some other stuff that we tend to pull in all the time anyway
13840 'dijit/form/DropDownButton':function(){
13842 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">▼</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n"}});
13843 define("dijit/form/DropDownButton", [
13844 "dojo/_base/declare", // declare
13845 "dojo/_base/lang", // hitch
13846 "dojo/query", // query
13847 "../registry", // registry.byNode
13848 "../popup", // dijit.popup2.hide
13852 "dojo/text!./templates/DropDownButton.html"
13853 ], function(declare
, lang
, query
, registry
, popup
, Button
, _Container
, _HasDropDown
, template
){
13856 // dijit/form/DropDownButton
13859 return declare("dijit.form.DropDownButton", [Button
, _Container
, _HasDropDown
], {
13861 // A button with a drop down
13864 // | <button data-dojo-type="dijit/form/DropDownButton">
13866 // | <div data-dojo-type="dijit/Menu">...</div>
13870 // | var button1 = new DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
13871 // | win.body().appendChild(button1);
13874 baseClass
: "dijitDropDownButton",
13876 templateString
: template
,
13878 _fillContent: function(){
13879 // Overrides Button._fillContent().
13881 // My inner HTML contains both the button contents and a drop down widget, like
13882 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
13883 // The first node is assumed to be the button content. The widget is the popup.
13885 if(this.srcNodeRef
){ // programatically created buttons might not define srcNodeRef
13886 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
13887 // content, not just nodes[0]
13888 var nodes
= query("*", this.srcNodeRef
);
13889 this.inherited(arguments
, [nodes
[0]]);
13891 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
13892 this.dropDownContainer
= this.srcNodeRef
;
13896 startup: function(){
13897 if(this._started
){ return; }
13899 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
13900 // make it invisible, and store a reference to pass to the popup code.
13901 if(!this.dropDown
&& this.dropDownContainer
){
13902 var dropDownNode
= query("[widgetId]", this.dropDownContainer
)[0];
13903 this.dropDown
= registry
.byNode(dropDownNode
);
13904 delete this.dropDownContainer
;
13907 popup
.hide(this.dropDown
);
13910 this.inherited(arguments
);
13913 isLoaded: function(){
13914 // Returns whether or not we are loaded - if our dropdown has an href,
13915 // then we want to check that.
13916 var dropDown
= this.dropDown
;
13917 return (!!dropDown
&& (!dropDown
.href
|| dropDown
.isLoaded
));
13920 loadDropDown: function(/*Function*/ callback
){
13921 // Default implementation assumes that drop down already exists,
13922 // but hasn't loaded it's data (ex: ContentPane w/href).
13923 // App must override if the drop down is lazy-created.
13924 var dropDown
= this.dropDown
;
13925 var handler
= dropDown
.on("load", lang
.hitch(this, function(){
13929 dropDown
.refresh(); // tell it to load
13932 isFocusable: function(){
13933 // Overridden so that focus is handled by the _HasDropDown mixin, not by
13934 // the _FormWidget mixin.
13935 return this.inherited(arguments
) && !this._mouseDown
;
13942 'dijit/form/_FormValueMixin':function(){
13943 define("dijit/form/_FormValueMixin", [
13944 "dojo/_base/declare", // declare
13945 "dojo/dom-attr", // domAttr.set
13946 "dojo/keys", // keys.ESCAPE
13947 "dojo/sniff", // has("ie"), has("quirks")
13948 "./_FormWidgetMixin"
13949 ], function(declare
, domAttr
, keys
, has
, _FormWidgetMixin
){
13952 // dijit/form/_FormValueMixin
13954 return declare("dijit.form._FormValueMixin", _FormWidgetMixin
, {
13956 // Mixin for widgets corresponding to native HTML elements such as `<input>` or `<select>`
13957 // that have user changeable values.
13959 // Each _FormValueMixin represents a single input value, and has a (possibly hidden) `<input>` element,
13960 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
13961 // works as expected.
13963 // readOnly: Boolean
13964 // Should this widget respond to user input?
13965 // In markup, this is specified as "readOnly".
13966 // Similar to disabled except readOnly form values are submitted.
13969 _setReadOnlyAttr: function(/*Boolean*/ value
){
13970 domAttr
.set(this.focusNode
, 'readOnly', value
);
13971 this._set("readOnly", value
);
13974 postCreate: function(){
13975 this.inherited(arguments
);
13977 if(has("ie")){ // IE won't stop the event with keypress
13978 this.connect(this.focusNode
|| this.domNode
, "onkeydown", this._onKeyDown
);
13980 // Update our reset value if it hasn't yet been set (because this.set()
13981 // is only called when there *is* a value)
13982 if(this._resetValue
=== undefined){
13983 this._lastValueReported
= this._resetValue
= this.value
;
13987 _setValueAttr: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
13989 // Hook so set('value', value) works.
13991 // Sets the value of the widget.
13992 // If the value has changed, then fire onChange event, unless priorityChange
13993 // is specified as null (or false?)
13994 this._handleOnChange(newValue
, priorityChange
);
13997 _handleOnChange: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
13999 // Called when the value of the widget has changed. Saves the new value in this.value,
14000 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
14001 this._set("value", newValue
);
14002 this.inherited(arguments
);
14007 // Restore the value to the last value passed to onChange
14008 this._setValueAttr(this._lastValueReported
, false);
14013 // Reset the widget's value to what it was at initialization time
14014 this._hasBeenBlurred
= false;
14015 this._setValueAttr(this._resetValue
, true);
14018 _onKeyDown: function(e
){
14019 if(e
.keyCode
== keys
.ESCAPE
&& !(e
.ctrlKey
|| e
.altKey
|| e
.metaKey
)){
14020 if(has("ie") < 9 || (has("ie") && has("quirks"))){
14021 e
.preventDefault(); // default behavior needs to be stopped here since keypress is too late
14022 var node
= e
.srcElement
,
14023 te
= node
.ownerDocument
.createEventObject();
14024 te
.keyCode
= keys
.ESCAPE
;
14025 te
.shiftKey
= e
.shiftKey
;
14026 node
.fireEvent('onkeypress', te
);
14034 'dijit/form/_FormWidgetMixin':function(){
14035 define("dijit/form/_FormWidgetMixin", [
14036 "dojo/_base/array", // array.forEach
14037 "dojo/_base/declare", // declare
14038 "dojo/dom-attr", // domAttr.set
14039 "dojo/dom-style", // domStyle.get
14040 "dojo/_base/lang", // lang.hitch lang.isArray
14041 "dojo/mouse", // mouse.isLeft
14042 "dojo/sniff", // has("webkit")
14043 "dojo/window", // winUtils.scrollIntoView
14044 "../a11y" // a11y.hasDefaultTabStop
14045 ], function(array
, declare
, domAttr
, domStyle
, lang
, mouse
, has
, winUtils
, a11y
){
14048 // dijit/form/_FormWidgetMixin
14050 return declare("dijit.form._FormWidgetMixin", null, {
14052 // Mixin for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
14053 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
14056 // Represents a single HTML element.
14057 // All these widgets should have these attributes just like native HTML input elements.
14058 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
14060 // They also share some common methods.
14062 // name: [const] String
14063 // Name used when submitting form; same as "name" attribute or plain HTML elements
14067 // Corresponds to the native HTML `<input>` element's attribute.
14071 // Corresponds to the native HTML `<input>` element's attribute.
14074 // type: [const] String
14075 // Corresponds to the native HTML `<input>` element's attribute.
14079 // Apply aria-label in markup to the widget's focusNode
14080 "aria-label": "focusNode",
14082 // tabIndex: String
14083 // Order fields are traversed when user hits the tab key
14085 _setTabIndexAttr
: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
14087 // disabled: Boolean
14088 // Should this widget respond to user input?
14089 // In markup, this is specified as "disabled='disabled'", or just "disabled".
14092 // intermediateChanges: Boolean
14093 // Fires onChange for each value change or only on demand
14094 intermediateChanges
: false,
14096 // scrollOnFocus: Boolean
14097 // On focus, should this widget scroll into view?
14098 scrollOnFocus
: true,
14100 // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
14101 // works with screen reader
14102 _setIdAttr
: "focusNode",
14104 _setDisabledAttr: function(/*Boolean*/ value
){
14105 this._set("disabled", value
);
14106 domAttr
.set(this.focusNode
, 'disabled', value
);
14107 if(this.valueNode
){
14108 domAttr
.set(this.valueNode
, 'disabled', value
);
14110 this.focusNode
.setAttribute("aria-disabled", value
? "true" : "false");
14113 // reset these, because after the domNode is disabled, we can no longer receive
14114 // mouse related events, see #4200
14115 this._set("hovering", false);
14116 this._set("active", false);
14118 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
14119 var attachPointNames
= "tabIndex" in this.attributeMap
? this.attributeMap
.tabIndex
:
14120 ("_setTabIndexAttr" in this) ? this._setTabIndexAttr
: "focusNode";
14121 array
.forEach(lang
.isArray(attachPointNames
) ? attachPointNames
: [attachPointNames
], function(attachPointName
){
14122 var node
= this[attachPointName
];
14123 // complex code because tabIndex=-1 on a <div> doesn't work on FF
14124 if(has("webkit") || a11y
.hasDefaultTabStop(node
)){ // see #11064 about webkit bug
14125 node
.setAttribute('tabIndex', "-1");
14127 node
.removeAttribute('tabIndex');
14131 if(this.tabIndex
!= ""){
14132 this.set('tabIndex', this.tabIndex
);
14137 _onFocus: function(/*String*/ by
){
14138 // If user clicks on the widget, even if the mouse is released outside of it,
14139 // this widget's focusNode should get focus (to mimic native browser hehavior).
14140 // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
14141 if(by
== "mouse" && this.isFocusable()){
14142 // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
14143 var focusConnector
= this.connect(this.focusNode
, "onfocus", function(){
14144 this.disconnect(mouseUpConnector
);
14145 this.disconnect(focusConnector
);
14147 // Set a global event to handle mouseup, so it fires properly
14148 // even if the cursor leaves this.domNode before the mouse up event.
14149 var mouseUpConnector
= this.connect(this.ownerDocumentBody
, "onmouseup", function(){
14150 this.disconnect(mouseUpConnector
);
14151 this.disconnect(focusConnector
);
14152 // if here, then the mousedown did not focus the focusNode as the default action
14158 if(this.scrollOnFocus
){
14159 this.defer(function(){ winUtils
.scrollIntoView(this.domNode
); }); // without defer, the input caret position can change on mouse click
14161 this.inherited(arguments
);
14164 isFocusable: function(){
14166 // Tells if this widget is focusable or not. Used internally by dijit.
14169 return !this.disabled
&& this.focusNode
&& (domStyle
.get(this.domNode
, "display") != "none");
14174 // Put focus on this widget
14175 if(!this.disabled
&& this.focusNode
.focus
){
14176 try{ this.focusNode
.focus(); }catch(e
){}/*squelch errors from hidden nodes*/
14180 compare: function(/*anything*/ val1
, /*anything*/ val2
){
14182 // Compare 2 values (as returned by get('value') for this widget).
14185 if(typeof val1
== "number" && typeof val2
== "number"){
14186 return (isNaN(val1
) && isNaN(val2
)) ? 0 : val1
- val2
;
14187 }else if(val1
> val2
){
14189 }else if(val1
< val2
){
14196 onChange: function(/*===== newValue =====*/){
14198 // Callback when this widget's value is changed.
14203 // _onChangeActive: [private] Boolean
14204 // Indicates that changes to the value should call onChange() callback.
14205 // This is false during widget initialization, to avoid calling onChange()
14206 // when the initial value is set.
14207 _onChangeActive
: false,
14209 _handleOnChange: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
14211 // Called when the value of the widget is set. Calls onChange() if appropriate
14215 // For a slider, for example, dragging the slider is priorityChange==false,
14216 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
14217 // onChange is only called form priorityChange=true events.
14220 if(this._lastValueReported
== undefined && (priorityChange
=== null || !this._onChangeActive
)){
14221 // this block executes not for a change, but during initialization,
14222 // and is used to store away the original value (or for ToggleButton, the original checked state)
14223 this._resetValue
= this._lastValueReported
= newValue
;
14225 this._pendingOnChange
= this._pendingOnChange
14226 || (typeof newValue
!= typeof this._lastValueReported
)
14227 || (this.compare(newValue
, this._lastValueReported
) != 0);
14228 if((this.intermediateChanges
|| priorityChange
|| priorityChange
=== undefined) && this._pendingOnChange
){
14229 this._lastValueReported
= newValue
;
14230 this._pendingOnChange
= false;
14231 if(this._onChangeActive
){
14232 if(this._onChangeHandle
){
14233 this._onChangeHandle
.remove();
14235 // defer allows hidden value processing to run and
14236 // also the onChange handler can safely adjust focus, etc
14237 this._onChangeHandle
= this.defer(
14239 this._onChangeHandle
= null;
14240 this.onChange(newValue
);
14241 }); // try to collapse multiple onChange's fired faster than can be processed
14246 create: function(){
14247 // Overrides _Widget.create()
14248 this.inherited(arguments
);
14249 this._onChangeActive
= true;
14252 destroy: function(){
14253 if(this._onChangeHandle
){ // destroy called before last onChange has fired
14254 this._onChangeHandle
.remove();
14255 this.onChange(this._lastValueReported
);
14257 this.inherited(arguments
);
14264 'dijit/a11yclick':function(){
14265 define("dijit/a11yclick", [
14267 "dojo/_base/array", // array.forEach
14268 "dojo/keys", // keys.ENTER keys.SPACE
14269 "dojo/_base/declare", // declare
14270 "dojo/has", // has("dom-addeventlistener")
14271 "dojo/_base/unload", // unload.addOnWindowUnload
14272 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
14273 ], function(on
, array
, keys
, declare
, has
, unload
, win
){
14278 // Keep track of where the last keydown event was, to help avoid generating
14279 // spurious ondijitclick events when:
14280 // 1. focus is on a <button> or <a>
14281 // 2. user presses then releases the ENTER key
14282 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
14283 // 4. onkeyup event fires, causing the ondijitclick handler to fire
14284 var lastKeyDownNode
= null;
14285 if(has("dom-addeventlistener")){
14286 win
.doc
.addEventListener('keydown', function(evt
){
14287 lastKeyDownNode
= evt
.target
;
14290 // Fallback path for IE6-8
14292 var keydownCallback = function(evt
){
14293 lastKeyDownNode
= evt
.srcElement
;
14295 win
.doc
.attachEvent('onkeydown', keydownCallback
);
14296 unload
.addOnWindowUnload(function(){
14297 win
.doc
.detachEvent('onkeydown', keydownCallback
);
14302 function clickKey(/*Event*/ e
){
14303 return (e
.keyCode
=== keys
.ENTER
|| e
.keyCode
=== keys
.SPACE
) &&
14304 !e
.ctrlKey
&& !e
.shiftKey
&& !e
.altKey
&& !e
.metaKey
;
14307 return function(node
, listener
){
14309 // Custom a11yclick (a.k.a. ondijitclick) event
14310 // which triggers on a mouse click, touch, or space/enter keyup.
14312 if(/input|button/i.test(node
.nodeName
)){
14313 // pass through, the browser already generates click event on SPACE/ENTER key
14314 return on(node
, "click", listener
);
14316 // Don't fire the click event unless both the keydown and keyup occur on this node.
14317 // Avoids problems where focus shifted to this node or away from the node on keydown,
14318 // either causing this node to process a stray keyup event, or causing another node
14319 // to get a stray keyup event.
14322 on(node
, "keydown", function(e
){
14323 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14325 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
14326 lastKeyDownNode
= e
.target
;
14328 // Prevent viewport scrolling on space key in IE<9.
14329 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
14330 e
.preventDefault();
14334 on(node
, "keyup", function(e
){
14335 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14336 if(clickKey(e
) && e
.target
== lastKeyDownNode
){ // === breaks greasemonkey
14337 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
14338 lastKeyDownNode
= null;
14339 on
.emit(e
.target
, "click", {
14346 on(node
, "click", function(e
){
14347 // catch mouse clicks, plus the on.emit() calls from above and below
14348 listener
.call(this, e
);
14353 // touchstart-->touchend will automatically generate a click event, but there are problems
14354 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
14355 // if click doesn't fire naturally.
14359 on(node
, "touchend", function(e
){
14360 var target
= e
.target
;
14361 clickTimer
= setTimeout(function(){
14363 on
.emit(target
, "click", {
14369 on(node
, "click", function(e
){
14370 // If browser generates a click naturally, clear the timer to fire a synthetic click event
14372 clearTimeout(clickTimer
);
14375 // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
14376 // event <300ms after the touchend event, then clear the synthetic click timer, because user
14377 // is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
14378 // zoom level has changed.
14383 remove: function(){
14384 array
.forEach(handles
, function(h
){ h
.remove(); });
14386 clearTimeout(clickTimer
);
14398 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\"> </span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
14399 'dijit/Destroyable':function(){
14400 define("dijit/Destroyable", [
14401 "dojo/_base/array", // array.forEach array.map
14403 "dojo/_base/declare"
14404 ], function(array
, aspect
, declare
){
14407 // dijit/Destroyable
14409 return declare("dijit.Destroyable", null, {
14411 // Mixin to track handles and release them when instance is destroyed.
14413 // Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
14414 // dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
14415 // Then call destroy() later to destroy this instance and release the resources.
14417 destroy: function(/*Boolean*/ preserveDom
){
14419 // Destroy this class, releasing any resources registered via own().
14420 this._destroyed
= true;
14425 // Track specified handles and remove/destroy them when this instance is destroyed, unless they were
14426 // already removed/destroyed manually.
14430 // The array of specified handles, so you can do for example:
14431 // | var handle = this.own(on(...))[0];
14433 array
.forEach(arguments
, function(handle
){
14434 var destroyMethodName
=
14435 "destroyRecursive" in handle
? "destroyRecursive" : // remove "destroyRecursive" for 2.0
14436 "destroy" in handle
? "destroy" :
14439 // When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
14440 // the handle will be destroyed before a subclass's destroy() method starts running, before it calls
14441 // this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
14442 // onDestroy() method and connect to that instead.
14443 var odh
= aspect
.before(this, "destroy", function(preserveDom
){
14444 handle
[destroyMethodName
](preserveDom
);
14447 // If handle is destroyed manually before this.destroy() is called, remove the listener set directly above.
14448 var hdh
= aspect
.after(handle
, destroyMethodName
, function(){
14454 return arguments
; // handle
14461 'dijit/layout/_ContentPaneResizeMixin':function(){
14462 define("dijit/layout/_ContentPaneResizeMixin", [
14463 "dojo/_base/array", // array.filter array.forEach
14464 "dojo/_base/declare", // declare
14465 "dojo/dom-class", // domClass.contains domClass.toggle
14466 "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
14468 "dojo/_base/lang", // lang.mixin
14469 "dojo/query", // query
14470 "dojo/sniff", // has("ie")
14471 "../registry", // registry.byId
14473 "./utils" // marginBox2contextBox
14474 ], function(array
, declare
, domClass
, domGeometry
, domStyle
, lang
, query
, has
,
14475 registry
, Viewport
, layoutUtils
){
14478 // dijit/layout/_ContentPaneResizeMixin
14481 return declare("dijit.layout._ContentPaneResizeMixin", null, {
14483 // Resize() functionality of ContentPane. If there's a single layout widget
14484 // child then it will call resize() with the same dimensions as the ContentPane.
14485 // Otherwise just calls resize on each child.
14487 // Also implements basic startup() functionality, where starting the parent
14488 // will start the children
14490 // doLayout: Boolean
14491 // - false - don't adjust size of children
14492 // - true - if there is a single visible child widget, set it's size to however big the ContentPane is
14495 // isLayoutContainer: [protected] Boolean
14496 // Indicates that this widget will call resize() on it's child widgets
14497 // when they become visible.
14498 isLayoutContainer
: true,
14500 startup: function(){
14502 // See `dijit/layout/_LayoutWidget.startup()` for description.
14503 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14506 if(this._started
){ return; }
14508 var parent
= this.getParent();
14509 this._childOfLayoutWidget
= parent
&& parent
.isLayoutContainer
;
14511 // I need to call resize() on my child/children (when I become visible), unless
14512 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
14513 this._needLayout
= !this._childOfLayoutWidget
;
14515 this.inherited(arguments
);
14517 if(this._isShown()){
14521 if(!this._childOfLayoutWidget
){
14522 // Since my parent isn't a layout container, and my style *may be* width=height=100%
14523 // or something similar (either set directly or via a CSS class),
14524 // monitor when viewport size changes so that I can re-layout.
14525 // This is more for subclasses of ContentPane than ContentPane itself, although it
14526 // could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size.
14527 this.own(Viewport
.on("resize", lang
.hitch(this, "resize")));
14531 _checkIfSingleChild: function(){
14533 // Test if we have exactly one visible widget as a child,
14534 // and if so assume that we are a container for that widget,
14535 // and should propagate startup() and resize() calls to it.
14536 // Skips over things like data stores since they aren't visible.
14538 var candidateWidgets
= [],
14539 otherVisibleNodes
= false;
14541 query("> *", this.containerNode
).some(function(node
){
14542 var widget
= registry
.byNode(node
);
14543 if(widget
&& widget
.resize
){
14544 candidateWidgets
.push(widget
);
14545 }else if(node
.offsetHeight
){
14546 otherVisibleNodes
= true;
14550 this._singleChild
= candidateWidgets
.length
== 1 && !otherVisibleNodes
?
14551 candidateWidgets
[0] : null;
14553 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
14554 domClass
.toggle(this.containerNode
, this.baseClass
+ "SingleChild", !!this._singleChild
);
14557 resize: function(changeSize
, resultSize
){
14559 // See `dijit/layout/_LayoutWidget.resize()` for description.
14560 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14563 this._resizeCalled
= true;
14565 this._scheduleLayout(changeSize
, resultSize
);
14568 _scheduleLayout: function(changeSize
, resultSize
){
14570 // Resize myself, and call resize() on each of my child layout widgets, either now
14571 // (if I'm currently visible) or when I become visible
14572 if(this._isShown()){
14573 this._layout(changeSize
, resultSize
);
14575 this._needLayout
= true;
14576 this._changeSize
= changeSize
;
14577 this._resultSize
= resultSize
;
14581 _layout: function(changeSize
, resultSize
){
14583 // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
14584 // Also, since I am an isLayoutContainer widget, each of my children expects me to
14585 // call resize() or layout() on it.
14587 // Should be called on initialization and also whenever we get new content
14588 // (from an href, or from set('content', ...))... but deferred until
14589 // the ContentPane is visible
14591 delete this._needLayout
;
14593 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
14594 // never called directly, so resize() is our trigger to do the initial href download (see [20099]).
14595 // However, don't load href for closed TitlePanes.
14596 if(!this._wasShown
&& this.open
!== false){
14600 // Set margin box size, unless it wasn't specified, in which case use current size.
14602 domGeometry
.setMarginBox(this.domNode
, changeSize
);
14605 // Compute content box size of containerNode in case we [later] need to size our single child.
14606 var cn
= this.containerNode
;
14607 if(cn
=== this.domNode
){
14608 // If changeSize or resultSize was passed to this method and this.containerNode ==
14609 // this.domNode then we can compute the content-box size without querying the node,
14610 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
14611 var mb
= resultSize
|| {};
14612 lang
.mixin(mb
, changeSize
|| {}); // changeSize overrides resultSize
14613 if(!("h" in mb
) || !("w" in mb
)){
14614 mb
= lang
.mixin(domGeometry
.getMarginBox(cn
), mb
); // just use domGeometry.setMarginBox() to fill in missing values
14616 this._contentBox
= layoutUtils
.marginBox2contentBox(cn
, mb
);
14618 this._contentBox
= domGeometry
.getContentBox(cn
);
14621 this._layoutChildren();
14624 _layoutChildren: function(){
14625 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
14626 // of the ContentPane (rather than changing it through the set("content", ...) API.
14628 this._checkIfSingleChild();
14631 if(this._singleChild
&& this._singleChild
.resize
){
14632 var cb
= this._contentBox
|| domGeometry
.getContentBox(this.containerNode
);
14634 // note: if widget has padding this._contentBox will have l and t set,
14635 // but don't pass them to resize() or it will doubly-offset the child
14636 this._singleChild
.resize({w
: cb
.w
, h
: cb
.h
});
14638 // All my child widgets are independently sized (rather than matching my size),
14639 // but I still need to call resize() on each child to make it layout.
14640 array
.forEach(this.getChildren(), function(widget
){
14648 _isShown: function(){
14650 // Returns true if the content is currently shown.
14652 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
14653 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
14654 // tree every call, and at least solves the performance problem on page load by deferring loading
14655 // hidden ContentPanes until they are first shown
14657 if(this._childOfLayoutWidget
){
14658 // If we are TitlePane, etc - we return that only *IF* we've been resized
14659 if(this._resizeCalled
&& "open" in this){
14662 return this._resizeCalled
;
14663 }else if("open" in this){
14664 return this.open
; // for TitlePane, etc.
14666 var node
= this.domNode
, parent
= this.domNode
.parentNode
;
14667 return (node
.style
.display
!= 'none') && (node
.style
.visibility
!= 'hidden') && !domClass
.contains(node
, "dijitHidden") &&
14668 parent
&& parent
.style
&& (parent
.style
.display
!= 'none');
14672 _onShow: function(){
14674 // Called when the ContentPane is made visible
14676 // For a plain ContentPane, this is called on initialization, from startup().
14677 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
14678 // called whenever the pane is made visible.
14680 // Does layout/resize of child widget(s)
14682 // Need to keep track of whether ContentPane has been shown (which is different than
14683 // whether or not it's currently visible).
14684 this._wasShown
= true;
14686 if(this._needLayout
){
14687 // If a layout has been scheduled for when we become visible, do it now
14688 this._layout(this._changeSize
, this._resultSize
);
14691 this.inherited(arguments
);
14698 'dijit/WidgetSet':function(){
14699 define("dijit/WidgetSet", [
14700 "dojo/_base/array", // array.forEach array.map
14701 "dojo/_base/declare", // declare
14702 "dojo/_base/kernel", // kernel.global
14703 "./registry" // to add functions to dijit.registry
14704 ], function(array
, declare
, kernel
, registry
){
14709 var WidgetSet
= declare("dijit.WidgetSet", null, {
14711 // A set of widgets indexed by id.
14712 // Deprecated, will be removed in 2.0.
14715 // Create a small list of widgets:
14716 // | require(["dijit/WidgetSet", "dijit/registry"],
14717 // | function(WidgetSet, registry){
14718 // | var ws = new WidgetSet();
14719 // | ws.add(registry.byId("one"));
14720 // | ws.add(registry.byId("two"));
14721 // | // destroy both:
14722 // | ws.forEach(function(w){ w.destroy(); });
14725 constructor: function(){
14730 add: function(/*dijit/_WidgetBase*/ widget){
14732 // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
14734 // widget: dijit/_WidgetBase
14735 // Any dijit/_WidgetBase subclass.
14736 if(this._hash[widget.id]){
14737 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
14739 this._hash[widget.id] = widget;
14743 remove: function(/*String*/ id
){
14745 // Remove a widget from this WidgetSet. Does not destroy the widget; simply
14746 // removes the reference.
14747 if(this._hash
[id
]){
14748 delete this._hash
[id
];
14753 forEach: function(/*Function*/ func
, /* Object? */thisObj
){
14755 // Call specified function for each widget in this set.
14758 // A callback function to run for each item. Is passed the widget, the index
14759 // in the iteration, and the full hash, similar to `array.forEach`.
14762 // An optional scope parameter
14765 // Using the default `dijit.registry` instance:
14766 // | require(["dijit/WidgetSet", "dijit/registry"],
14767 // | function(WidgetSet, registry){
14768 // | registry.forEach(function(widget){
14769 // | console.log(widget.declaredClass);
14774 // Returns self, in order to allow for further chaining.
14776 thisObj
= thisObj
|| kernel
.global
;
14778 for(id
in this._hash
){
14779 func
.call(thisObj
, this._hash
[id
], i
++, this._hash
);
14781 return this; // dijit/WidgetSet
14784 filter: function(/*Function*/ filter
, /* Object? */thisObj
){
14786 // Filter down this WidgetSet to a smaller new WidgetSet
14787 // Works the same as `array.filter` and `NodeList.filter`
14790 // Callback function to test truthiness. Is passed the widget
14791 // reference and the pseudo-index in the object.
14793 // thisObj: Object?
14794 // Option scope to use for the filter function.
14797 // Arbitrary: select the odd widgets in this list
14801 // | require(["dijit/WidgetSet", "dijit/registry"],
14802 // | function(WidgetSet, registry){
14803 // | registry.filter(function(w, i){
14804 // | return i % 2 == 0;
14805 // | }).forEach(function(w){ /* odd ones */ });
14808 thisObj
= thisObj
|| kernel
.global
;
14809 var res
= new WidgetSet(), i
= 0, id
;
14810 for(id
in this._hash
){
14811 var w
= this._hash
[id
];
14812 if(filter
.call(thisObj
, w
, i
++, this._hash
)){
14816 return res
; // dijit/WidgetSet
14819 byId: function(/*String*/ id
){
14821 // Find a widget in this list by it's id.
14823 // Test if an id is in a particular WidgetSet
14824 // | require(["dijit/WidgetSet", "dijit/registry"],
14825 // | function(WidgetSet, registry){
14826 // | var ws = new WidgetSet();
14827 // | ws.add(registry.byId("bar"));
14828 // | var t = ws.byId("bar") // returns a widget
14829 // | var x = ws.byId("foo"); // returns undefined
14832 return this._hash
[id
]; // dijit/_WidgetBase
14835 byClass: function(/*String*/ cls
){
14837 // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
14840 // The Class to scan for. Full dot-notated string.
14843 // Find all `dijit.TitlePane`s in a page:
14844 // | require(["dijit/WidgetSet", "dijit/registry"],
14845 // | function(WidgetSet, registry){
14846 // | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
14849 var res
= new WidgetSet(), id
, widget
;
14850 for(id
in this._hash
){
14851 widget
= this._hash
[id
];
14852 if(widget
.declaredClass
== cls
){
14856 return res
; // dijit/WidgetSet
14859 toArray: function(){
14861 // Convert this WidgetSet into a true Array
14864 // Work with the widget .domNodes in a real Array
14865 // | require(["dijit/WidgetSet", "dijit/registry"],
14866 // | function(WidgetSet, registry){
14867 // | array.map(registry.toArray(), function(w){ return w.domNode; });
14872 for(var id
in this._hash
){
14873 ar
.push(this._hash
[id
]);
14875 return ar
; // dijit/_WidgetBase[]
14878 map: function(/* Function */func
, /* Object? */thisObj
){
14880 // Create a new Array from this WidgetSet, following the same rules as `array.map`
14882 // | require(["dijit/WidgetSet", "dijit/registry"],
14883 // | function(WidgetSet, registry){
14884 // | var nodes = registry.map(function(w){ return w.domNode; });
14888 // A new array of the returned values.
14889 return array
.map(this.toArray(), func
, thisObj
); // Array
14892 every: function(func
, thisObj
){
14894 // A synthetic clone of `array.every` acting explicitly on this WidgetSet
14897 // A callback function run for every widget in this list. Exits loop
14898 // when the first false return is encountered.
14900 // thisObj: Object?
14901 // Optional scope parameter to use for the callback
14903 thisObj
= thisObj
|| kernel
.global
;
14905 for(i
in this._hash
){
14906 if(!func
.call(thisObj
, this._hash
[i
], x
++, this._hash
)){
14907 return false; // Boolean
14910 return true; // Boolean
14913 some: function(func
, thisObj
){
14915 // A synthetic clone of `array.some` acting explicitly on this WidgetSet
14918 // A callback function run for every widget in this list. Exits loop
14919 // when the first true return is encountered.
14921 // thisObj: Object?
14922 // Optional scope parameter to use for the callback
14924 thisObj
= thisObj
|| kernel
.global
;
14926 for(i
in this._hash
){
14927 if(func
.call(thisObj
, this._hash
[i
], x
++, this._hash
)){
14928 return true; // Boolean
14931 return false; // Boolean
14936 // Add in 1.x compatibility methods to dijit/registry.
14937 // These functions won't show up in the API doc but since they are deprecated anyway,
14938 // that's probably for the best.
14939 array
.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func
){
14940 registry
[func
] = WidgetSet
.prototype[func
];
14948 'dojo/dnd/Moveable':function(){
14949 define("dojo/dnd/Moveable", [
14950 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang",
14951 "../dom", "../dom-class", "../Evented", "../on", "../topic", "../touch", "./common", "./Mover", "../_base/window"
14952 ], function(array
, declare
, event
, lang
, dom
, domClass
, Evented
, on
, topic
, touch
, dnd
, Mover
, win
){
14955 // dojo/dnd/Moveable
14958 var Moveable
= declare("dojo.dnd.Moveable", [Evented
], {
14960 // an object, which makes a node movable
14962 // object attributes (for markup)
14967 constructor: function(node
, params
){
14969 // a node (or node's id) to be moved
14970 // params: Moveable.__MoveableArgs?
14971 // optional parameters
14972 this.node
= dom
.byId(node
);
14973 if(!params
){ params
= {}; }
14974 this.handle
= params
.handle
? dom
.byId(params
.handle
) : null;
14975 if(!this.handle
){ this.handle
= this.node
; }
14976 this.delay
= params
.delay
> 0 ? params
.delay
: 0;
14977 this.skip
= params
.skip
;
14978 this.mover
= params
.mover
? params
.mover
: Mover
;
14980 on(this.handle
, touch
.press
, lang
.hitch(this, "onMouseDown")),
14981 // cancel text selection and text dragging
14982 on(this.handle
, "dragstart", lang
.hitch(this, "onSelectStart")),
14983 on(this.handle
, "selectstart", lang
.hitch(this, "onSelectStart"))
14988 markupFactory: function(params
, node
, Ctor
){
14989 return new Ctor(node
, params
);
14993 destroy: function(){
14995 // stops watching for possible move, deletes all references, so the object can be garbage-collected
14996 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
14997 this.events
= this.node
= this.handle
= null;
15000 // mouse event processors
15001 onMouseDown: function(e
){
15003 // event processor for onmousedown/ontouchstart, creates a Mover for the node
15005 // mouse/touch event
15006 if(this.skip
&& dnd
.isFormElement(e
)){ return; }
15009 on(this.handle
, touch
.move, lang
.hitch(this, "onMouseMove")),
15010 on(this.handle
, touch
.release
, lang
.hitch(this, "onMouseUp"))
15012 this._lastX
= e
.pageX
;
15013 this._lastY
= e
.pageY
;
15015 this.onDragDetected(e
);
15019 onMouseMove: function(e
){
15021 // event processor for onmousemove/ontouchmove, used only for delayed drags
15023 // mouse/touch event
15024 if(Math
.abs(e
.pageX
- this._lastX
) > this.delay
|| Math
.abs(e
.pageY
- this._lastY
) > this.delay
){
15026 this.onDragDetected(e
);
15030 onMouseUp: function(e
){
15032 // event processor for onmouseup, used only for delayed drags
15035 for(var i
= 0; i
< 2; ++i
){
15036 this.events
.pop().remove();
15040 onSelectStart: function(e
){
15042 // event processor for onselectevent and ondragevent
15045 if(!this.skip
|| !dnd
.isFormElement(e
)){
15051 onDragDetected: function(/*Event*/ e
){
15053 // called when the drag is detected;
15054 // responsible for creation of the mover
15055 new this.mover(this.node
, e
, this);
15057 onMoveStart: function(/*Mover*/ mover
){
15059 // called before every move operation
15060 topic
.publish("/dnd/move/start", mover
);
15061 domClass
.add(win
.body(), "dojoMove");
15062 domClass
.add(this.node
, "dojoMoveItem");
15064 onMoveStop: function(/*Mover*/ mover
){
15066 // called after every move operation
15067 topic
.publish("/dnd/move/stop", mover
);
15068 domClass
.remove(win
.body(), "dojoMove");
15069 domClass
.remove(this.node
, "dojoMoveItem");
15071 onFirstMove: function(/*===== mover, e =====*/){
15073 // called during the very first move notification;
15074 // can be used to initialize coordinates, can be overwritten.
15078 // default implementation does nothing
15080 onMove: function(mover
, leftTop
/*=====, e =====*/){
15082 // called during every move notification;
15083 // should actually move the node; can be overwritten.
15087 this.onMoving(mover
, leftTop
);
15088 var s
= mover
.node
.style
;
15089 s
.left
= leftTop
.l
+ "px";
15090 s
.top
= leftTop
.t
+ "px";
15091 this.onMoved(mover
, leftTop
);
15093 onMoving: function(/*===== mover, leftTop =====*/){
15095 // called before every incremental move; can be overwritten.
15099 // default implementation does nothing
15101 onMoved: function(/*===== mover, leftTop =====*/){
15103 // called after every incremental move; can be overwritten.
15107 // default implementation does nothing
15112 Moveable.__MoveableArgs = declare([], {
15113 // handle: Node||String
15114 // A node (or node's id), which is used as a mouse handle.
15115 // If omitted, the node itself is used as a handle.
15119 // delay move by this number of pixels
15123 // skip move of form elements
15127 // a constructor of custom Mover
15136 'dijit/TooltipDialog':function(){
15138 'url:dijit/templates/TooltipDialog.html':"<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" data-dojo-attach-point=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n"}});
15139 define("dijit/TooltipDialog", [
15140 "dojo/_base/declare", // declare
15141 "dojo/dom-class", // domClass.replace
15142 "dojo/_base/event", // event.stop
15143 "dojo/keys", // keys
15144 "dojo/_base/lang", // lang.hitch
15146 "./layout/ContentPane",
15148 "./form/_FormMixin",
15149 "./_TemplatedMixin",
15150 "dojo/text!./templates/TooltipDialog.html",
15151 "./main" // exports methods to dijit global
15152 ], function(declare
, domClass
, event
, keys
, lang
,
15153 focus
, ContentPane
, _DialogMixin
, _FormMixin
, _TemplatedMixin
, template
, dijit
){
15156 // dijit/TooltipDialog
15159 return declare("dijit.TooltipDialog",
15160 [ContentPane
, _TemplatedMixin
, _FormMixin
, _DialogMixin
], {
15162 // Pops up a dialog that appears like a Tooltip
15165 // Description of tooltip dialog (required for a11y)
15168 // doLayout: [protected] Boolean
15169 // Don't change this parameter from the default value.
15170 // This ContentPane parameter doesn't make sense for TooltipDialog, since TooltipDialog
15171 // is never a child of a layout container, nor can you specify the size of
15172 // TooltipDialog in order to control the size of an inner widget.
15175 // autofocus: Boolean
15176 // A Toggle to modify the default focus behavior of a Dialog, which
15177 // is to focus on the first dialog element after opening the dialog.
15178 // False will disable autofocusing. Default: true.
15181 // baseClass: [protected] String
15182 // The root className to use for the various states of this widget
15183 baseClass
: "dijitTooltipDialog",
15185 // _firstFocusItem: [private readonly] DomNode
15186 // The pointer to the first focusable node in the dialog.
15187 // Set by `dijit/_DialogMixin._getFocusItems()`.
15188 _firstFocusItem
: null,
15190 // _lastFocusItem: [private readonly] DomNode
15191 // The pointer to which node has focus prior to our dialog.
15192 // Set by `dijit/_DialogMixin._getFocusItems()`.
15193 _lastFocusItem
: null,
15195 templateString
: template
,
15197 _setTitleAttr: function(/*String*/ title
){
15198 this.containerNode
.title
= title
;
15199 this._set("title", title
);
15202 postCreate: function(){
15203 this.inherited(arguments
);
15204 this.connect(this.containerNode
, "onkeypress", "_onKey");
15207 orient: function(/*DomNode*/ node
, /*String*/ aroundCorner
, /*String*/ tooltipCorner
){
15209 // Configure widget to be displayed in given position relative to the button.
15210 // This is called from the dijit.popup code, and should not be called
15215 // Note: intentionally not using dijitTooltip class since that sets position:absolute, which
15216 // confuses dijit/popup trying to get the size of the tooltip.
15218 "MR-ML": "dijitTooltipRight",
15219 "ML-MR": "dijitTooltipLeft",
15220 "TM-BM": "dijitTooltipAbove",
15221 "BM-TM": "dijitTooltipBelow",
15222 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
15223 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
15224 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
15225 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
15226 "BR-BL": "dijitTooltipRight",
15227 "BL-BR": "dijitTooltipLeft"
15228 }[aroundCorner
+ "-" + tooltipCorner
];
15230 domClass
.replace(this.domNode
, newC
, this._currentOrientClass
|| "");
15231 this._currentOrientClass
= newC
;
15233 // Tooltip.orient() has code to reposition connector for when Tooltip is before/after anchor.
15234 // Not putting here to avoid code bloat, and since TooltipDialogs are generally above/below.
15235 // Should combine code from Tooltip and TooltipDialog.
15240 // Focus on first field
15241 this._getFocusItems(this.containerNode
);
15242 focus
.focus(this._firstFocusItem
);
15245 onOpen: function(/*Object*/ pos
){
15247 // Called when dialog is displayed.
15248 // This is called from the dijit.popup code, and should not be called directly.
15252 this.orient(this.domNode
,pos
.aroundCorner
, pos
.corner
);
15254 // Position the tooltip connector for middle alignment.
15255 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
15256 var aroundNodeCoords
= pos
.aroundNodePos
;
15257 if(pos
.corner
.charAt(0) == 'M' && pos
.aroundCorner
.charAt(0) == 'M'){
15258 this.connectorNode
.style
.top
= aroundNodeCoords
.y
+ ((aroundNodeCoords
.h
- this.connectorNode
.offsetHeight
) >> 1) - pos
.y
+ "px";
15259 this.connectorNode
.style
.left
= "";
15260 }else if(pos
.corner
.charAt(1) == 'M' && pos
.aroundCorner
.charAt(1) == 'M'){
15261 this.connectorNode
.style
.left
= aroundNodeCoords
.x
+ ((aroundNodeCoords
.w
- this.connectorNode
.offsetWidth
) >> 1) - pos
.x
+ "px";
15264 this._onShow(); // lazy load trigger (TODO: shouldn't we load before positioning?)
15267 onClose: function(){
15269 // Called when dialog is hidden.
15270 // This is called from the dijit.popup code, and should not be called directly.
15276 _onKey: function(/*Event*/ evt
){
15278 // Handler for keyboard events
15280 // Keep keyboard focus in dialog; close dialog on escape key
15284 var node
= evt
.target
;
15285 if(evt
.charOrCode
=== keys
.TAB
){
15286 this._getFocusItems(this.containerNode
);
15288 var singleFocusItem
= (this._firstFocusItem
== this._lastFocusItem
);
15289 if(evt
.charOrCode
== keys
.ESCAPE
){
15290 // Use defer to avoid crash on IE, see #10396.
15291 this.defer("onCancel");
15293 }else if(node
== this._firstFocusItem
&& evt
.shiftKey
&& evt
.charOrCode
=== keys
.TAB
){
15294 if(!singleFocusItem
){
15295 focus
.focus(this._lastFocusItem
); // send focus to last item in dialog
15298 }else if(node
== this._lastFocusItem
&& evt
.charOrCode
=== keys
.TAB
&& !evt
.shiftKey
){
15299 if(!singleFocusItem
){
15300 focus
.focus(this._firstFocusItem
); // send focus to first item in dialog
15303 }else if(evt
.charOrCode
=== keys
.TAB
){
15304 // we want the browser's default tab handling to move focus
15305 // but we don't want the tab to propagate upwards
15306 evt
.stopPropagation();
15313 'dojo/store/util/SimpleQueryEngine':function(){
15314 define("dojo/store/util/SimpleQueryEngine", ["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil
/*=====, Store =====*/){
15317 // dojo/store/util/SimpleQueryEngine
15319 return function(query
, options
){
15321 // Simple query engine that matches using filter functions, named filter
15322 // functions or objects by name-value on a query object hash
15325 // The SimpleQueryEngine provides a way of getting a QueryResults through
15326 // the use of a simple object hash as a filter. The hash will be used to
15327 // match properties on data objects with the corresponding value given. In
15328 // other words, only exact matches will be returned.
15330 // This function can be used as a template for more complex query engines;
15331 // for example, an engine can be created that accepts an object hash that
15332 // contains filtering functions, or a string that gets evaluated, etc.
15334 // When creating a new dojo.store, simply set the store's queryEngine
15335 // field as a reference to this function.
15338 // An object hash with fields that may match fields of items in the store.
15339 // Values in the hash will be compared by normal == operator, but regular expressions
15340 // or any object that provides a test() method are also supported and can be
15341 // used to match strings by more complex expressions
15342 // (and then the regex's or object's test() method will be used to match values).
15344 // options: dojo/store/api/Store.QueryOptions?
15345 // An object that contains optional information such as sort, start, and count.
15347 // returns: Function
15348 // A function that caches the passed query under the field "matches". See any
15349 // of the "query" methods on dojo.stores.
15352 // Define a store with a reference to this engine, and set up a query method.
15354 // | var myStore = function(options){
15355 // | // ...more properties here
15356 // | this.queryEngine = SimpleQueryEngine;
15357 // | // define our query method
15358 // | this.query = function(query, options){
15359 // | return QueryResults(this.queryEngine(query, options)(this.data));
15363 // create our matching query function
15364 switch(typeof query
){
15366 throw new Error("Can not query with a " + typeof query
);
15367 case "object": case "undefined":
15368 var queryObject
= query
;
15369 query = function(object
){
15370 for(var key
in queryObject
){
15371 var required
= queryObject
[key
];
15372 if(required
&& required
.test
){
15373 // an object can provide a test method, which makes it work with regex
15374 if(!required
.test(object
[key
], object
)){
15377 }else if(required
!= object
[key
]){
15387 throw new Error("No filter function " + query
+ " was found in store");
15389 query
= this[query
];
15394 function execute(array
){
15395 // execute the whole query, first we filter
15396 var results
= arrayUtil
.filter(array
, query
);
15398 var sortSet
= options
&& options
.sort
;
15400 results
.sort(typeof sortSet
== "function" ? sortSet : function(a
, b
){
15401 for(var sort
, i
=0; sort
= sortSet
[i
]; i
++){
15402 var aValue
= a
[sort
.attribute
];
15403 var bValue
= b
[sort
.attribute
];
15404 if (aValue
!= bValue
){
15405 return !!sort
.descending
== (aValue
== null || aValue
> bValue
) ? -1 : 1;
15412 if(options
&& (options
.start
|| options
.count
)){
15413 var total
= results
.length
;
15414 results
= results
.slice(options
.start
|| 0, (options
.start
|| 0) + (options
.count
|| Infinity
));
15415 results
.total
= total
;
15419 execute
.matches
= query
;
15426 'dijit/typematic':function(){
15427 define("dijit/typematic", [
15428 "dojo/_base/array", // array.forEach
15429 "dojo/_base/connect", // connect.connect
15430 "dojo/_base/event", // event.stop
15431 "dojo/_base/kernel", // kernel.deprecated
15432 "dojo/_base/lang", // lang.mixin, lang.hitch
15434 "dojo/sniff", // has("ie")
15435 "./main" // setting dijit.typematic global
15436 ], function(array
, connect
, event
, kernel
, lang
, on
, has
, dijit
){
15441 var typematic
= (dijit
.typematic
= {
15443 // These functions are used to repetitively call a user specified callback
15444 // method when a specific key or mouse click over a specific DOM node is
15445 // held down for a specific amount of time.
15446 // Only 1 such event is allowed to occur on the browser page at 1 time.
15448 _fireEventAndReload: function(){
15449 this._timer
= null;
15450 this._callback(++this._count
, this._node
, this._evt
);
15452 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
15453 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
15454 this._currentTimeout
= Math
.max(
15455 this._currentTimeout
< 0 ? this._initialDelay
:
15456 (this._subsequentDelay
> 1 ? this._subsequentDelay
: Math
.round(this._currentTimeout
* this._subsequentDelay
)),
15458 this._timer
= setTimeout(lang
.hitch(this, "_fireEventAndReload"), this._currentTimeout
);
15461 trigger: function(/*Event*/ evt
, /*Object*/ _this
, /*DOMNode*/ node
, /*Function*/ callback
, /*Object*/ obj
, /*Number?*/ subsequentDelay
, /*Number?*/ initialDelay
, /*Number?*/ minDelay
){
15463 // Start a timed, repeating callback sequence.
15464 // If already started, the function call is ignored.
15465 // This method is not normally called by the user but can be
15466 // when the normal listener code is insufficient.
15468 // key or mouse event object to pass to the user callback
15470 // pointer to the user's widget space.
15472 // the DOM node object to pass the the callback function
15474 // function to call until the sequence is stopped called with 3 parameters:
15476 // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
15478 // the DOM node object passed in
15480 // key or mouse event object
15482 // user space object used to uniquely identify each typematic sequence
15483 // subsequentDelay:
15484 // if > 1, the number of milliseconds until the 3->n events occur
15485 // or else the fractional time multiplier for the next event's delay, default=0.9
15487 // the number of milliseconds until the 2nd event occurs, default=500ms
15489 // the maximum delay in milliseconds for event to fire, default=10ms
15490 if(obj
!= this._obj
){
15492 this._initialDelay
= initialDelay
|| 500;
15493 this._subsequentDelay
= subsequentDelay
|| 0.90;
15494 this._minDelay
= minDelay
|| 10;
15497 this._currentTimeout
= -1;
15499 this._callback
= lang
.hitch(_this
, callback
);
15500 this._evt
= { faux
: true };
15501 for(var attr
in evt
){
15502 if(attr
!= "layerX" && attr
!= "layerY"){ // prevent WebKit warnings
15504 if(typeof v
!= "function" && typeof v
!= "undefined"){ this._evt
[attr
] = v
}
15507 this._fireEventAndReload();
15513 // Stop an ongoing timed, repeating callback sequence.
15515 clearTimeout(this._timer
);
15516 this._timer
= null;
15519 this._callback(-1, this._node
, this._evt
);
15524 addKeyListener: function(/*DOMNode*/ node
, /*Object*/ keyObject
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15526 // Start listening for a specific typematic key.
15527 // See also the trigger method for other parameters.
15529 // an object defining the key to listen for:
15531 // - charOrCode: the printable character (string) or keyCode (number) to listen for.
15532 // - keyCode: (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
15533 // - charCode: (deprecated - use charOrCode) the charCode (number) to listen for.
15534 // - ctrlKey: desired ctrl key state to initiate the callback sequence:
15535 // - pressed (true)
15536 // - released (false)
15537 // - either (unspecified)
15538 // - altKey: same as ctrlKey but for the alt key
15539 // - shiftKey: same as ctrlKey but for the shift key
15541 // a connection handle
15543 if(keyObject
.keyCode
){
15544 keyObject
.charOrCode
= keyObject
.keyCode
;
15545 kernel
.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15546 }else if(keyObject
.charCode
){
15547 keyObject
.charOrCode
= String
.fromCharCode(keyObject
.charCode
);
15548 kernel
.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15551 on(node
, connect
._keypress
, lang
.hitch(this, function(evt
){
15552 if(evt
.charOrCode
== keyObject
.charOrCode
&&
15553 (keyObject
.ctrlKey
=== undefined || keyObject
.ctrlKey
== evt
.ctrlKey
) &&
15554 (keyObject
.altKey
=== undefined || keyObject
.altKey
== evt
.altKey
) &&
15555 (keyObject
.metaKey
=== undefined || keyObject
.metaKey
== (evt
.metaKey
|| false)) && // IE doesn't even set metaKey
15556 (keyObject
.shiftKey
=== undefined || keyObject
.shiftKey
== evt
.shiftKey
)){
15558 typematic
.trigger(evt
, _this
, node
, callback
, keyObject
, subsequentDelay
, initialDelay
, minDelay
);
15559 }else if(typematic
._obj
== keyObject
){
15563 on(node
, "keyup", lang
.hitch(this, function(){
15564 if(typematic
._obj
== keyObject
){
15569 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15572 addMouseListener: function(/*DOMNode*/ node
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15574 // Start listening for a typematic mouse click.
15575 // See the trigger method for other parameters.
15577 // a connection handle
15579 on(node
, "mousedown", lang
.hitch(this, function(evt
){
15580 evt
.preventDefault();
15581 typematic
.trigger(evt
, _this
, node
, callback
, node
, subsequentDelay
, initialDelay
, minDelay
);
15583 on(node
, "mouseup", lang
.hitch(this, function(evt
){
15585 evt
.preventDefault();
15589 on(node
, "mouseout", lang
.hitch(this, function(evt
){
15591 evt
.preventDefault();
15595 on(node
, "dblclick", lang
.hitch(this, function(evt
){
15596 evt
.preventDefault();
15598 typematic
.trigger(evt
, _this
, node
, callback
, node
, subsequentDelay
, initialDelay
, minDelay
);
15599 setTimeout(lang
.hitch(this, typematic
.stop
), 50);
15603 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15606 addListener: function(/*Node*/ mouseNode
, /*Node*/ keyNode
, /*Object*/ keyObject
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15608 // Start listening for a specific typematic key and mouseclick.
15609 // This is a thin wrapper to addKeyListener and addMouseListener.
15610 // See the addMouseListener and addKeyListener methods for other parameters.
15612 // the DOM node object to listen on for mouse events.
15614 // the DOM node object to listen on for key events.
15616 // a connection handle
15618 this.addKeyListener(keyNode
, keyObject
, _this
, callback
, subsequentDelay
, initialDelay
, minDelay
),
15619 this.addMouseListener(mouseNode
, _this
, callback
, subsequentDelay
, initialDelay
, minDelay
)
15621 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15630 'dijit/MenuItem':function(){
15632 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"}});
15633 define("dijit/MenuItem", [
15634 "dojo/_base/declare", // declare
15635 "dojo/dom", // dom.setSelectable
15636 "dojo/dom-attr", // domAttr.set
15637 "dojo/dom-class", // domClass.toggle
15638 "dojo/_base/kernel", // kernel.deprecated
15639 "dojo/sniff", // has("ie")
15641 "./_TemplatedMixin",
15643 "./_CssStateMixin",
15644 "dojo/text!./templates/MenuItem.html"
15645 ], function(declare
, dom
, domAttr
, domClass
, kernel
, has
,
15646 _Widget
, _TemplatedMixin
, _Contained
, _CssStateMixin
, template
){
15651 return declare("dijit.MenuItem",
15652 [_Widget
, _TemplatedMixin
, _Contained
, _CssStateMixin
],
15655 // A line item in a Menu Widget
15658 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15659 templateString
: template
,
15661 baseClass
: "dijitMenuItem",
15666 _setLabelAttr: function(val
){
15667 this.containerNode
.innerHTML
= val
;
15668 this._set("label", val
);
15669 if(this.textDir
=== "auto"){
15670 this.applyTextDir(this.focusNode
, this.label
);
15674 // iconClass: String
15675 // Class to apply to DOMNode to make it display an icon.
15676 iconClass
: "dijitNoIcon",
15677 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
15679 // accelKey: String
15680 // Text for the accelerator (shortcut) key combination.
15681 // Note that although Menu can display accelerator keys there
15682 // is no infrastructure to actually catch and execute these
15686 // disabled: Boolean
15687 // If true, the menu item is disabled.
15688 // If false, the menu item is enabled.
15691 _fillContent: function(/*DomNode*/ source
){
15692 // If button label is specified as srcNodeRef.innerHTML rather than
15693 // this.params.label, handle it here.
15694 if(source
&& !("label" in this.params
)){
15695 this.set('label', source
.innerHTML
);
15699 buildRendering: function(){
15700 this.inherited(arguments
);
15701 var label
= this.id
+"_text";
15702 domAttr
.set(this.containerNode
, "id", label
);
15703 if(this.accelKeyNode
){
15704 domAttr
.set(this.accelKeyNode
, "id", this.id
+ "_accel");
15705 label
+= " " + this.id
+ "_accel";
15707 this.domNode
.setAttribute("aria-labelledby", label
);
15708 dom
.setSelectable(this.domNode
, false);
15711 onClick: function(/*Event*/){
15713 // User defined function to handle clicks
15720 // Focus on this MenuItem
15722 if(has("ie") == 8){
15723 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15724 this.containerNode
.focus();
15726 this.focusNode
.focus();
15728 // this throws on IE (at least) in some scenarios
15732 _onFocus: function(){
15734 // This is called by the focus manager when focus
15735 // goes to this MenuItem or a child menu.
15738 this._setSelected(true);
15739 this.getParent()._onItemFocus(this);
15741 this.inherited(arguments
);
15744 _setSelected: function(selected
){
15746 // Indicate that this node is the currently selected one
15751 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15752 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15753 * That's not supposed to happen, but the problem is:
15754 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15755 * points to the parent Menu, bypassing the parent MenuItem... thus the
15756 * MenuItem is not in the chain of active widgets and gets a premature call to
15760 domClass
.toggle(this.domNode
, "dijitMenuItemSelected", selected
);
15763 setLabel: function(/*String*/ content
){
15765 // Deprecated. Use set('label', ...) instead.
15768 kernel
.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
15769 this.set("label", content
);
15772 setDisabled: function(/*Boolean*/ disabled
){
15774 // Deprecated. Use set('disabled', bool) instead.
15777 kernel
.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
15778 this.set('disabled', disabled
);
15780 _setDisabledAttr: function(/*Boolean*/ value
){
15782 // Hook for attr('disabled', ...) to work.
15783 // Enable or disable this menu item.
15785 this.focusNode
.setAttribute('aria-disabled', value
? 'true' : 'false');
15786 this._set("disabled", value
);
15788 _setAccelKeyAttr: function(/*String*/ value
){
15790 // Hook for attr('accelKey', ...) to work.
15791 // Set accelKey on this menu item.
15793 this.accelKeyNode
.style
.display
=value
?"":"none";
15794 this.accelKeyNode
.innerHTML
=value
;
15795 //have to use colSpan to make it work in IE
15796 domAttr
.set(this.containerNode
,'colSpan',value
?"1":"2");
15798 this._set("accelKey", value
);
15800 _setTextDirAttr: function(/*String*/ textDir
){
15802 // Setter for textDir.
15804 // Users shouldn't call this function; they should be calling
15805 // set('textDir', value)
15809 // only if new textDir is different from the old one
15810 // and on widgets creation.
15811 if(!this._created
|| this.textDir
!= textDir
){
15812 this._set("textDir", textDir
);
15813 this.applyTextDir(this.focusNode
, this.label
);
15820 'dijit/layout/TabController':function(){
15822 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n"}});
15823 define("dijit/layout/TabController", [
15824 "dojo/_base/declare", // declare
15825 "dojo/dom", // dom.setSelectable
15826 "dojo/dom-attr", // domAttr.attr
15827 "dojo/dom-class", // domClass.toggle
15828 "dojo/i18n", // i18n.getLocalization
15829 "dojo/_base/lang", // lang.hitch lang.trim
15830 "./StackController",
15834 "dojo/text!./templates/_TabButton.html",
15835 "dojo/i18n!../nls/common"
15836 ], function(declare
, dom
, domAttr
, domClass
, i18n
, lang
, StackController
, registry
, Menu
, MenuItem
, template
){
15839 // dijit/layout/TabController
15841 var TabButton
= declare("dijit.layout._TabButton", StackController
.StackButton
, {
15843 // A tab (the thing you click to select a pane).
15845 // Contains the title of the pane, and optionally a close-button to destroy the pane.
15846 // This is an internal widget and should not be instantiated directly.
15850 // baseClass: String
15851 // The CSS class applied to the domNode.
15852 baseClass
: "dijitTab",
15854 // Apply dijitTabCloseButtonHover when close button is hovered
15856 closeNode
: "dijitTabCloseButton"
15859 templateString
: template
,
15861 // Override _FormWidget.scrollOnFocus.
15862 // Don't scroll the whole tab container into view when the button is focused.
15863 scrollOnFocus
: false,
15865 buildRendering: function(){
15866 this.inherited(arguments
);
15868 dom
.setSelectable(this.containerNode
, false);
15871 startup: function(){
15872 this.inherited(arguments
);
15873 var n
= this.domNode
;
15875 // Required to give IE6 a kick, as it initially hides the
15876 // tabs until they are focused on.
15877 this.defer(function(){
15878 n
.className
= n
.className
;
15882 _setCloseButtonAttr: function(/*Boolean*/ disp
){
15884 // Hide/show close button
15885 this._set("closeButton", disp
);
15886 domClass
.toggle(this.domNode
, "dijitClosable", disp
);
15887 this.closeNode
.style
.display
= disp
? "" : "none";
15889 var _nlsResources
= i18n
.getLocalization("dijit", "common");
15890 if(this.closeNode
){
15891 domAttr
.set(this.closeNode
, "title", _nlsResources
.itemClose
);
15896 _setDisabledAttr: function(/*Boolean*/ disabled
){
15898 // Make tab selected/unselectable
15900 this.inherited(arguments
);
15902 // Don't show tooltip for close button when tab is disabled
15903 if(this.closeNode
){
15905 domAttr
.remove(this.closeNode
, "title");
15907 var _nlsResources
= i18n
.getLocalization("dijit", "common");
15908 domAttr
.set(this.closeNode
, "title", _nlsResources
.itemClose
);
15913 _setLabelAttr: function(/*String*/ content
){
15915 // Hook for set('label', ...) to work.
15917 // takes an HTML string.
15918 // Inherited ToggleButton implementation will Set the label (text) of the button;
15919 // Need to set the alt attribute of icon on tab buttons if no label displayed
15920 this.inherited(arguments
);
15921 if(!this.showLabel
&& !this.params
.title
){
15922 this.iconNode
.alt
= lang
.trim(this.containerNode
.innerText
|| this.containerNode
.textContent
|| '');
15927 var TabController
= declare("dijit.layout.TabController", StackController
, {
15929 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
15930 // Used internally by `dijit/layout/TabContainer`.
15932 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
15933 // TabController also monitors the TabContainer, and whenever a pane is
15934 // added or deleted updates itself accordingly.
15938 baseClass
: "dijitTabController",
15940 templateString
: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
15942 // tabPosition: String
15943 // Defines where tabs go relative to the content.
15944 // "top", "bottom", "left-h", "right-h"
15945 tabPosition
: "top",
15947 // buttonWidget: Constructor
15948 // The tab widget to create to correspond to each page
15949 buttonWidget
: TabButton
,
15951 // buttonWidgetCloseClass: String
15952 // Class of [x] close icon, used by event delegation code to tell when close button was clicked
15953 buttonWidgetCloseClass
: "dijitTabCloseButton",
15955 postCreate: function(){
15956 this.inherited(arguments
);
15958 // Setup a close menu to be shared between all the closable tabs (excluding disabled tabs)
15959 var closeMenu
= new Menu({
15960 id
: this.id
+"_Menu",
15961 ownerDocument
: this.ownerDocument
,
15964 textDir
: this.textDir
,
15965 targetNodeIds
: [this.domNode
],
15966 selector: function(node
){
15967 return domClass
.contains(node
, "dijitClosable") && !domClass
.contains(node
, "dijitTabDisabled");
15970 this.own(closeMenu
);
15972 var _nlsResources
= i18n
.getLocalization("dijit", "common"),
15974 closeMenu
.addChild(new MenuItem({
15975 label
: _nlsResources
.itemClose
,
15976 ownerDocument
: this.ownerDocument
,
15979 textDir
: this.textDir
,
15980 onClick: function(evt
){
15981 var button
= registry
.byNode(this.getParent().currentTarget
);
15982 controller
.onCloseButtonClick(button
.page
);
15988 TabController
.TabButton
= TabButton
; // for monkey patching
15990 return TabController
;
15994 'dijit/ToolbarSeparator':function(){
15995 define("dijit/ToolbarSeparator", [
15996 "dojo/_base/declare", // declare
15997 "dojo/dom", // dom.setSelectable
15999 "./_TemplatedMixin"
16000 ], function(declare
, dom
, _Widget
, _TemplatedMixin
){
16003 // dijit/ToolbarSeparator
16006 return declare("dijit.ToolbarSeparator", [_Widget
, _TemplatedMixin
], {
16008 // A spacer between two `dijit.Toolbar` items
16010 templateString
: '<div class="dijitToolbarSeparator dijitInline" role="presentation"></div>',
16012 buildRendering: function(){
16013 this.inherited(arguments
);
16014 dom
.setSelectable(this.domNode
, false);
16017 isFocusable: function(){
16019 // This widget isn't focusable, so pass along that fact.
16028 'dijit/layout/_LayoutWidget':function(){
16029 define("dijit/layout/_LayoutWidget", [
16030 "dojo/_base/lang", // lang.mixin
16035 "dojo/_base/declare", // declare
16036 "dojo/dom-class", // domClass.add domClass.remove
16037 "dojo/dom-geometry", // domGeometry.marginBox
16038 "dojo/dom-style" // domStyle.getComputedStyle
16039 ], function(lang
, _Widget
, _Container
, _Contained
, Viewport
,
16040 declare
, domClass
, domGeometry
, domStyle
){
16043 // dijit/layout/_LayoutWidget
16046 return declare("dijit.layout._LayoutWidget", [_Widget
, _Container
, _Contained
], {
16048 // Base class for a _Container widget which is responsible for laying out its children.
16049 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
16051 // baseClass: [protected extension] String
16052 // This class name is applied to the widget's domNode
16053 // and also may be used to generate names for sub nodes,
16054 // for example dijitTabContainer-content.
16055 baseClass
: "dijitLayoutContainer",
16057 // isLayoutContainer: [protected] Boolean
16058 // Indicates that this widget is going to call resize() on its
16059 // children widgets, setting their size, when they become visible.
16060 isLayoutContainer
: true,
16062 buildRendering: function(){
16063 this.inherited(arguments
);
16064 domClass
.add(this.domNode
, "dijitContainer");
16067 startup: function(){
16069 // Called after all the widgets have been instantiated and their
16070 // dom nodes have been inserted somewhere under win.doc.body.
16072 // Widgets should override this method to do any initialization
16073 // dependent on other widgets existing, and then call
16074 // this superclass method to finish things off.
16076 // startup() in subclasses shouldn't do anything
16077 // size related because the size of the widget hasn't been set yet.
16079 if(this._started
){ return; }
16081 // Need to call inherited first - so that child widgets get started
16083 this.inherited(arguments
);
16085 // If I am a not being controlled by a parent layout widget...
16086 var parent
= this.getParent
&& this.getParent();
16087 if(!(parent
&& parent
.isLayoutContainer
)){
16088 // Do recursive sizing and layout of all my descendants
16089 // (passing in no argument to resize means that it has to glean the size itself)
16092 // Since my parent isn't a layout container, and my style *may be* width=height=100%
16093 // or something similar (either set directly or via a CSS class),
16094 // monitor when viewport size changes so that I can re-layout.
16095 this.own(Viewport
.on("resize", lang
.hitch(this, "resize")));
16099 resize: function(changeSize
, resultSize
){
16101 // Call this to resize a widget, or after its size has changed.
16103 // ####Change size mode:
16105 // When changeSize is specified, changes the marginBox of this widget
16106 // and forces it to re-layout its contents accordingly.
16107 // changeSize may specify height, width, or both.
16109 // If resultSize is specified it indicates the size the widget will
16110 // become after changeSize has been applied.
16112 // ####Notification mode:
16114 // When changeSize is null, indicates that the caller has already changed
16115 // the size of the widget, or perhaps it changed because the browser
16116 // window was resized. Tells widget to re-layout its contents accordingly.
16118 // If resultSize is also specified it indicates the size the widget has
16121 // In either mode, this method also:
16123 // 1. Sets this._borderBox and this._contentBox to the new size of
16124 // the widget. Queries the current domNode size if necessary.
16125 // 2. Calls layout() to resize contents (and maybe adjust child widgets).
16126 // changeSize: Object?
16127 // Sets the widget to this margin-box size and position.
16128 // May include any/all of the following properties:
16129 // | {w: int, h: int, l: int, t: int}
16130 // resultSize: Object?
16131 // The margin-box size of this widget after applying changeSize (if
16132 // changeSize is specified). If caller knows this size and
16133 // passes it in, we don't need to query the browser to get the size.
16134 // | {w: int, h: int}
16136 var node
= this.domNode
;
16138 // set margin box size, unless it wasn't specified, in which case use current size
16140 domGeometry
.setMarginBox(node
, changeSize
);
16143 // If either height or width wasn't specified by the user, then query node for it.
16144 // But note that setting the margin box and then immediately querying dimensions may return
16145 // inaccurate results, so try not to depend on it.
16146 var mb
= resultSize
|| {};
16147 lang
.mixin(mb
, changeSize
|| {}); // changeSize overrides resultSize
16148 if( !("h" in mb
) || !("w" in mb
) ){
16149 mb
= lang
.mixin(domGeometry
.getMarginBox(node
), mb
); // just use domGeometry.marginBox() to fill in missing values
16152 // Compute and save the size of my border box and content box
16153 // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
16154 var cs
= domStyle
.getComputedStyle(node
);
16155 var me
= domGeometry
.getMarginExtents(node
, cs
);
16156 var be
= domGeometry
.getBorderExtents(node
, cs
);
16157 var bb
= (this._borderBox
= {
16158 w
: mb
.w
- (me
.w
+ be
.w
),
16159 h
: mb
.h
- (me
.h
+ be
.h
)
16161 var pe
= domGeometry
.getPadExtents(node
, cs
);
16162 this._contentBox
= {
16163 l
: domStyle
.toPixelValue(node
, cs
.paddingLeft
),
16164 t
: domStyle
.toPixelValue(node
, cs
.paddingTop
),
16169 // Callback for widget to adjust size of its children
16173 layout: function(){
16175 // Widgets override this method to size and position their contents/children.
16176 // When this is called this._contentBox is guaranteed to be set (see resize()).
16178 // This is called after startup(), and also when the widget's size has been
16181 // protected extension
16184 _setupChild: function(/*dijit/_WidgetBase*/child){
16186 // Common setup for initial children and children which are added after startup
16188 // protected extension
16190 var cls = this.baseClass + "-child "
16191 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
16192 domClass.add(child.domNode, cls);
16195 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
16196 // Overrides _Container.addChild() to call _setupChild()
16197 this.inherited(arguments
);
16199 this._setupChild(child
);
16203 removeChild: function(/*dijit/_WidgetBase*/ child){
16204 // Overrides _Container.removeChild() to remove class added by _setupChild()
16205 var cls = this.baseClass + "-child"
16206 + (child.baseClass ?
16207 " " + this.baseClass + "-" + child.baseClass : "");
16208 domClass.remove(child.domNode, cls);
16210 this.inherited(arguments);
16216 'dijit/popup':function(){
16217 define("dijit/popup", [
16218 "dojo/_base/array", // array.forEach array.some
16220 "dojo/_base/connect", // connect._keypress
16221 "dojo/_base/declare", // declare
16222 "dojo/dom", // dom.isDescendant
16223 "dojo/dom-attr", // domAttr.set
16224 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
16225 "dojo/dom-geometry", // domGeometry.isBodyLtr
16226 "dojo/dom-style", // domStyle.set
16227 "dojo/_base/event", // event.stop
16229 "dojo/_base/lang", // lang.hitch
16231 "dojo/sniff", // has("ie") has("mozilla")
16233 "./BackgroundIframe",
16234 "./main" // dijit (defining dijit.popup to match API doc)
16235 ], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has,
16236 place, BackgroundIframe, dijit){
16244 // widget to display
16246 // the button etc. that is displaying this popup
16248 // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
16250 // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
16252 // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
16253 // orient: Object|String
16254 // When the around parameter is specified, orient should be a list of positions to try, ex:
16255 // | [ "below", "above" ]
16256 // For backwards compatibility it can also be an (ordered) hash of tuples of the form
16257 // (around-node-corner, popup-node-corner), ex:
16258 // | { "BL": "TL", "TL": "BL" }
16259 // where BL means "bottom left" and "TL" means "top left", etc.
16261 // dijit/popup.open() tries to position the popup according to each specified position, in order,
16262 // until the popup appears fully within the viewport.
16264 // The default value is ["below", "above"]
16266 // When an (x,y) position is specified rather than an around node, orient is either
16267 // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
16268 // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
16269 // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
16270 // and the top-right corner.
16271 // onCancel: Function
16272 // callback when user has canceled the popup by:
16274 // 1. hitting ESC or
16275 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
16276 // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
16277 // onClose: Function
16278 // callback whenever this popup is closed
16279 // onExecute: Function
16280 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
16281 // padding: place.__Position
16282 // adding a buffer around the opening position. This is only useful when around is not set.
16286 function destroyWrapper(){
16288 // Function to destroy wrapper when popup widget is destroyed.
16289 // Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
16290 if(this._popupWrapper
){
16291 domConstruct
.destroy(this._popupWrapper
);
16292 delete this._popupWrapper
;
16296 var PopupManager
= declare(null, {
16298 // Used to show drop downs (ex: the select list of a ComboBox)
16299 // or popups (ex: right-click context menus).
16301 // _stack: dijit/_WidgetBase[]
16302 // Stack of currently popped up widgets.
16303 // (someone opened _stack[0], and then it opened _stack[1], etc.)
16306 // _beginZIndex: Number
16307 // Z-index of the first popup. (If first popup opens other
16308 // popups they get a higher z-index.)
16309 _beginZIndex
: 1000,
16313 _createWrapper: function(/*Widget*/ widget
){
16315 // Initialization for widgets that will be used as popups.
16316 // Puts widget inside a wrapper DIV (if not already in one),
16317 // and returns pointer to that wrapper DIV.
16319 var wrapper
= widget
._popupWrapper
,
16320 node
= widget
.domNode
;
16323 // Create wrapper <div> for when this widget [in the future] will be used as a popup.
16324 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
16325 // to go wonky, see tests/robot/Toolbar.html to reproduce
16326 wrapper
= domConstruct
.create("div", {
16327 "class":"dijitPopup",
16328 style
:{ display
: "none"},
16329 role
: "presentation"
16330 }, widget
.ownerDocumentBody
);
16331 wrapper
.appendChild(node
);
16333 var s
= node
.style
;
16339 widget
._popupWrapper
= wrapper
;
16340 aspect
.after(widget
, "destroy", destroyWrapper
, true);
16346 moveOffScreen: function(/*Widget*/ widget
){
16348 // Moves the popup widget off-screen.
16349 // Do not use this method to hide popups when not in use, because
16350 // that will create an accessibility issue: the offscreen popup is
16351 // still in the tabbing order.
16353 // Create wrapper if not already there
16354 var wrapper
= this._createWrapper(widget
);
16356 domStyle
.set(wrapper
, {
16357 visibility
: "hidden",
16358 top
: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
16363 hide: function(/*Widget*/ widget
){
16365 // Hide this popup widget (until it is ready to be shown).
16366 // Initialization for widgets that will be used as popups
16368 // Also puts widget inside a wrapper DIV (if not already in one)
16370 // If popup widget needs to layout it should
16371 // do so when it is made visible, and popup._onShow() is called.
16373 // Create wrapper if not already there
16374 var wrapper
= this._createWrapper(widget
);
16376 domStyle
.set(wrapper
, "display", "none");
16379 getTopPopup: function(){
16381 // Compute the closest ancestor popup that's *not* a child of another popup.
16382 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
16383 var stack
= this._stack
;
16384 for(var pi
=stack
.length
-1; pi
> 0 && stack
[pi
].parent
=== stack
[pi
-1].widget
; pi
--){
16385 /* do nothing, just trying to get right value for pi */
16390 open: function(/*__OpenArgs*/ args
){
16392 // Popup the widget at the specified position
16395 // opening at the mouse position
16396 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
16399 // opening the widget as a dropdown
16400 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
16402 // Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
16403 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
16405 var stack
= this._stack
,
16406 widget
= args
.popup
,
16407 orient
= args
.orient
|| ["below", "below-alt", "above", "above-alt"],
16408 ltr
= args
.parent
? args
.parent
.isLeftToRight() : domGeometry
.isBodyLtr(widget
.ownerDocument
),
16409 around
= args
.around
,
16410 id
= (args
.around
&& args
.around
.id
) ? (args
.around
.id
+"_dropdown") : ("popup_"+this._idGen
++);
16412 // If we are opening a new popup that isn't a child of a currently opened popup, then
16413 // close currently opened popup(s). This should happen automatically when the old popups
16414 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
16415 while(stack
.length
&& (!args
.parent
|| !dom
.isDescendant(args
.parent
.domNode
, stack
[stack
.length
-1].widget
.domNode
))){
16416 this.close(stack
[stack
.length
-1].widget
);
16419 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
16420 var wrapper
= this._createWrapper(widget
);
16423 domAttr
.set(wrapper
, {
16426 zIndex
: this._beginZIndex
+ stack
.length
16428 "class": "dijitPopup " + (widget
.baseClass
|| widget
["class"] || "").split(" ")[0] +"Popup",
16429 dijitPopupParent
: args
.parent
? args
.parent
.id
: ""
16432 if(has("ie") || has("mozilla")){
16433 if(!widget
.bgIframe
){
16434 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
16435 widget
.bgIframe
= new BackgroundIframe(wrapper
);
16439 // position the wrapper node and make it visible
16440 var best
= around
?
16441 place
.around(wrapper
, around
, orient
, ltr
, widget
.orient
? lang
.hitch(widget
, "orient") : null) :
16442 place
.at(wrapper
, args
, orient
== 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args
.padding
);
16444 wrapper
.style
.display
= "";
16445 wrapper
.style
.visibility
= "visible";
16446 widget
.domNode
.style
.visibility
= "visible"; // counteract effects from _HasDropDown
16450 // provide default escape and tab key handling
16451 // (this will work for any widget, not just menu)
16452 handlers
.push(on(wrapper
, connect
._keypress
, lang
.hitch(this, function(evt
){
16453 if(evt
.charOrCode
== keys
.ESCAPE
&& args
.onCancel
){
16456 }else if(evt
.charOrCode
=== keys
.TAB
){
16458 var topPopup
= this.getTopPopup();
16459 if(topPopup
&& topPopup
.onCancel
){
16460 topPopup
.onCancel();
16465 // watch for cancel/execute events on the popup and notify the caller
16466 // (for a menu, "execute" means clicking an item)
16467 if(widget
.onCancel
&& args
.onCancel
){
16468 handlers
.push(widget
.on("cancel", args
.onCancel
));
16471 handlers
.push(widget
.on(widget
.onExecute
? "execute" : "change", lang
.hitch(this, function(){
16472 var topPopup
= this.getTopPopup();
16473 if(topPopup
&& topPopup
.onExecute
){
16474 topPopup
.onExecute();
16480 parent
: args
.parent
,
16481 onExecute
: args
.onExecute
,
16482 onCancel
: args
.onCancel
,
16483 onClose
: args
.onClose
,
16488 // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
16489 widget
.onOpen(best
);
16495 close: function(/*Widget?*/ popup
){
16497 // Close specified popup and any popups that it parented.
16498 // If no popup is specified, closes all popups.
16500 var stack
= this._stack
;
16502 // Basically work backwards from the top of the stack closing popups
16503 // until we hit the specified popup, but IIRC there was some issue where closing
16504 // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
16505 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
16506 // so the while condition is constructed defensively.
16507 while((popup
&& array
.some(stack
, function(elem
){return elem
.widget
== popup
;})) ||
16508 (!popup
&& stack
.length
)){
16509 var top
= stack
.pop(),
16510 widget
= top
.widget
,
16511 onClose
= top
.onClose
;
16513 if(widget
.onClose
){
16514 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
16519 while(h
= top
.handlers
.pop()){ h
.remove(); }
16521 // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
16522 if(widget
&& widget
.domNode
){
16533 return (dijit
.popup
= new PopupManager());
16537 'dijit/_base/manager':function(){
16538 define("dijit/_base/manager", [
16539 "dojo/_base/array",
16540 "dojo/_base/config", // defaultDuration
16543 "../main" // for setting exports to dijit namespace
16544 ], function(array
, config
, lang
, registry
, dijit
){
16547 // dijit/_base/manager
16551 // Deprecated. Shim to methods on registry, plus a few other declarations.
16552 // New code should access dijit/registry directly when possible.
16555 array
.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name
){
16556 exports
[name
] = registry
[name
];
16559 lang
.mixin(exports
, {
16560 // defaultDuration: Integer
16561 // The default fx.animation speed (in ms) to use for all Dijit
16562 // transitional fx.animations, unless otherwise specified
16563 // on a per-instance basis. Defaults to 200, overrided by
16564 // `djConfig.defaultDuration`
16565 defaultDuration
: config
["defaultDuration"] || 200
16568 lang
.mixin(dijit
, exports
);
16570 /*===== return exports; =====*/
16571 return dijit
; // for back compat :-(
16575 'dijit/layout/StackController':function(){
16576 define("dijit/layout/StackController", [
16577 "dojo/_base/array", // array.forEach array.indexOf array.map
16578 "dojo/_base/declare", // declare
16580 "dojo/_base/event", // event.stop
16581 "dojo/keys", // keys
16582 "dojo/_base/lang", // lang.getObject
16584 "../focus", // focus.focus()
16585 "../registry", // registry.byId
16587 "../_TemplatedMixin",
16589 "../form/ToggleButton",
16590 "dojo/i18n!../nls/common"
16591 ], function(array
, declare
, domClass
, event
, keys
, lang
, on
,
16592 focus
, registry
, _Widget
, _TemplatedMixin
, _Container
, ToggleButton
){
16595 // dijit/layout/StackController
16597 var StackButton
= declare("dijit.layout._StackButton", ToggleButton
, {
16599 // Internal widget used by StackContainer.
16601 // The button-like or tab-like object you click to select or delete a page
16605 // Override _FormWidget.tabIndex.
16606 // StackContainer buttons are not in the tab order by default.
16607 // Probably we should be calling this.startupKeyNavChildren() instead.
16610 // closeButton: Boolean
16611 // When true, display close button for this tab
16612 closeButton
: false,
16614 _aria_attr
: "aria-selected",
16616 buildRendering: function(/*Event*/ evt
){
16617 this.inherited(arguments
);
16618 (this.focusNode
|| this.domNode
).setAttribute("role", "tab");
16623 var StackController
= declare("dijit.layout.StackController", [_Widget
, _TemplatedMixin
, _Container
], {
16625 // Set of buttons to select a page in a `dijit/layout/StackContainer`
16627 // Monitors the specified StackContainer, and whenever a page is
16628 // added, deleted, or selected, updates itself accordingly.
16630 baseClass
: "dijitStackController",
16632 templateString
: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
16634 // containerId: [const] String
16635 // The id of the page container that I point to
16638 // buttonWidget: [const] Constructor
16639 // The button widget to create to correspond to each page
16640 buttonWidget
: StackButton
,
16642 // buttonWidgetCloseClass: String
16643 // CSS class of [x] close icon, used by event delegation code to tell when close button was clicked
16644 buttonWidgetCloseClass
: "dijitStackCloseButton",
16646 constructor: function(params
/*===== , srcNodeRef =====*/){
16648 // Create the widget.
16649 // params: Object|null
16650 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
16651 // and functions, typically callbacks like onClick.
16652 // The hash can contain any of the widget's properties, excluding read-only properties.
16653 // srcNodeRef: DOMNode|String?
16654 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
16656 this.pane2button
= {}; // mapping from pane id to buttons
16659 postCreate: function(){
16660 this.inherited(arguments
);
16662 // Listen to notifications from StackContainer.
16663 // TODO: do this through bubbled events instead of topics
16664 this.subscribe(this.containerId
+"-startup", "onStartup");
16665 this.subscribe(this.containerId
+"-addChild", "onAddChild");
16666 this.subscribe(this.containerId
+"-removeChild", "onRemoveChild");
16667 this.subscribe(this.containerId
+"-selectChild", "onSelectChild");
16668 this.subscribe(this.containerId
+"-containerKeyPress", "onContainerKeyPress");
16670 // Listen for click events to select or close tabs.
16671 // No need to worry about ENTER/SPACE key handling: tabs are selected via left/right arrow keys,
16672 // and closed via shift-F10 (to show the close menu).
16673 this.connect(this.containerNode
, 'click', function(evt
){
16674 var button
= registry
.getEnclosingWidget(evt
.target
);
16675 if(button
!= this.containerNode
&& !button
.disabled
&& button
.page
){
16676 for(var target
= evt
.target
; target
!== this.containerNode
; target
= target
.parentNode
){
16677 if(domClass
.contains(target
, this.buttonWidgetCloseClass
)){
16678 this.onCloseButtonClick(button
.page
);
16680 }else if(target
== button
.domNode
){
16681 this.onButtonClick(button
.page
);
16689 onStartup: function(/*Object*/ info
){
16691 // Called after StackContainer has finished initializing
16694 array
.forEach(info
.children
, this.onAddChild
, this);
16696 // Show button corresponding to selected pane (unless selected
16697 // is null because there are no panes)
16698 this.onSelectChild(info
.selected
);
16701 // Reflect events like page title changes to tab buttons
16702 var containerNode
= registry
.byId(this.containerId
).containerNode
,
16703 pane2button
= this.pane2button
,
16704 paneToButtonAttr
= {
16706 "showtitle": "showLabel",
16707 "iconclass": "iconClass",
16708 "closable": "closeButton",
16709 "tooltip": "title",
16710 "disabled": "disabled"
16712 connectFunc = function(attr
, buttonAttr
){
16713 return on(containerNode
, "attrmodified-" + attr
, function(evt
){
16714 var button
= pane2button
[evt
.detail
&& evt
.detail
.widget
&& evt
.detail
.widget
.id
];
16716 button
.set(buttonAttr
, evt
.detail
.newValue
);
16720 for(var attr
in paneToButtonAttr
){
16721 this.own(connectFunc(attr
, paneToButtonAttr
[attr
]));
16725 destroy: function(){
16726 // Since the buttons are internal to the StackController widget, destroy() should remove them, which is
16727 // done by calling onRemoveChild().
16728 for(var pane
in this.pane2button
){
16729 this.onRemoveChild(registry
.byId(pane
));
16732 // TODO: destroyRecursive() will call destroy() on each child button twice. Once from the above code,
16733 // and once because _WidgetBase.destroyDescendants() deletes anything inside of this.containerNode.
16734 // Probably shouldn't attach that DOMNode as this.containerNode.
16736 this.inherited(arguments
);
16739 onAddChild: function(/*dijit/_WidgetBase*/ page, /*Integer?*/ insertIndex
){
16741 // Called whenever a page is added to the container.
16742 // Create button corresponding to the page.
16746 // create an instance of the button widget
16747 // (remove typeof buttonWidget == string support in 2.0)
16748 var Cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
16749 var button
= new Cls({
16750 id
: this.id
+ "_" + page
.id
,
16751 name
: this.id
+ "_" + page
.id
,
16753 disabled
: page
.disabled
,
16754 ownerDocument
: this.ownerDocument
,
16757 textDir
: page
.textDir
,
16758 showLabel
: page
.showTitle
,
16759 iconClass
: page
.iconClass
,
16760 closeButton
: page
.closable
,
16761 title
: page
.tooltip
,
16765 this.addChild(button
, insertIndex
);
16766 this.pane2button
[page
.id
] = button
;
16767 page
.controlButton
= button
; // this value might be overwritten if two tabs point to same container
16768 if(!this._currentChild
){
16769 // If this is the first child then StackContainer will soon publish that it's selected,
16770 // but before that StackContainer calls layout(), and before layout() is called the
16771 // StackController needs to have the proper height... which means that the button needs
16772 // to be marked as selected now. See test_TabContainer_CSS.html for test.
16773 this.onSelectChild(page
);
16777 onRemoveChild: function(/*dijit/_WidgetBase*/ page){
16779 // Called whenever a page is removed from the container.
16780 // Remove the button corresponding to the page.
16784 if(this._currentChild === page){ this._currentChild = null; }
16786 var button = this.pane2button[page.id];
16788 this.removeChild(button);
16789 delete this.pane2button[page.id];
16792 delete page.controlButton;
16795 onSelectChild: function(/*dijit/_WidgetBase*/ page){
16797 // Called when a page has been selected in the StackContainer, either by me or by another StackController
16801 if(!page){ return; }
16803 if(this._currentChild){
16804 var oldButton=this.pane2button[this._currentChild.id];
16805 oldButton.set('checked', false);
16806 oldButton.focusNode.setAttribute("tabIndex", "-1");
16809 var newButton=this.pane2button[page.id];
16810 newButton.set('checked', true);
16811 this._currentChild = page;
16812 newButton.focusNode.setAttribute("tabIndex", "0");
16813 var container = registry.byId(this.containerId);
16814 container.containerNode.setAttribute("aria-labelledby", newButton.id);
16817 onButtonClick: function(/*dijit/_WidgetBase*/ page){
16819 // Called whenever one of my child buttons is pressed in an attempt to select a page
16823 var button = this.pane2button[page.id];
16825 // For TabContainer where the tabs are <span>, need to set focus explicitly when left/right arrow
16826 focus.focus(button.focusNode);
16828 if(this._currentChild && this._currentChild.id === page.id) {
16829 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
16830 button.set('checked', true);
16832 var container = registry.byId(this.containerId);
16833 container.selectChild(page);
16836 onCloseButtonClick: function(/*dijit/_WidgetBase*/ page){
16838 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
16842 var container = registry.byId(this.containerId);
16843 container.closeChild(page);
16844 if(this._currentChild){
16845 var b = this.pane2button[this._currentChild.id];
16847 focus.focus(b.focusNode || b.domNode);
16852 // TODO: this is a bit redundant with forward, back api in StackContainer
16853 adjacent: function(/*Boolean*/ forward
){
16855 // Helper for onkeypress to find next/previous button
16859 if(!this.isLeftToRight() && (!this.tabPosition
|| /top|bottom/.test(this.tabPosition
))){ forward
= !forward
; }
16860 // find currently focused button in children array
16861 var children
= this.getChildren();
16862 var idx
= array
.indexOf(children
, this.pane2button
[this._currentChild
.id
]),
16863 current
= children
[idx
];
16865 // Pick next/previous non-disabled button to focus on. If we get back to the original button it means
16866 // that all buttons must be disabled, so return current child to avoid an infinite loop.
16869 idx
= (idx
+ (forward
? 1 : children
.length
- 1)) % children
.length
;
16870 child
= children
[idx
];
16871 }while(child
.disabled
&& child
!= current
);
16873 return child
; // dijit/_WidgetBase
16876 onkeypress: function(/*Event*/ e
){
16878 // Handle keystrokes on the page list, for advancing to next/previous button
16879 // and closing the current page if the page is closable.
16883 if(this.disabled
|| e
.altKey
){ return; }
16884 var forward
= null;
16885 if(e
.ctrlKey
|| !e
._djpage
){
16886 switch(e
.charOrCode
){
16887 case keys
.LEFT_ARROW
:
16888 case keys
.UP_ARROW
:
16889 if(!e
._djpage
){ forward
= false; }
16892 if(e
.ctrlKey
){ forward
= false; }
16894 case keys
.RIGHT_ARROW
:
16895 case keys
.DOWN_ARROW
:
16896 if(!e
._djpage
){ forward
= true; }
16898 case keys
.PAGE_DOWN
:
16899 if(e
.ctrlKey
){ forward
= true; }
16902 // Navigate to first non-disabled child
16903 var children
= this.getChildren();
16904 for(var idx
= 0; idx
< children
.length
; idx
++){
16905 var child
= children
[idx
];
16906 if(!child
.disabled
){
16907 this.onButtonClick(child
.page
);
16914 // Navigate to last non-disabled child
16915 var children
= this.getChildren();
16916 for(var idx
= children
.length
-1; idx
>= 0; idx
--){
16917 var child
= children
[idx
];
16918 if(!child
.disabled
){
16919 this.onButtonClick(child
.page
);
16926 if(this._currentChild
.closable
){
16927 this.onCloseButtonClick(this._currentChild
);
16933 if(e
.charOrCode
=== keys
.TAB
){
16934 this.onButtonClick(this.adjacent(!e
.shiftKey
).page
);
16936 }else if(e
.charOrCode
== "w"){
16937 if(this._currentChild
.closable
){
16938 this.onCloseButtonClick(this._currentChild
);
16940 event
.stop(e
); // avoid browser tab closing.
16944 // handle next/previous page navigation (left/right arrow, etc.)
16945 if(forward
!== null){
16946 this.onButtonClick(this.adjacent(forward
).page
);
16952 onContainerKeyPress: function(/*Object*/ info
){
16954 // Called when there was a keypress on the container
16957 info
.e
._djpage
= info
.page
;
16958 this.onkeypress(info
.e
);
16962 StackController
.StackButton
= StackButton
; // for monkey patching
16964 return StackController
;
16968 'url:dijit/templates/TooltipDialog.html':"<div role=\"presentation\" tabIndex=\"-1\">\n\t<div class=\"dijitTooltipContainer\" role=\"presentation\">\n\t\t<div class =\"dijitTooltipContents dijitTooltipFocusNode\" data-dojo-attach-point=\"containerNode\" role=\"dialog\"></div>\n\t</div>\n\t<div class=\"dijitTooltipConnector\" role=\"presentation\" data-dojo-attach-point=\"connectorNode\"></div>\n</div>\n",
16969 'dojo/dnd/Mover':function(){
16970 define("dojo/dnd/Mover", [
16971 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window",
16972 "../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll"
16973 ], function(array
, declare
, event
, lang
, has
, win
, dom
, domGeom
, domStyle
, Evented
, on
, touch
, dnd
, autoscroll
){
16978 return declare("dojo.dnd.Mover", [Evented
], {
16980 // an object which makes a node follow the mouse, or touch-drag on touch devices.
16981 // Used as a default mover, and as a base class for custom movers.
16983 constructor: function(node
, e
, host
){
16985 // a node (or node's id) to be moved
16987 // a mouse event, which started the move;
16988 // only pageX and pageY properties are used
16990 // object which implements the functionality of the move,
16991 // and defines proper events (onMoveStart and onMoveStop)
16992 this.node
= dom
.byId(node
);
16993 this.marginBox
= {l
: e
.pageX
, t
: e
.pageY
};
16994 this.mouseButton
= e
.button
;
16995 var h
= (this.host
= host
), d
= node
.ownerDocument
;
16997 // At the start of a drag, onFirstMove is called, and then the following
16998 // listener is disconnected.
16999 on(d
, touch
.move, lang
.hitch(this, "onFirstMove")),
17001 // These are called continually during the drag
17002 on(d
, touch
.move, lang
.hitch(this, "onMouseMove")),
17004 // And these are called at the end of the drag
17005 on(d
, touch
.release
, lang
.hitch(this, "onMouseUp")),
17007 // cancel text selection and text dragging
17008 on(d
, "dragstart", event
.stop
),
17009 on(d
.body
, "selectstart", event
.stop
)
17012 // Tell autoscroll that a drag is starting
17013 autoscroll
.autoScrollStart(d
);
17015 // notify that the move has started
17016 if(h
&& h
.onMoveStart
){
17017 h
.onMoveStart(this);
17020 // mouse event processors
17021 onMouseMove: function(e
){
17023 // event processor for onmousemove/ontouchmove
17025 // mouse/touch event
17026 autoscroll
.autoScroll(e
);
17027 var m
= this.marginBox
;
17028 this.host
.onMove(this, {l
: m
.l
+ e
.pageX
, t
: m
.t
+ e
.pageY
}, e
);
17031 onMouseUp: function(e
){
17032 if(has("webkit") && has("mac") && this.mouseButton
== 2 ?
17033 e
.button
== 0 : this.mouseButton
== e
.button
){ // TODO Should condition be met for touch devices, too?
17039 onFirstMove: function(e
){
17041 // makes the node absolute; it is meant to be called only once.
17042 // relative and absolutely positioned nodes are assumed to use pixel units
17043 var s
= this.node
.style
, l
, t
, h
= this.host
;
17044 switch(s
.position
){
17047 // assume that left and top values are in pixels already
17048 l
= Math
.round(parseFloat(s
.left
)) || 0;
17049 t
= Math
.round(parseFloat(s
.top
)) || 0;
17052 s
.position
= "absolute"; // enforcing the absolute mode
17053 var m
= domGeom
.getMarginBox(this.node
);
17054 // event.pageX/pageY (which we used to generate the initial
17055 // margin box) includes padding and margin set on the body.
17056 // However, setting the node's position to absolute and then
17057 // doing domGeom.marginBox on it *doesn't* take that additional
17058 // space into account - so we need to subtract the combined
17059 // padding and margin. We use getComputedStyle and
17060 // _getMarginBox/_getContentBox to avoid the extra lookup of
17061 // the computed style.
17062 var b
= win
.doc
.body
;
17063 var bs
= domStyle
.getComputedStyle(b
);
17064 var bm
= domGeom
.getMarginBox(b
, bs
);
17065 var bc
= domGeom
.getContentBox(b
, bs
);
17066 l
= m
.l
- (bc
.l
- bm
.l
);
17067 t
= m
.t
- (bc
.t
- bm
.t
);
17070 this.marginBox
.l
= l
- this.marginBox
.l
;
17071 this.marginBox
.t
= t
- this.marginBox
.t
;
17072 if(h
&& h
.onFirstMove
){
17073 h
.onFirstMove(this, e
);
17076 // Disconnect touch.move that call this function
17077 this.events
.shift().remove();
17079 destroy: function(){
17081 // stops the move, deletes all references, so the object can be garbage-collected
17082 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
17083 // undo global settings
17085 if(h
&& h
.onMoveStop
){
17086 h
.onMoveStop(this);
17089 this.events
= this.node
= this.host
= null;
17096 'dijit/layout/TabContainer':function(){
17097 define("dijit/layout/TabContainer", [
17098 "dojo/_base/lang", // lang.getObject
17099 "dojo/_base/declare", // declare
17100 "./_TabContainerBase",
17102 "./ScrollingTabController"
17103 ], function(lang
, declare
, _TabContainerBase
, TabController
, ScrollingTabController
){
17106 // dijit/layout/TabContainer
17109 return declare("dijit.layout.TabContainer", _TabContainerBase
, {
17111 // A Container with tabs to select each child (only one of which is displayed at a time).
17113 // A TabContainer is a container that has multiple panes, but shows only
17114 // one pane at a time. There are a set of tabs corresponding to each pane,
17115 // where each tab has the name (aka title) of the pane, and optionally a close button.
17117 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
17118 // children of a `TabContainer`.
17120 // useMenu: [const] Boolean
17121 // True if a menu should be used to select tabs when they are too
17122 // wide to fit the TabContainer, false otherwise.
17125 // useSlider: [const] Boolean
17126 // True if a slider should be used to select tabs when they are too
17127 // wide to fit the TabContainer, false otherwise.
17130 // controllerWidget: Class
17131 // An optional parameter to override the widget used to display the tab labels
17132 controllerWidget
: "",
17134 _makeController: function(/*DomNode*/ srcNode
){
17136 // Instantiate tablist controller widget and return reference to it.
17137 // Callback from _TabContainerBase.postCreate().
17139 // protected extension
17141 // "string" branch for back-compat, remove for 2.0
17142 var cls
= this.baseClass
+ "-tabs" + (this.doLayout
? "" : " dijitTabNoLayout"),
17143 TabController
= typeof this.controllerWidget
== "string" ? lang
.getObject(this.controllerWidget
) :
17144 this.controllerWidget
;
17146 return new TabController({
17147 id
: this.id
+ "_tablist",
17148 ownerDocument
: this.ownerDocument
,
17151 textDir
: this.textDir
,
17152 tabPosition
: this.tabPosition
,
17153 doLayout
: this.doLayout
,
17154 containerId
: this.id
,
17156 nested
: this.nested
,
17157 useMenu
: this.useMenu
,
17158 useSlider
: this.useSlider
,
17159 tabStripClass
: this.tabStrip
? this.baseClass
+ (this.tabStrip
? "":"No") + "Strip": null
17163 postMixInProperties: function(){
17164 this.inherited(arguments
);
17166 // Scrolling controller only works for horizontal non-nested tabs
17167 if(!this.controllerWidget
){
17168 this.controllerWidget
= (this.tabPosition
== "top" || this.tabPosition
== "bottom") && !this.nested
?
17169 ScrollingTabController
: TabController
;
17176 'dijit/BackgroundIframe':function(){
17177 define("dijit/BackgroundIframe", [
17178 "require", // require.toUrl
17179 "./main", // to export dijit.BackgroundIframe
17180 "dojo/_base/config",
17181 "dojo/dom-construct", // domConstruct.create
17182 "dojo/dom-style", // domStyle.set
17183 "dojo/_base/lang", // lang.extend lang.hitch
17185 "dojo/sniff", // has("ie"), has("mozilla"), has("quirks")
17186 "dojo/_base/window" // win.doc.createElement
17187 ], function(require
, dijit
, config
, domConstruct
, domStyle
, lang
, on
, has
, win
){
17190 // dijit/BackgroundIFrame
17192 // TODO: remove _frames, it isn't being used much, since popups never release their
17193 // iframes (see [22236])
17194 var _frames
= new function(){
17196 // cache of iframes
17200 this.pop = function(){
17203 iframe
= queue
.pop();
17204 iframe
.style
.display
="";
17207 var burl
= config
["dojoBlankHtmlUrl"] || require
.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
17208 var html
="<iframe src='" + burl
+ "' role='presentation'"
17209 + " style='position: absolute; left: 0px; top: 0px;"
17210 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
17211 iframe
= win
.doc
.createElement(html
);
17213 iframe
= domConstruct
.create("iframe");
17214 iframe
.src
= 'javascript:""';
17215 iframe
.className
= "dijitBackgroundIframe";
17216 iframe
.setAttribute("role", "presentation");
17217 domStyle
.set(iframe
, "opacity", 0.1);
17219 iframe
.tabIndex
= -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
17224 this.push = function(iframe
){
17225 iframe
.style
.display
="none";
17226 queue
.push(iframe
);
17231 dijit
.BackgroundIframe = function(/*DomNode*/ node
){
17233 // For IE/FF z-index schenanigans. id attribute is required.
17236 // new dijit.BackgroundIframe(node).
17238 // Makes a background iframe as a child of node, that fills
17239 // area (and position) of node
17241 if(!node
.id
){ throw new Error("no id"); }
17242 if(has("ie") || has("mozilla")){
17243 var iframe
= (this.iframe
= _frames
.pop());
17244 node
.appendChild(iframe
);
17245 if(has("ie")<7 || has("quirks")){
17247 this._conn
= on(node
, 'resize', lang
.hitch(this, function(){
17251 domStyle
.set(iframe
, {
17259 lang
.extend(dijit
.BackgroundIframe
, {
17260 resize: function(node
){
17262 // Resize the iframe so it's the same size as node.
17263 // Needed on IE6 and IE/quirks because height:100% doesn't work right.
17265 domStyle
.set(this.iframe
, {
17266 width
: node
.offsetWidth
+ 'px',
17267 height
: node
.offsetHeight
+ 'px'
17271 destroy: function(){
17273 // destroy the iframe
17275 this._conn
.remove();
17279 _frames
.push(this.iframe
);
17280 delete this.iframe
;
17285 return dijit
.BackgroundIframe
;
17289 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
17290 'dojo/dnd/Avatar':function(){
17291 define("dojo/dnd/Avatar", [
17292 "../_base/declare",
17297 "../dom-construct",
17300 ], function(declare
, win
, dom
, domAttr
, domClass
, domConstruct
, has
, query
){
17305 return declare("dojo.dnd.Avatar", null, {
17307 // Object that represents transferred DnD items visually
17309 // a DnD manager object
17311 constructor: function(manager
){
17312 this.manager
= manager
;
17317 construct: function(){
17319 // constructor function;
17320 // it is separate so it can be (dynamically) overwritten in case of need
17322 var a
= domConstruct
.create("table", {
17323 "class": "dojoDndAvatar",
17325 position
: "absolute",
17330 source
= this.manager
.source
, node
,
17331 b
= domConstruct
.create("tbody", null, a
),
17332 tr
= domConstruct
.create("tr", null, b
),
17333 td
= domConstruct
.create("td", null, tr
),
17334 k
= Math
.min(5, this.manager
.nodes
.length
), i
= 0;
17336 if(has("highcontrast")){
17337 domConstruct
.create("span", {
17339 innerHTML
: this.manager
.copy
? '+' : "<"
17342 domConstruct
.create("span", {
17343 innerHTML
: source
.generateText
? this._generateText() : ""
17346 // we have to set the opacity on IE only after the node is live
17348 "class": "dojoDndAvatarHeader",
17349 style
: {opacity
: 0.9}
17352 if(source
.creator
){
17353 // create an avatar representation of the node
17354 node
= source
._normalizedCreator(source
.getItem(this.manager
.nodes
[i
].id
).data
, "avatar").node
;
17356 // or just clone the node and hope it works
17357 node
= this.manager
.nodes
[i
].cloneNode(true);
17358 if(node
.tagName
.toLowerCase() == "tr"){
17359 // insert extra table nodes
17360 var table
= domConstruct
.create("table"),
17361 tbody
= domConstruct
.create("tbody", null, table
);
17362 tbody
.appendChild(node
);
17367 tr
= domConstruct
.create("tr", null, b
);
17368 td
= domConstruct
.create("td", null, tr
);
17369 td
.appendChild(node
);
17371 "class": "dojoDndAvatarItem",
17372 style
: {opacity
: (9 - i
) / 10}
17377 destroy: function(){
17379 // destructor for the avatar; called to remove all references so it can be garbage-collected
17380 domConstruct
.destroy(this.node
);
17383 update: function(){
17385 // updates the avatar to reflect the current DnD state
17386 domClass
.toggle(this.node
, "dojoDndAvatarCanDrop", this.manager
.canDropFlag
);
17387 if(has("highcontrast")){
17388 var icon
= dom
.byId("a11yIcon");
17389 var text
= '+'; // assume canDrop && copy
17390 if (this.manager
.canDropFlag
&& !this.manager
.copy
){
17391 text
= '< '; // canDrop && move
17392 }else if (!this.manager
.canDropFlag
&& !this.manager
.copy
){
17393 text
= "o"; //!canDrop && move
17394 }else if(!this.manager
.canDropFlag
){
17395 text
= 'x'; // !canDrop && copy
17397 icon
.innerHTML
=text
;
17400 query(("tr.dojoDndAvatarHeader td span" +(has("highcontrast") ? " span" : "")), this.node
).forEach(
17402 node
.innerHTML
= this.manager
.source
.generateText
? this._generateText() : "";
17405 _generateText: function(){
17407 // generates a proper text to reflect copying or moving of items
17408 return this.manager
.nodes
.length
.toString();
17415 'dijit/form/Button':function(){
17417 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">●</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
17418 define("dijit/form/Button", [
17420 "dojo/_base/declare", // declare
17421 "dojo/dom-class", // domClass.toggle
17422 "dojo/has", // has("dijit-legacy-requires")
17423 "dojo/_base/kernel", // kernel.deprecated
17424 "dojo/_base/lang", // lang.trim
17428 "dojo/text!./templates/Button.html"
17429 ], function(require
, declare
, domClass
, has
, kernel
, lang
, ready
, _FormWidget
, _ButtonMixin
, template
){
17432 // dijit/form/Button
17434 // Back compat w/1.6, remove for 2.0
17435 if(has("dijit-legacy-requires")){
17436 ready(0, function(){
17437 var requires
= ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
17438 require(requires
); // use indirection so modules not rolled into a build
17442 return declare("dijit.form.Button", [_FormWidget
, _ButtonMixin
], {
17444 // Basically the same thing as a normal HTML button, but with special styling.
17446 // Buttons can display a label, an icon, or both.
17447 // A label should always be specified (through innerHTML) or the label
17448 // attribute. It can be hidden via showLabel=false.
17450 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
17453 // | var button1 = new Button({label: "hello world", onClick: foo});
17454 // | dojo.body().appendChild(button1.domNode);
17456 // showLabel: Boolean
17457 // Set this to true to hide the label text and display only the icon.
17458 // (If showLabel=false then iconClass must be specified.)
17459 // Especially useful for toolbars.
17460 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
17462 // The exception case is for computers in high-contrast mode, where the label
17463 // will still be displayed, since the icon doesn't appear.
17466 // iconClass: String
17467 // Class to apply to DOMNode in button to make it display an icon
17468 iconClass
: "dijitNoIcon",
17469 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
17471 baseClass
: "dijitButton",
17473 templateString
: template
,
17475 // Map widget attributes to DOMNode attributes.
17476 _setValueAttr
: "valueNode",
17478 _onClick: function(/*Event*/ e
){
17480 // Internal function to handle click actions
17481 var ok
= this.inherited(arguments
);
17483 if(this.valueNode
){
17484 this.valueNode
.click();
17485 e
.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
17486 e
.stopPropagation(); // avoid two events bubbling from Button widget
17487 // leave ok = true so that subclasses can do what they need to do
17493 _fillContent: function(/*DomNode*/ source
){
17494 // Overrides _Templated._fillContent().
17495 // If button label is specified as srcNodeRef.innerHTML rather than
17496 // this.params.label, handle it here.
17497 // TODO: remove the method in 2.0, parser will do it all for me
17498 if(source
&& (!this.params
|| !("label" in this.params
))){
17499 var sourceLabel
= lang
.trim(source
.innerHTML
);
17501 this.label
= sourceLabel
; // _applyAttributes will be called after buildRendering completes to update the DOM
17506 _setShowLabelAttr: function(val
){
17507 if(this.containerNode
){
17508 domClass
.toggle(this.containerNode
, "dijitDisplayNone", !val
);
17510 this._set("showLabel", val
);
17513 setLabel: function(/*String*/ content
){
17515 // Deprecated. Use set('label', ...) instead.
17516 kernel
.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
17517 this.set("label", content
);
17520 _setLabelAttr: function(/*String*/ content
){
17522 // Hook for set('label', ...) to work.
17524 // Set the label (text) of the button; takes an HTML string.
17525 // If the label is hidden (showLabel=false) then and no title has
17526 // been specified, then label is also set as title attribute of icon.
17527 this.inherited(arguments
);
17528 if(!this.showLabel
&& !("title" in this.params
)){
17529 this.titleNode
.title
= lang
.trim(this.containerNode
.innerText
|| this.containerNode
.textContent
|| '');
17539 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n",
17540 'dojo/dnd/move':function(){
17541 define("dojo/dnd/move", [
17542 "../_base/declare",
17543 "../dom-geometry", "../dom-style",
17544 "./common", "./Mover", "./Moveable"
17545 ], function(declare
, domGeom
, domStyle
, dnd
, Mover
, Moveable
){
17551 var __constrainedMoveableArgs = declare([Moveable.__MoveableArgs], {
17552 // constraints: Function
17553 // Calculates a constraint box.
17554 // It is called in a context of the moveable object.
17555 constraints: function(){},
17558 // restrict move within boundaries.
17563 var constrainedMoveable
= declare("dojo.dnd.move.constrainedMoveable", Moveable
, {
17564 // object attributes (for markup)
17565 constraints: function(){},
17568 constructor: function(node
, params
){
17570 // an object that makes a node moveable
17572 // a node (or node's id) to be moved
17573 // params: __constrainedMoveableArgs?
17574 // an optional object with additional parameters;
17575 // the rest is passed to the base class
17576 if(!params
){ params
= {}; }
17577 this.constraints
= params
.constraints
;
17578 this.within
= params
.within
;
17580 onFirstMove: function(/*Mover*/ mover
){
17582 // called during the very first move notification;
17583 // can be used to initialize coordinates, can be overwritten.
17584 var c
= this.constraintBox
= this.constraints
.call(this, mover
);
17588 var mb
= domGeom
.getMarginSize(mover
.node
);
17593 onMove: function(/*Mover*/ mover
, /*Object*/ leftTop
){
17595 // called during every move notification;
17596 // should actually move the node; can be overwritten.
17597 var c
= this.constraintBox
, s
= mover
.node
.style
;
17598 this.onMoving(mover
, leftTop
);
17599 leftTop
.l
= leftTop
.l
< c
.l
? c
.l
: c
.r
< leftTop
.l
? c
.r
: leftTop
.l
;
17600 leftTop
.t
= leftTop
.t
< c
.t
? c
.t
: c
.b
< leftTop
.t
? c
.b
: leftTop
.t
;
17601 s
.left
= leftTop
.l
+ "px";
17602 s
.top
= leftTop
.t
+ "px";
17603 this.onMoved(mover
, leftTop
);
17608 var __boxConstrainedMoveableArgs = declare([__constrainedMoveableArgs], {
17610 // a constraint box
17615 var boxConstrainedMoveable
= declare("dojo.dnd.move.boxConstrainedMoveable", constrainedMoveable
, {
17617 // object attributes (for markup)
17620 constructor: function(node
, params
){
17622 // an object, which makes a node moveable
17624 // a node (or node's id) to be moved
17625 // params: __boxConstrainedMoveableArgs?
17626 // an optional object with parameters
17627 var box
= params
&& params
.box
;
17628 this.constraints = function(){ return box
; };
17633 var __parentConstrainedMoveableArgs = declare( [__constrainedMoveableArgs], {
17635 // A parent's area to restrict the move.
17636 // Can be "margin", "border", "padding", or "content".
17641 var parentConstrainedMoveable
= declare("dojo.dnd.move.parentConstrainedMoveable", constrainedMoveable
, {
17643 // object attributes (for markup)
17646 constructor: function(node
, params
){
17648 // an object, which makes a node moveable
17650 // a node (or node's id) to be moved
17651 // params: __parentConstrainedMoveableArgs?
17652 // an optional object with parameters
17653 var area
= params
&& params
.area
;
17654 this.constraints = function(){
17655 var n
= this.node
.parentNode
,
17656 s
= domStyle
.getComputedStyle(n
),
17657 mb
= domGeom
.getMarginBox(n
, s
);
17658 if(area
== "margin"){
17659 return mb
; // Object
17661 var t
= domGeom
.getMarginExtents(n
, s
);
17662 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17663 if(area
== "border"){
17664 return mb
; // Object
17666 t
= domGeom
.getBorderExtents(n
, s
);
17667 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17668 if(area
== "padding"){
17669 return mb
; // Object
17671 t
= domGeom
.getPadExtents(n
, s
);
17672 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17673 return mb
; // Object
17682 constrainedMoveable
: constrainedMoveable
,
17683 boxConstrainedMoveable
: boxConstrainedMoveable
,
17684 parentConstrainedMoveable
: parentConstrainedMoveable
17690 'dijit/_WidgetBase':function(){
17691 define("dijit/_WidgetBase", [
17692 "require", // require.toUrl
17693 "dojo/_base/array", // array.forEach array.map
17695 "dojo/_base/config", // config.blankGif
17696 "dojo/_base/connect", // connect.connect
17697 "dojo/_base/declare", // declare
17698 "dojo/dom", // dom.byId
17699 "dojo/dom-attr", // domAttr.set domAttr.remove
17700 "dojo/dom-class", // domClass.add domClass.replace
17701 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
17702 "dojo/dom-geometry", // isBodyLtr
17703 "dojo/dom-style", // domStyle.set, domStyle.get
17705 "dojo/_base/kernel",
17706 "dojo/_base/lang", // mixin(), isArray(), etc.
17709 "dojo/Stateful", // Stateful
17711 "dojo/_base/window", // win.doc, win.body()
17713 "./registry" // registry.getUniqueId(), registry.findWidgets()
17714 ], function(require
, array
, aspect
, config
, connect
, declare
,
17715 dom
, domAttr
, domClass
, domConstruct
, domGeometry
, domStyle
, has
, kernel
,
17716 lang
, on
, ready
, Stateful
, topic
, win
, Destroyable
, registry
){
17719 // dijit/_WidgetBase
17721 // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
17722 has
.add("dijit-legacy-requires", !kernel
.isAsync
);
17724 // For back-compat, remove in 2.0.
17725 if(has("dijit-legacy-requires")){
17726 ready(0, function(){
17727 var requires
= ["dijit/_base/manager"];
17728 require(requires
); // use indirection so modules not rolled into a build
17732 // Nested hash listing attributes for each tag, all strings in lowercase.
17733 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
17735 function getAttrs(obj
){
17737 for(var attr
in obj
){
17738 ret
[attr
.toLowerCase()] = true;
17743 function nonEmptyAttrToDom(attr
){
17745 // Returns a setter function that copies the attribute to this.domNode,
17746 // or removes the attribute from this.domNode, depending on whether the
17747 // value is defined or not.
17748 return function(val
){
17749 domAttr
[val
? "set" : "remove"](this.domNode
, attr
, val
);
17750 this._set(attr
, val
);
17754 return declare("dijit._WidgetBase", [Stateful
, Destroyable
], {
17756 // Future base class for all Dijit widgets.
17758 // Future base class for all Dijit widgets.
17759 // _Widget extends this class adding support for various features needed by desktop.
17761 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
17762 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
17764 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
17765 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
17767 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
17769 // - DOM node attribute
17770 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
17771 // | _setFocusAttr: "focusNode" (shorthand)
17772 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
17773 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
17775 // - DOM node innerHTML
17776 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
17777 // Maps this.title to this.titleNode.innerHTML
17779 // - DOM node innerText
17780 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
17781 // Maps this.title to this.titleNode.innerText
17783 // - DOM node CSS class
17784 // | _setMyClassAttr: { node: "domNode", type: "class" }
17785 // Maps this.myClass to this.domNode.className
17787 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
17788 // formats of the above list.
17790 // If the custom setter is null, no action is performed other than saving the new value
17791 // in the widget (in this).
17793 // If no custom setter is defined for an attribute, then it will be copied
17794 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
17795 // That's only done though for attributes that match DOMNode attributes (title,
17796 // alt, aria-labelledby, etc.)
17798 // id: [const] String
17799 // A unique, opaque ID string that can be assigned by users or by the
17800 // system. If the developer passes an ID which is known not to be
17801 // unique, the specified ID is ignored and the system-generated ID is
17804 _setIdAttr
: "domNode", // to copy to this.domNode even for auto-generated id's
17806 // lang: [const] String
17807 // Rarely used. Overrides the default Dojo locale used to render this widget,
17808 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
17809 // Value must be among the list of locales specified during by the Dojo bootstrap,
17810 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
17812 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
17813 _setLangAttr
: nonEmptyAttrToDom("lang"),
17815 // dir: [const] String
17816 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
17817 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
17818 // default direction.
17820 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
17821 _setDirAttr
: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
17824 // Bi-directional support, the main variable which is responsible for the direction of the text.
17825 // The text direction can be different than the GUI direction by using this parameter in creation
17832 // 3. "auto" - contextual the direction of a text defined by first strong letter.
17834 // By default is as the page direction.
17838 // HTML class attribute
17840 _setClassAttr
: { node
: "domNode", type
: "class" },
17842 // style: String||Object
17843 // HTML style attributes as cssText string or name/value hash
17847 // HTML title attribute.
17849 // For form widgets this specifies a tooltip to display when hovering over
17850 // the widget (just like the native HTML title attribute).
17852 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
17853 // etc., it's used to specify the tab label, accordion pane title, etc.
17857 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
17858 // this specifies the tooltip to appear when the mouse is hovered over that text.
17861 // baseClass: [protected] String
17862 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
17866 // srcNodeRef: [readonly] DomNode
17867 // pointer to original DOM node
17870 // domNode: [readonly] DomNode
17871 // This is our visible representation of the widget! Other DOM
17872 // Nodes may by assigned to other properties, usually through the
17873 // template system's data-dojo-attach-point syntax, but the domNode
17874 // property is the canonical "top level" node in widget UI.
17877 // containerNode: [readonly] DomNode
17878 // Designates where children of the source DOM node will be placed.
17879 // "Children" in this case refers to both DOM nodes and widgets.
17880 // For example, for myWidget:
17882 // | <div data-dojo-type=myWidget>
17883 // | <b> here's a plain DOM node
17884 // | <span data-dojo-type=subWidget>and a widget</span>
17885 // | <i> and another plain DOM node </i>
17888 // containerNode would point to:
17890 // | <b> here's a plain DOM node
17891 // | <span data-dojo-type=subWidget>and a widget</span>
17892 // | <i> and another plain DOM node </i>
17894 // In templated widgets, "containerNode" is set via a
17895 // data-dojo-attach-point assignment.
17897 // containerNode must be defined for any widget that accepts innerHTML
17898 // (like ContentPane or BorderContainer or even Button), and conversely
17899 // is null for widgets that don't, like TextBox.
17900 containerNode
: null,
17902 // ownerDocument: [const] Document?
17903 // The document this widget belongs to. If not specified to constructor, will default to
17904 // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
17905 ownerDocument
: null,
17906 _setOwnerDocumentAttr: function(val
){
17907 // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
17908 this._set("ownerDocument", val
);
17912 // _started: [readonly] Boolean
17913 // startup() has completed.
17917 // attributeMap: [protected] Object
17918 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
17919 // for each XXX attribute to be mapped to the DOM.
17921 // attributeMap sets up a "binding" between attributes (aka properties)
17922 // of the widget and the widget's DOM.
17923 // Changes to widget attributes listed in attributeMap will be
17924 // reflected into the DOM.
17926 // For example, calling set('title', 'hello')
17927 // on a TitlePane will automatically cause the TitlePane's DOM to update
17928 // with the new title.
17930 // attributeMap is a hash where the key is an attribute of the widget,
17931 // and the value reflects a binding to a:
17933 // - DOM node attribute
17934 // | focus: {node: "focusNode", type: "attribute"}
17935 // Maps this.focus to this.focusNode.focus
17937 // - DOM node innerHTML
17938 // | title: { node: "titleNode", type: "innerHTML" }
17939 // Maps this.title to this.titleNode.innerHTML
17941 // - DOM node innerText
17942 // | title: { node: "titleNode", type: "innerText" }
17943 // Maps this.title to this.titleNode.innerText
17945 // - DOM node CSS class
17946 // | myClass: { node: "domNode", type: "class" }
17947 // Maps this.myClass to this.domNode.className
17949 // If the value is an array, then each element in the array matches one of the
17950 // formats of the above list.
17952 // There are also some shorthands for backwards compatibility:
17954 // - string --> { node: string, type: "attribute" }, for example:
17956 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
17958 // - "" --> { node: "domNode", type: "attribute" }
17961 // _blankGif: [protected] String
17962 // Path to a blank 1x1 image.
17963 // Used by `<img>` nodes in templates that really get their image via CSS background-image.
17964 _blankGif
: config
.blankGif
|| require
.toUrl("dojo/resources/blank.gif"),
17966 //////////// INITIALIZATION METHODS ///////////////////////////////////////
17969 constructor: function(params, srcNodeRef){
17971 // Create the widget.
17972 // params: Object|null
17973 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17974 // and functions, typically callbacks like onClick.
17975 // The hash can contain any of the widget's properties, excluding read-only properties.
17976 // srcNodeRef: DOMNode|String?
17977 // If a srcNodeRef (DOM node) is specified:
17979 // - use srcNodeRef.innerHTML as my contents
17980 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17981 // - otherwise, replace srcNodeRef with my generated DOM tree
17985 postscript: function(/*Object?*/params
, /*DomNode|String*/srcNodeRef
){
17987 // Kicks off widget instantiation. See create() for details.
17990 this.create(params
, srcNodeRef
);
17993 create: function(params
, srcNodeRef
){
17995 // Kick off the life-cycle of a widget
17997 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
17998 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
17999 // for a discussion of the widget creation lifecycle.
18001 // Of course, adventurous developers could override create entirely, but this should
18002 // only be done as a last resort.
18003 // params: Object|null
18004 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
18005 // and functions, typically callbacks like onClick.
18006 // The hash can contain any of the widget's properties, excluding read-only properties.
18007 // srcNodeRef: DOMNode|String?
18008 // If a srcNodeRef (DOM node) is specified:
18010 // - use srcNodeRef.innerHTML as my contents
18011 // - if this is a behavioral widget then apply behavior to that srcNodeRef
18012 // - otherwise, replace srcNodeRef with my generated DOM tree
18016 // store pointer to original DOM tree
18017 this.srcNodeRef
= dom
.byId(srcNodeRef
);
18019 // No longer used, remove for 2.0.
18020 this._connects
= [];
18021 this._supportingWidgets
= [];
18023 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
18024 if(this.srcNodeRef
&& (typeof this.srcNodeRef
.id
== "string")){ this.id
= this.srcNodeRef
.id
; }
18026 // mix in our passed parameters
18028 this.params
= params
;
18029 lang
.mixin(this, params
);
18031 this.postMixInProperties();
18033 // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
18034 // Do this before buildRendering() because it might expect the id to be there.
18036 this.id
= registry
.getUniqueId(this.declaredClass
.replace(/\./g,"_"));
18038 // if params contains {id: undefined}, prevent _applyAttributes() from processing it
18039 delete this.params
.id
;
18043 // The document and <body> node this widget is associated with
18044 this.ownerDocument
= this.ownerDocument
|| (this.srcNodeRef
? this.srcNodeRef
.ownerDocument
: win
.doc
);
18045 this.ownerDocumentBody
= win
.body(this.ownerDocument
);
18047 registry
.add(this);
18049 this.buildRendering();
18051 var deleteSrcNodeRef
;
18054 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
18055 // Also calls custom setters for all attributes with custom setters.
18056 this._applyAttributes();
18058 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
18059 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
18060 // widget being attached to the DOM since it isn't when a widget is created programmatically like
18061 // new MyWidget({}). See #11635.
18062 var source
= this.srcNodeRef
;
18063 if(source
&& source
.parentNode
&& this.domNode
!== source
){
18064 source
.parentNode
.replaceChild(this.domNode
, source
);
18065 deleteSrcNodeRef
= true;
18068 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
18069 // assuming that dojo._scopeName even exists in 2.0
18070 this.domNode
.setAttribute("widgetId", this.id
);
18074 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
18075 // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
18076 if(deleteSrcNodeRef
){
18077 delete this.srcNodeRef
;
18080 this._created
= true;
18083 _applyAttributes: function(){
18085 // Step during widget creation to copy widget attributes to the
18086 // DOM according to attributeMap and _setXXXAttr objects, and also to call
18087 // custom _setXXXAttr() methods.
18089 // Skips over blank/false attribute values, unless they were explicitly specified
18090 // as parameters to the widget, since those are the default anyway,
18091 // and setting tabIndex="" is different than not setting tabIndex at all.
18093 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
18094 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
18098 // Get list of attributes where this.set(name, value) will do something beyond
18099 // setting this[name] = value. Specifically, attributes that have:
18100 // - associated _setXXXAttr() method/hash/string/array
18101 // - entries in attributeMap (remove this for 2.0);
18102 var ctor
= this.constructor,
18103 list
= ctor
._setterAttrs
;
18105 list
= (ctor
._setterAttrs
= []);
18106 for(var attr
in this.attributeMap
){
18110 var proto
= ctor
.prototype;
18111 for(var fxName
in proto
){
18112 if(fxName
in this.attributeMap
){ continue; }
18113 var setterName
= "_set" + fxName
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); }) + "Attr";
18114 if(setterName
in proto
){
18120 // Call this.set() for each property that was either specified as parameter to constructor,
18121 // or is in the list found above. For correlated properties like value and displayedValue, the one
18122 // specified as a parameter should take precedence.
18123 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
18124 // NaN and thus is not ignored like a default value of "".
18126 // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
18127 // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
18129 for(var key
in this.params
|| {}){
18130 params
[key
] = this[key
];
18133 // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
18134 array
.forEach(list
, function(attr
){
18135 if(attr
in params
){
18136 // skip this one, do it below
18137 }else if(this[attr
]){
18138 this.set(attr
, this[attr
]);
18142 // Step 3: Call set() for each property that was specified as parameter to constructor.
18143 // Use params hash created above to ignore side effects from step #2 above.
18144 for(key
in params
){
18145 this.set(key
, params
[key
]);
18149 postMixInProperties: function(){
18151 // Called after the parameters to the widget have been read-in,
18152 // but before the widget template is instantiated. Especially
18153 // useful to set properties that are referenced in the widget
18159 buildRendering: function(){
18161 // Construct the UI for this widget, setting this.domNode.
18162 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
18167 // Create root node if it wasn't created by _Templated
18168 this.domNode
= this.srcNodeRef
|| this.ownerDocument
.createElement("div");
18171 // baseClass is a single class name or occasionally a space-separated list of names.
18172 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
18173 // TODO: make baseClass custom setter
18174 if(this.baseClass
){
18175 var classes
= this.baseClass
.split(" ");
18176 if(!this.isLeftToRight()){
18177 classes
= classes
.concat( array
.map(classes
, function(name
){ return name
+"Rtl"; }));
18179 domClass
.add(this.domNode
, classes
);
18183 postCreate: function(){
18185 // Processing after the DOM fragment is created
18187 // Called after the DOM fragment has been created, but not necessarily
18188 // added to the document. Do not include any operations which rely on
18189 // node dimensions or placement.
18194 startup: function(){
18196 // Processing after the DOM fragment is added to the document
18198 // Called after a widget and its children have been created and added to the page,
18199 // and all related widgets have finished their create() cycle, up through postCreate().
18200 // This is useful for composite widgets that need to control or layout sub-widgets.
18201 // Many layout widgets can use this as a wiring phase.
18202 if(this._started
){ return; }
18203 this._started
= true;
18204 array
.forEach(this.getChildren(), function(obj
){
18205 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
18207 obj
._started
= true;
18212 //////////// DESTROY FUNCTIONS ////////////////////////////////
18214 destroyRecursive: function(/*Boolean?*/ preserveDom
){
18216 // Destroy this widget and its descendants
18218 // This is the generic "destructor" function that all widget users
18219 // should call to cleanly discard with a widget. Once a widget is
18220 // destroyed, it is removed from the manager object.
18222 // If true, this method will leave the original DOM structure
18223 // alone of descendant Widgets. Note: This will NOT work with
18224 // dijit._Templated widgets.
18226 this._beingDestroyed
= true;
18227 this.destroyDescendants(preserveDom
);
18228 this.destroy(preserveDom
);
18231 destroy: function(/*Boolean*/ preserveDom
){
18233 // Destroy this widget, but not its descendants.
18234 // This method will, however, destroy internal widgets such as those used within a template.
18235 // preserveDom: Boolean
18236 // If true, this method will leave the original DOM structure alone.
18237 // Note: This will not yet work with _Templated widgets
18239 this._beingDestroyed
= true;
18240 this.uninitialize();
18242 function destroy(w
){
18243 if(w
.destroyRecursive
){
18244 w
.destroyRecursive(preserveDom
);
18245 }else if(w
.destroy
){
18246 w
.destroy(preserveDom
);
18250 // Back-compat, remove for 2.0
18251 array
.forEach(this._connects
, lang
.hitch(this, "disconnect"));
18252 array
.forEach(this._supportingWidgets
, destroy
);
18254 // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
18255 // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
18257 array
.forEach(registry
.findWidgets(this.domNode
, this.containerNode
), destroy
);
18260 this.destroyRendering(preserveDom
);
18261 registry
.remove(this.id
);
18262 this._destroyed
= true;
18265 destroyRendering: function(/*Boolean?*/ preserveDom
){
18267 // Destroys the DOM nodes associated with this widget
18269 // If true, this method will leave the original DOM structure alone
18270 // during tear-down. Note: this will not work with _Templated
18276 this.bgIframe
.destroy(preserveDom
);
18277 delete this.bgIframe
;
18282 domAttr
.remove(this.domNode
, "widgetId");
18284 domConstruct
.destroy(this.domNode
);
18286 delete this.domNode
;
18289 if(this.srcNodeRef
){
18291 domConstruct
.destroy(this.srcNodeRef
);
18293 delete this.srcNodeRef
;
18297 destroyDescendants: function(/*Boolean?*/ preserveDom
){
18299 // Recursively destroy the children of this widget and their
18302 // If true, the preserveDom attribute is passed to all descendant
18303 // widget's .destroy() method. Not for use with _Templated
18306 // get all direct descendants and destroy them recursively
18307 array
.forEach(this.getChildren(), function(widget
){
18308 if(widget
.destroyRecursive
){
18309 widget
.destroyRecursive(preserveDom
);
18314 uninitialize: function(){
18316 // Deprecated. Override destroy() instead to implement custom widget tear-down
18323 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
18325 _setStyleAttr: function(/*String||Object*/ value
){
18327 // Sets the style attribute of the widget according to value,
18328 // which is either a hash like {height: "5px", width: "3px"}
18329 // or a plain string
18331 // Determines which node to set the style on based on style setting
18332 // in attributeMap.
18336 var mapNode
= this.domNode
;
18338 // Note: technically we should revert any style setting made in a previous call
18339 // to his method, but that's difficult to keep track of.
18341 if(lang
.isObject(value
)){
18342 domStyle
.set(mapNode
, value
);
18344 if(mapNode
.style
.cssText
){
18345 mapNode
.style
.cssText
+= "; " + value
;
18347 mapNode
.style
.cssText
= value
;
18351 this._set("style", value
);
18354 _attrToDom: function(/*String*/ attr
, /*String*/ value
, /*Object?*/ commands
){
18356 // Reflect a widget attribute (title, tabIndex, duration etc.) to
18357 // the widget DOM, as specified by commands parameter.
18358 // If commands isn't specified then it's looked up from attributeMap.
18359 // Note some attributes like "type"
18360 // cannot be processed this way as they are not mutable.
18362 // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
18363 // to DOMNode inside the widget, or alternately pointing to a subwidget
18367 commands
= arguments
.length
>= 3 ? commands
: this.attributeMap
[attr
];
18369 array
.forEach(lang
.isArray(commands
) ? commands
: [commands
], function(command
){
18371 // Get target node and what we are doing to that node
18372 var mapNode
= this[command
.node
|| command
|| "domNode"]; // DOM node
18373 var type
= command
.type
|| "attribute"; // class, innerHTML, innerText, or attribute
18377 if(lang
.isFunction(value
)){ // functions execute in the context of the widget
18378 value
= lang
.hitch(this, value
);
18381 // Get the name of the DOM node attribute; usually it's the same
18382 // as the name of the attribute in the widget (attr), but can be overridden.
18383 // Also maps handler names to lowercase, like onSubmit --> onsubmit
18384 var attrName
= command
.attribute
? command
.attribute
:
18385 (/^on[A-Z][a-zA-Z]*$/.test(attr
) ? attr
.toLowerCase() : attr
);
18387 if(mapNode
.tagName
){
18388 // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
18389 // method, but for consistency we still call domAttr
18390 domAttr
.set(mapNode
, attrName
, value
);
18392 // mapping to a sub-widget
18393 mapNode
.set(attrName
, value
);
18397 mapNode
.innerHTML
= "";
18398 mapNode
.appendChild(this.ownerDocument
.createTextNode(value
));
18401 mapNode
.innerHTML
= value
;
18404 domClass
.replace(mapNode
, value
, this[attr
]);
18410 get: function(name
){
18412 // Get a property from a widget.
18414 // The property to get.
18416 // Get a named property from a widget. The property may
18417 // potentially be retrieved via a getter method. If no getter is defined, this
18418 // just retrieves the object's property.
18420 // For example, if the widget has properties `foo` and `bar`
18421 // and a method named `_getFooAttr()`, calling:
18422 // `myWidget.get("foo")` would be equivalent to calling
18423 // `widget._getFooAttr()` and `myWidget.get("bar")`
18424 // would be equivalent to the expression
18426 var names
= this._getAttrNames(name
);
18427 return this[names
.g
] ? this[names
.g
]() : this[name
];
18430 set: function(name
, value
){
18432 // Set a property on a widget
18434 // The property to set.
18436 // The value to set in the property.
18438 // Sets named properties on a widget which may potentially be handled by a
18439 // setter in the widget.
18441 // For example, if the widget has properties `foo` and `bar`
18442 // and a method named `_setFooAttr()`, calling
18443 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
18444 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
18445 // would be equivalent to the statement `widget.bar = 3;`
18447 // set() may also be called with a hash of name/value pairs, ex:
18449 // | myWidget.set({
18454 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
18456 if(typeof name
=== "object"){
18457 for(var x
in name
){
18458 this.set(x
, name
[x
]);
18462 var names
= this._getAttrNames(name
),
18463 setter
= this[names
.s
];
18464 if(lang
.isFunction(setter
)){
18465 // use the explicit setter
18466 var result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
18468 // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
18469 // Map according to:
18470 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
18471 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
18472 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
18473 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
18474 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
18475 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
18476 var defaultNode
= this.focusNode
&& !lang
.isFunction(this.focusNode
) ? "focusNode" : "domNode",
18477 tag
= this[defaultNode
].tagName
,
18478 attrsForTag
= tagAttrs
[tag
] || (tagAttrs
[tag
] = getAttrs(this[defaultNode
])),
18479 map
= name
in this.attributeMap
? this.attributeMap
[name
] :
18480 names
.s
in this ? this[names
.s
] :
18481 ((names
.l
in attrsForTag
&& typeof value
!= "function") ||
18482 /^aria-|^data-|^role$/.test(name
)) ? defaultNode
: null;
18484 this._attrToDom(name
, value
, map
);
18486 this._set(name
, value
);
18488 return result
|| this;
18491 _attrPairNames
: {}, // shared between all widgets
18492 _getAttrNames: function(name
){
18494 // Helper function for get() and set().
18495 // Caches attribute name values so we don't do the string ops every time.
18499 var apn
= this._attrPairNames
;
18500 if(apn
[name
]){ return apn
[name
]; }
18501 var uc
= name
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); });
18502 return (apn
[name
] = {
18504 s
: "_set"+uc
+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
18505 g
: "_get"+uc
+"Attr",
18506 l
: uc
.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
18510 _set: function(/*String*/ name
, /*anything*/ value
){
18512 // Helper function to set new value for specified attribute, and call handlers
18513 // registered with watch() if the value has changed.
18514 var oldValue
= this[name
];
18515 this[name
] = value
;
18516 if(this._created
&& value
!== oldValue
){
18517 if(this._watchCallbacks
){
18518 this._watchCallbacks(name
, oldValue
, value
);
18520 this.emit("attrmodified-" + name
, {
18522 prevValue
: oldValue
,
18529 emit: function(/*String*/ type
, /*Object?*/ eventObj
, /*Array?*/ callbackArgs
){
18531 // Used by widgets to signal that a synthetic event occurred, ex:
18532 // | myWidget.emit("attrmodified-selectedChildWidget", {}).
18534 // Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
18535 // Also calls onType() method, if present, and returns value from that method.
18536 // By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
18537 // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
18541 // Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
18542 // Also set pointer to widget, although since we can't add a pointer to the widget for native events
18543 // (see #14729), maybe we shouldn't do it here?
18544 eventObj
= eventObj
|| {};
18545 if(eventObj
.bubbles
=== undefined){ eventObj
.bubbles
= true; }
18546 if(eventObj
.cancelable
=== undefined){ eventObj
.cancelable
= true; }
18547 if(!eventObj
.detail
){ eventObj
.detail
= {}; }
18548 eventObj
.detail
.widget
= this;
18550 var ret
, callback
= this["on"+type
];
18552 ret
= callback
.apply(this, callbackArgs
? callbackArgs
: [eventObj
]);
18555 // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
18556 if(this._started
&& !this._beingDestroyed
){
18557 on
.emit(this.domNode
, type
.toLowerCase(), eventObj
);
18563 on: function(/*String|Function*/ type
, /*Function*/ func
){
18565 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
18567 // Name of event (ex: "click") or extension event like touch.press.
18569 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
18570 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
18571 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
18573 // For backwards compatibility, if there's an onType() method in the widget then connect to that.
18575 var widgetMethod
= this._onMap(type
);
18577 return aspect
.after(this, widgetMethod
, func
, true);
18580 // Otherwise, just listen for the event on this.domNode.
18581 return this.own(on(this.domNode
, type
, func
))[0];
18584 _onMap: function(/*String|Function*/ type
){
18586 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
18587 // If type is a synthetic event like touch.press then returns undefined.
18588 var ctor
= this.constructor, map
= ctor
._onMap
;
18590 map
= (ctor
._onMap
= {});
18591 for(var attr
in ctor
.prototype){
18592 if(/^on/.test(attr
)){
18593 map
[attr
.replace(/^on/, "").toLowerCase()] = attr
;
18597 return map
[typeof type
== "string" && type
.toLowerCase()]; // String
18600 toString: function(){
18602 // Returns a string that represents the widget
18604 // When a widget is cast to a string, this method will be used to generate the
18605 // output. Currently, it does not implement any sort of reversible
18607 return '[Widget ' + this.declaredClass
+ ', ' + (this.id
|| 'NO ID') + ']'; // String
18610 getChildren: function(){
18612 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
18613 // Does not return nested widgets, nor widgets that are part of this widget's template.
18614 return this.containerNode
? registry
.findWidgets(this.containerNode
) : []; // dijit/_WidgetBase[]
18617 getParent: function(){
18619 // Returns the parent widget of this widget
18620 return registry
.getEnclosingWidget(this.domNode
.parentNode
);
18624 /*Object|null*/ obj
,
18625 /*String|Function*/ event
,
18626 /*String|Function*/ method
){
18628 // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
18630 // Connects specified obj/event to specified method of this object
18631 // and registers for disconnect() on widget destroy.
18633 // Provide widget-specific analog to dojo.connect, except with the
18634 // implicit use of this widget as the target object.
18635 // Events connected with `this.connect` are disconnected upon
18638 // A handle that can be passed to `disconnect` in order to disconnect before
18639 // the widget is destroyed.
18641 // | var btn = new Button();
18642 // | // when foo.bar() is called, call the listener we're going to
18643 // | // provide in the scope of btn
18644 // | btn.connect(foo, "bar", function(){
18645 // | console.debug(this.toString());
18650 return this.own(connect
.connect(obj
, event
, this, method
))[0]; // handle
18653 disconnect: function(handle
){
18655 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18657 // Disconnects handle created by `connect`.
18664 subscribe: function(t
, method
){
18666 // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
18668 // Subscribes to the specified topic and calls the specified method
18669 // of this object and registers for unsubscribe() on widget destroy.
18671 // Provide widget-specific analog to dojo.subscribe, except with the
18672 // implicit use of this widget as the target object.
18675 // method: Function
18678 // | var btn = new Button();
18679 // | // when /my/topic is published, this button changes its label to
18680 // | // be the parameter of the topic.
18681 // | btn.subscribe("/my/topic", function(v){
18682 // | this.set("label", v);
18686 return this.own(topic
.subscribe(t
, lang
.hitch(this, method
)))[0]; // handle
18689 unsubscribe: function(/*Object*/ handle
){
18691 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18693 // Unsubscribes handle created by this.subscribe.
18694 // Also removes handle from this widget's list of subscriptions
18701 isLeftToRight: function(){
18703 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
18706 return this.dir
? (this.dir
== "ltr") : domGeometry
.isBodyLtr(this.ownerDocument
); //Boolean
18709 isFocusable: function(){
18711 // Return true if this widget can currently be focused
18712 // and false if not
18713 return this.focus
&& (domStyle
.get(this.domNode
, "display") != "none");
18716 placeAt: function(/* String|DomNode|_Widget */ reference
, /* String|Int? */ position
){
18718 // Place this widget somewhere in the DOM based
18719 // on standard domConstruct.place() conventions.
18721 // A convenience function provided in all _Widgets, providing a simple
18722 // shorthand mechanism to put an existing (or newly created) Widget
18723 // somewhere in the dom, and allow chaining.
18725 // Widget, DOMNode, or id of widget or DOMNode
18727 // If reference is a widget (or id of widget), and that widget has an ".addChild" method,
18728 // it will be called passing this widget instance into that method, supplying the optional
18729 // position index passed. In this case position (if specified) should be an integer.
18731 // If reference is a DOMNode (or id matching a DOMNode but not a widget),
18732 // the position argument can be a numeric index or a string
18733 // "first", "last", "before", or "after", same as dojo/dom-construct::place().
18734 // returns: dijit/_WidgetBase
18735 // Provides a useful return of the newly created dijit._Widget instance so you
18736 // can "chain" this function by instantiating, placing, then saving the return value
18739 // | // create a Button with no srcNodeRef, and place it in the body:
18740 // | var button = new Button({ label:"click" }).placeAt(win.body());
18741 // | // now, 'button' is still the widget reference to the newly created button
18742 // | button.on("click", function(e){ console.log('click'); }));
18744 // | // create a button out of a node with id="src" and append it to id="wrapper":
18745 // | var button = new Button({},"src").placeAt("wrapper");
18747 // | // place a new button as the first element of some div
18748 // | var button = new Button({ label:"click" }).placeAt("wrapper","first");
18750 // | // create a contentpane and add it to a TabContainer
18751 // | var tc = dijit.byId("myTabs");
18752 // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
18754 var refWidget
= !reference
.tagName
&& registry
.byId(reference
);
18755 if(refWidget
&& refWidget
.addChild
&& (!position
|| typeof position
=== "number")){
18756 // Adding this to refWidget and can use refWidget.addChild() to handle everything.
18757 refWidget
.addChild(this, position
);
18759 // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
18760 // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
18761 // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
18762 var ref
= refWidget
?
18763 (refWidget
.containerNode
&& !/after|before|replace/.test(position
||"") ?
18764 refWidget
.containerNode
: refWidget
.domNode
) : dom
.byId(reference
, this.ownerDocument
);
18765 domConstruct
.place(this.domNode
, ref
, position
);
18767 // Start this iff it has a parent widget that's already started.
18768 if(!this._started
&& (this.getParent() || {})._started
){
18775 getTextDir: function(/*String*/ text
,/*String*/ originalDir
){
18777 // Return direction of the text.
18778 // The function overridden in the _BidiSupport module,
18779 // its main purpose is to calculate the direction of the
18780 // text, if was defined by the programmer through textDir.
18783 return originalDir
;
18786 applyTextDir: function(/*===== element, text =====*/){
18788 // The function overridden in the _BidiSupport module,
18789 // originally used for setting element.dir according to this.textDir.
18790 // In this case does nothing.
18791 // element: DOMNode
18797 defer: function(fcn
, delay
){
18799 // Wrapper to setTimeout to avoid deferred functions executing
18800 // after the originating widget has been destroyed.
18801 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
18802 // fcn: function reference
18803 // delay: Optional number (defaults to 0)
18806 var timer
= setTimeout(lang
.hitch(this,
18809 if(!this._destroyed
){
18810 lang
.hitch(this, fcn
)();
18816 remove: function(){
18818 clearTimeout(timer
);
18821 return null; // so this works well: handle = handle.remove();
18830 'dijit/layout/_TabContainerBase':function(){
18832 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}});
18833 define("dijit/layout/_TabContainerBase", [
18834 "dojo/text!./templates/TabContainer.html",
18835 "./StackContainer",
18836 "./utils", // marginBox2contextBox, layoutChildren
18837 "../_TemplatedMixin",
18838 "dojo/_base/declare", // declare
18839 "dojo/dom-class", // domClass.add
18840 "dojo/dom-geometry", // domGeometry.contentBox
18841 "dojo/dom-style" // domStyle.style
18842 ], function(template
, StackContainer
, layoutUtils
, _TemplatedMixin
, declare
, domClass
, domGeometry
, domStyle
){
18845 // dijit/layout/_TabContainerBase
18848 return declare("dijit.layout._TabContainerBase", [StackContainer
, _TemplatedMixin
], {
18850 // Abstract base class for TabContainer. Must define _makeController() to instantiate
18851 // and return the widget that displays the tab labels
18853 // A TabContainer is a container that has multiple panes, but shows only
18854 // one pane at a time. There are a set of tabs corresponding to each pane,
18855 // where each tab has the name (aka title) of the pane, and optionally a close button.
18857 // tabPosition: String
18858 // Defines where tabs go relative to tab content.
18859 // "top", "bottom", "left-h", "right-h"
18860 tabPosition
: "top",
18862 baseClass
: "dijitTabContainer",
18864 // tabStrip: [const] Boolean
18865 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
18866 // around the set of tabs. Not supported by claro theme.
18869 // nested: [const] Boolean
18870 // If true, use styling for a TabContainer nested inside another TabContainer.
18871 // For tundra etc., makes tabs look like links, and hides the outer
18872 // border since the outer TabContainer already has a border.
18875 templateString
: template
,
18877 postMixInProperties: function(){
18878 // set class name according to tab position, ex: dijitTabContainerTop
18879 this.baseClass
+= this.tabPosition
.charAt(0).toUpperCase() + this.tabPosition
.substr(1).replace(/-.*/, "");
18881 this.srcNodeRef
&& domStyle
.set(this.srcNodeRef
, "visibility", "hidden");
18883 this.inherited(arguments
);
18886 buildRendering: function(){
18887 this.inherited(arguments
);
18889 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
18890 this.tablist
= this._makeController(this.tablistNode
);
18892 if(!this.doLayout
){ domClass
.add(this.domNode
, "dijitTabContainerNoLayout"); }
18895 /* workaround IE's lack of support for "a > b" selectors by
18896 * tagging each node in the template.
18898 domClass
.add(this.domNode
, "dijitTabContainerNested");
18899 domClass
.add(this.tablist
.containerNode
, "dijitTabContainerTabListNested");
18900 domClass
.add(this.tablistSpacer
, "dijitTabContainerSpacerNested");
18901 domClass
.add(this.containerNode
, "dijitTabPaneWrapperNested");
18903 domClass
.add(this.domNode
, "tabStrip-" + (this.tabStrip
? "enabled" : "disabled"));
18907 _setupChild: function(/*dijit/_WidgetBase*/ tab){
18908 // Overrides StackContainer._setupChild().
18909 domClass.add(tab.domNode, "dijitTabPane");
18910 this.inherited(arguments);
18913 startup: function(){
18914 if(this._started){ return; }
18916 // wire up the tablist and its tabs
18917 this.tablist.startup();
18919 this.inherited(arguments);
18922 layout: function(){
18923 // Overrides StackContainer.layout().
18924 // Configure the content pane to take up all the space except for where the tabs are
18926 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
18928 var sc = this.selectedChildWidget;
18931 // position and size the titles and the container node
18932 var titleAlign = this.tabPosition.replace(/-h/, "");
18933 this.tablist.layoutAlign = titleAlign;
18934 var children = [this.tablist, {
18935 domNode: this.tablistSpacer,
18936 layoutAlign: titleAlign
18938 domNode: this.containerNode,
18939 layoutAlign: "client"
18941 layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
18943 // Compute size to make each of my children.
18944 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
18945 this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
18947 if(sc && sc.resize){
18948 sc.resize(this._containerContentBox);
18951 // just layout the tab controller, so it can position left/right buttons etc.
18952 if(this.tablist.resize){
18953 //make the tabs zero width so that they don't interfere with width calc, then reset
18954 var s = this.tablist.domNode.style;
18956 var width = domGeometry.getContentBox(this.domNode).w;
18958 this.tablist.resize({w: width});
18961 // and call resize() on the selected pane just to tell it that it's been made visible
18962 if(sc && sc.resize){
18968 destroy: function(){
18970 this.tablist.destroy();
18972 this.inherited(arguments);
18979 'dijit/form/Form':function(){
18980 define("dijit/form/Form", [
18981 "dojo/_base/declare", // declare
18982 "dojo/dom-attr", // domAttr.set
18983 "dojo/_base/event", // event.stop
18984 "dojo/_base/kernel", // kernel.deprecated
18985 "dojo/sniff", // has("ie")
18987 "../_TemplatedMixin",
18989 "../layout/_ContentPaneResizeMixin"
18990 ], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
18996 return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
18998 // Widget corresponding to HTML form tag, for validation and serialization
19001 // | <form data-dojo-type="dijit/form/Form" id="myForm">
19002 // | Name: <input type="text" name="name" />
19004 // | myObj = {name: "John Doe"};
19005 // | dijit.byId('myForm').set('value', myObj);
19007 // | myObj=dijit.byId('myForm').get('value');
19009 // HTML <FORM> attributes
19012 // Name of form for scripting.
19016 // Server-side form handler.
19020 // HTTP method used to submit the form, either "GET" or "POST".
19023 // encType: String?
19024 // Encoding type for the form, ex: application/x-www-form-urlencoded.
19027 // accept-charset: String?
19028 // List of supported charsets.
19029 "accept-charset": "",
19032 // List of MIME types for file upload.
19036 // Target frame for the document to be opened in.
19039 templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
19041 postMixInProperties: function(){
19042 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
19043 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
19044 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
19045 this.inherited(arguments);
19048 execute: function(/*Object*/ /*===== formContents =====*/){
19050 // Deprecated: use submit()
19055 onExecute: function(){
19057 // Deprecated: use onSubmit()
19062 _setEncTypeAttr: function(/*String*/ value
){
19063 this.encType
= value
;
19064 domAttr
.set(this.domNode
, "encType", value
);
19065 if(has("ie")){ this.domNode
.encoding
= value
; }
19068 reset: function(/*Event?*/ e
){
19070 // restores all widget values back to their init values,
19071 // calls onReset() which can cancel the reset by returning false
19073 // create fake event so we can know if preventDefault() is called
19075 returnValue
: true, // the IE way
19076 preventDefault: function(){ // not IE
19077 this.returnValue
= false;
19079 stopPropagation: function(){},
19080 currentTarget
: e
? e
.target
: this.domNode
,
19081 target
: e
? e
.target
: this.domNode
19083 // if return value is not exactly false, and haven't called preventDefault(), then reset
19084 if(!(this.onReset(faux
) === false) && faux
.returnValue
){
19085 this.inherited(arguments
, []);
19089 onReset: function(/*Event?*/ /*===== e =====*/){
19091 // Callback when user resets the form. This method is intended
19092 // to be over-ridden. When the `reset` method is called
19093 // programmatically, the return value from `onReset` is used
19094 // to compute whether or not resetting should proceed
19097 return true; // Boolean
19100 _onReset: function(e
){
19106 _onSubmit: function(e
){
19107 var fp
= this.constructor.prototype;
19108 // TODO: remove this if statement beginning with 2.0
19109 if(this.execute
!= fp
.execute
|| this.onExecute
!= fp
.onExecute
){
19110 kernel
.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
19112 this.execute(this.getValues());
19114 if(this.onSubmit(e
) === false){ // only exactly false stops submit
19119 onSubmit: function(/*Event?*/ /*===== e =====*/){
19121 // Callback when user submits the form.
19123 // This method is intended to be over-ridden, but by default it checks and
19124 // returns the validity of form elements. When the `submit`
19125 // method is called programmatically, the return value from
19126 // `onSubmit` is used to compute whether or not submission
19131 return this.isValid(); // Boolean
19134 submit: function(){
19136 // programmatically submit form if and only if the `onSubmit` returns true
19137 if(!(this.onSubmit() === false)){
19138 this.containerNode
.submit();
19145 'dojo/store/Memory':function(){
19146 define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
19147 function(declare
, QueryResults
, SimpleQueryEngine
/*=====, Store =====*/){
19150 // dojo/store/Memory
19152 // No base class, but for purposes of documentation, the base class is dojo/store/api/Store
19154 /*===== base = Store; =====*/
19156 return declare("dojo.store.Memory", base
, {
19158 // This is a basic in-memory object store. It implements dojo/store/api/Store.
19159 constructor: function(options
){
19161 // Creates a memory object store.
19162 // options: dojo/store/Memory
19163 // This provides any configuration information that will be mixed into the store.
19164 // This should generally include the data property to provide the starting set of data.
19165 for(var i
in options
){
19166 this[i
] = options
[i
];
19168 this.setData(this.data
|| []);
19171 // The array of all the objects in the memory store
19174 // idProperty: String
19175 // Indicates the property to use as the identity property. The values of this
19176 // property should be unique.
19180 // An index of data indices into the data array by id
19183 // queryEngine: Function
19184 // Defines the query engine to use for querying the data store
19185 queryEngine
: SimpleQueryEngine
,
19188 // Retrieves an object by its identity
19190 // The identity to use to lookup the object
19192 // The object in the store that matches the given id.
19193 return this.data
[this.index
[id
]];
19195 getIdentity: function(object
){
19197 // Returns an object's identity
19199 // The object to get the identity from
19201 return object
[this.idProperty
];
19203 put: function(object
, options
){
19205 // Stores an object
19207 // The object to store.
19208 // options: dojo/store/api/Store.PutDirectives?
19209 // Additional metadata for storing the data. Includes an "id"
19210 // property if a specific id is to be used.
19212 var data
= this.data
,
19213 index
= this.index
,
19214 idProperty
= this.idProperty
;
19215 var id
= object
[idProperty
] = (options
&& "id" in options
) ? options
.id
: idProperty
in object
? object
[idProperty
] : Math
.random();
19218 if(options
&& options
.overwrite
=== false){
19219 throw new Error("Object already exists");
19221 // replace the entry in data
19222 data
[index
[id
]] = object
;
19224 // add the new object
19225 index
[id
] = data
.push(object
) - 1;
19229 add: function(object
, options
){
19231 // Creates an object, throws an error if the object already exists
19233 // The object to store.
19234 // options: dojo/store/api/Store.PutDirectives?
19235 // Additional metadata for storing the data. Includes an "id"
19236 // property if a specific id is to be used.
19238 (options
= options
|| {}).overwrite
= false;
19239 // call put with overwrite being false
19240 return this.put(object
, options
);
19242 remove: function(id
){
19244 // Deletes an object by its identity
19246 // The identity to use to delete the object
19247 // returns: Boolean
19248 // Returns true if an object was removed, falsy (undefined) if no object matched the id
19249 var index
= this.index
;
19250 var data
= this.data
;
19252 data
.splice(index
[id
], 1);
19253 // now we have to reindex
19254 this.setData(data
);
19258 query: function(query
, options
){
19260 // Queries the store for objects.
19262 // The query to use for retrieving objects from the store.
19263 // options: dojo/store/api/Store.QueryOptions?
19264 // The optional arguments to apply to the resultset.
19265 // returns: dojo/store/api/Store.QueryResults
19266 // The results of the query, extended with iterative methods.
19269 // Given the following store:
19271 // | var store = new Memory({
19273 // | {id: 1, name: "one", prime: false },
19274 // | {id: 2, name: "two", even: true, prime: true},
19275 // | {id: 3, name: "three", prime: true},
19276 // | {id: 4, name: "four", even: true, prime: false},
19277 // | {id: 5, name: "five", prime: true}
19281 // ...find all items where "prime" is true:
19283 // | var results = store.query({ prime: true });
19285 // ...or find all items where "even" is true:
19287 // | var results = store.query({ even: true });
19288 return QueryResults(this.queryEngine(query
, options
)(this.data
));
19290 setData: function(data
){
19292 // Sets the given data as the source for this store, and indexes it
19294 // An array of objects to use as the source of data.
19296 // just for convenience with the data format IFRS expects
19297 this.idProperty
= data
.identifier
;
19298 data
= this.data
= data
.items
;
19303 for(var i
= 0, l
= data
.length
; i
< l
; i
++){
19304 this.index
[data
[i
][this.idProperty
]] = i
;
19312 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n",
19313 'dijit/_base/sniff':function(){
19314 define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
19317 // dijit/_base/sniff
19322 // Deprecated, back compatibility module, new code should require dojo/uacss directly instead of this module.
19328 'dijit/Toolbar':function(){
19329 define("dijit/Toolbar", [
19331 "dojo/_base/declare", // declare
19333 "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
19336 "./_KeyNavContainer",
19337 "./_TemplatedMixin"
19338 ], function(require
, declare
, has
, keys
, ready
, _Widget
, _KeyNavContainer
, _TemplatedMixin
){
19344 // Back compat w/1.6, remove for 2.0
19345 if(has("dijit-legacy-requires")){
19346 ready(0, function(){
19347 var requires
= ["dijit/ToolbarSeparator"];
19348 require(requires
); // use indirection so modules not rolled into a build
19352 return declare("dijit.Toolbar", [_Widget
, _TemplatedMixin
, _KeyNavContainer
], {
19354 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
19357 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
19360 baseClass
: "dijitToolbar",
19362 postCreate: function(){
19363 this.inherited(arguments
);
19365 this.connectKeyNavHandlers(
19366 this.isLeftToRight() ? [keys
.LEFT_ARROW
] : [keys
.RIGHT_ARROW
],
19367 this.isLeftToRight() ? [keys
.RIGHT_ARROW
] : [keys
.LEFT_ARROW
]
19374 'dijit/layout/StackContainer':function(){
19375 define("dijit/layout/StackContainer", [
19376 "dojo/_base/array", // array.forEach array.indexOf array.some
19377 "dojo/cookie", // cookie
19378 "dojo/_base/declare", // declare
19379 "dojo/dom-class", // domClass.add domClass.replace
19380 "dojo/has", // has("dijit-legacy-requires")
19381 "dojo/_base/lang", // lang.extend
19383 "dojo/topic", // publish
19384 "../registry", // registry.byId
19387 "dojo/i18n!../nls/common"
19388 ], function(array
, cookie
, declare
, domClass
, has
, lang
, ready
, topic
,
19389 registry
, _WidgetBase
, _LayoutWidget
){
19392 // dijit/layout/StackContainer
19394 // Back compat w/1.6, remove for 2.0
19395 if(has("dijit-legacy-requires")){
19396 ready(0, function(){
19397 var requires
= ["dijit/layout/StackController"];
19398 require(requires
); // use indirection so modules not rolled into a build
19402 var StackContainer
= declare("dijit.layout.StackContainer", _LayoutWidget
, {
19404 // A container that has multiple children, but shows only
19405 // one child at a time
19408 // A container for widgets (ContentPanes, for example) That displays
19409 // only one Widget at a time.
19411 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
19413 // Can be base class for container, Wizard, Show, etc.
19415 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
19416 // children of a `StackContainer`.
19418 // doLayout: Boolean
19419 // If true, change the size of my currently displayed child to match my size
19422 // persist: Boolean
19423 // Remembers the selected child across sessions
19426 baseClass
: "dijitStackContainer",
19429 // selectedChildWidget: [readonly] dijit._Widget
19430 // References the currently selected child widget, if any.
19431 // Adjust selected child with selectChild() method.
19432 selectedChildWidget: null,
19435 buildRendering: function(){
19436 this.inherited(arguments
);
19437 domClass
.add(this.domNode
, "dijitLayoutContainer");
19438 this.containerNode
.setAttribute("role", "tabpanel");
19441 postCreate: function(){
19442 this.inherited(arguments
);
19443 this.connect(this.domNode
, "onkeypress", this._onKeyPress
);
19446 startup: function(){
19447 if(this._started
){ return; }
19449 var children
= this.getChildren();
19451 // Setup each page panel to be initially hidden
19452 array
.forEach(children
, this._setupChild
, this);
19454 // Figure out which child to initially display, defaulting to first one
19456 this.selectedChildWidget
= registry
.byId(cookie(this.id
+ "_selectedChild"));
19458 array
.some(children
, function(child
){
19459 if(child
.selected
){
19460 this.selectedChildWidget
= child
;
19462 return child
.selected
;
19465 var selected
= this.selectedChildWidget
;
19466 if(!selected
&& children
[0]){
19467 selected
= this.selectedChildWidget
= children
[0];
19468 selected
.selected
= true;
19471 // Publish information about myself so any StackControllers can initialize.
19472 // This needs to happen before this.inherited(arguments) so that for
19473 // TabContainer, this._contentBox doesn't include the space for the tab labels.
19474 topic
.publish(this.id
+"-startup", {children
: children
, selected
: selected
});
19476 // Startup each child widget, and do initial layout like setting this._contentBox,
19477 // then calls this.resize() which does the initial sizing on the selected child.
19478 this.inherited(arguments
);
19481 resize: function(){
19482 // Overrides _LayoutWidget.resize()
19483 // Resize is called when we are first made visible (it's called from startup()
19484 // if we are initially visible). If this is the first time we've been made
19485 // visible then show our first child.
19486 if(!this._hasBeenShown
){
19487 this._hasBeenShown
= true;
19488 var selected
= this.selectedChildWidget
;
19490 this._showChild(selected
);
19493 this.inherited(arguments
);
19496 _setupChild: function(/*dijit/_WidgetBase*/ child){
19497 // Overrides _LayoutWidget._setupChild()
19499 this.inherited(arguments);
19501 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
19503 // remove the title attribute so it doesn't show up when i hover
19505 child.domNode.title = "";
19508 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
19509 // Overrides _Container.addChild() to do layout and publish events
19511 this.inherited(arguments
);
19514 topic
.publish(this.id
+"-addChild", child
, insertIndex
); // publish
19516 // in case the tab titles have overflowed from one line to two lines
19517 // (or, if this if first child, from zero lines to one line)
19518 // TODO: w/ScrollingTabController this is no longer necessary, although
19519 // ScrollTabController.resize() does need to get called to show/hide
19520 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
19521 // If this is updated to not layout [except for initial child added / last child removed], update
19522 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
19525 // if this is the first child, then select it
19526 if(!this.selectedChildWidget
){
19527 this.selectChild(child
);
19532 removeChild: function(/*dijit/_WidgetBase*/ page){
19533 // Overrides _Container.removeChild() to do layout and publish events
19535 this.inherited(arguments);
19538 // this will notify any tablists to remove a button; do this first because it may affect sizing
19539 topic.publish(this.id + "-removeChild", page); // publish
19542 // If all our children are being destroyed than don't run the code below (to select another page),
19543 // because we are deleting every page one by one
19544 if(this._descendantsBeingDestroyed){ return; }
19546 // Select new page to display, also updating TabController to show the respective tab.
19547 // Do this before layout call because it can affect the height of the TabController.
19548 if(this.selectedChildWidget === page){
19549 this.selectedChildWidget = undefined;
19551 var children = this.getChildren();
19552 if(children.length){
19553 this.selectChild(children[0]);
19559 // In case the tab titles now take up one line instead of two lines
19560 // (note though that ScrollingTabController never overflows to multiple lines),
19561 // or the height has changed slightly because of addition/removal of tab which close icon
19566 selectChild: function(/*dijit/_WidgetBase|String*/ page, /*Boolean*/ animate
){
19568 // Show the given widget (which must be one of my children)
19570 // Reference to child widget or id of child widget
19572 page
= registry
.byId(page
);
19574 if(this.selectedChildWidget
!= page
){
19575 // Deselect old page and select new one
19576 var d
= this._transition(page
, this.selectedChildWidget
, animate
);
19577 this._set("selectedChildWidget", page
);
19578 topic
.publish(this.id
+"-selectChild", page
); // publish
19581 cookie(this.id
+ "_selectedChild", this.selectedChildWidget
.id
);
19585 return d
; // If child has an href, promise that fires when the child's href finishes loading
19588 _transition: function(newWidget
, oldWidget
/*===== , animate =====*/){
19590 // Hide the old widget and display the new widget.
19591 // Subclasses should override this.
19592 // newWidget: dijit/_WidgetBase
19593 // The newly selected widget.
19594 // oldWidget: dijit/_WidgetBase
19595 // The previously selected widget.
19596 // animate: Boolean
19597 // Used by AccordionContainer to turn on/off slide effect.
19599 // protected extension
19601 this._hideChild(oldWidget
);
19603 var d
= this._showChild(newWidget
);
19605 // Size the new widget, in case this is the first time it's being shown,
19606 // or I have been resized since the last time it was shown.
19607 // Note that page must be visible for resizing to work.
19608 if(newWidget
.resize
){
19610 newWidget
.resize(this._containerContentBox
|| this._contentBox
);
19612 // the child should pick it's own size but we still need to call resize()
19613 // (with no arguments) to let the widget lay itself out
19614 newWidget
.resize();
19618 return d
; // If child has an href, promise that fires when the child's href finishes loading
19621 _adjacent: function(/*Boolean*/ forward
){
19623 // Gets the next/previous child widget in this container from the current selection.
19625 // TODO: remove for 2.0 if this isn't being used. Otherwise, fix to skip disabled tabs.
19627 var children
= this.getChildren();
19628 var index
= array
.indexOf(children
, this.selectedChildWidget
);
19629 index
+= forward
? 1 : children
.length
- 1;
19630 return children
[ index
% children
.length
]; // dijit/_WidgetBase
19633 forward: function(){
19635 // Advance to next page.
19636 return this.selectChild(this._adjacent(true), true);
19641 // Go back to previous page.
19642 return this.selectChild(this._adjacent(false), true);
19645 _onKeyPress: function(e
){
19646 topic
.publish(this.id
+"-containerKeyPress", { e
: e
, page
: this}); // publish
19649 layout: function(){
19650 // Implement _LayoutWidget.layout() virtual method.
19651 var child
= this.selectedChildWidget
;
19652 if(child
&& child
.resize
){
19654 child
.resize(this._containerContentBox
|| this._contentBox
);
19661 _showChild: function(/*dijit/_WidgetBase*/ page){
19663 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
19664 // it can do any updates it needs regarding loading href's etc.
19666 // Promise that fires when page has finished showing, or true if there's no href
19667 var children = this.getChildren();
19668 page.isFirstChild = (page == children[0]);
19669 page.isLastChild = (page == children[children.length-1]);
19670 page._set("selected", true);
19672 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
19674 return (page._onShow && page._onShow()) || true;
19677 _hideChild: function(/*dijit/_WidgetBase*/ page){
19679 // Hide the specified child by changing it's CSS, and call _onHide() so
19681 page._set("selected", false);
19682 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
19684 page.onHide && page.onHide();
19687 closeChild: function(/*dijit/_WidgetBase*/ page){
19689 // Callback when user clicks the [X] to remove a page.
19690 // If onClose() returns true then remove and destroy the child.
19693 var remove = page.onClose(this, page);
19695 this.removeChild(page);
19696 // makes sure we can clean up executeScripts in ContentPane onUnLoad
19697 page.destroyRecursive();
19701 destroyDescendants: function(/*Boolean*/ preserveDom
){
19702 this._descendantsBeingDestroyed
= true;
19703 this.selectedChildWidget
= undefined;
19704 array
.forEach(this.getChildren(), function(child
){
19706 this.removeChild(child
);
19708 child
.destroyRecursive(preserveDom
);
19710 this._descendantsBeingDestroyed
= false;
19714 StackContainer
.ChildWidgetProperties
= {
19716 // These properties can be specified for the children of a StackContainer.
19718 // selected: Boolean
19719 // Specifies that this widget should be the initially displayed pane.
19720 // Note: to change the selected child use `dijit/layout/StackContainer.selectChild`
19723 // disabled: Boolean
19724 // Specifies that the button to select this pane should be disabled.
19725 // Doesn't affect programmatic selection of the pane, nor does it deselect the pane if it is currently selected.
19728 // closable: Boolean
19729 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
19732 // iconClass: String
19733 // CSS Class specifying icon to use in label associated with this pane.
19734 iconClass
: "dijitNoIcon",
19736 // showTitle: Boolean
19737 // When true, display title of this widget as tab label etc., rather than just using
19738 // icon specified in iconClass
19742 // Since any widget can be specified as a StackContainer child, mix them
19743 // into the base widget class. (This is a hack, but it's effective.)
19744 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
19745 lang
.extend(_WidgetBase
, /*===== {} || =====*/ StackContainer
.ChildWidgetProperties
);
19747 return StackContainer
;
19751 'dojo/regexp':function(){
19752 define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo
, lang
){
19759 // Regular expressions and Builder resources
19761 lang
.setObject("dojo.regexp", regexp
);
19763 regexp
.escapeString = function(/*String*/str
, /*String?*/except
){
19765 // Adds escape sequences for special characters in regular expressions
19767 // a String with special characters to be left unescaped
19769 return str
.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch
){
19770 if(except
&& except
.indexOf(ch
) != -1){
19777 regexp
.buildGroupRE = function(/*Object|Array*/arr
, /*Function*/re
, /*Boolean?*/nonCapture
){
19779 // Builds a regular expression that groups subexpressions
19781 // A utility function used by some of the RE generators. The
19782 // subexpressions are constructed by the function, re, in the second
19783 // parameter. re builds one subexpression for each elem in the array
19784 // a, in the first parameter. Returns a string for a regular
19785 // expression that groups all the subexpressions.
19787 // A single value or an array of values.
19789 // A function. Takes one parameter and converts it to a regular
19792 // If true, uses non-capturing match, otherwise matches are retained
19793 // by regular expression. Defaults to false
19795 // case 1: a is a single value.
19796 if(!(arr
instanceof Array
)){
19797 return re(arr
); // String
19800 // case 2: a is an array
19802 for(var i
= 0; i
< arr
.length
; i
++){
19803 // convert each elem to a RE
19804 b
.push(re(arr
[i
]));
19807 // join the REs as alternatives in a RE group.
19808 return regexp
.group(b
.join("|"), nonCapture
); // String
19811 regexp
.group = function(/*String*/expression
, /*Boolean?*/nonCapture
){
19813 // adds group match to expression
19815 // If true, uses non-capturing match, otherwise matches are retained
19816 // by regular expression.
19817 return "(" + (nonCapture
? "?:":"") + expression
+ ")"; // String
19824 'dijit/DropDownMenu':function(){
19826 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
19827 define("dijit/DropDownMenu", [
19828 "dojo/_base/declare", // declare
19829 "dojo/_base/event", // event.stop
19830 "dojo/keys", // keys
19831 "dojo/text!./templates/Menu.html",
19832 "./_OnDijitClickMixin",
19834 ], function(declare
, event
, keys
, template
, _OnDijitClickMixin
, _MenuBase
){
19837 // dijit/DropDownMenu
19839 return declare("dijit.DropDownMenu", [_MenuBase
, _OnDijitClickMixin
], {
19841 // A menu, without features for context menu (Meaning, drop down menu)
19843 templateString
: template
,
19845 baseClass
: "dijitMenu",
19847 postCreate: function(){
19848 this.inherited(arguments
);
19849 var l
= this.isLeftToRight();
19850 this._openSubMenuKey
= l
? keys
.RIGHT_ARROW
: keys
.LEFT_ARROW
;
19851 this._closeSubMenuKey
= l
? keys
.LEFT_ARROW
: keys
.RIGHT_ARROW
;
19852 this.connectKeyNavHandlers([keys
.UP_ARROW
], [keys
.DOWN_ARROW
]);
19855 _onKeyPress: function(/*Event*/ evt
){
19857 // Handle keyboard based menu navigation.
19861 if(evt
.ctrlKey
|| evt
.altKey
){ return; }
19863 switch(evt
.charOrCode
){
19864 case this._openSubMenuKey
:
19865 this._moveToPopup(evt
);
19868 case this._closeSubMenuKey
:
19869 if(this.parentMenu
){
19870 if(this.parentMenu
._isMenuBar
){
19871 this.parentMenu
.focusPrev();
19873 this.onCancel(false);
19885 'dijit/form/_FormMixin':function(){
19886 define("dijit/form/_FormMixin", [
19887 "dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
19888 "dojo/_base/declare", // declare
19889 "dojo/_base/kernel", // kernel.deprecated
19890 "dojo/_base/lang", // lang.hitch lang.isArray
19892 "dojo/window" // winUtils.scrollIntoView
19893 ], function(array
, declare
, kernel
, lang
, on
, winUtils
){
19896 // dijit/form/_FormMixin
19898 return declare("dijit.form._FormMixin", null, {
19900 // Mixin for containers of form widgets (i.e. widgets that represent a single value
19901 // and can be children of a `<form>` node or `dijit/form/Form` widget)
19903 // Can extract all the form widgets
19904 // values and combine them into a single javascript object, or alternately
19905 // take such an object and set the values for all the contained
19910 // Name/value hash for each child widget with a name and value.
19911 // Child widgets without names are not part of the hash.
19913 // If there are multiple child widgets w/the same name, value is an array,
19914 // unless they are radio buttons in which case value is a scalar (since only
19915 // one radio button can be checked at a time).
19917 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
19920 // | { name: "John Smith", interests: ["sports", "movies"] }
19923 // state: [readonly] String
19924 // Will be "Error" if one or more of the child widgets has an invalid value,
19925 // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
19926 // which indicates that the form is ready to be submitted.
19931 // * better handling for arrays. Often form elements have names with [] like
19932 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
19935 _getDescendantFormWidgets: function(/*dijit/_WidgetBase[]?*/ children){
19937 // Returns all form widget descendants, searching through non-form child widgets like BorderContainer
19939 array.forEach(children || this.getChildren(), function(child){
19940 if("value" in child){
19943 res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
19950 array.forEach(this._getDescendantFormWidgets(), function(widget){
19957 validate: function(){
19959 // returns if the form is valid - same as isValid - but
19960 // provides a few additional (ui-specific) features:
19962 // 1. it will highlight any sub-widgets that are not valid
19963 // 2. it will call focus() on the first invalid sub-widget
19964 var didFocus = false;
19965 return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
19966 // Need to set this so that "required" widgets get their
19968 widget._hasBeenBlurred = true;
19969 var valid = widget.disabled || !widget.validate || widget.validate();
19970 if(!valid && !didFocus){
19971 // Set focus of the first non-valid widget
19972 winUtils.scrollIntoView(widget.containerNode || widget.domNode);
19977 }), function(item){ return item; });
19980 setValues: function(val){
19981 kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
19982 return this.set('value', val);
19984 _setValueAttr: function(/*Object*/ obj
){
19986 // Fill in form values from according to an Object (in the format returned by get('value'))
19988 // generate map from name --> [list of widgets with that name]
19990 array
.forEach(this._getDescendantFormWidgets(), function(widget
){
19991 if(!widget
.name
){ return; }
19992 var entry
= map
[widget
.name
] || (map
[widget
.name
] = [] );
19993 entry
.push(widget
);
19996 for(var name
in map
){
19997 if(!map
.hasOwnProperty(name
)){
20000 var widgets
= map
[name
], // array of widgets w/this name
20001 values
= lang
.getObject(name
, false, obj
); // list of values for those widgets
20003 if(values
=== undefined){
20006 if(!lang
.isArray(values
)){
20007 values
= [ values
];
20009 if(typeof widgets
[0].checked
== 'boolean'){
20010 // for checkbox/radio, values is a list of which widgets should be checked
20011 array
.forEach(widgets
, function(w
){
20012 w
.set('value', array
.indexOf(values
, w
.value
) != -1);
20014 }else if(widgets
[0].multiple
){
20015 // it takes an array (e.g. multi-select)
20016 widgets
[0].set('value', values
);
20018 // otherwise, values is a list of values to be assigned sequentially to each widget
20019 array
.forEach(widgets
, function(w
, i
){
20020 w
.set('value', values
[i
]);
20026 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
20028 array.forEach(this.containerNode.elements, function(element){
20029 if(element.name == ''){return}; // like "continue"
20030 var namePath = element.name.split(".");
20032 var name=namePath[namePath.length-1];
20033 for(var j=1,len2=namePath.length;j<len2;++j){
20034 var p=namePath[j - 1];
20035 // repeater support block
20036 var nameA=p.split("[");
20037 if(nameA.length > 1){
20038 if(typeof(myObj[nameA[0]]) == "undefined"){
20039 myObj[nameA[0]]=[ ];
20042 nameIndex=parseInt(nameA[1]);
20043 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
20044 myObj[nameA[0]][nameIndex] = { };
20046 myObj=myObj[nameA[0]][nameIndex];
20048 } // repeater support ends
20050 if(typeof(myObj[p]) == "undefined"){
20057 if(typeof(myObj) == "undefined"){
20058 return; // like "continue"
20060 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
20061 return; // like "continue"
20064 // TODO: widget values (just call set('value', ...) on the widget)
20066 // TODO: maybe should call dojo.getNodeProp() instead
20067 switch(element.type){
20069 element.checked = (name in myObj) &&
20070 array.some(myObj[name], function(val){ return val == element.value; });
20073 element.checked = (name in myObj) && myObj[name] == element.value;
20075 case "select-multiple":
20076 element.selectedIndex=-1;
20077 array.forEach(element.options, function(option){
20078 option.selected = array.some(myObj[name], function(val){ return option.value == val; });
20082 element.selectedIndex="0";
20083 array.forEach(element.options, function(option){
20084 option.selected = option.value == myObj[name];
20091 element.value = myObj[name] || "";
20097 // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
20098 // which I am monitoring.
20101 getValues: function(){
20102 kernel
.deprecated(this.declaredClass
+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
20103 return this.get('value');
20105 _getValueAttr: function(){
20107 // Returns Object representing form values. See description of `value` for details.
20110 // The value is updated into this.value every time a child has an onChange event,
20111 // so in the common case this function could just return this.value. However,
20112 // that wouldn't work when:
20114 // 1. User presses return key to submit a form. That doesn't fire an onchange event,
20115 // and even if it did it would come too late due to the defer(...) in _handleOnChange()
20117 // 2. app for some reason calls this.get("value") while the user is typing into a
20118 // form field. Not sure if that case needs to be supported or not.
20120 // get widget values
20122 array
.forEach(this._getDescendantFormWidgets(), function(widget
){
20123 var name
= widget
.name
;
20124 if(!name
|| widget
.disabled
){ return; }
20126 // Single value widget (checkbox, radio, or plain <input> type widget)
20127 var value
= widget
.get('value');
20129 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
20130 if(typeof widget
.checked
== 'boolean'){
20131 if(/Radio/.test(widget
.declaredClass
)){
20133 if(value
!== false){
20134 lang
.setObject(name
, value
, obj
);
20136 // give radio widgets a default of null
20137 value
= lang
.getObject(name
, false, obj
);
20138 if(value
=== undefined){
20139 lang
.setObject(name
, null, obj
);
20143 // checkbox/toggle button
20144 var ary
=lang
.getObject(name
, false, obj
);
20147 lang
.setObject(name
, ary
, obj
);
20149 if(value
!== false){
20154 var prev
=lang
.getObject(name
, false, obj
);
20155 if(typeof prev
!= "undefined"){
20156 if(lang
.isArray(prev
)){
20159 lang
.setObject(name
, [prev
, value
], obj
);
20163 lang
.setObject(name
, value
, obj
);
20169 * code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
20170 * but it doesn't understand [] notation, presumably)
20172 array.forEach(this.containerNode.elements, function(elm){
20174 return; // like "continue"
20176 var namePath = elm.name.split(".");
20178 var name=namePath[namePath.length-1];
20179 for(var j=1,len2=namePath.length;j<len2;++j){
20180 var nameIndex = null;
20181 var p=namePath[j - 1];
20182 var nameA=p.split("[");
20183 if(nameA.length > 1){
20184 if(typeof(myObj[nameA[0]]) == "undefined"){
20185 myObj[nameA[0]]=[ ];
20187 nameIndex=parseInt(nameA[1]);
20188 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
20189 myObj[nameA[0]][nameIndex] = { };
20191 }else if(typeof(myObj[nameA[0]]) == "undefined"){
20192 myObj[nameA[0]] = { }
20195 if(nameA.length == 1){
20196 myObj=myObj[nameA[0]];
20198 myObj=myObj[nameA[0]][nameIndex];
20202 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
20203 if(name == name.split("[")[0]){
20204 myObj[name]=elm.value;
20206 // can not set value when there is no name
20208 }else if(elm.type == "checkbox" && elm.checked){
20209 if(typeof(myObj[name]) == 'undefined'){
20212 myObj[name].push(elm.value);
20213 }else if(elm.type == "select-multiple"){
20214 if(typeof(myObj[name]) == 'undefined'){
20217 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
20218 if(elm.options[jdx].selected){
20219 myObj[name].push(elm.options[jdx].value);
20229 isValid: function(){
20231 // Returns true if all of the widgets are valid.
20232 // Deprecated, will be removed in 2.0. Use get("state") instead.
20234 return this.state
== "";
20237 onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
20239 // Stub function to connect to if you want to do something
20240 // (like disable/enable a submit button) when the valid
20241 // state changes on the form as a whole.
20243 // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
20246 _getState: function(){
20248 // Compute what this.state should be based on state of children
20249 var states
= array
.map(this._descendants
, function(w
){
20250 return w
.get("state") || "";
20253 return array
.indexOf(states
, "Error") >= 0 ? "Error" :
20254 array
.indexOf(states
, "Incomplete") >= 0 ? "Incomplete" : "";
20257 disconnectChildren: function(){
20259 // Deprecated method. Applications no longer need to call this. Remove for 2.0.
20262 connectChildren: function(/*Boolean*/ inStartup
){
20264 // You can call this function directly, ex. in the event that you
20265 // programmatically add a widget to the form *after* the form has been
20268 // TODO: rename for 2.0
20270 this._descendants
= this._getDescendantFormWidgets();
20272 // To get notifications from children they need to be started. Children didn't used to need to be started,
20273 // so for back-compat, start them here
20274 array
.forEach(this._descendants
, function(child
){
20275 if(!child
._started
){ child
.startup(); }
20279 this._onChildChange();
20283 _onChildChange: function(/*String*/ attr
){
20285 // Called when child's value or disabled state changes
20287 // The unit tests expect state update to be synchronous, so update it immediately.
20288 if(!attr
|| attr
== "state" || attr
== "disabled"){
20289 this._set("state", this._getState());
20292 // Use defer() to collapse value changes in multiple children into a single
20293 // update to my value. Multiple updates will occur on:
20296 // 3. user selecting a radio button (which will de-select another radio button,
20297 // causing two onChange events)
20298 if(!attr
|| attr
== "value" || attr
== "disabled" || attr
== "checked"){
20299 if(this._onChangeDelayTimer
){
20300 this._onChangeDelayTimer
.remove();
20302 this._onChangeDelayTimer
= this.defer(function(){
20303 delete this._onChangeDelayTimer
;
20304 this._set("value", this.get("value"));
20309 startup: function(){
20310 this.inherited(arguments
);
20312 // Set initial this.value and this.state. Don't emit watch() notifications.
20313 this._descendants
= this._getDescendantFormWidgets();
20314 this.value
= this.get("value");
20315 this.state
= this._getState();
20317 // Initialize value and valid/invalid state tracking.
20321 this.containerNode
,
20322 "attrmodified-state, attrmodified-disabled, attrmodified-value, attrmodified-checked",
20324 if(evt
.target
== self
.domNode
){
20325 return; // ignore events that I fire on myself because my children changed
20327 self
._onChildChange(evt
.type
.replace("attrmodified-", ""));
20332 // Make state change call onValidStateChange(), will be removed in 2.0
20333 this.watch("state", function(attr
, oldVal
, newVal
){ this.onValidStateChange(newVal
== ""); });
20336 destroy: function(){
20337 this.inherited(arguments
);
20344 'dojo/data/util/simpleFetch':function(){
20345 define("dojo/data/util/simpleFetch", ["../../_base/lang", "../../_base/kernel", "./sorter"],
20346 function(lang
, kernel
, sorter
){
20348 // dojo/data/util/simpleFetch
20350 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20351 // be mixed into other datastore implementations to accelerate their development.
20353 var simpleFetch
= {};
20354 lang
.setObject("dojo.data.util.simpleFetch", simpleFetch
);
20356 simpleFetch
.errorHandler = function(/*Object*/ errorData
, /*Object*/ requestObject
){
20358 // The error handler when there is an error fetching items. This function should not be called
20359 // directly and is used by simpleFetch.fetch().
20360 if(requestObject
.onError
){
20361 var scope
= requestObject
.scope
|| kernel
.global
;
20362 requestObject
.onError
.call(scope
, errorData
, requestObject
);
20366 simpleFetch
.fetchHandler = function(/*Array*/ items
, /*Object*/ requestObject
){
20368 // The handler when items are sucessfully fetched. This function should not be called directly
20369 // and is used by simpleFetch.fetch().
20370 var oldAbortFunction
= requestObject
.abort
|| null,
20373 startIndex
= requestObject
.start
?requestObject
.start
: 0,
20374 endIndex
= (requestObject
.count
&& (requestObject
.count
!== Infinity
))?(startIndex
+ requestObject
.count
):items
.length
;
20376 requestObject
.abort = function(){
20378 if(oldAbortFunction
){
20379 oldAbortFunction
.call(requestObject
);
20383 var scope
= requestObject
.scope
|| kernel
.global
;
20384 if(!requestObject
.store
){
20385 requestObject
.store
= this;
20387 if(requestObject
.onBegin
){
20388 requestObject
.onBegin
.call(scope
, items
.length
, requestObject
);
20390 if(requestObject
.sort
){
20391 items
.sort(sorter
.createSortFunction(requestObject
.sort
, this));
20393 if(requestObject
.onItem
){
20394 for(var i
= startIndex
; (i
< items
.length
) && (i
< endIndex
); ++i
){
20395 var item
= items
[i
];
20397 requestObject
.onItem
.call(scope
, item
, requestObject
);
20401 if(requestObject
.onComplete
&& !aborted
){
20403 if(!requestObject
.onItem
){
20404 subset
= items
.slice(startIndex
, endIndex
);
20406 requestObject
.onComplete
.call(scope
, subset
, requestObject
);
20410 simpleFetch
.fetch = function(/* Object? */ request
){
20412 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20413 // be mixed into other datastore implementations to accelerate their development.
20415 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
20416 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
20417 // is not designed to work for datastores that respond to a fetch() call by incrementally
20418 // loading items, or sequentially loading partial batches of the result
20419 // set. For datastores that mixin simpleFetch, simpleFetch
20420 // implements a fetch method that automatically handles eight of the fetch()
20421 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
20422 // The class mixing in simpleFetch should not implement fetch(),
20423 // but should instead implement a _fetchItems() method. The _fetchItems()
20424 // method takes three arguments, the keywordArgs object that was passed
20425 // to fetch(), a callback function to be called when the result array is
20426 // available, and an error callback to be called if something goes wrong.
20427 // The _fetchItems() method should ignore any keywordArgs parameters for
20428 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
20429 // The _fetchItems() method needs to correctly handle any other keywordArgs
20430 // parameters, including the query parameter and any optional parameters
20431 // (such as includeChildren). The _fetchItems() method should create an array of
20432 // result items and pass it to the fetchHandler along with the original request object --
20433 // or, the _fetchItems() method may, if it wants to, create an new request object
20434 // with other specifics about the request that are specific to the datastore and pass
20435 // that as the request object to the handler.
20437 // For more information on this specific function, see dojo/data/api/Read.fetch()
20440 // The keywordArgs parameter may either be an instance of
20441 // conforming to dojo/data/api/Request or may be a simple anonymous object
20442 // that may contain any of the following:
20444 // | query: query-object or query-string,
20445 // | queryOptions: object,
20446 // | onBegin: Function,
20447 // | onItem: Function,
20448 // | onComplete: Function,
20449 // | onError: Function,
20450 // | scope: object,
20455 // All implementations should accept keywordArgs objects with any of
20456 // the 9 standard properties: query, onBegin, onItem, onComplete, onError
20457 // scope, sort, start, and count. Some implementations may accept additional
20458 // properties in the keywordArgs object as valid parameters, such as
20459 // {includeOutliers:true}.
20461 // ####The *query* parameter
20463 // The query may be optional in some data store implementations.
20464 // The dojo/data/api/Read API does not specify the syntax or semantics
20465 // of the query itself -- each different data store implementation
20466 // may have its own notion of what a query should look like.
20467 // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data
20468 // and dojox.data support an object structure query, where the object is a set of
20469 // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the
20470 // dijit widgets, such as ComboBox assume this to be the case when working with a datastore
20471 // when they dynamically update the query. Therefore, for maximum compatibility with dijit
20472 // widgets the recommended query parameter is a key/value object. That does not mean that the
20473 // the datastore may not take alternative query forms, such as a simple string, a Date, a number,
20474 // or a mix of such. Ultimately, The dojo/data/api/Read API is agnostic about what the query
20477 // Further note: In general for query objects that accept strings as attribute
20478 // value matches, the store should also support basic filtering capability, such as *
20479 // (match any character) and ? (match single character). An example query that is a query object
20480 // would be like: { attrFoo: "value*"}. Which generally means match all items where they have
20481 // an attribute named attrFoo, with a value that starts with 'value'.
20483 // ####The *queryOptions* parameter
20485 // The queryOptions parameter is an optional parameter used to specify options that may modify
20486 // the query in some fashion, such as doing a case insensitive search, or doing a deep search
20487 // where all items in a hierarchical representation of data are scanned instead of just the root
20488 // items. It currently defines two options that all datastores should attempt to honor if possible:
20490 // | ignoreCase: boolean, // Whether or not the query should match case sensitively or not. Default behaviour is false.
20491 // | deep: boolean // Whether or not a fetch should do a deep search of items and all child
20492 // | // items instead of just root-level items in a datastore. Default is false.
20495 // ####The *onBegin* parameter.
20497 // function(size, request);
20498 // If an onBegin callback function is provided, the callback function
20499 // will be called just once, before the first onItem callback is called.
20500 // The onBegin callback function will be passed two arguments, the
20501 // the total number of items identified and the Request object. If the total number is
20502 // unknown, then size will be -1. Note that size is not necessarily the size of the
20503 // collection of items returned from the query, as the request may have specified to return only a
20504 // subset of the total set of items through the use of the start and count parameters.
20506 // ####The *onItem* parameter.
20508 // function(item, request);
20510 // If an onItem callback function is provided, the callback function
20511 // will be called as each item in the result is received. The callback
20512 // function will be passed two arguments: the item itself, and the
20515 // ####The *onComplete* parameter.
20517 // function(items, request);
20519 // If an onComplete callback function is provided, the callback function
20520 // will be called just once, after the last onItem callback is called.
20521 // Note that if the onItem callback is not present, then onComplete will be passed
20522 // an array containing all items which matched the query and the request object.
20523 // If the onItem callback is present, then onComplete is called as:
20524 // onComplete(null, request).
20526 // ####The *onError* parameter.
20528 // function(errorData, request);
20530 // If an onError callback function is provided, the callback function
20531 // will be called if there is any sort of error while attempting to
20532 // execute the query.
20533 // The onError callback function will be passed two arguments:
20534 // an Error object and the Request object.
20536 // ####The *scope* parameter.
20538 // If a scope object is provided, all of the callback functions (onItem,
20539 // onComplete, onError, etc) will be invoked in the context of the scope
20540 // object. In the body of the callback function, the value of the "this"
20541 // keyword will be the scope object. If no scope object is provided,
20542 // the callback functions will be called in the context of dojo.global().
20543 // For example, onItem.call(scope, item, request) vs.
20544 // onItem.call(dojo.global(), item, request)
20546 // ####The *start* parameter.
20548 // If a start parameter is specified, this is a indication to the datastore to
20549 // only start returning items once the start number of items have been located and
20550 // skipped. When this parameter is paired with 'count', the store should be able
20551 // to page across queries with millions of hits by only returning subsets of the
20552 // hits for each query
20554 // ####The *count* parameter.
20556 // If a count parameter is specified, this is a indication to the datastore to
20557 // only return up to that many items. This allows a fetch call that may have
20558 // millions of item matches to be paired down to something reasonable.
20560 // ####The *sort* parameter.
20562 // If a sort parameter is specified, this is a indication to the datastore to
20563 // sort the items in some manner before returning the items. The array is an array of
20564 // javascript objects that must conform to the following format to be applied to the
20565 // fetching of items:
20567 // | attribute: attribute || attribute-name-string,
20568 // | descending: true|false; // Optional. Default is false.
20570 // Note that when comparing attributes, if an item contains no value for the attribute
20571 // (undefined), then it the default ascending sort logic should push it to the bottom
20572 // of the list. In the descending order case, it such items should appear at the top of the list.
20574 request
= request
|| {};
20575 if(!request
.store
){
20576 request
.store
= this;
20579 this._fetchItems(request
, lang
.hitch(this, "fetchHandler"), lang
.hitch(this, "errorHandler"));
20580 return request
; // Object
20583 return simpleFetch
;
20587 'dijit/Menu':function(){
20588 define("dijit/Menu", [
20590 "dojo/_base/array", // array.forEach
20591 "dojo/_base/declare", // declare
20592 "dojo/_base/event", // event.stop
20593 "dojo/dom", // dom.byId dom.isDescendant
20594 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
20595 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
20596 "dojo/dom-style", // domStyle.getComputedStyle
20597 "dojo/keys", // keys.F10
20598 "dojo/_base/lang", // lang.hitch
20600 "dojo/sniff", // has("ie"), has("quirks")
20601 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames
20602 "dojo/window", // winUtils.get
20606 ], function(require
, array
, declare
, event
, dom
, domAttr
, domGeometry
, domStyle
, keys
, lang
, on
,
20607 has
, win
, winUtils
, pm
, DropDownMenu
, ready
){
20612 // Back compat w/1.6, remove for 2.0
20613 if(has("dijit-legacy-requires")){
20614 ready(0, function(){
20615 var requires
= ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
20616 require(requires
); // use indirection so modules not rolled into a build
20620 return declare("dijit.Menu", DropDownMenu
, {
20622 // A context menu you can assign to multiple elements
20624 constructor: function(/*===== params, srcNodeRef =====*/){
20626 // Create the widget.
20627 // params: Object|null
20628 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
20629 // and functions, typically callbacks like onClick.
20630 // The hash can contain any of the widget's properties, excluding read-only properties.
20631 // srcNodeRef: DOMNode|String?
20632 // If a srcNodeRef (DOM node) is specified:
20634 // - use srcNodeRef.innerHTML as my contents
20635 // - replace srcNodeRef with my generated DOM tree
20637 this._bindings
= [];
20640 // targetNodeIds: [const] String[]
20641 // Array of dom node ids of nodes to attach to.
20642 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
20645 // selector: String?
20646 // CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
20647 // the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
20648 // a range of rows in a table, tree, etc.
20650 // The application must require() an appropriate level of dojo/query to handle the selector.
20653 // TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
20654 // So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
20657 // currentTarget: [readonly] DOMNode
20658 // For context menus, set to the current node that the Menu is being displayed for.
20659 // Useful so that the menu actions can be tailored according to the node
20660 currentTarget: null,
20663 // contextMenuForWindow: [const] Boolean
20664 // If true, right clicking anywhere on the window will cause this context menu to open.
20665 // If false, must specify targetNodeIds.
20666 contextMenuForWindow
: false,
20668 // leftClickToOpen: [const] Boolean
20669 // If true, menu will open on left click instead of right click, similar to a file menu.
20670 leftClickToOpen
: false,
20672 // refocus: Boolean
20673 // When this menu closes, re-focus the element which had focus before it was opened.
20676 postCreate: function(){
20677 if(this.contextMenuForWindow
){
20678 this.bindDomNode(this.ownerDocumentBody
);
20680 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
20681 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
20682 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
20683 array
.forEach(this.targetNodeIds
, this.bindDomNode
, this);
20685 this.inherited(arguments
);
20688 // thanks burstlib!
20689 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el
){
20691 // Returns the window reference of the passed iframe
20694 return winUtils
.get(this._iframeContentDocument(iframe_el
)) ||
20695 // Moz. TODO: is this available when defaultView isn't?
20696 this._iframeContentDocument(iframe_el
)['__parent__'] ||
20697 (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
]) || null; // Window
20700 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el
){
20702 // Returns a reference to the document object inside iframe_el
20705 return iframe_el
.contentDocument
// W3
20706 || (iframe_el
.contentWindow
&& iframe_el
.contentWindow
.document
) // IE
20707 || (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
] && win
.doc
.frames
[iframe_el
.name
].document
)
20708 || null; // HTMLDocument
20711 bindDomNode: function(/*String|DomNode*/ node
){
20713 // Attach menu to given node
20714 node
= dom
.byId(node
, this.ownerDocument
);
20716 var cn
; // Connect node
20718 // Support context menus on iframes. Rather than binding to the iframe itself we need
20719 // to bind to the <body> node inside the iframe.
20720 if(node
.tagName
.toLowerCase() == "iframe"){
20722 window
= this._iframeContentWindow(iframe
);
20723 cn
= win
.body(window
.document
);
20725 // To capture these events at the top level, attach to <html>, not <body>.
20726 // Otherwise right-click context menu just doesn't work.
20727 cn
= (node
== win
.body(this.ownerDocument
) ? this.ownerDocument
.documentElement
: node
);
20731 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
20737 // Save info about binding in _bindings[], and make node itself record index(+1) into
20738 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
20739 // start with a number, which fails on FF/safari.
20740 domAttr
.set(node
, "_dijitMenu" + this.id
, this._bindings
.push(binding
));
20742 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
20743 // loading yet, in which case we need to wait for the onload event first, and then connect
20744 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
20745 // we need to monitor keyboard events in addition to the oncontextmenu event.
20746 var doConnects
= lang
.hitch(this, function(cn
){
20747 var selector
= this.selector
,
20748 delegatedEvent
= selector
?
20749 function(eventType
){ return on
.selector(selector
, eventType
); } :
20750 function(eventType
){ return eventType
; },
20753 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
20754 // rather than shift-F10?
20755 on(cn
, delegatedEvent(this.leftClickToOpen
? "click" : "contextmenu"), function(evt
){
20756 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
20758 self
._scheduleOpen(this, iframe
, {x
: evt
.pageX
, y
: evt
.pageY
});
20760 on(cn
, delegatedEvent("keydown"), function(evt
){
20761 if(evt
.shiftKey
&& evt
.keyCode
== keys
.F10
){
20763 self
._scheduleOpen(this, iframe
); // no coords - open near target node
20768 binding
.connects
= cn
? doConnects(cn
) : [];
20771 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
20772 // and every time the contents change.
20773 // Need to do this b/c we are actually binding to the iframe's <body> node.
20774 // Note: can't use connect.connect(), see #9609.
20776 binding
.onloadHandler
= lang
.hitch(this, function(){
20777 // want to remove old connections, but IE throws exceptions when trying to
20778 // access the <body> node because it's already gone, or at least in a state of limbo
20780 var window
= this._iframeContentWindow(iframe
);
20781 cn
= win
.body(window
.document
)
20782 binding
.connects
= doConnects(cn
);
20784 if(iframe
.addEventListener
){
20785 iframe
.addEventListener("load", binding
.onloadHandler
, false);
20787 iframe
.attachEvent("onload", binding
.onloadHandler
);
20792 unBindDomNode: function(/*String|DomNode*/ nodeName
){
20794 // Detach menu from given node
20798 node
= dom
.byId(nodeName
, this.ownerDocument
);
20800 // On IE the dom.byId() call will get an exception if the attach point was
20801 // the <body> node of an <iframe> that has since been reloaded (and thus the
20802 // <body> node is in a limbo state of destruction.
20806 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
20807 var attrName
= "_dijitMenu" + this.id
;
20808 if(node
&& domAttr
.has(node
, attrName
)){
20809 var bid
= domAttr
.get(node
, attrName
)-1, b
= this._bindings
[bid
], h
;
20810 while((h
= b
.connects
.pop())){
20814 // Remove listener for iframe onload events
20815 var iframe
= b
.iframe
;
20817 if(iframe
.removeEventListener
){
20818 iframe
.removeEventListener("load", b
.onloadHandler
, false);
20820 iframe
.detachEvent("onload", b
.onloadHandler
);
20824 domAttr
.remove(node
, attrName
);
20825 delete this._bindings
[bid
];
20829 _scheduleOpen: function(/*DomNode?*/ target
, /*DomNode?*/ iframe
, /*Object?*/ coords
){
20831 // Set timer to display myself. Using a timer rather than displaying immediately solves
20834 // 1. IE: without the delay, focus work in "open" causes the system
20835 // context menu to appear in spite of stopEvent.
20837 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
20838 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
20839 // oncontextmenu event.)
20841 if(!this._openTimer
){
20842 this._openTimer
= this.defer(function(){
20843 delete this._openTimer
;
20853 _openMyself: function(args
){
20855 // Internal function for opening myself when the user does a right-click or something similar.
20857 // This is an Object containing:
20859 // - target: The node that is being clicked
20860 // - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
20861 // - coords: Put menu at specified x/y position in viewport, or if iframe is
20862 // specified, then relative to iframe.
20864 // _openMyself() formerly took the event object, and since various code references
20865 // evt.target (after connecting to _openMyself()), using an Object for parameters
20866 // (so that old code still works).
20868 var target
= args
.target
,
20869 iframe
= args
.iframe
,
20870 coords
= args
.coords
;
20872 // To be used by MenuItem event handlers to tell which node the menu was opened on
20873 this.currentTarget
= target
;
20875 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
20876 // then near the node the menu is assigned to.
20879 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
20880 var ifc
= domGeometry
.position(iframe
, true),
20881 window
= this._iframeContentWindow(iframe
),
20882 scroll
= domGeometry
.docScroll(window
.document
);
20884 var cs
= domStyle
.getComputedStyle(iframe
),
20885 tp
= domStyle
.toPixelValue
,
20886 left
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingLeft
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderLeftWidth
) : 0),
20887 top
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingTop
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderTopWidth
) : 0);
20889 coords
.x
+= ifc
.x
+ left
- scroll
.x
;
20890 coords
.y
+= ifc
.y
+ top
- scroll
.y
;
20893 coords
= domGeometry
.position(target
, true);
20899 var prevFocusNode
= this._focusManager
.get("prevNode");
20900 var curFocusNode
= this._focusManager
.get("curNode");
20901 var savedFocusNode
= !curFocusNode
|| (dom
.isDescendant(curFocusNode
, this.domNode
)) ? prevFocusNode
: curFocusNode
;
20903 function closeAndRestoreFocus(){
20904 // user has clicked on a menu or popup
20905 if(self
.refocus
&& savedFocusNode
){
20906 savedFocusNode
.focus();
20914 onExecute
: closeAndRestoreFocus
,
20915 onCancel
: closeAndRestoreFocus
,
20916 orient
: this.isLeftToRight() ? 'L' : 'R'
20920 this._onBlur = function(){
20921 this.inherited('_onBlur', arguments
);
20922 // Usually the parent closes the child widget but if this is a context
20923 // menu then there is no parent
20925 // don't try to restore focus; user has clicked another part of the screen
20926 // and set focus there
20930 destroy: function(){
20931 array
.forEach(this._bindings
, function(b
){ if(b
){ this.unBindDomNode(b
.node
); } }, this);
20932 this.inherited(arguments
);
20939 'dijit/form/_CheckBoxMixin':function(){
20940 define("dijit/form/_CheckBoxMixin", [
20941 "dojo/_base/declare", // declare
20942 "dojo/dom-attr", // domAttr.set
20943 "dojo/_base/event" // event.stop
20944 ], function(declare
, domAttr
, event
){
20947 // dijit/form/_CheckBoxMixin
20949 return declare("dijit.form._CheckBoxMixin", null, {
20951 // Mixin to provide widget functionality corresponding to an HTML checkbox
20954 // User interacts with real html inputs.
20955 // On onclick (which occurs by mouse click, space-bar, or
20956 // using the arrow keys to switch the selected radio button),
20957 // we update the state of the checkbox/radio.
20960 // type: [private] String
20961 // type attribute on `<input>` node.
20962 // Overrides `dijit/form/Button.type`. Users should not change this value.
20966 // As an initialization parameter, equivalent to value field on normal checkbox
20967 // (if checked, the value is passed as the value when form is submitted).
20970 // readOnly: Boolean
20971 // Should this widget respond to user input?
20972 // In markup, this is specified as "readOnly".
20973 // Similar to disabled except readOnly form values are submitted.
20976 // aria-pressed for toggle buttons, and aria-checked for checkboxes
20977 _aria_attr
: "aria-checked",
20979 _setReadOnlyAttr: function(/*Boolean*/ value
){
20980 this._set("readOnly", value
);
20981 domAttr
.set(this.focusNode
, 'readOnly', value
);
20984 // Override dijit/form/Button._setLabelAttr() since we don't even have a containerNode.
20985 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox/layout/TabContainer
20986 _setLabelAttr
: undefined,
20988 _getSubmitValue: function(/*String*/ value
){
20989 return !value
&& value
!== 0 ? "on" : value
;
20992 _setValueAttr: function(newValue
){
20993 newValue
= this._getSubmitValue(newValue
); // "on" to match browser native behavior when value unspecified
20994 this._set("value", newValue
);
20995 domAttr
.set(this.focusNode
, "value", newValue
);
20999 this.inherited(arguments
);
21000 // Handle unlikely event that the <input type=checkbox> value attribute has changed
21001 this._set("value", this.params
.value
|| "on");
21002 domAttr
.set(this.focusNode
, 'value', this.value
);
21005 _onClick: function(/*Event*/ e
){
21007 // Internal function to handle click actions - need to check
21008 // readOnly, since button no longer does that check.
21013 return this.inherited(arguments
);
21019 'dijit/layout/ContentPane':function(){
21020 define("dijit/layout/ContentPane", [
21021 "dojo/_base/kernel", // kernel.deprecated
21022 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
21025 "./_ContentPaneResizeMixin",
21026 "dojo/string", // string.substitute
21027 "dojo/html", // html._ContentSetter
21028 "dojo/i18n!../nls/loading",
21029 "dojo/_base/array", // array.forEach
21030 "dojo/_base/declare", // declare
21031 "dojo/_base/Deferred", // Deferred
21032 "dojo/dom", // dom.byId
21033 "dojo/dom-attr", // domAttr.attr
21034 "dojo/dom-construct", // empty()
21035 "dojo/_base/xhr", // xhr.get
21036 "dojo/i18n", // i18n.getLocalization
21038 ], function(kernel
, lang
, _Widget
, _Container
, _ContentPaneResizeMixin
, string
, html
, nlsLoading
,
21039 array
, declare
, Deferred
, dom
, domAttr
, domConstruct
, xhr
, i18n
, when
){
21042 // dijit/layout/ContentPane
21045 return declare("dijit.layout.ContentPane", [_Widget
, _Container
, _ContentPaneResizeMixin
], {
21047 // A widget containing an HTML fragment, specified inline
21048 // or by uri. Fragment may include widgets.
21051 // This widget embeds a document fragment in the page, specified
21052 // either by uri, javascript generated markup or DOM reference.
21053 // Any widgets within this content are instantiated and managed,
21054 // but laid out according to the HTML structure. Unlike IFRAME,
21055 // ContentPane embeds a document fragment as would be found
21056 // inside the BODY tag of a full HTML document. It should not
21057 // contain the HTML, HEAD, or BODY tags.
21058 // For more advanced functionality with scripts and
21059 // stylesheets, see dojox/layout/ContentPane. This widget may be
21060 // used stand alone or as a base class for other widgets.
21061 // ContentPane is useful as a child of other layout containers
21062 // such as BorderContainer or TabContainer, but note that those
21063 // widgets can contain any widget as a child.
21066 // Some quick samples:
21067 // To change the innerHTML:
21068 // | cp.set('content', '<b>new content</b>')`
21069 // Or you can send it a NodeList:
21070 // | cp.set('content', dojo.query('div [class=selected]', userSelection))
21071 // To do an ajax update:
21072 // | cp.set('href', url)
21075 // The href of the content that displays now.
21076 // Set this at construction if you want to load data externally when the
21077 // pane is shown. (Set preload=true to load it immediately.)
21078 // Changing href after creation doesn't have any effect; Use set('href', ...);
21081 // content: String|DomNode|NodeList|dijit/_Widget
21082 // The innerHTML of the ContentPane.
21083 // Note that the initialization parameter / argument to set("content", ...)
21084 // can be a String, DomNode, Nodelist, or _Widget.
21087 // extractContent: Boolean
21088 // Extract visible content from inside of `<body> .... </body>`.
21089 // I.e., strip `<html>` and `<head>` (and it's contents) from the href
21090 extractContent
: false,
21092 // parseOnLoad: Boolean
21093 // Parse content and create the widgets, if any.
21096 // parserScope: String
21097 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
21098 // will search for data-dojo-type (or dojoType). For backwards compatibility
21099 // reasons defaults to dojo._scopeName (which is "dojo" except when
21100 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
21101 parserScope
: kernel
._scopeName
,
21103 // preventCache: Boolean
21104 // Prevent caching of data from href's by appending a timestamp to the href.
21105 preventCache
: false,
21107 // preload: Boolean
21108 // Force load of data on initialization even if pane is hidden.
21111 // refreshOnShow: Boolean
21112 // Refresh (re-download) content when pane goes from hidden to shown
21113 refreshOnShow
: false,
21115 // loadingMessage: String
21116 // Message that shows while downloading
21117 loadingMessage
: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
21119 // errorMessage: String
21120 // Message that shows if an error occurs
21121 errorMessage
: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
21123 // isLoaded: [readonly] Boolean
21124 // True if the ContentPane has data in it, either specified
21125 // during initialization (via href or inline content), or set
21126 // via set('content', ...) / set('href', ...)
21128 // False if it doesn't have any content, or if ContentPane is
21129 // still in the process of downloading href.
21132 baseClass
: "dijitContentPane",
21135 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
21136 // Function that should grab the content specified via href.
21137 ioMethod: dojo.xhrGet,
21141 // Parameters to pass to xhrGet() request, for example:
21142 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
21145 // onLoadDeferred: [readonly] dojo.Deferred
21146 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
21147 // Calling onLoadDeferred.then() registers your
21148 // callback to be called only once, when the prior set('href', ...) call or
21149 // the initial href parameter to the constructor finishes loading.
21151 // This is different than an onLoad() handler which gets called any time any href
21152 // or content is loaded.
21153 onLoadDeferred
: null,
21155 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
21156 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
21158 _setTitleAttr
: null,
21160 // Flag to parser that I'll parse my contents, so it shouldn't.
21163 // template: [private] Boolean
21164 // Flag from the parser that this ContentPane is inside a template
21165 // so the contents are pre-parsed.
21166 // TODO: this declaration can be commented out in 2.0
21169 create: function(params
, srcNodeRef
){
21170 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
21171 // processed in the same way as contents set via set("content", ...), calling the parser etc.
21172 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
21173 if((!params
|| !params
.template
) && srcNodeRef
&& !("href" in params
) && !("content" in params
)){
21174 srcNodeRef
= dom
.byId(srcNodeRef
);
21175 var df
= srcNodeRef
.ownerDocument
.createDocumentFragment();
21176 while(srcNodeRef
.firstChild
){
21177 df
.appendChild(srcNodeRef
.firstChild
);
21179 params
= lang
.delegate(params
, {content
: df
});
21181 this.inherited(arguments
, [params
, srcNodeRef
]);
21184 postMixInProperties: function(){
21185 this.inherited(arguments
);
21186 var messages
= i18n
.getLocalization("dijit", "loading", this.lang
);
21187 this.loadingMessage
= string
.substitute(this.loadingMessage
, messages
);
21188 this.errorMessage
= string
.substitute(this.errorMessage
, messages
);
21191 buildRendering: function(){
21192 this.inherited(arguments
);
21194 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
21195 // For subclasses of ContentPane that do have a template, does nothing.
21196 if(!this.containerNode
){
21197 this.containerNode
= this.domNode
;
21200 // remove the title attribute so it doesn't show up when hovering
21201 // over a node (TODO: remove in 2.0, no longer needed after #11490)
21202 this.domNode
.title
= "";
21204 if(!domAttr
.get(this.domNode
,"role")){
21205 this.domNode
.setAttribute("role", "group");
21209 startup: function(){
21211 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
21213 // This starts all the widgets
21214 this.inherited(arguments
);
21216 // And this catches stuff like dojo/dnd/Source
21217 if(this._contentSetter
){
21218 array
.forEach(this._contentSetter
.parseResults
, function(obj
){
21219 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
21221 obj
._started
= true;
21227 _startChildren: function(){
21229 // Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
21230 // itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
21232 // This starts all the widgets
21233 array
.forEach(this.getChildren(), function(obj
){
21234 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
21236 obj
._started
= true;
21240 // And this catches stuff like dojo/dnd/Source
21241 if(this._contentSetter
){
21242 array
.forEach(this._contentSetter
.parseResults
, function(obj
){
21243 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
21245 obj
._started
= true;
21251 setHref: function(/*String|Uri*/ href
){
21253 // Deprecated. Use set('href', ...) instead.
21254 kernel
.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
21255 return this.set("href", href
);
21257 _setHrefAttr: function(/*String|Uri*/ href
){
21259 // Hook so set("href", ...) works.
21261 // Reset the (external defined) content of this pane and replace with new url
21262 // Note: It delays the download until widget is shown if preload is false.
21264 // url to the page you want to get, must be within the same domain as your mainpage
21266 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
21269 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
21270 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
21272 this._set("href", href
);
21274 // _setHrefAttr() is called during creation and by the user, after creation.
21275 // Assuming preload == false, only in the second case do we actually load the URL;
21276 // otherwise it's done in startup(), and only if this widget is shown.
21277 if(this.preload
|| (this._created
&& this._isShown())){
21280 // Set flag to indicate that href needs to be loaded the next time the
21281 // ContentPane is made visible
21282 this._hrefChanged
= true;
21285 return this.onLoadDeferred
; // Deferred
21288 setContent: function(/*String|DomNode|Nodelist*/data
){
21290 // Deprecated. Use set('content', ...) instead.
21291 kernel
.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
21292 this.set("content", data
);
21294 _setContentAttr: function(/*String|DomNode|Nodelist*/data
){
21296 // Hook to make set("content", ...) work.
21297 // Replaces old content with data content, include style classes from old content
21299 // the new Content may be String, DomNode or NodeList
21301 // if data is a NodeList (or an array of nodes) nodes are copied
21302 // so you can import nodes from another document implicitly
21304 // clear href so we can't run refresh and clear content
21305 // refresh should only work if we downloaded the content
21306 this._set("href", "");
21308 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
21311 // Even though user is just setting content directly, still need to define an onLoadDeferred
21312 // because the _onLoadHandler() handler is still getting called from setContent()
21313 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
21315 // For back-compat reasons, call onLoad() for set('content', ...)
21316 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
21317 // or as initialization parameter (ie: new ContentPane({content: ...})
21318 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
21321 this._setContent(data
|| "");
21323 this._isDownloaded
= false; // mark that content is from a set('content') not a set('href')
21325 return this.onLoadDeferred
; // Deferred
21327 _getContentAttr: function(){
21329 // Hook to make get("content") work
21330 return this.containerNode
.innerHTML
;
21333 cancel: function(){
21335 // Cancels an in-flight download of content
21336 if(this._xhrDfd
&& (this._xhrDfd
.fired
== -1)){
21337 this._xhrDfd
.cancel();
21339 delete this._xhrDfd
; // garbage collect
21341 this.onLoadDeferred
= null;
21344 destroy: function(){
21346 this.inherited(arguments
);
21349 destroyRecursive: function(/*Boolean*/ preserveDom
){
21351 // Destroy the ContentPane and its contents
21353 // if we have multiple controllers destroying us, bail after the first
21354 if(this._beingDestroyed
){
21357 this.inherited(arguments
);
21360 _onShow: function(){
21362 // Called when the ContentPane is made visible
21364 // For a plain ContentPane, this is called on initialization, from startup().
21365 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
21366 // called whenever the pane is made visible.
21368 // Does necessary processing, including href download and layout/resize of
21371 this.inherited(arguments
);
21374 if(!this._xhrDfd
&& // if there's an href that isn't already being loaded
21375 (!this.isLoaded
|| this._hrefChanged
|| this.refreshOnShow
)
21377 return this.refresh(); // If child has an href, promise that fires when the load is complete
21382 refresh: function(){
21384 // [Re]download contents of href and display
21386 // 1. cancels any currently in-flight requests
21387 // 2. posts "loading..." message
21388 // 3. sends XHR to download new data
21390 // Cancel possible prior in-flight request
21393 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
21394 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
21396 return this.onLoadDeferred
; // If child has an href, promise that fires when refresh is complete
21401 // Load/reload the href specified in this.href
21403 // display loading message
21404 this._setContent(this.onDownloadStart(), true);
21408 preventCache
: (this.preventCache
|| this.refreshOnShow
),
21412 if(lang
.isObject(this.ioArgs
)){
21413 lang
.mixin(getArgs
, this.ioArgs
);
21416 var hand
= (this._xhrDfd
= (this.ioMethod
|| xhr
.get)(getArgs
)),
21421 returnedHtml
= html
;
21423 self
._isDownloaded
= true;
21424 return self
._setContent(html
, false);
21426 self
._onError('Content', err
); // onContentError
21430 if(!hand
.canceled
){
21431 // show error message in the pane
21432 self
._onError('Download', err
); // onDownloadError
21434 delete self
._xhrDfd
;
21438 self
.onDownloadEnd();
21439 delete self
._xhrDfd
;
21440 return returnedHtml
;
21443 // Remove flag saying that a load is needed
21444 delete this._hrefChanged
;
21447 _onLoadHandler: function(data
){
21449 // This is called whenever new content is being loaded
21450 this._set("isLoaded", true);
21452 this.onLoadDeferred
.resolve(data
);
21454 console
.error('Error '+this.widgetId
+' running custom onLoad code: ' + e
.message
);
21458 _onUnloadHandler: function(){
21460 // This is called whenever the content is being unloaded
21461 this._set("isLoaded", false);
21465 console
.error('Error '+this.widgetId
+' running custom onUnload code: ' + e
.message
);
21469 destroyDescendants: function(/*Boolean*/ preserveDom
){
21471 // Destroy all the widgets inside the ContentPane and empty containerNode
21473 // Make sure we call onUnload (but only when the ContentPane has real content)
21475 this._onUnloadHandler();
21478 // Even if this.isLoaded == false there might still be a "Loading..." message
21479 // to erase, so continue...
21481 // For historical reasons we need to delete all widgets under this.containerNode,
21482 // even ones that the user has created manually.
21483 var setter
= this._contentSetter
;
21484 array
.forEach(this.getChildren(), function(widget
){
21485 if(widget
.destroyRecursive
){
21486 // All widgets will hit this branch
21487 widget
.destroyRecursive(preserveDom
);
21488 }else if(widget
.destroy
){
21489 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21490 widget
.destroy(preserveDom
);
21492 widget
._destroyed
= true;
21495 // Most of the widgets in setter.parseResults have already been destroyed, but
21496 // things like Menu that have been moved to <body> haven't yet
21497 array
.forEach(setter
.parseResults
, function(widget
){
21498 if(!widget
._destroyed
){
21499 if(widget
.destroyRecursive
){
21500 // All widgets will hit this branch
21501 widget
.destroyRecursive(preserveDom
);
21502 }else if(widget
.destroy
){
21503 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21504 widget
.destroy(preserveDom
);
21506 widget
._destroyed
= true;
21509 delete setter
.parseResults
;
21512 // And then clear away all the DOM nodes
21514 domConstruct
.empty(this.containerNode
);
21517 // Delete any state information we have about current contents
21518 delete this._singleChild
;
21521 _setContent: function(/*String|DocumentFragment*/ cont
, /*Boolean*/ isFakeContent
){
21523 // Insert the content into the container node
21525 // Returns a Deferred promise that is resolved when the content is parsed.
21527 // first get rid of child widgets
21528 this.destroyDescendants();
21530 // html.set will take care of the rest of the details
21531 // we provide an override for the error handling to ensure the widget gets the errors
21532 // configure the setter instance with only the relevant widget instance properties
21533 // NOTE: unless we hook into attr, or provide property setters for each property,
21534 // we need to re-configure the ContentSetter with each use
21535 var setter
= this._contentSetter
;
21536 if(! (setter
&& setter
instanceof html
._ContentSetter
)){
21537 setter
= this._contentSetter
= new html
._ContentSetter({
21538 node
: this.containerNode
,
21539 _onError
: lang
.hitch(this, this._onError
),
21540 onContentError
: lang
.hitch(this, function(e
){
21541 // fires if a domfault occurs when we are appending this.errorMessage
21542 // like for instance if domNode is a UL and we try append a DIV
21543 var errMess
= this.onContentError(e
);
21545 this.containerNode
.innerHTML
= errMess
;
21547 console
.error('Fatal '+this.id
+' could not change content due to '+e
.message
, e
);
21554 var setterParams
= lang
.mixin({
21555 cleanContent
: this.cleanContent
,
21556 extractContent
: this.extractContent
,
21557 parseContent
: !cont
.domNode
&& this.parseOnLoad
,
21558 parserScope
: this.parserScope
,
21562 textDir
: this.textDir
21563 }, this._contentSetterParams
|| {});
21565 var p
= setter
.set( (lang
.isObject(cont
) && cont
.domNode
) ? cont
.domNode
: cont
, setterParams
);
21567 // dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
21568 // dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
21569 // So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
21571 return when(p
&& p
.then
? p
: setter
.parseDeferred
, function(){
21572 // setter params must be pulled afresh from the ContentPane each time
21573 delete self
._contentSetterParams
;
21575 if(!isFakeContent
){
21577 // Startup each top level child widget (and they will start their children, recursively)
21578 self
._startChildren();
21580 // Call resize() on each of my child layout widgets,
21581 // or resize() on my single child layout widget...
21582 // either now (if I'm currently visible) or when I become visible
21583 self
._scheduleLayout();
21585 self
._onLoadHandler(cont
);
21590 _onError: function(type
, err
, consoleText
){
21591 this.onLoadDeferred
.reject(err
);
21593 // shows user the string that is returned by on[type]Error
21594 // override on[type]Error and return your own string to customize
21595 var errText
= this['on' + type
+ 'Error'].call(this, err
);
21597 console
.error(consoleText
, err
);
21598 }else if(errText
){// a empty string won't change current content
21599 this._setContent(errText
, true);
21603 // EVENT's, should be overide-able
21604 onLoad: function(/*===== data =====*/){
21606 // Event hook, is called after everything is loaded and widgetified
21611 onUnload: function(){
21613 // Event hook, is called before old content is cleared
21618 onDownloadStart: function(){
21620 // Called before download starts.
21622 // The string returned by this function will be the html
21623 // that tells the user we are loading something.
21624 // Override with your own function if you want to change text.
21627 return this.loadingMessage
;
21630 onContentError: function(/*Error*/ /*===== error =====*/){
21632 // Called on DOM faults, require faults etc. in content.
21634 // In order to display an error message in the pane, return
21635 // the error message from this method, as an HTML string.
21637 // By default (if this method is not overriden), it returns
21638 // nothing, so the error message is just printed to the console.
21643 onDownloadError: function(/*Error*/ /*===== error =====*/){
21645 // Called when download error occurs.
21647 // In order to display an error message in the pane, return
21648 // the error message from this method, as an HTML string.
21650 // Default behavior (if this method is not overriden) is to display
21651 // the error message inside the pane.
21654 return this.errorMessage
;
21657 onDownloadEnd: function(){
21659 // Called when download is finished.
21668 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21669 'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21670 'dijit/_KeyNavContainer':function(){
21671 define("dijit/_KeyNavContainer", [
21672 "dojo/_base/kernel", // kernel.deprecated
21675 "dojo/_base/array", // array.forEach
21676 "dojo/keys", // keys.END keys.HOME
21677 "dojo/_base/declare", // declare
21678 "dojo/_base/event", // event.stop
21679 "dojo/dom-attr", // domAttr.set
21680 "dojo/_base/lang" // lang.hitch
21681 ], function(kernel
, _Container
, _FocusMixin
, array
, keys
, declare
, event
, domAttr
, lang
){
21685 // dijit/_KeyNavContainer
21687 return declare("dijit._KeyNavContainer", [_FocusMixin
, _Container
], {
21689 // A _Container with keyboard navigation of its children.
21691 // To use this mixin, call connectKeyNavHandlers() in
21693 // It provides normalized keyboard and focusing code for Container
21697 // focusedChild: [protected] Widget
21698 // The currently focused child widget, or null if there isn't one
21699 focusedChild: null,
21702 // tabIndex: String
21703 // Tab index of the container; same as HTML tabIndex attribute.
21704 // Note then when user tabs into the container, focus is immediately
21705 // moved to the first item in the container.
21708 connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes
, /*keys[]*/ nextKeyCodes
){
21710 // Call in postCreate() to attach the keyboard handlers
21711 // to the container.
21712 // preKeyCodes: keys[]
21713 // Key codes for navigating to the previous child.
21714 // nextKeyCodes: keys[]
21715 // Key codes for navigating to the next child.
21719 // TODO: call this automatically from my own postCreate()
21721 var keyCodes
= (this._keyNavCodes
= {});
21722 var prev
= lang
.hitch(this, "focusPrev");
21723 var next
= lang
.hitch(this, "focusNext");
21724 array
.forEach(prevKeyCodes
, function(code
){ keyCodes
[code
] = prev
; });
21725 array
.forEach(nextKeyCodes
, function(code
){ keyCodes
[code
] = next
; });
21726 keyCodes
[keys
.HOME
] = lang
.hitch(this, "focusFirstChild");
21727 keyCodes
[keys
.END
] = lang
.hitch(this, "focusLastChild");
21728 this.connect(this.domNode
, "onkeypress", "_onContainerKeypress");
21729 this.connect(this.domNode
, "onfocus", "_onContainerFocus");
21732 startupKeyNavChildren: function(){
21733 kernel
.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
21736 startup: function(){
21737 this.inherited(arguments
);
21738 array
.forEach(this.getChildren(), lang
.hitch(this, "_startupChild"));
21741 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex
){
21742 this.inherited(arguments
);
21743 this._startupChild(widget
);
21748 // Default focus() implementation: focus the first child.
21749 this.focusFirstChild();
21752 focusFirstChild: function(){
21754 // Focus the first focusable child in the container.
21757 this.focusChild(this._getFirstFocusableChild());
21760 focusLastChild: function(){
21762 // Focus the last focusable child in the container.
21765 this.focusChild(this._getLastFocusableChild());
21768 focusNext: function(){
21770 // Focus the next widget
21773 this.focusChild(this._getNextFocusableChild(this.focusedChild
, 1));
21776 focusPrev: function(){
21778 // Focus the last focusable node in the previous widget
21779 // (ex: go to the ComboButton icon section rather than button section)
21782 this.focusChild(this._getNextFocusableChild(this.focusedChild
, -1), true);
21785 focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last
){
21787 // Focus specified child widget.
21789 // Reference to container's child widget
21791 // If true and if widget has multiple focusable nodes, focus the
21792 // last one instead of the first one
21796 if(!widget
){ return; }
21798 if(this.focusedChild
&& widget
!== this.focusedChild
){
21799 this._onChildBlur(this.focusedChild
); // used by _MenuBase
21801 widget
.set("tabIndex", this.tabIndex
); // for IE focus outline to appear, must set tabIndex before focs
21802 widget
.focus(last
? "end" : "start");
21803 this._set("focusedChild", widget
);
21806 _startupChild: function(/*dijit/_WidgetBase*/ widget){
21808 // Setup for each child widget
21810 // Sets tabIndex=-1 on each child, so that the tab key will
21811 // leave the container rather than visiting each child.
21815 widget.set("tabIndex", "-1");
21817 this.connect(widget, "_onFocus", function(){
21818 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
21819 widget.set("tabIndex", this.tabIndex);
21821 this.connect(widget, "_onBlur", function(){
21822 widget.set("tabIndex", "-1");
21826 _onContainerFocus: function(evt){
21828 // Handler for when the container gets focus
21830 // Initially the container itself has a tabIndex, but when it gets
21831 // focus, switch focus to first child...
21835 // Note that we can't use _onFocus() because switching focus from the
21836 // _onFocus() handler confuses the focus.js code
21837 // (because it causes _onFocusNode() to be called recursively)
21838 // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
21840 // Ignore spurious focus events:
21841 // 1. focus on a child widget bubbles on FF
21842 // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
21843 if(evt.target !== this.domNode || this.focusedChild){ return; }
21845 this.focusFirstChild();
21847 // and then set the container's tabIndex to -1,
21848 // (don't remove as that breaks Safari 4)
21849 // so that tab or shift-tab will go to the fields after/before
21850 // the container, rather than the container itself
21851 domAttr.set(this.domNode, "tabIndex", "-1");
21854 _onBlur: function(evt){
21855 // When focus is moved away the container, and its descendant (popup) widgets,
21856 // then restore the container's tabIndex so that user can tab to it again.
21857 // Note that using _onBlur() so that this doesn't happen when focus is shifted
21858 // to one of my child widgets (typically a popup)
21860 domAttr.set(this.domNode, "tabIndex", this.tabIndex);
21862 this.focusedChild = null;
21863 this.inherited(arguments);
21866 _onContainerKeypress: function(evt){
21868 // When a key is pressed, if it's an arrow key etc. then
21869 // it's handled here.
21872 if(evt.ctrlKey || evt.altKey){ return; }
21873 var func = this._keyNavCodes[evt.charOrCode];
21880 _onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
21882 // Called when focus leaves a child widget to go
21883 // to a sibling widget.
21884 // Used by MenuBase.js (TODO: move code there)
21889 _getFirstFocusableChild: function(){
21891 // Returns first child that can be focused
21892 return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
21895 _getLastFocusableChild: function(){
21897 // Returns last child that can be focused
21898 return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
21901 _getNextFocusableChild: function(child
, dir
){
21903 // Returns the next or previous focusable child, compared
21906 // The current widget
21911 child
= this._getSiblingOfChild(child
, dir
);
21913 var children
= this.getChildren();
21914 for(var i
=0; i
< children
.length
; i
++){
21916 child
= children
[(dir
>0) ? 0 : (children
.length
-1)];
21918 if(child
.isFocusable()){
21919 return child
; // dijit/_WidgetBase
21921 child
= this._getSiblingOfChild(child
, dir
);
21923 // no focusable child found
21924 return null; // dijit/_WidgetBase
21930 'dijit/layout/utils':function(){
21931 define("dijit/layout/utils", [
21932 "dojo/_base/array", // array.filter array.forEach
21933 "dojo/dom-class", // domClass.add domClass.remove
21934 "dojo/dom-geometry", // domGeometry.marginBox
21935 "dojo/dom-style", // domStyle.getComputedStyle
21936 "dojo/_base/lang", // lang.mixin
21937 "../main" // for exporting symbols to dijit, remove in 2.0
21938 ], function(array
, domClass
, domGeometry
, domStyle
, lang
, dijit
){
21941 // dijit/layout/utils
21943 var layout
= lang
.getObject("layout", true, dijit
);
21947 // marginBox2contentBox() and layoutChildren()
21951 layout
.marginBox2contentBox = function(/*DomNode*/ node
, /*Object*/ mb
){
21953 // Given the margin-box size of a node, return its content box size.
21954 // Functions like domGeometry.contentBox() but is more reliable since it doesn't have
21955 // to wait for the browser to compute sizes.
21956 var cs
= domStyle
.getComputedStyle(node
);
21957 var me
= domGeometry
.getMarginExtents(node
, cs
);
21958 var pb
= domGeometry
.getPadBorderExtents(node
, cs
);
21960 l
: domStyle
.toPixelValue(node
, cs
.paddingLeft
),
21961 t
: domStyle
.toPixelValue(node
, cs
.paddingTop
),
21962 w
: mb
.w
- (me
.w
+ pb
.w
),
21963 h
: mb
.h
- (me
.h
+ pb
.h
)
21967 function capitalize(word
){
21968 return word
.substring(0,1).toUpperCase() + word
.substring(1);
21971 function size(widget
, dim
){
21973 var newSize
= widget
.resize
? widget
.resize(dim
) : domGeometry
.setMarginBox(widget
.domNode
, dim
);
21975 // record child's size
21977 // if the child returned it's new size then use that
21978 lang
.mixin(widget
, newSize
);
21980 // otherwise, call getMarginBox(), but favor our own numbers when we have them.
21981 // the browser lies sometimes
21982 lang
.mixin(widget
, domGeometry
.getMarginBox(widget
.domNode
));
21983 lang
.mixin(widget
, dim
);
21987 layout
.layoutChildren = function(/*DomNode*/ container
, /*Object*/ dim
, /*Widget[]*/ children
,
21988 /*String?*/ changedRegionId
, /*Number?*/ changedRegionSize
){
21990 // Layout a bunch of child dom nodes within a parent dom node
21994 // {l, t, w, h} object specifying dimensions of container into which to place children
21996 // An array of Widgets or at least objects containing:
21998 // - domNode: pointer to DOM node to position
21999 // - region or layoutAlign: position to place DOM node
22000 // - resize(): (optional) method to set size of node
22001 // - id: (optional) Id of widgets, referenced from resize object, below.
22002 // changedRegionId:
22003 // If specified, the slider for the region with the specified id has been dragged, and thus
22004 // the region's height or width should be adjusted according to changedRegionSize
22005 // changedRegionSize:
22006 // See changedRegionId.
22008 // copy dim because we are going to modify it
22009 dim
= lang
.mixin({}, dim
);
22011 domClass
.add(container
, "dijitLayoutContainer");
22013 // Move "client" elements to the end of the array for layout. a11y dictates that the author
22014 // needs to be able to put them in the document in tab-order, but this algorithm requires that
22015 // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
22016 children
= array
.filter(children
, function(item
){ return item
.region
!= "center" && item
.layoutAlign
!= "client"; })
22017 .concat(array
.filter(children
, function(item
){ return item
.region
== "center" || item
.layoutAlign
== "client"; }));
22019 // set positions/sizes
22020 array
.forEach(children
, function(child
){
22021 var elm
= child
.domNode
,
22022 pos
= (child
.region
|| child
.layoutAlign
);
22024 throw new Error("No region setting for " + child
.id
)
22027 // set elem to upper left corner of unused space; may move it later
22028 var elmStyle
= elm
.style
;
22029 elmStyle
.left
= dim
.l
+"px";
22030 elmStyle
.top
= dim
.t
+"px";
22031 elmStyle
.position
= "absolute";
22033 domClass
.add(elm
, "dijitAlign" + capitalize(pos
));
22035 // Size adjustments to make to this child widget
22036 var sizeSetting
= {};
22038 // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
22039 // panes and width adjustment for left/right align panes.
22040 if(changedRegionId
&& changedRegionId
== child
.id
){
22041 sizeSetting
[child
.region
== "top" || child
.region
== "bottom" ? "h" : "w"] = changedRegionSize
;
22044 // set size && adjust record of remaining space.
22045 // note that setting the width of a <div> may affect its height.
22046 if(pos
== "top" || pos
== "bottom"){
22047 sizeSetting
.w
= dim
.w
;
22048 size(child
, sizeSetting
);
22053 elmStyle
.top
= dim
.t
+ dim
.h
+ "px";
22055 }else if(pos
== "left" || pos
== "right"){
22056 sizeSetting
.h
= dim
.h
;
22057 size(child
, sizeSetting
);
22062 elmStyle
.left
= dim
.l
+ dim
.w
+ "px";
22064 }else if(pos
== "client" || pos
== "center"){
22072 marginBox2contentBox
: layout
.marginBox2contentBox
,
22073 layoutChildren
: layout
.layoutChildren
22078 'dijit/_Contained':function(){
22079 define("dijit/_Contained", [
22080 "dojo/_base/declare", // declare
22081 "./registry" // registry.getEnclosingWidget(), registry.byNode()
22082 ], function(declare
, registry
){
22085 // dijit/_Contained
22087 return declare("dijit._Contained", null, {
22089 // Mixin for widgets that are children of a container widget
22092 // | // make a basic custom widget that knows about it's parents
22093 // | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
22095 _getSibling: function(/*String*/ which
){
22097 // Returns next or previous sibling
22099 // Either "next" or "previous"
22102 var node
= this.domNode
;
22104 node
= node
[which
+"Sibling"];
22105 }while(node
&& node
.nodeType
!= 1);
22106 return node
&& registry
.byNode(node
); // dijit/_WidgetBase
22109 getPreviousSibling: function(){
22111 // Returns null if this is the first child of the parent,
22112 // otherwise returns the next element sibling to the "left".
22114 return this._getSibling("previous"); // dijit/_WidgetBase
22117 getNextSibling: function(){
22119 // Returns null if this is the last child of the parent,
22120 // otherwise returns the next element sibling to the "right".
22122 return this._getSibling("next"); // dijit/_WidgetBase
22125 getIndexInParent: function(){
22127 // Returns the index of this widget within its container parent.
22128 // It returns -1 if the parent does not exist, or if the parent
22129 // is not a dijit._Container
22131 var p
= this.getParent();
22132 if(!p
|| !p
.getIndexOfChild
){
22135 return p
.getIndexOfChild(this); // int
22141 'dijit/form/DataList':function(){
22142 define("dijit/form/DataList", [
22143 "dojo/_base/declare", // declare
22144 "dojo/dom", // dom.byId
22145 "dojo/_base/lang", // lang.trim
22146 "dojo/query", // query
22147 "dojo/store/Memory",
22148 "../registry" // registry.add registry.remove
22149 ], function(declare
, dom
, lang
, query
, MemoryStore
, registry
){
22152 // dijit/form/DataList
22154 function toItem(/*DOMNode*/ option
){
22156 // Convert `<option>` node to hash
22159 value
: option
.value
,
22160 name
: lang
.trim(option
.innerText
|| option
.textContent
|| '')
22164 return declare("dijit.form.DataList", MemoryStore
, {
22166 // Inefficient but small data store specialized for inlined data via OPTION tags
22169 // Provides a store for inlined data like:
22172 // | <option value="AL">Alabama</option>
22175 constructor: function(params
, srcNodeRef
){
22177 // Create the widget.
22178 // params: Object|null
22179 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
22180 // and functions, typically callbacks like onClick.
22181 // The hash can contain any of the widget's properties, excluding read-only properties.
22182 // srcNodeRef: DOMNode|String
22183 // Attach widget to this DOM node.
22185 // store pointer to original DOM tree
22186 this.domNode
= dom
.byId(srcNodeRef
);
22188 lang
.mixin(this, params
);
22190 registry
.add(this); // add to registry so it can be easily found by id
22192 this.domNode
.style
.display
= "none";
22194 this.inherited(arguments
, [{
22195 data
: query("option", this.domNode
).map(toItem
)
22199 destroy: function(){
22200 registry
.remove(this.id
);
22203 fetchSelectedItem: function(){
22205 // Get the option marked as selected, like `<option selected>`.
22206 // Not part of dojo.data API.
22207 var option
= query("> option[selected]", this.domNode
)[0] || query("> option", this.domNode
)[0];
22208 return option
&& toItem(option
);
22214 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
22215 'dijit/form/CheckBox':function(){
22217 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n"}});
22218 define("dijit/form/CheckBox", [
22220 "dojo/_base/declare", // declare
22221 "dojo/dom-attr", // domAttr.set
22222 "dojo/has", // has("dijit-legacy-requires")
22223 "dojo/query", // query
22226 "./_CheckBoxMixin",
22227 "dojo/text!./templates/CheckBox.html",
22228 "dojo/NodeList-dom" // NodeList.addClass/removeClass
22229 ], function(require
, declare
, domAttr
, has
, query
, ready
, ToggleButton
, _CheckBoxMixin
, template
){
22232 // dijit/form/CheckBox
22234 // Back compat w/1.6, remove for 2.0
22235 if(has("dijit-legacy-requires")){
22236 ready(0, function(){
22237 var requires
= ["dijit/form/RadioButton"];
22238 require(requires
); // use indirection so modules not rolled into a build
22242 return declare("dijit.form.CheckBox", [ToggleButton
, _CheckBoxMixin
], {
22244 // Same as an HTML checkbox, but with fancy styling.
22247 // User interacts with real html inputs.
22248 // On onclick (which occurs by mouse click, space-bar, or
22249 // using the arrow keys to switch the selected radio button),
22250 // we update the state of the checkbox/radio.
22252 // There are two modes:
22254 // 1. High contrast mode
22257 // In case 1, the regular html inputs are shown and used by the user.
22258 // In case 2, the regular html inputs are invisible but still used by
22259 // the user. They are turned quasi-invisible and overlay the background-image.
22261 templateString
: template
,
22263 baseClass
: "dijitCheckBox",
22265 _setValueAttr: function(/*String|Boolean*/ newValue
, /*Boolean*/ priorityChange
){
22267 // Handler for value= attribute to constructor, and also calls to
22268 // set('value', val).
22270 // During initialization, just saves as attribute to the `<input type=checkbox>`.
22272 // After initialization,
22273 // when passed a boolean, controls whether or not the CheckBox is checked.
22274 // If passed a string, changes the value attribute of the CheckBox (the one
22275 // specified as "value" when the CheckBox was constructed
22276 // (ex: `<input data-dojo-type="dijit/CheckBox" value="chicken">`).
22278 // `widget.set('value', string)` will check the checkbox and change the value to the
22279 // specified string.
22281 // `widget.set('value', boolean)` will change the checked state.
22283 if(typeof newValue
== "string"){
22284 this.inherited(arguments
);
22288 this.set('checked', newValue
, priorityChange
);
22291 _getValueAttr: function(){
22293 // Hook so get('value') works.
22295 // If the CheckBox is checked, returns the value attribute.
22296 // Otherwise returns false.
22297 return (this.checked
? this.value
: false);
22300 // Override behavior from Button, since we don't have an iconNode
22301 _setIconClassAttr
: null,
22303 postMixInProperties: function(){
22304 this.inherited(arguments
);
22306 // Need to set initial checked state as part of template, so that form submit works.
22307 // domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
22308 // to <body>, see #8666
22309 this.checkedAttrSetting
= this.checked
? "checked" : "";
22312 _fillContent: function(){
22313 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
22314 // since CheckBox doesn't even have a container
22317 _onFocus: function(){
22319 query("label[for='"+this.id
+"']").addClass("dijitFocusedLabel");
22321 this.inherited(arguments
);
22324 _onBlur: function(){
22326 query("label[for='"+this.id
+"']").removeClass("dijitFocusedLabel");
22328 this.inherited(arguments
);
22334 'dijit/tree/_dndSelector':function(){
22335 define("dijit/tree/_dndSelector", [
22336 "dojo/_base/array", // array.filter array.forEach array.map
22337 "dojo/_base/connect", // connect.isCopyKey
22338 "dojo/_base/declare", // declare
22339 "dojo/_base/Deferred", // Deferred
22340 "dojo/_base/kernel", // global
22341 "dojo/_base/lang", // lang.hitch
22342 "dojo/cookie", // cookie
22343 "dojo/mouse", // mouse.isLeft
22347 ], function(array
, connect
, declare
, Deferred
, kernel
, lang
, cookie
, mouse
, on
, touch
, _dndContainer
){
22350 // dijit/tree/_dndSelector
22353 return declare("dijit.tree._dndSelector", _dndContainer
, {
22355 // This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
22356 // It's based on `dojo/dnd/Selector`.
22361 // selection: Object
22362 // (id to DomNode) map for every TreeNode that's currently selected.
22363 // The DOMNode is the TreeNode.rowNode.
22367 constructor: function(){
22374 this.anchor
= null;
22376 if(!this.cookieName
&& this.tree
.id
){
22377 this.cookieName
= this.tree
.id
+ "SaveSelectedCookie";
22381 on(this.tree
.domNode
, touch
.press
, lang
.hitch(this,"onMouseDown")),
22382 on(this.tree
.domNode
, touch
.release
, lang
.hitch(this,"onMouseUp")),
22383 on(this.tree
.domNode
, touch
.move, lang
.hitch(this,"onMouseMove"))
22387 // singular: Boolean
22388 // Allows selection of only one element, if true.
22389 // Tree hasn't been tested in singular=true mode, unclear if it works.
22393 getSelectedTreeNodes: function(){
22395 // Returns a list of selected node(s).
22396 // Used by dndSource on the start of a drag.
22399 var nodes
=[], sel
= this.selection
;
22401 nodes
.push(sel
[i
]);
22406 selectNone: function(){
22408 // Unselects all items
22412 this.setSelection([]);
22413 return this; // self
22416 destroy: function(){
22418 // Prepares the object to be garbage-collected
22419 this.inherited(arguments
);
22420 this.selection
= this.anchor
= null;
22422 addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor
){
22424 // add node to current selection
22427 // isAnchor: Boolean
22428 // Whether the node should become anchor.
22430 this.setSelection(this.getSelectedTreeNodes().concat( [node
] ));
22431 if(isAnchor
){ this.anchor
= node
; }
22434 removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
22436 // remove node from current selection
22439 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
22442 isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
22444 // return true if node is currently selected
22446 // the node to check whether it's in the current selection
22448 return node.id && !!this.selection[node.id];
22450 setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
22452 // set the list of selected nodes to be exactly newSelection. All changes to the
22453 // selection should be passed through this function, which ensures that derived
22454 // attributes are kept up to date. Anchor will be deleted if it has been removed
22455 // from the selection, but no new anchor will be added by this function.
22456 // newSelection: Node[]
22457 // list of tree nodes to make selected
22458 var oldSelection = this.getSelectedTreeNodes();
22459 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
22460 node.setSelected(false);
22461 if(this.anchor == node){
22462 delete this.anchor;
22464 delete this.selection[node.id];
22466 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
22467 node.setSelected(true);
22468 this.selection[node.id] = node;
22470 this._updateSelectionProperties();
22472 _setDifference: function(xs,ys){
22474 // Returns a copy of xs which lacks any objects
22475 // occurring in ys. Checks for membership by
22476 // modifying and then reading the object, so it will
22477 // not properly handle sets of numbers or strings.
22479 array.forEach(ys, function(y){ y.__exclude__ = true; });
22480 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
22482 // clean up after ourselves.
22483 array.forEach(ys, function(y){ delete y['__exclude__'] });
22486 _updateSelectionProperties: function(){
22488 // Update the following tree properties from the current selection:
22489 // path[s], selectedItem[s], selectedNode[s]
22491 var selected = this.getSelectedTreeNodes();
22492 var paths = [], nodes = [], selects = [];
22493 array.forEach(selected, function(node){
22494 var ary = node.getTreePath(), model = this.tree.model;
22497 ary = array.map(ary, function(item){
22498 return model.getIdentity(item);
22500 selects.push(ary.join("/"))
22502 var items = array.map(nodes,function(node){ return node.item; });
22503 this.tree._set("paths", paths);
22504 this.tree._set("path", paths[0] || []);
22505 this.tree._set("selectedNodes", nodes);
22506 this.tree._set("selectedNode", nodes[0] || null);
22507 this.tree._set("selectedItems", items);
22508 this.tree._set("selectedItem", items[0] || null);
22509 if (this.tree.persist && selects.length > 0) {
22510 cookie(this.cookieName, selects.join(","), {expires:365});
22513 _getSavedPaths: function(){
22515 // Returns paths of nodes that were selected previously and saved in the cookie.
22517 var tree = this.tree;
22518 if(tree.persist && tree.dndController.cookieName){
22519 var oreo, paths = [];
22520 oreo = cookie(tree.dndController.cookieName);
22522 paths = array.map(oreo.split(","), function(path){
22523 return path.split("/");
22530 onMouseDown: function(e){
22532 // Event processor for onmousedown/ontouchstart
22534 // onmousedown/ontouchstart event
22538 // ignore click on expando node
22539 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
22541 if(mouse.isLeft(e)){
22542 // Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
22543 // for mobile because it will break things completely, see #15838.
22544 e.preventDefault();
22545 }else if(e.type != "touchstart"){
22546 // Ignore right click
22550 var treeNode = this.current,
22551 copy = connect.isCopyKey(e), id = treeNode.id;
22553 // if shift key is not pressed, and the node is already in the selection,
22554 // delay deselection until onmouseup so in the case of DND, deselection
22555 // will be canceled by onmousemove.
22556 if(!this.singular && !e.shiftKey && this.selection[id]){
22557 this._doDeselect = true;
22560 this._doDeselect = false;
22562 this.userSelect(treeNode, copy, e.shiftKey);
22565 onMouseUp: function(e){
22567 // Event processor for onmouseup/ontouchend
22569 // onmouseup/ontouchend event
22573 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
22574 // a already selected item (to deselect the item), or click on a not-yet selected item
22575 // (which should remove all current selection, and add the clicked item). This can not
22576 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
22577 // the deselection logic here, the user can drags an already selected item.
22578 if(!this._doDeselect){ return; }
22579 this._doDeselect = false;
22580 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
22582 onMouseMove: function(/*===== e =====*/){
22584 // event processor for onmousemove/ontouchmove
22586 // onmousemove/ontouchmove event
22587 this._doDeselect
= false;
22590 _compareNodes: function(n1
, n2
){
22595 if('sourceIndex' in document
.documentElement
){ //IE
22596 //TODO: does not yet work if n1 and/or n2 is a text node
22597 return n1
.sourceIndex
- n2
.sourceIndex
;
22598 }else if('compareDocumentPosition' in document
.documentElement
){ //FF, Opera
22599 return n1
.compareDocumentPosition(n2
) & 2 ? 1: -1;
22600 }else if(document
.createRange
){ //Webkit
22601 var r1
= doc
.createRange();
22602 r1
.setStartBefore(n1
);
22604 var r2
= doc
.createRange();
22605 r2
.setStartBefore(n2
);
22607 return r1
.compareBoundaryPoints(r1
.END_TO_END
, r2
);
22609 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
22613 userSelect: function(node
, multi
, range
){
22615 // Add or remove the given node from selection, responding
22616 // to a user action such as a click or keypress.
22618 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
22620 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
22625 if(this.anchor
== node
&& multi
){
22628 this.setSelection([node
]);
22629 this.anchor
= node
;
22632 if(range
&& this.anchor
){
22633 var cr
= this._compareNodes(this.anchor
.rowNode
, node
.rowNode
),
22634 begin
, end
, anchor
= this.anchor
;
22636 if(cr
< 0){ //current is after anchor
22639 }else{ //current is before anchor
22644 //add everything betweeen begin and end inclusively
22645 while(begin
!= end
){
22647 begin
= this.tree
._getNextNode(begin
);
22651 this.setSelection(nodes
);
22653 if( this.selection
[ node
.id
] && multi
){
22654 this.removeTreeNode( node
);
22656 this.addTreeNode(node
, true);
22658 this.setSelection([node
]);
22659 this.anchor
= node
;
22665 getItem: function(/*String*/ key
){
22667 // Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
22668 // Called by dojo/dnd/Source.checkAcceptance().
22672 var widget
= this.selection
[key
];
22676 }; // dojo/dnd/Container._Item
22679 forInSelectedItems: function(/*Function*/ f
, /*Object?*/ o
){
22681 // Iterates over selected items;
22682 // see `dojo/dnd/Container.forInItems()` for details
22683 o
= o
|| kernel
.global
;
22684 for(var id
in this.selection
){
22685 // console.log("selected item id: " + id);
22686 f
.call(o
, this.getItem(id
), id
, this);
22693 'dijit/_Container':function(){
22694 define("dijit/_Container", [
22695 "dojo/_base/array", // array.forEach array.indexOf
22696 "dojo/_base/declare", // declare
22697 "dojo/dom-construct" // domConstruct.place
22698 ], function(array
, declare
, domConstruct
){
22701 // dijit/_Container
22703 return declare("dijit._Container", null, {
22705 // Mixin for widgets that contain HTML and/or a set of widget children.
22707 buildRendering: function(){
22708 this.inherited(arguments
);
22709 if(!this.containerNode
){
22710 // all widgets with descendants must set containerNode
22711 this.containerNode
= this.domNode
;
22715 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex
){
22717 // Makes the given widget a child of this widget.
22719 // Inserts specified child widget's dom node as a child of this widget's
22720 // container node, and possibly does other processing (such as layout).
22722 // Functionality is undefined if this widget contains anything besides
22723 // a list of child widgets (ie, if it contains arbitrary non-widget HTML).
22725 var refNode
= this.containerNode
;
22726 if(insertIndex
&& typeof insertIndex
== "number"){
22727 var children
= this.getChildren();
22728 if(children
&& children
.length
>= insertIndex
){
22729 refNode
= children
[insertIndex
-1].domNode
;
22730 insertIndex
= "after";
22733 domConstruct
.place(widget
.domNode
, refNode
, insertIndex
);
22735 // If I've been started but the child widget hasn't been started,
22736 // start it now. Make sure to do this after widget has been
22737 // inserted into the DOM tree, so it can see that it's being controlled by me,
22738 // so it doesn't try to size itself.
22739 if(this._started
&& !widget
._started
){
22744 removeChild: function(/*Widget|int*/ widget
){
22746 // Removes the passed widget instance from this widget but does
22747 // not destroy it. You can also pass in an integer indicating
22748 // the index within the container to remove (ie, removeChild(5) removes the sixth widget).
22750 if(typeof widget
== "number"){
22751 widget
= this.getChildren()[widget
];
22755 var node
= widget
.domNode
;
22756 if(node
&& node
.parentNode
){
22757 node
.parentNode
.removeChild(node
); // detach but don't destroy
22762 hasChildren: function(){
22764 // Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
22765 return this.getChildren().length
> 0; // Boolean
22768 _getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir
){
22770 // Get the next or previous widget sibling of child
22772 // if 1, get the next sibling
22773 // if -1, get the previous sibling
22776 var children
= this.getChildren(),
22777 idx
= array
.indexOf(this.getChildren(), child
); // int
22778 return children
[idx
+ dir
];
22781 getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
22783 // Gets the index of the child in this container or -1 if not found
22784 return array.indexOf(this.getChildren(), child); // int
22790 'dojo/data/ItemFileReadStore':function(){
22791 define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
22792 "../Evented", "./util/filter", "./util/simpleFetch", "../date/stamp"
22793 ], function(kernel, lang, declare, array, xhr, Evented, filterUtil, simpleFetch, dateStamp){
22796 // dojo/data/ItemFileReadStore
22798 var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
22800 // The ItemFileReadStore implements the dojo/data/api/Read API and reads
22801 // data from JSON files that have contents in this format --
22803 // | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
22804 // | { name:'Fozzie Bear', wears:['hat', 'tie']},
22805 // | { name:'Miss Piggy', pets:'Foo-Foo'}
22807 // Note that it can also contain an 'identifier' property that specified which attribute on the items
22808 // in the array of items that acts as the unique identifier for that item.
22810 constructor: function(/* Object */ keywordParameters
){
22813 // keywordParameters:
22814 // {url: String} {data: jsonObject} {typeMap: object}
22815 // The structure of the typeMap object is as follows:
22817 // | type0: function || object,
22818 // | type1: function || object,
22820 // | typeN: function || object
22822 // Where if it is a function, it is assumed to be an object constructor that takes the
22823 // value of _value as the initialization parameters. If it is an object, then it is assumed
22824 // to be an object of general form:
22826 // | type: function, //constructor.
22827 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
22830 this._arrayOfAllItems
= [];
22831 this._arrayOfTopLevelItems
= [];
22832 this._loadFinished
= false;
22833 this._jsonFileUrl
= keywordParameters
.url
;
22834 this._ccUrl
= keywordParameters
.url
;
22835 this.url
= keywordParameters
.url
;
22836 this._jsonData
= keywordParameters
.data
;
22838 this._datatypeMap
= keywordParameters
.typeMap
|| {};
22839 if(!this._datatypeMap
['Date']){
22840 //If no default mapping for dates, then set this as default.
22841 //We use the dojo/date/stamp here because the ISO format is the 'dojo way'
22842 //of generically representing dates.
22843 this._datatypeMap
['Date'] = {
22845 deserialize: function(value
){
22846 return dateStamp
.fromISOString(value
);
22850 this._features
= {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
22851 this._itemsByIdentity
= null;
22852 this._storeRefPropName
= "_S"; // Default name for the store reference to attach to every item.
22853 this._itemNumPropName
= "_0"; // Default Item Id for isItem to attach to every item.
22854 this._rootItemPropName
= "_RI"; // Default Item Id for isItem to attach to every item.
22855 this._reverseRefMap
= "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
22856 this._loadInProgress
= false; //Got to track the initial load to prevent duelling loads of the dataset.
22857 this._queuedFetches
= [];
22858 if(keywordParameters
.urlPreventCache
!== undefined){
22859 this.urlPreventCache
= keywordParameters
.urlPreventCache
?true:false;
22861 if(keywordParameters
.hierarchical
!== undefined){
22862 this.hierarchical
= keywordParameters
.hierarchical
?true:false;
22864 if(keywordParameters
.clearOnClose
){
22865 this.clearOnClose
= true;
22867 if("failOk" in keywordParameters
){
22868 this.failOk
= keywordParameters
.failOk
?true:false;
22872 url
: "", // use "" rather than undefined for the benefit of the parser (#3539)
22874 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
22875 //when clearOnClose and close is used.
22878 data
: null, // define this so that the parser can populate it
22880 typeMap
: null, //Define so parser can populate.
22882 // clearOnClose: Boolean
22883 // Parameter to allow users to specify if a close call should force a reload or not.
22884 // By default, it retains the old behavior of not clearing if close is called. But
22885 // if set true, the store will be reset to default state. Note that by doing this,
22886 // all item handles will become invalid and a new fetch must be issued.
22887 clearOnClose
: false,
22889 // urlPreventCache: Boolean
22890 // Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
22891 // Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
22892 // Added for tracker: #6072
22893 urlPreventCache
: false,
22896 // Parameter for specifying that it is OK for the xhrGet call to fail silently.
22899 // hierarchical: Boolean
22900 // Parameter to indicate to process data from the url as hierarchical
22901 // (data items can contain other data items in js form). Default is true
22902 // for backwards compatibility. False means only root items are processed
22903 // as items, all child objects outside of type-mapped objects and those in
22904 // specific reference format, are left straight JS data objects.
22905 hierarchical
: true,
22907 _assertIsItem: function(/* dojo/data/api/Item */ item){
22909 // This function tests whether the item passed in is indeed an item in the store.
22911 // The item to test for being contained by the store.
22912 if(!this.isItem(item)){
22913 throw new Error(this.declaredClass + ": Invalid item argument.");
22917 _assertIsAttribute: function(/* attribute-name-string */ attribute
){
22919 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
22921 // The attribute to test for being contained by the store.
22922 if(typeof attribute
!== "string"){
22923 throw new Error(this.declaredClass
+ ": Invalid attribute argument.");
22927 getValue: function( /* dojo/data/api/Item */ item,
22928 /* attribute-name-string */ attribute
,
22929 /* value? */ defaultValue
){
22931 // See dojo/data/api/Read.getValue()
22932 var values
= this.getValues(item
, attribute
);
22933 return (values
.length
> 0)?values
[0]:defaultValue
; // mixed
22936 getValues: function(/* dojo/data/api/Item */ item,
22937 /* attribute-name-string */ attribute
){
22939 // See dojo/data/api/Read.getValues()
22941 this._assertIsItem(item
);
22942 this._assertIsAttribute(attribute
);
22943 // Clone it before returning. refs: #10474
22944 return (item
[attribute
] || []).slice(0); // Array
22947 getAttributes: function(/* dojo/data/api/Item */ item){
22949 // See dojo/data/api/Read.getAttributes()
22950 this._assertIsItem(item);
22951 var attributes = [];
22952 for(var key in item){
22953 // Save off only the real item attributes, not the special id marks for O(1) isItem.
22954 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
22955 attributes.push(key);
22958 return attributes; // Array
22961 hasAttribute: function( /* dojo/data/api/Item */ item,
22962 /* attribute-name-string */ attribute
){
22964 // See dojo/data/api/Read.hasAttribute()
22965 this._assertIsItem(item
);
22966 this._assertIsAttribute(attribute
);
22967 return (attribute
in item
);
22970 containsValue: function(/* dojo/data/api/Item */ item,
22971 /* attribute-name-string */ attribute
,
22972 /* anything */ value
){
22974 // See dojo/data/api/Read.containsValue()
22975 var regexp
= undefined;
22976 if(typeof value
=== "string"){
22977 regexp
= filterUtil
.patternToRegExp(value
, false);
22979 return this._containsValue(item
, attribute
, value
, regexp
); //boolean.
22982 _containsValue: function( /* dojo/data/api/Item */ item,
22983 /* attribute-name-string */ attribute
,
22984 /* anything */ value
,
22985 /* RegExp?*/ regexp
){
22987 // Internal function for looking at the values contained by the item.
22989 // Internal function for looking at the values contained by the item. This
22990 // function allows for denoting if the comparison should be case sensitive for
22991 // strings or not (for handling filtering cases where string case should not matter)
22993 // The data item to examine for attribute values.
22995 // The attribute to inspect.
22997 // The value to match.
22999 // Optional regular expression generated off value if value was of string type to handle wildcarding.
23000 // If present and attribute values are string, then it can be used for comparison instead of 'value'
23001 return array
.some(this.getValues(item
, attribute
), function(possibleValue
){
23002 if(possibleValue
!== null && !lang
.isObject(possibleValue
) && regexp
){
23003 if(possibleValue
.toString().match(regexp
)){
23004 return true; // Boolean
23006 }else if(value
=== possibleValue
){
23007 return true; // Boolean
23012 isItem: function(/* anything */ something
){
23014 // See dojo/data/api/Read.isItem()
23015 if(something
&& something
[this._storeRefPropName
] === this){
23016 if(this._arrayOfAllItems
[something
[this._itemNumPropName
]] === something
){
23020 return false; // Boolean
23023 isItemLoaded: function(/* anything */ something
){
23025 // See dojo/data/api/Read.isItemLoaded()
23026 return this.isItem(something
); //boolean
23029 loadItem: function(/* object */ keywordArgs
){
23031 // See dojo/data/api/Read.loadItem()
23032 this._assertIsItem(keywordArgs
.item
);
23035 getFeatures: function(){
23037 // See dojo/data/api/Read.getFeatures()
23038 return this._features
; //Object
23041 getLabel: function(/* dojo/data/api/Item */ item){
23043 // See dojo/data/api/Read.getLabel()
23044 if(this._labelAttr && this.isItem(item)){
23045 return this.getValue(item,this._labelAttr); //String
23047 return undefined; //undefined
23050 getLabelAttributes: function(/* dojo/data/api/Item */ item){
23052 // See dojo/data/api/Read.getLabelAttributes()
23053 if(this._labelAttr){
23054 return [this._labelAttr]; //array
23056 return null; //null
23059 filter: function(/* Object */ requestArgs
, /* item[] */ arrayOfItems
, /* Function */ findCallback
){
23061 // This method handles the basic filtering needs for ItemFile* based stores.
23065 if(requestArgs
.query
){
23067 ignoreCase
= requestArgs
.queryOptions
? requestArgs
.queryOptions
.ignoreCase
: false;
23069 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
23070 //same value for each item examined. Much more efficient.
23071 var regexpList
= {};
23072 for(key
in requestArgs
.query
){
23073 value
= requestArgs
.query
[key
];
23074 if(typeof value
=== "string"){
23075 regexpList
[key
] = filterUtil
.patternToRegExp(value
, ignoreCase
);
23076 }else if(value
instanceof RegExp
){
23077 regexpList
[key
] = value
;
23080 for(i
= 0; i
< arrayOfItems
.length
; ++i
){
23082 var candidateItem
= arrayOfItems
[i
];
23083 if(candidateItem
=== null){
23086 for(key
in requestArgs
.query
){
23087 value
= requestArgs
.query
[key
];
23088 if(!this._containsValue(candidateItem
, key
, value
, regexpList
[key
])){
23094 items
.push(candidateItem
);
23097 findCallback(items
, requestArgs
);
23099 // We want a copy to pass back in case the parent wishes to sort the array.
23100 // We shouldn't allow resort of the internal list, so that multiple callers
23101 // can get lists and sort without affecting each other. We also need to
23102 // filter out any null values that have been left as a result of deleteItem()
23103 // calls in ItemFileWriteStore.
23104 for(i
= 0; i
< arrayOfItems
.length
; ++i
){
23105 var item
= arrayOfItems
[i
];
23110 findCallback(items
, requestArgs
);
23114 _fetchItems: function( /* Object */ keywordArgs
,
23115 /* Function */ findCallback
,
23116 /* Function */ errorCallback
){
23118 // See dojo/data/util.simpleFetch.fetch()
23121 if(this._loadFinished
){
23122 this.filter(keywordArgs
, this._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
23124 //Do a check on the JsonFileUrl and crosscheck it.
23125 //If it doesn't match the cross-check, it needs to be updated
23126 //This allows for either url or _jsonFileUrl to he changed to
23127 //reset the store load location. Done this way for backwards
23128 //compatibility. People use _jsonFileUrl (even though officially
23130 if(this._jsonFileUrl
!== this._ccUrl
){
23131 kernel
.deprecated(this.declaredClass
+ ": ",
23132 "To change the url, set the url property of the store," +
23133 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23134 this._ccUrl
= this._jsonFileUrl
;
23135 this.url
= this._jsonFileUrl
;
23136 }else if(this.url
!== this._ccUrl
){
23137 this._jsonFileUrl
= this.url
;
23138 this._ccUrl
= this.url
;
23141 //See if there was any forced reset of data.
23142 if(this.data
!= null){
23143 this._jsonData
= this.data
;
23147 if(this._jsonFileUrl
){
23148 //If fetches come in before the loading has finished, but while
23149 //a load is in progress, we have to defer the fetching to be
23150 //invoked in the callback.
23151 if(this._loadInProgress
){
23152 this._queuedFetches
.push({args
: keywordArgs
, filter
: lang
.hitch(self
, "filter"), findCallback
: lang
.hitch(self
, findCallback
)});
23154 this._loadInProgress
= true;
23156 url
: self
._jsonFileUrl
,
23157 handleAs
: "json-comment-optional",
23158 preventCache
: this.urlPreventCache
,
23159 failOk
: this.failOk
23161 var getHandler
= xhr
.get(getArgs
);
23162 getHandler
.addCallback(function(data
){
23164 self
._getItemsFromLoadedData(data
);
23165 self
._loadFinished
= true;
23166 self
._loadInProgress
= false;
23168 self
.filter(keywordArgs
, self
._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
23169 self
._handleQueuedFetches();
23171 self
._loadFinished
= true;
23172 self
._loadInProgress
= false;
23173 errorCallback(e
, keywordArgs
);
23176 getHandler
.addErrback(function(error
){
23177 self
._loadInProgress
= false;
23178 errorCallback(error
, keywordArgs
);
23181 //Wire up the cancel to abort of the request
23182 //This call cancel on the deferred if it hasn't been called
23183 //yet and then will chain to the simple abort of the
23184 //simpleFetch keywordArgs
23185 var oldAbort
= null;
23186 if(keywordArgs
.abort
){
23187 oldAbort
= keywordArgs
.abort
;
23189 keywordArgs
.abort = function(){
23190 var df
= getHandler
;
23191 if(df
&& df
.fired
=== -1){
23196 oldAbort
.call(keywordArgs
);
23200 }else if(this._jsonData
){
23202 this._loadFinished
= true;
23203 this._getItemsFromLoadedData(this._jsonData
);
23204 this._jsonData
= null;
23205 self
.filter(keywordArgs
, this._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
23207 errorCallback(e
, keywordArgs
);
23210 errorCallback(new Error(this.declaredClass
+ ": No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs
);
23215 _handleQueuedFetches: function(){
23217 // Internal function to execute delayed request in the store.
23219 //Execute any deferred fetches now.
23220 if(this._queuedFetches
.length
> 0){
23221 for(var i
= 0; i
< this._queuedFetches
.length
; i
++){
23222 var fData
= this._queuedFetches
[i
],
23223 delayedQuery
= fData
.args
,
23224 delayedFilter
= fData
.filter
,
23225 delayedFindCallback
= fData
.findCallback
;
23227 delayedFilter(delayedQuery
, this._getItemsArray(delayedQuery
.queryOptions
), delayedFindCallback
);
23229 this.fetchItemByIdentity(delayedQuery
);
23232 this._queuedFetches
= [];
23236 _getItemsArray: function(/*object?*/queryOptions
){
23238 // Internal function to determine which list of items to search over.
23239 // queryOptions: The query options parameter, if any.
23240 if(queryOptions
&& queryOptions
.deep
){
23241 return this._arrayOfAllItems
;
23243 return this._arrayOfTopLevelItems
;
23246 close: function(/*dojo/data/api/Request|Object?*/ request){
23248 // See dojo/data/api/Read.close()
23249 if(this.clearOnClose &&
23250 this._loadFinished &&
23251 !this._loadInProgress){
23252 //Reset all internalsback to default state. This will force a reload
23253 //on next fetch. This also checks that the data or url param was set
23254 //so that the store knows it can get data. Without one of those being set,
23255 //the next fetch will trigger an error.
23257 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
23258 (this.url == "" || this.url == null)
23259 ) && this.data == null){
23260 console.debug(this.declaredClass + ": WARNING! Data reload " +
23261 " information has not been provided." +
23262 " Please set 'url' or 'data' to the appropriate value before" +
23263 " the next fetch");
23265 this._arrayOfAllItems = [];
23266 this._arrayOfTopLevelItems = [];
23267 this._loadFinished = false;
23268 this._itemsByIdentity = null;
23269 this._loadInProgress = false;
23270 this._queuedFetches = [];
23274 _getItemsFromLoadedData: function(/* Object */ dataObject
){
23276 // Function to parse the loaded data into item format and build the internal items array.
23278 // Function to parse the loaded data into item format and build the internal items array.
23280 // The JS data object containing the raw data to convery into item format.
23282 // Array of items in store item format.
23284 // First, we define a couple little utility functions...
23285 var addingArrays
= false,
23288 function valueIsAnItem(/* anything */ aValue
){
23290 // Given any sort of value that could be in the raw json data,
23291 // return true if we should interpret the value as being an
23292 // item itself, rather than a literal value or a reference.
23294 // | false == valueIsAnItem("Kermit");
23295 // | false == valueIsAnItem(42);
23296 // | false == valueIsAnItem(new Date());
23297 // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
23298 // | false == valueIsAnItem({_reference:'Kermit'});
23299 // | true == valueIsAnItem({name:'Kermit', color:'green'});
23300 // | true == valueIsAnItem({iggy:'pop'});
23301 // | true == valueIsAnItem({foo:42});
23302 return (aValue
!== null) &&
23303 (typeof aValue
=== "object") &&
23304 (!lang
.isArray(aValue
) || addingArrays
) &&
23305 (!lang
.isFunction(aValue
)) &&
23306 (aValue
.constructor == Object
|| lang
.isArray(aValue
)) &&
23307 (typeof aValue
._reference
=== "undefined") &&
23308 (typeof aValue
._type
=== "undefined") &&
23309 (typeof aValue
._value
=== "undefined") &&
23313 function addItemAndSubItemsToArrayOfAllItems(/* dojo/data/api/Item */ anItem){
23314 self._arrayOfAllItems.push(anItem);
23315 for(var attribute in anItem){
23316 var valueForAttribute = anItem[attribute];
23317 if(valueForAttribute){
23318 if(lang.isArray(valueForAttribute)){
23319 var valueArray = valueForAttribute;
23320 for(var k = 0; k < valueArray.length; ++k){
23321 var singleValue = valueArray[k];
23322 if(valueIsAnItem(singleValue)){
23323 addItemAndSubItemsToArrayOfAllItems(singleValue);
23327 if(valueIsAnItem(valueForAttribute)){
23328 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
23335 this._labelAttr = dataObject.label;
23337 // We need to do some transformations to convert the data structure
23338 // that we read from the file into a format that will be convenient
23339 // to work with in memory.
23341 // Step 1: Walk through the object hierarchy and build a list of all items
23344 this._arrayOfAllItems = [];
23345 this._arrayOfTopLevelItems = dataObject.items;
23347 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
23348 item = this._arrayOfTopLevelItems[i];
23349 if(lang.isArray(item)){
23350 addingArrays = true;
23352 addItemAndSubItemsToArrayOfAllItems(item);
23353 item[this._rootItemPropName]=true;
23356 // Step 2: Walk through all the attribute values of all the items,
23357 // and replace single values with arrays. For example, we change this:
23358 // { name:'Miss Piggy', pets:'Foo-Foo'}
23360 // { name:['Miss Piggy'], pets:['Foo-Foo']}
23362 // We also store the attribute names so we can validate our store
23363 // reference and item id special properties for the O(1) isItem
23364 var allAttributeNames = {},
23367 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23368 item = this._arrayOfAllItems[i];
23370 if(key !== this._rootItemPropName){
23371 var value = item[key];
23372 if(value !== null){
23373 if(!lang.isArray(value)){
23374 item[key] = [value];
23377 item[key] = [null];
23380 allAttributeNames[key]=key;
23384 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
23385 // This should go really fast, it will generally never even run the loop.
23386 while(allAttributeNames[this._storeRefPropName]){
23387 this._storeRefPropName += "_";
23389 while(allAttributeNames[this._itemNumPropName]){
23390 this._itemNumPropName += "_";
23392 while(allAttributeNames[this._reverseRefMap]){
23393 this._reverseRefMap += "_";
23396 // Step 4: Some data files specify an optional 'identifier', which is
23397 // the name of an attribute that holds the identity of each item.
23398 // If this data file specified an identifier attribute, then build a
23399 // hash table of items keyed by the identity of the items.
23402 var identifier = dataObject.identifier;
23404 this._itemsByIdentity = {};
23405 this._features['dojo.data.api.Identity'] = identifier;
23406 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23407 item = this._arrayOfAllItems[i];
23408 arrayOfValues = item[identifier];
23409 var identity = arrayOfValues[0];
23410 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23411 this._itemsByIdentity[identity] = item;
23413 if(this._jsonFileUrl){
23414 throw new Error(this.declaredClass + ": The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23415 }else if(this._jsonData){
23416 throw new Error(this.declaredClass + ": The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23421 this._features['dojo.data.api.Identity'] = Number;
23424 // Step 5: Walk through all the items, and set each item's properties
23425 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
23426 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23427 item = this._arrayOfAllItems[i];
23428 item[this._storeRefPropName] = this;
23429 item[this._itemNumPropName] = i;
23432 // Step 6: We walk through all the attribute values of all the items,
23433 // looking for type/value literals and item-references.
23435 // We replace item-references with pointers to items. For example, we change:
23436 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23438 // { name:['Kermit'], friends:[miss_piggy] }
23439 // (where miss_piggy is the object representing the 'Miss Piggy' item).
23441 // We replace type/value pairs with typed-literals. For example, we change:
23442 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
23444 // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
23446 // We also generate the associate map for all items for the O(1) isItem function.
23447 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23448 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23450 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
23451 for(var j = 0; j < arrayOfValues.length; ++j){
23452 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
23453 if(value !== null && typeof value == "object"){
23454 if(("_type" in value) && ("_value" in value)){
23455 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
23456 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
23458 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
23459 }else if(lang.isFunction(mappingObj)){
23460 arrayOfValues[j] = new mappingObj(value._value);
23461 }else if(lang.isFunction(mappingObj.deserialize)){
23462 arrayOfValues[j] = mappingObj.deserialize(value._value);
23464 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
23467 if(value._reference){
23468 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
23469 if(!lang.isObject(referenceDescription)){
23470 // example: 'Miss Piggy'
23471 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
23472 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
23474 // example: {name:'Miss Piggy'}
23475 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23476 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
23477 var candidateItem = this._arrayOfAllItems[k],
23479 for(var refKey in referenceDescription){
23480 if(candidateItem[refKey] != referenceDescription[refKey]){
23485 arrayOfValues[j] = candidateItem;
23489 if(this.referenceIntegrity){
23490 var refItem = arrayOfValues[j];
23491 if(this.isItem(refItem)){
23492 this._addReferenceToMap(refItem, item, key);
23495 }else if(this.isItem(value)){
23496 //It's a child item (not one referenced through _reference).
23497 //We need to treat this as a referenced item, so it can be cleaned up
23498 //in a write store easily.
23499 if(this.referenceIntegrity){
23500 this._addReferenceToMap(value, item, key);
23509 _addReferenceToMap: function(/*item*/ refItem
, /*item*/ parentItem
, /*string*/ attribute
){
23511 // Method to add an reference map entry for an item and attribute.
23513 // Method to add an reference map entry for an item and attribute.
23515 // The item that is referenced.
23517 // The item that holds the new reference to refItem.
23519 // The attribute on parentItem that contains the new reference.
23521 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
23524 getIdentity: function(/* dojo/data/api/Item */ item){
23526 // See dojo/data/api/Identity.getIdentity()
23527 var identifier = this._features['dojo.data.api.Identity'];
23528 if(identifier === Number){
23529 return item[this._itemNumPropName]; // Number
23531 var arrayOfValues = item[identifier];
23533 return arrayOfValues[0]; // Object|String
23536 return null; // null
23539 fetchItemByIdentity: function(/* Object */ keywordArgs
){
23541 // See dojo/data/api/Identity.fetchItemByIdentity()
23543 // Hasn't loaded yet, we have to trigger the load.
23546 if(!this._loadFinished
){
23548 //Do a check on the JsonFileUrl and crosscheck it.
23549 //If it doesn't match the cross-check, it needs to be updated
23550 //This allows for either url or _jsonFileUrl to he changed to
23551 //reset the store load location. Done this way for backwards
23552 //compatibility. People use _jsonFileUrl (even though officially
23554 if(this._jsonFileUrl
!== this._ccUrl
){
23555 kernel
.deprecated(this.declaredClass
+ ": ",
23556 "To change the url, set the url property of the store," +
23557 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23558 this._ccUrl
= this._jsonFileUrl
;
23559 this.url
= this._jsonFileUrl
;
23560 }else if(this.url
!== this._ccUrl
){
23561 this._jsonFileUrl
= this.url
;
23562 this._ccUrl
= this.url
;
23565 //See if there was any forced reset of data.
23566 if(this.data
!= null && this._jsonData
== null){
23567 this._jsonData
= this.data
;
23571 if(this._jsonFileUrl
){
23573 if(this._loadInProgress
){
23574 this._queuedFetches
.push({args
: keywordArgs
});
23576 this._loadInProgress
= true;
23578 url
: self
._jsonFileUrl
,
23579 handleAs
: "json-comment-optional",
23580 preventCache
: this.urlPreventCache
,
23581 failOk
: this.failOk
23583 var getHandler
= xhr
.get(getArgs
);
23584 getHandler
.addCallback(function(data
){
23585 var scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23587 self
._getItemsFromLoadedData(data
);
23588 self
._loadFinished
= true;
23589 self
._loadInProgress
= false;
23590 item
= self
._getItemByIdentity(keywordArgs
.identity
);
23591 if(keywordArgs
.onItem
){
23592 keywordArgs
.onItem
.call(scope
, item
);
23594 self
._handleQueuedFetches();
23596 self
._loadInProgress
= false;
23597 if(keywordArgs
.onError
){
23598 keywordArgs
.onError
.call(scope
, error
);
23602 getHandler
.addErrback(function(error
){
23603 self
._loadInProgress
= false;
23604 if(keywordArgs
.onError
){
23605 var scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23606 keywordArgs
.onError
.call(scope
, error
);
23611 }else if(this._jsonData
){
23612 // Passed in data, no need to xhr.
23613 self
._getItemsFromLoadedData(self
._jsonData
);
23614 self
._jsonData
= null;
23615 self
._loadFinished
= true;
23616 item
= self
._getItemByIdentity(keywordArgs
.identity
);
23617 if(keywordArgs
.onItem
){
23618 scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23619 keywordArgs
.onItem
.call(scope
, item
);
23623 // Already loaded. We can just look it up and call back.
23624 item
= this._getItemByIdentity(keywordArgs
.identity
);
23625 if(keywordArgs
.onItem
){
23626 scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23627 keywordArgs
.onItem
.call(scope
, item
);
23632 _getItemByIdentity: function(/* Object */ identity
){
23634 // Internal function to look an item up by its identity map.
23636 if(this._itemsByIdentity
){
23637 // If this map is defined, we need to just try to get it. If it fails
23638 // the item does not exist.
23639 if(Object
.hasOwnProperty
.call(this._itemsByIdentity
, identity
)){
23640 item
= this._itemsByIdentity
[identity
];
23642 }else if (Object
.hasOwnProperty
.call(this._arrayOfAllItems
, identity
)){
23643 item
= this._arrayOfAllItems
[identity
];
23645 if(item
=== undefined){
23648 return item
; // Object
23651 getIdentityAttributes: function(/* dojo/data/api/Item */ item){
23653 // See dojo/data/api/Identity.getIdentityAttributes()
23655 var identifier = this._features['dojo.data.api.Identity'];
23656 if(identifier === Number){
23657 // If (identifier === Number) it means getIdentity() just returns
23658 // an integer item-number for each item. The dojo/data/api/Identity
23659 // spec says we need to return null if the identity is not composed
23661 return null; // null
23663 return [identifier]; // Array
23667 _forceLoad: function(){
23669 // Internal function to force a load of the store if it hasn't occurred yet. This is required
23670 // for specific functions to work properly.
23672 //Do a check on the JsonFileUrl and crosscheck it.
23673 //If it doesn't match the cross-check, it needs to be updated
23674 //This allows for either url or _jsonFileUrl to he changed to
23675 //reset the store load location. Done this way for backwards
23676 //compatibility. People use _jsonFileUrl (even though officially
23678 if(this._jsonFileUrl !== this._ccUrl){
23679 kernel.deprecated(this.declaredClass + ": ",
23680 "To change the url, set the url property of the store," +
23681 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23682 this._ccUrl = this._jsonFileUrl;
23683 this.url = this._jsonFileUrl;
23684 }else if(this.url !== this._ccUrl){
23685 this._jsonFileUrl = this.url;
23686 this._ccUrl = this.url;
23689 //See if there was any forced reset of data.
23690 if(this.data != null){
23691 this._jsonData = this.data;
23695 if(this._jsonFileUrl){
23697 url: this._jsonFileUrl,
23698 handleAs: "json-comment-optional",
23699 preventCache: this.urlPreventCache,
23700 failOk: this.failOk,
23703 var getHandler = xhr.get(getArgs);
23704 getHandler.addCallback(function(data){
23706 //Check to be sure there wasn't another load going on concurrently
23707 //So we don't clobber data that comes in on it. If there is a load going on
23708 //then do not save this data. It will potentially clobber current data.
23709 //We mainly wanted to sync/wait here.
23710 //TODO: Revisit the loading scheme of this store to improve multi-initial
23711 //request handling.
23712 if(self._loadInProgress !== true && !self._loadFinished){
23713 self._getItemsFromLoadedData(data);
23714 self._loadFinished = true;
23715 }else if(self._loadInProgress){
23716 //Okay, we hit an error state we can't recover from. A forced load occurred
23717 //while an async load was occurring. Since we cannot block at this point, the best
23718 //that can be managed is to throw an error.
23719 throw new Error(this.declaredClass + ": Unable to perform a synchronous load, an async load is in progress.");
23726 getHandler.addErrback(function(error){
23729 }else if(this._jsonData){
23730 self._getItemsFromLoadedData(self._jsonData);
23731 self._jsonData = null;
23732 self._loadFinished = true;
23736 //Mix in the simple fetch implementation to this class.
23737 lang.extend(ItemFileReadStore,simpleFetch);
23739 return ItemFileReadStore;
23744 'dojo/html':function(){
23745 define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
23746 function(kernel, lang, darray, declare, dom, domConstruct, parser){
23754 lang.setObject("dojo.html", html);
23756 // the parser might be needed..
23758 // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
23761 html._secureForInnerHtml = function(/*String*/ cont
){
23763 // removes !DOCTYPE and title elements from the html string.
23765 // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
23766 // must go into head, so we need to cut out those tags
23768 // An html string for insertion into the dom
23770 return cont
.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
23773 html
._emptyNode
= domConstruct
.empty
;
23775 dojo.html._emptyNode = function(node){
23777 // Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
23780 // the parent element
23784 html
._setNodeContent = function(/*DomNode*/ node
, /*String|DomNode|NodeList*/ cont
){
23786 // inserts the given content into the given node
23788 // the parent element
23790 // the content to be set on the parent element.
23791 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23794 domConstruct
.empty(node
);
23797 if(typeof cont
== "string"){
23798 cont
= domConstruct
.toDom(cont
, node
.ownerDocument
);
23800 if(!cont
.nodeType
&& lang
.isArrayLike(cont
)){
23801 // handle as enumerable, but it may shrink as we enumerate it
23802 for(var startlen
=cont
.length
, i
=0; i
<cont
.length
; i
=startlen
==cont
.length
? i
+1 : 0){
23803 domConstruct
.place( cont
[i
], node
, "last");
23806 // pass nodes, documentFragments and unknowns through to dojo.place
23807 domConstruct
.place(cont
, node
, "last");
23815 // we wrap up the content-setting operation in a object
23816 html
._ContentSetter
= declare("dojo.html._ContentSetter", null,
23818 // node: DomNode|String
23819 // An node which will be the parent element that we set content into
23822 // content: String|DomNode|DomNode[]
23823 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
23827 // Usually only used internally, and auto-generated with each instance
23830 // cleanContent: Boolean
23831 // Should the content be treated as a full html document,
23832 // and the real content stripped of <html>, <body> wrapper before injection
23833 cleanContent
: false,
23835 // extractContent: Boolean
23836 // Should the content be treated as a full html document,
23837 // and the real content stripped of `<html> <body>` wrapper before injection
23838 extractContent
: false,
23840 // parseContent: Boolean
23841 // Should the node by passed to the parser after the new content is set
23842 parseContent
: false,
23844 // parserScope: String
23845 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
23846 // will search for data-dojo-type (or dojoType). For backwards compatibility
23847 // reasons defaults to dojo._scopeName (which is "dojo" except when
23848 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
23849 parserScope
: kernel
._scopeName
,
23851 // startup: Boolean
23852 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
23855 // lifecycle methods
23856 constructor: function(/*Object*/ params
, /*String|DomNode*/ node
){
23858 // Provides a configurable, extensible object to wrap the setting on content on a node
23859 // call the set() method to actually set the content..
23861 // the original params are mixed directly into the instance "this"
23862 lang
.mixin(this, params
|| {});
23864 // give precedence to params.node vs. the node argument
23865 // and ensure its a node, not an id string
23866 node
= this.node
= dom
.byId( this.node
|| node
);
23871 (node
) ? node
.id
|| node
.tagName
: "",
23876 set: function(/* String|DomNode|NodeList? */ cont
, /*Object?*/ params
){
23878 // front-end to the set-content sequence
23880 // An html string, node or enumerable list of nodes for insertion into the dom
23881 // If not provided, the object's content property will be used
23882 if(undefined !== cont
){
23883 this.content
= cont
;
23885 // in the re-use scenario, set needs to be able to mixin new configuration
23887 this._mixin(params
);
23893 var ret
= this.onEnd();
23895 if(ret
&& ret
.then
){
23896 // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
23899 // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
23900 // return a Deferred like above.
23905 setContent: function(){
23907 // sets the content on the node
23909 var node
= this.node
;
23912 throw new Error(this.declaredClass
+ ": setContent given no node");
23915 node
= html
._setNodeContent(node
, this.content
);
23917 // check if a domfault occurs when we are appending this.errorMessage
23918 // like for instance if domNode is a UL and we try append a DIV
23920 // FIXME: need to allow the user to provide a content error message string
23921 var errMess
= this.onContentError(e
);
23923 node
.innerHTML
= errMess
;
23925 console
.error('Fatal ' + this.declaredClass
+ '.setContent could not change content due to '+e
.message
, e
);
23928 // always put back the node for the next method
23929 this.node
= node
; // DomNode
23934 // cleanly empty out existing content
23936 // If there is a parse in progress, cancel it.
23937 if(this.parseDeferred
){
23938 if(!this.parseDeferred
.isResolved()){
23939 this.parseDeferred
.cancel();
23941 delete this.parseDeferred
;
23944 // destroy any widgets from a previous run
23945 // NOTE: if you don't want this you'll need to empty
23946 // the parseResults array property yourself to avoid bad things happening
23947 if(this.parseResults
&& this.parseResults
.length
){
23948 darray
.forEach(this.parseResults
, function(w
){
23953 delete this.parseResults
;
23955 // this is fast, but if you know its already empty or safe, you could
23956 // override empty to skip this step
23957 domConstruct
.empty(this.node
);
23960 onBegin: function(){
23962 // Called after instantiation, but before set();
23963 // It allows modification of any of the object properties -
23964 // including the node and content provided - before the set operation actually takes place
23965 // This default implementation checks for cleanContent and extractContent flags to
23966 // optionally pre-process html string content
23967 var cont
= this.content
;
23969 if(lang
.isString(cont
)){
23970 if(this.cleanContent
){
23971 cont
= html
._secureForInnerHtml(cont
);
23974 if(this.extractContent
){
23975 var match
= cont
.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
23976 if(match
){ cont
= match
[1]; }
23980 // clean out the node and any cruft associated with it - like widgets
23983 this.content
= cont
;
23984 return this.node
; // DomNode
23989 // Called after set(), when the new content has been pushed into the node
23990 // It provides an opportunity for post-processing before handing back the node to the caller
23991 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
23992 if(this.parseContent
){
23993 // populates this.parseResults and this.parseDeferred if you need those..
23996 return this.node
; // DomNode
23997 // TODO: for 2.0 return a Promise indicating that the parse completed.
24000 tearDown: function(){
24002 // manually reset the Setter instance if its being re-used for example for another set()
24004 // tearDown() is not called automatically.
24005 // In normal use, the Setter instance properties are simply allowed to fall out of scope
24006 // but the tearDown method can be called to explicitly reset this instance.
24007 delete this.parseResults
;
24008 delete this.parseDeferred
;
24010 delete this.content
;
24013 onContentError: function(err
){
24014 return "Error occurred setting content: " + err
;
24017 onExecError: function(err
){
24018 return "Error occurred executing scripts: " + err
;
24021 _mixin: function(params
){
24022 // mix properties/methods into the instance
24023 // TODO: the intention with tearDown is to put the Setter's state
24024 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
24025 // so we could do something here to move the original properties aside for later restoration
24026 var empty
= {}, key
;
24027 for(key
in params
){
24028 if(key
in empty
){ continue; }
24029 // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
24030 // .. but history shows we'll almost always guess wrong
24031 this[key
] = params
[key
];
24034 _parse: function(){
24036 // runs the dojo parser over the node contents, storing any results in this.parseResults
24037 // and the parse promise in this.parseDeferred
24038 // Any errors resulting from parsing are passed to _onError for handling
24040 var rootNode
= this.node
;
24042 // store the results (widgets, whatever) for potential retrieval
24043 var inherited
= {};
24044 darray
.forEach(["dir", "lang", "textDir"], function(name
){
24046 inherited
[name
] = this[name
];
24050 this.parseDeferred
= parser
.parse({
24051 rootNode
: rootNode
,
24052 noStart
: !this.startup
,
24053 inherited
: inherited
,
24054 scope
: this.parserScope
24055 }).then(function(results
){
24056 return self
.parseResults
= results
;
24059 this._onError('Content', e
, "Error parsing in _ContentSetter#"+this.id
);
24063 _onError: function(type
, err
, consoleText
){
24065 // shows user the string that is returned by on[type]Error
24066 // override/implement on[type]Error and return your own string to customize
24067 var errText
= this['on' + type
+ 'Error'].call(this, err
);
24069 console
.error(consoleText
, err
);
24070 }else if(errText
){ // a empty string won't change current content
24071 html
._setNodeContent(this.node
, errText
, true);
24074 }); // end declare()
24076 html
.set = function(/*DomNode*/ node
, /*String|DomNode|NodeList*/ cont
, /*Object?*/ params
){
24078 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
24079 // may be a better choice for simple HTML insertion.
24081 // Unless you need to use the params capabilities of this method, you should use
24082 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
24083 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
24084 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
24085 // or the other capabilities as defined by the params object for this method.
24087 // the parent element that will receive the content
24089 // the content to be set on the parent element.
24090 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
24092 // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
24094 // A safe string/node/nodelist content replacement/injection with hooks for extension
24096 // | html.set(node, "some string");
24097 // | html.set(node, contentNode, {options});
24098 // | html.set(node, myNode.childNodes, {options});
24099 if(undefined == cont
){
24100 console
.warn("dojo.html.set: no cont argument provided, using empty string");
24105 return html
._setNodeContent(node
, cont
, true);
24107 // more options but slower
24108 // note the arguments are reversed in order, to match the convention for instantiation via the parser
24109 var op
= new html
._ContentSetter(lang
.mixin(
24111 { content
: cont
, node
: node
}
24121 'dijit/_PaletteMixin':function(){
24122 define("dijit/_PaletteMixin", [
24123 "dojo/_base/declare", // declare
24124 "dojo/dom-attr", // domAttr.set
24125 "dojo/dom-class", // domClass.add domClass.remove
24126 "dojo/dom-construct", // domConstruct.create domConstruct.place
24127 "dojo/_base/event", // event.stop
24128 "dojo/keys", // keys
24129 "dojo/_base/lang", // lang.getObject
24130 "./_CssStateMixin",
24133 ], function(declare
, domAttr
, domClass
, domConstruct
, event
, keys
, lang
, _CssStateMixin
, focus
, typematic
){
24136 // dijit/_PaletteMixin
24138 return declare("dijit._PaletteMixin", [_CssStateMixin
], {
24140 // A keyboard accessible palette, for picking a color/emoticon/etc.
24142 // A mixin for a grid showing various entities, so the user can pick a certain entity.
24144 // defaultTimeout: Number
24145 // Number of milliseconds before a held key or button becomes typematic
24146 defaultTimeout
: 500,
24148 // timeoutChangeRate: Number
24149 // Fraction of time used to change the typematic timer between events
24150 // 1.0 means that each typematic event fires at defaultTimeout intervals
24151 // Less than 1.0 means that each typematic event fires at an increasing faster rate
24152 timeoutChangeRate
: 0.90,
24155 // Currently selected color/emoticon/etc.
24158 // _selectedCell: [private] Integer
24159 // Index of the currently selected cell. Initially, none selected
24163 // _currentFocus: [private] DomNode
24164 // The currently focused cell (if the palette itself has focus), or otherwise
24165 // the cell to be focused when the palette itself gets focus.
24166 // Different from value, which represents the selected (i.e. clicked) cell.
24167 _currentFocus: null,
24171 // _xDim: [protected] Integer
24172 // This is the number of cells horizontally across.
24177 // _yDim: [protected] Integer
24178 // This is the number of cells vertically down.
24182 // tabIndex: String
24183 // Widget tab index.
24186 // cellClass: [protected] String
24187 // CSS class applied to each cell in the palette
24188 cellClass
: "dijitPaletteCell",
24190 // dyeClass: [protected] Constructor
24191 // Constructor for Object created for each cell of the palette.
24192 // dyeClass should implements dijit.Dye interface
24196 // Localized summary for the palette table
24198 _setSummaryAttr
: "paletteTableNode",
24200 _dyeFactory: function(value
/*===== , row, col, title =====*/){
24202 // Return instance of dijit.Dye for specified cell of palette
24206 // Remove string support for 2.0
24207 var dyeClassObj
= typeof this.dyeClass
== "string" ? lang
.getObject(this.dyeClass
) : this.dyeClass
;
24208 return new dyeClassObj(value
);
24211 _preparePalette: function(choices
, titles
) {
24213 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
24215 // choices: String[][]
24216 // id's for each cell of the palette, used to create Dye JS object for each cell
24217 // titles: String[]
24218 // Localized tooltip for each cell
24221 var url
= this._blankGif
;
24223 this.connect(this.gridNode
, "ondijitclick", "_onCellClick");
24225 for(var row
=0; row
< choices
.length
; row
++){
24226 var rowNode
= domConstruct
.create("tr", {tabIndex
: "-1"}, this.gridNode
);
24227 for(var col
=0; col
< choices
[row
].length
; col
++){
24228 var value
= choices
[row
][col
];
24230 var cellObject
= this._dyeFactory(value
, row
, col
, titles
[value
]);
24232 var cellNode
= domConstruct
.create("td", {
24233 "class": this.cellClass
,
24235 title
: titles
[value
],
24239 // prepare cell inner structure
24240 cellObject
.fillCell(cellNode
, url
);
24242 cellNode
.idx
= this._cells
.length
;
24244 // save cell info into _cells
24245 this._cells
.push({node
:cellNode
, dye
:cellObject
});
24249 this._xDim
= choices
[0].length
;
24250 this._yDim
= choices
.length
;
24252 // Now set all events
24253 // The palette itself is navigated to with the tab key on the keyboard
24254 // Keyboard navigation within the Palette is with the arrow keys
24255 // Spacebar selects the cell.
24256 // For the up key the index is changed by negative the x dimension.
24258 var keyIncrementMap
= {
24259 UP_ARROW
: -this._xDim
,
24260 // The down key the index is increase by the x dimension.
24261 DOWN_ARROW
: this._xDim
,
24262 // Right and left move the index by 1.
24263 RIGHT_ARROW
: this.isLeftToRight() ? 1 : -1,
24264 LEFT_ARROW
: this.isLeftToRight() ? -1 : 1
24266 for(var key
in keyIncrementMap
){
24268 typematic
.addKeyListener(
24270 {charOrCode
:keys
[key
], ctrlKey
:false, altKey
:false, shiftKey
:false},
24273 var increment
= keyIncrementMap
[key
];
24274 return function(count
){ this._navigateByKey(increment
, count
); };
24276 this.timeoutChangeRate
,
24277 this.defaultTimeout
24283 postCreate: function(){
24284 this.inherited(arguments
);
24286 // Set initial navigable node.
24287 this._setCurrent(this._cells
[0].node
);
24292 // Focus this widget. Puts focus on the most recently focused cell.
24294 // The cell already has tabIndex set, just need to set CSS and focus it
24295 focus
.focus(this._currentFocus
);
24298 _onCellClick: function(/*Event*/ evt
){
24300 // Handler for click, enter key & space key. Selects the cell.
24306 var target
= evt
.target
;
24308 // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
24309 while(target
.tagName
!= "TD"){
24310 if(!target
.parentNode
|| target
== this.gridNode
){ // probably can never happen, but just in case
24313 target
= target
.parentNode
;
24316 var value
= this._getDye(target
).getValue();
24318 // First focus the clicked cell, and then send onChange() notification.
24319 // onChange() (via _setValueAttr) must be after the focus call, because
24320 // it may trigger a refocus to somewhere else (like the Editor content area), and that
24321 // second focus should win.
24322 this._setCurrent(target
);
24323 focus
.focus(target
);
24324 this._setValueAttr(value
, true);
24329 _setCurrent: function(/*DomNode*/ node
){
24331 // Sets which node is the focused cell.
24333 // At any point in time there's exactly one
24334 // cell with tabIndex != -1. If focus is inside the palette then
24335 // focus is on that cell.
24337 // After calling this method, arrow key handlers and mouse click handlers
24338 // should focus the cell in a setTimeout().
24341 if("_currentFocus" in this){
24342 // Remove tabIndex on old cell
24343 domAttr
.set(this._currentFocus
, "tabIndex", "-1");
24346 // Set tabIndex of new cell
24347 this._currentFocus
= node
;
24349 domAttr
.set(node
, "tabIndex", this.tabIndex
);
24353 _setValueAttr: function(value
, priorityChange
){
24355 // This selects a cell. It triggers the onChange event.
24357 // Value of the cell to select
24360 // priorityChange: Boolean?
24361 // Optional parameter used to tell the select whether or not to fire
24364 // clear old selected cell
24365 if(this._selectedCell
>= 0){
24366 domClass
.remove(this._cells
[this._selectedCell
].node
, this.cellClass
+ "Selected");
24368 this._selectedCell
= -1;
24370 // search for cell matching specified value
24372 for(var i
= 0; i
< this._cells
.length
; i
++){
24373 if(value
== this._cells
[i
].dye
.getValue()){
24374 this._selectedCell
= i
;
24375 domClass
.add(this._cells
[i
].node
, this.cellClass
+ "Selected");
24381 // record new value, or null if no matching cell
24382 this._set("value", this._selectedCell
>= 0 ? value
: null);
24384 if(priorityChange
|| priorityChange
=== undefined){
24385 this.onChange(value
);
24389 onChange: function(/*===== value =====*/){
24391 // Callback when a cell is selected.
24393 // Value corresponding to cell.
24396 _navigateByKey: function(increment
, typeCount
){
24398 // This is the callback for typematic.
24399 // It changes the focus and the highlighed cell.
24401 // How much the key is navigated.
24403 // How many times typematic has fired.
24407 // typecount == -1 means the key is released.
24408 if(typeCount
== -1){ return; }
24410 var newFocusIndex
= this._currentFocus
.idx
+ increment
;
24411 if(newFocusIndex
< this._cells
.length
&& newFocusIndex
> -1){
24412 var focusNode
= this._cells
[newFocusIndex
].node
;
24413 this._setCurrent(focusNode
);
24415 // Actually focus the node, for the benefit of screen readers.
24416 // Use defer because IE doesn't like changing focus inside of an event handler
24417 this.defer(lang
.hitch(focus
, "focus", focusNode
));
24421 _getDye: function(/*DomNode*/ cell
){
24423 // Get JS object for given cell DOMNode
24425 return this._cells
[cell
.idx
].dye
;
24430 declare("dijit.Dye",
24434 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
24436 constructor: function(alias, row, col){
24438 // Initialize according to value or alias like "white"
24442 getValue: function(){
24444 // Return "value" of cell; meaning of "value" varies by subclass.
24446 // For example color hex value, emoticon ascii value etc, entity hex value.
24449 fillCell: function(cell, blankGif){
24451 // Add cell DOMNode inner structure
24453 // The surrounding cell
24454 // blankGif: String
24455 // URL for blank cell image
24464 'dijit/form/ValidationTextBox':function(){
24466 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
24467 define("dijit/form/ValidationTextBox", [
24468 "dojo/_base/declare", // declare
24469 "dojo/_base/kernel", // kernel.deprecated
24470 "dojo/i18n", // i18n.getLocalization
24473 "dojo/text!./templates/ValidationTextBox.html",
24474 "dojo/i18n!./nls/validate"
24475 ], function(declare
, kernel
, i18n
, TextBox
, Tooltip
, template
){
24478 // dijit/form/ValidationTextBox
24482 var __Constraints = {
24484 // locale used for validation, picks up value from this widget's lang attribute
24485 // _flags_: anything
24486 // various flags passed to pattern function
24490 var ValidationTextBox
;
24491 return ValidationTextBox
= declare("dijit.form.ValidationTextBox", TextBox
, {
24493 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
24495 templateString
: template
,
24497 // required: Boolean
24498 // User is required to enter data into this field.
24501 // promptMessage: String
24502 // If defined, display this hint string immediately on focus to the textbox, if empty.
24503 // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
24504 // Think of this like a tooltip that tells the user what to do, not an error message
24505 // that tells the user what they've done wrong.
24507 // Message disappears when user starts typing.
24510 // invalidMessage: String
24511 // The message to display if value is invalid.
24512 // The translated string value is read from the message file by default.
24513 // Set to "" to use the promptMessage instead.
24514 invalidMessage
: "$_unset_$",
24516 // missingMessage: String
24517 // The message to display if value is empty and the field is required.
24518 // The translated string value is read from the message file by default.
24519 // Set to "" to use the invalidMessage instead.
24520 missingMessage
: "$_unset_$",
24523 // Currently error/prompt message.
24524 // When using the default tooltip implementation, this will only be
24525 // displayed when the field is focused.
24528 // constraints: __Constraints
24529 // user-defined object needed to pass parameters to the validator functions
24532 // pattern: [extension protected] String|Function(constraints) returning a string.
24533 // This defines the regular expression used to validate the input.
24534 // Do not add leading ^ or $ characters since the widget adds these.
24535 // A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
24536 // set('pattern', String|Function).
24539 // regExp: Deprecated [extension protected] String. Use "pattern" instead.
24542 regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
24544 // Deprecated. Use set('pattern', Function) instead.
24547 // state: [readonly] String
24548 // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
24551 // tooltipPosition: String[]
24552 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
24553 tooltipPosition
: [],
24555 _deprecateRegExp: function(attr
, value
){
24556 if(value
!= ValidationTextBox
.prototype[attr
]){
24557 kernel
.deprecated("ValidationTextBox id="+this.id
+", set('" + attr
+ "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0");
24558 this.set('pattern', value
);
24561 _setRegExpGenAttr: function(/*Function*/ newFcn
){
24562 this._deprecateRegExp("regExpGen", newFcn
);
24563 this.regExpGen
= this._getPatternAttr
; // backward compat with this.regExpGen(this.constraints)
24565 _setRegExpAttr: function(/*String*/ value
){
24566 this._deprecateRegExp("regExp", value
);
24569 _setValueAttr: function(){
24571 // Hook so set('value', ...) works.
24572 this.inherited(arguments
);
24573 this.validate(this.focused
);
24576 validator: function(/*anything*/ value
, /*__Constraints*/ constraints
){
24578 // Overridable function used to validate the text input against the regular expression.
24581 return (new RegExp("^(?:" + this._getPatternAttr(constraints
) + ")"+(this.required
?"":"?")+"$")).test(value
) &&
24582 (!this.required
|| !this._isEmpty(value
)) &&
24583 (this._isEmpty(value
) || this.parse(value
, constraints
) !== undefined); // Boolean
24586 _isValidSubset: function(){
24588 // Returns true if the value is either already valid or could be made valid by appending characters.
24589 // This is used for validation while the user [may be] still typing.
24590 return this.textbox
.value
.search(this._partialre
) == 0;
24593 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
24595 // Tests if value is valid.
24596 // Can override with your own routine in a subclass.
24599 return this.validator(this.textbox
.value
, this.constraints
);
24602 _isEmpty: function(value
){
24604 // Checks for whitespace
24605 return (this.trim
? /^\s*$/ : /^$/).test(value
); // Boolean
24608 getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24610 // Return an error message to show if appropriate
24613 var invalid
= this.invalidMessage
== "$_unset_$" ? this.messages
.invalidMessage
:
24614 !this.invalidMessage
? this.promptMessage
: this.invalidMessage
;
24615 var missing
= this.missingMessage
== "$_unset_$" ? this.messages
.missingMessage
:
24616 !this.missingMessage
? invalid
: this.missingMessage
;
24617 return (this.required
&& this._isEmpty(this.textbox
.value
)) ? missing
: invalid
; // String
24620 getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24622 // Return a hint message to show when widget is first focused
24625 return this.promptMessage
; // String
24628 _maskValidSubsetError
: true,
24629 validate: function(/*Boolean*/ isFocused
){
24631 // Called by oninit, onblur, and onkeypress.
24633 // Show missing or invalid messages if appropriate, and highlight textbox field.
24637 var isValid
= this.disabled
|| this.isValid(isFocused
);
24638 if(isValid
){ this._maskValidSubsetError
= true; }
24639 var isEmpty
= this._isEmpty(this.textbox
.value
);
24640 var isValidSubset
= !isValid
&& isFocused
&& this._isValidSubset();
24641 this._set("state", isValid
? "" : (((((!this._hasBeenBlurred
|| isFocused
) && isEmpty
) || isValidSubset
) && (this._maskValidSubsetError
|| (isValidSubset
&& !this._hasBeenBlurred
&& isFocused
))) ? "Incomplete" : "Error"));
24642 this.focusNode
.setAttribute("aria-invalid", isValid
? "false" : "true");
24644 if(this.state
== "Error"){
24645 this._maskValidSubsetError
= isFocused
&& isValidSubset
; // we want the error to show up after a blur and refocus
24646 message
= this.getErrorMessage(isFocused
);
24647 }else if(this.state
== "Incomplete"){
24648 message
= this.getPromptMessage(isFocused
); // show the prompt whenever the value is not yet complete
24649 this._maskValidSubsetError
= !this._hasBeenBlurred
|| isFocused
; // no Incomplete warnings while focused
24651 message
= this.getPromptMessage(isFocused
); // show the prompt whenever there's no error and no text
24653 this.set("message", message
);
24658 displayMessage: function(/*String*/ message
){
24660 // Overridable method to display validation errors/hints.
24661 // By default uses a tooltip.
24664 if(message
&& this.focused
){
24665 Tooltip
.show(message
, this.domNode
, this.tooltipPosition
, !this.isLeftToRight());
24667 Tooltip
.hide(this.domNode
);
24671 _refreshState: function(){
24672 // Overrides TextBox._refreshState()
24674 this.validate(this.focused
);
24676 this.inherited(arguments
);
24679 //////////// INITIALIZATION METHODS ///////////////////////////////////////
24681 constructor: function(params
/*===== , srcNodeRef =====*/){
24683 // Create the widget.
24684 // params: Object|null
24685 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
24686 // and functions, typically callbacks like onClick.
24687 // The hash can contain any of the widget's properties, excluding read-only properties.
24688 // srcNodeRef: DOMNode|String?
24689 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
24691 this.constraints
= {};
24692 this.baseClass
+= ' dijitValidationTextBox';
24695 startup: function(){
24696 this.inherited(arguments
);
24697 this._refreshState(); // after all _set* methods have run
24700 _setConstraintsAttr: function(/*__Constraints*/ constraints
){
24701 if(!constraints
.locale
&& this.lang
){
24702 constraints
.locale
= this.lang
;
24704 this._set("constraints", constraints
);
24705 this._refreshState();
24708 _setPatternAttr: function(/*String|Function*/ pattern
){
24709 this._set("pattern", pattern
); // don't set on INPUT to avoid native HTML5 validation
24712 _getPatternAttr: function(/*__Constraints*/ constraints
){
24714 // Hook to get the current regExp and to compute the partial validation RE.
24715 var p
= this.pattern
;
24716 var type
= (typeof p
).toLowerCase();
24717 if(type
== "function"){
24718 p
= this.pattern(constraints
|| this.constraints
);
24720 if(p
!= this._lastRegExp
){
24721 var partialre
= "";
24722 this._lastRegExp
= p
;
24723 // parse the regexp and produce a new regexp that matches valid subsets
24724 // if the regexp is .* then there's no use in matching subsets since everything is valid
24726 p
.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
24728 switch(re
.charAt(0)){
24740 partialre
+= "|$)";
24743 partialre
+= "(?:"+re
+"|$)";
24748 try{ // this is needed for now since the above regexp parsing needs more test verification
24749 "".search(partialre
);
24750 }catch(e
){ // should never be here unless the original RE is bad or the parsing is bad
24751 partialre
= this.pattern
;
24752 console
.warn('RegExp error in ' + this.declaredClass
+ ': ' + this.pattern
);
24753 } // should never be here unless the original RE is bad or the parsing is bad
24754 this._partialre
= "^(?:" + partialre
+ ")$";
24759 postMixInProperties: function(){
24760 this.inherited(arguments
);
24761 this.messages
= i18n
.getLocalization("dijit.form", "validate", this.lang
);
24762 this._setConstraintsAttr(this.constraints
); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
24765 _setDisabledAttr: function(/*Boolean*/ value
){
24766 this.inherited(arguments
); // call FormValueWidget._setDisabledAttr()
24767 this._refreshState();
24770 _setRequiredAttr: function(/*Boolean*/ value
){
24771 this._set("required", value
);
24772 this.focusNode
.setAttribute("aria-required", value
);
24773 this._refreshState();
24776 _setMessageAttr: function(/*String*/ message
){
24777 this._set("message", message
);
24778 this.displayMessage(message
);
24782 // Overrides dijit/form/TextBox.reset() by also
24783 // hiding errors about partial matches
24784 this._maskValidSubsetError
= true;
24785 this.inherited(arguments
);
24788 _onBlur: function(){
24789 // the message still exists but for back-compat, and to erase the tooltip
24790 // (if the message is being displayed as a tooltip), call displayMessage('')
24791 this.displayMessage('');
24793 this.inherited(arguments
);
24799 'dijit/_base/typematic':function(){
24800 define("dijit/_base/typematic", ["../typematic"], function(){
24805 // Deprecated, for back-compat, just loads top level module
24812 'dijit/layout/BorderContainer':function(){
24813 define("dijit/layout/BorderContainer", [
24814 "dojo/_base/array", // array.filter array.forEach array.map
24815 "dojo/cookie", // cookie
24816 "dojo/_base/declare", // declare
24817 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
24818 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
24819 "dojo/dom-geometry", // domGeometry.marginBox
24820 "dojo/dom-style", // domStyle.style
24821 "dojo/_base/event", // event.stop
24823 "dojo/_base/lang", // lang.getObject lang.hitch
24828 "../_TemplatedMixin",
24830 "./utils" // layoutUtils.layoutChildren
24831 ], function(array
, cookie
, declare
, domClass
, domConstruct
, domGeometry
, domStyle
, event
, keys
, lang
, on
, touch
,
24832 _WidgetBase
, _Widget
, _TemplatedMixin
, _LayoutWidget
, layoutUtils
){
24835 // dijit/layout/BorderContainer
24837 var _Splitter
= declare("dijit.layout._Splitter", [_Widget
, _TemplatedMixin
],
24840 // A draggable spacer between two items in a `dijit/layout/BorderContainer`.
24842 // This is instantiated by `dijit/layout/BorderContainer`. Users should not
24843 // create it directly.
24848 // container: [const] dijit/layout/BorderContainer
24849 // Pointer to the parent BorderContainer
24852 // child: [const] dijit/layout/_LayoutWidget
24853 // Pointer to the pane associated with this splitter
24856 // region: [const] String
24857 // Region of pane associated with this splitter.
24858 // "top", "bottom", "left", "right".
24862 // live: [const] Boolean
24863 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
24864 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
24867 templateString
: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
24869 constructor: function(){
24870 this._handlers
= [];
24873 postMixInProperties: function(){
24874 this.inherited(arguments
);
24876 this.horizontal
= /top|bottom/.test(this.region
);
24877 this._factor
= /top|left/.test(this.region
) ? 1 : -1;
24878 this._cookieName
= this.container
.id
+ "_" + this.region
;
24881 buildRendering: function(){
24882 this.inherited(arguments
);
24884 domClass
.add(this.domNode
, "dijitSplitter" + (this.horizontal
? "H" : "V"));
24886 if(this.container
.persist
){
24887 // restore old size
24888 var persistSize
= cookie(this._cookieName
);
24890 this.child
.domNode
.style
[this.horizontal
? "height" : "width"] = persistSize
;
24895 _computeMaxSize: function(){
24897 // Return the maximum size that my corresponding pane can be set to
24899 var dim
= this.horizontal
? 'h' : 'w',
24900 childSize
= domGeometry
.getMarginBox(this.child
.domNode
)[dim
],
24901 center
= array
.filter(this.container
.getChildren(), function(child
){ return child
.region
== "center";})[0],
24902 spaceAvailable
= domGeometry
.getMarginBox(center
.domNode
)[dim
]; // can expand until center is crushed to 0
24904 return Math
.min(this.child
.maxSize
, childSize
+ spaceAvailable
);
24907 _startDrag: function(e
){
24909 this.cover
= domConstruct
.place("<div class=dijitSplitterCover></div>", this.child
.domNode
, "after");
24911 domClass
.add(this.cover
, "dijitSplitterCoverActive");
24913 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
24914 if(this.fake
){ domConstruct
.destroy(this.fake
); }
24915 if(!(this._resize
= this.live
)){ //TODO: disable live for IE6?
24916 // create fake splitter to display at old position while we drag
24917 (this.fake
= this.domNode
.cloneNode(true)).removeAttribute("id");
24918 domClass
.add(this.domNode
, "dijitSplitterShadow");
24919 domConstruct
.place(this.fake
, this.domNode
, "after");
24921 domClass
.add(this.domNode
, "dijitSplitterActive dijitSplitter" + (this.horizontal
? "H" : "V") + "Active");
24923 domClass
.remove(this.fake
, "dijitSplitterHover dijitSplitter" + (this.horizontal
? "H" : "V") + "Hover");
24926 //Performance: load data info local vars for onmousevent function closure
24927 var factor
= this._factor
,
24928 isHorizontal
= this.horizontal
,
24929 axis
= isHorizontal
? "pageY" : "pageX",
24930 pageStart
= e
[axis
],
24931 splitterStyle
= this.domNode
.style
,
24932 dim
= isHorizontal
? 'h' : 'w',
24933 childStart
= domGeometry
.getMarginBox(this.child
.domNode
)[dim
],
24934 max
= this._computeMaxSize(),
24935 min
= this.child
.minSize
|| 20,
24936 region
= this.region
,
24937 splitterAttr
= region
== "top" || region
== "bottom" ? "top" : "left", // style attribute of splitter to adjust
24938 splitterStart
= parseInt(splitterStyle
[splitterAttr
], 10),
24939 resize
= this._resize
,
24940 layoutFunc
= lang
.hitch(this.container
, "_layoutChildren", this.child
.id
),
24941 de
= this.ownerDocument
;
24943 this._handlers
= this._handlers
.concat([
24944 on(de
, touch
.move, this._drag = function(e
, forceResize
){
24945 var delta
= e
[axis
] - pageStart
,
24946 childSize
= factor
* delta
+ childStart
,
24947 boundChildSize
= Math
.max(Math
.min(childSize
, max
), min
);
24949 if(resize
|| forceResize
){
24950 layoutFunc(boundChildSize
);
24952 // TODO: setting style directly (usually) sets content box size, need to set margin box size
24953 splitterStyle
[splitterAttr
] = delta
+ splitterStart
+ factor
*(boundChildSize
- childSize
) + "px";
24955 on(de
, "dragstart", event
.stop
),
24956 on(this.ownerDocumentBody
, "selectstart", event
.stop
),
24957 on(de
, touch
.release
, lang
.hitch(this, "_stopDrag"))
24962 _onMouse: function(e
){
24964 // Handler for onmouseenter / onmouseleave events
24965 var o
= (e
.type
== "mouseover" || e
.type
== "mouseenter");
24966 domClass
.toggle(this.domNode
, "dijitSplitterHover", o
);
24967 domClass
.toggle(this.domNode
, "dijitSplitter" + (this.horizontal
? "H" : "V") + "Hover", o
);
24970 _stopDrag: function(e
){
24973 domClass
.remove(this.cover
, "dijitSplitterCoverActive");
24975 if(this.fake
){ domConstruct
.destroy(this.fake
); }
24976 domClass
.remove(this.domNode
, "dijitSplitterActive dijitSplitter"
24977 + (this.horizontal
? "H" : "V") + "Active dijitSplitterShadow");
24978 this._drag(e
); //TODO: redundant with onmousemove?
24979 this._drag(e
, true);
24981 this._cleanupHandlers();
24985 if(this.container
.persist
){
24986 cookie(this._cookieName
, this.child
.domNode
.style
[this.horizontal
? "height" : "width"], {expires
:365});
24990 _cleanupHandlers: function(){
24992 while(h
= this._handlers
.pop()){ h
.remove(); }
24995 _onKeyPress: function(/*Event*/ e
){
24996 // should we apply typematic to this?
24997 this._resize
= true;
24998 var horizontal
= this.horizontal
;
25000 switch(e
.charOrCode
){
25001 case horizontal
? keys
.UP_ARROW
: keys
.LEFT_ARROW
:
25004 case horizontal
? keys
.DOWN_ARROW
: keys
.RIGHT_ARROW
:
25007 // this.inherited(arguments);
25010 var childSize
= domGeometry
.getMarginSize(this.child
.domNode
)[ horizontal
? 'h' : 'w' ] + this._factor
* tick
;
25011 this.container
._layoutChildren(this.child
.id
, Math
.max(Math
.min(childSize
, this._computeMaxSize()), this.child
.minSize
));
25015 destroy: function(){
25016 this._cleanupHandlers();
25018 delete this.container
;
25021 this.inherited(arguments
);
25025 var _Gutter
= declare("dijit.layout._Gutter", [_Widget
, _TemplatedMixin
],
25028 // Just a spacer div to separate side pane from center pane.
25029 // Basically a trick to lookup the gutter/splitter width from the theme.
25031 // Instantiated by `dijit/layout/BorderContainer`. Users should not
25032 // create directly.
25036 templateString
: '<div class="dijitGutter" role="presentation"></div>',
25038 postMixInProperties: function(){
25039 this.inherited(arguments
);
25040 this.horizontal
= /top|bottom/.test(this.region
);
25043 buildRendering: function(){
25044 this.inherited(arguments
);
25045 domClass
.add(this.domNode
, "dijitGutter" + (this.horizontal
? "H" : "V"));
25049 var BorderContainer
= declare("dijit.layout.BorderContainer", _LayoutWidget
, {
25051 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
25053 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
25054 // that contains a child widget marked region="center" and optionally children widgets marked
25055 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
25056 // Children along the edges will be laid out according to width or height dimensions and may
25057 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
25058 // space is designated for the center region.
25060 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
25061 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
25062 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
25063 // "left" and "right" except that they will be reversed in right-to-left environments.
25065 // For complex layouts, multiple children can be specified for a single region. In this case, the
25066 // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
25067 // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
25068 // instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
25070 // See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on
25071 // children of a `BorderContainer`.
25073 // | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
25074 // | style="width: 400px; height: 300px;">
25075 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div>
25076 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
25077 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div>
25081 // Which design is used for the layout:
25083 // - "headline" (default) where the top and bottom extend the full width of the container
25084 // - "sidebar" where the left and right sides extend from top to bottom.
25085 design
: "headline",
25087 // gutters: [const] Boolean
25088 // Give each pane a border and margin.
25089 // Margin determined by domNode.paddingLeft.
25090 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
25093 // liveSplitters: [const] Boolean
25094 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
25095 liveSplitters
: true,
25097 // persist: Boolean
25098 // Save splitter positions in a cookie.
25101 baseClass
: "dijitBorderContainer",
25103 // _splitterClass: Function||String
25104 // Optional hook to override the default Splitter widget used by BorderContainer
25105 _splitterClass
: _Splitter
,
25107 postMixInProperties: function(){
25108 // change class name to indicate that BorderContainer is being used purely for
25109 // layout (like LayoutContainer) rather than for pretty formatting.
25111 this.baseClass
+= "NoGutter";
25113 this.inherited(arguments
);
25116 startup: function(){
25117 if(this._started
){ return; }
25118 array
.forEach(this.getChildren(), this._setupChild
, this);
25119 this.inherited(arguments
);
25122 _setupChild: function(/*dijit/_WidgetBase*/ child){
25123 // Override _LayoutWidget._setupChild().
25125 var region = child.region;
25127 this.inherited(arguments);
25129 domClass.add(child.domNode, this.baseClass+"Pane");
25131 var ltr = this.isLeftToRight();
25132 if(region == "leading"){ region = ltr ? "left" : "right"; }
25133 if(region == "trailing"){ region = ltr ? "right" : "left"; }
25135 // Create draggable splitter for resizing pane,
25136 // or alternately if splitter=false but BorderContainer.gutters=true then
25137 // insert dummy div just for spacing
25138 if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
25139 var _Splitter = child.splitter ? this._splitterClass : _Gutter;
25140 if(lang.isString(_Splitter)){
25141 _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
25143 var splitter = new _Splitter({
25144 id: child.id + "_splitter",
25148 live: this.liveSplitters
25150 splitter.isSplitter = true;
25151 child._splitterWidget = splitter;
25153 domConstruct.place(splitter.domNode, child.domNode, "after");
25155 // Splitters aren't added as Contained children, so we need to call startup explicitly
25156 splitter.startup();
25158 child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
25162 layout: function(){
25163 // Implement _LayoutWidget.layout() virtual method.
25164 this._layoutChildren();
25167 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
25168 // Override _LayoutWidget.addChild().
25169 this.inherited(arguments
);
25171 this.layout(); //OPT
25175 removeChild: function(/*dijit/_WidgetBase*/ child){
25176 // Override _LayoutWidget.removeChild().
25178 var region = child.region;
25179 var splitter = child._splitterWidget;
25181 splitter.destroy();
25182 delete child._splitterWidget;
25184 this.inherited(arguments);
25187 this._layoutChildren();
25189 // Clean up whatever style changes we made to the child pane.
25190 // Unclear how height and width should be handled.
25191 domClass.remove(child.domNode, this.baseClass+"Pane");
25192 domStyle.set(child.domNode, {
25199 domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
25202 getChildren: function(){
25203 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
25204 return array.filter(this.inherited(arguments), function(widget){
25205 return !widget.isSplitter;
25209 // TODO: remove in 2.0
25210 getSplitter: function(/*String*/region
){
25212 // Returns the widget responsible for rendering the splitter associated with region
25215 return array
.filter(this.getChildren(), function(child
){
25216 return child
.region
== region
;
25217 })[0]._splitterWidget
;
25220 resize: function(newSize
, currentSize
){
25221 // Overrides _LayoutWidget.resize().
25223 // resetting potential padding to 0px to provide support for 100% width/height + padding
25224 // TODO: this hack doesn't respect the box model and is a temporary fix
25225 if(!this.cs
|| !this.pe
){
25226 var node
= this.domNode
;
25227 this.cs
= domStyle
.getComputedStyle(node
);
25228 this.pe
= domGeometry
.getPadExtents(node
, this.cs
);
25229 this.pe
.r
= domStyle
.toPixelValue(node
, this.cs
.paddingRight
);
25230 this.pe
.b
= domStyle
.toPixelValue(node
, this.cs
.paddingBottom
);
25232 domStyle
.set(node
, "padding", "0px");
25235 this.inherited(arguments
);
25238 _layoutChildren: function(/*String?*/ changedChildId
, /*Number?*/ changedChildSize
){
25240 // This is the main routine for setting size/position of each child.
25242 // With no arguments, measures the height of top/bottom panes, the width
25243 // of left/right panes, and then sizes all panes accordingly.
25245 // With changedRegion specified (as "left", "top", "bottom", or "right"),
25246 // it changes that region's width/height to changedRegionSize and
25247 // then resizes other regions that were affected.
25249 // Id of the child which should be resized because splitter was dragged.
25250 // changedChildSize:
25251 // The new width/height (in pixels) to make specified child
25253 if(!this._borderBox
|| !this._borderBox
.h
){
25254 // We are currently hidden, or we haven't been sized by our parent yet.
25255 // Abort. Someone will resize us later.
25259 // Generate list of wrappers of my children in the order that I want layoutChildren()
25260 // to process them (i.e. from the outside to the inside)
25261 var wrappers
= array
.map(this.getChildren(), function(child
, idx
){
25265 child
.region
== "center" ? Infinity
: 0,
25266 child
.layoutPriority
,
25267 (this.design
== "sidebar" ? 1 : -1) * (/top|bottom/.test(child
.region
) ? 1 : -1),
25272 wrappers
.sort(function(a
, b
){
25273 var aw
= a
.weight
, bw
= b
.weight
;
25274 for(var i
=0; i
<aw
.length
; i
++){
25275 if(aw
[i
] != bw
[i
]){
25276 return aw
[i
] - bw
[i
];
25282 // Make new list, combining the externally specified children with splitters and gutters
25283 var childrenAndSplitters
= [];
25284 array
.forEach(wrappers
, function(wrapper
){
25285 var pane
= wrapper
.pane
;
25286 childrenAndSplitters
.push(pane
);
25287 if(pane
._splitterWidget
){
25288 childrenAndSplitters
.push(pane
._splitterWidget
);
25292 // Compute the box in which to lay out my children
25296 w
: this._borderBox
.w
- this.pe
.w
,
25297 h
: this._borderBox
.h
- this.pe
.h
25300 // Layout the children, possibly changing size due to a splitter drag
25301 layoutUtils
.layoutChildren(this.domNode
, dim
, childrenAndSplitters
,
25302 changedChildId
, changedChildSize
);
25305 destroyRecursive: function(){
25306 // Destroy splitters first, while getChildren() still works
25307 array
.forEach(this.getChildren(), function(child
){
25308 var splitter
= child
._splitterWidget
;
25310 splitter
.destroy();
25312 delete child
._splitterWidget
;
25315 // Then destroy the real children, and myself
25316 this.inherited(arguments
);
25320 BorderContainer
.ChildWidgetProperties
= {
25322 // These properties can be specified for the children of a BorderContainer.
25324 // region: [const] String
25325 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
25326 // See the `dijit/layout/BorderContainer` description for details.
25329 // layoutPriority: [const] Number
25330 // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
25331 // between children with a lower layoutPriority.
25334 // splitter: [const] Boolean
25335 // Parameter for children where region != "center".
25336 // If true, enables user to resize the widget by putting a draggable splitter between
25337 // this widget and the region=center widget.
25340 // minSize: [const] Number
25341 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
25344 // maxSize: [const] Number
25345 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
25349 // Since any widget can be specified as a LayoutContainer child, mix it
25350 // into the base widget class. (This is a hack, but it's effective.)
25351 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
25352 lang
.extend(_WidgetBase
, /*===== {} || =====*/ BorderContainer
.ChildWidgetProperties
);
25354 // For monkey patching
25355 BorderContainer
._Splitter
= _Splitter
;
25356 BorderContainer
._Gutter
= _Gutter
;
25358 return BorderContainer
;
25362 'dijit/_base':function(){
25363 define("dijit/_base", [
25365 "./a11y", // used to be in dijit/_base/manager
25366 "./WidgetSet", // used to be in dijit/_base/manager
25373 "./_base/typematic",
25376 ], function(dijit
){
25384 // Includes all the modules in dijit/_base
25388 return dijit
._base
;
25392 'dojo/window':function(){
25393 define("dojo/window", ["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
25394 function(lang
, has
, baseWindow
, dom
, geom
, style
){
25403 getBox: function(/*Document?*/ doc
){
25405 // Returns the dimensions and scroll position of the viewable area of a browser window
25407 doc
= doc
|| baseWindow
.doc
;
25410 scrollRoot
= (doc
.compatMode
== 'BackCompat') ? baseWindow
.body(doc
) : doc
.documentElement
,
25411 // get scroll position
25412 scroll
= geom
.docScroll(doc
), // scrollRoot.scrollTop/Left should work
25415 if(has("touch")){ // if(scrollbars not supported)
25416 var uiWindow
= window
.get(doc
); // use UI window, not dojo.global window
25417 // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
25418 w
= uiWindow
.innerWidth
|| scrollRoot
.clientWidth
; // || scrollRoot.clientXXX probably never evaluated
25419 h
= uiWindow
.innerHeight
|| scrollRoot
.clientHeight
;
25421 // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
25422 // uiWindow.innerWidth/Height includes the scrollbar and cannot be used
25423 w
= scrollRoot
.clientWidth
;
25424 h
= scrollRoot
.clientHeight
;
25434 get: function(/*Document*/ doc
){
25436 // Get window object associated with document doc.
25438 // The document to get the associated window for.
25440 // In some IE versions (at least 6.0), document.parentWindow does not return a
25441 // reference to the real window object (maybe a copy), so we must fix it as well
25442 // We use IE specific execScript to attach the real window reference to
25443 // document._parentWindow for later use
25444 if(has("ie") && window
!== document
.parentWindow
){
25446 In IE 6, only the variable "window" can be used to connect events (others
25447 may be only copies).
25449 doc
.parentWindow
.execScript("document._parentWindow = window;", "Javascript");
25450 //to prevent memory leak, unset it after use
25451 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
25452 var win
= doc
._parentWindow
;
25453 doc
._parentWindow
= null;
25454 return win
; // Window
25457 return doc
.parentWindow
|| doc
.defaultView
; // Window
25460 scrollIntoView: function(/*DomNode*/ node
, /*Object?*/ pos
){
25462 // Scroll the passed node into view, if it is not already.
25464 // don't rely on node.scrollIntoView working just because the function is there
25466 try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
25467 node
= dom
.byId(node
);
25468 var doc
= node
.ownerDocument
|| baseWindow
.doc
, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined?
25469 body
= baseWindow
.body(doc
),
25470 html
= doc
.documentElement
|| body
.parentNode
,
25471 isIE
= has("ie"), isWK
= has("webkit");
25472 // if an untested browser, then use the native method
25473 if((!(has("mozilla") || isIE
|| isWK
|| has("opera")) || node
== body
|| node
== html
) && (typeof node
.scrollIntoView
!= "undefined")){
25474 node
.scrollIntoView(false); // short-circuit to native if possible
25477 var backCompat
= doc
.compatMode
== 'BackCompat',
25478 clientAreaRoot
= (isIE
>= 9 && "frameElement" in node
.ownerDocument
.parentWindow
)
25479 ? ((html
.clientHeight
> 0 && html
.clientWidth
> 0 && (body
.clientHeight
== 0 || body
.clientWidth
== 0 || body
.clientHeight
> html
.clientHeight
|| body
.clientWidth
> html
.clientWidth
)) ? html
: body
)
25480 : (backCompat
? body
: html
),
25481 scrollRoot
= isWK
? body
: clientAreaRoot
,
25482 rootWidth
= clientAreaRoot
.clientWidth
,
25483 rootHeight
= clientAreaRoot
.clientHeight
,
25484 rtl
= !geom
.isBodyLtr(doc
),
25485 nodePos
= pos
|| geom
.position(node
),
25486 el
= node
.parentNode
,
25487 isFixed = function(el
){
25488 return ((isIE
<= 6 || (isIE
&& backCompat
))? false : (style
.get(el
, 'position').toLowerCase() == "fixed"));
25490 if(isFixed(node
)){ return; } // nothing to do
25493 if(el
== body
){ el
= scrollRoot
; }
25494 var elPos
= geom
.position(el
),
25495 fixedPos
= isFixed(el
);
25497 if(el
== scrollRoot
){
25498 elPos
.w
= rootWidth
; elPos
.h
= rootHeight
;
25499 if(scrollRoot
== html
&& isIE
&& rtl
){ elPos
.x
+= scrollRoot
.offsetWidth
-elPos
.w
; } // IE workaround where scrollbar causes negative x
25500 if(elPos
.x
< 0 || !isIE
){ elPos
.x
= 0; } // IE can have values > 0
25501 if(elPos
.y
< 0 || !isIE
){ elPos
.y
= 0; }
25503 var pb
= geom
.getPadBorderExtents(el
);
25504 elPos
.w
-= pb
.w
; elPos
.h
-= pb
.h
; elPos
.x
+= pb
.l
; elPos
.y
+= pb
.t
;
25505 var clientSize
= el
.clientWidth
,
25506 scrollBarSize
= elPos
.w
- clientSize
;
25507 if(clientSize
> 0 && scrollBarSize
> 0){
25508 elPos
.w
= clientSize
;
25509 elPos
.x
+= (rtl
&& (isIE
|| el
.clientLeft
> pb
.l
/*Chrome*/)) ? scrollBarSize
: 0;
25511 clientSize
= el
.clientHeight
;
25512 scrollBarSize
= elPos
.h
- clientSize
;
25513 if(clientSize
> 0 && scrollBarSize
> 0){
25514 elPos
.h
= clientSize
;
25517 if(fixedPos
){ // bounded by viewport, not parents
25519 elPos
.h
+= elPos
.y
; elPos
.y
= 0;
25522 elPos
.w
+= elPos
.x
; elPos
.x
= 0;
25524 if(elPos
.y
+ elPos
.h
> rootHeight
){
25525 elPos
.h
= rootHeight
- elPos
.y
;
25527 if(elPos
.x
+ elPos
.w
> rootWidth
){
25528 elPos
.w
= rootWidth
- elPos
.x
;
25531 // calculate overflow in all 4 directions
25532 var l
= nodePos
.x
- elPos
.x
, // beyond left: < 0
25533 t
= nodePos
.y
- Math
.max(elPos
.y
, 0), // beyond top: < 0
25534 r
= l
+ nodePos
.w
- elPos
.w
, // beyond right: > 0
25535 bot
= t
+ nodePos
.h
- elPos
.h
; // beyond bottom: > 0
25537 var s
= Math
[l
< 0? "max" : "min"](l
, r
);
25538 if(rtl
&& ((isIE
== 8 && !backCompat
) || isIE
>= 9)){ s
= -s
; }
25539 nodePos
.x
+= el
.scrollLeft
;
25540 el
.scrollLeft
+= s
;
25541 nodePos
.x
-= el
.scrollLeft
;
25544 nodePos
.y
+= el
.scrollTop
;
25545 el
.scrollTop
+= Math
[t
< 0? "max" : "min"](t
, bot
);
25546 nodePos
.y
-= el
.scrollTop
;
25548 el
= (el
!= scrollRoot
) && !fixedPos
&& el
.parentNode
;
25551 console
.error('scrollIntoView: ' + error
);
25552 node
.scrollIntoView(false);
25557 1 && lang
.setObject("dojo.window", window
);
25563 'dojo/number':function(){
25564 define("dojo/number", [/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
25565 function(/*===== declare, =====*/ lang
, i18n
, nlsNumber
, dstring
, dregexp
){
25572 // localized formatting and parsing routines for Number
25574 lang
.setObject("dojo.number", number
);
25577 number.__FormatOptions = declare(null, {
25578 // pattern: String?
25579 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25580 // with this string. Default value is based on locale. Overriding this property will defeat
25581 // localization. Literal characters in patterns are not supported.
25583 // choose a format type based on the locale from the following:
25584 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25586 // fixed number of decimal places to show. This overrides any
25587 // information in the provided pattern.
25589 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25590 // means do not round.
25592 // override the locale used to determine formatting rules
25593 // fractional: Boolean?
25594 // If false, show no decimal places, overriding places and pattern settings.
25598 number
.format = function(/*Number*/ value
, /*number.__FormatOptions?*/ options
){
25600 // Format a Number as a String, using locale-specific settings
25602 // Create a string from a Number using a known localized pattern.
25603 // Formatting patterns appropriate to the locale are chosen from the
25604 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
25606 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
25608 // the number to be formatted
25610 options
= lang
.mixin({}, options
|| {});
25611 var locale
= i18n
.normalizeLocale(options
.locale
),
25612 bundle
= i18n
.getLocalization("dojo.cldr", "number", locale
);
25613 options
.customs
= bundle
;
25614 var pattern
= options
.pattern
|| bundle
[(options
.type
|| "decimal") + "Format"];
25615 if(isNaN(value
) || Math
.abs(value
) == Infinity
){ return null; } // null
25616 return number
._applyPattern(value
, pattern
, options
); // String
25619 //number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
25620 number
._numberPatternRE
= /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
25622 number
._applyPattern = function(/*Number*/ value
, /*String*/ pattern
, /*number.__FormatOptions?*/ options
){
25624 // Apply pattern to format value as a string using options. Gives no
25625 // consideration to local customs.
25627 // the number to be formatted.
25629 // a pattern string as described by
25630 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25631 // options: number.__FormatOptions?
25632 // _applyPattern is usually called via `dojo/number.format()` which
25633 // populates an extra property in the options parameter, "customs".
25634 // The customs object specifies group and decimal parameters if set.
25636 //TODO: support escapes
25637 options
= options
|| {};
25638 var group
= options
.customs
.group
,
25639 decimal = options
.customs
.decimal,
25640 patternList
= pattern
.split(';'),
25641 positivePattern
= patternList
[0];
25642 pattern
= patternList
[(value
< 0) ? 1 : 0] || ("-" + positivePattern
);
25644 //TODO: only test against unescaped
25645 if(pattern
.indexOf('%') != -1){
25647 }else if(pattern
.indexOf('\u2030') != -1){
25648 value
*= 1000; // per mille
25649 }else if(pattern
.indexOf('\u00a4') != -1){
25650 group
= options
.customs
.currencyGroup
|| group
;//mixins instead?
25651 decimal = options
.customs
.currencyDecimal
|| decimal;// Should these be mixins instead?
25652 pattern
= pattern
.replace(/\u00a4{1,3}/, function(match
){
25653 var prop
= ["symbol", "currency", "displayName"][match
.length
-1];
25654 return options
[prop
] || options
.currency
|| "";
25656 }else if(pattern
.indexOf('E') != -1){
25657 throw new Error("exponential notation not supported");
25660 //TODO: support @ sig figs?
25661 var numberPatternRE
= number
._numberPatternRE
;
25662 var numberPattern
= positivePattern
.match(numberPatternRE
);
25663 if(!numberPattern
){
25664 throw new Error("unable to find a number expression in pattern: "+pattern
);
25666 if(options
.fractional
=== false){ options
.places
= 0; }
25667 return pattern
.replace(numberPatternRE
,
25668 number
._formatAbsolute(value
, numberPattern
[0], {decimal: decimal, group
: group
, places
: options
.places
, round
: options
.round
}));
25671 number
.round = function(/*Number*/ value
, /*Number?*/ places
, /*Number?*/ increment
){
25673 // Rounds to the nearest value with the given number of decimal places, away from zero
25675 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
25676 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
25677 // fractional increments also, such as the nearest quarter.
25678 // NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround.
25680 // The number to round
25682 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
25683 // Must be non-negative.
25685 // Rounds next place to nearest value of increment/10. 10 by default.
25687 // | >>> number.round(-0.5)
25689 // | >>> number.round(162.295, 2)
25690 // | 162.29 // note floating point error. Should be 162.3
25691 // | >>> number.round(10.71, 0, 2.5)
25693 var factor
= 10 / (increment
|| 10);
25694 return (factor
* +value
).toFixed(places
) / factor
; // Number
25697 if((0.9).toFixed() == 0){
25698 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
25699 // is just after the rounding place and is >=5
25700 var round
= number
.round
;
25701 number
.round = function(v
, p
, m
){
25702 var d
= Math
.pow(10, -p
|| 0), a
= Math
.abs(v
);
25707 if(a
< 0.5 || a
>= 0.95){
25711 return round(v
, p
, m
) + (v
> 0 ? d
: -d
);
25714 // Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above.
25715 /*===== number.round = round; =====*/
25719 number.__FormatAbsoluteOptions = declare(null, {
25720 // decimal: String?
25721 // the decimal separator
25723 // the group separator
25724 // places: Number|String?
25725 // number of decimal places. the range "n,m" will format to m places.
25727 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25728 // means don't round.
25732 number
._formatAbsolute = function(/*Number*/ value
, /*String*/ pattern
, /*number.__FormatAbsoluteOptions?*/ options
){
25734 // Apply numeric pattern to absolute value using options. Gives no
25735 // consideration to local customs.
25737 // the number to be formatted, ignores sign
25739 // the number portion of a pattern (e.g. `#,##0.00`)
25740 options
= options
|| {};
25741 if(options
.places
=== true){options
.places
=0;}
25742 if(options
.places
=== Infinity
){options
.places
=6;} // avoid a loop; pick a limit
25744 var patternParts
= pattern
.split("."),
25745 comma
= typeof options
.places
== "string" && options
.places
.indexOf(","),
25746 maxPlaces
= options
.places
;
25748 maxPlaces
= options
.places
.substring(comma
+ 1);
25749 }else if(!(maxPlaces
>= 0)){
25750 maxPlaces
= (patternParts
[1] || []).length
;
25752 if(!(options
.round
< 0)){
25753 value
= number
.round(value
, maxPlaces
, options
.round
);
25756 var valueParts
= String(Math
.abs(value
)).split("."),
25757 fractional
= valueParts
[1] || "";
25758 if(patternParts
[1] || options
.places
){
25760 options
.places
= options
.places
.substring(0, comma
);
25762 // Pad fractional with trailing zeros
25763 var pad
= options
.places
!== undefined ? options
.places
: (patternParts
[1] && patternParts
[1].lastIndexOf("0") + 1);
25764 if(pad
> fractional
.length
){
25765 valueParts
[1] = dstring
.pad(fractional
, pad
, '0', true);
25768 // Truncate fractional
25769 if(maxPlaces
< fractional
.length
){
25770 valueParts
[1] = fractional
.substr(0, maxPlaces
);
25773 if(valueParts
[1]){ valueParts
.pop(); }
25776 // Pad whole with leading zeros
25777 var patternDigits
= patternParts
[0].replace(',', '');
25778 pad
= patternDigits
.indexOf("0");
25780 pad
= patternDigits
.length
- pad
;
25781 if(pad
> valueParts
[0].length
){
25782 valueParts
[0] = dstring
.pad(valueParts
[0], pad
);
25786 if(patternDigits
.indexOf("#") == -1){
25787 valueParts
[0] = valueParts
[0].substr(valueParts
[0].length
- pad
);
25791 // Add group separators
25792 var index
= patternParts
[0].lastIndexOf(','),
25793 groupSize
, groupSize2
;
25795 groupSize
= patternParts
[0].length
- index
- 1;
25796 var remainder
= patternParts
[0].substr(0, index
);
25797 index
= remainder
.lastIndexOf(',');
25799 groupSize2
= remainder
.length
- index
- 1;
25803 for(var whole
= valueParts
[0]; whole
;){
25804 var off
= whole
.length
- groupSize
;
25805 pieces
.push((off
> 0) ? whole
.substr(off
) : whole
);
25806 whole
= (off
> 0) ? whole
.slice(0, off
) : "";
25808 groupSize
= groupSize2
;
25812 valueParts
[0] = pieces
.reverse().join(options
.group
|| ",");
25814 return valueParts
.join(options
.decimal || ".");
25818 number.__RegexpOptions = declare(null, {
25819 // pattern: String?
25820 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25821 // with this string. Default value is based on locale. Overriding this property will defeat
25824 // choose a format type based on the locale from the following:
25825 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25827 // override the locale used to determine formatting rules
25828 // strict: Boolean?
25829 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25830 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25831 // places: Number|String?
25832 // number of decimal places to accept: Infinity, a positive number, or
25833 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
25836 number
.regexp = function(/*number.__RegexpOptions?*/ options
){
25838 // Builds the regular needed to parse a number
25840 // Returns regular expression with positive and negative match, group
25841 // and decimal separators
25842 return number
._parseInfo(options
).regexp
; // String
25845 number
._parseInfo = function(/*Object?*/ options
){
25846 options
= options
|| {};
25847 var locale
= i18n
.normalizeLocale(options
.locale
),
25848 bundle
= i18n
.getLocalization("dojo.cldr", "number", locale
),
25849 pattern
= options
.pattern
|| bundle
[(options
.type
|| "decimal") + "Format"],
25851 group
= bundle
.group
,
25852 decimal = bundle
.decimal,
25855 if(pattern
.indexOf('%') != -1){
25857 }else if(pattern
.indexOf('\u2030') != -1){
25858 factor
/= 1000; // per mille
25860 var isCurrency
= pattern
.indexOf('\u00a4') != -1;
25862 group
= bundle
.currencyGroup
|| group
;
25863 decimal = bundle
.currencyDecimal
|| decimal;
25867 //TODO: handle quoted escapes
25868 var patternList
= pattern
.split(';');
25869 if(patternList
.length
== 1){
25870 patternList
.push("-" + patternList
[0]);
25873 var re
= dregexp
.buildGroupRE(patternList
, function(pattern
){
25874 pattern
= "(?:"+dregexp
.escapeString(pattern
, '.')+")";
25875 return pattern
.replace(number
._numberPatternRE
, function(format
){
25878 separator
: options
.strict
? group
: [group
,""],
25879 fractional
: options
.fractional
,
25884 parts
= format
.split('.'),
25885 places
= options
.places
;
25887 // special condition for percent (factor != 1)
25888 // allow decimal places even if not specified in pattern
25889 if(parts
.length
== 1 && factor
!= 1){
25892 if(parts
.length
== 1 || places
=== 0){
25893 flags
.fractional
= false;
25895 if(places
=== undefined){ places
= options
.pattern
? parts
[1].lastIndexOf('0') + 1 : Infinity
; }
25896 if(places
&& options
.fractional
== undefined){flags
.fractional
= true;} // required fractional, unless otherwise specified
25897 if(!options
.places
&& (places
< parts
[1].length
)){ places
+= "," + parts
[1].length
; }
25898 flags
.places
= places
;
25900 var groups
= parts
[0].split(',');
25901 if(groups
.length
> 1){
25902 flags
.groupSize
= groups
.pop().length
;
25903 if(groups
.length
> 1){
25904 flags
.groupSize2
= groups
.pop().length
;
25907 return "("+number
._realNumberRegexp(flags
)+")";
25912 // substitute the currency symbol for the placeholder in the pattern
25913 re
= re
.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match
, before
, target
, after
){
25914 var prop
= ["symbol", "currency", "displayName"][target
.length
-1],
25915 symbol
= dregexp
.escapeString(options
[prop
] || options
.currency
|| "");
25916 before
= before
? "[\\s\\xa0]" : "";
25917 after
= after
? "[\\s\\xa0]" : "";
25918 if(!options
.strict
){
25919 if(before
){before
+= "*";}
25920 if(after
){after
+= "*";}
25921 return "(?:"+before
+symbol
+after
+")?";
25923 return before
+symbol
+after
;
25927 //TODO: substitute localized sign/percent/permille/etc.?
25929 // normalize whitespace and return
25930 return {regexp
: re
.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group
: group
, decimal: decimal, factor
: factor
}; // Object
25934 number.__ParseOptions = declare(null, {
25935 // pattern: String?
25936 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25937 // with this string. Default value is based on locale. Overriding this property will defeat
25938 // localization. Literal characters in patterns are not supported.
25940 // choose a format type based on the locale from the following:
25941 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25943 // override the locale used to determine formatting rules
25944 // strict: Boolean?
25945 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25946 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25947 // fractional: Boolean|Array?
25948 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
25949 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
25952 number
.parse = function(/*String*/ expression
, /*number.__ParseOptions?*/ options
){
25954 // Convert a properly formatted string to a primitive Number, using
25955 // locale-specific settings.
25957 // Create a Number from a string using a known localized pattern.
25958 // Formatting patterns are chosen appropriate to the locale
25959 // and follow the syntax described by
25960 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25961 // Note that literal characters in patterns are not supported.
25963 // A string representation of a Number
25964 var info
= number
._parseInfo(options
),
25965 results
= (new RegExp("^"+info
.regexp
+"$")).exec(expression
);
25969 var absoluteMatch
= results
[1]; // match for the positive expression
25974 // matched the negative pattern
25975 absoluteMatch
=results
[2];
25979 // Transform it to something Javascript can parse as a number. Normalize
25980 // decimal point and strip out group separators or alternate forms of whitespace
25981 absoluteMatch
= absoluteMatch
.
25982 replace(new RegExp("["+info
.group
+ "\\s\\xa0"+"]", "g"), "").
25983 replace(info
.decimal, ".");
25984 // Adjust for negative sign, percent, etc. as necessary
25985 return absoluteMatch
* info
.factor
; //Number
25989 number.__RealNumberRegexpFlags = declare(null, {
25991 // The integer number of decimal places or a range given as "n,m". If
25992 // not given, the decimal part is optional and the number of places is
25994 // decimal: String?
25995 // A string for the character used as the decimal point. Default
25997 // fractional: Boolean|Array?
25998 // Whether decimal places are used. Can be true, false, or [true,
25999 // false]. Default is [true, false] which means optional.
26000 // exponent: Boolean|Array?
26001 // Express in exponential notation. Can be true, false, or [true,
26002 // false]. Default is [true, false], (i.e. will match if the
26003 // exponential part is present are not).
26004 // eSigned: Boolean|Array?
26005 // The leading plus-or-minus sign on the exponent. Can be true,
26006 // false, or [true, false]. Default is [true, false], (i.e. will
26007 // match if it is signed or unsigned). flags in regexp.integer can be
26012 number
._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags
){
26014 // Builds a regular expression to match a real number in exponential
26017 // assign default values to missing parameters
26018 flags
= flags
|| {};
26019 //TODO: use mixin instead?
26020 if(!("places" in flags
)){ flags
.places
= Infinity
; }
26021 if(typeof flags
.decimal != "string"){ flags
.decimal = "."; }
26022 if(!("fractional" in flags
) || /^0/.test(flags
.places
)){ flags
.fractional
= [true, false]; }
26023 if(!("exponent" in flags
)){ flags
.exponent
= [true, false]; }
26024 if(!("eSigned" in flags
)){ flags
.eSigned
= [true, false]; }
26026 var integerRE
= number
._integerRegexp(flags
),
26027 decimalRE
= dregexp
.buildGroupRE(flags
.fractional
,
26030 if(q
&& (flags
.places
!==0)){
26031 re
= "\\" + flags
.decimal;
26032 if(flags
.places
== Infinity
){
26033 re
= "(?:" + re
+ "\\d+)?";
26035 re
+= "\\d{" + flags
.places
+ "}";
26043 var exponentRE
= dregexp
.buildGroupRE(flags
.exponent
,
26045 if(q
){ return "([eE]" + number
._integerRegexp({ signed
: flags
.eSigned
}) + ")"; }
26050 var realRE
= integerRE
+ decimalRE
;
26051 // allow for decimals without integers, e.g. .25
26052 if(decimalRE
){realRE
= "(?:(?:"+ realRE
+ ")|(?:" + decimalRE
+ "))";}
26053 return realRE
+ exponentRE
; // String
26057 number.__IntegerRegexpFlags = declare(null, {
26058 // signed: Boolean?
26059 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
26060 // Default is `[true, false]`, (i.e. will match if it is signed
26062 // separator: String?
26063 // The character used as the thousands separator. Default is no
26064 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
26065 // makes ',' optional.
26066 // groupSize: Number?
26067 // group size between separators
26068 // groupSize2: Number?
26069 // second grouping, where separators 2..n have a different interval than the first separator (for India)
26073 number
._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags
){
26075 // Builds a regular expression that matches an integer
26077 // assign default values to missing parameters
26078 flags
= flags
|| {};
26079 if(!("signed" in flags
)){ flags
.signed
= [true, false]; }
26080 if(!("separator" in flags
)){
26081 flags
.separator
= "";
26082 }else if(!("groupSize" in flags
)){
26083 flags
.groupSize
= 3;
26086 var signRE
= dregexp
.buildGroupRE(flags
.signed
,
26087 function(q
){ return q
? "[-+]" : ""; },
26091 var numberRE
= dregexp
.buildGroupRE(flags
.separator
,
26097 sep
= dregexp
.escapeString(sep
);
26098 if(sep
== " "){ sep
= "\\s"; }
26099 else if(sep
== "\xa0"){ sep
= "\\s\\xa0"; }
26101 var grp
= flags
.groupSize
, grp2
= flags
.groupSize2
;
26102 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
26104 var grp2RE
= "(?:0|[1-9]\\d{0," + (grp2
-1) + "}(?:[" + sep
+ "]\\d{" + grp2
+ "})*[" + sep
+ "]\\d{" + grp
+ "})";
26105 return ((grp
-grp2
) > 0) ? "(?:" + grp2RE
+ "|(?:0|[1-9]\\d{0," + (grp
-1) + "}))" : grp2RE
;
26107 return "(?:0|[1-9]\\d{0," + (grp
-1) + "}(?:[" + sep
+ "]\\d{" + grp
+ "})*)";
26112 return signRE
+ numberRE
; // String
26119 'dijit/_FocusMixin':function(){
26120 define("dijit/_FocusMixin", [
26123 "dojo/_base/declare", // declare
26124 "dojo/_base/lang" // lang.extend
26125 ], function(focus
, _WidgetBase
, declare
, lang
){
26128 // dijit/_FocusMixin
26130 // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
26131 // to be last in the inheritance chain, so mixin to _WidgetBase.
26132 lang
.extend(_WidgetBase
, {
26133 // focused: [readonly] Boolean
26134 // This widget or a widget it contains has focus, or is "active" because
26135 // it was recently clicked.
26138 onFocus: function(){
26140 // Called when the widget becomes "active" because
26141 // it or a widget inside of it either has focus, or has recently
26147 onBlur: function(){
26149 // Called when the widget stops being "active" because
26150 // focus moved to something outside of it, or the user
26151 // clicked somewhere outside of it, or the widget was
26157 _onFocus: function(){
26159 // This is where widgets do processing for when they are active,
26160 // such as changing CSS classes. See onFocus() for more details.
26166 _onBlur: function(){
26168 // This is where widgets do processing for when they stop being active,
26169 // such as changing CSS classes. See onBlur() for more details.
26176 return declare("dijit._FocusMixin", null, {
26178 // Mixin to widget to provide _onFocus() and _onBlur() methods that
26179 // fire when a widget or its descendants get/lose focus
26181 // flag that I want _onFocus()/_onBlur() notifications from focus manager
26182 _focusManager
: focus
26188 'dojo/data/util/filter':function(){
26189 define("dojo/data/util/filter", ["../../_base/lang"], function(lang
){
26191 // dojo/data/util/filter
26196 lang
.setObject("dojo.data.util.filter", filter
);
26198 filter
.patternToRegExp = function(/*String*/pattern
, /*boolean?*/ ignoreCase
){
26200 // Helper function to convert a simple pattern to a regular expression for matching.
26202 // Returns a regular expression object that conforms to the defined conversion rules.
26205 // - ca* -> /^ca.*$/
26206 // - *ca* -> /^.*ca.*$/
26207 // - *c\*a* -> /^.*c\*a.*$/
26208 // - *c\*a?* -> /^.*c\*a..*$/
26212 // A simple matching pattern to convert that follows basic rules:
26214 // - * Means match anything, so ca* means match anything starting with ca
26215 // - ? Means match single character. So, b?b will match to bob and bab, and so on.
26216 // - \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
26218 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
26219 // represented by \\ to be treated as an ordinary \ character instead of an escape.
26221 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
26222 // By default, it is assumed case sensitive.
26226 for(var i
= 0; i
< pattern
.length
; i
++){
26227 c
= pattern
.charAt(i
);
26232 rxp
+= pattern
.charAt(i
);
26235 rxp
+= ".*"; break;
26250 rxp
+= "\\"; //fallthrough
26257 return new RegExp(rxp
,"mi"); //RegExp
26259 return new RegExp(rxp
,"m"); //RegExp
26268 'dijit/_WidgetsInTemplateMixin':function(){
26269 define("dijit/_WidgetsInTemplateMixin", [
26270 "dojo/_base/array", // array.forEach
26271 "dojo/_base/declare", // declare
26272 "dojo/parser" // parser.parse
26273 ], function(array
, declare
, parser
){
26276 // dijit/_WidgetsInTemplateMixin
26278 return declare("dijit._WidgetsInTemplateMixin", null, {
26280 // Mixin to supplement _TemplatedMixin when template contains widgets
26282 // _earlyTemplatedStartup: Boolean
26283 // A fallback to preserve the 1.0 - 1.3 behavior of children in
26284 // templates having their startup called before the parent widget
26285 // fires postCreate. Defaults to 'false', causing child widgets to
26286 // have their .startup() called immediately before a parent widget
26287 // .startup(), but always after the parent .postCreate(). Set to
26288 // 'true' to re-enable to previous, arguably broken, behavior.
26289 _earlyTemplatedStartup
: false,
26291 // widgetsInTemplate: [protected] Boolean
26292 // Should we parse the template to find widgets that might be
26293 // declared in markup inside it? (Remove for 2.0 and assume true)
26294 widgetsInTemplate
: true,
26296 _beforeFillContent: function(){
26297 if(this.widgetsInTemplate
){
26298 // Before copying over content, instantiate widgets in template
26299 var node
= this.domNode
;
26301 var cw
= (this._startupWidgets
= parser
.parse(node
, {
26302 noStart
: !this._earlyTemplatedStartup
,
26304 inherited
: {dir
: this.dir
, lang
: this.lang
, textDir
: this.textDir
},
26305 propsThis
: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
26306 scope
: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
26309 if(!cw
.isFulfilled()){
26310 throw new Error(this.declaredClass
+ ": parser returned unfilled promise (probably waiting for module auto-load), " +
26311 "unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.");
26314 // _WidgetBase::destroy() will destroy any supporting widgets under this.domNode.
26315 // If we wanted to, we could call this.own() on anything in this._startupWidgets that was moved outside
26316 // of this.domNode (like Dialog, which is moved to <body>).
26318 this._attachTemplateNodes(cw
, function(n
,p
){
26324 startup: function(){
26325 array
.forEach(this._startupWidgets
, function(w
){
26326 if(w
&& !w
._started
&& w
.startup
){
26330 this.inherited(arguments
);
26336 'dojo/fx/Toggler':function(){
26337 define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
26338 function(lang
, declare
, baseFx
, connectUtil
){
26342 return declare("dojo.fx.Toggler", null, {
26344 // A simple `dojo.Animation` toggler API.
26346 // class constructor for an animation toggler. It accepts a packed
26347 // set of arguments about what type of animation to use in each
26348 // direction, duration, etc. All available members are mixed into
26349 // these animations from the constructor (for example, `node`,
26350 // `showDuration`, `hideDuration`).
26352 // | var t = new dojo/fx/Toggler({
26353 // | node: "nodeId",
26354 // | showDuration: 500,
26355 // | // hideDuration will default to "200"
26356 // | showFunc: dojo/fx/wipeIn,
26357 // | // hideFunc will default to "fadeOut"
26359 // | t.show(100); // delay showing for 100ms
26360 // | // ...time passes...
26364 // the node to target for the showing and hiding animations
26367 // showFunc: Function
26368 // The function that returns the `dojo.Animation` to show the node
26369 showFunc
: baseFx
.fadeIn
,
26371 // hideFunc: Function
26372 // The function that returns the `dojo.Animation` to hide the node
26373 hideFunc
: baseFx
.fadeOut
,
26376 // Time in milliseconds to run the show Animation
26380 // Time in milliseconds to run the hide Animation
26383 // FIXME: need a policy for where the toggler should "be" the next
26384 // time show/hide are called if we're stopped somewhere in the
26386 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
26387 // each animation individually.
26388 // FIXME: also would be nice to have events from the animations exposed/bridged
26401 constructor: function(args
){
26404 lang
.mixin(_t
, args
);
26405 _t
.node
= args
.node
;
26406 _t
._showArgs
= lang
.mixin({}, args
);
26407 _t
._showArgs
.node
= _t
.node
;
26408 _t
._showArgs
.duration
= _t
.showDuration
;
26409 _t
.showAnim
= _t
.showFunc(_t
._showArgs
);
26411 _t
._hideArgs
= lang
.mixin({}, args
);
26412 _t
._hideArgs
.node
= _t
.node
;
26413 _t
._hideArgs
.duration
= _t
.hideDuration
;
26414 _t
.hideAnim
= _t
.hideFunc(_t
._hideArgs
);
26416 connectUtil
.connect(_t
.showAnim
, "beforeBegin", lang
.hitch(_t
.hideAnim
, "stop", true));
26417 connectUtil
.connect(_t
.hideAnim
, "beforeBegin", lang
.hitch(_t
.showAnim
, "stop", true));
26420 show: function(delay
){
26422 // Toggle the node to showing
26424 // Amount of time to stall playing the show animation
26425 return this.showAnim
.play(delay
|| 0);
26428 hide: function(delay
){
26430 // Toggle the node to hidden
26432 // Amount of time to stall playing the hide animation
26433 return this.hideAnim
.play(delay
|| 0);
26440 'dijit/form/FilteringSelect':function(){
26441 define("dijit/form/FilteringSelect", [
26442 "dojo/data/util/filter", // filter.patternToRegExp
26443 "dojo/_base/declare", // declare
26444 "dojo/_base/lang", // lang.mixin
26448 ], function(filter
, declare
, lang
, when
, MappedTextBox
, ComboBoxMixin
){
26451 // dijit/form/FilteringSelect
26453 return declare("dijit.form.FilteringSelect", [MappedTextBox
, ComboBoxMixin
], {
26455 // An enhanced version of the HTML SELECT tag, populated dynamically
26458 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
26459 // very nicely with very large data sets because it can load and page data as needed.
26460 // It also resembles ComboBox, but does not allow values outside of the provided ones.
26461 // If OPTION tags are used as the data provider via markup, then the
26462 // OPTION tag's child text node is used as the displayed value when selected
26463 // while the OPTION tag's value attribute is used as the widget value on form submit.
26464 // To set the default value when using OPTION tags, specify the selected
26465 // attribute on 1 of the child OPTION tags.
26467 // Similar features:
26469 // - There is a drop down list of possible values.
26470 // - You can only enter a value from the drop down list. (You can't
26471 // enter an arbitrary value.)
26472 // - The value submitted with the form is the hidden value (ex: CA),
26473 // not the displayed value a.k.a. label (ex: California)
26475 // Enhancements over plain HTML version:
26477 // - If you type in some text then it will filter down the list of
26478 // possible values in the drop down list.
26479 // - List can be specified either as a static list or via a javascript
26480 // function (that can get the list from a server)
26482 // required: Boolean
26483 // True (default) if user is required to enter a value into this field.
26486 _lastDisplayedValue
: "",
26488 _isValidSubset: function(){
26489 return this._opened
;
26492 isValid: function(){
26493 // Overrides ValidationTextBox.isValid()
26494 return !!this.item
|| (!this.required
&& this.get('displayedValue') == ""); // #5974
26497 _refreshState: function(){
26498 if(!this.searchTimer
){ // state will be refreshed after results are returned
26499 this.inherited(arguments
);
26503 _callbackSetLabel: function(
26506 /*Object*/ options
,
26507 /*Boolean?*/ priorityChange
){
26509 // Callback from dojo.store after lookup of user entered value finishes
26511 // setValue does a synchronous lookup,
26512 // so it calls _callbackSetLabel directly,
26513 // and so does not pass dataObject
26514 // still need to test against _lastQuery in case it came too late
26515 if((query
&& query
[this.searchAttr
] !== this._lastQuery
) || (!query
&& result
.length
&& this.store
.getIdentity(result
[0]) != this._lastQuery
)){
26518 if(!result
.length
){
26519 //#3268: don't modify display value on bad input
26520 //#3285: change CSS to indicate error
26521 this.set("value", '', priorityChange
|| (priorityChange
=== undefined && !this.focused
), this.textbox
.value
, null);
26523 this.set('item', result
[0], priorityChange
);
26527 _openResultList: function(/*Object*/ results
, /*Object*/ query
, /*Object*/ options
){
26528 // Callback when a data store query completes.
26529 // Overrides ComboBox._openResultList()
26531 // #3285: tap into search callback to see if user's query resembles a match
26532 if(query
[this.searchAttr
] !== this._lastQuery
){
26535 this.inherited(arguments
);
26537 if(this.item
=== undefined){ // item == undefined for keyboard search
26538 // If the search returned no items that means that the user typed
26539 // in something invalid (and they can't make it valid by typing more characters),
26540 // so flag the FilteringSelect as being in an invalid state
26541 this.validate(true);
26545 _getValueAttr: function(){
26547 // Hook for get('value') to work.
26549 // don't get the textbox value but rather the previously set hidden value.
26550 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
26551 return this.valueNode
.value
;
26554 _getValueField: function(){
26555 // Overrides ComboBox._getValueField()
26559 _setValueAttr: function(/*String*/ value
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
, /*item?*/ item
){
26561 // Hook so set('value', value) works.
26563 // Sets the value of the select.
26564 // Also sets the label to the corresponding value by reverse lookup.
26565 if(!this._onChangeActive
){ priorityChange
= null; }
26567 if(item
=== undefined){
26568 if(value
=== null || value
=== ''){
26570 if(!lang
.isString(displayedValue
)){
26571 this._setDisplayedValueAttr(displayedValue
||'', priorityChange
);
26577 this._lastQuery
= value
;
26578 when(this.store
.get(value
), function(item
){
26579 self
._callbackSetLabel(item
? [item
] : [], undefined, undefined, priorityChange
);
26582 this.valueNode
.value
= value
;
26583 this.inherited(arguments
);
26587 _setItemAttr: function(/*item*/ item
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
){
26589 // Set the displayed valued in the input box, and the hidden value
26590 // that gets submitted, based on a dojo.data store item.
26592 // Users shouldn't call this function; they should be calling
26593 // set('item', value)
26596 this.inherited(arguments
);
26597 this._lastDisplayedValue
= this.textbox
.value
;
26600 _getDisplayQueryString: function(/*String*/ text
){
26601 return text
.replace(/([\\\*\?])/g, "\\$1");
26604 _setDisplayedValueAttr: function(/*String*/ label
, /*Boolean?*/ priorityChange
){
26606 // Hook so set('displayedValue', label) works.
26608 // Sets textbox to display label. Also performs reverse lookup
26609 // to set the hidden value. label should corresponding to item.searchAttr.
26611 if(label
== null){ label
= ''; }
26613 // This is called at initialization along with every custom setter.
26614 // Usually (or always?) the call can be ignored. If it needs to be
26615 // processed then at least make sure that the XHR request doesn't trigger an onChange()
26616 // event, even if it returns after creation has finished
26617 if(!this._created
){
26618 if(!("displayedValue" in this.params
)){
26621 priorityChange
= false;
26624 // Do a reverse lookup to map the specified displayedValue to the hidden value.
26625 // Note that if there's a custom labelFunc() this code
26627 this.closeDropDown();
26628 var query
= lang
.clone(this.query
); // #6196: populate query with user-specifics
26631 var qs
= this._getDisplayQueryString(label
), q
;
26632 if(this.store
._oldAPI
){
26633 // remove this branch for 2.0
26636 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
26637 // but with a toString() method to help dojo/store/JsonRest.
26638 // Search string like "Co*" converted to regex like /^Co.*$/i.
26639 q
= filter
.patternToRegExp(qs
, this.ignoreCase
);
26640 q
.toString = function(){ return qs
; };
26642 this._lastQuery
= query
[this.searchAttr
] = q
;
26644 // If the label is not valid, the callback will never set it,
26645 // so the last valid value will get the warning textbox. Set the
26646 // textbox value now so that the impending warning will make
26647 // sense to the user
26648 this.textbox
.value
= label
;
26649 this._lastDisplayedValue
= label
;
26650 this._set("displayedValue", label
); // for watch("displayedValue") notification
26653 ignoreCase
: this.ignoreCase
,
26656 lang
.mixin(options
, this.fetchProperties
);
26657 this._fetchHandle
= this.store
.query(query
, options
);
26658 when(this._fetchHandle
, function(result
){
26659 _this
._fetchHandle
= null;
26660 _this
._callbackSetLabel(result
|| [], query
, options
, priorityChange
);
26662 _this
._fetchHandle
= null;
26663 if(!_this
._cancelingQuery
){ // don't treat canceled query as an error
26664 console
.error('dijit.form.FilteringSelect: ' + err
.toString());
26671 this.set('displayedValue', this._lastDisplayedValue
);
26677 'dojo/data/util/sorter':function(){
26678 define("dojo/data/util/sorter", ["../../_base/lang"], function(lang
){
26680 // dojo/data/util/sorter
26685 lang
.setObject("dojo.data.util.sorter", sorter
);
26687 sorter
.basicComparator = function( /*anything*/ a
,
26690 // Basic comparison function that compares if an item is greater or less than another item
26692 // returns 1 if a > b, -1 if a < b, 0 if equal.
26693 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
26694 // And compared to each other, null is equivalent to undefined.
26696 //null is a problematic compare, so if null, we set to undefined.
26697 //Makes the check logic simple, compact, and consistent
26698 //And (null == undefined) === true, so the check later against null
26699 //works for undefined and is less bytes.
26709 }else if(a
> b
|| a
== null){
26712 return r
; //int {-1,0,1}
26715 sorter
.createSortFunction = function( /* attributes[] */sortSpec
, /*dojo/data/api/Read*/ store){
26717 // Helper function to generate the sorting function based off the list of sort attributes.
26719 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
26720 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
26721 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
26722 // Returns the sorting function for this particular list of attributes and sorting directions.
26724 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
26725 // The objects should be formatted as follows:
26727 // | attribute: "attributeName-string" || attribute,
26728 // | descending: true|false; // Default is false.
26731 // The datastore object to look up item values from.
26733 var sortFunctions=[];
26735 function createSortFunction(attr, dir, comp, s){
26736 //Passing in comp and s (comparator and store), makes this
26737 //function much faster.
26738 return function(itemA, itemB){
26739 var a = s.getValue(itemA, attr);
26740 var b = s.getValue(itemB, attr);
26741 return dir * comp(a,b); //int
26745 var map = store.comparatorMap;
26746 var bc = sorter.basicComparator;
26747 for(var i = 0; i < sortSpec.length; i++){
26748 sortAttribute = sortSpec[i];
26749 var attr = sortAttribute.attribute;
26751 var dir = (sortAttribute.descending) ? -1 : 1;
26754 if(typeof attr !== "string" && ("toString" in attr)){
26755 attr = attr.toString();
26757 comp = map[attr] || bc;
26759 sortFunctions.push(createSortFunction(attr,
26760 dir, comp, store));
26763 return function(rowA, rowB){
26765 while(i < sortFunctions.length){
26766 var ret = sortFunctions[i++](rowA, rowB);
26779 'dijit/form/_ButtonMixin':function(){
26780 define("dijit/form/_ButtonMixin", [
26781 "dojo/_base/declare", // declare
26782 "dojo/dom", // dom.setSelectable
26783 "dojo/_base/event", // event.stop
26784 "../registry" // registry.byNode
26785 ], function(declare, dom, event, registry){
26788 // dijit/form/_ButtonMixin
26790 return declare("dijit.form._ButtonMixin", null, {
26792 // A mixin to add a thin standard API wrapper to a normal HTML button
26794 // A label should always be specified (through innerHTML) or the label attribute.
26798 // - focusNode (required): this node receives focus
26799 // - valueNode (optional): this node's value gets submitted with FORM elements
26800 // - containerNode (optional): this node gets the innerHTML assignment for label
26802 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
26804 // | var button1 = new Button({label: "hello world", onClick: foo});
26805 // | dojo.body().appendChild(button1.domNode);
26807 // label: HTML String
26808 // Content to display in button.
26811 // type: [const] String
26812 // Type of button (submit, reset, button, checkbox, radio)
26815 _onClick: function(/*Event*/ e
){
26817 // Internal function to handle click actions
26822 var preventDefault
= this.onClick(e
) === false; // user click actions
26823 if(!preventDefault
&& this.type
== "submit" && !(this.valueNode
||this.focusNode
).form
){ // see if a non-form widget needs to be signalled
26824 for(var node
=this.domNode
; node
.parentNode
; node
=node
.parentNode
){
26825 var widget
=registry
.byNode(node
);
26826 if(widget
&& typeof widget
._onSubmit
== "function"){
26827 widget
._onSubmit(e
);
26828 preventDefault
= true;
26833 if(preventDefault
){
26834 e
.preventDefault();
26836 return !preventDefault
;
26839 postCreate: function(){
26840 this.inherited(arguments
);
26841 dom
.setSelectable(this.focusNode
, false);
26844 onClick: function(/*Event*/ /*===== e =====*/){
26846 // Callback for when button is clicked.
26847 // If type="submit", return true to perform submit, or false to cancel it.
26850 return true; // Boolean
26853 _setLabelAttr: function(/*String*/ content
){
26855 // Hook for set('label', ...) to work.
26857 // Set the label (text) of the button; takes an HTML string.
26858 this._set("label", content
);
26859 (this.containerNode
||this.focusNode
).innerHTML
= content
;
26866 'dojo/colors':function(){
26867 define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo
, lang
, Color
, ArrayUtil
){
26874 // Color utilities, extending Base dojo.Color
26879 lang
.setObject("dojo.colors", ColorExt
);
26881 //TODO: this module appears to break naming conventions
26883 // this is a standard conversion prescribed by the CSS3 Color Module
26884 var hue2rgb = function(m1
, m2
, h
){
26888 if(h6
< 1){ return m1
+ (m2
- m1
) * h6
; }
26889 if(2 * h
< 1){ return m2
; }
26890 if(3 * h
< 2){ return m1
+ (m2
- m1
) * (2 / 3 - h
) * 6; }
26893 // Override base Color.fromRgb with the impl in this module
26894 dojo
.colorFromRgb
= Color
.fromRgb = function(/*String*/ color
, /*dojo/_base/Color?*/ obj
){
26896 // get rgb(a) array from css-style color declarations
26898 // this function can handle all 4 CSS3 Color Module formats: rgb,
26899 // rgba, hsl, hsla, including rgb(a) with percentage values.
26900 var m
= color
.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
26902 var c
= m
[2].split(/\s*,\s*/), l
= c
.length
, t
= m
[1], a
;
26903 if((t
== "rgb" && l
== 3) || (t
== "rgba" && l
== 4)){
26905 if(r
.charAt(r
.length
- 1) == "%"){
26906 // 3 rgb percentage values
26907 a
= ArrayUtil
.map(c
, function(x
){
26908 return parseFloat(x
) * 2.56;
26910 if(l
== 4){ a
[3] = c
[3]; }
26911 return Color
.fromArray(a
, obj
); // dojo/_base/Color
26913 return Color
.fromArray(c
, obj
); // dojo/_base/Color
26915 if((t
== "hsl" && l
== 3) || (t
== "hsla" && l
== 4)){
26916 // normalize hsl values
26917 var H
= ((parseFloat(c
[0]) % 360) + 360) % 360 / 360,
26918 S
= parseFloat(c
[1]) / 100,
26919 L
= parseFloat(c
[2]) / 100,
26920 // calculate rgb according to the algorithm
26921 // recommended by the CSS3 Color Module
26922 m2
= L
<= 0.5 ? L
* (S
+ 1) : L
+ S
- L
* S
,
26925 hue2rgb(m1
, m2
, H
+ 1 / 3) * 256,
26926 hue2rgb(m1
, m2
, H
) * 256,
26927 hue2rgb(m1
, m2
, H
- 1 / 3) * 256,
26930 if(l
== 4){ a
[3] = c
[3]; }
26931 return Color
.fromArray(a
, obj
); // dojo/_base/Color
26934 return null; // dojo/_base/Color
26937 var confine = function(c
, low
, high
){
26939 // sanitize a color component by making sure it is a number,
26940 // and clamping it to valid values
26942 return isNaN(c
) ? high
: c
< low
? low
: c
> high
? high
: c
; // Number
26945 Color
.prototype.sanitize = function(){
26947 // makes sure that the object has correct attributes
26949 t
.r
= Math
.round(confine(t
.r
, 0, 255));
26950 t
.g
= Math
.round(confine(t
.g
, 0, 255));
26951 t
.b
= Math
.round(confine(t
.b
, 0, 255));
26952 t
.a
= confine(t
.a
, 0, 1);
26953 return this; // dojo/_base/Color
26956 ColorExt
.makeGrey
= Color
.makeGrey = function(/*Number*/ g
, /*Number?*/ a
){
26958 // creates a greyscale color with an optional alpha
26959 return Color
.fromArray([g
, g
, g
, a
]); // dojo/_base/Color
26962 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
26963 lang
.mixin(Color
.named
, {
26964 "aliceblue": [240,248,255],
26965 "antiquewhite": [250,235,215],
26966 "aquamarine": [127,255,212],
26967 "azure": [240,255,255],
26968 "beige": [245,245,220],
26969 "bisque": [255,228,196],
26970 "blanchedalmond": [255,235,205],
26971 "blueviolet": [138,43,226],
26972 "brown": [165,42,42],
26973 "burlywood": [222,184,135],
26974 "cadetblue": [95,158,160],
26975 "chartreuse": [127,255,0],
26976 "chocolate": [210,105,30],
26977 "coral": [255,127,80],
26978 "cornflowerblue": [100,149,237],
26979 "cornsilk": [255,248,220],
26980 "crimson": [220,20,60],
26981 "cyan": [0,255,255],
26982 "darkblue": [0,0,139],
26983 "darkcyan": [0,139,139],
26984 "darkgoldenrod": [184,134,11],
26985 "darkgray": [169,169,169],
26986 "darkgreen": [0,100,0],
26987 "darkgrey": [169,169,169],
26988 "darkkhaki": [189,183,107],
26989 "darkmagenta": [139,0,139],
26990 "darkolivegreen": [85,107,47],
26991 "darkorange": [255,140,0],
26992 "darkorchid": [153,50,204],
26993 "darkred": [139,0,0],
26994 "darksalmon": [233,150,122],
26995 "darkseagreen": [143,188,143],
26996 "darkslateblue": [72,61,139],
26997 "darkslategray": [47,79,79],
26998 "darkslategrey": [47,79,79],
26999 "darkturquoise": [0,206,209],
27000 "darkviolet": [148,0,211],
27001 "deeppink": [255,20,147],
27002 "deepskyblue": [0,191,255],
27003 "dimgray": [105,105,105],
27004 "dimgrey": [105,105,105],
27005 "dodgerblue": [30,144,255],
27006 "firebrick": [178,34,34],
27007 "floralwhite": [255,250,240],
27008 "forestgreen": [34,139,34],
27009 "gainsboro": [220,220,220],
27010 "ghostwhite": [248,248,255],
27011 "gold": [255,215,0],
27012 "goldenrod": [218,165,32],
27013 "greenyellow": [173,255,47],
27014 "grey": [128,128,128],
27015 "honeydew": [240,255,240],
27016 "hotpink": [255,105,180],
27017 "indianred": [205,92,92],
27018 "indigo": [75,0,130],
27019 "ivory": [255,255,240],
27020 "khaki": [240,230,140],
27021 "lavender": [230,230,250],
27022 "lavenderblush": [255,240,245],
27023 "lawngreen": [124,252,0],
27024 "lemonchiffon": [255,250,205],
27025 "lightblue": [173,216,230],
27026 "lightcoral": [240,128,128],
27027 "lightcyan": [224,255,255],
27028 "lightgoldenrodyellow": [250,250,210],
27029 "lightgray": [211,211,211],
27030 "lightgreen": [144,238,144],
27031 "lightgrey": [211,211,211],
27032 "lightpink": [255,182,193],
27033 "lightsalmon": [255,160,122],
27034 "lightseagreen": [32,178,170],
27035 "lightskyblue": [135,206,250],
27036 "lightslategray": [119,136,153],
27037 "lightslategrey": [119,136,153],
27038 "lightsteelblue": [176,196,222],
27039 "lightyellow": [255,255,224],
27040 "limegreen": [50,205,50],
27041 "linen": [250,240,230],
27042 "magenta": [255,0,255],
27043 "mediumaquamarine": [102,205,170],
27044 "mediumblue": [0,0,205],
27045 "mediumorchid": [186,85,211],
27046 "mediumpurple": [147,112,219],
27047 "mediumseagreen": [60,179,113],
27048 "mediumslateblue": [123,104,238],
27049 "mediumspringgreen": [0,250,154],
27050 "mediumturquoise": [72,209,204],
27051 "mediumvioletred": [199,21,133],
27052 "midnightblue": [25,25,112],
27053 "mintcream": [245,255,250],
27054 "mistyrose": [255,228,225],
27055 "moccasin": [255,228,181],
27056 "navajowhite": [255,222,173],
27057 "oldlace": [253,245,230],
27058 "olivedrab": [107,142,35],
27059 "orange": [255,165,0],
27060 "orangered": [255,69,0],
27061 "orchid": [218,112,214],
27062 "palegoldenrod": [238,232,170],
27063 "palegreen": [152,251,152],
27064 "paleturquoise": [175,238,238],
27065 "palevioletred": [219,112,147],
27066 "papayawhip": [255,239,213],
27067 "peachpuff": [255,218,185],
27068 "peru": [205,133,63],
27069 "pink": [255,192,203],
27070 "plum": [221,160,221],
27071 "powderblue": [176,224,230],
27072 "rosybrown": [188,143,143],
27073 "royalblue": [65,105,225],
27074 "saddlebrown": [139,69,19],
27075 "salmon": [250,128,114],
27076 "sandybrown": [244,164,96],
27077 "seagreen": [46,139,87],
27078 "seashell": [255,245,238],
27079 "sienna": [160,82,45],
27080 "skyblue": [135,206,235],
27081 "slateblue": [106,90,205],
27082 "slategray": [112,128,144],
27083 "slategrey": [112,128,144],
27084 "snow": [255,250,250],
27085 "springgreen": [0,255,127],
27086 "steelblue": [70,130,180],
27087 "tan": [210,180,140],
27088 "thistle": [216,191,216],
27089 "tomato": [255,99,71],
27090 "turquoise": [64,224,208],
27091 "violet": [238,130,238],
27092 "wheat": [245,222,179],
27093 "whitesmoke": [245,245,245],
27094 "yellowgreen": [154,205,50]
27097 return Color
; // TODO: return ColorExt, not Color
27101 'dijit/registry':function(){
27102 define("dijit/registry", [
27103 "dojo/_base/array", // array.forEach array.map
27104 "dojo/sniff", // has("ie")
27105 "dojo/_base/unload", // unload.addOnWindowUnload
27106 "dojo/_base/window", // win.body
27107 "./main" // dijit._scopeName
27108 ], function(array
, has
, unload
, win
, dijit
){
27113 var _widgetTypeCtr
= {}, hash
= {};
27117 // Registry of existing widget on page, plus some utility methods.
27120 // Number of registered widgets
27123 add: function(widget
){
27125 // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
27126 // widget: dijit/_WidgetBase
27127 // Any dijit/_WidgetBase subclass.
27128 if(hash
[widget
.id
]){
27129 throw new Error("Tried to register widget with id==" + widget
.id
+ " but that id is already registered");
27131 hash
[widget
.id
] = widget
;
27135 remove: function(/*String*/ id
){
27137 // Remove a widget from the registry. Does not destroy the widget; simply
27138 // removes the reference.
27145 byId: function(/*String|Widget*/ id
){
27147 // Find a widget by it's id.
27148 // If passed a widget then just returns the widget.
27149 return typeof id
== "string" ? hash
[id
] : id
; // dijit/_WidgetBase
27152 byNode: function(/*DOMNode*/ node
){
27154 // Returns the widget corresponding to the given DOMNode
27155 return hash
[node
.getAttribute("widgetId")]; // dijit/_WidgetBase
27158 toArray: function(){
27160 // Convert registry into a true Array
27163 // Work with the widget .domNodes in a real Array
27164 // | array.map(registry.toArray(), function(w){ return w.domNode; });
27167 for(var id
in hash
){
27170 return ar
; // dijit/_WidgetBase[]
27173 getUniqueId: function(/*String*/widgetType
){
27175 // Generates a unique id for a given widgetType
27179 id
= widgetType
+ "_" +
27180 (widgetType
in _widgetTypeCtr
?
27181 ++_widgetTypeCtr
[widgetType
] : _widgetTypeCtr
[widgetType
] = 0);
27183 return dijit
._scopeName
== "dijit" ? id
: dijit
._scopeName
+ "_" + id
; // String
27186 findWidgets: function(root
, skipNode
){
27188 // Search subtree under root returning widgets found.
27189 // Doesn't search for nested widgets (ie, widgets inside other widgets).
27191 // Node to search under.
27192 // skipNode: DOMNode
27193 // If specified, don't search beneath this node (usually containerNode).
27197 function getChildrenHelper(root
){
27198 for(var node
= root
.firstChild
; node
; node
= node
.nextSibling
){
27199 if(node
.nodeType
== 1){
27200 var widgetId
= node
.getAttribute("widgetId");
27202 var widget
= hash
[widgetId
];
27203 if(widget
){ // may be null on page w/multiple dojo's loaded
27204 outAry
.push(widget
);
27206 }else if(node
!== skipNode
){
27207 getChildrenHelper(node
);
27213 getChildrenHelper(root
);
27217 _destroyAll: function(){
27219 // Code to destroy all widgets and do other cleanup on page unload
27221 // Clean up focus manager lingering references to widgets and nodes
27222 dijit
._curFocus
= null;
27223 dijit
._prevFocus
= null;
27224 dijit
._activeStack
= [];
27226 // Destroy all the widgets, top down
27227 array
.forEach(registry
.findWidgets(win
.body()), function(widget
){
27228 // Avoid double destroy of widgets like Menu that are attached to <body>
27229 // even though they are logically children of other widgets.
27230 if(!widget
._destroyed
){
27231 if(widget
.destroyRecursive
){
27232 widget
.destroyRecursive();
27233 }else if(widget
.destroy
){
27240 getEnclosingWidget: function(/*DOMNode*/ node
){
27242 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
27243 // the node is not contained within the DOM tree of any widget
27245 var id
= node
.nodeType
== 1 && node
.getAttribute("widgetId");
27249 node
= node
.parentNode
;
27254 // In case someone needs to access hash.
27255 // Actually, this is accessed from WidgetSet back-compatibility code
27259 dijit
.registry
= registry
;
27265 'dijit/tree/_dndContainer':function(){
27266 define("dijit/tree/_dndContainer", [
27267 "dojo/aspect", // aspect.after
27268 "dojo/_base/declare", // declare
27269 "dojo/dom-class", // domClass.add domClass.remove domClass.replace
27270 "dojo/_base/event", // event.stop
27271 "dojo/_base/lang", // lang.mixin lang.hitch
27274 ], function(aspect
, declare
,domClass
, event
, lang
, on
, touch
){
27277 // dijit/tree/_dndContainer
27282 // A dict of parameters for Tree source configuration.
27283 // isSource: Boolean?
27284 // Can be used as a DnD source. Defaults to true.
27285 // accept: String[]
27286 // List of accepted types (text strings) for a target; defaults to
27287 // ["text", "treeNode"]
27288 // copyOnly: Boolean?
27289 // Copy items, if true, use a state of Ctrl key otherwise,
27290 // dragThreshold: Number
27291 // The move delay in pixels before detecting a drag; 0 by default
27292 // betweenThreshold: Integer
27293 // Distance from upper/lower edge of node to allow drop to reorder nodes
27297 return declare("dijit.tree._dndContainer", null, {
27300 // This is a base class for `dijit/tree/_dndSelector`, and isn't meant to be used directly.
27301 // It's modeled after `dojo/dnd/Container`.
27306 // current: DomNode
27307 // The currently hovered TreeNode.rowNode (which is the DOM node
27308 // associated w/a given node in the tree, excluding it's descendants)
27312 constructor: function(tree
, params
){
27314 // A constructor of the Container
27316 // Node or node's id to build the container on
27318 // A dict of parameters, which gets mixed into the object
27322 this.node
= tree
.domNode
; // TODO: rename; it's not a TreeNode but the whole Tree
27323 lang
.mixin(this, params
);
27325 // class-specific variables
27326 this.current
= null; // current TreeNode's DOM node
27329 this.containerState
= "";
27330 domClass
.add(this.node
, "dojoDndContainer");
27334 // Mouse (or touch) enter/leave on Tree itself
27335 on(this.node
, touch
.enter
, lang
.hitch(this, "onOverEvent")),
27336 on(this.node
, touch
.leave
, lang
.hitch(this, "onOutEvent")),
27338 // switching between TreeNodes
27339 aspect
.after(this.tree
, "_onNodeMouseEnter", lang
.hitch(this, "onMouseOver"), true),
27340 aspect
.after(this.tree
, "_onNodeMouseLeave", lang
.hitch(this, "onMouseOut"), true),
27342 // cancel text selection and text dragging
27343 on(this.node
, "dragstart", lang
.hitch(event
, "stop")),
27344 on(this.node
, "selectstart", lang
.hitch(event
, "stop"))
27348 destroy: function(){
27350 // Prepares this object to be garbage-collected
27353 while(h
= this.events
.pop()){ h
.remove(); }
27355 // this.clearItems();
27356 this.node
= this.parent
= null;
27360 onMouseOver: function(widget
/*===== , evt =====*/){
27362 // Called when mouse is moved over a TreeNode
27363 // widget: TreeNode
27367 this.current
= widget
;
27370 onMouseOut: function(/*===== widget, evt =====*/){
27372 // Called when mouse is moved away from a TreeNode
27373 // widget: TreeNode
27377 this.current
= null;
27380 _changeState: function(type
, newState
){
27382 // Changes a named state to new state value
27384 // A name of the state to change
27385 // newState: String
27387 var prefix
= "dojoDnd" + type
;
27388 var state
= type
.toLowerCase() + "State";
27389 //domClass.replace(this.node, prefix + newState, prefix + this[state]);
27390 domClass
.replace(this.node
, prefix
+ newState
, prefix
+ this[state
]);
27391 this[state
] = newState
;
27394 _addItemClass: function(node
, type
){
27396 // Adds a class with prefix "dojoDndItem"
27400 // A variable suffix for a class name
27401 domClass
.add(node
, "dojoDndItem" + type
);
27404 _removeItemClass: function(node
, type
){
27406 // Removes a class with prefix "dojoDndItem"
27410 // A variable suffix for a class name
27411 domClass
.remove(node
, "dojoDndItem" + type
);
27414 onOverEvent: function(){
27416 // This function is called once, when mouse is over our container
27419 this._changeState("Container", "Over");
27422 onOutEvent: function(){
27424 // This function is called once, when mouse is out of our container
27427 this._changeState("Container", "");
27433 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n",
27434 'dijit/_base/wai':function(){
27435 define("dijit/_base/wai", [
27436 "dojo/dom-attr", // domAttr.attr
27437 "dojo/_base/lang", // lang.mixin
27438 "../main", // export symbols to dijit
27439 "../hccss" // not using this module directly, but loading it sets CSS flag on <html>
27440 ], function(domAttr
, lang
, dijit
){
27447 // Deprecated methods for setting/getting wai roles and states.
27448 // New code should call setAttribute()/getAttribute() directly.
27450 // Also loads hccss to apply dj_a11y class to root node if machine is in high-contrast mode.
27452 hasWaiRole: function(/*Element*/ elem
, /*String?*/ role
){
27454 // Determines if an element has a particular role.
27456 // True if elem has the specific role attribute and false if not.
27457 // For backwards compatibility if role parameter not provided,
27458 // returns true if has a role
27459 var waiRole
= this.getWaiRole(elem
);
27460 return role
? (waiRole
.indexOf(role
) > -1) : (waiRole
.length
> 0);
27463 getWaiRole: function(/*Element*/ elem
){
27465 // Gets the role for an element (which should be a wai role).
27467 // The role of elem or an empty string if elem
27468 // does not have a role.
27469 return lang
.trim((domAttr
.get(elem
, "role") || "").replace("wairole:",""));
27472 setWaiRole: function(/*Element*/ elem
, /*String*/ role
){
27474 // Sets the role on an element.
27476 // Replace existing role attribute with new role.
27478 domAttr
.set(elem
, "role", role
);
27481 removeWaiRole: function(/*Element*/ elem
, /*String*/ role
){
27483 // Removes the specified role from an element.
27484 // Removes role attribute if no specific role provided (for backwards compat.)
27486 var roleValue
= domAttr
.get(elem
, "role");
27487 if(!roleValue
){ return; }
27489 var t
= lang
.trim((" " + roleValue
+ " ").replace(" " + role
+ " ", " "));
27490 domAttr
.set(elem
, "role", t
);
27492 elem
.removeAttribute("role");
27496 hasWaiState: function(/*Element*/ elem
, /*String*/ state
){
27498 // Determines if an element has a given state.
27500 // Checks for an attribute called "aria-"+state.
27502 // true if elem has a value for the given state and
27503 // false if it does not.
27505 return elem
.hasAttribute
? elem
.hasAttribute("aria-"+state
) : !!elem
.getAttribute("aria-"+state
);
27508 getWaiState: function(/*Element*/ elem
, /*String*/ state
){
27510 // Gets the value of a state on an element.
27512 // Checks for an attribute called "aria-"+state.
27514 // The value of the requested state on elem
27515 // or an empty string if elem has no value for state.
27517 return elem
.getAttribute("aria-"+state
) || "";
27520 setWaiState: function(/*Element*/ elem
, /*String*/ state
, /*String*/ value
){
27522 // Sets a state on an element.
27524 // Sets an attribute called "aria-"+state.
27526 elem
.setAttribute("aria-"+state
, value
);
27529 removeWaiState: function(/*Element*/ elem
, /*String*/ state
){
27531 // Removes a state from an element.
27533 // Sets an attribute called "aria-"+state.
27535 elem
.removeAttribute("aria-"+state
);
27539 lang
.mixin(dijit
, exports
);
27541 /*===== return exports; =====*/
27542 return dijit
; // for back compat :-(
27546 'dijit/form/_FormSelectWidget':function(){
27547 define("dijit/form/_FormSelectWidget", [
27548 "dojo/_base/array", // array.filter array.forEach array.map array.some
27549 "dojo/_base/Deferred",
27550 "dojo/aspect", // aspect.after
27551 "dojo/data/util/sorter", // util.sorter.createSortFunction
27552 "dojo/_base/declare", // declare
27553 "dojo/dom", // dom.setSelectable
27554 "dojo/dom-class", // domClass.toggle
27555 "dojo/_base/kernel", // _scopeName
27556 "dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
27557 "dojo/query", // query
27559 "dojo/store/util/QueryResults",
27560 "./_FormValueWidget"
27561 ], function(array
, Deferred
, aspect
, sorter
, declare
, dom
, domClass
, kernel
, lang
, query
, when
,
27562 QueryResults
, _FormValueWidget
){
27565 // dijit/form/_FormSelectWidget
27568 var __SelectOption = {
27570 // The value of the option. Setting to empty (or missing) will
27571 // place a separator at that location
27573 // The label for our option. It can contain html tags.
27574 // selected: Boolean
27575 // Whether or not we are a selected option
27576 // disabled: Boolean
27577 // Whether or not this specific option is disabled
27581 var _FormSelectWidget
= declare("dijit.form._FormSelectWidget", _FormValueWidget
, {
27583 // Extends _FormValueWidget in order to provide "select-specific"
27584 // values - i.e., those values that are unique to `<select>` elements.
27585 // This also provides the mechanism for reading the elements from
27586 // a store, if desired.
27588 // multiple: [const] Boolean
27589 // Whether or not we are multi-valued
27592 // options: __SelectOption[]
27593 // The set of options for our select item. Roughly corresponds to
27594 // the html `<option>` tag.
27597 // store: dojo/store/api/Store
27598 // A store to use for getting our list of options - rather than reading them
27599 // from the `<option>` html tags. Should support getIdentity().
27600 // For back-compat store can also be a dojo/data/api/Identity.
27604 // A query to use when fetching items from our store
27607 // queryOptions: object
27608 // Query options to use when fetching from the store
27609 queryOptions
: null,
27611 // labelAttr: String?
27612 // The entries in the drop down list come from this attribute in the dojo.store items.
27613 // If ``store`` is set, labelAttr must be set too, unless store is an old-style
27614 // dojo.data store rather than a new dojo/store.
27617 // onFetch: Function
27618 // A callback to do with an onFetch - but before any items are actually
27619 // iterated over (i.e. to filter even further what you want to add)
27622 // sortByLabel: Boolean
27623 // Flag to sort the options returned from a store by the label of
27628 // loadChildrenOnOpen: Boolean
27629 // By default loadChildren is called when the items are fetched from the
27630 // store. This property allows delaying loadChildren (and the creation
27631 // of the options/menuitems) until the user clicks the button to open the
27633 loadChildrenOnOpen
: false,
27635 // onLoadDeferred: [readonly] dojo.Deferred
27636 // This is the `dojo.Deferred` returned by setStore().
27637 // Calling onLoadDeferred.then() registers your
27638 // callback to be called only once, when the prior setStore completes.
27639 onLoadDeferred
: null,
27641 getOptions: function(/*anything*/ valueOrIdx
){
27643 // Returns a given option (or options).
27645 // If passed in as a string, that string is used to look up the option
27646 // in the array of options - based on the value property.
27647 // (See dijit/form/_FormSelectWidget.__SelectOption).
27649 // If passed in a number, then the option with the given index (0-based)
27650 // within this select will be returned.
27652 // If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
27653 // returned if and only if it exists within this select.
27655 // If passed an array, then an array will be returned with each element
27656 // in the array being looked up.
27658 // If not passed a value, then all options will be returned
27661 // The option corresponding with the given value or index. null
27662 // is returned if any of the following are true:
27664 // - A string value is passed in which doesn't exist
27665 // - An index is passed in which is outside the bounds of the array of options
27666 // - A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
27668 // NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
27669 // if the value property matches - NOT if the exact option exists
27670 // NOTE: if passing in an array, null elements will be placed in the returned
27671 // array when a value is not found.
27672 var lookupValue
= valueOrIdx
, opts
= this.options
|| [], l
= opts
.length
;
27674 if(lookupValue
=== undefined){
27675 return opts
; // __SelectOption[]
27677 if(lang
.isArray(lookupValue
)){
27678 return array
.map(lookupValue
, "return this.getOptions(item);", this); // __SelectOption[]
27680 if(lang
.isObject(valueOrIdx
)){
27681 // We were passed an option - so see if it's in our array (directly),
27682 // and if it's not, try and find it by value.
27683 if(!array
.some(this.options
, function(o
, idx
){
27684 if(o
=== lookupValue
||
27685 (o
.value
&& o
.value
=== lookupValue
.value
)){
27694 if(typeof lookupValue
== "string"){
27695 for(var i
=0; i
<l
; i
++){
27696 if(opts
[i
].value
=== lookupValue
){
27702 if(typeof lookupValue
== "number" && lookupValue
>= 0 && lookupValue
< l
){
27703 return this.options
[lookupValue
]; // __SelectOption
27705 return null; // null
27708 addOption: function(/*__SelectOption|__SelectOption[]*/ option
){
27710 // Adds an option or options to the end of the select. If value
27711 // of the option is empty or missing, a separator is created instead.
27712 // Passing in an array of options will yield slightly better performance
27713 // since the children are only loaded once.
27714 if(!lang
.isArray(option
)){ option
= [option
]; }
27715 array
.forEach(option
, function(i
){
27716 if(i
&& lang
.isObject(i
)){
27717 this.options
.push(i
);
27720 this._loadChildren();
27723 removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx
){
27725 // Removes the given option or options. You can remove by string
27726 // (in which case the value is removed), number (in which case the
27727 // index in the options array is removed), or select option (in
27728 // which case, the select option with a matching value is removed).
27729 // You can also pass in an array of those values for a slightly
27730 // better performance since the children are only loaded once.
27731 if(!lang
.isArray(valueOrIdx
)){ valueOrIdx
= [valueOrIdx
]; }
27732 var oldOpts
= this.getOptions(valueOrIdx
);
27733 array
.forEach(oldOpts
, function(i
){
27734 // We can get null back in our array - if our option was not found. In
27735 // that case, we don't want to blow up...
27737 this.options
= array
.filter(this.options
, function(node
){
27738 return (node
.value
!== i
.value
|| node
.label
!== i
.label
);
27740 this._removeOptionItem(i
);
27743 this._loadChildren();
27746 updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption
){
27748 // Updates the values of the given option. The option to update
27749 // is matched based on the value of the entered option. Passing
27750 // in an array of new options will yield better performance since
27751 // the children will only be loaded once.
27752 if(!lang
.isArray(newOption
)){ newOption
= [newOption
]; }
27753 array
.forEach(newOption
, function(i
){
27754 var oldOpt
= this.getOptions(i
), k
;
27756 for(k
in i
){ oldOpt
[k
] = i
[k
]; }
27759 this._loadChildren();
27762 setStore: function(store
,
27766 // Sets the store you would like to use with this select widget.
27767 // The selected value is the value of the new store to set. This
27768 // function returns the original store, in case you want to reuse
27769 // it or something.
27770 // store: dojo/store/api/Store
27771 // The dojo.store you would like to use - it MUST implement getIdentity()
27772 // and MAY implement observe().
27773 // For backwards-compatibility this can also be a data.data store, in which case
27774 // it MUST implement dojo/data/api/Identity,
27775 // and MAY implement dojo/data/api/Notification.
27776 // selectedValue: anything?
27777 // The value that this widget should set itself to *after* the store
27779 // fetchArgs: Object?
27780 // Hash of parameters to set filter on store, etc.
27782 // - query: new value for Select.query,
27783 // - queryOptions: new value for Select.queryOptions,
27784 // - onFetch: callback function for each item in data (Deprecated)
27785 var oStore
= this.store
;
27786 fetchArgs
= fetchArgs
|| {};
27788 if(oStore
!== store
){
27789 // Our store has changed, so cancel any listeners on old store (remove for 2.0)
27791 while((h
= this._notifyConnections
.pop())){ h
.remove(); }
27793 // For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
27795 lang
.mixin(store
, {
27799 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
27800 // Like dojo.store.DataStore.get() except returns native item.
27801 var deferred
= new Deferred();
27802 this.fetchItemByIdentity({
27804 onItem: function(object
){
27805 deferred
.resolve(object
);
27807 onError: function(error
){
27808 deferred
.reject(error
);
27811 return deferred
.promise
;
27813 query: function(query
, options
){
27815 // Queries the store for objects. Like dojo/store/DataStore.query()
27816 // except returned Deferred contains array of native items.
27817 var deferred
= new Deferred(function(){ if(fetchHandle
.abort
){ fetchHandle
.abort(); } } );
27818 deferred
.total
= new Deferred();
27819 var fetchHandle
= this.fetch(lang
.mixin({
27821 onBegin: function(count
){
27822 deferred
.total
.resolve(count
);
27824 onComplete: function(results
){
27825 deferred
.resolve(results
);
27827 onError: function(error
){
27828 deferred
.reject(error
);
27831 return new QueryResults(deferred
);
27835 if(store
.getFeatures()["dojo.data.api.Notification"]){
27836 this._notifyConnections
= [
27837 aspect
.after(store
, "onNew", lang
.hitch(this, "_onNewItem"), true),
27838 aspect
.after(store
, "onDelete", lang
.hitch(this, "_onDeleteItem"), true),
27839 aspect
.after(store
, "onSet", lang
.hitch(this, "_onSetItem"), true)
27843 this._set("store", store
); // Our store has changed, so update our notifications
27846 // Remove existing options (if there are any)
27847 if(this.options
&& this.options
.length
){
27848 this.removeOption(this.options
);
27851 // Cancel listener for updates to old store
27852 if(this._queryRes
&& this._queryRes
.close
){
27853 this._queryRes
.close();
27856 // If user has specified new query and query options along with this new store, then use them.
27857 if(fetchArgs
.query
){
27858 this._set("query", fetchArgs
.query
);
27859 this._set("queryOptions", fetchArgs
.queryOptions
);
27862 // Add our new options
27864 this._loadingStore
= true;
27865 this.onLoadDeferred
= new Deferred();
27868 // Save result in this._queryRes so we can cancel the listeners we register below
27869 this._queryRes
= store
.query(this.query
, this.queryOptions
);
27870 when(this._queryRes
, lang
.hitch(this, function(items
){
27872 if(this.sortByLabel
&& !fetchArgs
.sort
&& items
.length
){
27873 if(items
[0].getValue
){
27874 // Old dojo.data API to access items, remove for 2.0
27875 items
.sort(sorter
.createSortFunction([{
27876 attribute
: store
.getLabelAttributes(items
[0])[0]
27879 var labelAttr
= this.labelAttr
;
27880 items
.sort(function(a
, b
){
27881 return a
[labelAttr
] > b
[labelAttr
] ? 1 : b
[labelAttr
] > a
[labelAttr
] ? -1 : 0;
27886 if(fetchArgs
.onFetch
){
27887 items
= fetchArgs
.onFetch
.call(this, items
, fetchArgs
);
27890 // TODO: Add these guys as a batch, instead of separately
27891 array
.forEach(items
, function(i
){
27892 this._addOptionForItem(i
);
27895 // Register listener for store updates
27896 if(this._queryRes
.observe
){
27897 this._queryRes
.observe(lang
.hitch(this, function(object
, deletedFrom
, insertedInto
){
27898 if(deletedFrom
== insertedInto
){
27899 this._onSetItem(object
);
27901 if(deletedFrom
!= -1){
27902 this._onDeleteItem(object
);
27904 if(insertedInto
!= -1){
27905 this._onNewItem(object
);
27911 // Set our value (which might be undefined), and then tweak
27912 // it to send a change event with the real value
27913 this._loadingStore
= false;
27914 this.set("value", "_pendingValue" in this ? this._pendingValue
: selectedValue
);
27915 delete this._pendingValue
;
27917 if(!this.loadChildrenOnOpen
){
27918 this._loadChildren();
27920 this._pseudoLoadChildren(items
);
27922 this.onLoadDeferred
.resolve(true);
27925 console
.error('dijit.form.Select: ' + err
.toString());
27926 this.onLoadDeferred
.reject(err
);
27929 return oStore
; // dojo/data/api/Identity
27932 // TODO: implement set() and watch() for store and query, although not sure how to handle
27933 // setting them individually rather than together (as in setStore() above)
27935 _setValueAttr: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
27937 // set the value of the widget.
27938 // If a string is passed, then we set our value from looking it up.
27939 if(!this._onChangeActive
){ priorityChange
= null; }
27940 if(this._loadingStore
){
27941 // Our store is loading - so save our value, and we'll set it when
27943 this._pendingValue
= newValue
;
27946 var opts
= this.getOptions() || [];
27947 if(!lang
.isArray(newValue
)){
27948 newValue
= [newValue
];
27950 array
.forEach(newValue
, function(i
, idx
){
27951 if(!lang
.isObject(i
)){
27954 if(typeof i
=== "string"){
27955 newValue
[idx
] = array
.filter(opts
, function(node
){
27956 return node
.value
=== i
;
27957 })[0] || {value
: "", label
: ""};
27961 // Make sure some sane default is set
27962 newValue
= array
.filter(newValue
, function(i
){ return i
&& i
.value
; });
27963 if(!this.multiple
&& (!newValue
[0] || !newValue
[0].value
) && opts
.length
){
27964 newValue
[0] = opts
[0];
27966 array
.forEach(opts
, function(i
){
27967 i
.selected
= array
.some(newValue
, function(v
){ return v
.value
=== i
.value
; });
27969 var val
= array
.map(newValue
, function(i
){ return i
.value
; }),
27970 disp
= array
.map(newValue
, function(i
){ return i
.label
; });
27972 if(typeof val
== "undefined" || typeof val
[0] == "undefined"){ return; } // not fully initialized yet or a failed value lookup
27973 this._setDisplay(this.multiple
? disp
: disp
[0]);
27974 this.inherited(arguments
, [ this.multiple
? val
: val
[0], priorityChange
]);
27975 this._updateSelection();
27978 _getDisplayedValueAttr: function(){
27980 // returns the displayed value of the widget
27981 var val
= this.get("value");
27982 if(!lang
.isArray(val
)){
27985 var ret
= array
.map(this.getOptions(val
), function(v
){
27986 if(v
&& "label" in v
){
27993 return this.multiple
? ret
: ret
[0];
27996 _loadChildren: function(){
27998 // Loads the children represented by this widget's options.
27999 // reset the menu to make it populatable on the next click
28000 if(this._loadingStore
){ return; }
28001 array
.forEach(this._getChildren(), function(child
){
28002 child
.destroyRecursive();
28004 // Add each menu item
28005 array
.forEach(this.options
, this._addOptionItem
, this);
28008 this._updateSelection();
28011 _updateSelection: function(){
28013 // Sets the "selected" class on the item for styling purposes
28014 this._set("value", this._getValueFromOpts());
28015 var val
= this.value
;
28016 if(!lang
.isArray(val
)){
28020 array
.forEach(this._getChildren(), function(child
){
28021 var isSelected
= array
.some(val
, function(v
){
28022 return child
.option
&& (v
=== child
.option
.value
);
28024 domClass
.toggle(child
.domNode
, this.baseClass
.replace(/\s+|$/g, "SelectedOption "), isSelected
);
28025 child
.domNode
.setAttribute("aria-selected", isSelected
? "true" : "false");
28030 _getValueFromOpts: function(){
28032 // Returns the value of the widget by reading the options for
28033 // the selected flag
28034 var opts
= this.getOptions() || [];
28035 if(!this.multiple
&& opts
.length
){
28036 // Mirror what a select does - choose the first one
28037 var opt
= array
.filter(opts
, function(i
){
28040 if(opt
&& opt
.value
){
28043 opts
[0].selected
= true;
28044 return opts
[0].value
;
28046 }else if(this.multiple
){
28047 // Set value to be the sum of all selected
28048 return array
.map(array
.filter(opts
, function(i
){
28057 // Internal functions to call when we have store notifications come in
28058 _onNewItem: function(/*item*/ item
, /*Object?*/ parentInfo
){
28059 if(!parentInfo
|| !parentInfo
.parent
){
28060 // Only add it if we are top-level
28061 this._addOptionForItem(item
);
28064 _onDeleteItem: function(/*item*/ item
){
28065 var store
= this.store
;
28066 this.removeOption(store
.getIdentity(item
));
28068 _onSetItem: function(/*item*/ item
){
28069 this.updateOption(this._getOptionObjForItem(item
));
28072 _getOptionObjForItem: function(item
){
28074 // Returns an option object based off the given item. The "value"
28075 // of the option item will be the identity of the item, the "label"
28076 // of the option will be the label of the item.
28078 // remove getLabel() call for 2.0 (it's to support the old dojo.data API)
28079 var store
= this.store
,
28080 label
= (this.labelAttr
&& this.labelAttr
in item
) ? item
[this.labelAttr
] : store
.getLabel(item
),
28081 value
= (label
? store
.getIdentity(item
) : null);
28082 return {value
: value
, label
: label
, item
: item
}; // __SelectOption
28085 _addOptionForItem: function(/*item*/ item
){
28087 // Creates (and adds) the option for the given item
28088 var store
= this.store
;
28089 if(store
.isItemLoaded
&& !store
.isItemLoaded(item
)){
28090 // We are not loaded - so let's load it and add later.
28091 // Remove for 2.0 (it's the old dojo.data API)
28092 store
.loadItem({item
: item
, onItem: function(i
){
28093 this._addOptionForItem(i
);
28098 var newOpt
= this._getOptionObjForItem(item
);
28099 this.addOption(newOpt
);
28102 constructor: function(params
/*===== , srcNodeRef =====*/){
28104 // Create the widget.
28105 // params: Object|null
28106 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
28107 // and functions, typically callbacks like onClick.
28108 // The hash can contain any of the widget's properties, excluding read-only properties.
28109 // srcNodeRef: DOMNode|String?
28110 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
28112 // Saves off our value, if we have an initial one set so we
28113 // can use it if we have a store as well (see startup())
28114 this._oValue
= (params
|| {}).value
|| null;
28115 this._notifyConnections
= []; // remove for 2.0
28118 buildRendering: function(){
28119 this.inherited(arguments
);
28120 dom
.setSelectable(this.focusNode
, false);
28123 _fillContent: function(){
28125 // Loads our options and sets up our dropdown correctly. We
28126 // don't want any content, so we don't call any inherit chain
28131 ? query("> *", this.srcNodeRef
).map(
28133 if(node
.getAttribute("type") === "separator"){
28134 return { value
: "", label
: "", selected
: false, disabled
: false };
28137 value
: (node
.getAttribute("data-" + kernel
._scopeName
+ "-value") || node
.getAttribute("value")),
28138 label
: String(node
.innerHTML
),
28139 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
28140 // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
28141 // decide before 1.6
28142 selected
: node
.getAttribute("selected") || false,
28143 disabled
: node
.getAttribute("disabled") || false
28150 this._set("value", this._getValueFromOpts());
28151 }else if(this.multiple
&& typeof this.value
== "string"){
28152 this._set("value", this.value
.split(","));
28156 postCreate: function(){
28158 // sets up our event handling that we need for functioning
28160 this.inherited(arguments
);
28162 // Make our event connections for updating state
28163 this.connect(this, "onChange", "_updateSelection");
28165 // moved from startup
28166 // Connects in our store, if we have one defined
28167 var store
= this.store
;
28168 if(store
&& (store
.getIdentity
|| store
.getFeatures()["dojo.data.api.Identity"])){
28169 // Temporarily set our store to null so that it will get set
28170 // and connected appropriately
28172 this.setStore(store
, this._oValue
);
28176 startup: function(){
28178 this._loadChildren();
28179 this.inherited(arguments
);
28182 destroy: function(){
28184 // Clean up our connections
28187 while((h
= this._notifyConnections
.pop())){ h
.remove(); }
28189 // Cancel listener for store updates
28190 if(this._queryRes
&& this._queryRes
.close
){
28191 this._queryRes
.close();
28194 this.inherited(arguments
);
28197 _addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
28199 // User-overridable function which, for the given option, adds an
28200 // item to the select. If the option doesn't have a value, then a
28201 // separator is added in that place. Make sure to store the option
28202 // in the created option widget.
28205 _removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
28207 // User-overridable function which, for the given option, removes
28208 // its item from the select.
28211 _setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
28213 // Overridable function which will set the display for the
28214 // widget. newDisplay is either a string (in the case of
28215 // single selects) or array of strings (in the case of multi-selects)
28218 _getChildren: function(){
28220 // Overridable function to return the children that this widget contains.
28224 _getSelectedOptionsAttr: function(){
28226 // hooks into this.attr to provide a mechanism for getting the
28227 // option items for the current value of the widget.
28228 return this.getOptions(this.get("value"));
28231 _pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
28233 // a function that will "fake" loading children, if needed, and
28234 // if we have set to not load children until the widget opens.
28236 // An array of items that will be loaded, when needed
28239 onSetStore: function(){
28241 // a function that can be connected to in order to receive a
28242 // notification that the store has finished loading and all options
28243 // from that store are available
28248 _FormSelectWidget.__SelectOption = __SelectOption;
28251 return _FormSelectWidget
;
28256 'dijit/form/Select':function(){
28258 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" 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=\"▼ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n"}});
28259 define("dijit/form/Select", [
28260 "dojo/_base/array", // array.forEach
28261 "dojo/_base/declare", // declare
28262 "dojo/dom-attr", // domAttr.set
28263 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
28264 "dojo/dom-geometry", // domGeometry.setMarginBox
28265 "dojo/_base/event", // event.stop
28266 "dojo/i18n", // i18n.getLocalization
28267 "dojo/_base/lang", // lang.hitch
28268 "dojo/sniff", // has("ie")
28269 "./_FormSelectWidget",
28273 "../MenuSeparator",
28275 "dojo/text!./templates/Select.html",
28276 "dojo/i18n!./nls/validate"
28277 ], function(array
, declare
, domAttr
, domClass
, domGeometry
, event
, i18n
, lang
, has
,
28278 _FormSelectWidget
, _HasDropDown
, Menu
, MenuItem
, MenuSeparator
, Tooltip
, template
){
28281 // dijit/form/Select
28284 var _SelectMenu
= declare("dijit.form._SelectMenu", Menu
, {
28286 // An internally-used menu for dropdown that allows us a vertical scrollbar
28288 // Override Menu.autoFocus setting so that opening a Select highlights the current value.
28291 buildRendering: function(){
28293 // Stub in our own changes, so that our domNode is not a table
28294 // otherwise, we won't respond correctly to heights/overflows
28295 this.inherited(arguments
);
28296 var o
= (this.menuTableNode
= this.domNode
);
28297 var n
= (this.domNode
= this.ownerDocument
.createElement("div"));
28298 n
.style
.cssText
= "overflow-x: hidden; overflow-y: scroll";
28300 o
.parentNode
.replaceChild(n
, o
);
28302 domClass
.remove(o
, "dijitMenuTable");
28303 n
.className
= o
.className
+ " dijitSelectMenu";
28304 o
.className
= "dijitReset dijitMenuTable";
28305 o
.setAttribute("role", "listbox");
28306 n
.setAttribute("role", "presentation");
28310 postCreate: function(){
28312 // stop mousemove from selecting text on IE to be consistent with other browsers
28314 this.inherited(arguments
);
28316 this.connect(this.domNode
, "onselectstart", event
.stop
);
28322 // Overridden so that the previously selected value will be focused instead of only the first item
28324 val
= this.parentWidget
.value
;
28325 if(lang
.isArray(val
)){
28326 val
= val
[val
.length
-1];
28328 if(val
){ // if focus selected
28329 array
.forEach(this.parentWidget
._getChildren(), function(child
){
28330 if(child
.option
&& (val
=== child
.option
.value
)){ // find menu item widget with this value
28332 this.focusChild(child
, false); // focus previous selection
28337 this.inherited(arguments
); // focus first item by default
28341 resize: function(/*Object*/ mb
){
28343 // Overridden so that we are able to handle resizing our
28344 // internal widget. Note that this is not a "full" resize
28345 // implementation - it only works correctly if you pass it a
28349 // The margin box to set this dropdown to.
28351 domGeometry
.setMarginBox(this.domNode
, mb
);
28353 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
28354 // 100% is safer than a pixel value because there may be a scroll bar with
28355 // browser/OS specific width.
28356 this.menuTableNode
.style
.width
= "100%";
28362 var Select
= declare("dijit.form.Select", [_FormSelectWidget
, _HasDropDown
], {
28364 // This is a "styleable" select box - it is basically a DropDownButton which
28365 // can take a `<select>` as its input.
28367 baseClass
: "dijitSelect dijitValidationTextBox",
28369 templateString
: template
,
28371 _buttonInputDisabled
: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
28373 // required: Boolean
28374 // Can be true or false, default is false.
28377 // state: [readonly] String
28378 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
28382 // Currently displayed error/prompt message
28385 // tooltipPosition: String[]
28386 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
28387 tooltipPosition
: [],
28389 // emptyLabel: string
28390 // What to display in an "empty" dropdown
28391 emptyLabel
: " ", //
28393 // _isLoaded: Boolean
28394 // Whether or not we have been loaded
28397 // _childrenLoaded: Boolean
28398 // Whether or not our children have been loaded
28399 _childrenLoaded
: false,
28401 _fillContent: function(){
28403 // Set the value to be the first, or the selected index
28404 this.inherited(arguments
);
28405 // set value from selected option
28406 if(this.options
.length
&& !this.value
&& this.srcNodeRef
){
28407 var si
= this.srcNodeRef
.selectedIndex
|| 0; // || 0 needed for when srcNodeRef is not a SELECT
28408 this.value
= this.options
[si
>= 0 ? si
: 0].value
;
28410 // Create the dropDown widget
28411 this.dropDown
= new _SelectMenu({ id
: this.id
+ "_menu", parentWidget
: this });
28412 domClass
.add(this.dropDown
.domNode
, this.baseClass
.replace(/\s+|$/g, "Menu "));
28415 _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option
){
28417 // For the given option, return the menu item that should be
28418 // used to display it. This can be overridden as needed
28419 if(!option
.value
&& !option
.label
){
28420 // We are a separator (no label set for it)
28421 return new MenuSeparator({ownerDocument
: this.ownerDocument
});
28423 // Just a regular menu option
28424 var click
= lang
.hitch(this, "_setValueAttr", option
);
28425 var item
= new MenuItem({
28427 label
: option
.label
|| this.emptyLabel
,
28429 ownerDocument
: this.ownerDocument
,
28431 disabled
: option
.disabled
|| false
28433 item
.focusNode
.setAttribute("role", "option");
28438 _addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option
){
28440 // For the given option, add an option to our dropdown.
28441 // If the option doesn't have a value, then a separator is added
28444 this.dropDown
.addChild(this._getMenuItemForOption(option
));
28448 _getChildren: function(){
28449 if(!this.dropDown
){
28452 return this.dropDown
.getChildren();
28455 _loadChildren: function(/*Boolean*/ loadMenuItems
){
28457 // Resets the menu and the length attribute of the button - and
28458 // ensures that the label is appropriately set.
28459 // loadMenuItems: Boolean
28460 // actually loads the child menu items - we only do this when we are
28461 // populating for showing the dropdown.
28463 if(loadMenuItems
=== true){
28464 // this.inherited destroys this.dropDown's child widgets (MenuItems).
28465 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
28466 // issues later in _setSelected). (see #10296)
28468 delete this.dropDown
.focusedChild
;
28470 if(this.options
.length
){
28471 this.inherited(arguments
);
28473 // Drop down menu is blank but add one blank entry just so something appears on the screen
28474 // to let users know that they are no choices (mimicing native select behavior)
28475 array
.forEach(this._getChildren(), function(child
){ child
.destroyRecursive(); });
28476 var item
= new MenuItem({
28477 ownerDocument
: this.ownerDocument
,
28478 label
: this.emptyLabel
28480 this.dropDown
.addChild(item
);
28483 this._updateSelection();
28486 this._isLoaded
= false;
28487 this._childrenLoaded
= true;
28489 if(!this._loadingStore
){
28490 // Don't call this if we are loading - since we will handle it later
28491 this._setValueAttr(this.value
, false);
28495 _refreshState: function(){
28497 this.validate(this.focused
);
28501 startup: function(){
28502 this.inherited(arguments
);
28503 this._refreshState(); // after all _set* methods have run
28506 _setValueAttr: function(value
){
28507 this.inherited(arguments
);
28508 domAttr
.set(this.valueNode
, "value", this.get("value"));
28509 this._refreshState(); // to update this.state
28512 _setDisabledAttr: function(/*Boolean*/ value
){
28513 this.inherited(arguments
);
28514 this._refreshState(); // to update this.state
28517 _setRequiredAttr: function(/*Boolean*/ value
){
28518 this._set("required", value
);
28519 this.focusNode
.setAttribute("aria-required", value
);
28520 this._refreshState(); // to update this.state
28523 _setOptionsAttr: function(/*Array*/ options
){
28524 this._isLoaded
= false;
28525 this._set('options', options
);
28528 _setDisplay: function(/*String*/ newDisplay
){
28530 // sets the display for the given value (or values)
28531 var lbl
= newDisplay
|| this.emptyLabel
;
28532 this.containerNode
.innerHTML
= '<span role="option" class="dijitReset dijitInline ' + this.baseClass
.replace(/\s+|$/g, "Label ")+'">' + lbl
+ '</span>';
28535 validate: function(/*Boolean*/ isFocused
){
28537 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
28539 // Show missing or invalid messages if appropriate, and highlight textbox field.
28540 // Used when a select is initially set to no value and the user is required to
28543 var isValid
= this.disabled
|| this.isValid(isFocused
);
28544 this._set("state", isValid
? "" : (this._hasBeenBlurred
? "Error" : "Incomplete"));
28545 this.focusNode
.setAttribute("aria-invalid", isValid
? "false" : "true");
28546 var message
= isValid
? "" : this._missingMsg
;
28547 if(message
&& this.focused
&& this._hasBeenBlurred
){
28548 Tooltip
.show(message
, this.domNode
, this.tooltipPosition
, !this.isLeftToRight());
28550 Tooltip
.hide(this.domNode
);
28552 this._set("message", message
);
28556 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
28558 // Whether or not this is a valid value. The only way a Select
28559 // can be invalid is when it's required but nothing is selected.
28560 return (!this.required
|| this.value
=== 0 || !(/^\s*$/.test(this.value
|| ""))); // handle value is null or undefined
28565 // Overridden so that the state will be cleared.
28566 this.inherited(arguments
);
28567 Tooltip
.hide(this.domNode
);
28568 this._refreshState(); // to update this.state
28571 postMixInProperties: function(){
28573 // set the missing message
28574 this.inherited(arguments
);
28575 this._missingMsg
= i18n
.getLocalization("dijit.form", "validate", this.lang
).missingMessage
;
28578 postCreate: function(){
28580 // stop mousemove from selecting text on IE to be consistent with other browsers
28582 this.inherited(arguments
);
28584 this.connect(this.domNode
, "onselectstart", event
.stop
);
28585 this.domNode
.setAttribute("aria-expanded", "false");
28588 // IE INPUT tag fontFamily has to be set directly using STYLE
28589 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
28590 this.defer(function(){
28592 var s
= domStyle
.getComputedStyle(this.domNode
); // can throw an exception if widget is immediately destroyed
28594 var ff
= s
.fontFamily
;
28596 var inputs
= this.domNode
.getElementsByTagName("INPUT");
28598 for(var i
=0; i
< inputs
.length
; i
++){
28599 inputs
[i
].style
.fontFamily
= ff
;
28604 }catch(e
){/*when used in a Dialog, and this is called before the dialog is
28605 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
28610 _setStyleAttr: function(/*String||Object*/ value
){
28611 this.inherited(arguments
);
28612 domClass
.toggle(this.domNode
, this.baseClass
.replace(/\s+|$/g, "FixedWidth "), !!this.domNode
.style
.width
);
28615 isLoaded: function(){
28616 return this._isLoaded
;
28619 loadDropDown: function(/*Function*/ loadCallback
){
28621 // populates the menu
28622 this._loadChildren(true);
28623 this._isLoaded
= true;
28627 closeDropDown: function(){
28628 // overriding _HasDropDown.closeDropDown()
28629 this.inherited(arguments
);
28631 if(this.dropDown
&& this.dropDown
.menuTableNode
){
28632 // Erase possible width: 100% setting from _SelectMenu.resize().
28633 // Leaving it would interfere with the next openDropDown() call, which
28634 // queries the natural size of the drop down.
28635 this.dropDown
.menuTableNode
.style
.width
= "";
28639 destroy: function(preserveDom
){
28640 if(this.dropDown
&& !this.dropDown
._destroyed
){
28641 this.dropDown
.destroyRecursive(preserveDom
);
28642 delete this.dropDown
;
28644 this.inherited(arguments
);
28647 _onFocus: function(){
28648 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
28649 this.inherited(arguments
);
28652 _onBlur: function(){
28653 Tooltip
.hide(this.domNode
);
28654 this.inherited(arguments
);
28655 this.validate(false);
28659 Select
._Menu
= _SelectMenu
; // for monkey patching
28665 'dojo/store/util/QueryResults':function(){
28666 define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
28667 ], function(array
, lang
, Deferred
){
28670 // dojo/store/util/QueryResults
28672 var QueryResults = function(results
){
28674 // A function that wraps the results of a store query with additional
28677 // QueryResults is a basic wrapper that allows for array-like iteration
28678 // over any kind of returned data from a query. While the simplest store
28679 // will return a plain array of data, other stores may return deferreds or
28680 // promises; this wrapper makes sure that *all* results can be treated
28683 // Additional methods include `forEach`, `filter` and `map`.
28684 // results: Array|dojo/promise/Promise
28685 // The result set as an array, or a promise for an array.
28687 // An array-like object that can be used for iterating over.
28689 // Query a store and iterate over the results.
28691 // | store.query({ prime: true }).forEach(function(item){
28692 // | // do something
28698 // if it is a promise it may be frozen
28700 results
= lang
.delegate(results
);
28702 function addIterativeMethod(method
){
28703 if(!results
[method
]){
28704 results
[method
] = function(){
28705 var args
= arguments
;
28706 return Deferred
.when(results
, function(results
){
28707 Array
.prototype.unshift
.call(args
, results
);
28708 return QueryResults(array
[method
].apply(array
, args
));
28713 addIterativeMethod("forEach");
28714 addIterativeMethod("filter");
28715 addIterativeMethod("map");
28716 if(!results
.total
){
28717 results
.total
= Deferred
.when(results
, function(results
){
28718 return results
.length
;
28721 return results
; // Object
28724 lang
.setObject("dojo.store.util.QueryResults", QueryResults
);
28726 return QueryResults
;
28731 'dijit/form/_ListBase':function(){
28732 define("dijit/form/_ListBase", [
28733 "dojo/_base/declare", // declare
28735 "dojo/window" // winUtils.scrollIntoView
28736 ], function(declare
, on
, winUtils
){
28739 // dijit/form/_ListBase
28741 return declare( "dijit.form._ListBase", null, {
28743 // Focus-less menu to handle UI events consistently
28744 // Abstract methods that must be defined externally:
28746 // - onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
28747 // - onDeselect: cancels onSelect
28751 // selected: DOMNode
28752 // currently selected node
28755 _listConnect: function(/*String|Function*/ eventType
, /*String*/ callbackFuncName
){
28757 // Connects 'containerNode' to specified method of this object
28758 // and automatically registers for 'disconnect' on widget destroy.
28760 // Provide widget-specific analog to 'connect'.
28761 // The callback function is called with the normal event object,
28762 // but also a second parameter is passed that indicates which list item
28763 // actually received the event.
28765 // A handle that can be passed to `disconnect` in order to disconnect
28766 // before the widget is destroyed.
28771 return self
.own(on(self
.containerNode
,
28773 function(eventTarget
, selector
, target
){
28774 return eventTarget
.parentNode
== target
;
28779 evt
.preventDefault();
28780 self
[callbackFuncName
](evt
, this);
28785 selectFirstNode: function(){
28787 // Select the first displayed item in the list.
28788 var first
= this.containerNode
.firstChild
;
28789 while(first
&& first
.style
.display
== "none"){
28790 first
= first
.nextSibling
;
28792 this._setSelectedAttr(first
);
28795 selectLastNode: function(){
28797 // Select the last displayed item in the list
28798 var last
= this.containerNode
.lastChild
;
28799 while(last
&& last
.style
.display
== "none"){
28800 last
= last
.previousSibling
;
28802 this._setSelectedAttr(last
);
28805 selectNextNode: function(){
28807 // Select the item just below the current selection.
28808 // If nothing selected, select first node.
28809 var selectedNode
= this.selected
;
28811 this.selectFirstNode();
28813 var next
= selectedNode
.nextSibling
;
28814 while(next
&& next
.style
.display
== "none"){
28815 next
= next
.nextSibling
;
28818 this.selectFirstNode();
28820 this._setSelectedAttr(next
);
28825 selectPreviousNode: function(){
28827 // Select the item just above the current selection.
28828 // If nothing selected, select last node (if
28829 // you select Previous and try to keep scrolling up the list).
28830 var selectedNode
= this.selected
;
28832 this.selectLastNode();
28834 var prev
= selectedNode
.previousSibling
;
28835 while(prev
&& prev
.style
.display
== "none"){
28836 prev
= prev
.previousSibling
;
28839 this.selectLastNode();
28841 this._setSelectedAttr(prev
);
28846 _setSelectedAttr: function(/*DomNode*/ node
){
28848 // Does the actual select.
28849 if(this.selected
!= node
){
28850 var selectedNode
= this.selected
;
28852 this.onDeselect(selectedNode
);
28853 this.selected
= null;
28856 this.selected
= node
;
28857 winUtils
.scrollIntoView(node
);
28858 this.onSelect(node
);
28861 this.onSelect(node
);
28869 'dijit/form/_FormWidget':function(){
28870 define("dijit/form/_FormWidget", [
28871 "dojo/_base/declare", // declare
28872 "dojo/has", // has("dijit-legacy-requires")
28873 "dojo/_base/kernel", // kernel.deprecated
28876 "../_CssStateMixin",
28877 "../_TemplatedMixin",
28878 "./_FormWidgetMixin"
28879 ], function(declare
, has
, kernel
, ready
, _Widget
, _CssStateMixin
, _TemplatedMixin
, _FormWidgetMixin
){
28883 // dijit/form/_FormWidget
28885 // Back compat w/1.6, remove for 2.0
28886 if(has("dijit-legacy-requires")){
28887 ready(0, function(){
28888 var requires
= ["dijit/form/_FormValueWidget"];
28889 require(requires
); // use indirection so modules not rolled into a build
28893 return declare("dijit.form._FormWidget", [_Widget
, _TemplatedMixin
, _CssStateMixin
, _FormWidgetMixin
], {
28895 // Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
28896 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
28899 // Represents a single HTML element.
28900 // All these widgets should have these attributes just like native HTML input elements.
28901 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
28903 // They also share some common methods.
28905 setDisabled: function(/*Boolean*/ disabled
){
28907 // Deprecated. Use set('disabled', ...) instead.
28908 kernel
.deprecated("setDisabled("+disabled
+") is deprecated. Use set('disabled',"+disabled
+") instead.", "", "2.0");
28909 this.set('disabled', disabled
);
28912 setValue: function(/*String*/ value
){
28914 // Deprecated. Use set('value', ...) instead.
28915 kernel
.deprecated("dijit.form._FormWidget:setValue("+value
+") is deprecated. Use set('value',"+value
+") instead.", "", "2.0");
28916 this.set('value', value
);
28919 getValue: function(){
28921 // Deprecated. Use get('value') instead.
28922 kernel
.deprecated(this.declaredClass
+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
28923 return this.get('value');
28926 postMixInProperties: function(){
28927 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
28928 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
28929 // Regarding escaping, see heading "Attribute values" in
28930 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
28931 this.nameAttrSetting
= this.name
? ('name="' + this.name
.replace(/"/g, ""
;") + '"') : '';
28932 this.inherited(arguments);
28935 // Override automatic assigning type --> focusNode, it causes exception on IE.
28936 // Instead, type must be specified as ${type} in the template, as part of the original DOM
28943 'dojo
/DeferredList
':function(){
28944 define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
28946 // dojo/DeferredList
28949 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
28951 // Deprecated, use dojo/promise/all instead.
28952 // Provides event handling for a group of Deferred objects.
28954 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
28955 // this new deferred will typically have its callback fired when all of the deferreds in
28956 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
28957 // fireOnOneErrback, will fire before all the deferreds as appropriate
28959 // The list of deferreds to be synchronizied with this DeferredList
28960 // fireOnOneCallback:
28961 // Will cause the DeferredLists callback to be fired as soon as any
28962 // of the deferreds in its list have been fired instead of waiting until
28963 // the entire list has finished
28964 // fireonOneErrback:
28965 // Will cause the errback to fire upon any of the deferreds errback
28967 // A deferred canceller function, see dojo.Deferred
28968 var resultList = [];
28969 Deferred.call(this);
28971 if(list.length === 0 && !fireOnOneCallback){
28972 this.resolve([0, []]);
28975 darray.forEach(list, function(item, i){
28976 item.then(function(result){
28977 if(fireOnOneCallback){
28978 self.resolve([i, result]);
28980 addResult(true, result);
28983 if(fireOnOneErrback){
28984 self.reject(error);
28986 addResult(false, error);
28993 function addResult(succeeded, result){
28994 resultList[i] = [succeeded, result];
28996 if(finished === list.length){
28997 self.resolve(resultList);
29003 dojo.DeferredList.prototype = new Deferred();
29005 dojo.DeferredList.prototype.gatherResults = function(deferredList){
29007 // Gathers the results of the deferreds for packaging
29008 // as the parameters to the Deferred Lists' callback
29009 // deferredList: dojo/DeferredList
29010 // The deferred list from which this function gathers results.
29011 // returns: dojo/DeferredList
29012 // The newly created deferred list which packs results as
29013 // parameters to its callback.
29015 var d
= new dojo
.DeferredList(deferredList
, false, true, false);
29016 d
.addCallback(function(results
){
29018 darray
.forEach(results
, function(result
){
29019 ret
.push(result
[1]);
29026 return dojo
.DeferredList
;
29030 'dojo/dnd/common':function(){
29031 define("dojo/dnd/common", ["../_base/connect", "../_base/kernel", "../_base/lang", "../dom"],
29032 function(connect
, kernel
, lang
, dom
){
29037 var exports
= lang
.getObject("dojo.dnd", true);
29039 // TODO: for 2.0, replace line above with this code.
29046 exports
.getCopyKeyState
= connect
.isCopyKey
;
29048 exports
._uniqueId
= 0;
29049 exports
.getUniqueId = function(){
29051 // returns a unique string for use with any DOM element
29054 id
= kernel
._scopeName
+ "Unique" + (++exports
._uniqueId
);
29055 }while(dom
.byId(id
));
29059 exports
._empty
= {};
29061 exports
.isFormElement = function(/*Event*/ e
){
29063 // returns true if user clicked on a form element
29065 if(t
.nodeType
== 3 /*TEXT_NODE*/){
29068 return " button textarea input select option ".indexOf(" " + t
.tagName
.toLowerCase() + " ") >= 0; // Boolean
29075 'dijit/CheckedMenuItem':function(){
29077 'url:dijit/templates/CheckedMenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitemcheckbox\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuItemIcon dijitCheckedMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t<span class=\"dijitCheckedMenuItemIconChar\">✓</span>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode,labelNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\"> </td>\n</tr>\n"}});
29078 define("dijit/CheckedMenuItem", [
29079 "dojo/_base/declare", // declare
29080 "dojo/dom-class", // domClass.toggle
29082 "dojo/text!./templates/CheckedMenuItem.html",
29084 ], function(declare
, domClass
, MenuItem
, template
){
29087 // dijit/CheckedMenuItem
29089 return declare("dijit.CheckedMenuItem", MenuItem
, {
29091 // A checkbox-like menu item for toggling on and off
29093 templateString
: template
,
29095 // checked: Boolean
29096 // Our checked state
29098 _setCheckedAttr: function(/*Boolean*/ checked
){
29100 // Hook so attr('checked', bool) works.
29101 // Sets the class and state for the check box.
29102 domClass
.toggle(this.domNode
, "dijitCheckedMenuItemChecked", checked
);
29103 this.domNode
.setAttribute("aria-checked", checked
? "true" : "false");
29104 this._set("checked", checked
);
29107 iconClass
: "", // override dijitNoIcon
29109 onChange: function(/*Boolean*/ /*===== checked =====*/){
29111 // User defined function to handle check/uncheck events
29116 _onClick: function(evt
){
29118 // Clicking this item just toggles its state
29121 if(!this.disabled
){
29122 this.set("checked", !this.checked
);
29123 this.onChange(this.checked
);
29131 'dijit/Viewport':function(){
29132 define("dijit/Viewport", [
29137 "dojo/_base/window", // global
29138 "dojo/window" // getBox()
29139 ], function(Evented
, on
, ready
, has
, win
, winUtils
){
29147 // Utility singleton to watch for viewport resizes, avoiding duplicate notifications
29148 // which can lead to infinite loops.
29150 // Usage: Viewport.on("resize", myCallback).
29152 // myCallback() is called without arguments in case it's _WidgetBase.resize(),
29153 // which would interpret the argument as the size to make the widget.
29157 var Viewport
= new Evented();
29159 ready(200, function(){
29160 var oldBox
= winUtils
.getBox();
29161 Viewport
._rlh
= on(win
.global
, "resize", function(){
29162 var newBox
= winUtils
.getBox();
29163 if(oldBox
.h
== newBox
.h
&& oldBox
.w
== newBox
.w
){ return; }
29165 Viewport
.emit("resize");
29168 // Also catch zoom changes on IE8, since they don't naturally generate resize events
29169 if(has("ie") == 8){
29170 var deviceXDPI
= screen
.deviceXDPI
;
29171 setInterval(function(){
29172 if(screen
.deviceXDPI
!= deviceXDPI
){
29173 deviceXDPI
= screen
.deviceXDPI
;
29174 Viewport
.emit("resize");
29184 'dijit/_base/place':function(){
29185 define("dijit/_base/place", [
29186 "dojo/_base/array", // array.forEach
29187 "dojo/_base/lang", // lang.isArray, lang.mixin
29188 "dojo/window", // windowUtils.getBox
29190 "../main" // export to dijit namespace
29191 ], function(array
, lang
, windowUtils
, place
, dijit
){
29194 // dijit/_base/place
29199 // Deprecated back compatibility module, new code should use dijit/place directly instead of using this module.
29202 exports
.getViewport = function(){
29204 // Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
29205 // New code should use windowUtils.getBox()
29207 return windowUtils
.getBox();
29210 exports
.placeOnScreen
= place
.at
;
29212 exports
.placeOnScreenAroundElement = function(node
, aroundNode
, aroundCorners
, layoutNode
){
29214 // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
29215 // for the "around" argument and finds a proper processor to place a node.
29216 // Deprecated, new code should use dijit/place.around() instead.
29218 // Convert old style {"BL": "TL", "BR": "TR"} type argument
29219 // to style needed by dijit.place code:
29221 // {aroundCorner: "BL", corner: "TL" },
29222 // {aroundCorner: "BR", corner: "TR" }
29225 if(lang
.isArray(aroundCorners
)){
29226 positions
= aroundCorners
;
29229 for(var key
in aroundCorners
){
29230 positions
.push({aroundCorner
: key
, corner
: aroundCorners
[key
]});
29234 return place
.around(node
, aroundNode
, positions
, true, layoutNode
);
29237 exports
.placeOnScreenAroundNode
= exports
.placeOnScreenAroundElement
;
29239 exports.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
29241 // Position node adjacent or kitty-corner to aroundNode
29242 // such that it's fully visible in viewport.
29243 // Deprecated, new code should use dijit/place.around() instead.
29247 exports
.placeOnScreenAroundRectangle
= exports
.placeOnScreenAroundElement
;
29249 exports.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, layoutNode){
29251 // Like dijit.placeOnScreenAroundNode(), except that the "around"
29252 // parameter is an arbitrary rectangle on the screen (x, y, width, height)
29253 // instead of a dom node.
29254 // Deprecated, new code should use dijit/place.around() instead.
29258 exports
.getPopupAroundAlignment = function(/*Array*/ position
, /*Boolean*/ leftToRight
){
29260 // Deprecated method, unneeded when using dijit/place directly.
29261 // Transforms the passed array of preferred positions into a format suitable for
29262 // passing as the aroundCorners argument to dijit/place.placeOnScreenAroundElement.
29263 // position: String[]
29264 // This variable controls the position of the drop down.
29265 // It's an array of strings with the following values:
29267 // - before: places drop down to the left of the target node/widget, or to the right in
29268 // the case of RTL scripts like Hebrew and Arabic
29269 // - after: places drop down to the right of the target node/widget, or to the left in
29270 // the case of RTL scripts like Hebrew and Arabic
29271 // - above: drop down goes above target node
29272 // - below: drop down goes below target node
29274 // The list is positions is tried, in order, until a position is found where the drop down fits
29275 // within the viewport.
29276 // leftToRight: Boolean
29277 // Whether the popup will be displaying in leftToRight mode.
29280 array
.forEach(position
, function(pos
){
29281 var ltr
= leftToRight
;
29284 align
[leftToRight
? "BR" : "BL"] = leftToRight
? "BL" : "BR";
29287 align
[leftToRight
? "BL" : "BR"] = leftToRight
? "BR" : "BL";
29293 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
29294 align
[ltr
? "BL" : "BR"] = ltr
? "TL" : "TR";
29295 align
[ltr
? "BR" : "BL"] = ltr
? "TR" : "TL";
29302 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
29303 align
[ltr
? "TL" : "TR"] = ltr
? "BL" : "BR";
29304 align
[ltr
? "TR" : "TL"] = ltr
? "BR" : "BL";
29311 lang
.mixin(dijit
, exports
);
29313 /*===== return exports; =====*/
29314 return dijit
; // for back compat :-(
29318 'dijit/MenuSeparator':function(){
29320 'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>"}});
29321 define("dijit/MenuSeparator", [
29322 "dojo/_base/declare", // declare
29323 "dojo/dom", // dom.setSelectable
29325 "./_TemplatedMixin",
29327 "dojo/text!./templates/MenuSeparator.html"
29328 ], function(declare
, dom
, _WidgetBase
, _TemplatedMixin
, _Contained
, template
){
29331 // dijit/MenuSeparator
29333 return declare("dijit.MenuSeparator", [_WidgetBase
, _TemplatedMixin
, _Contained
], {
29335 // A line between two menu items
29337 templateString
: template
,
29339 buildRendering: function(){
29340 this.inherited(arguments
);
29341 dom
.setSelectable(this.domNode
, false);
29344 isFocusable: function(){
29346 // Override to always return false
29350 return false; // Boolean
29356 'dijit/form/_ComboBoxMenu':function(){
29357 define("dijit/form/_ComboBoxMenu", [
29358 "dojo/_base/declare", // declare
29359 "dojo/dom-class", // domClass.add domClass.remove
29360 "dojo/dom-style", // domStyle.get
29361 "dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
29363 "../_TemplatedMixin",
29364 "./_ComboBoxMenuMixin",
29365 "./_ListMouseMixin"
29366 ], function(declare
, domClass
, domStyle
, keys
,
29367 _WidgetBase
, _TemplatedMixin
, _ComboBoxMenuMixin
, _ListMouseMixin
){
29371 // dijit/form/_ComboBoxMenu
29373 return declare("dijit.form._ComboBoxMenu",[_WidgetBase
, _TemplatedMixin
, _ListMouseMixin
, _ComboBoxMenuMixin
], {
29375 // Focus-less menu for internal use in `dijit/form/ComboBox`
29376 // Abstract methods that must be defined externally:
29378 // - onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
29379 // - onPage: next(1) or previous(-1) button pressed
29383 templateString
: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;' role='listbox'>"
29384 +"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
29385 +"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
29388 baseClass
: "dijitComboBoxMenu",
29390 postCreate: function(){
29391 this.inherited(arguments
);
29392 if(!this.isLeftToRight()){
29393 domClass
.add(this.previousButton
, "dijitMenuItemRtl");
29394 domClass
.add(this.nextButton
, "dijitMenuItemRtl");
29398 _createMenuItem: function(){
29399 // note: not using domConstruct.create() because need to specify document
29400 var item
= this.ownerDocument
.createElement("div");
29401 item
.className
= "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl");
29402 item
.setAttribute("role", "option");
29406 onHover: function(/*DomNode*/ node
){
29409 domClass
.add(node
, "dijitMenuItemHover");
29412 onUnhover: function(/*DomNode*/ node
){
29414 // Remove hover CSS
29415 domClass
.remove(node
, "dijitMenuItemHover");
29418 onSelect: function(/*DomNode*/ node
){
29420 // Add selected CSS
29421 domClass
.add(node
, "dijitMenuItemSelected");
29424 onDeselect: function(/*DomNode*/ node
){
29426 // Remove selected CSS
29427 domClass
.remove(node
, "dijitMenuItemSelected");
29430 _page: function(/*Boolean*/ up
){
29432 // Handles page-up and page-down keypresses
29434 var scrollamount
= 0;
29435 var oldscroll
= this.domNode
.scrollTop
;
29436 var height
= domStyle
.get(this.domNode
, "height");
29437 // if no item is highlighted, highlight the first option
29438 if(!this.getHighlightedOption()){
29439 this.selectNextNode();
29441 while(scrollamount
<height
){
29442 var highlighted_option
= this.getHighlightedOption();
29444 // stop at option 1
29445 if(!highlighted_option
.previousSibling
||
29446 highlighted_option
.previousSibling
.style
.display
== "none"){
29449 this.selectPreviousNode();
29451 // stop at last option
29452 if(!highlighted_option
.nextSibling
||
29453 highlighted_option
.nextSibling
.style
.display
== "none"){
29456 this.selectNextNode();
29459 var newscroll
= this.domNode
.scrollTop
;
29460 scrollamount
+= (newscroll
-oldscroll
)*(up
? -1:1);
29461 oldscroll
= newscroll
;
29465 handleKey: function(evt
){
29467 // Handle keystroke event forwarded from ComboBox, returning false if it's
29468 // a keystroke I recognize and process, true otherwise.
29469 switch(evt
.keyCode
){
29470 case keys
.DOWN_ARROW
:
29471 this.selectNextNode();
29473 case keys
.PAGE_DOWN
:
29476 case keys
.UP_ARROW
:
29477 this.selectPreviousNode();
29490 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</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\">◀</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\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
29491 'dijit/Dialog':function(){
29493 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
29494 define("dijit/Dialog", [
29496 "dojo/_base/array", // array.forEach array.indexOf array.map
29497 "dojo/_base/connect", // connect._keypress
29498 "dojo/_base/declare", // declare
29499 "dojo/_base/Deferred", // Deferred
29500 "dojo/dom", // dom.isDescendant
29501 "dojo/dom-class", // domClass.add domClass.contains
29502 "dojo/dom-geometry", // domGeometry.position
29503 "dojo/dom-style", // domStyle.set
29504 "dojo/_base/event", // event.stop
29505 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
29506 "dojo/i18n", // i18n.getLocalization
29508 "dojo/_base/lang", // lang.mixin lang.hitch
29511 "dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
29512 "dojo/window", // winUtils.getBox, winUtils.get
29513 "dojo/dnd/Moveable", // Moveable
29514 "dojo/dnd/TimedMoveable", // TimedMoveable
29516 "./_base/manager", // manager.defaultDuration
29518 "./_TemplatedMixin",
29519 "./_CssStateMixin",
29520 "./form/_FormMixin",
29522 "./DialogUnderlay",
29523 "./layout/ContentPane",
29524 "dojo/text!./templates/Dialog.html",
29525 "./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
29526 "dojo/i18n!./nls/common"
29527 ], function(require
, array
, connect
, declare
, Deferred
,
29528 dom
, domClass
, domGeometry
, domStyle
, event
, fx
, i18n
, keys
, lang
, on
, ready
, has
, winUtils
,
29529 Moveable
, TimedMoveable
, focus
, manager
, _Widget
, _TemplatedMixin
, _CssStateMixin
, _FormMixin
, _DialogMixin
,
29530 DialogUnderlay
, ContentPane
, template
, dijit
){
29536 dijit._underlay = function(kwArgs){
29538 // A shared instance of a `dijit.DialogUnderlay`
29541 // A shared instance of a `dijit.DialogUnderlay` created and
29542 // used by `dijit.Dialog`, though never created until some Dialog
29543 // or subclass thereof is shown.
29547 var _DialogBase
= declare("dijit._DialogBase", [_TemplatedMixin
, _FormMixin
, _DialogMixin
, _CssStateMixin
], {
29548 templateString
: template
,
29550 baseClass
: "dijitDialog",
29553 closeButtonNode
: "dijitDialogCloseIcon"
29556 // Map widget attributes to DOMNode attributes.
29558 { node
: "titleNode", type
: "innerHTML" },
29559 { node
: "titleBar", type
: "attribute" }
29562 // open: [readonly] Boolean
29563 // True if Dialog is currently displayed on screen.
29566 // duration: Integer
29567 // The time in milliseconds it takes the dialog to fade in and out
29568 duration
: manager
.defaultDuration
,
29570 // refocus: Boolean
29571 // A Toggle to modify the default focus behavior of a Dialog, which
29572 // is to re-focus the element which had focus before being opened.
29573 // False will disable refocusing. Default: true
29576 // autofocus: Boolean
29577 // A Toggle to modify the default focus behavior of a Dialog, which
29578 // is to focus on the first dialog element after opening the dialog.
29579 // False will disable autofocusing. Default: true
29582 // _firstFocusItem: [private readonly] DomNode
29583 // The pointer to the first focusable node in the dialog.
29584 // Set by `dijit/_DialogMixin._getFocusItems()`.
29585 _firstFocusItem
: null,
29587 // _lastFocusItem: [private readonly] DomNode
29588 // The pointer to which node has focus prior to our dialog.
29589 // Set by `dijit/_DialogMixin._getFocusItems()`.
29590 _lastFocusItem
: null,
29592 // doLayout: [protected] Boolean
29593 // Don't change this parameter from the default value.
29594 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
29595 // is never a child of a layout container, nor can you specify the size of
29596 // Dialog in order to control the size of an inner widget.
29599 // draggable: Boolean
29600 // Toggles the moveable aspect of the Dialog. If true, Dialog
29601 // can be dragged by it's title. If false it will remain centered
29602 // in the viewport.
29605 _setDraggableAttr: function(/*Boolean*/ val
){
29606 // Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
29607 // as that prevents text select on modern browsers (#14452)
29608 this._set("draggable", val
);
29611 // aria-describedby: String
29612 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
29613 // be the id of the container element of text that describes the dialog purpose (usually
29614 // the first text in the dialog).
29615 // | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
29616 // | <div id="intro">Introductory text</div>
29617 // | <div>rest of dialog contents</div>
29619 "aria-describedby": "",
29621 // maxRatio: Number
29622 // Maximum size to allow the dialog to expand to, relative to viewport size
29625 postMixInProperties: function(){
29626 var _nlsResources
= i18n
.getLocalization("dijit", "common");
29627 lang
.mixin(this, _nlsResources
);
29628 this.inherited(arguments
);
29631 postCreate: function(){
29632 domStyle
.set(this.domNode
, {
29634 position
:"absolute"
29636 this.ownerDocumentBody
.appendChild(this.domNode
);
29638 this.inherited(arguments
);
29640 this.connect(this, "onExecute", "hide");
29641 this.connect(this, "onCancel", "hide");
29642 this._modalconnects
= [];
29645 onLoad: function(){
29647 // Called when data has been loaded from an href.
29648 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
29649 // but should *not* be overridden.
29653 // when href is specified we need to reposition the dialog after the data is loaded
29654 // and find the focusable elements
29656 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
29657 this._getFocusItems(this.domNode
);
29658 focus
.focus(this._firstFocusItem
);
29660 this.inherited(arguments
);
29663 _onBlur: function(by
){
29664 this.inherited(arguments
);
29666 // If focus was accidentally removed from the dialog, such as if the user clicked a blank
29667 // area of the screen, or clicked the browser's address bar and then tabbed into the page,
29668 // then refocus. Won't do anything if focus was removed because the Dialog was closed, or
29669 // because a new Dialog popped up on top of the old one.
29670 var refocus
= lang
.hitch(this, function(){
29671 if(this.open
&& !this._destroyed
&& DialogLevelManager
.isTop(this)){
29672 this._getFocusItems(this.domNode
);
29673 focus
.focus(this._firstFocusItem
);
29677 // wait for mouse up, and then refocus dialog; otherwise doesn't work
29678 on
.once(this.ownerDocument
, "mouseup", refocus
);
29684 _endDrag: function(){
29686 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
29687 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
29688 var nodePosition
= domGeometry
.position(this.domNode
),
29689 viewport
= winUtils
.getBox(this.ownerDocument
);
29690 nodePosition
.y
= Math
.min(Math
.max(nodePosition
.y
, 0), (viewport
.h
- nodePosition
.h
));
29691 nodePosition
.x
= Math
.min(Math
.max(nodePosition
.x
, 0), (viewport
.w
- nodePosition
.w
));
29692 this._relativePosition
= nodePosition
;
29696 _setup: function(){
29698 // Stuff we need to do before showing the Dialog for the first
29699 // time (but we defer it until right beforehand, for
29700 // performance reasons).
29704 var node
= this.domNode
;
29706 if(this.titleBar
&& this.draggable
){
29707 this._moveable
= new ((has("ie") == 6) ? TimedMoveable
// prevent overload, see #5285
29708 : Moveable
)(node
, { handle
: this.titleBar
});
29709 this.connect(this._moveable
, "onMoveStop", "_endDrag");
29711 domClass
.add(node
,"dijitDialogFixed");
29714 this.underlayAttrs
= {
29716 "class": array
.map(this["class"].split(/\s/), function(s
){ return s
+"_underlay"; }).join(" "),
29717 ownerDocument
: this.ownerDocument
29723 // If necessary, shrink dialog contents so dialog fits in viewport
29727 this._checkIfSingleChild();
29729 // If we resized the dialog contents earlier, reset them back to original size, so
29730 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
29731 // Need to do this before the domGeometry.position(this.domNode) call below.
29732 if(this._singleChild
){
29733 if(typeof this._singleChildOriginalStyle
!= "undefined"){
29734 this._singleChild
.domNode
.style
.cssText
= this._singleChildOriginalStyle
;
29735 delete this._singleChildOriginalStyle
;
29738 domStyle
.set(this.containerNode
, {
29744 var bb
= domGeometry
.position(this.domNode
);
29746 // Get viewport size but then reduce it by a bit; Dialog should always have some space around it
29747 // to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
29748 var viewport
= winUtils
.getBox(this.ownerDocument
);
29749 viewport
.w
*= this.maxRatio
;
29750 viewport
.h
*= this.maxRatio
;
29752 if(bb
.w
>= viewport
.w
|| bb
.h
>= viewport
.h
){
29753 // Reduce size of dialog contents so that dialog fits in viewport
29755 var containerSize
= domGeometry
.position(this.containerNode
),
29756 w
= Math
.min(bb
.w
, viewport
.w
) - (bb
.w
- containerSize
.w
),
29757 h
= Math
.min(bb
.h
, viewport
.h
) - (bb
.h
- containerSize
.h
);
29759 if(this._singleChild
&& this._singleChild
.resize
){
29760 if(typeof this._singleChildOriginalStyle
== "undefined"){
29761 this._singleChildOriginalStyle
= this._singleChild
.domNode
.style
.cssText
;
29763 this._singleChild
.resize({w
: w
, h
: h
});
29765 domStyle
.set(this.containerNode
, {
29769 position
: "relative" // workaround IE bug moving scrollbar or dragging dialog
29773 if(this._singleChild
&& this._singleChild
.resize
){
29774 this._singleChild
.resize();
29779 _position: function(){
29781 // Position modal dialog in the viewport. If no relative offset
29782 // in the viewport has been determined (by dragging, for instance),
29783 // center the node. Otherwise, use the Dialog's stored relative offset,
29784 // and position the node to top: left: values based on the viewport.
29785 if(!domClass
.contains(this.ownerDocumentBody
, "dojoMove")){ // don't do anything if called during auto-scroll
29786 var node
= this.domNode
,
29787 viewport
= winUtils
.getBox(this.ownerDocument
),
29788 p
= this._relativePosition
,
29789 bb
= p
? null : domGeometry
.position(node
),
29790 l
= Math
.floor(viewport
.l
+ (p
? p
.x
: (viewport
.w
- bb
.w
) / 2)),
29791 t
= Math
.floor(viewport
.t
+ (p
? p
.y
: (viewport
.h
- bb
.h
) / 2))
29793 domStyle
.set(node
,{
29800 _onKey: function(/*Event*/ evt
){
29802 // Handles the keyboard events for accessibility reasons
29806 if(evt
.charOrCode
){
29807 var node
= evt
.target
;
29808 if(evt
.charOrCode
=== keys
.TAB
){
29809 this._getFocusItems(this.domNode
);
29811 var singleFocusItem
= (this._firstFocusItem
== this._lastFocusItem
);
29812 // see if we are shift-tabbing from first focusable item on dialog
29813 if(node
== this._firstFocusItem
&& evt
.shiftKey
&& evt
.charOrCode
=== keys
.TAB
){
29814 if(!singleFocusItem
){
29815 focus
.focus(this._lastFocusItem
); // send focus to last item in dialog
29818 }else if(node
== this._lastFocusItem
&& evt
.charOrCode
=== keys
.TAB
&& !evt
.shiftKey
){
29819 if(!singleFocusItem
){
29820 focus
.focus(this._firstFocusItem
); // send focus to first item in dialog
29824 // see if the key is for the dialog
29826 if(node
== this.domNode
|| domClass
.contains(node
, "dijitPopup")){
29827 if(evt
.charOrCode
== keys
.ESCAPE
){
29830 return; // just let it go
29833 node
= node
.parentNode
;
29835 // this key is for the disabled document window
29836 if(evt
.charOrCode
!== keys
.TAB
){ // allow tabbing into the dialog for a11y
29838 // opera won't tab to a div
29839 }else if(!has("opera")){
29841 this._firstFocusItem
.focus();
29842 }catch(e
){ /*squelch*/ }
29850 // Display the dialog
29851 // returns: dojo/_base/Deferred
29852 // Deferred object that resolves when the display animation is complete
29854 if(this.open
){ return; }
29856 if(!this._started
){
29860 // first time we show the dialog, there's some initialization stuff to do
29861 if(!this._alreadyInitialized
){
29863 this._alreadyInitialized
=true;
29866 if(this._fadeOutDeferred
){
29867 this._fadeOutDeferred
.cancel();
29870 // Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
29871 var win
= winUtils
.get(this.ownerDocument
);
29872 this._modalconnects
.push(on(win
, "scroll", lang
.hitch(this, "resize")));
29874 this._modalconnects
.push(on(this.domNode
, connect
._keypress
, lang
.hitch(this, "_onKey")));
29876 domStyle
.set(this.domNode
, {
29881 this._set("open", true);
29882 this._onShow(); // lazy load trigger
29887 // fade-in Animation object, setup below
29890 this._fadeInDeferred
= new Deferred(lang
.hitch(this, function(){
29892 delete this._fadeInDeferred
;
29895 fadeIn
= fx
.fadeIn({
29896 node
: this.domNode
,
29897 duration
: this.duration
,
29898 beforeBegin
: lang
.hitch(this, function(){
29899 DialogLevelManager
.show(this, this.underlayAttrs
);
29901 onEnd
: lang
.hitch(this, function(){
29902 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
29903 // find focusable items each time dialog is shown since if dialog contains a widget the
29904 // first focusable items can change
29905 this._getFocusItems(this.domNode
);
29906 focus
.focus(this._firstFocusItem
);
29908 this._fadeInDeferred
.resolve(true);
29909 delete this._fadeInDeferred
;
29913 return this._fadeInDeferred
;
29919 // returns: dojo/_base/Deferred
29920 // Deferred object that resolves when the hide animation is complete
29922 // If we haven't been initialized yet then we aren't showing and we can just return.
29923 // Likewise if we are already hidden, or are currently fading out.
29924 if(!this._alreadyInitialized
|| !this.open
){
29927 if(this._fadeInDeferred
){
29928 this._fadeInDeferred
.cancel();
29931 // fade-in Animation object, setup below
29934 this._fadeOutDeferred
= new Deferred(lang
.hitch(this, function(){
29936 delete this._fadeOutDeferred
;
29938 // fire onHide when the promise resolves.
29939 this._fadeOutDeferred
.then(lang
.hitch(this, 'onHide'));
29941 fadeOut
= fx
.fadeOut({
29942 node
: this.domNode
,
29943 duration
: this.duration
,
29944 onEnd
: lang
.hitch(this, function(){
29945 this.domNode
.style
.display
= "none";
29946 DialogLevelManager
.hide(this);
29947 this._fadeOutDeferred
.resolve(true);
29948 delete this._fadeOutDeferred
;
29952 if(this._scrollConnected
){
29953 this._scrollConnected
= false;
29956 while(h
= this._modalconnects
.pop()){
29960 if(this._relativePosition
){
29961 delete this._relativePosition
;
29963 this._set("open", false);
29965 return this._fadeOutDeferred
;
29968 resize: function(){
29970 // Called when viewport scrolled or size changed. Position the Dialog and the underlay.
29973 if(this.domNode
.style
.display
!= "none"){
29974 if(DialogUnderlay
._singleton
){ // avoid race condition during show()
29975 DialogUnderlay
._singleton
.layout();
29982 destroy: function(){
29983 if(this._fadeInDeferred
){
29984 this._fadeInDeferred
.cancel();
29986 if(this._fadeOutDeferred
){
29987 this._fadeOutDeferred
.cancel();
29989 if(this._moveable
){
29990 this._moveable
.destroy();
29993 while(h
= this._modalconnects
.pop()){
29997 DialogLevelManager
.hide(this);
29999 this.inherited(arguments
);
30003 var Dialog
= declare("dijit.Dialog", [ContentPane
, _DialogBase
], {
30005 // A modal dialog Widget.
30007 // Pops up a modal dialog window, blocking access to the screen
30008 // and also graying out the screen Dialog is extended from
30009 // ContentPane so it supports all the same parameters (href, etc.).
30011 // | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
30013 // | var foo = new Dialog({ title: "test dialog", content: "test content" };
30014 // | foo.placeAt(win.body());
30015 // | foo.startup();
30017 Dialog
._DialogBase
= _DialogBase
; // for monkey patching and dojox/widget/DialogSimple
30019 var DialogLevelManager
= Dialog
._DialogLevelManager
= {
30021 // Controls the various active "levels" on the page, starting with the
30022 // stuff initially visible on the page (at z-index 0), and then having an entry for
30023 // each Dialog shown.
30027 show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs
){
30029 // Call right before fade-in animation for new dialog.
30030 // Saves current focus, displays/adjusts underlay for new dialog,
30031 // and sets the z-index of the dialog itself.
30033 // New dialog will be displayed on top of all currently displayed dialogs.
30035 // Caller is responsible for setting focus in new dialog after the fade-in
30036 // animation completes.
30038 // Save current focus
30039 ds
[ds
.length
-1].focus
= focus
.curNode
;
30041 // Display the underlay, or if already displayed then adjust for this new dialog
30042 // TODO: one underlay per document (based on dialog.ownerDocument)
30043 var underlay
= DialogUnderlay
._singleton
;
30044 if(!underlay
|| underlay
._destroyed
){
30045 underlay
= dijit
._underlay
= DialogUnderlay
._singleton
= new DialogUnderlay(underlayAttrs
);
30047 underlay
.set(dialog
.underlayAttrs
);
30050 // Set z-index a bit above previous dialog
30051 var zIndex
= ds
[ds
.length
-1].dialog
? ds
[ds
.length
-1].zIndex
+ 2 : Dialog
._DialogLevelManager
._beginZIndex
;
30052 if(ds
.length
== 1){ // first dialog
30055 domStyle
.set(DialogUnderlay
._singleton
.domNode
, 'zIndex', zIndex
- 1);
30058 domStyle
.set(dialog
.domNode
, 'zIndex', zIndex
);
30060 ds
.push({dialog
: dialog
, underlayAttrs
: underlayAttrs
, zIndex
: zIndex
});
30063 hide: function(/*dijit/_WidgetBase*/ dialog){
30065 // Called when the specified dialog is hidden/destroyed, after the fade-out
30066 // animation ends, in order to reset page focus, fix the underlay, etc.
30067 // If the specified dialog isn't open then does nothing.
30069 // Caller is responsible for either setting display:none on the dialog domNode,
30070 // or calling dijit/popup.hide(), or removing it from the page DOM.
30072 if(ds[ds.length-1].dialog == dialog){
30073 // Removing the top (or only) dialog in the stack, return focus
30074 // to previous dialog
30078 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
30080 // Adjust underlay, unless the underlay widget has already been destroyed
30081 // because we are being called during page unload (when all widgets are destroyed)
30082 if(!DialogUnderlay._singleton._destroyed){
30083 if(ds.length == 1){
30084 // Returning to original page. Hide the underlay.
30085 DialogUnderlay._singleton.hide();
30087 // Popping back to previous dialog, adjust underlay.
30088 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
30089 DialogUnderlay._singleton.set(pd.underlayAttrs);
30094 if(dialog.refocus){
30095 // If we are returning control to a previous dialog but for some reason
30096 // that dialog didn't have a focused field, set focus to first focusable item.
30097 // This situation could happen if two dialogs appeared at nearly the same time,
30098 // since a dialog doesn't set it's focus until the fade-in is finished.
30099 var focus = pd.focus;
30100 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
30101 pd.dialog._getFocusItems(pd.dialog.domNode);
30102 focus = pd.dialog._firstFocusItem;
30106 // Refocus the button that spawned the Dialog. This will fail in corner cases including
30107 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
30108 // before this code runs. (#15058)
30115 // Removing a dialog out of order (#9944, #10705).
30116 // Don't need to mess with underlay or z-index or anything.
30117 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
30124 isTop: function(/*dijit/_WidgetBase*/ dialog){
30126 // Returns true if specified Dialog is the top in the task
30127 return ds[ds.length-1].dialog == dialog;
30131 // Stack representing the various active "levels" on the page, starting with the
30132 // stuff initially visible on the page (at z-index 0), and then having an entry for
30133 // each Dialog shown.
30134 // Each element in stack has form {
30135 // dialog: dialogWidget,
30136 // focus: returnFromGetFocus(),
30137 // underlayAttrs: attributes to set on underlay (when this widget is active)
30139 var ds = Dialog._dialogStack = [
30140 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
30143 // Back compat w/1.6, remove for 2.0
30144 if(has("dijit-legacy-requires")){
30145 ready(0, function(){
30146 var requires = ["dijit/TooltipDialog"];
30147 require(requires); // use indirection so modules not rolled into a build
30155 'dijit/_base/focus':function(){
30156 define("dijit/_base/focus", [
30157 "dojo/_base/array", // array.forEach
30158 "dojo/dom", // dom.isDescendant
30159 "dojo/_base/lang", // lang.isArray
30160 "dojo/topic", // publish
30161 "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
30163 "../main" // for exporting symbols to dijit
30164 ], function(array, dom, lang, topic, win, focus, dijit){
30167 // dijit/_base/focus
30171 // Deprecated module to monitor currently focused node and stack of currently focused widgets.
30172 // New code should access dijit/focus directly.
30174 // _curFocus: DomNode
30175 // Currently focused item on screen
30178 // _prevFocus: DomNode
30179 // Previously focused item on screen
30182 isCollapsed: function(){
30184 // Returns true if there is no text selected
30185 return dijit.getBookmark().isCollapsed;
30188 getBookmark: function(){
30190 // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
30191 var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
30193 if(win.global.getSelection){
30194 //W3C Range API for selections.
30195 sel = win.global.getSelection();
30197 if(sel.isCollapsed){
30198 tg = cf? cf.tagName : "";
30200 //Create a fake rangelike item to restore selections.
30201 tg = tg.toLowerCase();
30202 if(tg == "textarea" ||
30203 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
30205 start: cf.selectionStart,
30206 end: cf.selectionEnd,
30210 return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
30213 bm = {isCollapsed:true};
30214 if(sel.rangeCount){
30215 bm.mark = sel.getRangeAt(0).cloneRange();
30218 rg = sel.getRangeAt(0);
30219 bm = {isCollapsed: false, mark: rg.cloneRange()};
30223 // If the current focus was a input of some sort and no selection, don't bother saving
30224 // a native bookmark. This is because it causes issues with dialog/page selection restore.
30225 // So, we need to create psuedo bookmarks to work with.
30226 tg = cf ? cf.tagName : "";
30227 tg = tg.toLowerCase();
30228 if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
30229 if(sel.type && sel.type.toLowerCase() == "none"){
30235 rg = sel.createRange();
30237 isCollapsed: rg.text && rg.text.length?false:true,
30247 //'IE' way for selections.
30249 // createRange() throws exception when dojo in iframe
30250 //and nothing selected, see #9632
30251 rg = sel.createRange();
30252 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
30254 bm.isCollapsed = true;
30257 if(sel.type.toUpperCase() == 'CONTROL'){
30260 var i=0,len=rg.length;
30262 bm.mark.push(rg.item(i++));
30265 bm.isCollapsed = true;
30269 bm.mark = rg.getBookmark();
30272 console.warn("No idea how to store the current selection for this browser!");
30274 return bm; // Object
30277 moveToBookmark: function(/*Object*/ bookmark
){
30279 // Moves current selection to a bookmark
30281 // This should be a returned object from dijit.getBookmark()
30283 var _doc
= win
.doc
,
30284 mark
= bookmark
.mark
;
30286 if(win
.global
.getSelection
){
30287 //W3C Rangi API (FF, WebKit, Opera, etc)
30288 var sel
= win
.global
.getSelection();
30289 if(sel
&& sel
.removeAllRanges
){
30292 n
.selectionStart
= mark
.start
;
30293 n
.selectionEnd
= mark
.end
;
30295 sel
.removeAllRanges();
30296 sel
.addRange(mark
);
30299 console
.warn("No idea how to restore selection for this browser!");
30301 }else if(_doc
.selection
&& mark
){
30306 }else if(lang
.isArray(mark
)){
30307 rg
= _doc
.body
.createControlRange();
30308 //rg.addElement does not have call/apply method, so can not call it directly
30309 //rg is not available in "range.addElement(item)", so can't use that either
30310 array
.forEach(mark
, function(n
){
30314 rg
= _doc
.body
.createTextRange();
30315 rg
.moveToBookmark(mark
);
30322 getFocus: function(/*Widget?*/ menu
, /*Window?*/ openedForWindow
){
30324 // Called as getFocus(), this returns an Object showing the current focus
30325 // and selected text.
30327 // Called as getFocus(widget), where widget is a (widget representing) a button
30328 // that was just pressed, it returns where focus was before that button
30329 // was pressed. (Pressing the button may have either shifted focus to the button,
30330 // or removed focus altogether.) In this case the selected text is not returned,
30331 // since it can't be accurately determined.
30333 // menu: dijit/_WidgetBase|{domNode: DomNode} structure
30334 // The button that was just pressed. If focus has disappeared or moved
30335 // to this button, returns the previous focus. In this case the bookmark
30336 // information is already lost, and null is returned.
30338 // openedForWindow:
30339 // iframe in which menu was opened
30342 // A handle to restore focus/selection, to be passed to `dijit.focus`
30343 var node
= !focus
.curNode
|| (menu
&& dom
.isDescendant(focus
.curNode
, menu
.domNode
)) ? dijit
._prevFocus
: focus
.curNode
;
30346 bookmark
: node
&& (node
== focus
.curNode
) && win
.withGlobal(openedForWindow
|| win
.global
, dijit
.getBookmark
),
30347 openedForWindow
: openedForWindow
30351 // _activeStack: dijit/_WidgetBase[]
30352 // List of currently active widgets (focused widget and it's ancestors)
30355 registerIframe: function(/*DomNode*/ iframe
){
30357 // Registers listeners on the specified iframe so that any click
30358 // or focus event on that iframe (or anything in it) is reported
30359 // as a focus/click event on the `<iframe>` itself.
30361 // Currently only used by editor.
30363 // Handle to pass to unregisterIframe()
30364 return focus
.registerIframe(iframe
);
30367 unregisterIframe: function(/*Object*/ handle
){
30369 // Unregisters listeners on the specified iframe created by registerIframe.
30370 // After calling be sure to delete or null out the handle itself.
30372 // Handle returned by registerIframe()
30374 handle
&& handle
.remove();
30377 registerWin: function(/*Window?*/targetWindow
, /*DomNode?*/ effectiveNode
){
30379 // Registers listeners on the specified window (either the main
30380 // window or an iframe's window) to detect when the user has clicked somewhere
30381 // or focused somewhere.
30383 // Users should call registerIframe() instead of this method.
30385 // If specified this is the window associated with the iframe,
30386 // i.e. iframe.contentWindow.
30388 // If specified, report any focus events inside targetWindow as
30389 // an event on effectiveNode, rather than on evt.target.
30391 // Handle to pass to unregisterWin()
30393 return focus
.registerWin(targetWindow
, effectiveNode
);
30396 unregisterWin: function(/*Handle*/ handle
){
30398 // Unregisters listeners on the specified window (either the main
30399 // window or an iframe's window) according to handle returned from registerWin().
30400 // After calling be sure to delete or null out the handle itself.
30402 handle
&& handle
.remove();
30406 // Override focus singleton's focus function so that dijit.focus()
30407 // has backwards compatible behavior of restoring selection (although
30408 // probably no one is using that).
30409 focus
.focus = function(/*Object|DomNode */ handle
){
30411 // Sets the focused node and the selection according to argument.
30412 // To set focus to an iframe's content, pass in the iframe itself.
30414 // object returned by get(), or a DomNode
30416 if(!handle
){ return; }
30418 var node
= "node" in handle
? handle
.node
: handle
, // because handle is either DomNode or a composite object
30419 bookmark
= handle
.bookmark
,
30420 openedForWindow
= handle
.openedForWindow
,
30421 collapsed
= bookmark
? bookmark
.isCollapsed
: false;
30424 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
30425 // but we need to set focus to iframe.contentWindow
30427 var focusNode
= (node
.tagName
.toLowerCase() == "iframe") ? node
.contentWindow
: node
;
30428 if(focusNode
&& focusNode
.focus
){
30430 // Gecko throws sometimes if setting focus is impossible,
30431 // node not displayed or something like that
30433 }catch(e
){/*quiet*/}
30435 focus
._onFocusNode(node
);
30438 // set the selection
30439 // do not need to restore if current selection is not empty
30440 // (use keyboard to select a menu item) or if previous selection was collapsed
30441 // as it may cause focus shift (Esp in IE).
30442 if(bookmark
&& win
.withGlobal(openedForWindow
|| win
.global
, dijit
.isCollapsed
) && !collapsed
){
30443 if(openedForWindow
){
30444 openedForWindow
.focus();
30447 win
.withGlobal(openedForWindow
|| win
.global
, dijit
.moveToBookmark
, null, [bookmark
]);
30449 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
30454 // For back compatibility, monitor changes to focused node and active widget stack,
30455 // publishing events and copying changes from focus manager variables into dijit (top level) variables
30456 focus.watch("curNode", function(name, oldVal, newVal){
30457 dijit._curFocus = newVal;
30458 dijit._prevFocus = oldVal;
30460 topic.publish("focusNode", newVal); // publish
30463 focus.watch("activeStack", function(name, oldVal, newVal){
30464 dijit._activeStack = newVal;
30467 focus.on("widget-blur", function(widget, by){
30468 topic.publish("widgetBlur", widget, by); // publish
30470 focus.on("widget-focus", function(widget, by){
30471 topic.publish("widgetFocus", widget, by); // publish
30474 lang.mixin(dijit, exports);
30476 /*===== return exports; =====*/
30477 return dijit
; // for back compat :-(
30481 'dijit/tree/dndSource':function(){
30482 define("dijit/tree/dndSource", [
30483 "dojo/_base/array", // array.forEach array.indexOf array.map
30484 "dojo/_base/connect", // isCopyKey
30485 "dojo/_base/declare", // declare
30486 "dojo/dom-class", // domClass.add
30487 "dojo/dom-geometry", // domGeometry.position
30488 "dojo/_base/lang", // lang.mixin lang.hitch
30489 "dojo/on", // subscribe
30492 "dojo/dnd/Manager", // DNDManager.manager
30494 ], function(array
, connect
, declare
, domClass
, domGeometry
, lang
, on
, touch
, topic
, DNDManager
, _dndSelector
){
30497 // dijit/tree/dndSource
30499 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30504 // New item to be added to the Tree, like:
30512 var dndSource
= declare("dijit.tree.dndSource", _dndSelector
, {
30514 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30516 // isSource: Boolean
30517 // Can be used as a DnD source.
30520 // accept: String[]
30521 // List of accepted types (text strings) for the Tree; defaults to
30523 accept
: ["text", "treeNode"],
30525 // copyOnly: [private] Boolean
30526 // Copy items, if true, use a state of Ctrl key otherwise
30529 // dragThreshold: Number
30530 // The move delay in pixels before detecting a drag; 5 by default
30533 // betweenThreshold: Integer
30534 // Distance from upper/lower edge of node to allow drop to reorder nodes
30535 betweenThreshold
: 0,
30537 // Flag used by Avatar.js to signal to generate text node when dragging
30538 generateText
: true,
30540 constructor: function(/*dijit/Tree*/ tree, /*dijit/tree/dndSource*/ params
){
30542 // a constructor of the Tree DnD Source
30545 if(!params
){ params
= {}; }
30546 lang
.mixin(this, params
);
30547 var type
= params
.accept
instanceof Array
? params
.accept
: ["text", "treeNode"];
30548 this.accept
= null;
30551 for(var i
= 0; i
< type
.length
; ++i
){
30552 this.accept
[type
[i
]] = 1;
30556 // class-specific variables
30557 this.isDragging
= false;
30558 this.mouseDown
= false;
30559 this.targetAnchor
= null; // DOMNode corresponding to the currently moused over TreeNode
30560 this.targetBox
= null; // coordinates of this.targetAnchor
30561 this.dropPosition
= ""; // whether mouse is over/after/before this.targetAnchor
30566 this.sourceState
= "";
30568 domClass
.add(this.node
, "dojoDndSource");
30570 this.targetState
= "";
30572 domClass
.add(this.node
, "dojoDndTarget");
30577 topic
.subscribe("/dnd/source/over", lang
.hitch(this, "onDndSourceOver")),
30578 topic
.subscribe("/dnd/start", lang
.hitch(this, "onDndStart")),
30579 topic
.subscribe("/dnd/drop", lang
.hitch(this, "onDndDrop")),
30580 topic
.subscribe("/dnd/cancel", lang
.hitch(this, "onDndCancel"))
30585 checkAcceptance: function(/*===== source, nodes =====*/){
30587 // Checks if the target can accept nodes from this source
30588 // source: dijit/tree/dndSource
30589 // The source which provides items
30590 // nodes: DOMNode[]
30591 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
30592 // source is a dijit/Tree.
30595 return true; // Boolean
30598 copyState: function(keyPressed
){
30600 // Returns true, if we need to copy items, false to move.
30601 // It is separated to be overwritten dynamically, if needed.
30602 // keyPressed: Boolean
30603 // The "copy" control key was pressed
30606 return this.copyOnly
|| keyPressed
; // Boolean
30608 destroy: function(){
30610 // Prepares the object to be garbage-collected.
30611 this.inherited(arguments
);
30613 while(h
= this.topics
.pop()){ h
.remove(); }
30614 this.targetAnchor
= null;
30617 _onDragMouse: function(e
, firstTime
){
30619 // Helper method for processing onmousemove/onmouseover events while drag is in progress.
30620 // Keeps track of current drop target.
30622 // The mousemove event.
30623 // firstTime: Boolean?
30624 // If this flag is set, this is the first mouse move event of the drag, so call m.canDrop() etc.
30625 // even if newTarget == null because the user quickly dragged a node in the Tree to a position
30626 // over Tree.containerNode but not over any TreeNode (#7971)
30628 var m
= DNDManager
.manager(),
30629 oldTarget
= this.targetAnchor
, // the TreeNode corresponding to TreeNode mouse was previously over
30630 newTarget
= this.current
, // TreeNode corresponding to TreeNode mouse is currently over
30631 oldDropPosition
= this.dropPosition
; // the previous drop position (over/before/after)
30633 // calculate if user is indicating to drop the dragged node before, after, or over
30634 // (i.e., to become a child of) the target node
30635 var newDropPosition
= "Over";
30636 if(newTarget
&& this.betweenThreshold
> 0){
30637 // If mouse is over a new TreeNode, then get new TreeNode's position and size
30638 if(!this.targetBox
|| oldTarget
!= newTarget
){
30639 this.targetBox
= domGeometry
.position(newTarget
.rowNode
, true);
30641 if((e
.pageY
- this.targetBox
.y
) <= this.betweenThreshold
){
30642 newDropPosition
= "Before";
30643 }else if((e
.pageY
- this.targetBox
.y
) >= (this.targetBox
.h
- this.betweenThreshold
)){
30644 newDropPosition
= "After";
30648 if(firstTime
|| newTarget
!= oldTarget
|| newDropPosition
!= oldDropPosition
){
30650 this._removeItemClass(oldTarget
.rowNode
, oldDropPosition
);
30653 this._addItemClass(newTarget
.rowNode
, newDropPosition
);
30656 // Check if it's ok to drop the dragged node on/before/after the target node.
30659 }else if(newTarget
== this.tree
.rootNode
&& newDropPosition
!= "Over"){
30660 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
30663 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
30664 var sameId
= false;
30665 if(m
.source
== this){
30666 for(var dragId
in this.selection
){
30667 var dragNode
= this.selection
[dragId
];
30668 if(dragNode
.item
=== newTarget
.item
){
30676 }else if(this.checkItemAcceptance(newTarget
.rowNode
, m
.source
, newDropPosition
.toLowerCase())
30677 && !this._isParentChildDrop(m
.source
, newTarget
.rowNode
)){
30684 this.targetAnchor
= newTarget
;
30685 this.dropPosition
= newDropPosition
;
30689 onMouseMove: function(e
){
30691 // Called for any onmousemove/ontouchmove events over the Tree
30693 // onmousemouse/ontouchmove event
30696 if(this.isDragging
&& this.targetState
== "Disabled"){ return; }
30697 this.inherited(arguments
);
30698 var m
= DNDManager
.manager();
30699 if(this.isDragging
){
30700 this._onDragMouse(e
);
30702 if(this.mouseDown
&& this.isSource
&&
30703 (Math
.abs(e
.pageX
-this._lastX
)>=this.dragThreshold
|| Math
.abs(e
.pageY
-this._lastY
)>=this.dragThreshold
)){
30704 var nodes
= this.getSelectedTreeNodes();
30706 if(nodes
.length
> 1){
30707 //filter out all selected items which has one of their ancestor selected as well
30708 var seen
= this.selection
, i
= 0, r
= [], n
, p
;
30709 nextitem
: while((n
= nodes
[i
++])){
30710 for(p
= n
.getParent(); p
&& p
!== this.tree
; p
= p
.getParent()){
30711 if(seen
[p
.id
]){ //parent is already selected, skip this node
30715 //this node does not have any ancestors selected, add it
30720 nodes
= array
.map(nodes
, function(n
){return n
.domNode
});
30721 m
.startDrag(this, nodes
, this.copyState(connect
.isCopyKey(e
)));
30722 this._onDragMouse(e
, true); // because this may be the only mousemove event we get before the drop
30728 onMouseDown: function(e
){
30730 // Event processor for onmousedown/ontouchstart
30732 // onmousedown/ontouchend event
30735 this.mouseDown
= true;
30736 this.mouseButton
= e
.button
;
30737 this._lastX
= e
.pageX
;
30738 this._lastY
= e
.pageY
;
30739 this.inherited(arguments
);
30742 onMouseUp: function(e
){
30744 // Event processor for onmouseup/ontouchend
30746 // onmouseup/ontouchend event
30749 if(this.mouseDown
){
30750 this.mouseDown
= false;
30751 this.inherited(arguments
);
30755 onMouseOut: function(){
30757 // Event processor for when mouse is moved away from a TreeNode
30760 this.inherited(arguments
);
30761 this._unmarkTargetAnchor();
30764 checkItemAcceptance: function(/*===== target, source, position =====*/){
30766 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
30768 // In the base case, this is called to check if target can become a child of source.
30769 // When betweenThreshold is set, position="before" or "after" means that we
30770 // are asking if the source node can be dropped before/after the target node.
30772 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
30773 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
30774 // source: dijit/tree/dndSource
30775 // The (set of) nodes we are dropping
30776 // position: String
30777 // "over", "before", or "after"
30783 // topic event processors
30784 onDndSourceOver: function(source
){
30786 // Topic event processor for /dnd/source/over, called when detected a current source.
30788 // The dijit/tree/dndSource / dojo/dnd/Source which has the mouse over it
30791 if(this != source
){
30792 this.mouseDown
= false;
30793 this._unmarkTargetAnchor();
30794 }else if(this.isDragging
){
30795 var m
= DNDManager
.manager();
30799 onDndStart: function(source
, nodes
, copy
){
30801 // Topic event processor for /dnd/start, called to initiate the DnD operation
30803 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30804 // nodes: DomNode[]
30805 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30807 // Copy items, if true, move items otherwise
30812 this._changeState("Source", this == source
? (copy
? "Copied" : "Moved") : "");
30814 var accepted
= this.checkAcceptance(source
, nodes
);
30816 this._changeState("Target", accepted
? "" : "Disabled");
30818 if(this == source
){
30819 DNDManager
.manager().overSource(this);
30822 this.isDragging
= true;
30825 itemCreator: function(nodes
/*===== , target, source =====*/){
30827 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
30828 // dropped onto the tree. Developer must override this method to enable
30829 // dropping from external sources onto this Tree, unless the Tree.model's items
30830 // happen to look like {id: 123, name: "Apple" } with no other attributes.
30832 // For each node in nodes[], which came from source, create a hash of name/value
30833 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
30834 // nodes: DomNode[]
30836 // source: dojo/dnd/Source
30837 // returns: __Item[]
30838 // Array of name/value hashes for each new item to be added to the Tree
30842 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
30843 // make signature itemCreator(sourceItem, node, target) (or similar).
30845 return array
.map(nodes
, function(node
){
30848 "name": node
.textContent
|| node
.innerText
|| ""
30853 onDndDrop: function(source
, nodes
, copy
){
30855 // Topic event processor for /dnd/drop, called to finish the DnD operation.
30857 // Updates data store items according to where node was dragged from and dropped
30858 // to. The tree will then respond to those data store updates and redraw itself.
30860 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30861 // nodes: DomNode[]
30862 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30864 // Copy items, if true, move items otherwise
30867 if(this.containerState
== "Over"){
30868 var tree
= this.tree
,
30869 model
= tree
.model
,
30870 target
= this.targetAnchor
;
30872 this.isDragging
= false;
30874 // Compute the new parent item
30877 var before
; // drop source before (aka previous sibling) of target
30878 newParentItem
= (target
&& target
.item
) || tree
.item
;
30879 if(this.dropPosition
== "Before" || this.dropPosition
== "After"){
30880 // TODO: if there is no parent item then disallow the drop.
30881 // Actually this should be checked during onMouseMove too, to make the drag icon red.
30882 newParentItem
= (target
.getParent() && target
.getParent().item
) || tree
.item
;
30883 // Compute the insert index for reordering
30884 insertIndex
= target
.getIndexInParent();
30885 if(this.dropPosition
== "After"){
30886 insertIndex
= target
.getIndexInParent() + 1;
30887 before
= target
.getNextSibling() && target
.getNextSibling().item
;
30889 before
= target
.item
;
30892 newParentItem
= (target
&& target
.item
) || tree
.item
;
30895 // If necessary, use this variable to hold array of hashes to pass to model.newItem()
30896 // (one entry in the array for each dragged node).
30897 var newItemsParams
;
30899 array
.forEach(nodes
, function(node
, idx
){
30900 // dojo/dnd/Item representing the thing being dropped.
30901 // Don't confuse the use of item here (meaning a DnD item) with the
30902 // uses below where item means dojo.data item.
30903 var sourceItem
= source
.getItem(node
.id
);
30905 // Information that's available if the source is another Tree
30906 // (possibly but not necessarily this tree, possibly but not
30907 // necessarily the same model as this Tree)
30908 if(array
.indexOf(sourceItem
.type
, "treeNode") != -1){
30909 var childTreeNode
= sourceItem
.data
,
30910 childItem
= childTreeNode
.item
,
30911 oldParentItem
= childTreeNode
.getParent().item
;
30914 if(source
== this){
30915 // This is a node from my own tree, and we are moving it, not copying.
30916 // Remove item from old parent's children attribute.
30917 // TODO: dijit/tree/dndSelector should implement deleteSelectedNodes()
30918 // and this code should go there.
30920 if(typeof insertIndex
== "number"){
30921 if(newParentItem
== oldParentItem
&& childTreeNode
.getIndexInParent() < insertIndex
){
30925 model
.pasteItem(childItem
, oldParentItem
, newParentItem
, copy
, insertIndex
, before
);
30926 }else if(model
.isItem(childItem
)){
30927 // Item from same model
30928 // (maybe we should only do this branch if the source is a tree?)
30929 model
.pasteItem(childItem
, oldParentItem
, newParentItem
, copy
, insertIndex
, before
);
30931 // Get the hash to pass to model.newItem(). A single call to
30932 // itemCreator() returns an array of hashes, one for each drag source node.
30933 if(!newItemsParams
){
30934 newItemsParams
= this.itemCreator(nodes
, target
.rowNode
, source
);
30937 // Create new item in the tree, based on the drag source.
30938 model
.newItem(newItemsParams
[idx
], newParentItem
, insertIndex
, before
);
30942 // Expand the target node (if it's currently collapsed) so the user can see
30943 // where their node was dropped. In particular since that node is still selected.
30944 this.tree
._expandNode(target
);
30946 this.onDndCancel();
30949 onDndCancel: function(){
30951 // Topic event processor for /dnd/cancel, called to cancel the DnD operation
30954 this._unmarkTargetAnchor();
30955 this.isDragging
= false;
30956 this.mouseDown
= false;
30957 delete this.mouseButton
;
30958 this._changeState("Source", "");
30959 this._changeState("Target", "");
30962 // When focus moves in/out of the entire Tree
30963 onOverEvent: function(){
30965 // This method is called when mouse is moved over our container (like onmouseenter)
30968 this.inherited(arguments
);
30969 DNDManager
.manager().overSource(this);
30971 onOutEvent: function(){
30973 // This method is called when mouse is moved out of our container (like onmouseleave)
30976 this._unmarkTargetAnchor();
30977 var m
= DNDManager
.manager();
30978 if(this.isDragging
){
30983 this.inherited(arguments
);
30986 _isParentChildDrop: function(source
, targetRow
){
30988 // Checks whether the dragged items are parent rows in the tree which are being
30989 // dragged into their own children.
30992 // The DragSource object.
30995 // The tree row onto which the dragged nodes are being dropped.
31000 // If the dragged object is not coming from the tree this widget belongs to,
31001 // it cannot be invalid.
31002 if(!source
.tree
|| source
.tree
!= this.tree
){
31007 var root
= source
.tree
.domNode
;
31008 var ids
= source
.selection
;
31010 var node
= targetRow
.parentNode
;
31012 // Iterate up the DOM hierarchy from the target drop row,
31013 // checking of any of the dragged nodes have the same ID.
31014 while(node
!= root
&& !ids
[node
.id
]){
31015 node
= node
.parentNode
;
31018 return node
.id
&& ids
[node
.id
];
31021 _unmarkTargetAnchor: function(){
31023 // Removes hover class of the current target anchor
31026 if(!this.targetAnchor
){ return; }
31027 this._removeItemClass(this.targetAnchor
.rowNode
, this.dropPosition
);
31028 this.targetAnchor
= null;
31029 this.targetBox
= null;
31030 this.dropPosition
= null;
31033 _markDndStatus: function(copy
){
31035 // Changes source's state based on "copy" status
31036 this._changeState("Source", copy
? "Copied" : "Moved");
31041 dndSource.__Item = __Item;
31048 'dijit/a11y':function(){
31049 define("dijit/a11y", [
31050 "dojo/_base/array", // array.forEach array.map
31051 "dojo/_base/config", // defaultDuration
31052 "dojo/_base/declare", // declare
31053 "dojo/dom", // dom.byId
31054 "dojo/dom-attr", // domAttr.attr domAttr.has
31055 "dojo/dom-style", // style.style
31056 "dojo/sniff", // has("ie")
31057 "./main" // for exporting methods to dijit namespace
31058 ], function(array
, config
, declare
, dom
, domAttr
, domStyle
, has
, dijit
){
31063 var shown
= (dijit
._isElementShown = function(/*Element*/ elem
){
31064 var s
= domStyle
.get(elem
);
31065 return (s
.visibility
!= "hidden")
31066 && (s
.visibility
!= "collapsed")
31067 && (s
.display
!= "none")
31068 && (domAttr
.get(elem
, "type") != "hidden");
31071 dijit
.hasDefaultTabStop = function(/*Element*/ elem
){
31073 // Tests if element is tab-navigable even without an explicit tabIndex setting
31075 // No explicit tabIndex setting, need to investigate node type
31076 switch(elem
.nodeName
.toLowerCase()){
31078 // An <a> w/out a tabindex is only navigable if it has an href
31079 return domAttr
.has(elem
, "href");
31086 // These are navigable by default
31089 // If it's an editor <iframe> then it's tab navigable.
31093 var contentDocument
= elem
.contentDocument
;
31094 if("designMode" in contentDocument
&& contentDocument
.designMode
== "on"){
31097 body
= contentDocument
.body
;
31099 // contentWindow.document isn't accessible within IE7/8
31100 // if the iframe.src points to a foreign url and this
31101 // page contains an element, that could get focus
31103 body
= elem
.contentWindow
.document
.body
;
31108 return body
&& (body
.contentEditable
== 'true' ||
31109 (body
.firstChild
&& body
.firstChild
.contentEditable
== 'true'));
31111 return elem
.contentEditable
== 'true';
31115 var isTabNavigable
= (dijit
.isTabNavigable = function(/*Element*/ elem
){
31117 // Tests if an element is tab-navigable
31119 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
31120 if(domAttr
.get(elem
, "disabled")){
31122 }else if(domAttr
.has(elem
, "tabIndex")){
31123 // Explicit tab index setting
31124 return domAttr
.get(elem
, "tabIndex") >= 0; // boolean
31126 // No explicit tabIndex setting, so depends on node type
31127 return dijit
.hasDefaultTabStop(elem
);
31131 dijit
._getTabNavigable = function(/*DOMNode*/ root
){
31133 // Finds descendants of the specified root node.
31135 // Finds the following descendants of the specified root node:
31137 // - the first tab-navigable element in document order
31138 // without a tabIndex or with tabIndex="0"
31139 // - the last tab-navigable element in document order
31140 // without a tabIndex or with tabIndex="0"
31141 // - the first element in document order with the lowest
31142 // positive tabIndex value
31143 // - the last element in document order with the highest
31144 // positive tabIndex value
31145 var first
, last
, lowest
, lowestTabindex
, highest
, highestTabindex
, radioSelected
= {};
31147 function radioName(node
){
31148 // If this element is part of a radio button group, return the name for that group.
31149 return node
&& node
.tagName
.toLowerCase() == "input" &&
31150 node
.type
&& node
.type
.toLowerCase() == "radio" &&
31151 node
.name
&& node
.name
.toLowerCase();
31154 var walkTree = function(/*DOMNode*/ parent
){
31155 for(var child
= parent
.firstChild
; child
; child
= child
.nextSibling
){
31156 // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
31157 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
31158 if(child
.nodeType
!= 1 || (has("ie") <= 9 && child
.scopeName
!== "HTML") || !shown(child
)){
31162 if(isTabNavigable(child
)){
31163 var tabindex
= +domAttr
.get(child
, "tabIndex"); // + to convert string --> number
31164 if(!domAttr
.has(child
, "tabIndex") || tabindex
== 0){
31169 }else if(tabindex
> 0){
31170 if(!lowest
|| tabindex
< lowestTabindex
){
31171 lowestTabindex
= tabindex
;
31174 if(!highest
|| tabindex
>= highestTabindex
){
31175 highestTabindex
= tabindex
;
31179 var rn
= radioName(child
);
31180 if(domAttr
.get(child
, "checked") && rn
){
31181 radioSelected
[rn
] = child
;
31184 if(child
.nodeName
.toUpperCase() != 'SELECT'){
31193 // substitute checked radio button for unchecked one, if there is a checked one with the same name.
31194 return radioSelected
[radioName(node
)] || node
;
31197 return { first
: rs(first
), last
: rs(last
), lowest
: rs(lowest
), highest
: rs(highest
) };
31199 dijit
.getFirstInTabbingOrder = function(/*String|DOMNode*/ root
, /*Document?*/ doc
){
31201 // Finds the descendant of the specified root node
31202 // that is first in the tabbing order
31203 var elems
= dijit
._getTabNavigable(dom
.byId(root
, doc
));
31204 return elems
.lowest
? elems
.lowest
: elems
.first
; // DomNode
31207 dijit
.getLastInTabbingOrder = function(/*String|DOMNode*/ root
, /*Document?*/ doc
){
31209 // Finds the descendant of the specified root node
31210 // that is last in the tabbing order
31211 var elems
= dijit
._getTabNavigable(dom
.byId(root
, doc
));
31212 return elems
.last
? elems
.last
: elems
.highest
; // DomNode
31217 // Accessibility utility functions (keyboard, tab stops, etc.)
31219 hasDefaultTabStop
: dijit
.hasDefaultTabStop
,
31220 isTabNavigable
: dijit
.isTabNavigable
,
31221 _getTabNavigable
: dijit
._getTabNavigable
,
31222 getFirstInTabbingOrder
: dijit
.getFirstInTabbingOrder
,
31223 getLastInTabbingOrder
: dijit
.getLastInTabbingOrder
31228 'dijit/form/_ToggleButtonMixin':function(){
31229 define("dijit/form/_ToggleButtonMixin", [
31230 "dojo/_base/declare", // declare
31231 "dojo/dom-attr" // domAttr.set
31232 ], function(declare
, domAttr
){
31235 // dijit/form/_ToggleButtonMixin
31237 return declare("dijit.form._ToggleButtonMixin", null, {
31239 // A mixin to provide functionality to allow a button that can be in two states (checked or not).
31241 // checked: Boolean
31242 // Corresponds to the native HTML `<input>` element's attribute.
31243 // In markup, specified as "checked='checked'" or just "checked".
31244 // True if the button is depressed, or the checkbox is checked,
31245 // or the radio button is selected, etc.
31248 // aria-pressed for toggle buttons, and aria-checked for checkboxes
31249 _aria_attr
: "aria-pressed",
31251 _onClick: function(/*Event*/ evt
){
31252 var original
= this.checked
;
31253 this._set('checked', !original
); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
31254 var ret
= this.inherited(arguments
); // the user could reset the value here
31255 this.set('checked', ret
? this.checked
: original
); // officially set the toggled or user value, or reset it back
31259 _setCheckedAttr: function(/*Boolean*/ value
, /*Boolean?*/ priorityChange
){
31260 this._set("checked", value
);
31261 var node
= this.focusNode
|| this.domNode
;
31262 domAttr
.set(node
, "checked", !!value
); // "mixed" -> true
31264 node
.setAttribute("checked", "");
31266 node
.removeAttribute("checked");
31268 node
.setAttribute(this._aria_attr
, String(value
)); // aria values should be strings
31269 this._handleOnChange(value
, priorityChange
);
31274 // Reset the widget's value to what it was at initialization time
31276 this._hasBeenBlurred
= false;
31278 // set checked state to original setting
31279 this.set('checked', this.params
.checked
|| false);
31286 'dijit/_Widget':function(){
31287 define("dijit/_Widget", [
31288 "dojo/aspect", // aspect.around
31289 "dojo/_base/config", // config.isDebug
31290 "dojo/_base/connect", // connect.connect
31291 "dojo/_base/declare", // declare
31293 "dojo/_base/kernel", // kernel.deprecated
31294 "dojo/_base/lang", // lang.hitch
31297 "./registry", // registry.byNode
31299 "./_OnDijitClickMixin",
31301 "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
31302 "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
31303 ], function(aspect
, config
, connect
, declare
, has
, kernel
, lang
, query
, ready
,
31304 registry
, _WidgetBase
, _OnDijitClickMixin
, _FocusMixin
){
31311 function connectToDomNode(){
31313 // If user connects to a widget method === this function, then they will
31314 // instead actually be connecting the equivalent event on this.domNode
31317 // Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
31318 function aroundAdvice(originalConnect
){
31319 return function(obj
, event
, scope
, method
){
31320 if(obj
&& typeof event
== "string" && obj
[event
] == connectToDomNode
){
31321 return obj
.on(event
.substring(2).toLowerCase(), lang
.hitch(scope
, method
));
31323 return originalConnect
.apply(connect
, arguments
);
31326 aspect
.around(connect
, "connect", aroundAdvice
);
31327 if(kernel
.connect
){
31328 aspect
.around(kernel
, "connect", aroundAdvice
);
31331 var _Widget
= declare("dijit._Widget", [_WidgetBase
, _OnDijitClickMixin
, _FocusMixin
], {
31333 // Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
31335 // Old Base class for Dijit widgets.
31337 // Extends _WidgetBase, adding support for:
31339 // - declaratively/programatically specifying widget initialization parameters like
31340 // onMouseMove="foo" that call foo when this.domNode gets a mousemove event
31342 // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
31343 // - focus related functions:
31344 // In particular, the onFocus()/onBlur() callbacks. Driven internally by
31345 // dijit/_base/focus.js.
31346 // - deprecated methods
31347 // - onShow(), onHide(), onClose()
31349 // Also, by loading code in dijit/_base, turns on:
31351 // - browser sniffing (putting browser class like `dj_ie` on `<html>` node)
31352 // - high contrast mode sniffing (add `dijit_a11y` class to `<body>` if machine is in high contrast mode)
31355 ////////////////// DEFERRED CONNECTS ///////////////////
31357 onClick
: connectToDomNode
,
31359 onClick: function(event){
31361 // Connect to this function to receive notifications of mouse click events.
31368 onDblClick
: connectToDomNode
,
31370 onDblClick: function(event){
31372 // Connect to this function to receive notifications of mouse double click events.
31379 onKeyDown
: connectToDomNode
,
31381 onKeyDown: function(event){
31383 // Connect to this function to receive notifications of keys being pressed down.
31390 onKeyPress
: connectToDomNode
,
31392 onKeyPress: function(event){
31394 // Connect to this function to receive notifications of printable keys being typed.
31401 onKeyUp
: connectToDomNode
,
31403 onKeyUp: function(event){
31405 // Connect to this function to receive notifications of keys being released.
31412 onMouseDown
: connectToDomNode
,
31414 onMouseDown: function(event){
31416 // Connect to this function to receive notifications of when the mouse button is pressed down.
31423 onMouseMove
: connectToDomNode
,
31425 onMouseMove: function(event){
31427 // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
31434 onMouseOut
: connectToDomNode
,
31436 onMouseOut: function(event){
31438 // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
31445 onMouseOver
: connectToDomNode
,
31447 onMouseOver: function(event){
31449 // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
31456 onMouseLeave
: connectToDomNode
,
31458 onMouseLeave: function(event){
31460 // Connect to this function to receive notifications of when the mouse moves off of this widget.
31467 onMouseEnter
: connectToDomNode
,
31469 onMouseEnter: function(event){
31471 // Connect to this function to receive notifications of when the mouse moves onto this widget.
31478 onMouseUp
: connectToDomNode
,
31480 onMouseUp: function(event){
31482 // Connect to this function to receive notifications of when the mouse button is released.
31490 constructor: function(params
/*===== ,srcNodeRef =====*/){
31492 // Create the widget.
31493 // params: Object|null
31494 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
31495 // and functions, typically callbacks like onClick.
31496 // The hash can contain any of the widget's properties, excluding read-only properties.
31497 // srcNodeRef: DOMNode|String?
31498 // If a srcNodeRef (DOM node) is specified:
31500 // - use srcNodeRef.innerHTML as my contents
31501 // - if this is a behavioral widget then apply behavior to that srcNodeRef
31502 // - otherwise, replace srcNodeRef with my generated DOM tree
31504 // extract parameters like onMouseMove that should connect directly to this.domNode
31505 this._toConnect
= {};
31506 for(var name
in params
){
31507 if(this[name
] === connectToDomNode
){
31508 this._toConnect
[name
.replace(/^on/, "").toLowerCase()] = params
[name
];
31509 delete params
[name
];
31514 postCreate: function(){
31515 this.inherited(arguments
);
31517 // perform connection from this.domNode to user specified handlers (ex: onMouseMove)
31518 for(var name
in this._toConnect
){
31519 this.on(name
, this._toConnect
[name
]);
31521 delete this._toConnect
;
31524 on: function(/*String|Function*/ type
, /*Function*/ func
){
31525 if(this[this._onMap(type
)] === connectToDomNode
){
31526 // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
31527 // normalization of onkeypress/onkeydown to behave like firefox, etc.
31528 // Also, need to specify context as "this" rather than the default context of the DOMNode
31530 return connect
.connect(this.domNode
, type
.toLowerCase(), this, func
);
31532 return this.inherited(arguments
);
31535 _setFocusedAttr: function(val
){
31536 // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
31537 // (but since it's a private variable we aren't required to keep supporting it).
31538 this._focused
= val
;
31539 this._set("focused", val
);
31542 ////////////////// DEPRECATED METHODS ///////////////////
31544 setAttribute: function(/*String*/ attr
, /*anything*/ value
){
31546 // Deprecated. Use set() instead.
31549 kernel
.deprecated(this.declaredClass
+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
31550 this.set(attr
, value
);
31553 attr: function(/*String|Object*/name
, /*Object?*/value
){
31555 // Set or get properties on a widget instance.
31557 // The property to get or set. If an object is passed here and not
31558 // a string, its keys are used as names of attributes to be set
31559 // and the value of the object as values to set in the widget.
31561 // Optional. If provided, attr() operates as a setter. If omitted,
31562 // the current value of the named property is returned.
31564 // This method is deprecated, use get() or set() directly.
31566 // Print deprecation warning but only once per calling function
31567 if(config
.isDebug
){
31568 var alreadyCalledHash
= arguments
.callee
._ach
|| (arguments
.callee
._ach
= {}),
31569 caller
= (arguments
.callee
.caller
|| "unknown caller").toString();
31570 if(!alreadyCalledHash
[caller
]){
31571 kernel
.deprecated(this.declaredClass
+ "::attr() is deprecated. Use get() or set() instead, called from " +
31572 caller
, "", "2.0");
31573 alreadyCalledHash
[caller
] = true;
31577 var args
= arguments
.length
;
31578 if(args
>= 2 || typeof name
=== "object"){ // setter
31579 return this.set.apply(this, arguments
);
31581 return this.get(name
);
31585 getDescendants: function(){
31587 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
31588 // This method should generally be avoided as it returns widgets declared in templates, which are
31589 // supposed to be internal/hidden, but it's left here for back-compat reasons.
31591 kernel
.deprecated(this.declaredClass
+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
31592 return this.containerNode
? query('[widgetId]', this.containerNode
).map(registry
.byNode
) : []; // dijit/_WidgetBase[]
31595 ////////////////// MISCELLANEOUS METHODS ///////////////////
31597 _onShow: function(){
31599 // Internal method called when this widget is made visible.
31600 // See `onShow` for details.
31604 onShow: function(){
31606 // Called when this widget becomes the selected pane in a
31607 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31608 // `dijit/layout/AccordionContainer`, etc.
31610 // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31615 onHide: function(){
31617 // Called when another widget becomes the selected pane in a
31618 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31619 // `dijit/layout/AccordionContainer`, etc.
31621 // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31626 onClose: function(){
31628 // Called when this widget is being displayed as a popup (ex: a Calendar popped
31629 // up from a DateTextBox), and it is hidden.
31630 // This is called from the dijit.popup code, and should not be called directly.
31632 // Also used as a parameter for children of `dijit/layout/StackContainer` or subclasses.
31633 // Callback if a user tries to close the child. Child will be closed if this function returns true.
31637 return true; // Boolean
31641 // For back-compat, remove in 2.0.
31642 if(has("dijit-legacy-requires")){
31643 ready(0, function(){
31644 var requires
= ["dijit/_base"];
31645 require(requires
); // use indirection so modules not rolled into a build
31652 'dojo/touch':function(){
31653 define("dojo/touch", ["./_base/kernel", "./aspect", "./dom", "./on", "./has", "./mouse", "./ready", "./_base/window"],
31654 function(dojo
, aspect
, dom
, on
, has
, mouse
, ready
, win
){
31659 var hasTouch
= has("touch");
31661 // TODO: get iOS version from dojo/sniff after #15827 is fixed
31664 var ua
= navigator
.userAgent
;
31665 var v
= ua
.match(/OS ([\d_]+)/) ? RegExp
.$1 : "1";
31666 var os
= parseFloat(v
.replace(/_/, '.').replace(/_
/g
, ''));
31670 var touchmove
, hoveredNode
;
31674 // Keep track of currently hovered node
31675 hoveredNode
= win
.body(); // currently hovered node
31677 win
.doc
.addEventListener("touchstart", function(evt
){
31678 // Precede touchstart event with touch.over event. DnD depends on this.
31679 // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
31680 // and to ensure this code runs even if the listener on the node does event.stop().
31681 var oldNode
= hoveredNode
;
31682 hoveredNode
= evt
.target
;
31683 on
.emit(oldNode
, "dojotouchout", {
31685 relatedTarget
: hoveredNode
,
31688 on
.emit(hoveredNode
, "dojotouchover", {
31689 target
: hoveredNode
,
31690 relatedTarget
: oldNode
,
31695 // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
31696 on(win
.doc
, "touchmove", function(evt
){
31697 var newNode
= win
.doc
.elementFromPoint(
31698 evt
.pageX
- (ios4
? 0 : win
.global
.pageXOffset
), // iOS 4 expects page coords
31699 evt
.pageY
- (ios4
? 0 : win
.global
.pageYOffset
)
31701 if(newNode
&& hoveredNode
!== newNode
){
31702 // touch out on the old node
31703 on
.emit(hoveredNode
, "dojotouchout", {
31704 target
: hoveredNode
,
31705 relatedTarget
: newNode
,
31709 // touchover on the new node
31710 on
.emit(newNode
, "dojotouchover", {
31712 relatedTarget
: hoveredNode
,
31716 hoveredNode
= newNode
;
31721 // Define synthetic touch.move event that unlike the native touchmove, fires for the node the finger is
31722 // currently dragging over rather than the node where the touch started.
31723 touchmove = function(node
, listener
){
31724 return on(win
.doc
, "touchmove", function(evt
){
31725 if(node
=== win
.doc
|| dom
.isDescendant(hoveredNode
, node
)){
31726 evt
.target
= hoveredNode
;
31727 listener
.call(this, evt
);
31734 function _handle(type
){
31736 // press | move | release | cancel
31738 return function(node
, listener
){//called by on(), see dojo.on
31739 return on(node
, type
, listener
);
31743 //device neutral events - touch.press|move|release|cancel/over/out
31745 press
: _handle(hasTouch
? "touchstart": "mousedown"),
31746 move: hasTouch
? touchmove
:_handle("mousemove"),
31747 release
: _handle(hasTouch
? "touchend": "mouseup"),
31748 cancel
: hasTouch
? _handle("touchcancel") : mouse
.leave
,
31749 over
: _handle(hasTouch
? "dojotouchover": "mouseover"),
31750 out
: _handle(hasTouch
? "dojotouchout": "mouseout"),
31751 enter
: mouse
._eventHandler(hasTouch
? "dojotouchover" : "mouseover"),
31752 leave
: mouse
._eventHandler(hasTouch
? "dojotouchout" : "mouseout")
31757 // This module provides unified touch event handlers by exporting
31758 // press, move, release and cancel which can also run well on desktop.
31759 // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
31762 // Used with dojo.on
31763 // | define(["dojo/on", "dojo/touch"], function(on, touch){
31764 // | on(node, touch.press, function(e){});
31765 // | on(node, touch.move, function(e){});
31766 // | on(node, touch.release, function(e){});
31767 // | on(node, touch.cancel, function(e){});
31769 // Used with touch.* directly
31770 // | touch.press(node, function(e){});
31771 // | touch.move(node, function(e){});
31772 // | touch.release(node, function(e){});
31773 // | touch.cancel(node, function(e){});
31775 press: function(node, listener){
31777 // Register a listener to 'touchstart'|'mousedown' for the given node
31779 // Target node to listen to
31780 // listener: Function
31781 // Callback function
31783 // A handle which will be used to remove the listener by handle.remove()
31785 move: function(node, listener){
31787 // Register a listener to 'touchmove'|'mousemove' for the given node
31789 // Target node to listen to
31790 // listener: Function
31791 // Callback function
31793 // A handle which will be used to remove the listener by handle.remove()
31795 release: function(node, listener){
31797 // Register a listener to 'touchend'|'mouseup' for the given node
31799 // Target node to listen to
31800 // listener: Function
31801 // Callback function
31803 // A handle which will be used to remove the listener by handle.remove()
31805 cancel: function(node, listener){
31807 // Register a listener to 'touchcancel'|'mouseleave' for the given node
31809 // Target node to listen to
31810 // listener: Function
31811 // Callback function
31813 // A handle which will be used to remove the listener by handle.remove()
31815 over: function(node, listener){
31817 // Register a listener to 'mouseover' or touch equivalent for the given node
31819 // Target node to listen to
31820 // listener: Function
31821 // Callback function
31823 // A handle which will be used to remove the listener by handle.remove()
31825 out: function(node, listener){
31827 // Register a listener to 'mouseout' or touch equivalent for the given node
31829 // Target node to listen to
31830 // listener: Function
31831 // Callback function
31833 // A handle which will be used to remove the listener by handle.remove()
31835 enter: function(node, listener){
31837 // Register a listener to mouse.enter or touch equivalent for the given node
31839 // Target node to listen to
31840 // listener: Function
31841 // Callback function
31843 // A handle which will be used to remove the listener by handle.remove()
31845 leave: function(node, listener){
31847 // Register a listener to mouse.leave or touch equivalent for the given node
31849 // Target node to listen to
31850 // listener: Function
31851 // Callback function
31853 // A handle which will be used to remove the listener by handle.remove()
31858 1 && (dojo
.touch
= touch
);
31864 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" 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=\"▼ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n",
31865 'dojo/fx':function(){
31866 define("dojo/fx", [
31877 "require" // for context sensitive loading of Toggler
31878 ], function(lang
, Evented
, dojo
, arrayUtil
, connect
, baseFx
, dom
, domStyle
, geom
, ready
, require
){
31883 // For back-compat, remove in 2.0.
31885 ready(0, function(){
31886 var requires
= ["./fx/Toggler"];
31887 require(requires
); // use indirection so modules not rolled into a build
31891 var coreFx
= dojo
.fx
= {
31893 // Effects library on top of Base animations
31897 _fire: function(evt
, args
){
31899 this[evt
].apply(this, args
||[]);
31905 var _chain = function(animations
){
31907 this._animations
= animations
||[];
31908 this._current
= this._onAnimateCtx
= this._onEndCtx
= null;
31911 arrayUtil
.forEach(this._animations
, function(a
){
31912 this.duration
+= a
.duration
;
31913 if(a
.delay
){ this.duration
+= a
.delay
; }
31916 _chain
.prototype = new Evented();
31917 lang
.extend(_chain
, {
31918 _onAnimate: function(){
31919 this._fire("onAnimate", arguments
);
31921 _onEnd: function(){
31922 connect
.disconnect(this._onAnimateCtx
);
31923 connect
.disconnect(this._onEndCtx
);
31924 this._onAnimateCtx
= this._onEndCtx
= null;
31925 if(this._index
+ 1 == this._animations
.length
){
31926 this._fire("onEnd");
31928 // switch animations
31929 this._current
= this._animations
[++this._index
];
31930 this._onAnimateCtx
= connect
.connect(this._current
, "onAnimate", this, "_onAnimate");
31931 this._onEndCtx
= connect
.connect(this._current
, "onEnd", this, "_onEnd");
31932 this._current
.play(0, true);
31935 play: function(/*int?*/ delay
, /*Boolean?*/ gotoStart
){
31936 if(!this._current
){ this._current
= this._animations
[this._index
= 0]; }
31937 if(!gotoStart
&& this._current
.status() == "playing"){ return this; }
31938 var beforeBegin
= connect
.connect(this._current
, "beforeBegin", this, function(){
31939 this._fire("beforeBegin");
31941 onBegin
= connect
.connect(this._current
, "onBegin", this, function(arg
){
31942 this._fire("onBegin", arguments
);
31944 onPlay
= connect
.connect(this._current
, "onPlay", this, function(arg
){
31945 this._fire("onPlay", arguments
);
31946 connect
.disconnect(beforeBegin
);
31947 connect
.disconnect(onBegin
);
31948 connect
.disconnect(onPlay
);
31950 if(this._onAnimateCtx
){
31951 connect
.disconnect(this._onAnimateCtx
);
31953 this._onAnimateCtx
= connect
.connect(this._current
, "onAnimate", this, "_onAnimate");
31954 if(this._onEndCtx
){
31955 connect
.disconnect(this._onEndCtx
);
31957 this._onEndCtx
= connect
.connect(this._current
, "onEnd", this, "_onEnd");
31958 this._current
.play
.apply(this._current
, arguments
);
31963 var e
= connect
.connect(this._current
, "onPause", this, function(arg
){
31964 this._fire("onPause", arguments
);
31965 connect
.disconnect(e
);
31967 this._current
.pause();
31971 gotoPercent: function(/*Decimal*/percent
, /*Boolean?*/ andPlay
){
31973 var offset
= this.duration
* percent
;
31974 this._current
= null;
31975 arrayUtil
.some(this._animations
, function(a
){
31976 if(a
.duration
<= offset
){
31980 offset
-= a
.duration
;
31984 this._current
.gotoPercent(offset
/ this._current
.duration
, andPlay
);
31988 stop: function(/*boolean?*/ gotoEnd
){
31991 for(; this._index
+ 1 < this._animations
.length
; ++this._index
){
31992 this._animations
[this._index
].stop(true);
31994 this._current
= this._animations
[this._index
];
31996 var e
= connect
.connect(this._current
, "onStop", this, function(arg
){
31997 this._fire("onStop", arguments
);
31998 connect
.disconnect(e
);
32000 this._current
.stop();
32004 status: function(){
32005 return this._current
? this._current
.status() : "stopped";
32007 destroy: function(){
32008 if(this._onAnimateCtx
){ connect
.disconnect(this._onAnimateCtx
); }
32009 if(this._onEndCtx
){ connect
.disconnect(this._onEndCtx
); }
32012 lang
.extend(_chain
, _baseObj
);
32014 coreFx
.chain = function(/*dojo/_base/fx.Animation[]*/ animations
){
32016 // Chain a list of `dojo.Animation`s to run in sequence
32019 // Return a `dojo.Animation` which will play all passed
32020 // `dojo.Animation` instances in sequence, firing its own
32021 // synthesized events simulating a single animation. (eg:
32022 // onEnd of this animation means the end of the chain,
32023 // not the individual animations within)
32026 // Once `node` is faded out, fade in `otherNode`
32028 // | dojo.fadeIn({ node:node }),
32029 // | dojo.fadeOut({ node:otherNode })
32032 return new _chain(animations
); // dojo/_base/fx.Animation
32035 var _combine = function(animations
){
32036 this._animations
= animations
||[];
32037 this._connects
= [];
32038 this._finished
= 0;
32041 arrayUtil
.forEach(animations
, function(a
){
32042 var duration
= a
.duration
;
32043 if(a
.delay
){ duration
+= a
.delay
; }
32044 if(this.duration
< duration
){ this.duration
= duration
; }
32045 this._connects
.push(connect
.connect(a
, "onEnd", this, "_onEnd"));
32048 this._pseudoAnimation
= new baseFx
.Animation({curve
: [0, 1], duration
: this.duration
});
32050 arrayUtil
.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
32052 self
._connects
.push(connect
.connect(self
._pseudoAnimation
, evt
,
32053 function(){ self
._fire(evt
, arguments
); }
32058 lang
.extend(_combine
, {
32059 _doAction: function(action
, args
){
32060 arrayUtil
.forEach(this._animations
, function(a
){
32061 a
[action
].apply(a
, args
);
32065 _onEnd: function(){
32066 if(++this._finished
> this._animations
.length
){
32067 this._fire("onEnd");
32070 _call: function(action
, args
){
32071 var t
= this._pseudoAnimation
;
32072 t
[action
].apply(t
, args
);
32074 play: function(/*int?*/ delay
, /*Boolean?*/ gotoStart
){
32075 this._finished
= 0;
32076 this._doAction("play", arguments
);
32077 this._call("play", arguments
);
32081 this._doAction("pause", arguments
);
32082 this._call("pause", arguments
);
32085 gotoPercent: function(/*Decimal*/percent
, /*Boolean?*/ andPlay
){
32086 var ms
= this.duration
* percent
;
32087 arrayUtil
.forEach(this._animations
, function(a
){
32088 a
.gotoPercent(a
.duration
< ms
? 1 : (ms
/ a
.duration
), andPlay
);
32090 this._call("gotoPercent", arguments
);
32093 stop: function(/*boolean?*/ gotoEnd
){
32094 this._doAction("stop", arguments
);
32095 this._call("stop", arguments
);
32098 status: function(){
32099 return this._pseudoAnimation
.status();
32101 destroy: function(){
32102 arrayUtil
.forEach(this._connects
, connect
.disconnect
);
32105 lang
.extend(_combine
, _baseObj
);
32107 coreFx
.combine = function(/*dojo/_base/fx.Animation[]*/ animations
){
32109 // Combine a list of `dojo.Animation`s to run in parallel
32112 // Combine an array of `dojo.Animation`s to run in parallel,
32113 // providing a new `dojo.Animation` instance encompasing each
32114 // animation, firing standard animation events.
32117 // Fade out `node` while fading in `otherNode` simultaneously
32119 // | dojo.fadeIn({ node:node }),
32120 // | dojo.fadeOut({ node:otherNode })
32124 // When the longest animation ends, execute a function:
32125 // | var anim = fx.combine([
32126 // | dojo.fadeIn({ node: n, duration:700 }),
32127 // | dojo.fadeOut({ node: otherNode, duration: 300 })
32129 // | dojo.connect(anim, "onEnd", function(){
32130 // | // overall animation is done.
32132 // | anim.play(); // play the animation
32134 return new _combine(animations
); // dojo/_base/fx.Animation
32137 coreFx
.wipeIn = function(/*Object*/ args
){
32139 // Expand a node to it's natural height.
32142 // Returns an animation that will expand the
32143 // node defined in 'args' object from it's current height to
32144 // it's natural height (with no scrollbar).
32145 // Node must have no margin/border/padding.
32148 // A hash-map of standard `dojo.Animation` constructor properties
32149 // (such as easing: node: duration: and so on)
32155 var node
= args
.node
= dom
.byId(args
.node
), s
= node
.style
, o
;
32157 var anim
= baseFx
.animateProperty(lang
.mixin({
32160 // wrapped in functions so we wait till the last second to query (in case value has changed)
32162 // start at current [computed] height, but use 1px rather than 0
32163 // because 0 causes IE to display the whole panel
32165 s
.overflow
= "hidden";
32166 if(s
.visibility
== "hidden" || s
.display
== "none"){
32172 var height
= domStyle
.get(node
, "height");
32173 return Math
.max(height
, 1);
32177 return node
.scrollHeight
;
32183 var fini = function(){
32187 connect
.connect(anim
, "onStop", fini
);
32188 connect
.connect(anim
, "onEnd", fini
);
32190 return anim
; // dojo/_base/fx.Animation
32193 coreFx
.wipeOut = function(/*Object*/ args
){
32195 // Shrink a node to nothing and hide it.
32198 // Returns an animation that will shrink node defined in "args"
32199 // from it's current height to 1px, and then hide it.
32202 // A hash-map of standard `dojo.Animation` constructor properties
32203 // (such as easing: node: duration: and so on)
32206 // | fx.wipeOut({ node:"someId" }).play()
32208 var node
= args
.node
= dom
.byId(args
.node
), s
= node
.style
, o
;
32210 var anim
= baseFx
.animateProperty(lang
.mixin({
32213 end
: 1 // 0 causes IE to display the whole panel
32218 connect
.connect(anim
, "beforeBegin", function(){
32220 s
.overflow
= "hidden";
32223 var fini = function(){
32226 s
.display
= "none";
32228 connect
.connect(anim
, "onStop", fini
);
32229 connect
.connect(anim
, "onEnd", fini
);
32231 return anim
; // dojo/_base/fx.Animation
32234 coreFx
.slideTo = function(/*Object*/ args
){
32236 // Slide a node to a new top/left position
32239 // Returns an animation that will slide "node"
32240 // defined in args Object from its current position to
32241 // the position defined by (args.left, args.top).
32244 // A hash-map of standard `dojo.Animation` constructor properties
32245 // (such as easing: node: duration: and so on). Special args members
32246 // are `top` and `left`, which indicate the new position to slide to.
32249 // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
32251 var node
= args
.node
= dom
.byId(args
.node
),
32252 top
= null, left
= null;
32254 var init
= (function(n
){
32256 var cs
= domStyle
.getComputedStyle(n
);
32257 var pos
= cs
.position
;
32258 top
= (pos
== 'absolute' ? n
.offsetTop
: parseInt(cs
.top
) || 0);
32259 left
= (pos
== 'absolute' ? n
.offsetLeft
: parseInt(cs
.left
) || 0);
32260 if(pos
!= 'absolute' && pos
!= 'relative'){
32261 var ret
= geom
.position(n
, true);
32264 n
.style
.position
="absolute";
32265 n
.style
.top
=top
+"px";
32266 n
.style
.left
=left
+"px";
32272 var anim
= baseFx
.animateProperty(lang
.mixin({
32274 top
: args
.top
|| 0,
32275 left
: args
.left
|| 0
32278 connect
.connect(anim
, "beforeBegin", anim
, init
);
32280 return anim
; // dojo/_base/fx.Animation
32287 'dijit/_DialogMixin':function(){
32288 define("dijit/_DialogMixin", [
32289 "dojo/_base/declare", // declare
32290 "./a11y" // _getTabNavigable
32291 ], function(declare
, a11y
){
32294 // dijit/_DialogMixin
32296 return declare("dijit._DialogMixin", null, {
32298 // This provides functions useful to Dialog and TooltipDialog
32300 execute: function(/*Object*/ /*===== formContents =====*/){
32302 // Callback when the user hits the submit button.
32303 // Override this method to handle Dialog execution.
32305 // After the user has pressed the submit button, the Dialog
32306 // first calls onExecute() to notify the container to hide the
32307 // dialog and restore focus to wherever it used to be.
32309 // *Then* this method is called.
32314 onCancel: function(){
32316 // Called when user has pressed the Dialog's cancel button, to notify container.
32318 // Developer shouldn't override or connect to this method;
32319 // it's a private communication device between the TooltipDialog
32320 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
32325 onExecute: function(){
32327 // Called when user has pressed the dialog's OK button, to notify container.
32329 // Developer shouldn't override or connect to this method;
32330 // it's a private communication device between the TooltipDialog
32331 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
32336 _onSubmit: function(){
32338 // Callback when user hits submit button
32341 this.onExecute(); // notify container that we are about to execute
32342 this.execute(this.get('value'));
32345 _getFocusItems: function(){
32347 // Finds focusable items in dialog,
32348 // and sets this._firstFocusItem and this._lastFocusItem
32352 var elems
= a11y
._getTabNavigable(this.containerNode
);
32353 this._firstFocusItem
= elems
.lowest
|| elems
.first
|| this.closeButtonNode
|| this.domNode
;
32354 this._lastFocusItem
= elems
.last
|| elems
.highest
|| this._firstFocusItem
;
32360 'dijit/Tree':function(){
32362 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
32363 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});
32364 define("dijit/Tree", [
32365 "dojo/_base/array", // array.filter array.forEach array.map
32366 "dojo/_base/connect", // connect.isCopyKey()
32367 "dojo/cookie", // cookie
32368 "dojo/_base/declare", // declare
32369 "dojo/Deferred", // Deferred
32370 "dojo/DeferredList", // DeferredList
32371 "dojo/dom", // dom.isDescendant
32372 "dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
32373 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
32374 "dojo/dom-style",// domStyle.set
32375 "dojo/_base/event", // event.stop
32376 "dojo/errors/create", // createError
32377 "dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
32378 "dojo/_base/kernel", // kernel.deprecated
32379 "dojo/keys", // arrows etc.
32380 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
32381 "dojo/on", // on(), on.selector()
32386 "./registry", // registry.byNode(), registry.getEnclosingWidget()
32387 "./_base/manager", // manager.defaultDuration
32389 "./_TemplatedMixin",
32392 "./_CssStateMixin",
32393 "dojo/text!./templates/TreeNode.html",
32394 "dojo/text!./templates/Tree.html",
32395 "./tree/TreeStoreModel",
32396 "./tree/ForestStoreModel",
32397 "./tree/_dndSelector"
32398 ], function(array
, connect
, cookie
, declare
, Deferred
, DeferredList
,
32399 dom
, domClass
, domGeometry
, domStyle
, event
, createError
, fxUtils
, kernel
, keys
, lang
, on
, topic
, touch
, when
,
32400 focus
, registry
, manager
, _Widget
, _TemplatedMixin
, _Container
, _Contained
, _CssStateMixin
,
32401 treeNodeTemplate
, treeTemplate
, TreeStoreModel
, ForestStoreModel
, _dndSelector
){
32406 // Back-compat shim
32407 Deferred
= declare(Deferred
, {
32408 addCallback: function(callback
){ this.then(callback
); },
32409 addErrback: function(errback
){ this.then(null, errback
); }
32412 var TreeNode
= declare(
32414 [_Widget
, _TemplatedMixin
, _Container
, _Contained
, _CssStateMixin
],
32417 // Single node within a tree. This class is used internally
32418 // by Tree and should not be accessed directly.
32422 // item: [const] Item
32423 // the dojo.data entry this tree represents
32426 // isTreeNode: [protected] Boolean
32427 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
32428 // should not be accessed directly.
32432 // Text of this tree node
32434 _setLabelAttr
: {node
: "labelNode", type
: "innerText"},
32436 // isExpandable: [private] Boolean
32437 // This node has children, so show the expando node (+ sign)
32438 isExpandable
: null,
32440 // isExpanded: [readonly] Boolean
32441 // This node is currently expanded (ie, opened)
32444 // state: [private] String
32445 // Dynamic loading-related stuff.
32446 // When an empty folder node appears, it is "UNCHECKED" first,
32447 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
32448 state
: "UNCHECKED",
32450 templateString
: treeNodeTemplate
,
32452 baseClass
: "dijitTreeNode",
32454 // For hover effect for tree node, and focus effect for label
32456 rowNode
: "dijitTreeRow"
32459 // Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
32460 _setTooltipAttr
: {node
: "rowNode", type
: "attribute", attribute
: "title"},
32462 buildRendering: function(){
32463 this.inherited(arguments
);
32465 // set expand icon for leaf
32466 this._setExpando();
32468 // set icon and label class based on item
32469 this._updateItemClasses(this.item
);
32471 if(this.isExpandable
){
32472 this.labelNode
.setAttribute("aria-expanded", this.isExpanded
);
32475 //aria-selected should be false on all selectable elements.
32476 this.setSelected(false);
32479 _setIndentAttr: function(indent
){
32481 // Tell this node how many levels it should be indented
32483 // 0 for top level nodes, 1 for their children, 2 for their
32484 // grandchildren, etc.
32486 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
32487 var pixels
= (Math
.max(indent
, 0) * this.tree
._nodePixelIndent
) + "px";
32489 domStyle
.set(this.domNode
, "backgroundPosition", pixels
+ " 0px"); // TODOC: what is this for???
32490 domStyle
.set(this.indentNode
, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels
);
32492 array
.forEach(this.getChildren(), function(child
){
32493 child
.set("indent", indent
+1);
32496 this._set("indent", indent
);
32499 markProcessing: function(){
32501 // Visually denote that tree is loading data, etc.
32504 this.state
= "LOADING";
32505 this._setExpando(true);
32508 unmarkProcessing: function(){
32510 // Clear markup from markProcessing() call
32513 this._setExpando(false);
32516 _updateItemClasses: function(item
){
32518 // Set appropriate CSS classes for icon and label dom node
32519 // (used to allow for item updates to change respective CSS)
32522 var tree
= this.tree
, model
= tree
.model
;
32523 if(tree
._v10Compat
&& item
=== model
.root
){
32524 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
32527 this._applyClassAndStyle(item
, "icon", "Icon");
32528 this._applyClassAndStyle(item
, "label", "Label");
32529 this._applyClassAndStyle(item
, "row", "Row");
32531 this.tree
._startPaint(true); // signifies paint started and finished (synchronously)
32534 _applyClassAndStyle: function(item
, lower
, upper
){
32536 // Set the appropriate CSS classes and styles for labels, icons and rows.
32542 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
32545 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
32550 var clsName
= "_" + lower
+ "Class";
32551 var nodeName
= lower
+ "Node";
32552 var oldCls
= this[clsName
];
32554 this[clsName
] = this.tree
["get" + upper
+ "Class"](item
, this.isExpanded
);
32555 domClass
.replace(this[nodeName
], this[clsName
] || "", oldCls
|| "");
32557 domStyle
.set(this[nodeName
], this.tree
["get" + upper
+ "Style"](item
, this.isExpanded
) || {});
32560 _updateLayout: function(){
32562 // Set appropriate CSS classes for this.domNode
32565 var parent
= this.getParent();
32566 if(!parent
|| !parent
.rowNode
|| parent
.rowNode
.style
.display
== "none"){
32567 /* if we are hiding the root node then make every first level child look like a root node */
32568 domClass
.add(this.domNode
, "dijitTreeIsRoot");
32570 domClass
.toggle(this.domNode
, "dijitTreeIsLast", !this.getNextSibling());
32574 _setExpando: function(/*Boolean*/ processing
){
32576 // Set the right image for the expando node
32580 var styles
= ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
32581 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
32582 _a11yStates
= ["*","-","+","*"],
32583 idx
= processing
? 0 : (this.isExpandable
? (this.isExpanded
? 1 : 2) : 3);
32585 // apply the appropriate class to the expando node
32586 domClass
.replace(this.expandoNode
, styles
[idx
], styles
);
32588 // provide a non-image based indicator for images-off mode
32589 this.expandoNodeText
.innerHTML
= _a11yStates
[idx
];
32593 expand: function(){
32595 // Show my children
32597 // Deferred that fires when expansion is complete
32599 // If there's already an expand in progress or we are already expanded, just return
32600 if(this._expandDeferred
){
32601 return this._expandDeferred
; // dojo/_base/Deferred
32604 // cancel in progress collapse operation
32605 if(this._collapseDeferred
){
32606 this._collapseDeferred
.cancel();
32607 delete this._collapseDeferred
;
32610 // All the state information for when a node is expanded, maybe this should be
32611 // set when the animation completes instead
32612 this.isExpanded
= true;
32613 this.labelNode
.setAttribute("aria-expanded", "true");
32614 if(this.tree
.showRoot
|| this !== this.tree
.rootNode
){
32615 this.containerNode
.setAttribute("role", "group");
32617 domClass
.add(this.contentNode
,'dijitTreeContentExpanded');
32618 this._setExpando();
32619 this._updateItemClasses(this.item
);
32621 if(this == this.tree
.rootNode
&& this.tree
.showRoot
){
32622 this.tree
.domNode
.setAttribute("aria-expanded", "true");
32626 wipeIn
= fxUtils
.wipeIn({
32627 node
: this.containerNode
,
32628 duration
: manager
.defaultDuration
,
32634 // Deferred that fires when expand is complete
32635 def
= (this._expandDeferred
= new Deferred(function(){
32642 return def
; // dojo/_base/Deferred
32645 collapse: function(){
32647 // Collapse this node (if it's expanded)
32649 if(this._collapseDeferred
){
32650 // Node is already collapsed, or there's a collapse in progress, just return that Deferred
32651 return this._collapseDeferred
;
32654 // cancel in progress expand operation
32655 if(this._expandDeferred
){
32656 this._expandDeferred
.cancel();
32657 delete this._expandDeferred
;
32660 this.isExpanded
= false;
32661 this.labelNode
.setAttribute("aria-expanded", "false");
32662 if(this == this.tree
.rootNode
&& this.tree
.showRoot
){
32663 this.tree
.domNode
.setAttribute("aria-expanded", "false");
32665 domClass
.remove(this.contentNode
,'dijitTreeContentExpanded');
32666 this._setExpando();
32667 this._updateItemClasses(this.item
);
32670 wipeOut
= fxUtils
.wipeOut({
32671 node
: this.containerNode
,
32672 duration
: manager
.defaultDuration
,
32678 // Deferred that fires when expand is complete
32679 def
= (this._collapseDeferred
= new Deferred(function(){
32686 return def
; // dojo/_base/Deferred
32690 // Levels from this node to the root node
32693 setChildItems: function(/* Object[] */ items
){
32695 // Sets the child items of this node, removing/adding nodes
32696 // from current children to match specified items[] array.
32697 // Also, if this.persist == true, expands any children that were previously
32700 // Deferred object that fires after all previously opened children
32701 // have been expanded again (or fires instantly if there are no such children).
32703 var tree
= this.tree
,
32704 model
= tree
.model
,
32705 defs
= []; // list of deferreds that need to fire before I am complete
32708 // Orphan all my existing children.
32709 // If items contains some of the same items as before then we will reattach them.
32710 // Don't call this.removeChild() because that will collapse the tree etc.
32711 var oldChildren
= this.getChildren();
32712 array
.forEach(oldChildren
, function(child
){
32713 _Container
.prototype.removeChild
.call(this, child
);
32716 // All the old children of this TreeNode are subject for destruction if
32717 // 1) they aren't listed in the new children array (items)
32718 // 2) they aren't immediately adopted by another node (DnD)
32719 this.defer(function(){
32720 array
.forEach(oldChildren
, function(node
){
32721 if(!node
._destroyed
&& !node
.getParent()){
32722 // If node is in selection then remove it.
32723 tree
.dndController
.removeTreeNode(node
);
32725 // Deregister mapping from item id --> this node
32726 var id
= model
.getIdentity(node
.item
),
32727 ary
= tree
._itemNodesMap
[id
];
32728 if(ary
.length
== 1){
32729 delete tree
._itemNodesMap
[id
];
32731 var index
= array
.indexOf(ary
, node
);
32733 ary
.splice(index
, 1);
32737 // And finally we can destroy the node
32738 node
.destroyRecursive();
32743 this.state
= "LOADED";
32745 if(items
&& items
.length
> 0){
32746 this.isExpandable
= true;
32748 // Create _TreeNode widget for each specified tree node, unless one already
32749 // exists and isn't being used (presumably it's from a DnD move and was recently
32751 array
.forEach(items
, function(item
){ // MARKER: REUSE NODE
32752 var id
= model
.getIdentity(item
),
32753 existingNodes
= tree
._itemNodesMap
[id
],
32756 for(var i
=0;i
<existingNodes
.length
;i
++){
32757 if(existingNodes
[i
] && !existingNodes
[i
].getParent()){
32758 node
= existingNodes
[i
];
32759 node
.set('indent', this.indent
+1);
32765 node
= this.tree
._createTreeNode({
32768 isExpandable
: model
.mayHaveChildren(item
),
32769 label
: tree
.getLabel(item
),
32770 tooltip
: tree
.getTooltip(item
),
32771 ownerDocument
: tree
.ownerDocument
,
32774 textDir
: tree
.textDir
,
32775 indent
: this.indent
+ 1
32778 existingNodes
.push(node
);
32780 tree
._itemNodesMap
[id
] = [node
];
32783 this.addChild(node
);
32785 // If node was previously opened then open it again now (this may trigger
32786 // more data store accesses, recursively)
32787 if(this.tree
.autoExpand
|| this.tree
._state(node
)){
32788 defs
.push(tree
._expandNode(node
));
32792 // note that updateLayout() needs to be called on each child after
32793 // _all_ the children exist
32794 array
.forEach(this.getChildren(), function(child
){
32795 child
._updateLayout();
32798 this.isExpandable
=false;
32801 if(this._setExpando
){
32802 // change expando to/from dot or + icon, as appropriate
32803 this._setExpando(false);
32806 // Set leaf icon or folder icon, as appropriate
32807 this._updateItemClasses(this.item
);
32809 // On initial tree show, make the selected TreeNode as either the root node of the tree,
32810 // or the first child, if the root node is hidden
32811 if(this == tree
.rootNode
){
32812 var fc
= this.tree
.showRoot
? this : this.getChildren()[0];
32814 fc
.setFocusable(true);
32815 tree
.lastFocused
= fc
;
32817 // fallback: no nodes in tree so focus on Tree <div> itself
32818 tree
.domNode
.setAttribute("tabIndex", "0");
32822 var def
= new DeferredList(defs
);
32823 this.tree
._startPaint(def
); // to reset TreeNode widths after an item is added/removed from the Tree
32824 return def
; // dojo/_base/Deferred
32827 getTreePath: function(){
32830 while(node
&& node
!== this.tree
.rootNode
){
32831 path
.unshift(node
.item
);
32832 node
= node
.getParent();
32834 path
.unshift(this.tree
.rootNode
.item
);
32839 getIdentity: function(){
32840 return this.tree
.model
.getIdentity(this.item
);
32843 removeChild: function(/* treeNode */ node
){
32844 this.inherited(arguments
);
32846 var children
= this.getChildren();
32847 if(children
.length
== 0){
32848 this.isExpandable
= false;
32852 array
.forEach(children
, function(child
){
32853 child
._updateLayout();
32857 makeExpandable: function(){
32859 // if this node wasn't already showing the expando node,
32860 // turn it into one and call _setExpando()
32862 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
32864 this.isExpandable
= true;
32865 this._setExpando(false);
32868 setSelected: function(/*Boolean*/ selected
){
32870 // A Tree has a (single) currently selected node.
32871 // Mark that this node is/isn't that currently selected node.
32873 // In particular, setting a node as selected involves setting tabIndex
32874 // so that when user tabs to the tree, focus will go to that node (only).
32875 this.labelNode
.setAttribute("aria-selected", selected
? "true" : "false");
32876 domClass
.toggle(this.rowNode
, "dijitTreeRowSelected", selected
);
32879 setFocusable: function(/*Boolean*/ selected
){
32881 // A Tree has a (single) node that's focusable.
32882 // Mark that this node is/isn't that currently focsuable node.
32884 // In particular, setting a node as selected involves setting tabIndex
32885 // so that when user tabs to the tree, focus will go to that node (only).
32887 this.labelNode
.setAttribute("tabIndex", selected
? "0" : "-1");
32891 _setTextDirAttr: function(textDir
){
32892 if(textDir
&&((this.textDir
!= textDir
) || !this._created
)){
32893 this._set("textDir", textDir
);
32894 this.applyTextDir(this.labelNode
, this.labelNode
.innerText
|| this.labelNode
.textContent
|| "");
32895 array
.forEach(this.getChildren(), function(childNode
){
32896 childNode
.set("textDir", textDir
);
32902 var Tree
= declare("dijit.Tree", [_Widget
, _TemplatedMixin
], {
32904 // This widget displays hierarchical data from a store.
32906 // store: [deprecated] String|dojo/data/Store
32907 // Deprecated. Use "model" parameter instead.
32908 // The store to get data to display in the tree.
32911 // model: dijit/tree/model
32912 // Interface to read tree data, get notifications of changes to tree data,
32913 // and for handling drop operations (i.e drag and drop onto the tree)
32916 // query: [deprecated] anything
32917 // Deprecated. User should specify query to the model directly instead.
32918 // Specifies datastore query to return the root item or top items for the tree.
32921 // label: [deprecated] String
32922 // Deprecated. Use dijit/tree/ForestStoreModel directly instead.
32923 // Used in conjunction with query parameter.
32924 // If a query is specified (rather than a root node id), and a label is also specified,
32925 // then a fake root node is created and displayed, with this label.
32928 // showRoot: [const] Boolean
32929 // Should the root node be displayed, or hidden?
32932 // childrenAttr: [deprecated] String[]
32933 // Deprecated. This information should be specified in the model.
32934 // One ore more attributes that holds children of a tree node
32935 childrenAttr
: ["children"],
32937 // paths: String[][] or Item[][]
32938 // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
32939 // Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
32940 // returns a Deferred to indicate when the set is complete.
32943 // path: String[] or Item[]
32944 // Backward compatible singular variant of paths.
32947 // selectedItems: [readonly] Item[]
32948 // The currently selected items in this tree.
32949 // This property can only be set (via set('selectedItems', ...)) when that item is already
32950 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
32951 // Should generally use `paths` attribute to set the selected items instead.
32952 selectedItems
: null,
32954 // selectedItem: [readonly] Item
32955 // Backward compatible singular variant of selectedItems.
32956 selectedItem
: null,
32958 // openOnClick: Boolean
32959 // If true, clicking a folder node's label will open it, rather than calling onClick()
32960 openOnClick
: false,
32962 // openOnDblClick: Boolean
32963 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
32964 openOnDblClick
: false,
32966 templateString
: treeTemplate
,
32968 // persist: Boolean
32969 // Enables/disables use of cookies for state saving.
32972 // autoExpand: Boolean
32973 // Fully expand the tree on load. Overrides `persist`.
32976 // dndController: [protected] Function|String
32977 // Class to use as as the dnd controller. Specifying this class enables DnD.
32978 // Generally you should specify this as dijit/tree/dndSource.
32979 // Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
32980 dndController
: _dndSelector
,
32982 // parameters to pull off of the tree and pass on to the dndController as its params
32983 dndParams
: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
32985 //declare the above items so they can be pulled from the tree's markup
32987 // onDndDrop: [protected] Function
32988 // Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
32989 // Generally this doesn't need to be set.
32994 itemCreator: function(nodes, target, source){
32996 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
32997 // dropped onto the tree. Developer must override this method to enable
32998 // dropping from external sources onto this Tree, unless the Tree.model's items
32999 // happen to look like {id: 123, name: "Apple" } with no other attributes.
33001 // For each node in nodes[], which came from source, create a hash of name/value
33002 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
33003 // nodes: DomNode[]
33004 // The DOMNodes dragged from the source container
33006 // The target TreeNode.rowNode
33007 // source: dojo/dnd/Source
33008 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
33009 // returns: Object[]
33010 // Array of name/value hashes for each new item to be added to the Tree, like:
33012 // | { id: 123, label: "apple", foo: "bar" },
33013 // | { id: 456, label: "pear", zaz: "bam" }
33021 // onDndCancel: [protected] Function
33022 // Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
33023 // Generally this doesn't need to be set.
33027 checkAcceptance: function(source, nodes){
33029 // Checks if the Tree itself can accept nodes from this source
33030 // source: dijit/tree/dndSource
33031 // The source which provides items
33032 // nodes: DOMNode[]
33033 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
33034 // source is a dijit/Tree.
33037 return true; // Boolean
33040 checkAcceptance
: null,
33043 checkItemAcceptance: function(target, source, position){
33045 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
33047 // In the base case, this is called to check if target can become a child of source.
33048 // When betweenThreshold is set, position="before" or "after" means that we
33049 // are asking if the source node can be dropped before/after the target node.
33051 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
33052 // Use registry.getEnclosingWidget(target) to get the TreeNode.
33053 // source: dijit/tree/dndSource
33054 // The (set of) nodes we are dropping
33055 // position: String
33056 // "over", "before", or "after"
33059 return true; // Boolean
33062 checkItemAcceptance
: null,
33064 // dragThreshold: Integer
33065 // Number of pixels mouse moves before it's considered the start of a drag operation
33068 // betweenThreshold: Integer
33069 // Set to a positive value to allow drag and drop "between" nodes.
33071 // If during DnD mouse is over a (target) node but less than betweenThreshold
33072 // pixels from the bottom edge, dropping the the dragged node will make it
33073 // the next sibling of the target node, rather than the child.
33075 // Similarly, if mouse is over a target node but less that betweenThreshold
33076 // pixels from the top edge, dropping the dragged node will make it
33077 // the target node's previous sibling rather than the target node's child.
33078 betweenThreshold
: 0,
33080 // _nodePixelIndent: Integer
33081 // Number of pixels to indent tree nodes (relative to parent node).
33082 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
33083 // and calling resize() or startup() on tree after it's in the DOM.
33084 _nodePixelIndent
: 19,
33086 _publish: function(/*String*/ topicName
, /*Object*/ message
){
33088 // Publish a message for this widget/topic
33089 topic
.publish(this.id
, lang
.mixin({tree
: this, event
: topicName
}, message
|| {})); // publish
33092 postMixInProperties: function(){
33095 if(this.autoExpand
){
33096 // There's little point in saving opened/closed state of nodes for a Tree
33097 // that initially opens all it's nodes.
33098 this.persist
= false;
33101 this._itemNodesMap
= {};
33103 if(!this.cookieName
&& this.id
){
33104 this.cookieName
= this.id
+ "SaveStateCookie";
33107 // Deferred that fires when all the children have loaded.
33108 this.expandChildrenDeferred
= new Deferred();
33110 // Deferred that fires when all pending operations complete.
33111 this.pendingCommandsDeferred
= this.expandChildrenDeferred
;
33113 this.inherited(arguments
);
33116 postCreate: function(){
33119 // Catch events on TreeNodes
33122 on(this.domNode
, on
.selector(".dijitTreeNode", touch
.enter
), function(evt
){
33123 self
._onNodeMouseEnter(registry
.byNode(this), evt
);
33125 on(this.domNode
, on
.selector(".dijitTreeNode", touch
.leave
), function(evt
){
33126 self
._onNodeMouseLeave(registry
.byNode(this), evt
);
33128 on(this.domNode
, on
.selector(".dijitTreeNode", "click"), function(evt
){
33129 self
._onClick(registry
.byNode(this), evt
);
33131 on(this.domNode
, on
.selector(".dijitTreeNode", "dblclick"), function(evt
){
33132 self
._onDblClick(registry
.byNode(this), evt
);
33134 on(this.domNode
, on
.selector(".dijitTreeNode", "keypress"), function(evt
){
33135 self
._onKeyPress(registry
.byNode(this), evt
);
33137 on(this.domNode
, on
.selector(".dijitTreeNode", "keydown"), function(evt
){
33138 self
._onKeyDown(registry
.byNode(this), evt
);
33140 on(this.domNode
, on
.selector(".dijitTreeRow", "focusin"), function(evt
){
33141 self
._onNodeFocus(registry
.getEnclosingWidget(this), evt
);
33145 // Create glue between store and Tree, if not specified directly by user
33147 this._store2model();
33150 // monitor changes to items
33151 this.connect(this.model
, "onChange", "_onItemChange");
33152 this.connect(this.model
, "onChildrenChange", "_onItemChildrenChange");
33153 this.connect(this.model
, "onDelete", "_onItemDelete");
33155 this.inherited(arguments
);
33157 if(this.dndController
){
33158 if(lang
.isString(this.dndController
)){
33159 this.dndController
= lang
.getObject(this.dndController
);
33162 for(var i
=0; i
<this.dndParams
.length
;i
++){
33163 if(this[this.dndParams
[i
]]){
33164 params
[this.dndParams
[i
]] = this[this.dndParams
[i
]];
33167 this.dndController
= new this.dndController(this, params
);
33172 // If no path was specified to the constructor, use path saved in cookie
33173 if(!this.params
.path
&& !this.params
.paths
&& this.persist
){
33174 this.set("paths", this.dndController
._getSavedPaths());
33177 // onLoadDeferred should fire when all commands that are part of initialization have completed.
33178 // It will include all the set("paths", ...) commands that happen during initialization.
33179 this.onLoadDeferred
= this.pendingCommandsDeferred
;
33181 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
33184 _store2model: function(){
33186 // User specified a store&query rather than model, so create model from store/query
33187 this._v10Compat
= true;
33188 kernel
.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
33190 var modelParams
= {
33191 id
: this.id
+ "_ForestStoreModel",
33194 childrenAttrs
: this.childrenAttr
33197 // Only override the model's mayHaveChildren() method if the user has specified an override
33198 if(this.params
.mayHaveChildren
){
33199 modelParams
.mayHaveChildren
= lang
.hitch(this, "mayHaveChildren");
33202 if(this.params
.getItemChildren
){
33203 modelParams
.getChildren
= lang
.hitch(this, function(item
, onComplete
, onError
){
33204 this.getItemChildren((this._v10Compat
&& item
=== this.model
.root
) ? null : item
, onComplete
, onError
);
33207 this.model
= new ForestStoreModel(modelParams
);
33209 // For backwards compatibility, the visibility of the root node is controlled by
33210 // whether or not the user has specified a label
33211 this.showRoot
= Boolean(this.label
);
33214 onLoad: function(){
33216 // Called when tree finishes loading and expanding.
33218 // If persist == true the loading may encompass many levels of fetches
33219 // from the data store, each asynchronous. Waits for all to finish.
33226 // Initial load of the tree.
33227 // Load root node (possibly hidden) and it's children.
33228 this.model
.getRoot(
33229 lang
.hitch(this, function(item
){
33230 var rn
= (this.rootNode
= this.tree
._createTreeNode({
33233 isExpandable
: true,
33234 label
: this.label
|| this.getLabel(item
),
33235 textDir
: this.textDir
,
33236 indent
: this.showRoot
? 0 : -1
33239 if(!this.showRoot
){
33240 rn
.rowNode
.style
.display
="none";
33241 // if root is not visible, move tree role to the invisible
33242 // root node's containerNode, see #12135
33243 this.domNode
.setAttribute("role", "presentation");
33244 this.domNode
.removeAttribute("aria-expanded");
33245 this.domNode
.removeAttribute("aria-multiselectable");
33247 rn
.labelNode
.setAttribute("role", "presentation");
33248 rn
.containerNode
.setAttribute("role", "tree");
33249 rn
.containerNode
.setAttribute("aria-expanded","true");
33250 rn
.containerNode
.setAttribute("aria-multiselectable", !this.dndController
.singular
);
33252 this.domNode
.setAttribute("aria-multiselectable", !this.dndController
.singular
);
33255 this.domNode
.appendChild(rn
.domNode
);
33256 var identity
= this.model
.getIdentity(item
);
33257 if(this._itemNodesMap
[identity
]){
33258 this._itemNodesMap
[identity
].push(rn
);
33260 this._itemNodesMap
[identity
] = [rn
];
33263 rn
._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
33265 // Load top level children, and if persist==true, all nodes that were previously opened
33266 this._expandNode(rn
).then(lang
.hitch(this, function(){
33267 // Then, select the nodes that were selected last time, or
33268 // the ones specified by params.paths[].
33270 this.expandChildrenDeferred
.resolve(true);
33273 lang
.hitch(this, function(err
){
33274 console
.error(this, ": error loading root: ", err
);
33279 getNodesByItem: function(/*Item or id*/ item
){
33281 // Returns all tree nodes that refer to an item
33283 // Array of tree nodes that refer to passed item
33285 if(!item
){ return []; }
33286 var identity
= lang
.isString(item
) ? item
: this.model
.getIdentity(item
);
33287 // return a copy so widget don't get messed up by changes to returned array
33288 return [].concat(this._itemNodesMap
[identity
]);
33291 _setSelectedItemAttr: function(/*Item or id*/ item
){
33292 this.set('selectedItems', [item
]);
33295 _setSelectedItemsAttr: function(/*Items or ids*/ items
){
33297 // Select tree nodes related to passed items.
33298 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
33299 // behavior is undefined. Use set('paths', ...) instead.
33301 return this.pendingCommandsDeferred
= this.pendingCommandsDeferred
.then( lang
.hitch(this, function(){
33302 var identities
= array
.map(items
, function(item
){
33303 return (!item
|| lang
.isString(item
)) ? item
: tree
.model
.getIdentity(item
);
33306 array
.forEach(identities
, function(id
){
33307 nodes
= nodes
.concat(tree
._itemNodesMap
[id
] || []);
33309 this.set('selectedNodes', nodes
);
33313 _setPathAttr: function(/*Item[]|String[]*/ path
){
33315 // Singular variant of _setPathsAttr
33317 return this.set("paths", [path
]);
33319 // Empty list is interpreted as "select nothing"
33320 return this.set("paths", []);
33324 _setPathsAttr: function(/*Item[][]|String[][]*/ paths
){
33326 // Select the tree nodes identified by passed paths.
33328 // Array of arrays of items or item id's
33330 // Deferred to indicate when the set is complete
33334 // Let any previous set("path", ...) commands complete before this one starts.
33335 return this.pendingCommandsDeferred
= this.pendingCommandsDeferred
.then(function(){
33336 // We may need to wait for some nodes to expand, so setting
33337 // each path will involve a Deferred. We bring those deferreds
33338 // together with a DeferredList.
33339 return new DeferredList(array
.map(paths
, function(path
){
33340 var d
= new Deferred();
33342 // normalize path to use identity
33343 path
= array
.map(path
, function(item
){
33344 return lang
.isString(item
) ? item
: tree
.model
.getIdentity(item
);
33348 // Wait for the tree to load, if it hasn't already.
33349 selectPath(path
, [tree
.rootNode
], d
);
33351 d
.reject(new Tree
.PathError("Empty path"));
33357 function selectPath(path
, nodes
, def
){
33358 // Traverse path; the next path component should be among "nodes".
33359 var nextPath
= path
.shift();
33360 var nextNode
= array
.filter(nodes
, function(node
){
33361 return node
.getIdentity() == nextPath
;
33365 tree
._expandNode(nextNode
).then(function(){ selectPath(path
, nextNode
.getChildren(), def
); });
33367 // Successfully reached the end of this path
33368 def
.resolve(nextNode
);
33371 def
.reject(new Tree
.PathError("Could not expand path at " + nextPath
));
33375 function setNodes(newNodes
){
33376 // After all expansion is finished, set the selection to
33377 // the set of nodes successfully found.
33378 tree
.set("selectedNodes", array
.map(
33379 array
.filter(newNodes
,function(x
){return x
[0];}),
33380 function(x
){return x
[1];}));
33384 _setSelectedNodeAttr: function(node
){
33385 this.set('selectedNodes', [node
]);
33387 _setSelectedNodesAttr: function(nodes
){
33389 // Marks the specified TreeNodes as selected.
33390 // nodes: TreeNode[]
33391 // TreeNodes to mark.
33392 this.dndController
.setSelection(nodes
);
33396 expandAll: function(){
33398 // Expand all nodes in the tree
33400 // Deferred that fires when all nodes have expanded
33404 function expand(node
){
33405 var def
= new dojo
.Deferred();
33408 _this
._expandNode(node
).then(function(){
33409 // When node has expanded, call expand() recursively on each non-leaf child
33410 var childBranches
= array
.filter(node
.getChildren() || [], function(node
){
33411 return node
.isExpandable
;
33413 defs
= array
.map(childBranches
, expand
);
33415 // And when all those recursive calls finish, signal that I'm finished
33416 new dojo
.DeferredList(defs
).then(function(){
33424 return expand(this.rootNode
);
33427 collapseAll: function(){
33429 // Collapse all nodes in the tree
33431 // Deferred that fires when all nodes have collapsed
33435 function collapse(node
){
33436 var def
= new dojo
.Deferred();
33437 def
.label
= "collapseAllDeferred";
33439 // Collapse children first
33440 var childBranches
= array
.filter(node
.getChildren() || [], function(node
){
33441 return node
.isExpandable
;
33443 defs
= array
.map(childBranches
, collapse
);
33445 // And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
33446 // in which case collapseAll() is finished
33447 new dojo
.DeferredList(defs
).then(function(){
33448 if(!node
.isExpanded
|| (node
== _this
.rootNode
&& !_this
.showRoot
)){
33451 _this
._collapseNode(node
).then(function(){
33452 // When node has collapsed, signal that call is finished
33462 return collapse(this.rootNode
);
33465 ////////////// Data store related functions //////////////////////
33466 // These just get passed to the model; they are here for back-compat
33468 mayHaveChildren: function(/*dojo/data/Item*/ /*===== item =====*/){
33470 // Deprecated. This should be specified on the model itself.
33472 // Overridable function to tell if an item has or may have children.
33473 // Controls whether or not +/- expando icon is shown.
33474 // (For efficiency reasons we may not want to check if an element actually
33475 // has children until user clicks the expando node)
33480 getItemChildren: function(/*===== parentItem, onComplete =====*/){
33482 // Deprecated. This should be specified on the model itself.
33484 // Overridable function that return array of child items of given parent item,
33485 // or if parentItem==null then return top items in tree
33490 ///////////////////////////////////////////////////////
33491 // Functions for converting an item to a TreeNode
33492 getLabel: function(/*dojo/data/Item*/ item
){
33494 // Overridable function to get the label for a tree node (given the item)
33497 return this.model
.getLabel(item
); // String
33500 getIconClass: function(/*dojo/data/Item*/ item
, /*Boolean*/ opened
){
33502 // Overridable function to return CSS class name to display icon
33505 return (!item
|| this.model
.mayHaveChildren(item
)) ? (opened
? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
33508 getLabelClass: function(/*===== item, opened =====*/){
33510 // Overridable function to return CSS class name to display label
33511 // item: dojo/data/Item
33519 getRowClass: function(/*===== item, opened =====*/){
33521 // Overridable function to return CSS class name to display row
33522 // item: dojo/data/Item
33530 getIconStyle: function(/*===== item, opened =====*/){
33532 // Overridable function to return CSS styles to display icon
33533 // item: dojo/data/Item
33536 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
33541 getLabelStyle: function(/*===== item, opened =====*/){
33543 // Overridable function to return CSS styles to display label
33544 // item: dojo/data/Item
33547 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
33552 getRowStyle: function(/*===== item, opened =====*/){
33554 // Overridable function to return CSS styles to display row
33555 // item: dojo/data/Item
33558 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
33563 getTooltip: function(/*dojo/data/Item*/ /*===== item =====*/){
33565 // Overridable function to get the tooltip for a tree node (given the item)
33568 return ""; // String
33571 /////////// Keyboard and Mouse handlers ////////////////////
33573 _onKeyPress: function(/*TreeNode*/ treeNode
, /*Event*/ e
){
33575 // Handles keystrokes for printable keys, doing search navigation
33577 if(e
.charCode
<= 32){
33578 // Avoid duplicate events on firefox (this is an arrow key that will be handled by keydown handler)
33582 if(!e
.altKey
&& !e
.ctrlKey
&& !e
.shiftKey
&& !e
.metaKey
){
33583 var c
= String
.fromCharCode(e
.charCode
);
33584 this._onLetterKeyNav( { node
: treeNode
, key
: c
.toLowerCase() } );
33589 _onKeyDown: function(/*TreeNode*/ treeNode
, /*Event*/ e
){
33591 // Handles arrow, space, and enter keys
33593 var key
= e
.keyCode
;
33595 var map
= this._keyHandlerMap
;
33597 // Setup table mapping keys to events.
33598 // On WebKit based browsers, the combination ctrl-enter does not get passed through. To allow accessible
33599 // multi-select on those browsers, the space key is also used for selection.
33600 // Therefore, also allow space key for keyboard "click" operation.
33602 map
[keys
.ENTER
] = map
[keys
.SPACE
] = map
[" "] = "_onEnterKey";
33603 map
[this.isLeftToRight() ? keys
.LEFT_ARROW
: keys
.RIGHT_ARROW
] = "_onLeftArrow";
33604 map
[this.isLeftToRight() ? keys
.RIGHT_ARROW
: keys
.LEFT_ARROW
] = "_onRightArrow";
33605 map
[keys
.UP_ARROW
] = "_onUpArrow";
33606 map
[keys
.DOWN_ARROW
] = "_onDownArrow";
33607 map
[keys
.HOME
] = "_onHomeKey";
33608 map
[keys
.END
] = "_onEndKey";
33609 this._keyHandlerMap
= map
;
33612 if(this._keyHandlerMap
[key
]){
33613 // clear record of recent printables (being saved for multi-char letter navigation),
33614 // because "a", down-arrow, "b" shouldn't search for "ab"
33615 if(this._curSearch
){
33616 this._curSearch
.timer
.remove();
33617 delete this._curSearch
;
33620 this[this._keyHandlerMap
[key
]]( { node
: treeNode
, item
: treeNode
.item
, evt
: e
} );
33625 _onEnterKey: function(/*Object*/ message
){
33626 this._publish("execute", { item
: message
.item
, node
: message
.node
} );
33627 this.dndController
.userSelect(message
.node
, connect
.isCopyKey( message
.evt
), message
.evt
.shiftKey
);
33628 this.onClick(message
.item
, message
.node
, message
.evt
);
33631 _onDownArrow: function(/*Object*/ message
){
33633 // down arrow pressed; get next visible node, set focus there
33634 var node
= this._getNextNode(message
.node
);
33635 if(node
&& node
.isTreeNode
){
33636 this.focusNode(node
);
33640 _onUpArrow: function(/*Object*/ message
){
33642 // Up arrow pressed; move to previous visible node
33644 var node
= message
.node
;
33646 // if younger siblings
33647 var previousSibling
= node
.getPreviousSibling();
33648 if(previousSibling
){
33649 node
= previousSibling
;
33650 // if the previous node is expanded, dive in deep
33651 while(node
.isExpandable
&& node
.isExpanded
&& node
.hasChildren()){
33652 // move to the last child
33653 var children
= node
.getChildren();
33654 node
= children
[children
.length
-1];
33657 // if this is the first child, return the parent
33658 // unless the parent is the root of a tree with a hidden root
33659 var parent
= node
.getParent();
33660 if(!(!this.showRoot
&& parent
=== this.rootNode
)){
33665 if(node
&& node
.isTreeNode
){
33666 this.focusNode(node
);
33670 _onRightArrow: function(/*Object*/ message
){
33672 // Right arrow pressed; go to child node
33673 var node
= message
.node
;
33675 // if not expanded, expand, else move to 1st child
33676 if(node
.isExpandable
&& !node
.isExpanded
){
33677 this._expandNode(node
);
33678 }else if(node
.hasChildren()){
33679 node
= node
.getChildren()[0];
33680 if(node
&& node
.isTreeNode
){
33681 this.focusNode(node
);
33686 _onLeftArrow: function(/*Object*/ message
){
33688 // Left arrow pressed.
33689 // If not collapsed, collapse, else move to parent.
33691 var node
= message
.node
;
33693 if(node
.isExpandable
&& node
.isExpanded
){
33694 this._collapseNode(node
);
33696 var parent
= node
.getParent();
33697 if(parent
&& parent
.isTreeNode
&& !(!this.showRoot
&& parent
=== this.rootNode
)){
33698 this.focusNode(parent
);
33703 _onHomeKey: function(){
33705 // Home key pressed; get first visible node, and set focus there
33706 var node
= this._getRootOrFirstNode();
33708 this.focusNode(node
);
33712 _onEndKey: function(){
33714 // End key pressed; go to last visible node.
33716 var node
= this.rootNode
;
33717 while(node
.isExpanded
){
33718 var c
= node
.getChildren();
33719 node
= c
[c
.length
- 1];
33722 if(node
&& node
.isTreeNode
){
33723 this.focusNode(node
);
33727 // multiCharSearchDuration: Number
33728 // If multiple characters are typed where each keystroke happens within
33729 // multiCharSearchDuration of the previous keystroke,
33730 // search for nodes matching all the keystrokes.
33732 // For example, typing "ab" will search for entries starting with
33733 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
33734 multiCharSearchDuration
: 250,
33736 _onLetterKeyNav: function(message
){
33738 // Called when user presses a prinatable key; search for node starting with recently typed letters.
33740 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
33742 // Branch depending on whether this key starts a new search, or modifies an existing search
33743 var cs
= this._curSearch
;
33745 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
33746 // 'b', so we want to search for nodes starting w/"ab".
33747 cs
.pattern
= cs
.pattern
+ message
.key
;
33750 // We are starting a new search
33751 cs
= this._curSearch
= {
33752 pattern
: message
.key
,
33753 startNode
: message
.node
33757 // set/reset timer to forget recent keystrokes
33758 cs
.timer
= this.defer(function(){
33759 delete this._curSearch
;
33760 }, this.multiCharSearchDuration
);
33762 // Navigate to TreeNode matching keystrokes [entered so far].
33763 var node
= cs
.startNode
;
33765 node
= this._getNextNode(node
);
33766 //check for last node, jump to first node if necessary
33768 node
= this._getRootOrFirstNode();
33770 }while(node
!== cs
.startNode
&& (node
.label
.toLowerCase().substr(0, cs
.pattern
.length
) != cs
.pattern
));
33771 if(node
&& node
.isTreeNode
){
33772 // no need to set focus if back where we started
33773 if(node
!== cs
.startNode
){
33774 this.focusNode(node
);
33779 isExpandoNode: function(node
, widget
){
33781 // check whether a dom node is the expandoNode for a particular TreeNode widget
33782 return dom
.isDescendant(node
, widget
.expandoNode
) || dom
.isDescendant(node
, widget
.expandoNodeText
);
33785 _onClick: function(/*TreeNode*/ nodeWidget
, /*Event*/ e
){
33787 // Translates click events into commands for the controller to process
33789 var domElement
= e
.target
,
33790 isExpandoClick
= this.isExpandoNode(domElement
, nodeWidget
);
33792 if( (this.openOnClick
&& nodeWidget
.isExpandable
) || isExpandoClick
){
33793 // expando node was clicked, or label of a folder node was clicked; open it
33794 if(nodeWidget
.isExpandable
){
33795 this._onExpandoClick({node
:nodeWidget
});
33798 this._publish("execute", { item
: nodeWidget
.item
, node
: nodeWidget
, evt
: e
} );
33799 this.onClick(nodeWidget
.item
, nodeWidget
, e
);
33800 this.focusNode(nodeWidget
);
33804 _onDblClick: function(/*TreeNode*/ nodeWidget
, /*Event*/ e
){
33806 // Translates double-click events into commands for the controller to process
33808 var domElement
= e
.target
,
33809 isExpandoClick
= (domElement
== nodeWidget
.expandoNode
|| domElement
== nodeWidget
.expandoNodeText
);
33811 if( (this.openOnDblClick
&& nodeWidget
.isExpandable
) ||isExpandoClick
){
33812 // expando node was clicked, or label of a folder node was clicked; open it
33813 if(nodeWidget
.isExpandable
){
33814 this._onExpandoClick({node
:nodeWidget
});
33817 this._publish("execute", { item
: nodeWidget
.item
, node
: nodeWidget
, evt
: e
} );
33818 this.onDblClick(nodeWidget
.item
, nodeWidget
, e
);
33819 this.focusNode(nodeWidget
);
33824 _onExpandoClick: function(/*Object*/ message
){
33826 // User clicked the +/- icon; expand or collapse my children.
33827 var node
= message
.node
;
33829 // If we are collapsing, we might be hiding the currently focused node.
33830 // Also, clicking the expando node might have erased focus from the current node.
33831 // For simplicity's sake just focus on the node with the expando.
33832 this.focusNode(node
);
33834 if(node
.isExpanded
){
33835 this._collapseNode(node
);
33837 this._expandNode(node
);
33841 onClick: function(/*===== item, node, evt =====*/){
33843 // Callback when a tree node is clicked
33845 // Object from the dojo/store corresponding to this TreeNode
33847 // The TreeNode itself
33853 onDblClick: function(/*===== item, node, evt =====*/){
33855 // Callback when a tree node is double-clicked
33857 // Object from the dojo/store corresponding to this TreeNode
33859 // The TreeNode itself
33865 onOpen: function(/*===== item, node =====*/){
33867 // Callback when a node is opened
33868 // item: dojo/data/Item
33873 onClose: function(/*===== item, node =====*/){
33875 // Callback when a node is closed
33877 // Object from the dojo/store corresponding to this TreeNode
33879 // The TreeNode itself
33884 _getNextNode: function(node
){
33886 // Get next visible node
33888 if(node
.isExpandable
&& node
.isExpanded
&& node
.hasChildren()){
33889 // if this is an expanded node, get the first child
33890 return node
.getChildren()[0]; // TreeNode
33892 // find a parent node with a sibling
33893 while(node
&& node
.isTreeNode
){
33894 var returnNode
= node
.getNextSibling();
33896 return returnNode
; // TreeNode
33898 node
= node
.getParent();
33904 _getRootOrFirstNode: function(){
33906 // Get first visible node
33907 return this.showRoot
? this.rootNode
: this.rootNode
.getChildren()[0];
33910 _collapseNode: function(/*TreeNode*/ node
){
33912 // Called when the user has requested to collapse the node
33914 // Deferred that fires when the node is closed
33916 if(node
._expandNodeDeferred
){
33917 delete node
._expandNodeDeferred
;
33920 if(node
.state
== "LOADING"){
33921 // ignore clicks while we are in the process of loading data
33925 if(node
.isExpanded
){
33926 var ret
= node
.collapse();
33928 this.onClose(node
.item
, node
);
33929 this._state(node
, false);
33931 this._startPaint(ret
); // after this finishes, need to reset widths of TreeNodes
33937 _expandNode: function(/*TreeNode*/ node
){
33939 // Called when the user has requested to expand the node
33941 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
33942 // that were previously opened too
33944 // Signal that this call is complete
33945 var def
= new Deferred();
33947 if(node
._expandNodeDeferred
){
33948 // there's already an expand in progress, or completed, so just return
33949 return node
._expandNodeDeferred
; // dojo/_base/Deferred
33952 var model
= this.model
,
33956 // Load data if it's not already loaded
33957 if(!node
._loadDeferred
){
33958 // need to load all the children before expanding
33959 node
.markProcessing();
33961 // Setup deferred to signal when the load and expand are finished.
33962 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
33963 node
._loadDeferred
= new Deferred();
33965 // Get the children
33969 node
.unmarkProcessing();
33971 // Display the children and also start expanding any children that were previously expanded
33972 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
33973 node
.setChildItems(items
).then(function(){
33974 node
._loadDeferred
.resolve(items
);
33978 console
.error(_this
, ": error loading " + node
.label
+ " children: ", err
);
33979 node
._loadDeferred
.reject(err
);
33984 // Expand the node after data has loaded
33985 node
._loadDeferred
.then(lang
.hitch(this, function(){
33986 node
.expand().then(function(){
33987 def
.resolve(true); // signal that this _expandNode() call is complete
33990 // seems like these should be inside of then(), but left here for back-compat about
33991 // when this.isOpen flag gets set (ie, at the beginning of the animation)
33992 this.onOpen(node
.item
, node
);
33993 this._state(node
, true);
33996 this._startPaint(def
); // after this finishes, need to reset widths of TreeNodes
33998 return def
; // dojo/_base/Deferred
34001 ////////////////// Miscellaneous functions ////////////////
34003 focusNode: function(/* _tree.Node */ node
){
34005 // Focus on the specified node (which must be visible)
34009 // set focus so that the label will be voiced using screen readers
34010 focus
.focus(node
.labelNode
);
34013 _onNodeFocus: function(/*dijit/_WidgetBase*/ node){
34015 // Called when a TreeNode gets focus, either by user clicking
34016 // it, or programatically by arrow key handling code.
34018 // It marks that the current node is the selected one, and the previously
34019 // selected node no longer is.
34021 if(node && node != this.lastFocused){
34022 if(this.lastFocused && !this.lastFocused._destroyed){
34023 // mark that the previously focsable node is no longer focusable
34024 this.lastFocused.setFocusable(false);
34027 // mark that the new node is the currently selected one
34028 node.setFocusable(true);
34029 this.lastFocused = node;
34033 _onNodeMouseEnter: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
34035 // Called when mouse is over a node (onmouseenter event),
34036 // this is monitored by the DND code
34039 _onNodeMouseLeave: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
34041 // Called when mouse leaves a node (onmouseleave event),
34042 // this is monitored by the DND code
34045 //////////////// Events from the model //////////////////////////
34047 _onItemChange: function(/*Item*/ item
){
34049 // Processes notification of a change to an item's scalar values like label
34050 var model
= this.model
,
34051 identity
= model
.getIdentity(item
),
34052 nodes
= this._itemNodesMap
[identity
];
34055 var label
= this.getLabel(item
),
34056 tooltip
= this.getTooltip(item
);
34057 array
.forEach(nodes
, function(node
){
34059 item
: item
, // theoretically could be new JS Object representing same item
34063 node
._updateItemClasses(item
);
34068 _onItemChildrenChange: function(/*dojo/data/Item*/ parent
, /*dojo/data/Item[]*/ newChildrenList
){
34070 // Processes notification of a change to an item's children
34071 var model
= this.model
,
34072 identity
= model
.getIdentity(parent
),
34073 parentNodes
= this._itemNodesMap
[identity
];
34076 array
.forEach(parentNodes
,function(parentNode
){
34077 parentNode
.setChildItems(newChildrenList
);
34082 _onItemDelete: function(/*Item*/ item
){
34084 // Processes notification of a deletion of an item.
34085 // Not called from new dojo.store interface but there's cleanup code in setChildItems() instead.
34087 var model
= this.model
,
34088 identity
= model
.getIdentity(item
),
34089 nodes
= this._itemNodesMap
[identity
];
34092 array
.forEach(nodes
,function(node
){
34093 // Remove node from set of selected nodes (if it's selected)
34094 this.dndController
.removeTreeNode(node
);
34096 var parent
= node
.getParent();
34098 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
34099 parent
.removeChild(node
);
34101 node
.destroyRecursive();
34103 delete this._itemNodesMap
[identity
];
34107 /////////////// Miscellaneous funcs
34109 _initState: function(){
34111 // Load in which nodes should be opened automatically
34112 this._openedNodes
= {};
34113 if(this.persist
&& this.cookieName
){
34114 var oreo
= cookie(this.cookieName
);
34116 array
.forEach(oreo
.split(','), function(item
){
34117 this._openedNodes
[item
] = true;
34122 _state: function(node
, expanded
){
34124 // Query or set expanded state for an node
34128 var path
= array
.map(node
.getTreePath(), function(item
){
34129 return this.model
.getIdentity(item
);
34130 }, this).join("/");
34131 if(arguments
.length
=== 1){
34132 return this._openedNodes
[path
];
34135 this._openedNodes
[path
] = true;
34137 delete this._openedNodes
[path
];
34139 if(this.persist
&& this.cookieName
){
34141 for(var id
in this._openedNodes
){
34144 cookie(this.cookieName
, ary
.join(","), {expires
:365});
34149 destroy: function(){
34150 if(this._curSearch
){
34151 this._curSearch
.timer
.remove();
34152 delete this._curSearch
;
34155 this.rootNode
.destroyRecursive();
34157 if(this.dndController
&& !lang
.isString(this.dndController
)){
34158 this.dndController
.destroy();
34160 this.rootNode
= null;
34161 this.inherited(arguments
);
34164 destroyRecursive: function(){
34165 // A tree is treated as a leaf, not as a node with children (like a grid),
34166 // but defining destroyRecursive for back-compat.
34170 resize: function(changeSize
){
34172 domGeometry
.setMarginBox(this.domNode
, changeSize
);
34175 // The main JS sizing involved w/tree is the indentation, which is specified
34176 // in CSS and read in through this dummy indentDetector node (tree must be
34177 // visible and attached to the DOM to read this).
34178 // If the Tree is hidden domGeometry.position(this.tree.indentDetector).w will return 0, in which case just
34179 // keep the default value.
34180 this._nodePixelIndent
= domGeometry
.position(this.tree
.indentDetector
).w
|| this._nodePixelIndent
;
34182 // resize() may be called before this.rootNode is created, so wait until it's available
34183 this.expandChildrenDeferred
.then(lang
.hitch(this, function(){
34184 // If tree has already loaded, then reset indent for all the nodes
34185 this.rootNode
.set('indent', this.showRoot
? 0 : -1);
34187 // Also, adjust widths of all rows to match width of Tree
34188 this._adjustWidths();
34192 _outstandingPaintOperations
: 0,
34193 _startPaint: function(/*Promise|Boolean*/ p
){
34195 // Called at the start of an operation that will change what's displayed.
34197 // Promise that tells when the operation will complete. Alternately, if it's just a Boolean, it signifies
34198 // that the operation was synchronous, and already completed.
34200 this._outstandingPaintOperations
++;
34201 if(this._adjustWidthsTimer
){
34202 this._adjustWidthsTimer
.remove();
34203 delete this._adjustWidthsTimer
;
34206 var oc
= lang
.hitch(this, function(){
34207 this._outstandingPaintOperations
--;
34209 if(this._outstandingPaintOperations
<= 0 && !this._adjustWidthsTimer
&& this._started
){
34210 // Use defer() to avoid a width adjustment when another operation will immediately follow,
34211 // such as a sequence of opening a node, then it's children, then it's grandchildren, etc.
34212 this._adjustWidthsTimer
= this.defer("_adjustWidths");
34218 _adjustWidths: function(){
34220 // Get width of widest TreeNode, or the width of the Tree itself, whichever is greater,
34221 // and then set all TreeNodes to that width, so that selection/hover highlighting
34222 // extends to the edge of the Tree (#13141)
34224 if(this._adjustWidthsTimer
){
34225 this._adjustWidthsTimer
.remove();
34226 delete this._adjustWidthsTimer
;
34231 function collect(/*TreeNode*/ parent
){
34232 var node
= parent
.rowNode
;
34233 node
.style
.width
= "auto"; // erase setting from previous run
34234 maxWidth
= Math
.max(maxWidth
, node
.clientWidth
);
34236 if(parent
.isExpanded
){
34237 array
.forEach(parent
.getChildren(), collect
);
34240 collect(this.rootNode
);
34241 maxWidth
= Math
.max(maxWidth
, domGeometry
.getContentBox(this.domNode
).w
); // do after node.style.width="auto"
34242 array
.forEach(nodes
, function(node
){
34243 node
.style
.width
= maxWidth
+ "px"; // assumes no horizontal padding, border, or margin on rowNode
34247 _createTreeNode: function(/*Object*/ args
){
34249 // creates a TreeNode
34251 // Developers can override this method to define their own TreeNode class;
34252 // However it will probably be removed in a future release in favor of a way
34253 // of just specifying a widget for the label, rather than one that contains
34254 // the children too.
34255 return new TreeNode(args
);
34258 _setTextDirAttr: function(textDir
){
34259 if(textDir
&& this.textDir
!= textDir
){
34260 this._set("textDir",textDir
);
34261 this.rootNode
.set("textDir", textDir
);
34266 Tree
.PathError
= createError("TreePathError");
34267 Tree
._TreeNode
= TreeNode
; // for monkey patching or creating subclasses of TreeNode
34273 'dijit/form/_FormValueWidget':function(){
34274 define("dijit/form/_FormValueWidget", [
34275 "dojo/_base/declare", // declare
34276 "dojo/sniff", // has("ie")
34278 "./_FormValueMixin"
34279 ], function(declare
, has
, _FormWidget
, _FormValueMixin
){
34282 // dijit/form/_FormValueWidget
34284 return declare("dijit.form._FormValueWidget", [_FormWidget
, _FormValueMixin
],
34287 // Base class for widgets corresponding to native HTML elements such as `<input>` or `<select>`
34288 // that have user changeable values.
34290 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) `<input>` element,
34291 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
34292 // works as expected.
34294 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
34295 // directly in the template as read by the parser in order to function. IE is known to specifically
34296 // require the 'name' attribute at element creation time. See #8484, #8660.
34298 _layoutHackIE7: function(){
34300 // Work around table sizing bugs on IE7 by forcing redraw
34302 if(has("ie") == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
34303 var domNode
= this.domNode
;
34304 var parent
= domNode
.parentNode
;
34305 var pingNode
= domNode
.firstChild
|| domNode
; // target node most unlikely to have a custom filter
34306 var origFilter
= pingNode
.style
.filter
; // save custom filter, most likely nothing
34308 while(parent
&& parent
.clientHeight
== 0){ // search for parents that haven't rendered yet
34310 var disconnectHandle
= _this
.connect(parent
, "onscroll",
34312 _this
.disconnect(disconnectHandle
); // only call once
34313 pingNode
.style
.filter
= (new Date()).getMilliseconds(); // set to anything that's unique
34314 _this
.defer(function(){ pingNode
.style
.filter
= origFilter
; }); // restore custom filter, if any
34318 parent
= parent
.parentNode
;
34327 '*now':function(r
){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de","el","en-gb","en-us","es-es","fi-fi","fr-fr","he-il","hu","it-it","ja-jp","ko-kr","nl-nl","nb","pl","pt-br","pt-pt","ru","sk","sl","sv","th","tr","zh-tw","zh-cn","ROOT"]']);}
34329 define("dojo/tt-rss-layer", [], 1);