2 'dijit/form/TextBox':function(){
4 'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
5 define("dijit/form/TextBox", [
6 "dojo/_base/declare", // declare
7 "dojo/dom-construct", // domConstruct.create
8 "dojo/dom-style", // domStyle.getComputedStyle
9 "dojo/_base/kernel", // kernel.deprecated
10 "dojo/_base/lang", // lang.hitch
11 "dojo/sniff", // has("ie") has("mozilla")
14 "dojo/text!./templates/TextBox.html",
15 "../main" // to export dijit._setSelectionRange, remove in 2.0
16 ], function(declare
, domConstruct
, domStyle
, kernel
, lang
, has
,
17 _FormValueWidget
, _TextBoxMixin
, template
, dijit
){
22 var TextBox
= declare("dijit.form.TextBox", [_FormValueWidget
, _TextBoxMixin
], {
24 // A base class for textbox form inputs
26 templateString
: template
,
27 _singleNodeTemplate
: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
29 _buttonInputDisabled
: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
31 baseClass
: "dijitTextBox",
33 postMixInProperties: function(){
34 var type
= this.type
.toLowerCase();
35 if(this.templateString
&& this.templateString
.toLowerCase() == "input" || ((type
== "hidden" || type
== "file") && this.templateString
== this.constructor.prototype.templateString
)){
36 this.templateString
= this._singleNodeTemplate
;
38 this.inherited(arguments
);
41 postCreate: function(){
42 this.inherited(arguments
);
45 // IE INPUT tag fontFamily has to be set directly using STYLE
46 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
47 this.defer(function(){
49 var s
= domStyle
.getComputedStyle(this.domNode
); // can throw an exception if widget is immediately destroyed
51 var ff
= s
.fontFamily
;
53 var inputs
= this.domNode
.getElementsByTagName("INPUT");
55 for(var i
=0; i
< inputs
.length
; i
++){
56 inputs
[i
].style
.fontFamily
= ff
;
61 }catch(e
){/*when used in a Dialog, and this is called before the dialog is
62 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
67 _onInput: function(e
){
68 this.inherited(arguments
);
69 if(this.intermediateChanges
){ // _TextBoxMixin uses onInput
70 // allow the key to post to the widget input box
71 this.defer(function(){ this._handleOnChange(this.get('value'), false); });
75 _setPlaceHolderAttr: function(v
){
76 this._set("placeHolder", v
);
78 this._attachPoints
.push('_phspan');
79 // dijitInputField class gives placeHolder same padding as the input field
80 // parent node already has dijitInputField class but it doesn't affect this <span>
81 // since it's position: absolute.
82 this._phspan
= domConstruct
.create('span',{ onmousedown:function(e
){ e
.preventDefault(); }, className
:'dijitPlaceHolder dijitInputField'},this.textbox
,'after');
84 this._phspan
.innerHTML
="";
85 this._phspan
.appendChild(this._phspan
.ownerDocument
.createTextNode(v
));
86 this._updatePlaceHolder();
89 _updatePlaceHolder: function(){
91 this._phspan
.style
.display
=(this.placeHolder
&&!this.focused
&&!this.textbox
.value
)?"":"none";
95 _setValueAttr: function(value
, /*Boolean?*/ priorityChange
, /*String?*/ formattedValue
){
96 this.inherited(arguments
);
97 this._updatePlaceHolder();
100 getDisplayedValue: function(){
102 // Deprecated. Use get('displayedValue') instead.
105 kernel
.deprecated(this.declaredClass
+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
106 return this.get('displayedValue');
109 setDisplayedValue: function(/*String*/ value
){
111 // Deprecated. Use set('displayedValue', ...) instead.
114 kernel
.deprecated(this.declaredClass
+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
115 this.set('displayedValue', value
);
118 _onBlur: function(e
){
119 if(this.disabled
){ return; }
120 this.inherited(arguments
);
121 this._updatePlaceHolder();
124 if(this.selectOnClick
){
125 // clear selection so that the next mouse click doesn't reselect
126 this.textbox
.selectionStart
= this.textbox
.selectionEnd
= undefined;
131 _onFocus: function(/*String*/ by
){
132 if(this.disabled
|| this.readOnly
){ return; }
133 this.inherited(arguments
);
134 this._updatePlaceHolder();
139 TextBox
.prototype._isTextSelected = function(){
140 var range
= this.ownerDocument
.selection
.createRange();
141 var parent
= range
.parentElement();
142 return parent
== this.textbox
&& range
.text
.length
> 0;
145 // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
146 dijit
._setSelectionRange
= _TextBoxMixin
._setSelectionRange = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
147 if(element
.createTextRange
){
148 var r
= element
.createTextRange();
150 r
.moveStart("character", -99999); // move to 0
151 r
.moveStart("character", start
); // delta from 0 is the correct position
152 r
.moveEnd("character", stop
-start
);
162 'dijit/_base/scroll':function(){
163 define("dijit/_base/scroll", [
164 "dojo/window", // windowUtils.scrollIntoView
165 "../main" // export symbol to dijit
166 ], function(windowUtils
, dijit
){
168 // dijit/_base/scroll
173 // Back compatibility module, new code should use windowUtils directly instead of using this module.
177 dijit
.scrollIntoView = function(/*DomNode*/ node
, /*Object?*/ pos
){
179 // Scroll the passed node into view, if it is not already.
180 // Deprecated, use `windowUtils.scrollIntoView` instead.
182 windowUtils
.scrollIntoView(node
, pos
);
187 'dijit/_TemplatedMixin':function(){
188 define("dijit/_TemplatedMixin", [
189 "dojo/_base/lang", // lang.getObject
192 "dojo/string", // string.substitute string.trim
193 "dojo/cache", // dojo.cache
194 "dojo/_base/array", // array.forEach
195 "dojo/_base/declare", // declare
196 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
197 "dojo/sniff", // has("ie")
198 "dojo/_base/unload" // unload.addOnWindowUnload
199 ], function(lang
, touch
, _WidgetBase
, string
, cache
, array
, declare
, domConstruct
, has
, unload
) {
202 // dijit/_TemplatedMixin
204 var _TemplatedMixin
= declare("dijit._TemplatedMixin", null, {
206 // Mixin for widgets that are instantiated from a template
208 // templateString: [protected] String
209 // A string that represents the widget template.
210 // Use in conjunction with dojo.cache() to load from a file.
211 templateString
: null,
213 // templatePath: [protected deprecated] String
214 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
215 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
218 // skipNodeCache: [protected] Boolean
219 // If using a cached widget template nodes poses issues for a
220 // particular widget class, it can set this property to ensure
221 // that its template is always re-built from a string
222 _skipNodeCache
: false,
224 // _earlyTemplatedStartup: Boolean
225 // A fallback to preserve the 1.0 - 1.3 behavior of children in
226 // templates having their startup called before the parent widget
227 // fires postCreate. Defaults to 'false', causing child widgets to
228 // have their .startup() called immediately before a parent widget
229 // .startup(), but always after the parent .postCreate(). Set to
230 // 'true' to re-enable to previous, arguably broken, behavior.
231 _earlyTemplatedStartup
: false,
234 // _attachPoints: [private] String[]
235 // List of widget attribute names associated with data-dojo-attach-point=... in the
236 // template, ex: ["containerNode", "labelNode"]
239 // _attachEvents: [private] Handle[]
240 // List of connections associated with data-dojo-attach-event=... in the
245 constructor: function(/*===== params, srcNodeRef =====*/){
247 // Create the widget.
248 // params: Object|null
249 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
250 // and functions, typically callbacks like onClick.
251 // The hash can contain any of the widget's properties, excluding read-only properties.
252 // srcNodeRef: DOMNode|String?
253 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
255 this._attachPoints
= [];
256 this._attachEvents
= [];
259 _stringRepl: function(tmpl
){
261 // Does substitution of ${foo} type properties in template string
264 var className
= this.declaredClass
, _this
= this;
265 // Cache contains a string because we need to do property replacement
266 // do the property replacement
267 return string
.substitute(tmpl
, this, function(value
, key
){
268 if(key
.charAt(0) == '!'){ value
= lang
.getObject(key
.substr(1), false, _this
); }
269 if(typeof value
== "undefined"){ throw new Error(className
+" template:"+key
); } // a debugging aide
270 if(value
== null){ return ""; }
272 // Substitution keys beginning with ! will skip the transform step,
273 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
274 return key
.charAt(0) == "!" ? value
:
275 // Safer substitution, see heading "Attribute values" in
276 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
277 value
.toString().replace(/"/g,"""); //TODO
: add
&
? use encodeXML method
?
281 buildRendering: function(){
283 // Construct the UI for this widget from a template, setting this.domNode.
287 if(!this.templateString
){
288 this.templateString
= cache(this.templatePath
, {sanitize
: true});
291 // Lookup cached version of template, and download to cache if it
292 // isn't there already. Returns either a DomNode or a string, depending on
293 // whether or not the template contains ${foo} replacement parameters.
294 var cached
= _TemplatedMixin
.getCachedTemplate(this.templateString
, this._skipNodeCache
, this.ownerDocument
);
297 if(lang
.isString(cached
)){
298 node
= domConstruct
.toDom(this._stringRepl(cached
), this.ownerDocument
);
299 if(node
.nodeType
!= 1){
300 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
301 throw new Error("Invalid template: " + cached
);
304 // if it's a node, all we have to do is clone it
305 node
= cached
.cloneNode(true);
310 // Call down to _Widget.buildRendering() to get base classes assigned
311 // TODO: change the baseClass assignment to _setBaseClassAttr
312 this.inherited(arguments
);
314 // recurse through the node, looking for, and attaching to, our
315 // attachment points and events, which should be defined on the template node.
316 this._attachTemplateNodes(node
, function(n
,p
){ return n
.getAttribute(p
); });
318 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
320 this._fillContent(this.srcNodeRef
);
323 _beforeFillContent: function(){
326 _fillContent: function(/*DomNode*/ source
){
328 // Relocate source contents to templated container node.
329 // this.containerNode must be able to receive children, or exceptions will be thrown.
332 var dest
= this.containerNode
;
334 while(source
.hasChildNodes()){
335 dest
.appendChild(source
.firstChild
);
340 _attachTemplateNodes: function(rootNode
, getAttrFunc
){
342 // Iterate through the template and attach functions and nodes accordingly.
343 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
344 // etc. for those widgets.
346 // Map widget properties and functions to the handlers specified in
347 // the dom node and it's descendants. This function iterates over all
348 // nodes and looks for these properties:
350 // - dojoAttachPoint/data-dojo-attach-point
351 // - dojoAttachEvent/data-dojo-attach-event
352 // rootNode: DomNode|Widget[]
353 // the node to search for properties. All children will be searched.
354 // getAttrFunc: Function
355 // a function which will be used to obtain property for a given
360 var nodes
= lang
.isArray(rootNode
) ? rootNode
: (rootNode
.all
|| rootNode
.getElementsByTagName("*"));
361 var x
= lang
.isArray(rootNode
) ? 0 : -1;
362 for(; x
< 0 || nodes
[x
]; x
++){ // don't access nodes.length on IE, see #14346
363 var baseNode
= (x
== -1) ? rootNode
: nodes
[x
];
364 if(this.widgetsInTemplate
&& (getAttrFunc(baseNode
, "dojoType") || getAttrFunc(baseNode
, "data-dojo-type"))){
367 // Process data-dojo-attach-point
368 var attachPoint
= getAttrFunc(baseNode
, "dojoAttachPoint") || getAttrFunc(baseNode
, "data-dojo-attach-point");
370 var point
, points
= attachPoint
.split(/\s*,\s*/);
371 while((point
= points
.shift())){
372 if(lang
.isArray(this[point
])){
373 this[point
].push(baseNode
);
375 this[point
]=baseNode
;
377 this._attachPoints
.push(point
);
381 // Process data-dojo-attach-event
382 var attachEvent
= getAttrFunc(baseNode
, "dojoAttachEvent") || getAttrFunc(baseNode
, "data-dojo-attach-event");
384 // NOTE: we want to support attributes that have the form
385 // "domEvent: nativeEvent; ..."
386 var event
, events
= attachEvent
.split(/\s*,\s*/);
387 var trim
= lang
.trim
;
388 while((event
= events
.shift())){
391 if(event
.indexOf(":") != -1){
392 // oh, if only JS had tuple assignment
393 var funcNameArr
= event
.split(":");
394 event
= trim(funcNameArr
[0]);
395 thisFunc
= trim(funcNameArr
[1]);
402 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
403 this._attachEvents
.push(this.connect(baseNode
, touch
[event
] || event
, thisFunc
));
410 destroyRendering: function(){
411 // Delete all attach points to prevent IE6 memory leaks.
412 array
.forEach(this._attachPoints
, function(point
){
415 this._attachPoints
= [];
417 // And same for event handlers
418 array
.forEach(this._attachEvents
, this.disconnect
, this);
419 this._attachEvents
= [];
421 this.inherited(arguments
);
425 // key is templateString; object is either string or DOM tree
426 _TemplatedMixin
._templateCache
= {};
428 _TemplatedMixin
.getCachedTemplate = function(templateString
, alwaysUseString
, doc
){
430 // Static method to get a template based on the templatePath or
431 // templateString key
432 // templateString: String
434 // alwaysUseString: Boolean
435 // Don't cache the DOM tree for this template, even if it doesn't have any variables
437 // The target document. Defaults to document global if unspecified.
439 // Either string (if there are ${} variables that need to be replaced) or just
440 // a DOM tree (if the node can be cloned directly)
442 // is it already cached?
443 var tmplts
= _TemplatedMixin
._templateCache
;
444 var key
= templateString
;
445 var cached
= tmplts
[key
];
448 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the
449 // current document, then use the current cached value
450 if(!cached
.ownerDocument
|| cached
.ownerDocument
== (doc
|| document
)){
451 // string or node of the same document
454 }catch(e
){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
455 domConstruct
.destroy(cached
);
458 templateString
= string
.trim(templateString
);
460 if(alwaysUseString
|| templateString
.match(/\$\{([^\}]+)\}/g)){
461 // there are variables in the template so all we can do is cache the string
462 return (tmplts
[key
] = templateString
); //String
464 // there are no variables in the template so we can cache the DOM tree
465 var node
= domConstruct
.toDom(templateString
, doc
);
466 if(node
.nodeType
!= 1){
467 throw new Error("Invalid template: " + templateString
);
469 return (tmplts
[key
] = node
); //Node
474 unload
.addOnWindowUnload(function(){
475 var cache
= _TemplatedMixin
._templateCache
;
476 for(var key
in cache
){
477 var value
= cache
[key
];
478 if(typeof value
== "object"){ // value is either a string or a DOM node template
479 domConstruct
.destroy(value
);
486 // These arguments can be specified for widgets which are used in templates.
487 // Since any widget can be specified as sub widgets in template, mix it
488 // into the base widget class. (This is a hack, but it's effective.).
489 // Remove for 2.0. Also, hide from API doc parser.
490 lang
.extend(_WidgetBase
, /*===== {} || =====*/ {
495 return _TemplatedMixin
;
499 'dijit/_CssStateMixin':function(){
500 define("dijit/_CssStateMixin", [
501 "dojo/_base/array", // array.forEach array.map
502 "dojo/_base/declare", // declare
503 "dojo/dom", // dom.isDescendant()
504 "dojo/dom-class", // domClass.toggle
506 "dojo/_base/lang", // lang.hitch
509 "dojo/_base/window", // win.body
511 ], function(array
, declare
, dom
, domClass
, has
, lang
, on
, ready
, win
, registry
){
514 // dijit/_CssStateMixin
516 var CssStateMixin
= declare("dijit._CssStateMixin", [], {
518 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
519 // state changes, and also higher-level state changes such becoming disabled or selected.
522 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
523 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
524 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
525 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
527 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
529 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
530 // within the widget).
532 // cssStateNodes: [protected] Object
533 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
535 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
536 // (like "dijitUpArrowButton"). Example:
538 // | "upArrowButton": "dijitUpArrowButton",
539 // | "downArrowButton": "dijitDownArrowButton"
541 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
545 // hovering: [readonly] Boolean
546 // True if cursor is over this widget
549 // active: [readonly] Boolean
550 // True if mouse was pressed while over this widget, and hasn't been released yet
553 _applyAttributes: function(){
554 // This code would typically be in postCreate(), but putting in _applyAttributes() for
555 // performance: so the class changes happen before DOM is inserted into the document.
556 // Change back to postCreate() in 2.0. See #11635.
558 this.inherited(arguments
);
560 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
561 array
.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr
){
562 this.watch(attr
, lang
.hitch(this, "_setStateClass"));
565 // Track hover and active mouse events on widget root node, plus possibly on subnodes
566 for(var ap
in this.cssStateNodes
){
567 this._trackMouseState(this[ap
], this.cssStateNodes
[ap
]);
569 this._trackMouseState(this.domNode
, this.baseClass
);
571 // Set state initially; there's probably no hover/active/focus state but widget might be
572 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
573 this._setStateClass();
576 _cssMouseEvent: function(/*Event*/ event
){
578 // Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
579 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
584 this._set("hovering", true);
585 this._set("active", this._mouseDown
);
588 this._set("hovering", false);
589 this._set("active", false);
593 this._set("active", true);
597 this._set("active", false);
603 _setStateClass: function(){
605 // Update the visual state of the widget by setting the css classes on this.domNode
606 // (or this.stateNode if defined) by combining this.baseClass with
607 // various suffixes that represent the current widget state(s).
610 // In the case where a widget has multiple
611 // states, it sets the class based on all possible
612 // combinations. For example, an invalid form widget that is being hovered
613 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
615 // The widget may have one or more of the following states, determined
616 // by this.state, this.checked, this.valid, and this.selected:
618 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
619 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
620 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
621 // - Selected - ex: currently selected tab will have this.selected==true
623 // In addition, it may have one or more of the following states,
624 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
626 // - Disabled - if the widget is disabled
627 // - Active - if the mouse (or space/enter key?) is being pressed down
628 // - Focused - if the widget has focus
629 // - Hover - if the mouse is over the widget
631 // Compute new set of classes
632 var newStateClasses
= this.baseClass
.split(" ");
634 function multiply(modifier
){
635 newStateClasses
= newStateClasses
.concat(array
.map(newStateClasses
, function(c
){ return c
+modifier
; }), "dijit"+modifier
);
638 if(!this.isLeftToRight()){
639 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
643 var checkedState
= this.checked
== "mixed" ? "Mixed" : (this.checked
? "Checked" : "");
645 multiply(checkedState
);
648 multiply(this.state
);
651 multiply("Selected");
658 multiply("Disabled");
659 }else if(this.readOnly
){
660 multiply("ReadOnly");
664 }else if(this.hovering
){
673 // Remove old state classes and add new ones.
674 // For performance concerns we only write into domNode.className once.
675 var tn
= this.stateNode
|| this.domNode
,
676 classHash
= {}; // set of all classes (state and otherwise) for node
678 array
.forEach(tn
.className
.split(" "), function(c
){ classHash
[c
] = true; });
680 if("_stateClasses" in this){
681 array
.forEach(this._stateClasses
, function(c
){ delete classHash
[c
]; });
684 array
.forEach(newStateClasses
, function(c
){ classHash
[c
] = true; });
687 for(var c
in classHash
){
690 tn
.className
= newClasses
.join(" ");
692 this._stateClasses
= newStateClasses
;
695 _subnodeCssMouseEvent: function(node
, clazz
, evt
){
697 // Handler for hover/active mouse event on widget's subnode
698 if(this.disabled
|| this.readOnly
){
701 function hover(isHovering
){
702 domClass
.toggle(node
, clazz
+"Hover", isHovering
);
704 function active(isActive
){
705 domClass
.toggle(node
, clazz
+"Active", isActive
);
707 function focused(isFocused
){
708 domClass
.toggle(node
, clazz
+"Focused", isFocused
);
737 _trackMouseState: function(/*DomNode*/ node
, /*String*/ clazz
){
739 // Track mouse/focus events on specified node and set CSS class on that node to indicate
740 // current state. Usually not called directly, but via cssStateNodes attribute.
742 // Given class=foo, will set the following CSS class on the node
744 // - fooActive: if the user is currently pressing down the mouse button while over the node
745 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
746 // - fooFocus: if the node is focused
748 // Note that it won't set any classes if the widget is disabled.
750 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
751 // is handled specially and automatically just by mixing in this class.
753 // CSS class name (ex: dijitSliderUpArrow)
755 // Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
756 // when node is hovered/active
757 node
._cssState
= clazz
;
762 // Document level listener to catch hover etc. events on widget root nodes and subnodes.
763 // Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
764 // have been hovered or unhovered (try test_Accordion.html)
765 function handler(evt
){
766 // Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
767 // to avoid processing mouseout events moving from a widget's domNode to a descendant node;
768 // such events shouldn't be interpreted as a mouseleave on the widget.
769 if(!dom
.isDescendant(evt
.relatedTarget
, evt
.target
)){
770 for(var node
= evt
.target
; node
&& node
!= evt
.relatedTarget
; node
= node
.parentNode
){
771 // Process any nodes with _cssState property. They are generally widget root nodes,
772 // but could also be sub-nodes within a widget
774 var widget
= registry
.getEnclosingWidget(node
);
776 if(node
== widget
.domNode
){
777 // event on the widget's root node
778 widget
._cssMouseEvent(evt
);
780 // event on widget's sub-node
781 widget
._subnodeCssMouseEvent(node
, node
._cssState
, evt
);
788 function ieHandler(evt
){
789 evt
.target
= evt
.srcElement
;
793 // Use addEventListener() (and attachEvent() on IE) to catch the relevant events even if other handlers
794 // (on individual nodes) call evt.stopPropagation() or event.stopEvent().
795 // Currently typematic.js is doing that, not sure why.
796 // Don't monitor mouseover/mouseout on mobile because iOS generates "phantom" mouseover/mouseout events when
797 // drag-scrolling, at the point in the viewport where the drag originated. Test the Tree in api viewer.
798 var body
= win
.body(),
799 types
= (has("touch") ? [] : ["mouseover", "mouseout"]).concat(["mousedown", "touchstart", "mouseup", "touchend"]);
800 array
.forEach(types
, function(type
){
801 if(body
.addEventListener
){
802 body
.addEventListener(type
, handler
, true); // W3C
804 body
.attachEvent("on"+type
, ieHandler
); // IE
808 // Track focus events on widget sub-nodes that have been registered via _trackMouseState().
809 // However, don't track focus events on the widget root nodes, because focus is tracked via the
810 // focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
811 // nodes or a subwidget's node or a popup node, etc.)
812 // Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
813 on(body
, "focusin, focusout", function(evt
){
814 var node
= evt
.target
;
815 if(node
._cssState
&& !node
.getAttribute("widgetId")){
816 var widget
= registry
.getEnclosingWidget(node
);
817 widget
._subnodeCssMouseEvent(node
, node
._cssState
, evt
);
822 return CssStateMixin
;
826 'dijit/layout/ScrollingTabController':function(){
828 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</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>",
829 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>"}});
830 define("dijit/layout/ScrollingTabController", [
831 "dojo/_base/array", // array.forEach
832 "dojo/_base/declare", // declare
833 "dojo/dom-class", // domClass.add domClass.contains
834 "dojo/dom-geometry", // domGeometry.contentBox
835 "dojo/dom-style", // domStyle.style
836 "dojo/_base/fx", // Animation
837 "dojo/_base/lang", // lang.hitch
839 "dojo/query", // query
840 "dojo/sniff", // has("ie"), has("webkit"), has("quirks")
841 "../registry", // registry.byId()
842 "dojo/text!./templates/ScrollingTabController.html",
843 "dojo/text!./templates/_ScrollingTabControllerButton.html",
845 "./utils", // marginBox2contextBox, layoutChildren
846 "../_WidgetsInTemplateMixin",
851 "dojo/NodeList-dom" // NodeList.style
852 ], function(array
, declare
, domClass
, domGeometry
, domStyle
, fx
, lang
, on
, query
, has
,
853 registry
, tabControllerTemplate
, buttonTemplate
, TabController
, layoutUtils
, _WidgetsInTemplateMixin
,
854 Menu
, MenuItem
, Button
, _HasDropDown
){
857 // dijit/layout/ScrollingTabController
860 var ScrollingTabController
= declare("dijit.layout.ScrollingTabController", [TabController
, _WidgetsInTemplateMixin
], {
862 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
863 // all fitting on a single row.
864 // Works only for horizontal tabs (either above or below the content, not to the left
869 baseClass
: "dijitTabController dijitScrollingTabController",
871 templateString
: tabControllerTemplate
,
873 // useMenu: [const] Boolean
874 // True if a menu should be used to select tabs when they are too
875 // wide to fit the TabContainer, false otherwise.
878 // useSlider: [const] Boolean
879 // True if a slider should be used to select tabs when they are too
880 // wide to fit the TabContainer, false otherwise.
883 // tabStripClass: [const] String
884 // The css class to apply to the tab strip, if it is visible.
887 widgetsInTemplate
: true,
889 // _minScroll: Number
890 // The distance in pixels from the edge of the tab strip which,
891 // if a scroll animation is less than, forces the scroll to
892 // go all the way to the left/right.
895 // Override default behavior mapping class to DOMNode
896 _setClassAttr
: { node
: "containerNode", type
: "class" },
898 buildRendering: function(){
899 this.inherited(arguments
);
900 var n
= this.domNode
;
902 this.scrollNode
= this.tablistWrapper
;
905 if(!this.tabStripClass
){
906 this.tabStripClass
= "dijitTabContainer" +
907 this.tabPosition
.charAt(0).toUpperCase() +
908 this.tabPosition
.substr(1).replace(/-.*/, "") +
910 domClass
.add(n
, "tabStrip-disabled")
913 domClass
.add(this.tablistWrapper
, this.tabStripClass
);
916 onStartup: function(){
917 this.inherited(arguments
);
919 // TabController is hidden until it finishes drawing, to give
920 // a less visually jumpy instantiation. When it's finished, set visibility to ""
921 // to that the tabs are hidden/shown depending on the container's visibility setting.
922 domStyle
.set(this.domNode
, "visibility", "");
923 this._postStartup
= true;
925 // changes to the tab button label or iconClass will have changed the width of the
926 // buttons, so do a resize
927 this.own(on(this.containerNode
, "attrmodified-label, attrmodified-iconclass", lang
.hitch(this, function(evt
){
929 this.resize(this._dim
);
934 onAddChild: function(page
, insertIndex
){
935 this.inherited(arguments
);
937 // Increment the width of the wrapper when a tab is added
938 // This makes sure that the buttons never wrap.
939 // The value 200 is chosen as it should be bigger than most
940 // Tab button widths.
941 domStyle
.set(this.containerNode
, "width",
942 (domStyle
.get(this.containerNode
, "width") + 200) + "px");
945 onRemoveChild: function(page
, insertIndex
){
946 // null out _selectedTab because we are about to delete that dom node
947 var button
= this.pane2button
[page
.id
];
948 if(this._selectedTab
=== button
.domNode
){
949 this._selectedTab
= null;
952 this.inherited(arguments
);
955 _initButtons: function(){
957 // Creates the buttons used to scroll to view tabs that
958 // may not be visible if the TabContainer is too narrow.
960 // Make a list of the buttons to display when the tab labels become
961 // wider than the TabContainer, and hide the other buttons.
962 // Also gets the total width of the displayed buttons.
964 this._buttons
= query("> .tabStripButton", this.domNode
).filter(function(btn
){
965 if((this.useMenu
&& btn
== this._menuBtn
.domNode
) ||
966 (this.useSlider
&& (btn
== this._rightBtn
.domNode
|| btn
== this._leftBtn
.domNode
))){
967 this._btnWidth
+= domGeometry
.getMarginSize(btn
).w
;
970 domStyle
.set(btn
, "display", "none");
976 _getTabsWidth: function(){
977 var children
= this.getChildren();
979 var leftTab
= children
[this.isLeftToRight() ? 0 : children
.length
- 1].domNode
,
980 rightTab
= children
[this.isLeftToRight() ? children
.length
- 1 : 0].domNode
;
981 return rightTab
.offsetLeft
+ rightTab
.offsetWidth
- leftTab
.offsetLeft
;
987 _enableBtn: function(width
){
989 // Determines if the tabs are wider than the width of the TabContainer, and
990 // thus that we need to display left/right/menu navigation buttons.
991 var tabsWidth
= this._getTabsWidth();
992 width
= width
|| domStyle
.get(this.scrollNode
, "width");
993 return tabsWidth
> 0 && width
< tabsWidth
;
996 resize: function(dim
){
998 // Hides or displays the buttons used to scroll the tab list and launch the menu
999 // that selects tabs.
1001 // Save the dimensions to be used when a child is renamed.
1004 // Set my height to be my natural height (tall enough for one row of tab labels),
1005 // and my content-box width based on margin-box width specified in dim parameter.
1006 // But first reset scrollNode.height in case it was set by layoutChildren() call
1007 // in a previous run of this method.
1008 this.scrollNode
.style
.height
= "auto";
1009 var cb
= this._contentBox
= layoutUtils
.marginBox2contentBox(this.domNode
, {h
: 0, w
: dim
.w
});
1010 cb
.h
= this.scrollNode
.offsetHeight
;
1011 domGeometry
.setContentSize(this.domNode
, cb
);
1013 // Show/hide the left/right/menu navigation buttons depending on whether or not they
1015 var enable
= this._enableBtn(this._contentBox
.w
);
1016 this._buttons
.style("display", enable
? "" : "none");
1018 // Position and size the navigation buttons and the tablist
1019 this._leftBtn
.layoutAlign
= "left";
1020 this._rightBtn
.layoutAlign
= "right";
1021 this._menuBtn
.layoutAlign
= this.isLeftToRight() ? "right" : "left";
1022 layoutUtils
.layoutChildren(this.domNode
, this._contentBox
,
1023 [this._menuBtn
, this._leftBtn
, this._rightBtn
, {domNode
: this.scrollNode
, layoutAlign
: "client"}]);
1025 // set proper scroll so that selected tab is visible
1026 if(this._selectedTab
){
1027 if(this._anim
&& this._anim
.status() == "playing"){
1030 this.scrollNode
.scrollLeft
= this._convertToScrollLeft(this._getScrollForSelectedTab());
1033 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
1034 this._setButtonClass(this._getScroll());
1036 this._postResize
= true;
1038 // Return my size so layoutChildren() can use it.
1039 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
1040 return {h
: this._contentBox
.h
, w
: dim
.w
};
1043 _getScroll: function(){
1045 // Returns the current scroll of the tabs where 0 means
1046 // "scrolled all the way to the left" and some positive number, based on #
1047 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
1048 return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode
.scrollLeft
:
1049 domStyle
.get(this.containerNode
, "width") - domStyle
.get(this.scrollNode
, "width")
1050 + (has("ie") >= 8 ? -1 : 1) * this.scrollNode
.scrollLeft
;
1053 _convertToScrollLeft: function(val
){
1055 // Given a scroll value where 0 means "scrolled all the way to the left"
1056 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
1057 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
1058 // to achieve that scroll.
1060 // This method is to adjust for RTL funniness in various browsers and versions.
1061 if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
1064 var maxScroll
= domStyle
.get(this.containerNode
, "width") - domStyle
.get(this.scrollNode
, "width");
1065 return (has("ie") >= 8 ? -1 : 1) * (val
- maxScroll
);
1069 onSelectChild: function(/*dijit/_WidgetBase*/ page){
1071 // Smoothly scrolls to a tab when it is selected.
1073 var tab = this.pane2button[page.id];
1074 if(!tab || !page){return;}
1076 var node = tab.domNode;
1078 // Save the selection
1079 if(node != this._selectedTab){
1080 this._selectedTab = node;
1082 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
1083 if(this._postResize){
1084 var sl = this._getScroll();
1086 if(sl > node.offsetLeft ||
1087 sl + domStyle.get(this.scrollNode, "width") <
1088 node.offsetLeft + domStyle.get(node, "width")){
1089 this.createSmoothScroll().play();
1094 this.inherited(arguments);
1097 _getScrollBounds: function(){
1099 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
1100 // tabs (respectively)
1101 var children = this.getChildren(),
1102 scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
1103 containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
1104 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
1105 tabsWidth = this._getTabsWidth();
1107 if(children.length && tabsWidth > scrollNodeWidth){
1108 // Scrolling should happen
1110 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
1111 max: this.isLeftToRight() ?
1112 (children[children.length-1].domNode.offsetLeft + children[children.length-1].domNode.offsetWidth) - scrollNodeWidth :
1116 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
1117 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
1119 min: onlyScrollPosition,
1120 max: onlyScrollPosition
1125 _getScrollForSelectedTab: function(){
1127 // Returns the scroll value setting so that the selected tab
1128 // will appear in the center
1129 var w = this.scrollNode,
1130 n = this._selectedTab,
1131 scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
1132 scrollBounds = this._getScrollBounds();
1134 // TODO: scroll minimal amount (to either right or left) so that
1135 // selected tab is fully visible, and just return if it's already visible?
1136 var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
1137 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
1140 // If scrolling close to the left side or right side, scroll
1141 // all the way to the left or right. See this._minScroll.
1142 // (But need to make sure that doesn't scroll the tab out of view...)
1146 createSmoothScroll: function(x){
1148 // Creates a dojo._Animation object that smoothly scrolls the tab list
1149 // either to a fixed horizontal pixel value, or to the selected tab.
1151 // If an number argument is passed to the function, that horizontal
1152 // pixel position is scrolled to. Otherwise the currently selected
1153 // tab is scrolled to.
1155 // An optional pixel value to scroll to, indicating distance from left.
1157 // Calculate position to scroll to
1158 if(arguments.length > 0){
1159 // position specified by caller, just make sure it's within bounds
1160 var scrollBounds = this._getScrollBounds();
1161 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
1163 // scroll to center the current tab
1164 x = this._getScrollForSelectedTab();
1167 if(this._anim && this._anim.status() == "playing"){
1172 w = this.scrollNode,
1173 anim = new fx.Animation({
1174 beforeBegin: function(){
1175 if(this.curve){ delete this.curve; }
1176 var oldS = w.scrollLeft,
1177 newS = self._convertToScrollLeft(x);
1178 anim.curve = new fx._Line(oldS, newS);
1180 onAnimate: function(val){
1186 // Disable/enable left/right buttons according to new scroll position
1187 this._setButtonClass(x);
1189 return anim; // dojo/_base/fx/Animation
1192 _getBtnNode: function(/*Event*/ e
){
1194 // Gets a button DOM node from a mouse click event.
1196 // The mouse click event.
1198 while(n
&& !domClass
.contains(n
, "tabStripButton")){
1204 doSlideRight: function(/*Event*/ e
){
1206 // Scrolls the menu to the right.
1208 // The mouse click event.
1209 this.doSlide(1, this._getBtnNode(e
));
1212 doSlideLeft: function(/*Event*/ e
){
1214 // Scrolls the menu to the left.
1216 // The mouse click event.
1217 this.doSlide(-1,this._getBtnNode(e
));
1220 doSlide: function(/*Number*/ direction
, /*DomNode*/ node
){
1222 // Scrolls the tab list to the left or right by 75% of the widget width.
1224 // If the direction is 1, the widget scrolls to the right, if it is -1,
1225 // it scrolls to the left.
1227 if(node
&& domClass
.contains(node
, "dijitTabDisabled")){return;}
1229 var sWidth
= domStyle
.get(this.scrollNode
, "width");
1230 var d
= (sWidth
* 0.75) * direction
;
1232 var to
= this._getScroll() + d
;
1234 this._setButtonClass(to
);
1236 this.createSmoothScroll(to
).play();
1239 _setButtonClass: function(/*Number*/ scroll
){
1241 // Disables the left scroll button if the tabs are scrolled all the way to the left,
1242 // or the right scroll button in the opposite case.
1244 // amount of horizontal scroll
1246 var scrollBounds
= this._getScrollBounds();
1247 this._leftBtn
.set("disabled", scroll
<= scrollBounds
.min
);
1248 this._rightBtn
.set("disabled", scroll
>= scrollBounds
.max
);
1253 var ScrollingTabControllerButtonMixin
= declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
1254 baseClass
: "dijitTab tabStripButton",
1256 templateString
: buttonTemplate
,
1258 // Override inherited tabIndex: 0 from dijit/form/Button, because user shouldn't be
1259 // able to tab to the left/right/menu buttons
1262 // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
1263 // either (this override avoids focus() call in FormWidget.js)
1264 isFocusable: function(){ return false; }
1267 // Class used in template
1268 declare("dijit.layout._ScrollingTabControllerButton",
1269 [Button
, ScrollingTabControllerButtonMixin
]);
1271 // Class used in template
1273 "dijit.layout._ScrollingTabControllerMenuButton",
1274 [Button
, _HasDropDown
, ScrollingTabControllerButtonMixin
],
1276 // id of the TabContainer itself
1279 // -1 so user can't tab into the button, but so that button can still be focused programatically.
1280 // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
1283 isLoaded: function(){
1284 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
1288 loadDropDown: function(callback
){
1289 this.dropDown
= new Menu({
1290 id
: this.containerId
+ "_menu",
1291 ownerDocument
: this.ownerDocument
,
1294 textDir
: this.textDir
1296 var container
= registry
.byId(this.containerId
);
1297 array
.forEach(container
.getChildren(), function(page
){
1298 var menuItem
= new MenuItem({
1299 id
: page
.id
+ "_stcMi",
1301 iconClass
: page
.iconClass
,
1302 disabled
: page
.disabled
,
1303 ownerDocument
: this.ownerDocument
,
1306 textDir
: page
.textDir
,
1307 onClick: function(){
1308 container
.selectChild(page
);
1311 this.dropDown
.addChild(menuItem
);
1316 closeDropDown: function(/*Boolean*/ focus
){
1317 this.inherited(arguments
);
1319 this.dropDown
.destroyRecursive();
1320 delete this.dropDown
;
1325 return ScrollingTabController
;
1329 'dijit/DialogUnderlay':function(){
1330 define("dijit/DialogUnderlay", [
1331 "dojo/_base/declare", // declare
1332 "dojo/dom-attr", // domAttr.set
1333 "dojo/window", // winUtils.getBox
1335 "./_TemplatedMixin",
1336 "./BackgroundIframe"
1337 ], function(declare
, domAttr
, winUtils
, _Widget
, _TemplatedMixin
, BackgroundIframe
){
1340 // dijit/DialogUnderlay
1342 return declare("dijit.DialogUnderlay", [_Widget
, _TemplatedMixin
], {
1344 // The component that blocks the screen behind a `dijit.Dialog`
1347 // A component used to block input behind a `dijit.Dialog`. Only a single
1348 // instance of this widget is created by `dijit.Dialog`, and saved as
1349 // a reference to be shared between all Dialogs as `dijit._underlay`
1351 // The underlay itself can be styled based on and id:
1352 // | #myDialog_underlay { background-color:red; }
1354 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
1355 // suffixed with _underlay.
1357 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
1358 // Inner div has opacity specified in CSS file.
1359 templateString
: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
1361 // Parameters on creation or updatable later
1364 // Id of the dialog.... DialogUnderlay's id is based on this id
1368 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
1371 _setDialogIdAttr: function(id
){
1372 domAttr
.set(this.node
, "id", id
+ "_underlay");
1373 this._set("dialogId", id
);
1376 _setClassAttr: function(clazz
){
1377 this.node
.className
= "dijitDialogUnderlay " + clazz
;
1378 this._set("class", clazz
);
1381 postCreate: function(){
1383 // Append the underlay to the body
1384 this.ownerDocumentBody
.appendChild(this.domNode
);
1389 // Sets the background to the size of the viewport
1392 // Sets the background to the size of the viewport (rather than the size
1393 // of the document) since we need to cover the whole browser window, even
1394 // if the document is only a few lines long.
1398 var is
= this.node
.style
,
1399 os
= this.domNode
.style
;
1401 // hide the background temporarily, so that the background itself isn't
1402 // causing scrollbars to appear (might happen when user shrinks browser
1403 // window and then we are called to resize)
1404 os
.display
= "none";
1406 // then resize and show
1407 var viewport
= winUtils
.getBox(this.ownerDocument
);
1408 os
.top
= viewport
.t
+ "px";
1409 os
.left
= viewport
.l
+ "px";
1410 is
.width
= viewport
.w
+ "px";
1411 is
.height
= viewport
.h
+ "px";
1412 os
.display
= "block";
1417 // Show the dialog underlay
1418 this.domNode
.style
.display
= "block";
1420 this.bgIframe
= new BackgroundIframe(this.domNode
);
1425 // Hides the dialog underlay
1426 this.bgIframe
.destroy();
1427 delete this.bgIframe
;
1428 this.domNode
.style
.display
= "none";
1434 'dijit/place':function(){
1435 define("dijit/place", [
1436 "dojo/_base/array", // array.forEach array.map array.some
1437 "dojo/dom-geometry", // domGeometry.position
1438 "dojo/dom-style", // domStyle.getComputedStyle
1439 "dojo/_base/kernel", // kernel.deprecated
1440 "dojo/_base/window", // win.body
1441 "dojo/window", // winUtils.getBox
1442 "./main" // dijit (defining dijit.place to match API doc)
1443 ], function(array
, domGeometry
, domStyle
, kernel
, win
, winUtils
, dijit
){
1449 function _place(/*DomNode*/ node
, choices
, layoutNode
, aroundNodeCoords
){
1451 // Given a list of spots to put node, put it at the first spot where it fits,
1452 // of if it doesn't fit anywhere then the place with the least overflow
1454 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1455 // Above example says to put the top-left corner of the node at (10,20)
1456 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
1457 // for things like tooltip, they are displayed differently (and have different dimensions)
1458 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1459 // It also passes in the available size for the popup, which is useful for tooltips to
1460 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
1461 // how much the popup had to be modified to fit into the available space. This is used to determine
1462 // what the best placement is.
1463 // aroundNodeCoords: Object
1464 // Size of aroundNode, ex: {w: 200, h: 50}
1466 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1467 // viewport over document
1468 var view
= winUtils
.getBox(node
.ownerDocument
);
1470 // This won't work if the node is inside a <div style="position: relative">,
1471 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
1472 // and also it might get cutoff)
1473 if(!node
.parentNode
|| String(node
.parentNode
.tagName
).toLowerCase() != "body"){
1474 win
.body(node
.ownerDocument
).appendChild(node
);
1478 array
.some(choices
, function(choice
){
1479 var corner
= choice
.corner
;
1480 var pos
= choice
.pos
;
1483 // calculate amount of space available given specified position of node
1484 var spaceAvailable
= {
1486 'L': view
.l
+ view
.w
- pos
.x
,
1487 'R': pos
.x
- view
.l
,
1489 }[corner
.charAt(1)],
1491 'T': view
.t
+ view
.h
- pos
.y
,
1492 'B': pos
.y
- view
.t
,
1497 // Clear left/right position settings set earlier so they don't interfere with calculations,
1498 // specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
1500 s
.left
= s
.right
= "auto";
1502 // configure node to be displayed in given position relative to button
1503 // (need to do this in order to get an accurate size for the node, because
1504 // a tooltip's size changes based on position, due to triangle)
1506 var res
= layoutNode(node
, choice
.aroundCorner
, corner
, spaceAvailable
, aroundNodeCoords
);
1507 overflow
= typeof res
== "undefined" ? 0 : res
;
1511 var style
= node
.style
;
1512 var oldDisplay
= style
.display
;
1513 var oldVis
= style
.visibility
;
1514 if(style
.display
== "none"){
1515 style
.visibility
= "hidden";
1518 var bb
= domGeometry
.position(node
);
1519 style
.display
= oldDisplay
;
1520 style
.visibility
= oldVis
;
1522 // coordinates and size of node with specified corner placed at pos,
1523 // and clipped by viewport
1528 'M': Math
.max(view
.l
, Math
.min(view
.l
+ view
.w
, pos
.x
+ (bb
.w
>> 1)) - bb
.w
) // M orientation is more flexible
1529 }[corner
.charAt(1)],
1533 'M': Math
.max(view
.t
, Math
.min(view
.t
+ view
.h
, pos
.y
+ (bb
.h
>> 1)) - bb
.h
)
1534 }[corner
.charAt(0)],
1535 startX
= Math
.max(view
.l
, startXpos
),
1536 startY
= Math
.max(view
.t
, startYpos
),
1537 endX
= Math
.min(view
.l
+ view
.w
, startXpos
+ bb
.w
),
1538 endY
= Math
.min(view
.t
+ view
.h
, startYpos
+ bb
.h
),
1539 width
= endX
- startX
,
1540 height
= endY
- startY
;
1542 overflow
+= (bb
.w
- width
) + (bb
.h
- height
);
1544 if(best
== null || overflow
< best
.overflow
){
1547 aroundCorner
: choice
.aroundCorner
,
1553 spaceAvailable
: spaceAvailable
1560 // In case the best position is not the last one we checked, need to call
1561 // layoutNode() again.
1562 if(best
.overflow
&& layoutNode
){
1563 layoutNode(node
, best
.aroundCorner
, best
.corner
, best
.spaceAvailable
, aroundNodeCoords
);
1566 // And then position the node. Do this last, after the layoutNode() above
1567 // has sized the node, due to browser quirks when the viewport is scrolled
1568 // (specifically that a Tooltip will shrink to fit as though the window was
1569 // scrolled to the left).
1571 // In RTL mode, set style.right rather than style.left so in the common case,
1572 // window resizes move the popup along with the aroundNode.
1573 var l
= domGeometry
.isBodyLtr(node
.ownerDocument
),
1575 s
.top
= best
.y
+ "px";
1576 s
[l
? "left" : "right"] = (l
? best
.x
: view
.w
- best
.x
- best
.w
) + "px";
1577 s
[l
? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
1584 // Code to place a DOMNode relative to another DOMNode.
1585 // Load using require(["dijit/place"], function(place){ ... }).
1587 at: function(node
, pos
, corners
, padding
){
1589 // Positions one of the node's corners at specified position
1590 // such that node is fully visible in viewport.
1592 // NOTE: node is assumed to be absolutely or relatively positioned.
1594 // The node to position
1595 // pos: dijit/place.__Position
1596 // Object like {x: 10, y: 20}
1597 // corners: String[]
1598 // Array of Strings representing order to try corners in, like ["TR", "BL"].
1599 // Possible values are:
1601 // - "BL" - bottom left
1602 // - "BR" - bottom right
1603 // - "TL" - top left
1604 // - "TR" - top right
1605 // padding: dijit/place.__Position?
1606 // optional param to set padding, to put some buffer around the element you want to position.
1608 // Try to place node's top right corner at (10,20).
1609 // If that makes node go (partially) off screen, then try placing
1610 // bottom left corner at (10,20).
1611 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
1612 var choices
= array
.map(corners
, function(corner
){
1613 var c
= { corner
: corner
, pos
: {x
:pos
.x
,y
:pos
.y
} };
1615 c
.pos
.x
+= corner
.charAt(1) == 'L' ? padding
.x
: -padding
.x
;
1616 c
.pos
.y
+= corner
.charAt(0) == 'T' ? padding
.y
: -padding
.y
;
1621 return _place(node
, choices
);
1626 /*DomNode|dijit/place.__Rectangle*/ anchor,
1627 /*String[]*/ positions
,
1628 /*Boolean*/ leftToRight
,
1629 /*Function?*/ layoutNode
){
1632 // Position node adjacent or kitty-corner to anchor
1633 // such that it's fully visible in viewport.
1635 // Place node such that corner of node touches a corner of
1636 // aroundNode, and that node is fully visible.
1638 // Either a DOMNode or a rectangle (object with x, y, width, height).
1640 // Ordered list of positions to try matching up.
1642 // - before: places drop down to the left of the anchor node/widget, or to the right in the case
1643 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1644 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1645 // - after: places drop down to the right of the anchor node/widget, or to the left in the case
1646 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1647 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1648 // - before-centered: centers drop down to the left of the anchor node/widget, or to the right
1649 // in the case of RTL scripts like Hebrew and Arabic
1650 // - after-centered: centers drop down to the right of the anchor node/widget, or to the left
1651 // in the case of RTL scripts like Hebrew and Arabic
1652 // - above-centered: drop down is centered above anchor node
1653 // - above: drop down goes above anchor node, left sides aligned
1654 // - above-alt: drop down goes above anchor node, right sides aligned
1655 // - below-centered: drop down is centered above anchor node
1656 // - below: drop down goes below anchor node
1657 // - below-alt: drop down goes below anchor node, right sides aligned
1658 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1659 // For things like tooltip, they are displayed differently (and have different dimensions)
1660 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1662 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
1663 // positions slightly.
1665 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
1666 // This will try to position node such that node's top-left corner is at the same position
1667 // as the bottom left corner of the aroundNode (ie, put node below
1668 // aroundNode, with left edges aligned). If that fails it will try to put
1669 // the bottom-right corner of node where the top right corner of aroundNode is
1670 // (ie, put node above aroundNode, with right edges aligned)
1673 // if around is a DOMNode (or DOMNode id), convert to coordinates
1674 var aroundNodePos
= (typeof anchor
== "string" || "offsetWidth" in anchor
)
1675 ? domGeometry
.position(anchor
, true)
1678 // Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
1679 if(anchor
.parentNode
){
1680 // ignore nodes between position:relative and position:absolute
1681 var sawPosAbsolute
= domStyle
.getComputedStyle(anchor
).position
== "absolute";
1682 var parent
= anchor
.parentNode
;
1683 while(parent
&& parent
.nodeType
== 1 && parent
.nodeName
!= "BODY"){ //ignoring the body will help performance
1684 var parentPos
= domGeometry
.position(parent
, true),
1685 pcs
= domStyle
.getComputedStyle(parent
);
1686 if(/relative|absolute/.test(pcs
.position
)){
1687 sawPosAbsolute
= false;
1689 if(!sawPosAbsolute
&& /hidden|auto|scroll/.test(pcs
.overflow
)){
1690 var bottomYCoord
= Math
.min(aroundNodePos
.y
+ aroundNodePos
.h
, parentPos
.y
+ parentPos
.h
);
1691 var rightXCoord
= Math
.min(aroundNodePos
.x
+ aroundNodePos
.w
, parentPos
.x
+ parentPos
.w
);
1692 aroundNodePos
.x
= Math
.max(aroundNodePos
.x
, parentPos
.x
);
1693 aroundNodePos
.y
= Math
.max(aroundNodePos
.y
, parentPos
.y
);
1694 aroundNodePos
.h
= bottomYCoord
- aroundNodePos
.y
;
1695 aroundNodePos
.w
= rightXCoord
- aroundNodePos
.x
;
1697 if(pcs
.position
== "absolute"){
1698 sawPosAbsolute
= true;
1700 parent
= parent
.parentNode
;
1704 var x
= aroundNodePos
.x
,
1705 y
= aroundNodePos
.y
,
1706 width
= "w" in aroundNodePos
? aroundNodePos
.w
: (aroundNodePos
.w
= aroundNodePos
.width
),
1707 height
= "h" in aroundNodePos
? aroundNodePos
.h
: (kernel
.deprecated("place.around: dijit/place.__Rectangle: { x:"+x
+", y:"+y
+", height:"+aroundNodePos
.height
+", width:"+width
+" } has been deprecated. Please use { x:"+x
+", y:"+y
+", h:"+aroundNodePos
.height
+", w:"+width
+" }", "", "2.0"), aroundNodePos
.h
= aroundNodePos
.height
);
1709 // Convert positions arguments into choices argument for _place()
1711 function push(aroundCorner
, corner
){
1713 aroundCorner
: aroundCorner
,
1719 'M': x
+ (width
>> 1)
1720 }[aroundCorner
.charAt(1)],
1724 'M': y
+ (height
>> 1)
1725 }[aroundCorner
.charAt(0)]
1729 array
.forEach(positions
, function(pos
){
1730 var ltr
= leftToRight
;
1732 case "above-centered":
1735 case "below-centered":
1738 case "after-centered":
1741 case "before-centered":
1742 push(ltr
? "ML" : "MR", ltr
? "MR" : "ML");
1748 push(ltr
? "TL" : "TR", ltr
? "TR" : "TL");
1749 push(ltr
? "BL" : "BR", ltr
? "BR" : "BL");
1755 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1756 push(ltr
? "BL" : "BR", ltr
? "TL" : "TR");
1757 push(ltr
? "BR" : "BL", ltr
? "TR" : "TL");
1763 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1764 push(ltr
? "TL" : "TR", ltr
? "BL" : "BR");
1765 push(ltr
? "TR" : "TL", ltr
? "BR" : "BL");
1768 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
1769 // Not meant to be used directly.
1770 push(pos
.aroundCorner
, pos
.corner
);
1774 var position
= _place(node
, choices
, layoutNode
, {w
: width
, h
: height
});
1775 position
.aroundNodePos
= aroundNodePos
;
1782 place.__Position = {
1784 // horizontal coordinate in pixels, relative to document body
1786 // vertical coordinate in pixels, relative to document body
1788 place.__Rectangle = {
1790 // horizontal offset in pixels, relative to document body
1792 // vertical offset in pixels, relative to document body
1794 // width in pixels. Can also be specified as "width" for backwards-compatibility.
1796 // height in pixels. Can also be specified as "height" for backwards-compatibility.
1800 return dijit
.place
= place
; // setting dijit.place for back-compat, remove for 2.0
1804 'dijit/_HasDropDown':function(){
1805 define("dijit/_HasDropDown", [
1806 "dojo/_base/declare", // declare
1807 "dojo/_base/Deferred",
1808 "dojo/_base/event", // event.stop
1809 "dojo/dom", // dom.isDescendant
1810 "dojo/dom-attr", // domAttr.set
1811 "dojo/dom-class", // domClass.add domClass.contains domClass.remove
1812 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
1813 "dojo/dom-style", // domStyle.set
1814 "dojo/has", // has("touch")
1815 "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
1816 "dojo/_base/lang", // lang.hitch lang.isFunction
1818 "dojo/window", // winUtils.getBox
1819 "./registry", // registry.byNode()
1823 ], function(declare
, Deferred
, event
,dom
, domAttr
, domClass
, domGeometry
, domStyle
, has
, keys
, lang
, on
,
1824 winUtils
, registry
, focus
, popup
, _FocusMixin
){
1828 // dijit/_HasDropDown
1830 return declare("dijit._HasDropDown", _FocusMixin
, {
1832 // Mixin for widgets that need drop down ability.
1834 // _buttonNode: [protected] DomNode
1835 // The button/icon/node to click to display the drop down.
1836 // Can be set via a data-dojo-attach-point assignment.
1837 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
1840 // _arrowWrapperNode: [protected] DomNode
1841 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
1842 // on where the drop down is set to be positioned.
1843 // Can be set via a data-dojo-attach-point assignment.
1844 // If missing, then _buttonNode will be used.
1845 _arrowWrapperNode
: null,
1847 // _popupStateNode: [protected] DomNode
1848 // The node to set the popupActive class on.
1849 // Can be set via a data-dojo-attach-point assignment.
1850 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
1851 _popupStateNode
: null,
1853 // _aroundNode: [protected] DomNode
1854 // The node to display the popup around.
1855 // Can be set via a data-dojo-attach-point assignment.
1856 // If missing, then domNode will be used.
1859 // dropDown: [protected] Widget
1860 // The widget to display as a popup. This widget *must* be
1861 // defined before the startup function is called.
1864 // autoWidth: [protected] Boolean
1865 // Set to true to make the drop down at least as wide as this
1866 // widget. Set to false if the drop down should just be its
1870 // forceWidth: [protected] Boolean
1871 // Set to true to make the drop down exactly as wide as this
1872 // widget. Overrides autoWidth.
1875 // maxHeight: [protected] Integer
1876 // The max height for our dropdown.
1877 // Any dropdown taller than this will have scrollbars.
1878 // Set to 0 for no max height, or -1 to limit height to available space in viewport
1881 // dropDownPosition: [const] String[]
1882 // This variable controls the position of the drop down.
1883 // It's an array of strings with the following values:
1885 // - before: places drop down to the left of the target node/widget, or to the right in
1886 // the case of RTL scripts like Hebrew and Arabic
1887 // - after: places drop down to the right of the target node/widget, or to the left in
1888 // the case of RTL scripts like Hebrew and Arabic
1889 // - above: drop down goes above target node
1890 // - below: drop down goes below target node
1892 // The list is positions is tried, in order, until a position is found where the drop down fits
1893 // within the viewport.
1895 dropDownPosition
: ["below","above"],
1897 // _stopClickEvents: Boolean
1898 // When set to false, the click events will not be stopped, in
1899 // case you want to use them in your subclass
1900 _stopClickEvents
: true,
1902 _onDropDownMouseDown: function(/*Event*/ e
){
1904 // Callback when the user mousedown's on the arrow icon
1905 if(this.disabled
|| this.readOnly
){ return; }
1907 // Prevent default to stop things like text selection, but don't stop propagation, so that:
1908 // 1. TimeTextBox etc. can focus the <input> on mousedown
1909 // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
1910 // 3. user defined onMouseDown handler fires
1913 this._docHandler
= this.connect(this.ownerDocument
, "mouseup", "_onDropDownMouseUp");
1915 this.toggleDropDown();
1918 _onDropDownMouseUp: function(/*Event?*/ e
){
1920 // Callback when the user lifts their mouse after mouse down on the arrow icon.
1921 // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
1922 // drop down widget. If the event is missing, then we are not
1925 // This is useful for the common mouse movement pattern
1926 // with native browser `<select>` nodes:
1928 // 1. mouse down on the select node (probably on the arrow)
1929 // 2. move mouse to a menu item while holding down the mouse button
1930 // 3. mouse up. this selects the menu item as though the user had clicked it.
1931 if(e
&& this._docHandler
){
1932 this.disconnect(this._docHandler
);
1934 var dropDown
= this.dropDown
, overMenu
= false;
1936 if(e
&& this._opened
){
1937 // This code deals with the corner-case when the drop down covers the original widget,
1938 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
1939 // Find out if our target is somewhere in our dropdown widget,
1940 // but not over our _buttonNode (the clickable node)
1941 var c
= domGeometry
.position(this._buttonNode
, true);
1942 if(!(e
.pageX
>= c
.x
&& e
.pageX
<= c
.x
+ c
.w
) ||
1943 !(e
.pageY
>= c
.y
&& e
.pageY
<= c
.y
+ c
.h
)){
1945 while(t
&& !overMenu
){
1946 if(domClass
.contains(t
, "dijitPopup")){
1954 if(dropDown
.onItemClick
){
1956 while(t
&& !(menuItem
= registry
.byNode(t
))){
1959 if(menuItem
&& menuItem
.onClick
&& menuItem
.getParent
){
1960 menuItem
.getParent().onItemClick(menuItem
, e
);
1968 if(dropDown
.focus
&& dropDown
.autoFocus
!== false){
1969 // Focus the dropdown widget - do it on a delay so that we
1970 // don't steal back focus from the dropdown.
1971 this._focusDropDownTimer
= this.defer(function(){
1973 delete this._focusDropDownTimer
;
1977 // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
1978 // defer() needed to make it work on IE (test DateTextBox)
1979 this.defer("focus");
1983 this._justGotMouseUp
= true;
1984 this.defer(function(){
1985 this._justGotMouseUp
= false;
1990 _onDropDownClick: function(/*Event*/ e
){
1991 if(has("touch") && !this._justGotMouseUp
){
1992 // If there was no preceding mousedown/mouseup (like on android), then simulate them to
1993 // toggle the drop down.
1995 // The if(has("touch") is necessary since IE and desktop safari get spurious onclick events
1996 // when there are nested tables (specifically, clicking on a table that holds a dijit/form/Select,
1997 // but not on the Select itself, causes an onclick event on the Select)
1998 this._onDropDownMouseDown(e
);
1999 this._onDropDownMouseUp(e
);
2002 // The drop down was already opened on mousedown/keydown; just need to call stopEvent().
2003 if(this._stopClickEvents
){
2008 buildRendering: function(){
2009 this.inherited(arguments
);
2011 this._buttonNode
= this._buttonNode
|| this.focusNode
|| this.domNode
;
2012 this._popupStateNode
= this._popupStateNode
|| this.focusNode
|| this._buttonNode
;
2014 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
2015 // based on where drop down will normally appear
2017 "after" : this.isLeftToRight() ? "Right" : "Left",
2018 "before" : this.isLeftToRight() ? "Left" : "Right",
2023 }[this.dropDownPosition
[0]] || this.dropDownPosition
[0] || "Down";
2024 domClass
.add(this._arrowWrapperNode
|| this._buttonNode
, "dijit" + defaultPos
+ "ArrowButton");
2027 postCreate: function(){
2029 // set up nodes and connect our mouse and keyboard events
2031 this.inherited(arguments
);
2033 var keyboardEventNode
= this.focusNode
|| this.domNode
;
2035 on(this._buttonNode
, "mousedown", lang
.hitch(this, "_onDropDownMouseDown")),
2036 on(this._buttonNode
, "click", lang
.hitch(this, "_onDropDownClick")),
2037 on(keyboardEventNode
, "keydown", lang
.hitch(this, "_onKey")),
2038 on(keyboardEventNode
, "keyup", lang
.hitch(this, "_onKeyUp"))
2042 destroy: function(){
2044 // Destroy the drop down, unless it's already been destroyed. This can happen because
2045 // the drop down is a direct child of <body> even though it's logically my child.
2046 if(!this.dropDown
._destroyed
){
2047 this.dropDown
.destroyRecursive();
2049 delete this.dropDown
;
2051 this.inherited(arguments
);
2054 _onKey: function(/*Event*/ e
){
2056 // Callback when the user presses a key while focused on the button node
2058 if(this.disabled
|| this.readOnly
){ return; }
2059 var d
= this.dropDown
, target
= e
.target
;
2060 if(d
&& this._opened
&& d
.handleKey
){
2061 if(d
.handleKey(e
) === false){
2062 /* false return code means that the drop down handled the key */
2067 if(d
&& this._opened
&& e
.keyCode
== keys
.ESCAPE
){
2068 this.closeDropDown();
2070 }else if(!this._opened
&&
2071 (e
.keyCode
== keys
.DOWN_ARROW
||
2072 ( (e
.keyCode
== keys
.ENTER
|| e
.keyCode
== keys
.SPACE
) &&
2073 //ignore enter and space if the event is for a text input
2074 ((target
.tagName
|| "").toLowerCase() !== 'input' ||
2075 (target
.type
&& target
.type
.toLowerCase() !== 'text'))))){
2076 // Toggle the drop down, but wait until keyup so that the drop down doesn't
2077 // get a stray keyup event, or in the case of key-repeat (because user held
2078 // down key for too long), stray keydown events
2079 this._toggleOnKeyUp
= true;
2084 _onKeyUp: function(){
2085 if(this._toggleOnKeyUp
){
2086 delete this._toggleOnKeyUp
;
2087 this.toggleDropDown();
2088 var d
= this.dropDown
; // drop down may not exist until toggleDropDown() call
2090 this.defer(lang
.hitch(d
, "focus"), 1);
2095 _onBlur: function(){
2097 // Called magically when focus has shifted away from this widget and it's dropdown
2099 // Don't focus on button if the user has explicitly focused on something else (happens
2100 // when user clicks another control causing the current popup to close)..
2101 // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
2102 // it when you display:none a node with focus.
2103 var focusMe
= focus
.curNode
&& this.dropDown
&& dom
.isDescendant(focus
.curNode
, this.dropDown
.domNode
);
2105 this.closeDropDown(focusMe
);
2107 this.inherited(arguments
);
2110 isLoaded: function(){
2112 // Returns true if the dropdown exists and it's data is loaded. This can
2113 // be overridden in order to force a call to loadDropDown().
2120 loadDropDown: function(/*Function*/ loadCallback
){
2122 // Creates the drop down if it doesn't exist, loads the data
2123 // if there's an href and it hasn't been loaded yet, and then calls
2124 // the given callback.
2128 // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
2132 loadAndOpenDropDown: function(){
2134 // Creates the drop down if it doesn't exist, loads the data
2135 // if there's an href and it hasn't been loaded yet, and
2136 // then opens the drop down. This is basically a callback when the
2137 // user presses the down arrow button to open the drop down.
2138 // returns: Deferred
2139 // Deferred for the drop down widget that
2140 // fires when drop down is created and loaded
2143 var d
= new Deferred(),
2144 afterLoad
= lang
.hitch(this, function(){
2145 this.openDropDown();
2146 d
.resolve(this.dropDown
);
2148 if(!this.isLoaded()){
2149 this.loadDropDown(afterLoad
);
2156 toggleDropDown: function(){
2158 // Callback when the user presses the down arrow button or presses
2159 // the down arrow key to open/close the drop down.
2160 // Toggle the drop-down widget; if it is up, close it, if not, open it
2164 if(this.disabled
|| this.readOnly
){ return; }
2166 this.loadAndOpenDropDown();
2168 this.closeDropDown();
2172 openDropDown: function(){
2174 // Opens the dropdown for this widget. To be called only when this.dropDown
2175 // has been created and is ready to display (ie, it's data is loaded).
2177 // return value of dijit/popup.open()
2181 var dropDown
= this.dropDown
,
2182 ddNode
= dropDown
.domNode
,
2183 aroundNode
= this._aroundNode
|| this.domNode
,
2186 // Prepare our popup's height and honor maxHeight if it exists.
2188 // TODO: isn't maxHeight dependent on the return value from dijit/popup.open(),
2189 // ie, dependent on how much space is available (BK)
2191 if(!this._preparedNode
){
2192 this._preparedNode
= true;
2193 // Check if we have explicitly set width and height on the dropdown widget dom node
2194 if(ddNode
.style
.width
){
2195 this._explicitDDWidth
= true;
2197 if(ddNode
.style
.height
){
2198 this._explicitDDHeight
= true;
2202 // Code for resizing dropdown (height limitation, or increasing width to match my width)
2203 if(this.maxHeight
|| this.forceWidth
|| this.autoWidth
){
2206 visibility
: "hidden"
2208 if(!this._explicitDDWidth
){
2211 if(!this._explicitDDHeight
){
2212 myStyle
.height
= "";
2214 domStyle
.set(ddNode
, myStyle
);
2216 // Figure out maximum height allowed (if there is a height restriction)
2217 var maxHeight
= this.maxHeight
;
2218 if(maxHeight
== -1){
2219 // limit height to space available in viewport either above or below my domNode
2220 // (whichever side has more room)
2221 var viewport
= winUtils
.getBox(this.ownerDocument
),
2222 position
= domGeometry
.position(aroundNode
, false);
2223 maxHeight
= Math
.floor(Math
.max(position
.y
, viewport
.h
- (position
.y
+ position
.h
)));
2226 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
2227 // so we call startup() and also get the size
2228 popup
.moveOffScreen(dropDown
);
2230 if(dropDown
.startup
&& !dropDown
._started
){
2231 dropDown
.startup(); // this has to be done after being added to the DOM
2233 // Get size of drop down, and determine if vertical scroll bar needed. If no scroll bar needed,
2234 // use overflow:visible rather than overflow:hidden so off-by-one errors don't hide drop down border.
2235 var mb
= domGeometry
.getMarginSize(ddNode
);
2236 var overHeight
= (maxHeight
&& mb
.h
> maxHeight
);
2237 domStyle
.set(ddNode
, {
2238 overflowX
: "visible",
2239 overflowY
: overHeight
? "auto" : "visible"
2244 mb
.w
+= 16; // room for vertical scrollbar
2250 // Adjust dropdown width to match or be larger than my width
2251 if(this.forceWidth
){
2252 mb
.w
= aroundNode
.offsetWidth
;
2253 }else if(this.autoWidth
){
2254 mb
.w
= Math
.max(mb
.w
, aroundNode
.offsetWidth
);
2259 // And finally, resize the dropdown to calculated height and width
2260 if(lang
.isFunction(dropDown
.resize
)){
2261 dropDown
.resize(mb
);
2263 domGeometry
.setMarginBox(ddNode
, mb
);
2267 var retVal
= popup
.open({
2271 orient
: this.dropDownPosition
,
2272 onExecute: function(){
2273 self
.closeDropDown(true);
2275 onCancel: function(){
2276 self
.closeDropDown(true);
2278 onClose: function(){
2279 domAttr
.set(self
._popupStateNode
, "popupActive", false);
2280 domClass
.remove(self
._popupStateNode
, "dijitHasDropDownOpen");
2281 self
._set("_opened", false); // use set() because _CssStateMixin is watching
2284 domAttr
.set(this._popupStateNode
, "popupActive", "true");
2285 domClass
.add(this._popupStateNode
, "dijitHasDropDownOpen");
2286 this._set("_opened", true); // use set() because _CssStateMixin is watching
2287 this.domNode
.setAttribute("aria-expanded", "true");
2292 closeDropDown: function(/*Boolean*/ focus
){
2294 // Closes the drop down on this widget
2296 // If true, refocuses the button widget
2300 if(this._focusDropDownTimer
){
2301 this._focusDropDownTimer
.remove();
2302 delete this._focusDropDownTimer
;
2305 this.domNode
.setAttribute("aria-expanded", "false");
2306 if(focus
){ this.focus(); }
2307 popup
.close(this.dropDown
);
2308 this._opened
= false;
2316 'dijit/tree/TreeStoreModel':function(){
2317 define("dijit/tree/TreeStoreModel", [
2318 "dojo/_base/array", // array.filter array.forEach array.indexOf array.some
2319 "dojo/aspect", // aspect.after
2320 "dojo/_base/declare", // declare
2321 "dojo/_base/lang" // lang.hitch
2322 ], function(array
, aspect
, declare
, lang
){
2325 // dijit/tree/TreeStoreModel
2327 return declare("dijit.tree.TreeStoreModel", null, {
2329 // Implements dijit/Tree/model connecting to a dojo.data store with a single
2330 // root item. Any methods passed into the constructor will override
2331 // the ones defined here.
2333 // store: dojo/data/api/Read
2337 // childrenAttrs: String[]
2338 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
2339 childrenAttrs
: ["children"],
2341 // newItemIdAttr: String
2342 // Name of attribute in the Object passed to newItem() that specifies the id.
2344 // If newItemIdAttr is set then it's used when newItem() is called to see if an
2345 // item with the same id already exists, and if so just links to the old item
2346 // (so that the old item ends up with two parents).
2348 // Setting this to null or "" will make every drop create a new item.
2349 newItemIdAttr
: "id",
2351 // labelAttr: String
2352 // If specified, get label for tree node from this attribute, rather
2353 // than by calling store.getLabel()
2356 // root: [readonly] dojo/data/Item
2357 // Pointer to the root item (read only, not a parameter)
2361 // Specifies datastore query to return the root item for the tree.
2362 // Must only return a single item. Alternately can just pass in pointer
2368 // deferItemLoadingUntilExpand: Boolean
2369 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
2370 // until they are expanded. This allows for lazying loading where only one
2371 // loadItem (and generally one network call, consequently) per expansion
2372 // (rather than one for each child).
2373 // This relies on partial loading of the children items; each children item of a
2374 // fully loaded item should contain the label and info about having children.
2375 deferItemLoadingUntilExpand
: false,
2377 constructor: function(/* Object */ args
){
2379 // Passed the arguments listed above (store, etc)
2383 lang
.mixin(this, args
);
2387 var store
= this.store
;
2388 if(!store
.getFeatures()['dojo.data.api.Identity']){
2389 throw new Error("dijit.tree.TreeStoreModel: store must support dojo.data.Identity");
2392 // if the store supports Notification, subscribe to the notification events
2393 if(store
.getFeatures()['dojo.data.api.Notification']){
2394 this.connects
= this.connects
.concat([
2395 aspect
.after(store
, "onNew", lang
.hitch(this, "onNewItem"), true),
2396 aspect
.after(store
, "onDelete", lang
.hitch(this, "onDeleteItem"), true),
2397 aspect
.after(store
, "onSet", lang
.hitch(this, "onSetItem"), true)
2402 destroy: function(){
2404 while(h
= this.connects
.pop()){ h
.remove(); }
2405 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
2408 // =======================================================================
2409 // Methods for traversing hierarchy
2411 getRoot: function(onItem
, onError
){
2413 // Calls onItem with the root item for the tree, possibly a fabricated item.
2414 // Calls onError on error.
2420 onComplete
: lang
.hitch(this, function(items
){
2421 if(items
.length
!= 1){
2422 throw new Error("dijit.tree.TreeStoreModel: root query returned " + items
.length
+
2423 " items, but must return exactly one");
2425 this.root
= items
[0];
2433 mayHaveChildren: function(/*dojo/data/Item*/ item
){
2435 // Tells if an item has or may have children. Implementing logic here
2436 // avoids showing +/- expando icon for nodes that we know don't have children.
2437 // (For efficiency reasons we may not want to check if an element actually
2438 // has children until user clicks the expando node)
2439 return array
.some(this.childrenAttrs
, function(attr
){
2440 return this.store
.hasAttribute(item
, attr
);
2444 getChildren: function(/*dojo/data/Item*/ parentItem
, /*function(items)*/ onComplete
, /*function*/ onError
){
2446 // Calls onComplete() with array of child items of given parent item, all loaded.
2448 var store
= this.store
;
2449 if(!store
.isItemLoaded(parentItem
)){
2450 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
2451 // mode, so we will load it and just return the children (without loading each
2453 var getChildren
= lang
.hitch(this, arguments
.callee
);
2456 onItem: function(parentItem
){
2457 getChildren(parentItem
, onComplete
, onError
);
2463 // get children of specified item
2464 var childItems
= [];
2465 for(var i
=0; i
<this.childrenAttrs
.length
; i
++){
2466 var vals
= store
.getValues(parentItem
, this.childrenAttrs
[i
]);
2467 childItems
= childItems
.concat(vals
);
2470 // count how many items need to be loaded
2472 if(!this.deferItemLoadingUntilExpand
){
2473 array
.forEach(childItems
, function(item
){ if(!store
.isItemLoaded(item
)){ _waitCount
++; } });
2476 if(_waitCount
== 0){
2477 // all items are already loaded (or we aren't loading them). proceed...
2478 onComplete(childItems
);
2480 // still waiting for some or all of the items to load
2481 array
.forEach(childItems
, function(item
, idx
){
2482 if(!store
.isItemLoaded(item
)){
2485 onItem: function(item
){
2486 childItems
[idx
] = item
;
2487 if(--_waitCount
== 0){
2488 // all nodes have been loaded, send them to the tree
2489 onComplete(childItems
);
2499 // =======================================================================
2502 isItem: function(/* anything */ something
){
2503 return this.store
.isItem(something
); // Boolean
2506 fetchItemByIdentity: function(/* object */ keywordArgs
){
2507 this.store
.fetchItemByIdentity(keywordArgs
);
2510 getIdentity: function(/* item */ item
){
2511 return this.store
.getIdentity(item
); // Object
2514 getLabel: function(/*dojo/data/Item*/ item
){
2516 // Get the label for an item
2518 return this.store
.getValue(item
,this.labelAttr
); // String
2520 return this.store
.getLabel(item
); // String
2524 // =======================================================================
2527 newItem: function(/* dijit/tree/dndSource.__Item */ args
, /*dojo/data/api/Item*/ parent, /*int?*/ insertIndex
){
2529 // Creates a new item. See `dojo/data/api/Write` for details on args.
2530 // Used in drag & drop when item from external source dropped onto tree.
2532 // Developers will need to override this method if new items get added
2533 // to parents with multiple children attributes, in order to define which
2534 // children attribute points to the new item.
2536 var pInfo
= {parent
: parent
, attribute
: this.childrenAttrs
[0]}, LnewItem
;
2538 if(this.newItemIdAttr
&& args
[this.newItemIdAttr
]){
2539 // Maybe there's already a corresponding item in the store; if so, reuse it.
2540 this.fetchItemByIdentity({identity
: args
[this.newItemIdAttr
], scope
: this, onItem: function(item
){
2542 // There's already a matching item in store, use it
2543 this.pasteItem(item
, null, parent
, true, insertIndex
);
2545 // Create new item in the tree, based on the drag source.
2546 LnewItem
=this.store
.newItem(args
, pInfo
);
2547 if(LnewItem
&& (insertIndex
!=undefined)){
2548 // Move new item to desired position
2549 this.pasteItem(LnewItem
, parent
, parent
, false, insertIndex
);
2554 // [as far as we know] there is no id so we must assume this is a new item
2555 LnewItem
=this.store
.newItem(args
, pInfo
);
2556 if(LnewItem
&& (insertIndex
!=undefined)){
2557 // Move new item to desired position
2558 this.pasteItem(LnewItem
, parent
, parent
, false, insertIndex
);
2563 pasteItem: function(/*Item*/ childItem
, /*Item*/ oldParentItem
, /*Item*/ newParentItem
, /*Boolean*/ bCopy
, /*int?*/ insertIndex
){
2565 // Move or copy an item from one parent item to another.
2566 // Used in drag & drop
2567 var store
= this.store
,
2568 parentAttr
= this.childrenAttrs
[0]; // name of "children" attr in parent item
2570 // remove child from source item, and record the attribute that child occurred in
2572 array
.forEach(this.childrenAttrs
, function(attr
){
2573 if(store
.containsValue(oldParentItem
, attr
, childItem
)){
2575 var values
= array
.filter(store
.getValues(oldParentItem
, attr
), function(x
){
2576 return x
!= childItem
;
2578 store
.setValues(oldParentItem
, attr
, values
);
2585 // modify target item's children attribute to include this item
2587 if(typeof insertIndex
== "number"){
2588 // call slice() to avoid modifying the original array, confusing the data store
2589 var childItems
= store
.getValues(newParentItem
, parentAttr
).slice();
2590 childItems
.splice(insertIndex
, 0, childItem
);
2591 store
.setValues(newParentItem
, parentAttr
, childItems
);
2593 store
.setValues(newParentItem
, parentAttr
,
2594 store
.getValues(newParentItem
, parentAttr
).concat(childItem
));
2599 // =======================================================================
2602 onChange: function(/*dojo/data/Item*/ /*===== item =====*/){
2604 // Callback whenever an item has changed, so that Tree
2605 // can update the label, icon, etc. Note that changes
2606 // to an item's children or parent(s) will trigger an
2607 // onChildrenChange() so you can ignore those changes here.
2612 onChildrenChange: function(/*===== parent, newChildrenList =====*/){
2614 // Callback to do notifications about new, updated, or deleted items.
2615 // parent: dojo/data/Item
2616 // newChildrenList: dojo/data/Item[]
2621 onDelete: function(/*dojo/data/Item*/ /*===== item =====*/){
2623 // Callback when an item has been deleted.
2625 // Note that there will also be an onChildrenChange() callback for the parent
2631 // =======================================================================
2632 // Events from data store
2634 onNewItem: function(/* dojo/data/Item */ item
, /* Object */ parentInfo
){
2636 // Handler for when new items appear in the store, either from a drop operation
2637 // or some other way. Updates the tree view (if necessary).
2639 // If the new item is a child of an existing item,
2640 // calls onChildrenChange() with the new list of children
2641 // for that existing item.
2646 // We only care about the new item if it has a parent that corresponds to a TreeNode
2647 // we are currently displaying
2652 // Call onChildrenChange() on parent (ie, existing) item with new list of children
2653 // In the common case, the new list of children is simply parentInfo.newValue or
2654 // [ parentInfo.newValue ], although if items in the store has multiple
2655 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
2656 // so call getChildren() to be sure to get right answer.
2657 this.getChildren(parentInfo
.item
, lang
.hitch(this, function(children
){
2658 this.onChildrenChange(parentInfo
.item
, children
);
2662 onDeleteItem: function(/*Object*/ item
){
2664 // Handler for delete notifications from underlying store
2665 this.onDelete(item
);
2668 onSetItem: function(item
, attribute
/*===== , oldValue, newValue =====*/){
2670 // Updates the tree view according to changes in the data store.
2672 // Handles updates to an item's children by calling onChildrenChange(), and
2673 // other updates to an item by calling onChange().
2675 // See `onNewItem` for more details on handling updates to an item's children.
2677 // attribute: attribute-name-string
2678 // oldValue: Object|Array
2679 // newValue: Object|Array
2683 if(array
.indexOf(this.childrenAttrs
, attribute
) != -1){
2684 // item's children list changed
2685 this.getChildren(item
, lang
.hitch(this, function(children
){
2686 // See comments in onNewItem() about calling getChildren()
2687 this.onChildrenChange(item
, children
);
2690 // item's label/icon/etc. changed.
2691 this.onChange(item
);
2698 'dijit/_MenuBase':function(){
2699 define("dijit/_MenuBase", [
2700 "dojo/_base/array", // array.indexOf
2701 "dojo/_base/declare", // declare
2702 "dojo/dom", // dom.isDescendant domClass.replace
2704 "dojo/dom-class", // domClass.replace
2705 "dojo/_base/lang", // lang.hitch
2706 "dojo/mouse", // mouse.enter, mouse.leave
2713 "./_KeyNavContainer",
2715 ], function(array
, declare
, dom
, domAttr
, domClass
, lang
, mouse
, on
, winUtils
,
2716 a11yclick
, pm
, registry
, _Widget
, _KeyNavContainer
, _TemplatedMixin
){
2722 return declare("dijit._MenuBase",
2723 [_Widget
, _TemplatedMixin
, _KeyNavContainer
],
2726 // Base class for Menu and MenuBar
2728 // parentMenu: [readonly] Widget
2729 // pointer to menu that displayed me
2732 // popupDelay: Integer
2733 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
2736 // autoFocus: Boolean
2737 // A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
2738 // or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
2741 childSelector: function(/*DOMNode*/ node
){
2743 // Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
2744 // like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
2748 var widget
= registry
.byNode(node
);
2749 return node
.parentNode
== this.containerNode
&& widget
&& widget
.focus
;
2752 postCreate: function(){
2754 matches
= typeof this.childSelector
== "string" ? this.childSelector
: lang
.hitch(this, "childSelector");
2756 on(this.containerNode
, on
.selector(matches
, mouse
.enter
), function(){
2757 self
.onItemHover(registry
.byNode(this));
2759 on(this.containerNode
, on
.selector(matches
, mouse
.leave
), function(){
2760 self
.onItemUnhover(registry
.byNode(this));
2762 on(this.containerNode
, on
.selector(matches
, a11yclick
), function(evt
){
2763 self
.onItemClick(registry
.byNode(this), evt
);
2764 evt
.stopPropagation();
2765 evt
.preventDefault();
2768 this.inherited(arguments
);
2771 onExecute: function(){
2773 // Attach point for notification about when a menu item has been executed.
2774 // This is an internal mechanism used for Menus to signal to their parent to
2775 // close them, because they are about to execute the onClick handler. In
2776 // general developers should not attach to or override this method.
2781 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
2783 // Attach point for notification about when the user cancels the current menu
2784 // This is an internal mechanism used for Menus to signal to their parent to
2785 // close them. In general developers should not attach to or override this method.
2790 _moveToPopup: function(/*Event*/ evt
){
2792 // This handles the right arrow key (left arrow key on RTL systems),
2793 // which will either open a submenu, or move to the next item in the
2798 if(this.focusedChild
&& this.focusedChild
.popup
&& !this.focusedChild
.disabled
){
2799 this.onItemClick(this.focusedChild
, evt
);
2801 var topMenu
= this._getTopMenu();
2802 if(topMenu
&& topMenu
._isMenuBar
){
2803 topMenu
.focusNext();
2808 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
2810 // This handler is called when the mouse moves over the popup.
2814 // if the mouse hovers over a menu popup that is in pending-close state,
2815 // then stop the close operation.
2816 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
2817 if(this.currentPopup
&& this.currentPopup
._pendingClose_timer
){
2818 var parentMenu
= this.currentPopup
.parentMenu
;
2819 // highlight the parent menu item pointing to this popup
2820 if(parentMenu
.focusedChild
){
2821 parentMenu
.focusedChild
._setSelected(false);
2823 parentMenu
.focusedChild
= this.currentPopup
.from_item
;
2824 parentMenu
.focusedChild
._setSelected(true);
2825 // cancel the pending close
2826 this._stopPendingCloseTimer(this.currentPopup
);
2830 onItemHover: function(/*MenuItem*/ item
){
2832 // Called when cursor is over a MenuItem.
2836 // Don't do anything unless user has "activated" the menu by:
2838 // 2) opening it from a parent menu (which automatically focuses it)
2840 this.focusChild(item
);
2841 if(this.focusedChild
.popup
&& !this.focusedChild
.disabled
&& !this.hover_timer
){
2842 this.hover_timer
= this.defer("_openPopup", this.popupDelay
);
2845 // if the user is mixing mouse and keyboard navigation,
2846 // then the menu may not be active but a menu item has focus,
2847 // but it's not the item that the mouse just hovered over.
2848 // To avoid both keyboard and mouse selections, use the latest.
2849 if(this.focusedChild
){
2850 this.focusChild(item
);
2852 this._hoveredChild
= item
;
2854 item
._set("hovering", true);
2857 _onChildBlur: function(item
){
2859 // Called when a child MenuItem becomes inactive because focus
2860 // has been removed from the MenuItem *and* it's descendant menus.
2863 this._stopPopupTimer();
2864 item
._setSelected(false);
2865 // Close all popups that are open and descendants of this menu
2866 var itemPopup
= item
.popup
;
2868 this._stopPendingCloseTimer(itemPopup
);
2869 itemPopup
._pendingClose_timer
= this.defer(function(){
2870 itemPopup
._pendingClose_timer
= null;
2871 if(itemPopup
.parentMenu
){
2872 itemPopup
.parentMenu
.currentPopup
= null;
2874 pm
.close(itemPopup
); // this calls onClose
2875 }, this.popupDelay
);
2879 onItemUnhover: function(/*MenuItem*/ item
){
2881 // Callback fires when mouse exits a MenuItem
2886 this._stopPopupTimer();
2888 if(this._hoveredChild
== item
){ this._hoveredChild
= null; }
2890 item
._set("hovering", false);
2893 _stopPopupTimer: function(){
2895 // Cancels the popup timer because the user has stop hovering
2896 // on the MenuItem, etc.
2899 if(this.hover_timer
){
2900 this.hover_timer
= this.hover_timer
.remove();
2904 _stopPendingCloseTimer: function(/*dijit/_WidgetBase*/ popup){
2906 // Cancels the pending-close timer because the close has been preempted
2909 if(popup._pendingClose_timer){
2910 popup._pendingClose_timer = popup._pendingClose_timer.remove();
2914 _stopFocusTimer: function(){
2916 // Cancels the pending-focus timer because the menu was closed before focus occured
2919 if(this._focus_timer){
2920 this._focus_timer = this._focus_timer.remove();
2924 _getTopMenu: function(){
2926 // Returns the top menu in this chain of Menus
2929 for(var top=this; top.parentMenu; top=top.parentMenu);
2933 onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt
){
2935 // Handle clicks on an item.
2939 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
2940 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu
2944 this.focusChild(item
);
2946 if(item
.disabled
){ return false; }
2949 this._openPopup(evt
.type
== "keypress");
2951 // before calling user defined handler, close hierarchy of menus
2952 // and restore focus to place it was when menu was opened
2955 // user defined handler for click
2956 item
._onClick
? item
._onClick(evt
) : item
.onClick(evt
);
2960 _openPopup: function(/*Boolean*/ focus
){
2962 // Open the popup to the side of/underneath the current menu item, and optionally focus first item
2966 this._stopPopupTimer();
2967 var from_item
= this.focusedChild
;
2968 if(!from_item
){ return; } // the focused child lost focus since the timer was started
2969 var popup
= from_item
.popup
;
2970 if(!popup
.isShowingNow
){
2971 if(this.currentPopup
){
2972 this._stopPendingCloseTimer(this.currentPopup
);
2973 pm
.close(this.currentPopup
);
2975 popup
.parentMenu
= this;
2976 popup
.from_item
= from_item
; // helps finding the parent item that should be focused for this popup
2981 around
: from_item
.domNode
,
2982 orient
: this._orient
|| ["after", "before"],
2983 onCancel: function(){ // called when the child menu is canceled
2984 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
2985 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
2986 self
.focusChild(from_item
); // put focus back on my node
2987 self
._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
2988 from_item
._setSelected(true); // oops, _cleanUp() deselected the item
2989 self
.focusedChild
= from_item
; // and unset focusedChild
2991 onExecute
: lang
.hitch(this, "_cleanUp")
2994 this.currentPopup
= popup
;
2995 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
2996 popup
.connect(popup
.domNode
, "onmouseenter", lang
.hitch(self
, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
2999 if(focus
&& popup
.focus
){
3000 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar), then focus the popup.
3001 // If the cursor happens to collide with the popup, it will generate an onmouseover event
3002 // even though the mouse wasn't moved. Use defer() to call popup.focus so that
3003 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
3004 popup
._focus_timer
= this.defer(lang
.hitch(popup
, function(){
3005 this._focus_timer
= null;
3011 _markActive: function(){
3013 // Mark this menu's state as active.
3014 // Called when this Menu gets focus from:
3016 // 1. clicking it (mouse or via space/arrow key)
3017 // 2. being opened by a parent menu.
3019 // This is not called just from mouse hover.
3020 // Focusing a menu via TAB does NOT automatically set isActive
3021 // since TAB is a navigation operation and not a selection one.
3022 // For Windows apps, pressing the ALT key focuses the menubar
3023 // menus (similar to TAB navigation) but the menu is not active
3024 // (ie no dropdown) until an item is clicked.
3025 this.isActive
= true;
3026 domClass
.replace(this.domNode
, "dijitMenuActive", "dijitMenuPassive");
3029 onOpen: function(/*Event*/ /*===== e =====*/){
3031 // Callback when this menu is opened.
3032 // This is called by the popup manager as notification that the menu
3037 this.isShowingNow
= true;
3041 _markInactive: function(){
3043 // Mark this menu's state as inactive.
3044 this.isActive
= false; // don't do this in _onBlur since the state is pending-close until we get here
3045 domClass
.replace(this.domNode
, "dijitMenuPassive", "dijitMenuActive");
3048 onClose: function(){
3050 // Callback when this menu is closed.
3051 // This is called by the popup manager as notification that the menu
3056 this._stopFocusTimer();
3057 this._markInactive();
3058 this.isShowingNow
= false;
3059 this.parentMenu
= null;
3062 _closeChild: function(){
3064 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
3067 this._stopPopupTimer();
3069 if(this.currentPopup
){
3070 // If focus is on a descendant MenuItem then move focus to me,
3071 // because IE doesn't like it when you display:none a node with focus,
3072 // and also so keyboard users don't lose control.
3073 // Likely, immediately after a user defined onClick handler will move focus somewhere
3074 // else, like a Dialog.
3075 if(array
.indexOf(this._focusManager
.activeStack
, this.id
) >= 0){
3076 domAttr
.set(this.focusedChild
.focusNode
, "tabIndex", this.tabIndex
);
3077 this.focusedChild
.focusNode
.focus();
3079 // Close all popups that are open and descendants of this menu
3080 pm
.close(this.currentPopup
);
3081 this.currentPopup
= null;
3084 if(this.focusedChild
){ // unhighlight the focused item
3085 this.focusedChild
._setSelected(false);
3086 this.onItemUnhover(this.focusedChild
);
3087 this.focusedChild
= null;
3091 _onItemFocus: function(/*MenuItem*/ item
){
3093 // Called when child of this Menu gets focus from:
3096 // 2. tabbing into it
3097 // 3. being opened by a parent menu.
3099 // This is not called just from mouse hover.
3100 if(this._hoveredChild
&& this._hoveredChild
!= item
){
3101 this.onItemUnhover(this._hoveredChild
); // any previous mouse movement is trumped by focus selection
3105 _onBlur: function(){
3107 // Called when focus is moved away from this Menu and it's submenus.
3111 this.inherited(arguments
);
3114 _cleanUp: function(){
3116 // Called when the user is done with this menu. Closes hierarchy of menus.
3120 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
3121 if(typeof this.isShowingNow
== 'undefined'){ // non-popup menu doesn't call onClose
3122 this._markInactive();
3130 'dijit/focus':function(){
3131 define("dijit/focus", [
3133 "dojo/_base/declare", // declare
3134 "dojo/dom", // domAttr.get dom.isDescendant
3135 "dojo/dom-attr", // domAttr.get dom.isDescendant
3136 "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
3138 "dojo/_base/lang", // lang.hitch
3141 "dojo/sniff", // has("ie")
3143 "dojo/_base/unload", // unload.addOnWindowUnload
3144 "dojo/_base/window", // win.body
3145 "dojo/window", // winUtils.get
3146 "./a11y", // a11y.isTabNavigable
3147 "./registry", // registry.byId
3148 "./main" // to set dijit.focus
3149 ], function(aspect
, declare
, dom
, domAttr
, domConstruct
, Evented
, lang
, on
, ready
, has
, Stateful
, unload
, win
, winUtils
,
3150 a11y
, registry
, dijit
){
3155 var FocusManager
= declare([Stateful
, Evented
], {
3157 // Tracks the currently focused node, and which widgets are currently "active".
3158 // Access via require(["dijit/focus"], function(focus){ ... }).
3160 // A widget is considered active if it or a descendant widget has focus,
3161 // or if a non-focusable node of this widget or a descendant was recently clicked.
3163 // Call focus.watch("curNode", callback) to track the current focused DOMNode,
3164 // or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
3166 // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
3167 // when widgets become active/inactive
3169 // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
3172 // Currently focused item on screen
3175 // activeStack: dijit/_WidgetBase[]
3176 // List of currently active widgets (focused widget and it's ancestors)
3179 constructor: function(){
3180 // Don't leave curNode/prevNode pointing to bogus elements
3181 var check
= lang
.hitch(this, function(node
){
3182 if(dom
.isDescendant(this.curNode
, node
)){
3183 this.set("curNode", null);
3185 if(dom
.isDescendant(this.prevNode
, node
)){
3186 this.set("prevNode", null);
3189 aspect
.before(domConstruct
, "empty", check
);
3190 aspect
.before(domConstruct
, "destroy", check
);
3193 registerIframe: function(/*DomNode*/ iframe
){
3195 // Registers listeners on the specified iframe so that any click
3196 // or focus event on that iframe (or anything in it) is reported
3197 // as a focus/click event on the `<iframe>` itself.
3199 // Currently only used by editor.
3201 // Handle with remove() method to deregister.
3202 return this.registerWin(iframe
.contentWindow
, iframe
);
3205 registerWin: function(/*Window?*/targetWindow
, /*DomNode?*/ effectiveNode
){
3207 // Registers listeners on the specified window (either the main
3208 // window or an iframe's window) to detect when the user has clicked somewhere
3209 // or focused somewhere.
3211 // Users should call registerIframe() instead of this method.
3213 // If specified this is the window associated with the iframe,
3214 // i.e. iframe.contentWindow.
3216 // If specified, report any focus events inside targetWindow as
3217 // an event on effectiveNode, rather than on evt.target.
3219 // Handle with remove() method to deregister.
3221 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
3224 var mousedownListener = function(evt
){
3225 _this
._justMouseDowned
= true;
3226 setTimeout(function(){ _this
._justMouseDowned
= false; }, 0);
3228 // workaround weird IE bug where the click is on an orphaned node
3229 // (first time clicking a Select/DropDownButton inside a TooltipDialog)
3230 if(has("ie") && evt
&& evt
.srcElement
&& evt
.srcElement
.parentNode
== null){
3234 _this
._onTouchNode(effectiveNode
|| evt
.target
|| evt
.srcElement
, "mouse");
3237 // Listen for blur and focus events on targetWindow's document.
3238 // Using attachEvent()/addEventListener() rather than on() to try to catch mouseDown events even
3239 // if other code calls evt.stopPropagation(). But rethink for 2.0 since that doesn't work for attachEvent(),
3240 // which watches events at the bubbling phase rather than capturing phase, like addEventListener(..., false).
3241 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
3242 // (at least for FF) the focus event doesn't fire on <html> or <body>.
3243 var doc
= has("ie") ? targetWindow
.document
.documentElement
: targetWindow
.document
;
3246 targetWindow
.document
.body
.attachEvent('onmousedown', mousedownListener
);
3247 var focusinListener = function(evt
){
3248 // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
3249 // ignore those events
3250 var tag
= evt
.srcElement
.tagName
.toLowerCase();
3251 if(tag
== "#document" || tag
== "body"){ return; }
3253 // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
3254 // probably just ignore such an event as it will be handled by onmousedown handler above, but
3255 // leaving the code for now.
3256 if(a11y
.isTabNavigable(evt
.srcElement
)){
3257 _this
._onFocusNode(effectiveNode
|| evt
.srcElement
);
3259 _this
._onTouchNode(effectiveNode
|| evt
.srcElement
);
3262 doc
.attachEvent('onfocusin', focusinListener
);
3263 var focusoutListener = function(evt
){
3264 _this
._onBlurNode(effectiveNode
|| evt
.srcElement
);
3266 doc
.attachEvent('onfocusout', focusoutListener
);
3270 targetWindow
.document
.detachEvent('onmousedown', mousedownListener
);
3271 doc
.detachEvent('onfocusin', focusinListener
);
3272 doc
.detachEvent('onfocusout', focusoutListener
);
3273 doc
= null; // prevent memory leak (apparent circular reference via closure)
3277 doc
.body
.addEventListener('mousedown', mousedownListener
, true);
3278 doc
.body
.addEventListener('touchstart', mousedownListener
, true);
3279 var focusListener = function(evt
){
3280 _this
._onFocusNode(effectiveNode
|| evt
.target
);
3282 doc
.addEventListener('focus', focusListener
, true);
3283 var blurListener = function(evt
){
3284 _this
._onBlurNode(effectiveNode
|| evt
.target
);
3286 doc
.addEventListener('blur', blurListener
, true);
3290 doc
.body
.removeEventListener('mousedown', mousedownListener
, true);
3291 doc
.body
.removeEventListener('touchstart', mousedownListener
, true);
3292 doc
.removeEventListener('focus', focusListener
, true);
3293 doc
.removeEventListener('blur', blurListener
, true);
3294 doc
= null; // prevent memory leak (apparent circular reference via closure)
3301 _onBlurNode: function(/*DomNode*/ node
){
3303 // Called when focus leaves a node.
3304 // Usually ignored, _unless_ it *isn't* followed by touching another node,
3305 // which indicates that we tabbed off the last field on the page,
3306 // in which case every widget is marked inactive
3308 // If the blur event isn't followed by a focus event, it means the user clicked on something unfocusable,
3310 if(this._clearFocusTimer
){
3311 clearTimeout(this._clearFocusTimer
);
3313 this._clearFocusTimer
= setTimeout(lang
.hitch(this, function(){
3314 this.set("prevNode", this.curNode
);
3315 this.set("curNode", null);
3318 if(this._justMouseDowned
){
3319 // the mouse down caused a new widget to be marked as active; this blur event
3320 // is coming late, so ignore it.
3324 // If the blur event isn't followed by a focus or touch event then mark all widgets as inactive.
3325 if(this._clearActiveWidgetsTimer
){
3326 clearTimeout(this._clearActiveWidgetsTimer
);
3328 this._clearActiveWidgetsTimer
= setTimeout(lang
.hitch(this, function(){
3329 delete this._clearActiveWidgetsTimer
;
3334 _onTouchNode: function(/*DomNode*/ node
, /*String*/ by
){
3336 // Callback when node is focused or mouse-downed
3338 // The node that was touched.
3340 // "mouse" if the focus/touch was caused by a mouse down event
3342 // ignore the recent blurNode event
3343 if(this._clearActiveWidgetsTimer
){
3344 clearTimeout(this._clearActiveWidgetsTimer
);
3345 delete this._clearActiveWidgetsTimer
;
3348 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
3352 var popupParent
= domAttr
.get(node
, "dijitPopupParent");
3354 node
=registry
.byId(popupParent
).domNode
;
3355 }else if(node
.tagName
&& node
.tagName
.toLowerCase() == "body"){
3356 // is this the root of the document or just the root of an iframe?
3357 if(node
=== win
.body()){
3358 // node is the root of the main document
3361 // otherwise, find the iframe this node refers to (can't access it via parentNode,
3362 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
3363 node
=winUtils
.get(node
.ownerDocument
).frameElement
;
3365 // if this node is the root node of a widget, then add widget id to stack,
3366 // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
3367 // to support MenuItem)
3368 var id
= node
.getAttribute
&& node
.getAttribute("widgetId"),
3369 widget
= id
&& registry
.byId(id
);
3370 if(widget
&& !(by
== "mouse" && widget
.get("disabled"))){
3371 newStack
.unshift(id
);
3373 node
=node
.parentNode
;
3376 }catch(e
){ /* squelch */ }
3378 this._setStack(newStack
, by
);
3381 _onFocusNode: function(/*DomNode*/ node
){
3383 // Callback when node is focused
3389 if(node
.nodeType
== 9){
3390 // Ignore focus events on the document itself. This is here so that
3391 // (for example) clicking the up/down arrows of a spinner
3392 // (which don't get focus) won't cause that widget to blur. (FF issue)
3396 // There was probably a blur event right before this event, but since we have a new focus, don't
3397 // do anything with the blur
3398 if(this._clearFocusTimer
){
3399 clearTimeout(this._clearFocusTimer
);
3400 delete this._clearFocusTimer
;
3403 this._onTouchNode(node
);
3405 if(node
== this.curNode
){ return; }
3406 this.set("prevNode", this.curNode
);
3407 this.set("curNode", node
);
3410 _setStack: function(/*String[]*/ newStack
, /*String*/ by
){
3412 // The stack of active widgets has changed. Send out appropriate events and records new stack.
3414 // array of widget id's, starting from the top (outermost) widget
3416 // "mouse" if the focus/touch was caused by a mouse down event
3418 var oldStack
= this.activeStack
;
3419 this.set("activeStack", newStack
);
3421 // compare old stack to new stack to see how many elements they have in common
3422 for(var nCommon
=0; nCommon
<Math
.min(oldStack
.length
, newStack
.length
); nCommon
++){
3423 if(oldStack
[nCommon
] != newStack
[nCommon
]){
3429 // for all elements that have gone out of focus, set focused=false
3430 for(var i
=oldStack
.length
-1; i
>=nCommon
; i
--){
3431 widget
= registry
.byId(oldStack
[i
]);
3433 widget
._hasBeenBlurred
= true; // TODO: used by form widgets, should be moved there
3434 widget
.set("focused", false);
3435 if(widget
._focusManager
== this){
3438 this.emit("widget-blur", widget
, by
);
3442 // for all element that have come into focus, set focused=true
3443 for(i
=nCommon
; i
<newStack
.length
; i
++){
3444 widget
= registry
.byId(newStack
[i
]);
3446 widget
.set("focused", true);
3447 if(widget
._focusManager
== this){
3448 widget
._onFocus(by
);
3450 this.emit("widget-focus", widget
, by
);
3455 focus: function(node
){
3457 // Focus the specified node, suppressing errors if they occur
3459 try{ node
.focus(); }catch(e
){/*quiet*/}
3464 var singleton
= new FocusManager();
3466 // register top window and all the iframes it contains
3468 var handle
= singleton
.registerWin(winUtils
.get(win
.doc
));
3470 unload
.addOnWindowUnload(function(){
3471 if(handle
){ // because this gets called twice when doh.robot is running
3479 // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
3480 // as a function to set focus. Remove for 2.0.
3481 dijit
.focus = function(node
){
3482 singleton
.focus(node
); // indirection here allows dijit/_base/focus.js to override behavior
3484 for(var attr
in singleton
){
3485 if(!/^_/.test(attr
)){
3486 dijit
.focus
[attr
] = typeof singleton
[attr
] == "function" ? lang
.hitch(singleton
, attr
) : singleton
[attr
];
3489 singleton
.watch(function(attr
, oldVal
, newVal
){
3490 dijit
.focus
[attr
] = newVal
;
3497 'dojo/i18n':function(){
3498 define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json", "module"],
3499 function(dojo
, require
, has
, array
, config
, lang
, xhr
, json
, module
){
3504 has
.add("dojo-preload-i18n-Api",
3505 // if true, define the preload localizations machinery
3509 1 || has
.add("dojo-v1x-i18n-Api",
3510 // if true, define the v1.x i18n functions
3515 thisModule
= dojo
.i18n
=
3518 // This module implements the dojo/i18n! plugin and the v1.6- i18n API
3520 // We choose to include our own plugin to leverage functionality already contained in dojo
3521 // and thereby reduce the size of the plugin compared to various loader implementations. Also, this
3522 // allows foreign AMD loaders to be used without their plugins.
3526 // regexp for reconstructing the master bundle name from parts of the regexp match
3527 // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
3528 // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
3529 // nlsRe.exec("foo/bar/baz/nls/foo") gives:
3530 // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
3531 // so, if match[5] is blank, it means this is the top bundle definition.
3532 // courtesy of http://requirejs.org
3533 /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
3535 getAvailableLocales = function(
3542 // return a vector of module ids containing all available locales with respect to the target locale
3543 // For example, assuming:
3545 // - the root bundle indicates specific bundles for "fr" and "fr-ca",
3546 // - bundlePath is "myPackage/nls"
3547 // - bundleName is "myBundle"
3549 // Then a locale argument of "fr-ca" would return
3551 // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
3553 // Notice that bundles are returned least-specific to most-specific, starting with the root.
3555 // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
3556 // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
3558 for(var result
= [bundlePath
+ bundleName
], localeParts
= locale
.split("-"), current
= "", i
= 0; i
<localeParts
.length
; i
++){
3559 current
+= (current
? "-" : "") + localeParts
[i
];
3560 if(!root
|| root
[current
]){
3561 result
.push(bundlePath
+ current
+ "/" + bundleName
);
3569 getBundleName = function(moduleName
, bundleName
, locale
){
3570 locale
= locale
? locale
.toLowerCase() : dojo
.locale
;
3571 moduleName
= moduleName
.replace(/\./g, "/");
3572 bundleName
= bundleName
.replace(/\./g, "/");
3573 return (/root/i.test(locale
)) ?
3574 (moduleName
+ "/nls/" + bundleName
) :
3575 (moduleName
+ "/nls/" + locale
+ "/" + bundleName
);
3578 getL10nName
= dojo
.getL10nName = function(moduleName
, bundleName
, locale
){
3579 return moduleName
= module
.id
+ "!" + getBundleName(moduleName
, bundleName
, locale
);
3582 doLoad = function(require
, bundlePathAndName
, bundlePath
, bundleName
, locale
, load
){
3584 // get the root bundle which instructs which other bundles are required to construct the localized bundle
3585 require([bundlePathAndName
], function(root
){
3586 var current
= lang
.clone(root
.root
),
3587 availableLocales
= getAvailableLocales(!root
._v1x
&& root
, locale
, bundlePath
, bundleName
);
3588 require(availableLocales
, function(){
3589 for (var i
= 1; i
<availableLocales
.length
; i
++){
3590 current
= lang
.mixin(lang
.clone(current
), arguments
[i
]);
3592 // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
3593 var target
= bundlePathAndName
+ "/" + locale
;
3594 cache
[target
] = current
;
3600 normalize = function(id
, toAbsMid
){
3602 // id may be relative.
3603 // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and
3604 // therefore never looks like a relative
3605 return /^\./.test(id
) ? toAbsMid(id
) : id
;
3608 getLocalesToLoad = function(targetLocale
){
3609 var list
= config
.extraLocale
|| [];
3610 list
= lang
.isArray(list
) ? list
: [list
];
3611 list
.push(targetLocale
);
3615 load = function(id
, require
, load
){
3617 // id is in one of the following formats
3619 // 1. <path>/nls/<bundle>
3620 // => load the bundle, localized to config.locale; load all bundles localized to
3621 // config.extraLocale (if any); return the loaded bundle localized to config.locale.
3623 // 2. <path>/nls/<locale>/<bundle>
3624 // => load then return the bundle localized to <locale>
3626 // 3. *preload*<path>/nls/<module>*<JSON array of available locales>
3627 // => for config.locale and all config.extraLocale, load all bundles found
3628 // in the best-matching bundle rollup. A value of 1 is returned, which
3629 // is meaningless other than to say the plugin is executing the requested
3632 // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
3633 // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder.
3635 // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
3636 // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
3638 // <path>/nls/<bundle>/<locale>
3640 // will hold the value. Similarly, then plugin will publish this value to the loader by
3642 // define("<path>/nls/<bundle>/<locale>", <bundle-value>);
3644 // Given this algorithm, other machinery can provide fast load paths be preplacing
3645 // values in the plugin's cache, which is public. When a load is demanded the
3646 // cache is inspected before starting any loading. Explicitly placing values in the plugin
3647 // cache is an advanced/experimental feature that should not be needed; use at your own risk.
3649 // For the normal AMD algorithm, the root bundle is loaded first, which instructs the
3650 // plugin what additional localized bundles are required for a particular locale. These
3651 // additional locales are loaded and a mix of the root and each progressively-specific
3652 // locale is returned. For example:
3654 // 1. The client demands "dojo/i18n!some/path/nls/someBundle
3656 // 2. The loader demands load(some/path/nls/someBundle)
3658 // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
3660 // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
3661 // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
3662 // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
3664 // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
3667 // mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
3668 // require("some/path/nls/ab/someBundle")),
3669 // require("some/path/nls/ab-cd-ef/someBundle"));
3671 // This value is inserted into the cache and published to the loader at the
3672 // key/module-id some/path/nls/someBundle/ab-cd-ef.
3674 // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
3675 // (further preload requests will be serviced) until all ongoing preloading has completed.
3677 // The preload signature instructs the plugin that a special rollup module is available that contains
3678 // one or more flattened, localized bundles. The JSON array of available locales indicates which locales
3679 // are available. Here is an example:
3681 // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
3683 // This indicates the following rollup modules are available:
3685 // some/path/nls/someModule_ROOT
3686 // some/path/nls/someModule_ab
3687 // some/path/nls/someModule_ab-cd-ef
3689 // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
3690 // For example, assume someModule contained the bundles some/bundle/path/someBundle and
3691 // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows:
3694 // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
3695 // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
3698 // E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
3700 // require(["some/path/nls/someModule_ab"], function(rollup){
3701 // for(var p in rollup){
3702 // var id = p + "/ab",
3703 // cache[id] = rollup[p];
3704 // define(id, rollup[p]);
3708 // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
3709 // load accordingly.
3711 // The builder will write such rollups for every layer if a non-empty localeList profile property is
3712 // provided. Further, the builder will include the following cache entry in the cache associated with
3715 // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
3717 // The *now special cache module instructs the loader to apply the provided function to context-require
3718 // with respect to the particular layer being defined. This causes the plugin to hold all normal service
3719 // requests until all preloading is complete.
3721 // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
3722 // where the target locale has a single segment and a layer depends on a single bundle:
3724 // Without Preloads:
3726 // 1. Layer loads root bundle.
3727 // 2. bundle is demanded; plugin loads single localized bundle.
3731 // 1. Layer causes preloading of target bundle.
3732 // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
3734 // In each case a single transaction is required to load the target bundle. In cases where multiple bundles
3735 // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
3736 // the normal path requires an additional transaction for each additional bundle/locale-segment. However all
3737 // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
3738 // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
3740 if(has("dojo-preload-i18n-Api")){
3741 var split
= id
.split("*"),
3742 preloadDemand
= split
[1] == "preload";
3745 // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
3746 // who knows what over-aggressive human optimizers may attempt
3748 preloadL10n(split
[2], json
.parse(split
[3]), 1, require
);
3750 // don't stall the loader!
3753 if(preloadDemand
|| waitForPreloads(id
, require
, load
)){
3758 var match
= nlsRe
.exec(id
),
3759 bundlePath
= match
[1] + "/",
3760 bundleName
= match
[5] || match
[4],
3761 bundlePathAndName
= bundlePath
+ bundleName
,
3762 localeSpecified
= (match
[5] && match
[4]),
3763 targetLocale
= localeSpecified
|| dojo
.locale
,
3764 loadTarget
= bundlePathAndName
+ "/" + targetLocale
,
3765 loadList
= localeSpecified
? [targetLocale
] : getLocalesToLoad(targetLocale
),
3766 remaining
= loadList
.length
,
3767 finish = function(){
3769 load(lang
.delegate(cache
[loadTarget
]));
3772 array
.forEach(loadList
, function(locale
){
3773 var target
= bundlePathAndName
+ "/" + locale
;
3774 if(has("dojo-preload-i18n-Api")){
3775 checkForLegacyModules(target
);
3778 doLoad(require
, bundlePathAndName
, bundlePath
, bundleName
, locale
, finish
);
3785 if(has("dojo-unit-tests")){
3786 var unitTests
= thisModule
.unitTests
= [];
3789 if(has("dojo-preload-i18n-Api") || 1 ){
3790 var normalizeLocale
= thisModule
.normalizeLocale = function(locale
){
3791 var result
= locale
? locale
.toLowerCase() : dojo
.locale
;
3792 return result
== "root" ? "ROOT" : result
;
3795 isXd = function(mid
, contextRequire
){
3797 contextRequire
.isXdUrl(require
.toUrl(mid
+ ".js")) :
3803 preloadWaitQueue
= [],
3805 preloadL10n
= thisModule
._preloadLocalizations = function(/*String*/bundlePrefix
, /*Array*/localesGenerated
, /*boolean?*/ guaranteedAmdFormat
, /*function?*/ contextRequire
){
3807 // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any)
3809 // Only called by built layer files. The entire locale hierarchy is loaded. For example,
3810 // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
3811 // in that the v1.6- would only load ab-cd...which was *always* flattened.
3813 // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
3814 // and the extra possible extra transaction.
3816 // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function
3817 // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which
3818 // itself may have been mapped.
3819 contextRequire
= contextRequire
|| require
;
3821 function doRequire(mid
, callback
){
3822 if(isXd(mid
, contextRequire
) || guaranteedAmdFormat
){
3823 contextRequire([mid
], callback
);
3825 syncRequire([mid
], callback
, contextRequire
);
3829 function forEachLocale(locale
, func
){
3830 // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
3831 var parts
= locale
.split("-");
3832 while(parts
.length
){
3833 if(func(parts
.join("-"))){
3841 function preload(locale
){
3842 locale
= normalizeLocale(locale
);
3843 forEachLocale(locale
, function(loc
){
3844 if(array
.indexOf(localesGenerated
, loc
)>=0){
3845 var mid
= bundlePrefix
.replace(/\./g, "/")+"_"+loc
;
3847 doRequire(mid
, function(rollup
){
3848 for(var p
in rollup
){
3849 cache
[require
.toAbsMid(p
) + "/" + loc
] = rollup
[p
];
3852 while(!preloading
&& preloadWaitQueue
.length
){
3853 load
.apply(null, preloadWaitQueue
.shift());
3863 array
.forEach(dojo
.config
.extraLocale
, preload
);
3866 waitForPreloads = function(id
, require
, load
){
3868 preloadWaitQueue
.push([id
, require
, load
]);
3873 checkForLegacyModules = function()
3878 // this code path assumes the dojo loader and won't work with a standard AMD loader
3881 // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
3883 "__bundle", // the bundle to evalutate
3884 "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
3885 "__mid", // the mid that __bundle is intended to define
3889 // 1 => the bundle was an AMD bundle
3890 // a legacy bundle object that is the value of __mid
3891 // instance of Error => could not figure out how to evaluate bundle
3893 // used to detect when __bundle calls define
3894 "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;},"
3895 + " require = function(){define.called = 1;};"
3898 + "define.called = 0;"
3900 + "if(define.called==1)"
3901 // bundle called define; therefore signal it's an AMD bundle
3902 + "return __amdValue;"
3904 + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
3905 // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
3906 + "return __checkForLegacyModules;"
3909 // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
3910 // either way, re-eval *after* surrounding with parentheses
3913 + "return eval('('+__bundle+')');"
3919 syncRequire = function(deps
, callback
, require
){
3921 array
.forEach(deps
, function(mid
){
3922 var url
= require
.toUrl(mid
+ ".js");
3924 function load(text
){
3925 var result
= evalBundle(text
, checkForLegacyModules
, mid
, amdValue
);
3926 if(result
===amdValue
){
3927 // the bundle was an AMD module; re-inject it through the normal AMD path
3928 // we gotta do this since it could be an anonymous module and simply evaluating
3929 // the text here won't provide the loader with the context to know what
3930 // module is being defined()'d. With browser caching, this should be free; further
3931 // this entire code path can be circumvented by using the AMD format to begin with
3932 results
.push(cache
[url
] = amdValue
.result
);
3934 if(result
instanceof Error
){
3935 console
.error("failed to evaluate i18n bundle; url=" + url
, result
);
3938 // nls/<locale>/<bundle-name> indicates not the root.
3939 results
.push(cache
[url
] = (/nls\/[^\/]+\/[^\/]+$/.test(url
) ? result
: {root
:result
, _v1x
:1}));
3944 results
.push(cache
[url
]);
3946 var bundle
= require
.syncLoadNls(mid
);
3947 // don't need to check for legacy since syncLoadNls returns a module if the module
3948 // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
3949 // from getLocalization --> load, then load will have called checkForLegacyModules() before
3950 // calling syncRequire; if syncRequire is called from preloadLocalizations, then we
3951 // don't care about checkForLegacyModules() because that will be done when a particular
3952 // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
3953 // because cached modules are always v1.7+ built modules.
3955 results
.push(bundle
);
3959 require
.getText(url
, true, load
);
3961 results
.push(cache
[url
] = {});
3969 results
.push(cache
[url
] = {});
3976 callback
&& callback
.apply(null, results
);
3979 checkForLegacyModules = function(target
){
3980 // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
3981 for(var result
, names
= target
.split("/"), object
= dojo
.global
[names
[0]], i
= 1; object
&& i
<names
.length
-1; object
= object
[names
[i
++]]){}
3983 result
= object
[names
[i
]];
3985 // fallback for incorrect bundle build of 1.6
3986 result
= object
[names
[i
].replace(/-/g
,"_")];
3989 cache
[target
] = result
;
3995 thisModule
.getLocalization = function(moduleName
, bundleName
, locale
){
3997 l10nName
= getBundleName(moduleName
, bundleName
, locale
);
4001 // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module.
4002 // Since this legacy function does not have the concept of a reference module, resolve with respect to this
4003 // dojo/i18n module, which, itself may have been mapped.
4004 (!isXd(l10nName
, require
) ? function(deps
, callback
){ syncRequire(deps
, callback
, require
); } : require
),
4006 function(result_
){ result
= result_
; }
4011 if(has("dojo-unit-tests")){
4012 unitTests
.push(function(doh
){
4013 doh
.register("tests.i18n.unit", function(t
){
4016 check
= evalBundle("{prop:1}", checkForLegacyModules
, "nonsense", amdValue
);
4017 t
.is({prop
:1}, check
); t
.is(undefined, check
[1]);
4019 check
= evalBundle("({prop:1})", checkForLegacyModules
, "nonsense", amdValue
);
4020 t
.is({prop
:1}, check
); t
.is(undefined, check
[1]);
4022 check
= evalBundle("{'prop-x':1}", checkForLegacyModules
, "nonsense", amdValue
);
4023 t
.is({'prop-x':1}, check
); t
.is(undefined, check
[1]);
4025 check
= evalBundle("({'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4026 t
.is({'prop-x':1}, check
); t
.is(undefined, check
[1]);
4028 check
= evalBundle("define({'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4029 t
.is(amdValue
, check
); t
.is({'prop-x':1}, amdValue
.result
);
4031 check
= evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules
, "nonsense", amdValue
);
4032 t
.is(amdValue
, check
); t
.is({'prop-x':1}, amdValue
.result
);
4034 check
= evalBundle("this is total nonsense and should throw an error", checkForLegacyModules
, "nonsense", amdValue
);
4035 t
.is(check
instanceof Error
, true);
4041 return lang
.mixin(thisModule
, {
4043 normalize
:normalize
,
4050 'dijit/hccss':function(){
4051 define("dijit/hccss", ["dojo/dom-class", "dojo/hccss", "dojo/ready", "dojo/_base/window"], function(domClass
, has
, ready
, win
){
4059 // Test if computer is in high contrast mode, and sets `dijit_a11y` flag on `<body>` if it is.
4060 // Deprecated, use ``dojo/hccss`` instead.
4064 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
4065 // change this module to depend on dojo/domReady!
4066 ready(90, function(){
4067 if(has("highcontrast")){
4068 domClass
.add(win
.body(), "dijit_a11y");
4076 'dijit/tree/ForestStoreModel':function(){
4077 define("dijit/tree/ForestStoreModel", [
4078 "dojo/_base/array", // array.indexOf array.some
4079 "dojo/_base/declare", // declare
4080 "dojo/_base/kernel", // global
4081 "dojo/_base/lang", // lang.hitch
4083 ], function(array
, declare
, kernel
, lang
, TreeStoreModel
){
4086 // dijit/tree/ForestStoreModel
4088 return declare("dijit.tree.ForestStoreModel", TreeStoreModel
, {
4090 // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4091 // a.k.a. a store that has multiple "top level" items.
4094 // Use this class to wrap a dojo.data store, making all the items matching the specified query
4095 // appear as children of a fabricated "root item". If no query is specified then all the
4096 // items returned by fetch() on the underlying store become children of the root item.
4097 // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
4099 // When using this class the developer must override a number of methods according to their app and
4108 // Parameters to constructor
4111 // ID of fabricated root item
4114 // rootLabel: String
4115 // Label of fabricated root item
4119 // Specifies the set of children of the root item.
4121 // | {type:'continent'}
4124 // End of parameters to constructor
4126 constructor: function(params
){
4128 // Sets up variables, etc.
4132 // Make dummy root item
4137 label
: params
.rootLabel
,
4138 children
: params
.rootChildren
// optional param
4142 // =======================================================================
4143 // Methods for traversing hierarchy
4145 mayHaveChildren: function(/*dojo/data/Item*/ item
){
4147 // Tells if an item has or may have children. Implementing logic here
4148 // avoids showing +/- expando icon for nodes that we know don't have children.
4149 // (For efficiency reasons we may not want to check if an element actually
4150 // has children until user clicks the expando node)
4153 return item
=== this.root
|| this.inherited(arguments
);
4156 getChildren: function(/*dojo/data/Item*/ parentItem
, /*function(items)*/ callback
, /*function*/ onError
){
4158 // Calls onComplete() with array of child items of given parent item, all loaded.
4159 if(parentItem
=== this.root
){
4160 if(this.root
.children
){
4161 // already loaded, just return
4162 callback(this.root
.children
);
4166 onComplete
: lang
.hitch(this, function(items
){
4167 this.root
.children
= items
;
4174 this.inherited(arguments
);
4178 // =======================================================================
4181 isItem: function(/* anything */ something
){
4182 return (something
=== this.root
) ? true : this.inherited(arguments
);
4185 fetchItemByIdentity: function(/* object */ keywordArgs
){
4186 if(keywordArgs
.identity
== this.root
.id
){
4187 var scope
= keywordArgs
.scope
|| kernel
.global
;
4188 if(keywordArgs
.onItem
){
4189 keywordArgs
.onItem
.call(scope
, this.root
);
4192 this.inherited(arguments
);
4196 getIdentity: function(/* item */ item
){
4197 return (item
=== this.root
) ? this.root
.id
: this.inherited(arguments
);
4200 getLabel: function(/* item */ item
){
4201 return (item
=== this.root
) ? this.root
.label
: this.inherited(arguments
);
4204 // =======================================================================
4207 newItem: function(/* dijit/tree/dndSource.__Item */ args
, /*Item*/ parent
, /*int?*/ insertIndex
){
4209 // Creates a new item. See dojo/data/api/Write for details on args.
4210 // Used in drag & drop when item from external source dropped onto tree.
4211 if(parent
=== this.root
){
4212 this.onNewRootItem(args
);
4213 return this.store
.newItem(args
);
4215 return this.inherited(arguments
);
4219 onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
4221 // User can override this method to modify a new element that's being
4222 // added to the root of the tree, for example to add a flag like root=true
4225 pasteItem: function(/*Item*/ childItem
, /*Item*/ oldParentItem
, /*Item*/ newParentItem
, /*Boolean*/ bCopy
, /*int?*/ insertIndex
){
4227 // Move or copy an item from one parent item to another.
4228 // Used in drag & drop
4229 if(oldParentItem
=== this.root
){
4231 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
4232 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4233 // that this element is no longer a child of the root node
4234 this.onLeaveRoot(childItem
);
4237 this.inherited(arguments
, [childItem
,
4238 oldParentItem
=== this.root
? null : oldParentItem
,
4239 newParentItem
=== this.root
? null : newParentItem
,
4243 if(newParentItem
=== this.root
){
4244 // It's onAddToRoot()'s responsibility to modify the item so it matches
4245 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4246 // that this element is now a child of the root node
4247 this.onAddToRoot(childItem
);
4251 // =======================================================================
4252 // Handling for top level children
4254 onAddToRoot: function(/* item */ item
){
4256 // Called when item added to root of tree; user must override this method
4257 // to modify the item so that it matches the query for top level items
4259 // | store.setValue(item, "root", true);
4262 console
.log(this, ": item ", item
, " added to root");
4265 onLeaveRoot: function(/* item */ item
){
4267 // Called when item removed from root of tree; user must override this method
4268 // to modify the item so it doesn't match the query for top level items
4270 // | store.unsetAttribute(item, "root");
4273 console
.log(this, ": item ", item
, " removed from root");
4276 // =======================================================================
4277 // Events from data store
4279 _requeryTop: function(){
4280 // reruns the query for the children of the root node,
4281 // sending out an onSet notification if those children have changed
4282 var oldChildren
= this.root
.children
|| [];
4285 onComplete
: lang
.hitch(this, function(newChildren
){
4286 this.root
.children
= newChildren
;
4288 // If the list of children or the order of children has changed...
4289 if(oldChildren
.length
!= newChildren
.length
||
4290 array
.some(oldChildren
, function(item
, idx
){ return newChildren
[idx
] != item
;})){
4291 this.onChildrenChange(this.root
, newChildren
);
4297 onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo
){
4299 // Handler for when new items appear in the store. Developers should override this
4300 // method to be more efficient based on their app/data.
4302 // Note that the default implementation requeries the top level items every time
4303 // a new item is created, since any new item could be a top level item (even in
4304 // addition to being a child of another item, since items can have multiple parents).
4306 // If developers can detect which items are possible top level items (based on the item and the
4307 // parentInfo parameters), they should override this method to only call _requeryTop() for top
4308 // level items. Often all top level items have parentInfo==null, but
4309 // that will depend on which store you use and what your data is like.
4314 this.inherited(arguments
);
4317 onDeleteItem: function(/*Object*/ item
){
4319 // Handler for delete notifications from underlying store
4321 // check if this was a child of root, and if so send notification that root's children
4323 if(array
.indexOf(this.root
.children
, item
) != -1){
4327 this.inherited(arguments
);
4330 onSetItem: function(/* item */ item
,
4331 /* attribute-name-string */ attribute
,
4332 /* Object|Array */ oldValue
,
4333 /* Object|Array */ newValue
){
4335 // Updates the tree view according to changes to an item in the data store.
4336 // Developers should override this method to be more efficient based on their app/data.
4338 // Handles updates to an item's children by calling onChildrenChange(), and
4339 // other updates to an item by calling onChange().
4341 // Also, any change to any item re-executes the query for the tree's top-level items,
4342 // since this modified item may have started/stopped matching the query for top level items.
4344 // If possible, developers should override this function to only call _requeryTop() when
4345 // the change to the item has caused it to stop/start being a top level item in the tree.
4350 this.inherited(arguments
);
4358 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n",
4359 'dijit/form/_ComboBoxMenuMixin':function(){
4360 define("dijit/form/_ComboBoxMenuMixin", [
4361 "dojo/_base/array", // array.forEach
4362 "dojo/_base/declare", // declare
4363 "dojo/dom-attr", // domAttr.set
4364 "dojo/i18n", // i18n.getLocalization
4365 "dojo/i18n!./nls/ComboBox"
4366 ], function(array
, declare
, domAttr
, i18n
){
4369 // dijit/form/_ComboBoxMenuMixin
4371 return declare( "dijit.form._ComboBoxMenuMixin", null, {
4373 // Focus-less menu for internal use in `dijit/form/ComboBox`
4377 // _messages: Object
4378 // Holds "next" and "previous" text for paging buttons on drop down
4381 postMixInProperties: function(){
4382 this.inherited(arguments
);
4383 this._messages
= i18n
.getLocalization("dijit.form", "ComboBox", this.lang
);
4386 buildRendering: function(){
4387 this.inherited(arguments
);
4389 // fill in template with i18n messages
4390 this.previousButton
.innerHTML
= this._messages
["previousMessage"];
4391 this.nextButton
.innerHTML
= this._messages
["nextMessage"];
4394 _setValueAttr: function(/*Object*/ value
){
4396 this.onChange(value
);
4399 onClick: function(/*DomNode*/ node
){
4400 if(node
== this.previousButton
){
4401 this._setSelectedAttr(null);
4403 }else if(node
== this.nextButton
){
4404 this._setSelectedAttr(null);
4407 this.onChange(node
);
4412 onChange: function(/*Number*/ /*===== direction =====*/){
4414 // Notifies ComboBox/FilteringSelect that user selected an option.
4419 onPage: function(/*Number*/ /*===== direction =====*/){
4421 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
4426 onClose: function(){
4428 // Callback from dijit.popup code to this widget, notifying it that it closed
4431 this._setSelectedAttr(null);
4434 _createOption: function(/*Object*/ item
, labelFunc
){
4436 // Creates an option to appear on the popup menu subclassed by
4437 // `dijit/form/FilteringSelect`.
4439 var menuitem
= this._createMenuItem();
4440 var labelObject
= labelFunc(item
);
4441 if(labelObject
.html
){
4442 menuitem
.innerHTML
= labelObject
.label
;
4444 menuitem
.appendChild(
4445 menuitem
.ownerDocument
.createTextNode(labelObject
.label
)
4448 // #3250: in blank options, assign a normal height
4449 if(menuitem
.innerHTML
== ""){
4450 menuitem
.innerHTML
= " "; //
4453 // update menuitem.dir if BidiSupport was required
4454 this.applyTextDir(menuitem
, (menuitem
.innerText
|| menuitem
.textContent
|| ""));
4459 createOptions: function(results
, options
, labelFunc
){
4461 // Fills in the items in the drop down list
4465 // The options to the query function of the store
4468 // Function to produce a label in the drop down list from a dojo.data item
4470 this.items
= results
;
4472 // display "Previous . . ." button
4473 this.previousButton
.style
.display
= (options
.start
== 0) ? "none" : "";
4474 domAttr
.set(this.previousButton
, "id", this.id
+ "_prev");
4475 // create options using _createOption function defined by parent
4476 // ComboBox (or FilteringSelect) class
4478 // iterate over cache nondestructively
4479 array
.forEach(results
, function(item
, i
){
4480 var menuitem
= this._createOption(item
, labelFunc
);
4481 menuitem
.setAttribute("item", i
); // index to this.items; use indirection to avoid mem leak
4482 domAttr
.set(menuitem
, "id", this.id
+ i
);
4483 this.nextButton
.parentNode
.insertBefore(menuitem
, this.nextButton
);
4485 // display "Next . . ." button
4486 var displayMore
= false;
4487 // Try to determine if we should show 'more'...
4488 if(results
.total
&& !results
.total
.then
&& results
.total
!= -1){
4489 if((options
.start
+ options
.count
) < results
.total
){
4491 }else if((options
.start
+ options
.count
) > results
.total
&& options
.count
== results
.length
){
4492 // Weird return from a data store, where a start + count > maxOptions
4493 // implies maxOptions isn't really valid and we have to go into faking it.
4494 // And more or less assume more if count == results.length
4497 }else if(options
.count
== results
.length
){
4498 //Don't know the size, so we do the best we can based off count alone.
4499 //So, if we have an exact match to count, assume more.
4503 this.nextButton
.style
.display
= displayMore
? "" : "none";
4504 domAttr
.set(this.nextButton
,"id", this.id
+ "_next");
4507 clearResultList: function(){
4509 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
4510 var container
= this.containerNode
;
4511 while(container
.childNodes
.length
> 2){
4512 container
.removeChild(container
.childNodes
[container
.childNodes
.length
-2]);
4514 this._setSelectedAttr(null);
4517 highlightFirstOption: function(){
4519 // Highlight the first real item in the list (not Previous Choices).
4520 this.selectFirstNode();
4523 highlightLastOption: function(){
4525 // Highlight the last real item in the list (not More Choices).
4526 this.selectLastNode();
4529 selectFirstNode: function(){
4530 this.inherited(arguments
);
4531 if(this.getHighlightedOption() == this.previousButton
){
4532 this.selectNextNode();
4536 selectLastNode: function(){
4537 this.inherited(arguments
);
4538 if(this.getHighlightedOption() == this.nextButton
){
4539 this.selectPreviousNode();
4543 getHighlightedOption: function(){
4544 return this.selected
;
4551 'dijit/form/_SearchMixin':function(){
4552 define("dijit/form/_SearchMixin", [
4553 "dojo/data/util/filter", // patternToRegExp
4554 "dojo/_base/declare", // declare
4555 "dojo/_base/event", // event.stop
4556 "dojo/keys", // keys
4557 "dojo/_base/lang", // lang.clone lang.hitch
4558 "dojo/query", // query
4559 "dojo/sniff", // has("ie")
4560 "dojo/string", // string.substitute
4562 "../registry" // registry.byId
4563 ], function(filter
, declare
, event
, keys
, lang
, query
, has
, string
, when
, registry
){
4566 // dijit/form/_SearchMixin
4569 return declare("dijit.form._SearchMixin", null, {
4571 // A mixin that implements the base functionality to search a store based upon user-entered text such as
4572 // with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
4576 // pageSize: Integer
4577 // Argument to data provider.
4578 // Specifies maximum number of search results to return per query
4581 // store: [const] dojo/store/api/Store
4582 // Reference to data provider object used by this ComboBox.
4583 // The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
4586 // fetchProperties: Object
4587 // Mixin to the store's fetch.
4588 // For example, to set the sort order of the ComboBox menu, pass:
4589 // | { sort: [{attribute:"name",descending: true}] }
4590 // To override the default queryOptions so that deep=false, do:
4591 // | { queryOptions: {ignoreCase: true, deep: false} }
4595 // A query that can be passed to `store` to initially filter the items.
4596 // ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
4599 // searchDelay: Integer
4600 // Delay in milliseconds between when user types something and we start
4601 // searching based on that value
4604 // searchAttr: String
4605 // Search for items in the data store where this attribute (in the item)
4606 // matches what the user typed
4609 // queryExpr: String
4610 // This specifies what query is sent to the data store,
4611 // based on what the user has typed. Changing this expression will modify
4612 // whether the results are only exact matches, a "starting with" match,
4614 // dojo.data query expression pattern.
4615 // `${0}` will be substituted for the user text.
4616 // `*` is used for wildcards.
4617 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
4620 // ignoreCase: Boolean
4621 // Set true if the query should ignore case when matching possible items
4624 _abortQuery: function(){
4625 // stop in-progress query
4626 if(this.searchTimer
){
4627 this.searchTimer
= this.searchTimer
.remove();
4629 if(this._queryDeferHandle
){
4630 this._queryDeferHandle
= this._queryDeferHandle
.remove();
4632 if(this._fetchHandle
){
4633 if(this._fetchHandle
.abort
){
4634 this._cancelingQuery
= true;
4635 this._fetchHandle
.abort();
4636 this._cancelingQuery
= false;
4638 if(this._fetchHandle
.cancel
){
4639 this._cancelingQuery
= true;
4640 this._fetchHandle
.cancel();
4641 this._cancelingQuery
= false;
4643 this._fetchHandle
= null;
4647 _processInput: function(/*Event*/ evt
){
4649 // Handles input (keyboard/paste) events
4650 if(this.disabled
|| this.readOnly
){ return; }
4651 var key
= evt
.charOrCode
;
4653 // except for cutting/pasting case - ctrl + x/v
4654 if(evt
.altKey
|| ((evt
.ctrlKey
|| evt
.metaKey
) && (key
!= 'x' && key
!= 'v')) || key
== keys
.SHIFT
){
4655 return; // throw out weird key combinations and spurious events
4658 var doSearch
= false;
4659 this._prev_key_backspace
= false;
4663 case keys
.BACKSPACE
:
4664 this._prev_key_backspace
= true;
4665 this._maskValidSubsetError
= true;
4670 // Non char keys (F1-F12 etc..) shouldn't start a search..
4671 // Ascii characters and IME input (Chinese, Japanese etc.) should.
4672 //IME input produces keycode == 229.
4673 doSearch
= typeof key
== 'string' || key
== 229;
4676 // need to wait a tad before start search so that the event
4677 // bubbles through DOM and we have value visible
4681 this.searchTimer
= this.defer("_startSearchFromInput", 1);
4686 onSearch: function(/*===== results, query, options =====*/){
4688 // Callback when a search completes.
4691 // An array of items from the originating _SearchMixin's store.
4694 // A copy of the originating _SearchMixin's query property.
4697 // The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
4703 _startSearchFromInput: function(){
4704 this._startSearch(this.focusNode
.value
.replace(/([\\\*\?])/g, "\\$1"));
4707 _startSearch: function(/*String*/ text
){
4709 // Starts a search for elements matching text (text=="" means to return all items),
4710 // and calls onSearch(...) when the search completes, to display the results.
4715 // Setup parameters to be passed to store.query().
4716 // Create a new query to prevent accidentally querying for a hidden
4717 // value from FilteringSelect's keyField
4718 query
= lang
.clone(this.query
), // #5970
4721 count
: this.pageSize
,
4722 queryOptions
: { // remove for 2.0
4723 ignoreCase
: this.ignoreCase
,
4727 qs
= string
.substitute(this.queryExpr
, [text
]),
4729 startQuery = function(){
4730 var resPromise
= _this
._fetchHandle
= _this
.store
.query(query
, options
);
4731 if(_this
.disabled
|| _this
.readOnly
|| (q
!== _this
._lastQuery
)){
4733 } // avoid getting unwanted notify
4734 when(resPromise
, function(res
){
4735 _this
._fetchHandle
= null;
4736 if(!_this
.disabled
&& !_this
.readOnly
&& (q
=== _this
._lastQuery
)){ // avoid getting unwanted notify
4737 when(resPromise
.total
, function(total
){
4739 var pageSize
= _this
.pageSize
;
4740 if(isNaN(pageSize
) || pageSize
> res
.total
){ pageSize
= res
.total
; }
4741 // Setup method to fetching the next page of results
4742 res
.nextPage = function(direction
){
4743 // tell callback the direction of the paging so the screen
4744 // reader knows which menu option to shout
4745 options
.direction
= direction
= direction
!== false;
4746 options
.count
= pageSize
;
4748 options
.start
+= res
.length
;
4749 if(options
.start
>= res
.total
){
4753 options
.start
-= pageSize
;
4754 if(options
.start
< 0){
4755 options
.count
= Math
.max(pageSize
+ options
.start
, 0);
4759 if(options
.count
<= 0){
4761 _this
.onSearch(res
, query
, options
);
4766 _this
.onSearch(res
, query
, options
);
4770 _this
._fetchHandle
= null;
4771 if(!_this
._cancelingQuery
){ // don't treat canceled query as an error
4772 console
.error(_this
.declaredClass
+ ' ' + err
.toString());
4777 lang
.mixin(options
, this.fetchProperties
);
4780 if(this.store
._oldAPI
){
4781 // remove this branch for 2.0
4784 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
4785 // but with a toString() method to help dojo/store/JsonRest.
4786 // Search string like "Co*" converted to regex like /^Co.*$/i.
4787 q
= filter
.patternToRegExp(qs
, this.ignoreCase
);
4788 q
.toString = function(){ return qs
; };
4791 // set _lastQuery, *then* start the timeout
4792 // otherwise, if the user types and the last query returns before the timeout,
4793 // _lastQuery won't be set and their input gets rewritten
4794 this._lastQuery
= query
[this.searchAttr
] = q
;
4795 this._queryDeferHandle
= this.defer(startQuery
, this.searchDelay
);
4798 //////////// INITIALIZATION METHODS ///////////////////////////////////////
4800 constructor: function(){
4802 this.fetchProperties
={};
4805 postMixInProperties: function(){
4807 var list
= this.list
;
4809 this.store
= registry
.byId(list
);
4812 this.inherited(arguments
);
4818 'dojo/parser':function(){
4820 "dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window",
4821 "./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"],
4822 function(require
, dojo
, dlang
, darray
, config
, dhtml
, dwindow
, _Url
, djson
, aspect
, dates
, Deferred
, has
, query
, don
, ready
){
4827 new Date("X"); // workaround for #11279, new Date("") == NaN
4830 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
4831 // If BorderContainer is loaded after _Widget's parameter list has been cached,
4832 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
4834 aspect
.after(dlang
, "extend", function(){
4838 function getNameMap(ctor
){
4840 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
4841 var map
= ctor
._nameCaseMap
, proto
= ctor
.prototype;
4843 // Create the map if it's undefined.
4844 // Refresh the map if a superclass was possibly extended with new methods since the map was created.
4845 if(!map
|| map
._extendCnt
< extendCnt
){
4846 map
= ctor
._nameCaseMap
= {};
4847 for(var name
in proto
){
4848 if(name
.charAt(0) === "_"){ continue; } // skip internal properties
4849 map
[name
.toLowerCase()] = name
;
4851 map
._extendCnt
= extendCnt
;
4856 // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor.
4859 function getCtor(/*String[]*/ types
){
4861 // Retrieves a constructor. If the types array contains more than one class/MID then the
4862 // subsequent classes will be mixed into the first class and a unique constructor will be
4863 // returned for that array.
4865 var ts
= types
.join();
4868 for(var i
= 0, l
= types
.length
; i
< l
; i
++){
4870 // TODO: Consider swapping getObject and require in the future
4871 mixins
[mixins
.length
] = (_ctorMap
[t
] = _ctorMap
[t
] || (dlang
.getObject(t
) || (~t
.indexOf('/') && require(t
))));
4873 var ctor
= mixins
.shift();
4874 _ctorMap
[ts
] = mixins
.length
? (ctor
.createSubclass
? ctor
.createSubclass(mixins
) : ctor
.extend
.apply(ctor
, mixins
)) : ctor
;
4877 return _ctorMap
[ts
];
4882 // The Dom/Widget parsing package
4884 _clearCache: function(){
4886 // Clear cached data. Used mainly for benchmarking.
4891 _functionFromScript: function(script
, attrData
){
4893 // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>`
4896 // The `<script>` DOMNode
4898 // For HTML5 compliance, searches for attrData + "args" (typically
4899 // "data-dojo-args") instead of "args"
4902 argsStr
= (script
.getAttribute(attrData
+ "args") || script
.getAttribute("args")),
4903 withStr
= script
.getAttribute("with");
4905 // Convert any arguments supplied in script tag into an array to be passed to the
4906 var fnArgs
= (argsStr
|| "").split(/\s*,\s*/);
4908 if(withStr
&& withStr
.length
){
4909 darray
.forEach(withStr
.split(/\s*,\s*/), function(part
){
4910 preamble
+= "with("+part
+"){";
4915 return new Function(fnArgs
, preamble
+ script
.innerHTML
+ suffix
);
4918 instantiate: function(nodes
, mixin
, options
){
4920 // Takes array of nodes, and turns them into class instances and
4921 // potentially calls a startup method to allow them to connect with
4924 // Array of DOM nodes
4926 // An object that will be mixed in with each node in the array.
4927 // Values in the mixin will override values in the node, if they
4930 // An object used to hold kwArgs for instantiation.
4931 // See parse.options argument for details.
4933 mixin
= mixin
|| {};
4934 options
= options
|| {};
4936 var dojoType
= (options
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
4937 attrData
= "data-" + (options
.scope
|| dojo
._scopeName
) + "-",// typically "data-dojo-"
4938 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
4939 dataDojoMixins
= attrData
+ "mixins"; // typically "data-dojo-mixins"
4942 darray
.forEach(nodes
, function(node
){
4943 var type
= dojoType
in mixin
? mixin
[dojoType
] : node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
);
4945 var mixinsValue
= node
.getAttribute(dataDojoMixins
),
4946 types
= mixinsValue
? [type
].concat(mixinsValue
.split(/\s*,\s*/)) : [type
];
4955 // Instantiate the nodes and return the objects
4956 return this._instantiate(list
, mixin
, options
);
4959 _instantiate: function(nodes
, mixin
, options
){
4961 // Takes array of objects representing nodes, and turns them into class instances and
4962 // potentially calls a startup method to allow them to connect with
4965 // Array of objects like
4967 // | ctor: Function (may be null)
4968 // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified)
4970 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
4971 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
4974 // An object that will be mixed in with each node in the array.
4975 // Values in the mixin will override values in the node, if they
4978 // An options object used to hold kwArgs for instantiation.
4979 // See parse.options argument for details.
4981 // Call widget constructors
4982 var thelist
= darray
.map(nodes
, function(obj
){
4983 var ctor
= obj
.ctor
|| getCtor(obj
.types
);
4984 // If we still haven't resolved a ctor, it is fatal now
4986 throw new Error("Unable to resolve constructor for: '" + obj
.types
.join() + "'");
4988 return this.construct(ctor
, obj
.node
, mixin
, options
, obj
.scripts
, obj
.inherited
);
4991 // Call startup on each top level instance if it makes sense (as for
4992 // widgets). Parent widgets will recursively call startup on their
4993 // (non-top level) children
4994 if(!mixin
._started
&& !options
.noStart
){
4995 darray
.forEach(thelist
, function(instance
){
4996 if(typeof instance
.startup
=== "function" && !instance
._started
){
5005 construct: function(ctor
, node
, mixin
, options
, scripts
, inherited
){
5007 // Calls new ctor(params, node), where params is the hash of parameters specified on the node,
5008 // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget.
5010 // Widget constructor.
5012 // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor.
5014 // Attributes in this object will be passed as parameters to ctor,
5015 // overriding attributes specified on the node.
5017 // An options object used to hold kwArgs for instantiation. See parse.options argument for details.
5018 // scripts: DomNode[]?
5019 // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node.
5020 // inherited: Object?
5021 // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited.
5023 var proto
= ctor
&& ctor
.prototype;
5024 options
= options
|| {};
5026 // Setup hash to hold parameter settings for this widget. Start with the parameter
5027 // settings inherited from ancestors ("dir" and "lang").
5028 // Inherited setting may later be overridden by explicit settings on node itself.
5031 if(options
.defaults
){
5032 // settings for the document itself (or whatever subtree is being parsed)
5033 dlang
.mixin(params
, options
.defaults
);
5036 // settings from dir=rtl or lang=... on a node above this node
5037 dlang
.mixin(params
, inherited
);
5040 // Get list of attributes explicitly listed in the markup
5042 if(has("dom-attributes-explicit")){
5043 // Standard path to get list of user specified attributes
5044 attributes
= node
.attributes
;
5045 }else if(has("dom-attributes-specified-flag")){
5046 // Special processing needed for IE8, to skip a few faux values in attributes[]
5047 attributes
= darray
.filter(node
.attributes
, function(a
){ return a
.specified
;});
5049 // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes
5050 var clone
= /^input$|^img$/i.test(node
.nodeName
) ? node
: node
.cloneNode(false),
5051 attrs
= clone
.outerHTML
.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, "");
5053 attributes
= darray
.map(attrs
.split(/\s+/), function(name
){
5054 var lcName
= name
.toLowerCase();
5057 // getAttribute() doesn't work for button.value, returns innerHTML of button.
5058 // but getAttributeNode().value doesn't work for the form.encType or li.value
5059 value
: (node
.nodeName
== "LI" && name
== "value") || lcName
== "enctype" ?
5060 node
.getAttribute(lcName
) : node
.getAttributeNode(lcName
).value
5065 // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params)
5066 // TODO: remove scope for 2.0
5067 var scope
= options
.scope
|| dojo
._scopeName
,
5068 attrData
= "data-" + scope
+ "-", // typically "data-dojo-"
5070 if(scope
!== "dojo"){
5071 hash
[attrData
+ "props"] = "data-dojo-props";
5072 hash
[attrData
+ "type"] = "data-dojo-type";
5073 hash
[attrData
+ "mixins"] = "data-dojo-mixins";
5074 hash
[scope
+ "type"] = "dojoType";
5075 hash
[attrData
+ "id"] = "data-dojo-id";
5078 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
5079 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
5080 var i
=0, item
, funcAttrs
=[], jsname
, extra
;
5081 while(item
= attributes
[i
++]){
5082 var name
= item
.name
,
5083 lcName
= name
.toLowerCase(),
5086 switch(hash
[lcName
] || lcName
){
5087 // Already processed, just ignore
5088 case "data-dojo-type":
5090 case "data-dojo-mixins":
5093 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
5094 case "data-dojo-props":
5098 // data-dojo-id or jsId. TODO: drop jsId in 2.0
5099 case "data-dojo-id":
5104 // For the benefit of _Templated
5105 case "data-dojo-attach-point":
5106 case "dojoattachpoint":
5107 params
.dojoAttachPoint
= value
;
5109 case "data-dojo-attach-event":
5110 case "dojoattachevent":
5111 params
.dojoAttachEvent
= value
;
5114 // Special parameter handling needed for IE
5116 params
["class"] = node
.className
;
5119 params
["style"] = node
.style
&& node
.style
.cssText
;
5122 // Normal attribute, ex: value="123"
5124 // Find attribute in widget corresponding to specified name.
5125 // May involve case conversion, ex: onclick --> onClick
5126 if(!(name
in proto
)){
5127 var map
= getNameMap(ctor
);
5128 name
= map
[lcName
] || name
;
5131 // Set params[name] to value, doing type conversion
5133 switch(typeof proto
[name
]){
5135 params
[name
] = value
;
5138 params
[name
] = value
.length
? Number(value
) : NaN
;
5141 // for checked/disabled value might be "" or "checked". interpret as true.
5142 params
[name
] = value
.toLowerCase() != "false";
5145 if(value
=== "" || value
.search(/[^\w\.]+/i) != -1){
5146 // The user has specified some text for a function like "return x+5"
5147 params
[name
] = new Function(value
);
5149 // The user has specified the name of a global function like "myOnClick"
5150 // or a single word function "return"
5151 params
[name
] = dlang
.getObject(value
, false) || new Function(value
);
5153 funcAttrs
.push(name
); // prevent "double connect", see #15026
5156 var pVal
= proto
[name
];
5158 (pVal
&& "length" in pVal
) ? (value
? value
.split(/\s*,\s*/) : []) : // array
5159 (pVal
instanceof Date
) ?
5160 (value
== "" ? new Date("") : // the NaN of dates
5161 value
== "now" ? new Date() : // current date
5162 dates
.fromISOString(value
)) :
5163 (pVal
instanceof _Url
) ? (dojo
.baseUrl
+ value
) :
5164 djson
.fromJson(value
);
5167 params
[name
] = value
;
5172 // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026.
5173 // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though).
5174 for(var j
=0; j
<funcAttrs
.length
; j
++){
5175 var lcfname
= funcAttrs
[j
].toLowerCase();
5176 node
.removeAttribute(lcfname
);
5177 node
[lcfname
] = null;
5180 // Mix things found in data-dojo-props into the params, overriding any direct settings
5183 extra
= djson
.fromJson
.call(options
.propsThis
, "{" + extra
+ "}");
5184 dlang
.mixin(params
, extra
);
5186 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
5187 throw new Error(e
.toString() + " in data-dojo-props='" + extra
+ "'");
5191 // Any parameters specified in "mixin" override everything else.
5192 dlang
.mixin(params
, mixin
);
5194 // Get <script> nodes associated with this widget, if they weren't specified explicitly
5196 scripts
= (ctor
&& (ctor
._noScript
|| proto
._noScript
) ? [] : query("> script[type^='dojo/']", node
));
5199 // Process <script type="dojo/*"> script tags
5200 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
5201 // the widget on instantiation.
5202 // <script type="dojo/method"> tags (with no event) are executed after instantiation
5203 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
5204 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
5205 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
5206 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
5207 var aspects
= [], // aspects to connect after instantiation
5208 calls
= [], // functions to call after instantiation
5209 watches
= [], // functions to watch after instantiation
5210 ons
= []; // functions to on after instantiation
5213 for(i
=0; i
<scripts
.length
; i
++){
5214 var script
= scripts
[i
];
5215 node
.removeChild(script
);
5216 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
5217 var event
= (script
.getAttribute(attrData
+ "event") || script
.getAttribute("event")),
5218 prop
= script
.getAttribute(attrData
+ "prop"),
5219 method
= script
.getAttribute(attrData
+ "method"),
5220 advice
= script
.getAttribute(attrData
+ "advice"),
5221 scriptType
= script
.getAttribute("type"),
5222 nf
= this._functionFromScript(script
, attrData
);
5224 if(scriptType
== "dojo/connect"){
5225 aspects
.push({ method
: event
, func
: nf
});
5226 }else if(scriptType
== "dojo/on"){
5227 ons
.push({ event
: event
, func
: nf
});
5231 }else if(scriptType
== "dojo/aspect"){
5232 aspects
.push({ method
: method
, advice
: advice
, func
: nf
});
5233 }else if(scriptType
== "dojo/watch"){
5234 watches
.push({ prop
: prop
, func
: nf
});
5241 // create the instance
5242 var markupFactory
= ctor
.markupFactory
|| proto
.markupFactory
;
5243 var instance
= markupFactory
? markupFactory(params
, node
, ctor
) : new ctor(params
, node
);
5245 // map it to the JS namespace if that makes sense
5247 dlang
.setObject(jsname
, instance
);
5250 // process connections and startup functions
5251 for(i
=0; i
<aspects
.length
; i
++){
5252 aspect
[aspects
[i
].advice
|| "after"](instance
, aspects
[i
].method
, dlang
.hitch(instance
, aspects
[i
].func
), true);
5254 for(i
=0; i
<calls
.length
; i
++){
5255 calls
[i
].call(instance
);
5257 for(i
=0; i
<watches
.length
; i
++){
5258 instance
.watch(watches
[i
].prop
, watches
[i
].func
);
5260 for(i
=0; i
<ons
.length
; i
++){
5261 don(instance
, ons
[i
].event
, ons
[i
].func
);
5267 scan: function(root
, options
){
5269 // Scan a DOM tree and return an array of objects representing the DOMNodes
5270 // that need to be turned into widgets.
5272 // Search specified node (or document root node) recursively for class instances
5273 // and return an array of objects that represent potential widgets to be
5274 // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where
5275 // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name
5276 // like "dijit/form/Button". If the MID is not currently available, scan will
5277 // attempt to require() in the module.
5279 // See parser.parse() for details of markup.
5281 // A default starting root node from which to start the parsing. Can be
5282 // omitted, defaulting to the entire document. If omitted, the `options`
5283 // object can be passed in this place. If the `options` object has a
5284 // `rootNode` member, that is used.
5286 // a kwArgs options object, see parse() for details
5289 // A promise that is resolved with the nodes that have been parsed.
5291 var list
= [], // Output List
5292 mids
= [], // An array of modules that are not yet loaded
5293 midsHash
= {}; // Used to keep the mids array unique
5295 var dojoType
= (options
.scope
|| dojo
._scopeName
) + "Type", // typically "dojoType"
5296 attrData
= "data-" + (options
.scope
|| dojo
._scopeName
) + "-", // typically "data-dojo-"
5297 dataDojoType
= attrData
+ "type", // typically "data-dojo-type"
5298 dataDojoTextDir
= attrData
+ "textdir", // typically "data-dojo-textdir"
5299 dataDojoMixins
= attrData
+ "mixins"; // typically "data-dojo-mixins"
5301 // Info on DOMNode currently being processed
5302 var node
= root
.firstChild
;
5304 // Info on parent of DOMNode currently being processed
5305 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
5306 // - parent: pointer to identical structure for my parent (or null if no parent)
5307 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
5308 var inherited
= options
.inherited
;
5310 function findAncestorAttr(node
, attr
){
5311 return (node
.getAttribute
&& node
.getAttribute(attr
)) ||
5312 (node
.parentNode
&& findAncestorAttr(node
.parentNode
, attr
));
5315 dir
: findAncestorAttr(root
, "dir"),
5316 lang
: findAncestorAttr(root
, "lang"),
5317 textDir
: findAncestorAttr(root
, dataDojoTextDir
)
5319 for(var key
in inherited
){
5320 if(!inherited
[key
]){ delete inherited
[key
]; }
5324 // Metadata about parent node
5326 inherited
: inherited
5329 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
5332 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
5335 function getEffective(parent
){
5337 // Get effective dir, lang, textDir settings for specified obj
5338 // (matching "parent" object structure above), and do caching.
5339 // Take care not to return null entries.
5340 if(!parent
.inherited
){
5341 parent
.inherited
= {};
5342 var node
= parent
.node
,
5343 grandparent
= getEffective(parent
.parent
);
5345 dir
: node
.getAttribute("dir") || grandparent
.dir
,
5346 lang
: node
.getAttribute("lang") || grandparent
.lang
,
5347 textDir
: node
.getAttribute(dataDojoTextDir
) || grandparent
.textDir
5349 for(var key
in inherited
){
5351 parent
.inherited
[key
] = inherited
[key
];
5355 return parent
.inherited
;
5358 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
5361 // Finished this level, continue to parent's next sibling
5362 if(!parent
|| !parent
.node
){
5365 node
= parent
.node
.nextSibling
;
5366 scriptsOnly
= false;
5367 parent
= parent
.parent
;
5368 scripts
= parent
.scripts
;
5372 if(node
.nodeType
!= 1){
5373 // Text or comment node, skip to next sibling
5374 node
= node
.nextSibling
;
5378 if(scripts
&& node
.nodeName
.toLowerCase() == "script"){
5379 // Save <script type="dojo/..."> for parent, then continue to next sibling
5380 type
= node
.getAttribute("type");
5381 if(type
&& /^dojo\/\w/i.test(type
)){
5384 node
= node
.nextSibling
;
5388 // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't
5389 // continue further analysis of the node and will continue to the next sibling
5390 node
= node
.nextSibling
;
5394 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
5395 // TODO: Remove dojoType in 2.0
5396 var type
= node
.getAttribute(dataDojoType
) || node
.getAttribute(dojoType
);
5398 // Short circuit for leaf nodes containing nothing [but text]
5399 var firstChild
= node
.firstChild
;
5400 if(!type
&& (!firstChild
|| (firstChild
.nodeType
== 3 && !firstChild
.nextSibling
))){
5401 node
= node
.nextSibling
;
5405 // Meta data about current node
5410 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate.
5411 var mixinsValue
= node
.getAttribute(dataDojoMixins
),
5412 types
= mixinsValue
? [type
].concat(mixinsValue
.split(/\s*,\s*/)) : [type
];
5414 // Note: won't find classes declared via dojo/Declaration or any modules that haven't been
5415 // loaded yet so use try/catch to avoid throw from require()
5417 ctor
= getCtor(types
);
5420 // If the constructor was not found, check to see if it has modules that can be loaded
5422 darray
.forEach(types
, function(t
){
5423 if(~t
.indexOf('/') && !midsHash
[t
]){
5424 // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it.
5426 mids
[mids
.length
] = t
;
5431 var childScripts
= ctor
&& !ctor
.prototype._noScript
? [] : null; // <script> nodes that are parent's children
5433 // Setup meta data about this widget node, and save it to list of nodes to instantiate
5439 scripts
: childScripts
5441 current
.inherited
= getEffective(current
); // dir & lang settings for current node, explicit or inherited
5444 // Meta data about this non-widget node
5452 // Recurse, collecting <script type="dojo/..."> children, and also looking for
5453 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
5454 // When finished with children, go to my next sibling.
5456 scripts
= childScripts
;
5457 scriptsOnly
= ctor
&& ctor
.prototype.stopParser
&& !(options
.template
);
5461 var d
= new Deferred();
5463 // If there are modules to load then require them in
5465 // Warn that there are modules being auto-required
5466 if(has("dojo-debug-messages")){
5467 console
.warn("WARNING: Modules being Auto-Required: " + mids
.join(", "));
5469 require(mids
, function(){
5470 // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't
5471 // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to
5472 // auto-require of a module like ContentPane. Assumes list is in DFS order.
5473 d
.resolve(darray
.filter(list
, function(widget
){
5475 // Attempt to find the constructor again. Still won't find classes defined via
5476 // dijit/Declaration so need to try/catch.
5478 widget
.ctor
= getCtor(widget
.types
);
5482 // Get the parent widget
5483 var parent
= widget
.parent
;
5484 while(parent
&& !parent
.types
){
5485 parent
= parent
.parent
;
5488 // Return false if this node should be skipped due to stopParser on an ancestor.
5489 // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before
5490 // trying to compute widget.instantiate.
5491 var proto
= widget
.ctor
&& widget
.ctor
.prototype;
5492 widget
.instantiateChildren
= !(proto
&& proto
.stopParser
&& !(options
.template
));
5493 widget
.instantiate
= !parent
|| (parent
.instantiate
&& parent
.instantiateChildren
);
5494 return widget
.instantiate
;
5498 // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for
5499 // efficiency, to avoid running the require() and the callback code above.
5503 // Return the promise
5507 _require: function(/*DOMNode*/ script
){
5509 // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node,
5510 // calls require() to load the specified modules and (asynchronously) assign them to the specified global
5511 // variables, and returns a Promise for when that operation completes.
5513 // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
5515 var hash
= djson
.fromJson("{" + script
.innerHTML
+ "}"),
5520 for(var name
in hash
){
5522 mids
.push(hash
[name
]);
5525 require(mids
, function(){
5526 for(var i
=0; i
<vars
.length
; i
++){
5527 dlang
.setObject(vars
[i
], arguments
[i
]);
5529 d
.resolve(arguments
);
5535 _scanAmd: function(root
){
5537 // Scans the DOM for any declarative requires and returns their values.
5539 // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the
5540 // specified modules and (asynchronously) assign them to the specified global variables,
5541 // and returns a Promise for when those operations complete.
5543 // The node to base the scan from.
5545 // Promise that resolves when all the <script type=dojo/require> nodes have finished loading.
5546 var deferred
= new Deferred(),
5547 promise
= deferred
.promise
;
5548 deferred
.resolve(true);
5551 query("script[type='dojo/require']", root
).forEach(function(node
){
5552 // Fire off require() call for specified modules. Chain this require to fire after
5553 // any previous requires complete, so that layers can be loaded before individual module require()'s fire.
5554 promise
= promise
.then(function(){ return self
._require(node
); });
5556 // Remove from DOM so it isn't seen again
5557 node
.parentNode
.removeChild(node
);
5563 parse: function(rootNode
, options
){
5565 // Scan the DOM for class instances, and instantiate them.
5567 // Search specified node (or root node) recursively for class instances,
5568 // and instantiate them. Searches for either data-dojo-type="Class" or
5569 // dojoType="Class" where "Class" is a a fully qualified class name,
5570 // like `dijit/form/Button`
5572 // Using `data-dojo-type`:
5573 // Attributes using can be mixed into the parameters used to instantiate the
5574 // Class by using a `data-dojo-props` attribute on the node being converted.
5575 // `data-dojo-props` should be a string attribute to be converted from JSON.
5577 // Using `dojoType`:
5578 // Attributes are read from the original domNode and converted to appropriate
5579 // types by looking up the Class prototype values. This is the default behavior
5580 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
5581 // go away in Dojo 2.0.
5582 // rootNode: DomNode?
5583 // A default starting root node from which to start the parsing. Can be
5584 // omitted, defaulting to the entire document. If omitted, the `options`
5585 // object can be passed in this place. If the `options` object has a
5586 // `rootNode` member, that is used.
5588 // A hash of options.
5590 // - noStart: Boolean?:
5591 // when set will prevent the parser from calling .startup()
5592 // when locating the nodes.
5593 // - rootNode: DomNode?:
5594 // identical to the function's `rootNode` argument, though
5595 // allowed to be passed in via this `options object.
5596 // - template: Boolean:
5597 // If true, ignores ContentPane's stopParser flag and parses contents inside of
5598 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
5599 // nested inside the ContentPane to work.
5600 // - inherited: Object:
5601 // Hash possibly containing dir and lang settings to be applied to
5602 // parsed widgets, unless there's another setting on a sub-node that overrides
5604 // Root for attribute names to search for. If scopeName is dojo,
5605 // will search for data-dojo-type (or dojoType). For backwards compatibility
5606 // reasons defaults to dojo._scopeName (which is "dojo" except when
5607 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
5608 // - propsThis: Object:
5609 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
5610 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
5612 // Returns a blended object that is an array of the instantiated objects, but also can include
5613 // a promise that is resolved with the instantiated objects. This is done for backwards
5614 // compatibility. If the parser auto-requires modules, it will always behave in a promise
5615 // fashion and `parser.parse().then(function(instances){...})` should be used.
5617 // Parse all widgets on a page:
5618 // | parser.parse();
5620 // Parse all classes within the node with id="foo"
5621 // | parser.parse(dojo.byId('foo'));
5623 // Parse all classes in a page, but do not call .startup() on any
5625 // | parser.parse({ noStart: true })
5627 // Parse all classes in a node, but do not call .startup()
5628 // | parser.parse(someNode, { noStart:true });
5630 // | parser.parse({ noStart:true, rootNode: someNode });
5632 // determine the root node and options based on the passed arguments.
5634 if(!options
&& rootNode
&& rootNode
.rootNode
){
5636 root
= options
.rootNode
;
5637 }else if(rootNode
&& dlang
.isObject(rootNode
) && !("nodeType" in rootNode
)){
5642 root
= root
? dhtml
.byId(root
) : dwindow
.body();
5644 options
= options
|| {};
5646 var mixin
= options
.template
? { template
: true } : {},
5650 // First scan for any <script type=dojo/require> nodes, and execute.
5651 // Then scan for all nodes with data-dojo-type, and load any unloaded modules.
5652 // Then build the object instances. Add instances to already existing (but empty) instances[] array,
5653 // which may already have been returned to caller. Also, use otherwise to collect and throw any errors
5654 // that occur during the parse().
5656 this._scanAmd(root
, options
).then(function(){
5657 return self
.scan(root
, options
);
5658 }).then(function(parsedNodes
){
5659 return instances
= instances
.concat(self
._instantiate(parsedNodes
, mixin
, options
));
5660 }).otherwise(function(e
){
5661 // TODO Modify to follow better pattern for promise error managment when available
5662 console
.error("dojo/parser::parse() error", e
);
5666 // Blend the array with the promise
5667 dlang
.mixin(instances
, p
);
5673 dojo
.parser
= parser
;
5676 // Register the parser callback. It should be the first callback
5677 // after the a11y test.
5678 if(config
.parseOnLoad
){
5679 ready(100, parser
, "parse");
5686 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">▼</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n",
5687 'dojo/dnd/Manager':function(){
5688 define("dojo/dnd/Manager", [
5689 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window",
5690 "../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch",
5691 "./common", "./autoscroll", "./Avatar"
5692 ], function(array
, declare
, event
, lang
, win
, domClass
, Evented
, has
, keys
, on
, topic
, touch
,
5693 dnd
, autoscroll
, Avatar
){
5698 var Manager
= declare("dojo.dnd.Manager", [Evented
], {
5700 // the manager of DnD operations (usually a singleton)
5701 constructor: function(){
5707 this.canDropFlag
= false;
5711 // avatar's offset from the mouse
5712 OFFSET_X
: has("touch") ? 0 : 16,
5713 OFFSET_Y
: has("touch") ? -64 : 16,
5716 overSource: function(source
){
5718 // called when a source detected a mouse-over condition
5722 this.target
= (source
&& source
.targetState
!= "Disabled") ? source
: null;
5723 this.canDropFlag
= Boolean(this.target
);
5724 this.avatar
.update();
5726 topic
.publish("/dnd/source/over", source
);
5728 outSource: function(source
){
5730 // called when a source detected a mouse-out condition
5734 if(this.target
== source
){
5736 this.canDropFlag
= false;
5737 this.avatar
.update();
5738 topic
.publish("/dnd/source/over", null);
5741 topic
.publish("/dnd/source/over", null);
5744 startDrag: function(source
, nodes
, copy
){
5746 // called to initiate the DnD operation
5748 // the source which provides items
5750 // the list of transferred items
5752 // copy items, if true, move items otherwise
5754 // Tell autoscroll that a drag is starting
5755 autoscroll
.autoScrollStart(win
.doc
);
5757 this.source
= source
;
5759 this.copy
= Boolean(copy
); // normalizing to true boolean
5760 this.avatar
= this.makeAvatar();
5761 win
.body().appendChild(this.avatar
.node
);
5762 topic
.publish("/dnd/start", source
, nodes
, this.copy
);
5764 on(win
.doc
, touch
.move, lang
.hitch(this, "onMouseMove")),
5765 on(win
.doc
, touch
.release
, lang
.hitch(this, "onMouseUp")),
5766 on(win
.doc
, "keydown", lang
.hitch(this, "onKeyDown")),
5767 on(win
.doc
, "keyup", lang
.hitch(this, "onKeyUp")),
5768 // cancel text selection and text dragging
5769 on(win
.doc
, "dragstart", event
.stop
),
5770 on(win
.body(), "selectstart", event
.stop
)
5772 var c
= "dojoDnd" + (copy
? "Copy" : "Move");
5773 domClass
.add(win
.body(), c
);
5775 canDrop: function(flag
){
5777 // called to notify if the current target can accept items
5778 var canDropFlag
= Boolean(this.target
&& flag
);
5779 if(this.canDropFlag
!= canDropFlag
){
5780 this.canDropFlag
= canDropFlag
;
5781 this.avatar
.update();
5784 stopDrag: function(){
5786 // stop the DnD in progress
5787 domClass
.remove(win
.body(), ["dojoDndCopy", "dojoDndMove"]);
5788 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
5790 this.avatar
.destroy();
5792 this.source
= this.target
= null;
5795 makeAvatar: function(){
5797 // makes the avatar; it is separate to be overwritten dynamically, if needed
5798 return new Avatar(this);
5800 updateAvatar: function(){
5802 // updates the avatar; it is separate to be overwritten dynamically, if needed
5803 this.avatar
.update();
5806 // mouse event processors
5807 onMouseMove: function(e
){
5809 // event processor for onmousemove
5812 var a
= this.avatar
;
5814 autoscroll
.autoScrollNodes(e
);
5815 //autoscroll.autoScroll(e);
5816 var s
= a
.node
.style
;
5817 s
.left
= (e
.pageX
+ this.OFFSET_X
) + "px";
5818 s
.top
= (e
.pageY
+ this.OFFSET_Y
) + "px";
5819 var copy
= Boolean(this.source
.copyState(dnd
.getCopyKeyState(e
)));
5820 if(this.copy
!= copy
){
5821 this._setCopyStatus(copy
);
5825 // Prevent page from scrolling so that user can drag instead.
5829 onMouseUp: function(e
){
5831 // event processor for onmouseup
5835 if(this.target
&& this.canDropFlag
){
5836 var copy
= Boolean(this.source
.copyState(dnd
.getCopyKeyState(e
)));
5837 topic
.publish("/dnd/drop/before", this.source
, this.nodes
, copy
, this.target
, e
);
5838 topic
.publish("/dnd/drop", this.source
, this.nodes
, copy
, this.target
, e
);
5840 topic
.publish("/dnd/cancel");
5846 // keyboard event processors
5847 onKeyDown: function(e
){
5849 // event processor for onkeydown:
5850 // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
5856 var copy
= Boolean(this.source
.copyState(true));
5857 if(this.copy
!= copy
){
5858 this._setCopyStatus(copy
);
5862 topic
.publish("/dnd/cancel");
5868 onKeyUp: function(e
){
5870 // event processor for onkeyup, watching for CTRL for copy/move status
5873 if(this.avatar
&& e
.keyCode
== keys
.CTRL
){
5874 var copy
= Boolean(this.source
.copyState(false));
5875 if(this.copy
!= copy
){
5876 this._setCopyStatus(copy
);
5882 _setCopyStatus: function(copy
){
5884 // changes the copy status
5888 this.source
._markDndStatus(this.copy
);
5889 this.updateAvatar();
5890 domClass
.replace(win
.body(),
5891 "dojoDnd" + (this.copy
? "Copy" : "Move"),
5892 "dojoDnd" + (this.copy
? "Move" : "Copy"));
5897 // The manager singleton variable. Can be overwritten if needed.
5898 dnd
._manager
= null;
5900 Manager
.manager
= dnd
.manager = function(){
5902 // Returns the current DnD manager. Creates one if it is not created yet.
5904 dnd
._manager
= new Manager();
5906 return dnd
._manager
; // Object
5913 'dijit/form/ToggleButton':function(){
5914 define("dijit/form/ToggleButton", [
5915 "dojo/_base/declare", // declare
5916 "dojo/_base/kernel", // kernel.deprecated
5918 "./_ToggleButtonMixin"
5919 ], function(declare
, kernel
, Button
, _ToggleButtonMixin
){
5922 // dijit/form/ToggleButton
5925 return declare("dijit.form.ToggleButton", [Button
, _ToggleButtonMixin
], {
5927 // A templated button widget that can be in two states (checked or not).
5928 // Can be base class for things like tabs or checkbox or radio buttons.
5930 baseClass
: "dijitToggleButton",
5932 setChecked: function(/*Boolean*/ checked
){
5934 // Deprecated. Use set('checked', true/false) instead.
5935 kernel
.deprecated("setChecked("+checked
+") is deprecated. Use set('checked',"+checked
+") instead.", "", "2.0");
5936 this.set('checked', checked
);
5942 'dojo/date/stamp':function(){
5943 define("dojo/date/stamp", ["../_base/lang", "../_base/array"], function(lang
, array
){
5952 lang
.setObject("dojo.date.stamp", stamp
);
5954 // Methods to convert dates to or from a wire (string) format using well-known conventions
5956 stamp
.fromISOString = function(/*String*/ formattedString
, /*Number?*/ defaultTime
){
5958 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
5961 // Accepts a string formatted according to a profile of ISO8601 as defined by
5962 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
5963 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
5964 // The following combinations are valid:
5970 // - times only, with an optional time zone appended
5974 // - and "datetimes" which could be any combination of the above
5976 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
5977 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
5978 // input may return null. Arguments which are out of bounds will be handled
5979 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
5980 // Only years between 100 and 9999 are supported.
5982 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
5984 // Used for defaults for fields omitted in the formattedString.
5985 // Uses 1970-01-01T00:00:00.0Z by default.
5987 if(!stamp
._isoRegExp
){
5989 //TODO: could be more restrictive and check for 00-59, etc.
5990 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
5993 var match
= stamp
._isoRegExp
.exec(formattedString
),
5998 if(match
[1]){match
[1]--;} // Javascript Date months are 0-based
5999 if(match
[6]){match
[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
6002 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
6003 defaultTime
= new Date(defaultTime
);
6004 array
.forEach(array
.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop
){
6005 return defaultTime
["get" + prop
]();
6006 }), function(value
, index
){
6007 match
[index
] = match
[index
] || value
;
6010 result
= new Date(match
[0]||1970, match
[1]||0, match
[2]||1, match
[3]||0, match
[4]||0, match
[5]||0, match
[6]||0); //TODO: UTC defaults
6012 result
.setFullYear(match
[0] || 1970);
6016 zoneSign
= match
[7] && match
[7].charAt(0);
6017 if(zoneSign
!= 'Z'){
6018 offset
= ((match
[8] || 0) * 60) + (Number(match
[9]) || 0);
6019 if(zoneSign
!= '-'){ offset
*= -1; }
6022 offset
-= result
.getTimezoneOffset();
6025 result
.setTime(result
.getTime() + offset
* 60000);
6029 return result
; // Date or null
6035 // "date" or "time" for partial formatting of the Date object.
6036 // Both date and time will be formatted by default.
6038 // if true, UTC/GMT is used for a timezone
6039 // milliseconds: Boolean
6040 // if true, output milliseconds
6044 stamp
.toISOString = function(/*Date*/ dateObject
, /*__Options?*/ options
){
6046 // Format a Date object as a string according a subset of the ISO-8601 standard
6049 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
6050 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
6051 // Does not check bounds. Only years between 100 and 9999 are supported.
6056 var _ = function(n
){ return (n
< 10) ? "0" + n
: n
; };
6057 options
= options
|| {};
6058 var formattedDate
= [],
6059 getter
= options
.zulu
? "getUTC" : "get",
6061 if(options
.selector
!= "time"){
6062 var year
= dateObject
[getter
+"FullYear"]();
6063 date
= ["0000".substr((year
+"").length
)+year
, _(dateObject
[getter
+"Month"]()+1), _(dateObject
[getter
+"Date"]())].join('-');
6065 formattedDate
.push(date
);
6066 if(options
.selector
!= "date"){
6067 var time
= [_(dateObject
[getter
+"Hours"]()), _(dateObject
[getter
+"Minutes"]()), _(dateObject
[getter
+"Seconds"]())].join(':');
6068 var millis
= dateObject
[getter
+"Milliseconds"]();
6069 if(options
.milliseconds
){
6070 time
+= "."+ (millis
< 100 ? "0" : "") + _(millis
);
6074 }else if(options
.selector
!= "time"){
6075 var timezoneOffset
= dateObject
.getTimezoneOffset();
6076 var absOffset
= Math
.abs(timezoneOffset
);
6077 time
+= (timezoneOffset
> 0 ? "-" : "+") +
6078 _(Math
.floor(absOffset
/60)) + ":" + _(absOffset
%60);
6080 formattedDate
.push(time
);
6082 return formattedDate
.join('T'); // String
6089 'dojo/Stateful':function(){
6090 define("dojo/Stateful", ["./_base/declare", "./_base/lang", "./_base/array", "dojo/when"], function(declare
, lang
, array
, when
){
6094 return declare("dojo.Stateful", null, {
6096 // Base class for objects that provide named properties with optional getter/setter
6097 // control and the ability to watch for property changes
6099 // The class also provides the functionality to auto-magically manage getters
6100 // and setters for object attributes/properties.
6102 // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
6103 // the xxx is a name of the attribute to handle. So an attribute of "foo"
6104 // would have a custom getter of _fooGetter and a custom setter of _fooSetter.
6107 // | var obj = new dojo.Stateful();
6108 // | obj.watch("foo", function(){
6109 // | console.log("foo changed to " + this.get("foo"));
6111 // | obj.set("foo","bar");
6113 // _attrPairNames: Hash
6114 // Used across all instances a hash to cache attribute names and their getter
6115 // and setter names.
6118 _getAttrNames: function(name
){
6120 // Helper function for get() and set().
6121 // Caches attribute name values so we don't do the string ops every time.
6125 var apn
= this._attrPairNames
;
6126 if(apn
[name
]){ return apn
[name
]; }
6127 return (apn
[name
] = {
6128 s
: "_" + name
+ "Setter",
6129 g
: "_" + name
+ "Getter"
6133 postscript: function(/*Object?*/ params
){
6134 // Automatic setting of params during construction
6135 if (params
){ this.set(params
); }
6138 _get: function(name
, names
){
6140 // Private function that does a get based off a hash of names
6142 // Hash of names of custom attributes
6143 return typeof this[names
.g
] === "function" ? this[names
.g
]() : this[name
];
6145 get: function(/*String*/name
){
6147 // Get a property on a Stateful instance.
6149 // The property to get.
6151 // The property value on this Stateful instance.
6153 // Get a named property on a Stateful object. The property may
6154 // potentially be retrieved via a getter method in subclasses. In the base class
6155 // this just retrieves the object's property.
6157 // | stateful = new dojo.Stateful({foo: 3});
6158 // | stateful.get("foo") // returns 3
6159 // | stateful.foo // returns 3
6161 return this._get(name
, this._getAttrNames(name
)); //Any
6163 set: function(/*String*/name
, /*Object*/value
){
6165 // Set a property on a Stateful instance
6167 // The property to set.
6169 // The value to set in the property.
6171 // The function returns this dojo.Stateful instance.
6173 // Sets named properties on a stateful object and notifies any watchers of
6174 // the property. A programmatic setter may be defined in subclasses.
6176 // | stateful = new dojo.Stateful();
6177 // | stateful.watch(function(name, oldValue, value){
6178 // | // this will be called on the set below
6180 // | stateful.set(foo, 5);
6182 // set() may also be called with a hash of name/value pairs, ex:
6187 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
6189 // If an object is used, iterate through object
6190 if(typeof name
=== "object"){
6192 if(name
.hasOwnProperty(x
) && x
!="_watchCallbacks"){
6193 this.set(x
, name
[x
]);
6199 var names
= this._getAttrNames(name
),
6200 oldValue
= this._get(name
, names
),
6201 setter
= this[names
.s
],
6203 if(typeof setter
=== "function"){
6204 // use the explicit setter
6205 result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
6207 // no setter so set attribute directly
6210 if(this._watchCallbacks
){
6212 // If setter returned a promise, wait for it to complete, otherwise call watches immediatly
6213 when(result
, function(){
6214 self
._watchCallbacks(name
, oldValue
, value
);
6217 return this; // dojo/Stateful
6219 _changeAttrValue: function(name
, value
){
6221 // Internal helper for directly changing an attribute value.
6224 // The property to set.
6226 // The value to set in the property.
6229 // Directly change the value of an attribute on an object, bypassing any
6230 // accessor setter. Also handles the calling of watch and emitting events.
6231 // It is designed to be used by descendent class when there are two values
6232 // of attributes that are linked, but calling .set() is not appropriate.
6234 var oldValue
= this.get(name
);
6236 if(this._watchCallbacks
){
6237 this._watchCallbacks(name
, oldValue
, value
);
6239 return this; // dojo/Stateful
6241 watch: function(/*String?*/name
, /*Function*/callback
){
6243 // Watches a property for changes
6245 // Indicates the property to watch. This is optional (the callback may be the
6246 // only parameter), and if omitted, all the properties will be watched
6248 // An object handle for the watch. The unwatch method of this object
6249 // can be used to discontinue watching this property:
6250 // | var watchHandle = obj.watch("foo", callback);
6251 // | watchHandle.unwatch(); // callback won't be called now
6253 // The function to execute when the property changes. This will be called after
6254 // the property has been changed. The callback will be called with the |this|
6255 // set to the instance, the first argument as the name of the property, the
6256 // second argument as the old value and the third argument as the new value.
6258 var callbacks
= this._watchCallbacks
;
6261 callbacks
= this._watchCallbacks = function(name
, oldValue
, value
, ignoreCatchall
){
6262 var notify = function(propertyCallbacks
){
6263 if(propertyCallbacks
){
6264 propertyCallbacks
= propertyCallbacks
.slice();
6265 for(var i
= 0, l
= propertyCallbacks
.length
; i
< l
; i
++){
6266 propertyCallbacks
[i
].call(self
, name
, oldValue
, value
);
6270 notify(callbacks
['_' + name
]);
6271 if(!ignoreCatchall
){
6272 notify(callbacks
["*"]); // the catch-all
6274 }; // we use a function instead of an object so it will be ignored by JSON conversion
6276 if(!callback
&& typeof name
=== "function"){
6280 // prepend with dash to prevent name conflicts with function (like "name" property)
6283 var propertyCallbacks
= callbacks
[name
];
6284 if(typeof propertyCallbacks
!== "object"){
6285 propertyCallbacks
= callbacks
[name
] = [];
6287 propertyCallbacks
.push(callback
);
6289 // TODO: Remove unwatch in 2.0
6291 handle
.unwatch
= handle
.remove = function(){
6292 var index
= array
.indexOf(propertyCallbacks
, callback
);
6294 propertyCallbacks
.splice(index
, 1);
6297 return handle
; //Object
6305 'dijit/layout/AccordionContainer':function(){
6307 'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
6308 define("dijit/layout/AccordionContainer", [
6310 "dojo/_base/array", // array.forEach array.map
6311 "dojo/_base/declare", // declare
6312 "dojo/_base/event", // event.stop
6313 "dojo/_base/fx", // fx.Animation
6314 "dojo/dom", // dom.setSelectable
6315 "dojo/dom-attr", // domAttr.attr
6316 "dojo/dom-class", // domClass.remove
6317 "dojo/dom-construct", // domConstruct.place
6318 "dojo/dom-geometry",
6319 "dojo/keys", // keys
6320 "dojo/_base/lang", // lang.getObject lang.hitch
6321 "dojo/sniff", // has("ie") has("dijit-legacy-requires")
6322 "dojo/topic", // publish
6323 "../focus", // focus.focus()
6324 "../_base/manager", // manager.defaultDuration
6328 "../_TemplatedMixin",
6329 "../_CssStateMixin",
6332 "dojo/text!./templates/AccordionButton.html"
6333 ], function(require
, array
, declare
, event
, fx
, dom
, domAttr
, domClass
, domConstruct
, domGeometry
,
6334 keys
, lang
, has
, topic
, focus
, manager
, ready
,
6335 _Widget
, _Container
, _TemplatedMixin
, _CssStateMixin
, StackContainer
, ContentPane
, template
){
6338 // dijit/layout/AccordionContainer
6343 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
6344 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
6346 // The resulting markup will look like:
6348 // <div class=dijitAccordionContainer>
6349 // <div class=dijitAccordionInnerContainer> (one pane)
6350 // <div class=dijitAccordionTitle> (title bar) ... </div>
6351 // <div class=dijtAccordionChildWrapper> (content pane) </div>
6355 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
6356 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
6357 // which on claro has a 1px border plus a 2px bottom margin.
6359 // During animation there are two dijtAccordionChildWrapper's shown, so we need
6360 // to compensate for that.
6363 var AccordionButton
= declare("dijit.layout._AccordionButton", [_Widget
, _TemplatedMixin
, _CssStateMixin
], {
6365 // The title bar to click to open up an accordion pane.
6366 // Internal widget used by AccordionContainer.
6370 templateString
: template
,
6373 // Title of the pane
6375 _setLabelAttr
: {node
: "titleTextNode", type
: "innerHTML" },
6378 // Tooltip that appears on hover
6380 _setTitleAttr
: {node
: "titleTextNode", type
: "attribute", attribute
: "title"},
6382 // iconClassAttr: String
6383 // CSS class for icon to left of label
6385 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
6387 baseClass
: "dijitAccordionTitle",
6389 getParent: function(){
6391 // Returns the AccordionContainer parent.
6397 buildRendering: function(){
6398 this.inherited(arguments
);
6399 var titleTextNodeId
= this.id
.replace(' ','_');
6400 domAttr
.set(this.titleTextNode
, "id", titleTextNodeId
+"_title");
6401 this.focusNode
.setAttribute("aria-labelledby", domAttr
.get(this.titleTextNode
, "id"));
6402 dom
.setSelectable(this.domNode
, false);
6405 getTitleHeight: function(){
6407 // Returns the height of the title dom node.
6408 return domGeometry
.getMarginSize(this.domNode
).h
; // Integer
6411 // TODO: maybe the parent should set these methods directly rather than forcing the code
6412 // into the button widget?
6413 _onTitleClick: function(){
6415 // Callback when someone clicks my title.
6416 var parent
= this.getParent();
6417 parent
.selectChild(this.contentWidget
, true);
6418 focus
.focus(this.focusNode
);
6421 _onTitleKeyPress: function(/*Event*/ evt
){
6422 return this.getParent()._onKeyPress(evt
, this.contentWidget
);
6425 _setSelectedAttr: function(/*Boolean*/ isSelected
){
6426 this._set("selected", isSelected
);
6427 this.focusNode
.setAttribute("aria-expanded", isSelected
? "true" : "false");
6428 this.focusNode
.setAttribute("aria-selected", isSelected
? "true" : "false");
6429 this.focusNode
.setAttribute("tabIndex", isSelected
? "0" : "-1");
6433 var AccordionInnerContainer
= declare("dijit.layout._AccordionInnerContainer", [_Widget
, _CssStateMixin
], {
6435 // Internal widget placed as direct child of AccordionContainer.containerNode.
6436 // When other widgets are added as children to an AccordionContainer they are wrapped in
6440 // buttonWidget: Function|String
6441 // Class to use to instantiate title
6442 // (Wish we didn't have a separate widget for just the title but maintaining it
6443 // for backwards compatibility, is it worth it?)
6448 // contentWidget: dijit/_WidgetBase
6449 // Pointer to the real child widget
6450 contentWidget: null,
6453 baseClass
: "dijitAccordionInnerContainer",
6455 // tell nested layout widget that we will take care of sizing
6456 isLayoutContainer
: true,
6458 buildRendering: function(){
6459 // Builds a template like:
6460 // <div class=dijitAccordionInnerContainer>
6462 // <div class=dijitAccordionChildWrapper>
6467 // Create wrapper div, placed where the child is now
6468 this.domNode
= domConstruct
.place("<div class='" + this.baseClass
+
6469 "' role='presentation'>", this.contentWidget
.domNode
, "after");
6471 // wrapper div's first child is the button widget (ie, the title bar)
6472 var child
= this.contentWidget
,
6473 cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
6474 this.button
= child
._buttonWidget
= (new cls({
6475 contentWidget
: child
,
6477 title
: child
.tooltip
,
6480 textDir
: child
.textDir
,
6481 iconClass
: child
.iconClass
,
6482 id
: child
.id
+ "_button",
6484 })).placeAt(this.domNode
);
6486 // and then the actual content widget (changing it from prior-sibling to last-child),
6487 // wrapped by a <div class=dijitAccordionChildWrapper>
6488 this.containerNode
= domConstruct
.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode
);
6489 domConstruct
.place(this.contentWidget
.domNode
, this.containerNode
);
6492 postCreate: function(){
6493 this.inherited(arguments
);
6495 // Map changes in content widget's title etc. to changes in the button
6496 var button
= this.button
;
6497 this._contentWidgetWatches
= [
6498 this.contentWidget
.watch('title', lang
.hitch(this, function(name
, oldValue
, newValue
){
6499 button
.set("label", newValue
);
6501 this.contentWidget
.watch('tooltip', lang
.hitch(this, function(name
, oldValue
, newValue
){
6502 button
.set("title", newValue
);
6504 this.contentWidget
.watch('iconClass', lang
.hitch(this, function(name
, oldValue
, newValue
){
6505 button
.set("iconClass", newValue
);
6510 _setSelectedAttr: function(/*Boolean*/ isSelected
){
6511 this._set("selected", isSelected
);
6512 this.button
.set("selected", isSelected
);
6514 var cw
= this.contentWidget
;
6515 if(cw
.onSelected
){ cw
.onSelected(); }
6519 startup: function(){
6520 // Called by _Container.addChild()
6521 this.contentWidget
.startup();
6524 destroy: function(){
6525 this.button
.destroyRecursive();
6527 array
.forEach(this._contentWidgetWatches
|| [], function(w
){ w
.unwatch(); });
6529 delete this.contentWidget
._buttonWidget
;
6530 delete this.contentWidget
._wrapperWidget
;
6532 this.inherited(arguments
);
6535 destroyDescendants: function(/*Boolean*/ preserveDom
){
6536 // since getChildren isn't working for me, have to code this manually
6537 this.contentWidget
.destroyRecursive(preserveDom
);
6541 var AccordionContainer
= declare("dijit.layout.AccordionContainer", StackContainer
, {
6543 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
6544 // and switching between panes is visualized by sliding the other panes up/down.
6546 // | <div data-dojo-type="dijit/layout/AccordionContainer">
6547 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
6549 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
6550 // | <p>This is some text</p>
6554 // duration: Integer
6555 // Amount of time (in ms) it takes to slide panes
6556 duration
: manager
.defaultDuration
,
6558 // buttonWidget: [const] String
6559 // The name of the widget used to display the title of each pane
6560 buttonWidget
: AccordionButton
,
6563 // _verticalSpace: Number
6564 // Pixels of space available for the open pane
6565 // (my content box size minus the cumulative size of all the title bars)
6568 baseClass
: "dijitAccordionContainer",
6570 buildRendering: function(){
6571 this.inherited(arguments
);
6572 this.domNode
.style
.overflow
= "hidden"; // TODO: put this in dijit.css
6573 this.domNode
.setAttribute("role", "tablist"); // TODO: put this in template
6576 startup: function(){
6577 if(this._started
){ return; }
6578 this.inherited(arguments
);
6579 if(this.selectedChildWidget
){
6580 this.selectedChildWidget
._wrapperWidget
.set("selected", true);
6585 // Implement _LayoutWidget.layout() virtual method.
6586 // Set the height of the open pane based on what room remains.
6588 var openPane
= this.selectedChildWidget
;
6590 if(!openPane
){ return;}
6592 // space taken up by title, plus wrapper div (with border/margin) for open pane
6593 var wrapperDomNode
= openPane
._wrapperWidget
.domNode
,
6594 wrapperDomNodeMargin
= domGeometry
.getMarginExtents(wrapperDomNode
),
6595 wrapperDomNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperDomNode
),
6596 wrapperContainerNode
= openPane
._wrapperWidget
.containerNode
,
6597 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
6598 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
6599 mySize
= this._contentBox
;
6601 // get cumulative height of all the unselected title bars
6602 var totalCollapsedHeight
= 0;
6603 array
.forEach(this.getChildren(), function(child
){
6604 if(child
!= openPane
){
6605 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
6606 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
6607 // margin below the bottom pane (even though we don't want one).
6608 totalCollapsedHeight
+= domGeometry
.getMarginSize(child
._wrapperWidget
.domNode
).h
;
6611 this._verticalSpace
= mySize
.h
- totalCollapsedHeight
- wrapperDomNodeMargin
.h
6612 - wrapperDomNodePadBorder
.h
- wrapperContainerNodeMargin
.h
- wrapperContainerNodePadBorder
.h
6613 - openPane
._buttonWidget
.getTitleHeight();
6615 // Memo size to make displayed child
6616 this._containerContentBox
= {
6617 h
: this._verticalSpace
,
6618 w
: this._contentBox
.w
- wrapperDomNodeMargin
.w
- wrapperDomNodePadBorder
.w
6619 - wrapperContainerNodeMargin
.w
- wrapperContainerNodePadBorder
.w
6623 openPane
.resize(this._containerContentBox
);
6627 _setupChild: function(child
){
6628 // Overrides _LayoutWidget._setupChild().
6629 // Put wrapper widget around the child widget, showing title
6631 child
._wrapperWidget
= AccordionInnerContainer({
6632 contentWidget
: child
,
6633 buttonWidget
: this.buttonWidget
,
6634 id
: child
.id
+ "_wrapper",
6637 textDir
: child
.textDir
,
6641 this.inherited(arguments
);
6644 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
6645 // Overrides _LayoutWidget.addChild().
6647 // Adding a child to a started Accordion is complicated because children have
6648 // wrapper widgets. Default code path (calling this.inherited()) would add
6649 // the new child inside another child's wrapper.
6651 // First add in child as a direct child of this AccordionContainer
6652 var refNode
= this.containerNode
;
6653 if(insertIndex
&& typeof insertIndex
== "number"){
6654 var children
= _Widget
.prototype.getChildren
.call(this); // get wrapper panes
6655 if(children
&& children
.length
>= insertIndex
){
6656 refNode
= children
[insertIndex
-1].domNode
;
6657 insertIndex
= "after";
6660 domConstruct
.place(child
.domNode
, refNode
, insertIndex
);
6662 if(!child
._started
){
6666 // Then stick the wrapper widget around the child widget
6667 this._setupChild(child
);
6669 // Code below copied from StackContainer
6670 topic
.publish(this.id
+"-addChild", child
, insertIndex
); // publish
6672 if(!this.selectedChildWidget
){
6673 this.selectChild(child
);
6676 // We haven't been started yet so just add in the child widget directly,
6677 // and the wrapper will be created on startup()
6678 this.inherited(arguments
);
6682 removeChild: function(child
){
6683 // Overrides _LayoutWidget.removeChild().
6685 // Destroy wrapper widget first, before StackContainer.getChildren() call.
6686 // Replace wrapper widget with true child widget (ContentPane etc.).
6687 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
6688 if(child
._wrapperWidget
){
6689 domConstruct
.place(child
.domNode
, child
._wrapperWidget
.domNode
, "after");
6690 child
._wrapperWidget
.destroy();
6691 delete child
._wrapperWidget
;
6694 domClass
.remove(child
.domNode
, "dijitHidden");
6696 this.inherited(arguments
);
6699 getChildren: function(){
6700 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
6701 return array
.map(this.inherited(arguments
), function(child
){
6702 return child
.declaredClass
== "dijit.layout._AccordionInnerContainer" ? child
.contentWidget
: child
;
6706 destroy: function(){
6707 if(this._animation
){
6708 this._animation
.stop();
6710 array
.forEach(this.getChildren(), function(child
){
6711 // If AccordionContainer has been started, then each child has a wrapper widget which
6712 // also needs to be destroyed.
6713 if(child
._wrapperWidget
){
6714 child
._wrapperWidget
.destroy();
6716 child
.destroyRecursive();
6719 this.inherited(arguments
);
6722 _showChild: function(child
){
6723 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6724 child
._wrapperWidget
.containerNode
.style
.display
="block";
6725 return this.inherited(arguments
);
6728 _hideChild: function(child
){
6729 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6730 child
._wrapperWidget
.containerNode
.style
.display
="none";
6731 this.inherited(arguments
);
6734 _transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate
){
6735 // Overrides StackContainer._transition() to provide sliding of title bars etc.
6738 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
6742 if(this._animation
){
6743 // there's an in-progress animation. speedily end it so we can do the newly requested one
6744 this._animation
.stop(true);
6745 delete this._animation
;
6751 newWidget
._wrapperWidget
.set("selected", true);
6753 var d
= this._showChild(newWidget
); // prepare widget to be slid in
6755 // Size the new widget, in case this is the first time it's being shown,
6756 // or I have been resized since the last time it was shown.
6757 // Note that page must be visible for resizing to work.
6758 if(this.doLayout
&& newWidget
.resize
){
6759 newWidget
.resize(this._containerContentBox
);
6764 oldWidget
._wrapperWidget
.set("selected", false);
6766 this._hideChild(oldWidget
);
6771 var newContents
= newWidget
._wrapperWidget
.containerNode
,
6772 oldContents
= oldWidget
._wrapperWidget
.containerNode
;
6774 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
6775 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
6776 // Have to compensate for that by immediately shrinking the pane being closed.
6777 var wrapperContainerNode
= newWidget
._wrapperWidget
.containerNode
,
6778 wrapperContainerNodeMargin
= domGeometry
.getMarginExtents(wrapperContainerNode
),
6779 wrapperContainerNodePadBorder
= domGeometry
.getPadBorderExtents(wrapperContainerNode
),
6780 animationHeightOverhead
= wrapperContainerNodeMargin
.h
+ wrapperContainerNodePadBorder
.h
;
6782 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
) + "px";
6784 this._animation
= new fx
.Animation({
6786 duration
: this.duration
,
6787 curve
: [1, this._verticalSpace
- animationHeightOverhead
- 1],
6788 onAnimate: function(value
){
6789 value
= Math
.floor(value
); // avoid fractional values
6790 newContents
.style
.height
= value
+ "px";
6791 oldContents
.style
.height
= (self
._verticalSpace
- animationHeightOverhead
- value
) + "px";
6794 delete self
._animation
;
6795 newContents
.style
.height
= "auto";
6796 oldWidget
._wrapperWidget
.containerNode
.style
.display
= "none";
6797 oldContents
.style
.height
= "auto";
6798 self
._hideChild(oldWidget
);
6801 this._animation
.onStop
= this._animation
.onEnd
;
6802 this._animation
.play();
6805 return d
; // If child has an href, promise that fires when the widget has finished loading
6808 // note: we are treating the container as controller here
6809 _onKeyPress: function(/*Event*/ e
, /*dijit/_WidgetBase*/ fromTitle){
6811 // Handle keypress events
6813 // This is called from a handler on AccordionContainer.domNode
6814 // (setup in StackContainer), and is also called directly from
6815 // the click handler for accordion labels
6816 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
6819 var c = e.charOrCode;
6820 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
6821 (e.ctrlKey && c == keys.PAGE_UP)){
6822 this._adjacent(false)._buttonWidget._onTitleClick();
6824 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
6825 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
6826 this._adjacent(true)._buttonWidget._onTitleClick();
6832 // Back compat w/1.6, remove for 2.0
6833 if(has("dijit-legacy-requires")){
6834 ready(0, function(){
6835 var requires = ["dijit/layout/AccordionPane"];
6836 require(requires); // use indirection so modules not rolled into a build
6840 // For monkey patching
6841 AccordionContainer._InnerContainer = AccordionInnerContainer;
6842 AccordionContainer._Button = AccordionButton;
6844 return AccordionContainer;
6848 'dijit/form/_AutoCompleterMixin':function(){
6849 define("dijit/form/_AutoCompleterMixin", [
6850 "dojo/data/util/filter", // patternToRegExp
6851 "dojo/_base/declare", // declare
6852 "dojo/dom-attr", // domAttr.get
6853 "dojo/_base/event", // event.stop
6855 "dojo/_base/lang", // lang.clone lang.hitch
6856 "dojo/query", // query
6857 "dojo/regexp", // regexp.escapeString
6858 "dojo/sniff", // has("ie")
6859 "dojo/string", // string.substitute
6861 "../registry", // registry.byId
6862 "./_TextBoxMixin", // defines _TextBoxMixin.selectInputText
6864 ], function(filter, declare, domAttr, event, keys, lang, query, regexp, has, string,
6865 DataList, registry, _TextBoxMixin, SearchMixin){
6868 // dijit/form/_AutoCompleterMixin
6870 return declare("dijit.form._AutoCompleterMixin", SearchMixin, {
6872 // A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect`
6874 // All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`.
6879 // This is the item returned by the dojo/store/api/Store implementation that
6880 // provides the data for this ComboBox, it's the currently selected item.
6883 // autoComplete: Boolean
6884 // If user types in a partial string, and then tab out of the `<input>` box,
6885 // automatically copy the first entry displayed in the drop down list to
6886 // the `<input>` field
6889 // highlightMatch: String
6890 // One of: "first", "all" or "none".
6892 // If the ComboBox/FilteringSelect opens with the search results and the searched
6893 // string can be found, it will be highlighted. If set to "all"
6894 // then will probably want to change `queryExpr` parameter to '*${0}*'
6896 // Highlighting is only performed when `labelType` is "text", so as to not
6897 // interfere with any HTML markup an HTML label might contain.
6898 highlightMatch: "first",
6900 // labelAttr: String?
6901 // The entries in the drop down list come from this attribute in the
6903 // If not specified, the searchAttr attribute is used instead.
6906 // labelType: String
6907 // Specifies how to interpret the labelAttr in the data store items.
6908 // Can be "html" or "text".
6911 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
6914 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
6915 _stopClickEvents: false,
6917 _getCaretPos: function(/*DomNode*/ element
){
6918 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
6920 if(typeof(element
.selectionStart
) == "number"){
6921 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
6922 pos
= element
.selectionStart
;
6923 }else if(has("ie")){
6924 // in the case of a mouse click in a popup being handled,
6925 // then the win.doc.selection is not the textarea, but the popup
6926 // var r = win.doc.selection.createRange();
6927 // hack to get IE 6 to play nice. What a POS browser.
6928 var tr
= element
.ownerDocument
.selection
.createRange().duplicate();
6929 var ntr
= element
.createTextRange();
6930 tr
.move("character",0);
6931 ntr
.move("character",0);
6933 // If control doesn't have focus, you get an exception.
6934 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
6935 // There appears to be no workaround for this - googled for quite a while.
6936 ntr
.setEndPoint("EndToEnd", tr
);
6937 pos
= String(ntr
.text
).replace(/\r/g,"").length
;
6939 // If focus has shifted, 0 is fine for caret pos.
6945 _setCaretPos: function(/*DomNode*/ element
, /*Number*/ location
){
6946 location
= parseInt(location
);
6947 _TextBoxMixin
.selectInputText(element
, location
, location
);
6950 _setDisabledAttr: function(/*Boolean*/ value
){
6951 // Additional code to set disabled state of ComboBox node.
6952 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
6953 this.inherited(arguments
);
6954 this.domNode
.setAttribute("aria-disabled", value
? "true" : "false");
6957 _onKey: function(/*Event*/ evt
){
6959 // Handles keyboard events
6961 if(evt
.charCode
>= 32){ return; } // alphanumeric reserved for searching
6963 var key
= evt
.charCode
|| evt
.keyCode
;
6965 // except for cutting/pasting case - ctrl + x/v
6966 if(key
== keys
.ALT
|| key
== keys
.CTRL
|| key
== keys
.META
|| key
== keys
.SHIFT
){
6967 return; // throw out spurious events
6970 var pw
= this.dropDown
;
6971 var highlighted
= null;
6974 // _HasDropDown will do some of the work:
6976 // 1. when drop down is not yet shown:
6977 // - if user presses the down arrow key, call loadDropDown()
6978 // 2. when drop down is already displayed:
6979 // - on ESC key, call closeDropDown()
6980 // - otherwise, call dropDown.handleKey() to process the keystroke
6981 this.inherited(arguments
);
6983 if(evt
.altKey
|| evt
.ctrlKey
|| evt
.metaKey
){ return; } // don't process keys with modifiers - but we want shift+TAB
6986 highlighted
= pw
.getHighlightedOption();
6989 case keys
.PAGE_DOWN
:
6990 case keys
.DOWN_ARROW
:
6993 // Keystroke caused ComboBox_menu to move to a different item.
6994 // Copy new item to <input> box.
6996 this._announceOption(highlighted
);
7002 // prevent submitting form if user presses enter. Also
7003 // prevent accepting the value if either Next or Previous
7006 // only stop event on prev/next
7007 if(highlighted
== pw
.nextButton
){
7008 this._nextSearch(1);
7009 event
.stop(evt
); // prevent submit
7011 }else if(highlighted
== pw
.previousButton
){
7012 this._nextSearch(-1);
7013 event
.stop(evt
); // prevent submit
7016 event
.stop(evt
); // prevent submit if ENTER was to choose an item
7018 // Update 'value' (ex: KY) according to currently displayed text
7019 this._setBlurValue(); // set value if needed
7020 this._setCaretPos(this.focusNode
, this.focusNode
.value
.length
); // move cursor to end and cancel highlighting
7025 var newvalue
= this.get('displayedValue');
7026 // if the user had More Choices selected fall into the
7029 newvalue
== pw
._messages
["previousMessage"] ||
7030 newvalue
== pw
._messages
["nextMessage"])
7035 this._selectOption(highlighted
);
7041 this._lastQuery
= null; // in case results come back later
7042 this.closeDropDown();
7048 _autoCompleteText: function(/*String*/ text
){
7050 // Fill in the textbox with the first item from the drop down
7051 // list, and highlight the characters that were
7052 // auto-completed. For example, if user typed "CA" and the
7053 // drop down list appeared, the textbox would be changed to
7054 // "California" and "ifornia" would be highlighted.
7056 var fn
= this.focusNode
;
7058 // IE7: clear selection so next highlight works all the time
7059 _TextBoxMixin
.selectInputText(fn
, fn
.value
.length
);
7060 // does text autoComplete the value in the textbox?
7061 var caseFilter
= this.ignoreCase
? 'toLowerCase' : 'substr';
7062 if(text
[caseFilter
](0).indexOf(this.focusNode
.value
[caseFilter
](0)) == 0){
7063 var cpos
= this.autoComplete
? this._getCaretPos(fn
) : fn
.value
.length
;
7064 // only try to extend if we added the last character at the end of the input
7065 if((cpos
+1) > fn
.value
.length
){
7066 // only add to input node as we would overwrite Capitalisation of chars
7067 // actually, that is ok
7068 fn
.value
= text
;//.substr(cpos);
7069 // visually highlight the autocompleted characters
7070 _TextBoxMixin
.selectInputText(fn
, cpos
);
7073 // text does not autoComplete; replace the whole value and highlight
7075 _TextBoxMixin
.selectInputText(fn
);
7079 _openResultList: function(/*Object*/ results
, /*Object*/ query
, /*Object*/ options
){
7081 // Callback when a search completes.
7083 // 1. generates drop-down list and calls _showResultList() to display it
7084 // 2. if this result list is from user pressing "more choices"/"previous choices"
7085 // then tell screen reader to announce new option
7086 var wasSelected
= this.dropDown
.getHighlightedOption();
7087 this.dropDown
.clearResultList();
7088 if(!results
.length
&& options
.start
== 0){ // if no results and not just the previous choices button
7089 this.closeDropDown();
7092 this._nextSearch
= this.dropDown
.onPage
= lang
.hitch(this, function(direction
){
7093 results
.nextPage(direction
!== -1);
7097 // Fill in the textbox with the first item from the drop down list,
7098 // and highlight the characters that were auto-completed. For
7099 // example, if user typed "CA" and the drop down list appeared, the
7100 // textbox would be changed to "California" and "ifornia" would be
7103 this.dropDown
.createOptions(
7106 lang
.hitch(this, "_getMenuLabelFromItem")
7109 // show our list (only if we have content, else nothing)
7110 this._showResultList();
7113 // tell the screen reader that the paging callback finished by
7114 // shouting the next choice
7115 if("direction" in options
){
7116 if(options
.direction
){
7117 this.dropDown
.highlightFirstOption();
7118 }else if(!options
.direction
){
7119 this.dropDown
.highlightLastOption();
7122 this._announceOption(this.dropDown
.getHighlightedOption());
7124 }else if(this.autoComplete
&& !this._prev_key_backspace
7125 // when the user clicks the arrow button to show the full list,
7126 // startSearch looks for "*".
7127 // it does not make sense to autocomplete
7128 // if they are just previewing the options available.
7129 && !/^[*]+$/.test(query
[this.searchAttr
].toString())){
7130 this._announceOption(this.dropDown
.containerNode
.firstChild
.nextSibling
); // 1st real item
7134 _showResultList: function(){
7136 // Display the drop down if not already displayed, or if it is displayed, then
7137 // reposition it if necessary (reposition may be necessary if drop down's height changed).
7138 this.closeDropDown(true);
7139 this.openDropDown();
7140 this.domNode
.setAttribute("aria-expanded", "true");
7143 loadDropDown: function(/*Function*/ /*===== callback =====*/){
7144 // Overrides _HasDropDown.loadDropDown().
7145 // This is called when user has pressed button icon or pressed the down arrow key
7146 // to open the drop down.
7147 this._startSearchAll();
7150 isLoaded: function(){
7151 // signal to _HasDropDown that it needs to call loadDropDown() to load the
7152 // drop down asynchronously before displaying it
7156 closeDropDown: function(){
7157 // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
7158 // This method is the callback when the user types ESC or clicking
7159 // the button icon while the drop down is open. It's also called by other code.
7162 this.inherited(arguments
);
7163 this.domNode
.setAttribute("aria-expanded", "false");
7164 this.focusNode
.removeAttribute("aria-activedescendant");
7168 _setBlurValue: function(){
7169 // if the user clicks away from the textbox OR tabs away, set the
7170 // value to the textbox value
7172 // if value is now more choices or previous choices, revert
7174 var newvalue
= this.get('displayedValue');
7175 var pw
= this.dropDown
;
7177 newvalue
== pw
._messages
["previousMessage"] ||
7178 newvalue
== pw
._messages
["nextMessage"]
7181 this._setValueAttr(this._lastValueReported
, true);
7182 }else if(typeof this.item
== "undefined"){
7183 // Update 'value' (ex: KY) according to currently displayed text
7185 this.set('displayedValue', newvalue
);
7187 if(this.value
!= this._lastValueReported
){
7188 this._handleOnChange(this.value
, true);
7190 this._refreshState();
7194 _setItemAttr: function(/*item*/ item
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
){
7196 // Set the displayed valued in the input box, and the hidden value
7197 // that gets submitted, based on a dojo.data store item.
7199 // Users shouldn't call this function; they should be calling
7200 // set('item', value)
7205 if(!displayedValue
){
7206 displayedValue
= this.store
._oldAPI
? // remove getValue() for 2.0 (old dojo.data API)
7207 this.store
.getValue(item
, this.searchAttr
) : item
[this.searchAttr
];
7209 value
= this._getValueField() != this.searchAttr
? this.store
.getIdentity(item
) : displayedValue
;
7211 this.set('value', value
, priorityChange
, displayedValue
, item
);
7214 _announceOption: function(/*Node*/ node
){
7216 // a11y code that puts the highlighted option in the textbox.
7217 // This way screen readers will know what is happening in the
7223 // pull the text value from the item attached to the DOM node
7225 if(node
== this.dropDown
.nextButton
||
7226 node
== this.dropDown
.previousButton
){
7227 newValue
= node
.innerHTML
;
7228 this.item
= undefined;
7231 var item
= this.dropDown
.items
[node
.getAttribute("item")];
7232 newValue
= (this.store
._oldAPI
? // remove getValue() for 2.0 (old dojo.data API)
7233 this.store
.getValue(item
, this.searchAttr
) : item
[this.searchAttr
]).toString();
7234 this.set('item', item
, false, newValue
);
7236 // get the text that the user manually entered (cut off autocompleted text)
7237 this.focusNode
.value
= this.focusNode
.value
.substring(0, this._lastInput
.length
);
7238 // set up ARIA activedescendant
7239 this.focusNode
.setAttribute("aria-activedescendant", domAttr
.get(node
, "id"));
7240 // autocomplete the rest of the option to announce change
7241 this._autoCompleteText(newValue
);
7244 _selectOption: function(/*DomNode*/ target
){
7246 // Menu callback function, called when an item in the menu is selected.
7247 this.closeDropDown();
7249 this._announceOption(target
);
7251 this._setCaretPos(this.focusNode
, this.focusNode
.value
.length
);
7252 this._handleOnChange(this.value
, true);
7255 _startSearchAll: function(){
7256 this._startSearch('');
7259 _startSearchFromInput: function(){
7260 this.item
= undefined; // undefined means item needs to be set
7261 this.inherited(arguments
);
7264 _startSearch: function(/*String*/ key
){
7266 // Starts a search for elements matching key (key=="" means to return all items),
7267 // and calls _openResultList() when the search completes, to display the results.
7269 var popupId
= this.id
+ "_popup",
7270 dropDownConstructor
= lang
.isString(this.dropDownClass
) ?
7271 lang
.getObject(this.dropDownClass
, false) : this.dropDownClass
;
7272 this.dropDown
= new dropDownConstructor({
7273 onChange
: lang
.hitch(this, this._selectOption
),
7276 textDir
: this.textDir
7278 this.focusNode
.removeAttribute("aria-activedescendant");
7279 this.textbox
.setAttribute("aria-owns",popupId
); // associate popup with textbox
7281 this._lastInput
= key
; // Store exactly what was entered by the user.
7282 this.inherited(arguments
);
7285 _getValueField: function(){
7287 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
7288 // Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value.
7289 return this.searchAttr
;
7292 //////////// INITIALIZATION METHODS ///////////////////////////////////////
7294 postMixInProperties: function(){
7295 this.inherited(arguments
);
7297 var srcNodeRef
= this.srcNodeRef
;
7298 // if user didn't specify store, then assume there are option tags
7299 this.store
= new DataList({}, srcNodeRef
);
7301 // if there is no value set and there is an option list, set
7302 // the value to the first value to be consistent with native Select
7303 // Firefox and Safari set value
7304 // IE6 and Opera set selectedIndex, which is automatically set
7305 // by the selected attribute of an option tag
7306 // IE6 does not set value, Opera sets value = selectedIndex
7307 if(!("value" in this.params
)){
7308 var item
= (this.item
= this.store
.fetchSelectedItem());
7310 var valueField
= this._getValueField();
7311 // remove getValue() for 2.0 (old dojo.data API)
7312 this.value
= this.store
._oldAPI
? this.store
.getValue(item
, valueField
) : item
[valueField
];
7318 postCreate: function(){
7320 // Subclasses must call this method from their postCreate() methods
7324 // find any associated label element and add to ComboBox node.
7325 var label
=query('label[for="'+this.id
+'"]');
7327 if(!label
[0].id
){ label
[0].id
= this.id
+ "_label"; }
7328 this.domNode
.setAttribute("aria-labelledby", label
[0].id
);
7331 this.inherited(arguments
);
7332 this.connect(this, "onSearch", "_openResultList");
7335 _getMenuLabelFromItem: function(/*Item*/ item
){
7336 var label
= this.labelFunc(item
, this.store
),
7337 labelType
= this.labelType
;
7338 // If labelType is not "text" we don't want to screw any markup ot whatever.
7339 if(this.highlightMatch
!= "none" && this.labelType
== "text" && this._lastInput
){
7340 label
= this.doHighlight(label
, this._lastInput
);
7343 return {html
: labelType
== "html", label
: label
};
7346 doHighlight: function(/*String*/ label
, /*String*/ find
){
7348 // Highlights the string entered by the user in the menu. By default this
7349 // highlights the first occurrence found. Override this method
7350 // to implement your custom highlighting.
7355 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
7356 modifiers
= (this.ignoreCase
? "i" : "") + (this.highlightMatch
== "all" ? "g" : ""),
7357 i
= this.queryExpr
.indexOf("${0}");
7358 find
= regexp
.escapeString(find
); // escape regexp special chars
7359 //If < appears in label, and user presses t, we don't want to highlight the t in the escaped "<"
7360 //first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which
7361 //should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the
7362 //HTML highlight markup.
7363 return this._escapeHtml(label
.replace(
7364 new RegExp((i
== 0 ? "^" : "") + "("+ find
+")" + (i
== (this.queryExpr
.length
- 4) ? "$" : ""), modifiers
),
7365 '\uFFFF$1\uFFFF')).replace(
7366 /\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>'
7367 ); // returns String, (almost) valid HTML (entities encoded)
7370 _escapeHtml: function(/*String*/ str
){
7371 // TODO Should become dojo.html.entities(), when exists use instead
7373 // Adds escape sequences for special characters in XML: `&<>"'`
7374 str
= String(str
).replace(/&/gm, "&").replace(/</gm
, "<")
7375 .replace(/>/gm, ">").replace(/"/gm, ""
;"); //balance"
7376 return str
; // string
7380 // Overrides the _FormWidget.reset().
7381 // Additionally reset the .item (to clean up).
7383 this.inherited(arguments
);
7386 labelFunc: function(item
, store
){
7388 // Computes the label to display based on the dojo.data store item.
7390 // The item from the store
7391 // store: dojo/store/api/Store
7394 // The label that the ComboBox should display
7398 // Use toString() because XMLStore returns an XMLItem whereas this
7399 // method is expected to return a String (#9354).
7400 // Remove getValue() for 2.0 (old dojo.data API)
7401 return (store
._oldAPI
? store
.getValue(item
, this.labelAttr
|| this.searchAttr
) :
7402 item
[this.labelAttr
|| this.searchAttr
]).toString(); // String
7405 _setValueAttr: function(/*String*/ value
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
, /*item?*/ item
){
7407 // Hook so set('value', value) works.
7409 // Sets the value of the select.
7410 this._set("item", item
||null); // value not looked up in store
7411 if(value
== null /* or undefined */){ value
= ''; } // null translates to blank
7412 this.inherited(arguments
);
7414 _setTextDirAttr: function(/*String*/ textDir
){
7416 // Setter for textDir, needed for the dropDown's textDir update.
7418 // Users shouldn't call this function; they should be calling
7419 // set('textDir', value)
7422 this.inherited(arguments
);
7423 // update the drop down also (_ComboBoxMenuMixin)
7425 this.dropDown
._set("textDir", textDir
);
7432 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n",
7433 'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>",
7434 'dijit/form/MappedTextBox':function(){
7435 define("dijit/form/MappedTextBox", [
7436 "dojo/_base/declare", // declare
7437 "dojo/dom-construct", // domConstruct.place
7438 "./ValidationTextBox"
7439 ], function(declare
, domConstruct
, ValidationTextBox
){
7442 // dijit/form/MappedTextBox
7444 return declare("dijit.form.MappedTextBox", ValidationTextBox
, {
7446 // A dijit/form/ValidationTextBox subclass which provides a base class for widgets that have
7447 // a visible formatted display value, and a serializable
7448 // value in a hidden input field which is actually sent to the server.
7450 // The visible display may
7451 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
7452 // input field which uses the `name` attribute declared by the original widget. That value sent
7453 // to the server is defined by the dijit/form/MappedTextBox.serialize() method and is typically
7458 postMixInProperties: function(){
7459 this.inherited(arguments
);
7461 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
7462 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
7463 this.nameAttrSetting
= "";
7466 // Override default behavior to assign name to focusNode
7469 serialize: function(val
/*=====, options =====*/){
7471 // Overridable function used to convert the get('value') result to a canonical
7472 // (non-localized) string. For example, will print dates in ISO format, and
7473 // numbers the same way as they are represented in javascript.
7477 // protected extension
7478 return val
.toString
? val
.toString() : ""; // String
7481 toString: function(){
7483 // Returns widget as a printable string using the widget's value
7486 var val
= this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
7487 return val
!= null ? (typeof val
== "string" ? val
: this.serialize(val
, this.constraints
)) : ""; // String
7490 validate: function(){
7491 // Overrides `dijit/form/TextBox.validate`
7492 this.valueNode
.value
= this.toString();
7493 return this.inherited(arguments
);
7496 buildRendering: function(){
7497 // Overrides `dijit/_TemplatedMixin/buildRendering`
7499 this.inherited(arguments
);
7501 // Create a hidden <input> node with the serialized value used for submit
7502 // (as opposed to the displayed value).
7503 // Passing in name as markup rather than calling domConstruct.create() with an attrs argument
7504 // to make query(input[name=...]) work on IE. (see #8660)
7505 this.valueNode
= domConstruct
.place("<input type='hidden'" + (this.name
? ' name="' + this.name
.replace(/"/g, """) + '"' : "") + "/>", this.textbox, "after
");
7509 // Overrides `dijit/form/ValidationTextBox.reset` to
7510 // reset the hidden textbox value to ''
7511 this.valueNode.value = '';
7512 this.inherited(arguments);
7518 'dijit/form/ComboBoxMixin':function(){
7520 'url:dijit/form/templates/DropDownBox.html':"<div
class=\"dijit dijitReset dijitInline dijitLeft
\"\n\tid=\"widget_${id}
\"\n\trole=\"combobox
\"\n\t><div
class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo
-attach
-point
=\"_buttonNode
, _popupStateNode
\" role
=\"presentation
\"\n\t\t><input
class=\"dijitReset dijitInputField dijitArrowButtonInner
\" value
=\"▼ \" 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"}});
7521 define("dijit
/form/ComboBoxMixin
", [
7522 "dojo
/_base/declare", // declare
7523 "dojo
/_base/Deferred
",
7524 "dojo
/_base/kernel
", // kernel.deprecated
7525 "dojo
/_base/lang", // lang.mixin
7526 "dojo
/store/util/QueryResults
",
7527 "./_AutoCompleterMixin
",
7530 "dojo
/text!./templates
/DropDownBox
.html
"
7531 ], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
7535 // dijit/form/ComboBoxMixin
7537 return declare("dijit
.form
.ComboBoxMixin
", [_HasDropDown, _AutoCompleterMixin], {
7539 // Provides main functionality of ComboBox widget
7541 // dropDownClass: [protected extension] Function String
7542 // Dropdown widget class used to select a date/time.
7543 // Subclasses should specify this.
7544 dropDownClass: _ComboBoxMenu,
7546 // hasDownArrow: Boolean
7547 // Set this textbox to have a down arrow button, to display the drop down list.
7548 // Defaults to true.
7551 templateString: template,
7553 baseClass: "dijitTextBox dijitComboBox
",
7556 // store: [const] dojo/store/api/Store|dojo/data/api/Read
7557 // Reference to data provider object used by this ComboBox.
7559 // Should be dojo/store/api/Store, but dojo/data/api/Read supported
7560 // for backwards compatibility.
7564 // Set classes like dijitDownArrowButtonHover depending on
7565 // mouse action over button node
7567 "_buttonNode
": "dijitDownArrowButton
"
7570 _setHasDownArrowAttr: function(/*Boolean*/ val){
7571 this._set("hasDownArrow
", val);
7572 this._buttonNode.style.display = val ? "" : "none
";
7575 _showResultList: function(){
7577 this.displayMessage("");
7578 this.inherited(arguments);
7581 _setStoreAttr: function(store){
7582 // For backwards-compatibility, accept dojo.data store in addition to dojo/store/api/Store. Remove in 2.0.
7588 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
7589 // Like dojo/store/DataStore.get() except returns native item.
7590 var deferred = new Deferred();
7591 this.fetchItemByIdentity({
7593 onItem: function(object){
7594 deferred.resolve(object);
7596 onError: function(error){
7597 deferred.reject(error);
7600 return deferred.promise;
7602 query: function(query, options){
7604 // Queries the store for objects. Like dojo/store/DataStore.query()
7605 // except returned Deferred contains array of native items.
7606 var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
7607 deferred.total = new Deferred();
7608 var fetchHandle = this.fetch(lang.mixin({
7610 onBegin: function(count){
7611 deferred.total.resolve(count);
7613 onComplete: function(results){
7614 deferred.resolve(results);
7616 onError: function(error){
7617 deferred.reject(error);
7620 return QueryResults(deferred);
7624 this._set("store
", store);
7627 postMixInProperties: function(){
7628 // Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
7629 // Unfortunately, without special code, it ends up executing second.
7630 var store = this.params.store || this.store;
7632 this._setStoreAttr(store);
7635 this.inherited(arguments);
7637 // User may try to access this.store.getValue() etc. in a custom labelFunc() function.
7638 // It's not available with the new data store for handling inline <option> tags, so add it.
7639 if(!this.params.store && !this.store._oldAPI){
7640 var clazz = this.declaredClass;
7641 lang.mixin(this.store, {
7642 getValue: function(item, attr){
7643 kernel.deprecated(clazz + ".store
.getValue(item
, attr
) is deprecated
for builtin store
. Use item
.attr directly
", "", "2.0");
7646 getLabel: function(item){
7647 kernel.deprecated(clazz + ".store
.getLabel(item
) is deprecated
for builtin store
. Use item
.label directly
", "", "2.0");
7650 fetch: function(args){
7651 kernel.deprecated(clazz + ".store
.fetch() is deprecated
for builtin store
.", "Use store
.query()", "2.0");
7652 var shim = ["dojo
/data/ObjectStore
"]; // indirection so it doesn't get rolled into a build
7653 require(shim, lang.hitch(this, function(ObjectStore){
7654 new ObjectStore({objectStore: this}).fetch(args);
7664 'dijit/form/_TextBoxMixin':function(){
7665 define("dijit
/form/_TextBoxMixin
", [
7666 "dojo
/_base/array", // array.forEach
7667 "dojo
/_base/declare", // declare
7668 "dojo
/dom", // dom
.byId
7669 "dojo/_base/event", // event.stop
7670 "dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
7671 "dojo/_base/lang", // lang.mixin
7673 "../main" // for exporting dijit._setSelectionRange, dijit.selectInputText
7674 ], function(array
, declare
, dom
, event
, keys
, lang
, on
, dijit
){
7677 // dijit/form/_TextBoxMixin
7679 var _TextBoxMixin
= declare("dijit.form._TextBoxMixin", null, {
7681 // A mixin for textbox form input widgets
7684 // Removes leading and trailing whitespace if true. Default is false.
7687 // uppercase: Boolean
7688 // Converts all characters to uppercase if true. Default is false.
7691 // lowercase: Boolean
7692 // Converts all characters to lowercase if true. Default is false.
7695 // propercase: Boolean
7696 // Converts the first character of each word to uppercase if true.
7699 // maxLength: String
7700 // HTML INPUT tag maxLength declaration.
7703 // selectOnClick: [const] Boolean
7704 // If true, all text will be selected when focused with mouse
7705 selectOnClick
: false,
7707 // placeHolder: String
7708 // Defines a hint to help users fill out the input field (as defined in HTML 5).
7709 // This should only contain plain text (no html markup).
7712 _getValueAttr: function(){
7714 // Hook so get('value') works as we like.
7716 // For `dijit/form/TextBox` this basically returns the value of the `<input>`.
7718 // For `dijit/form/MappedTextBox` subclasses, which have both
7719 // a "displayed value" and a separate "submit value",
7720 // This treats the "displayed value" as the master value, computing the
7721 // submit value from it via this.parse().
7722 return this.parse(this.get('displayedValue'), this.constraints
);
7725 _setValueAttr: function(value
, /*Boolean?*/ priorityChange
, /*String?*/ formattedValue
){
7727 // Hook so set('value', ...) works.
7730 // Sets the value of the widget to "value" which can be of
7731 // any type as determined by the widget.
7734 // The visual element value is also set to a corresponding,
7735 // but not necessarily the same, value.
7738 // If specified, used to set the visual element value,
7739 // otherwise a computed visual value is used.
7742 // If true, an onChange event is fired immediately instead of
7743 // waiting for the next blur event.
7746 if(value
!== undefined){
7747 // TODO: this is calling filter() on both the display value and the actual value.
7748 // I added a comment to the filter() definition about this, but it should be changed.
7749 filteredValue
= this.filter(value
);
7750 if(typeof formattedValue
!= "string"){
7751 if(filteredValue
!== null && ((typeof filteredValue
!= "number") || !isNaN(filteredValue
))){
7752 formattedValue
= this.filter(this.format(filteredValue
, this.constraints
));
7753 }else{ formattedValue
= ''; }
7756 if(formattedValue
!= null /* and !undefined */ && ((typeof formattedValue
) != "number" || !isNaN(formattedValue
)) && this.textbox
.value
!= formattedValue
){
7757 this.textbox
.value
= formattedValue
;
7758 this._set("displayedValue", this.get("displayedValue"));
7761 if(this.textDir
== "auto"){
7762 this.applyTextDir(this.focusNode
, formattedValue
);
7765 this.inherited(arguments
, [filteredValue
, priorityChange
]);
7768 // displayedValue: String
7769 // For subclasses like ComboBox where the displayed value
7770 // (ex: Kentucky) and the serialized value (ex: KY) are different,
7771 // this represents the displayed value.
7773 // Setting 'displayedValue' through set('displayedValue', ...)
7774 // updates 'value', and vice-versa. Otherwise 'value' is updated
7775 // from 'displayedValue' periodically, like onBlur etc.
7777 // TODO: move declaration to MappedTextBox?
7778 // Problem is that ComboBox references displayedValue,
7779 // for benefit of FilteringSelect.
7782 _getDisplayedValueAttr: function(){
7784 // Hook so get('displayedValue') works.
7786 // Returns the displayed value (what the user sees on the screen),
7787 // after filtering (ie, trimming spaces etc.).
7789 // For some subclasses of TextBox (like ComboBox), the displayed value
7790 // is different from the serialized value that's actually
7791 // sent to the server (see `dijit/form/ValidationTextBox.serialize()`)
7793 // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
7795 // TODO: this isn't really the displayed value when the user is typing
7796 return this.filter(this.textbox
.value
);
7799 _setDisplayedValueAttr: function(/*String*/ value
){
7801 // Hook so set('displayedValue', ...) works.
7803 // Sets the value of the visual element to the string "value".
7804 // The widget value is also set to a corresponding,
7805 // but not necessarily the same, value.
7807 if(value
== null /* or undefined */){ value
= '' }
7808 else if(typeof value
!= "string"){ value
= String(value
) }
7810 this.textbox
.value
= value
;
7812 // sets the serialized value to something corresponding to specified displayedValue
7813 // (if possible), and also updates the textbox.value, for example converting "123"
7815 this._setValueAttr(this.get('value'), undefined);
7817 this._set("displayedValue", this.get('displayedValue'));
7820 if(this.textDir
== "auto"){
7821 this.applyTextDir(this.focusNode
, value
);
7825 format: function(value
/*=====, constraints =====*/){
7827 // Replaceable function to convert a value to a properly formatted string.
7829 // constraints: Object
7831 // protected extension
7832 return value
== null /* or undefined */ ? "" : (value
.toString
? value
.toString() : value
);
7835 parse: function(value
/*=====, constraints =====*/){
7837 // Replaceable function to convert a formatted string to a value
7839 // constraints: Object
7841 // protected extension
7843 return value
; // String
7846 _refreshState: function(){
7848 // After the user types some characters, etc., this method is
7849 // called to check the field for validity etc. The base method
7850 // in `dijit/form/TextBox` does nothing, but subclasses override.
7856 onInput: function(event){
7858 // Connect to this function to receive notifications of various user data-input events.
7859 // Return false to cancel the event and prevent it from being processed.
7861 // keydown | keypress | cut | paste | input
7866 onInput: function(){},
7868 __skipInputEvent
: false,
7869 _onInput: function(/*Event*/ evt
){
7871 // Called AFTER the input event has happened
7873 // set text direction according to textDir that was defined in creation
7874 if(this.textDir
== "auto"){
7875 this.applyTextDir(this.focusNode
, this.focusNode
.value
);
7878 this._processInput(evt
);
7881 _processInput: function(/*Event*/ evt
){
7883 // Default action handler for user input events
7885 this._refreshState();
7887 // In case someone is watch()'ing for changes to displayedValue
7888 this._set("displayedValue", this.get("displayedValue"));
7891 postCreate: function(){
7892 // setting the value here is needed since value="" in the template causes "undefined"
7893 // and setting in the DOM (instead of the JS object) helps with form reset actions
7894 this.textbox
.setAttribute("value", this.textbox
.value
); // DOM and JS values should be the same
7896 this.inherited(arguments
);
7898 // normalize input events to reduce spurious event processing
7899 // onkeydown: do not forward modifier keys
7900 // set charOrCode to numeric keycode
7901 // onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
7902 // onpaste & oncut: set charOrCode to 229 (IME)
7903 // oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
7904 var handleEvent = function(e
){
7906 if(e
.type
== "keydown"){
7907 charOrCode
= e
.keyCode
;
7908 switch(charOrCode
){ // ignore state keys
7913 case keys
.CAPS_LOCK
:
7915 case keys
.SCROLL_LOCK
:
7918 if(!e
.ctrlKey
&& !e
.metaKey
&& !e
.altKey
){ // no modifiers
7919 switch(charOrCode
){ // ignore location keys
7930 case keys
.NUMPAD_MULTIPLY
:
7931 case keys
.NUMPAD_PLUS
:
7932 case keys
.NUMPAD_ENTER
:
7933 case keys
.NUMPAD_MINUS
:
7934 case keys
.NUMPAD_PERIOD
:
7935 case keys
.NUMPAD_DIVIDE
:
7938 if((charOrCode
>= 65 && charOrCode
<= 90) || (charOrCode
>= 48 && charOrCode
<= 57) || charOrCode
== keys
.SPACE
){
7939 return; // keypress will handle simple non-modified printable keys
7943 if(keys
[i
] === e
.keyCode
){
7948 if(!named
){ return; } // only allow named ones through
7951 charOrCode
= e
.charCode
>= 32 ? String
.fromCharCode(e
.charCode
) : e
.charCode
;
7953 charOrCode
= (e
.keyCode
>= 65 && e
.keyCode
<= 90) || (e
.keyCode
>= 48 && e
.keyCode
<= 57) || e
.keyCode
== keys
.SPACE
? String
.fromCharCode(e
.keyCode
) : e
.keyCode
;
7956 charOrCode
= 229; // IME
7958 if(e
.type
== "keypress"){
7959 if(typeof charOrCode
!= "string"){ return; }
7960 if((charOrCode
>= 'a' && charOrCode
<= 'z') || (charOrCode
>= 'A' && charOrCode
<= 'Z') || (charOrCode
>= '0' && charOrCode
<= '9') || (charOrCode
=== ' ')){
7961 if(e
.ctrlKey
|| e
.metaKey
|| e
.altKey
){ return; } // can only be stopped reliably in keydown
7964 if(e
.type
== "input"){
7965 if(this.__skipInputEvent
){ // duplicate event
7966 this.__skipInputEvent
= false;
7970 this.__skipInputEvent
= true;
7972 // create fake event to set charOrCode and to know if preventDefault() was called
7973 var faux
= { faux
: true }, attr
;
7975 if(attr
!= "layerX" && attr
!= "layerY"){ // prevent WebKit warnings
7977 if(typeof v
!= "function" && typeof v
!= "undefined"){ faux
[attr
] = v
; }
7981 charOrCode
: charOrCode
,
7982 _wasConsumed
: false,
7983 preventDefault: function(){
7984 faux
._wasConsumed
= true;
7987 stopPropagation: function(){ e
.stopPropagation(); }
7989 // give web page author a chance to consume the event
7990 //console.log(faux.type + ', charOrCode = (' + (typeof charOrCode) + ') ' + charOrCode + ', ctrl ' + !!faux.ctrlKey + ', alt ' + !!faux.altKey + ', meta ' + !!faux.metaKey + ', shift ' + !!faux.shiftKey);
7991 if(this.onInput(faux
) === false){ // return false means stop
7992 faux
.preventDefault();
7993 faux
.stopPropagation();
7995 if(faux
._wasConsumed
){ return; } // if preventDefault was called
7996 this.defer(function(){ this._onInput(faux
); }); // widget notification after key has posted
7998 this.own(on(this.textbox
, "keydown, keypress, paste, cut, input, compositionend", lang
.hitch(this, handleEvent
)));
8001 _blankValue
: '', // if the textbox is blank, what value should be reported
8002 filter: function(val
){
8004 // Auto-corrections (such as trimming) that are applied to textbox
8005 // value on blur or form submit.
8007 // For MappedTextBox subclasses, this is called twice
8009 // - once with the display value
8010 // - once the value as set/returned by set('value', ...)
8012 // and get('value'), ex: a Number for NumberTextBox.
8014 // In the latter case it does corrections like converting null to NaN. In
8015 // the former case the NumberTextBox.filter() method calls this.inherited()
8016 // to execute standard trimming code in TextBox.filter().
8018 // TODO: break this into two methods in 2.0
8021 // protected extension
8022 if(val
=== null){ return this._blankValue
; }
8023 if(typeof val
!= "string"){ return val
; }
8025 val
= lang
.trim(val
);
8028 val
= val
.toUpperCase();
8031 val
= val
.toLowerCase();
8033 if(this.propercase
){
8034 val
= val
.replace(/[^\s]+/g, function(word
){
8035 return word
.substring(0,1).toUpperCase() + word
.substring(1);
8041 _setBlurValue: function(){
8042 this._setValueAttr(this.get('value'), true);
8045 _onBlur: function(e
){
8046 if(this.disabled
){ return; }
8047 this._setBlurValue();
8048 this.inherited(arguments
);
8051 _isTextSelected: function(){
8052 return this.textbox
.selectionStart
!= this.textbox
.selectionEnd
;
8055 _onFocus: function(/*String*/ by
){
8056 if(this.disabled
|| this.readOnly
){ return; }
8058 // Select all text on focus via click if nothing already selected.
8059 // Since mouse-up will clear the selection, need to defer selection until after mouse-up.
8060 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
8061 if(this.selectOnClick
&& by
== "mouse"){
8062 this._selectOnClickHandle
= this.connect(this.domNode
, "onmouseup", function(){
8063 // Only select all text on first click; otherwise users would have no way to clear
8065 this.disconnect(this._selectOnClickHandle
);
8066 this._selectOnClickHandle
= null;
8068 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
8069 // and if not, then select all the text
8070 if(!this._isTextSelected()){
8071 _TextBoxMixin
.selectInputText(this.textbox
);
8074 // in case the mouseup never comes
8075 this.defer(function(){
8076 if(this._selectOnClickHandle
){
8077 this.disconnect(this._selectOnClickHandle
);
8078 this._selectOnClickHandle
= null;
8080 }, 500); // if mouseup not received soon, then treat it as some gesture
8082 // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
8083 // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
8084 this.inherited(arguments
);
8086 this._refreshState();
8090 // Overrides `dijit/_FormWidget/reset()`.
8091 // Additionally resets the displayed textbox value to ''
8092 this.textbox
.value
= '';
8093 this.inherited(arguments
);
8096 _setTextDirAttr: function(/*String*/ textDir
){
8098 // Setter for textDir.
8100 // Users shouldn't call this function; they should be calling
8101 // set('textDir', value)
8105 // only if new textDir is different from the old one
8106 // and on widgets creation.
8108 || this.textDir
!= textDir
){
8109 this._set("textDir", textDir
);
8110 // so the change of the textDir will take place immediately.
8111 this.applyTextDir(this.focusNode
, this.focusNode
.value
);
8117 _TextBoxMixin
._setSelectionRange
= dijit
._setSelectionRange = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
8118 if(element
.setSelectionRange
){
8119 element
.setSelectionRange(start
, stop
);
8123 _TextBoxMixin
.selectInputText
= dijit
.selectInputText = function(/*DomNode*/ element
, /*Number?*/ start
, /*Number?*/ stop
){
8125 // Select text in the input element argument, from start (default 0), to stop (default end).
8127 // TODO: use functions in _editor/selection.js?
8128 element
= dom
.byId(element
);
8129 if(isNaN(start
)){ start
= 0; }
8130 if(isNaN(stop
)){ stop
= element
.value
? element
.value
.length
: 0; }
8133 _TextBoxMixin
._setSelectionRange(element
, start
, stop
);
8134 }catch(e
){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
8137 return _TextBoxMixin
;
8141 'dijit/form/SimpleTextarea':function(){
8142 define("dijit/form/SimpleTextarea", [
8143 "dojo/_base/declare", // declare
8144 "dojo/dom-class", // domClass.add
8145 "dojo/sniff", // has("ie") has("opera")
8147 ], function(declare
, domClass
, has
, TextBox
){
8150 // dijit/form/SimpleTextarea
8153 return declare("dijit.form.SimpleTextarea", TextBox
, {
8155 // A simple textarea that degrades, and responds to
8156 // minimal LayoutContainer usage, and works with dijit/form/Form.
8157 // Doesn't automatically size according to input, like Textarea.
8160 // | <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
8163 // | new SimpleTextarea({ rows:20, cols:30 }, "foo");
8165 baseClass
: "dijitTextBox dijitTextArea",
8168 // The number of rows of text.
8172 // The number of characters per line.
8175 templateString
: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
8177 postMixInProperties: function(){
8178 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
8179 // TODO: parser will handle this in 2.0
8180 if(!this.value
&& this.srcNodeRef
){
8181 this.value
= this.srcNodeRef
.value
;
8183 this.inherited(arguments
);
8186 buildRendering: function(){
8187 this.inherited(arguments
);
8188 if(has("ie") && this.cols
){ // attribute selectors is not supported in IE6
8189 domClass
.add(this.textbox
, "dijitTextAreaCols");
8193 filter: function(/*String*/ value
){
8194 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
8195 // as \r\n instead of just \n
8197 value
= value
.replace(/\r/g,"");
8199 return this.inherited(arguments
);
8202 _onInput: function(/*Event?*/ e
){
8203 // Override TextBox._onInput() to enforce maxLength restriction
8205 var maxLength
= parseInt(this.maxLength
);
8206 var value
= this.textbox
.value
.replace(/\r/g,'');
8207 var overflow
= value
.length
- maxLength
;
8209 var textarea
= this.textbox
;
8210 if(textarea
.selectionStart
){
8211 var pos
= textarea
.selectionStart
;
8214 cr
= (this.textbox
.value
.substring(0,pos
).match(/\r/g) || []).length
;
8216 this.textbox
.value
= value
.substring(0,pos
-overflow
-cr
)+value
.substring(pos
-cr
);
8217 textarea
.setSelectionRange(pos
-overflow
, pos
-overflow
);
8218 }else if(this.ownerDocument
.selection
){ //IE
8220 var range
= this.ownerDocument
.selection
.createRange();
8221 // delete overflow characters
8222 range
.moveStart("character", -overflow
);
8229 this.inherited(arguments
);
8236 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n",
8237 'dijit/_base/window':function(){
8238 define("dijit/_base/window", [
8239 "dojo/window", // windowUtils.get
8240 "../main" // export symbol to dijit
8241 ], function(windowUtils
, dijit
){
8243 // dijit/_base/window
8248 // Back compatibility module, new code should use windowUtils directly instead of using this module.
8252 dijit
.getDocumentWindow = function(doc
){
8253 return windowUtils
.get(doc
);
8258 'dojo/hccss':function(){
8259 define("dojo/hccss", [
8260 "require", // require.toUrl
8261 "./_base/config", // config.blankGif
8262 "./dom-class", // domClass.add
8263 "./dom-style", // domStyle.getComputedStyle
8266 "./_base/window" // win.body
8267 ], function(require
, config
, domClass
, domStyle
, has
, ready
, win
){
8275 // Test if computer is in high contrast mode (i.e. if browser is not displaying background images).
8276 // Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode.
8277 // Returns `has()` method;
8281 // Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!.
8282 has
.add("highcontrast", function(){
8283 // note: if multiple documents, doesn't matter which one we use
8284 var div
= win
.doc
.createElement("div");
8285 div
.style
.cssText
= "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" +
8286 "background-image: url(" + (config
.blankGif
|| require
.toUrl("./resources/blank.gif")) + ");";
8287 win
.body().appendChild(div
);
8289 var cs
= domStyle
.getComputedStyle(div
),
8290 bkImg
= cs
.backgroundImage
,
8291 hc
= (cs
.borderTopColor
== cs
.borderRightColor
) ||
8292 (bkImg
&& (bkImg
== "none" || bkImg
== "url(invalid-url:)" ));
8295 div
.outerHTML
= ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
8297 win
.body().removeChild(div
);
8303 // Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
8304 // change this module to depend on dojo/domReady!
8305 ready(90, function(){
8306 if(has("highcontrast")){
8307 domClass
.add(win
.body(), "dj_a11y");
8315 'dijit/form/RadioButton':function(){
8316 define("dijit/form/RadioButton", [
8317 "dojo/_base/declare", // declare
8319 "./_RadioButtonMixin"
8320 ], function(declare
, CheckBox
, _RadioButtonMixin
){
8323 // dijit/form/RadioButton
8325 return declare("dijit.form.RadioButton", [CheckBox
, _RadioButtonMixin
], {
8327 // Same as an HTML radio, but with fancy styling.
8329 baseClass
: "dijitRadio"
8334 'dijit/main':function(){
8335 define("dijit/main", [
8344 // The dijit package main module.
8345 // Deprecated. Users should access individual modules (ex: dijit/registry) directly.
8353 'dijit/_OnDijitClickMixin':function(){
8354 define("dijit/_OnDijitClickMixin", [
8356 "dojo/_base/array", // array.forEach
8357 "dojo/keys", // keys.ENTER keys.SPACE
8358 "dojo/_base/declare", // declare
8359 "dojo/has", // has("dom-addeventlistener")
8360 "dojo/_base/unload", // unload.addOnWindowUnload
8361 "dojo/_base/window", // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
8363 ], function(on
, array
, keys
, declare
, has
, unload
, win
, a11yclick
){
8366 // dijit/_OnDijitClickMixin
8368 var ret
= declare("dijit._OnDijitClickMixin", null, {
8370 /*Object|null*/ obj
,
8371 /*String|Function*/ event
,
8372 /*String|Function*/ method
){
8374 // Connects specified obj/event to specified method of this object
8375 // and registers for disconnect() on widget destroy.
8377 // Provide widget-specific analog to connect.connect, except with the
8378 // implicit use of this widget as the target object.
8379 // This version of connect also provides a special "ondijitclick"
8380 // event which triggers on a click or space or enter keyup.
8381 // Events connected with `this.connect` are disconnected upon
8384 // A handle that can be passed to `disconnect` in order to disconnect before
8385 // the widget is destroyed.
8387 // | var btn = new Button();
8388 // | // when foo.bar() is called, call the listener we're going to
8389 // | // provide in the scope of btn
8390 // | btn.connect(foo, "bar", function(){
8391 // | console.debug(this.toString());
8396 return this.inherited(arguments
, [obj
, event
== "ondijitclick" ? a11yclick
: event
, method
]);
8400 ret
.a11yclick
= a11yclick
; // back compat
8406 'dijit/InlineEditBox':function(){
8408 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
8409 define("dijit/InlineEditBox", [
8411 "dojo/_base/array", // array.forEach
8412 "dojo/_base/declare", // declare
8413 "dojo/dom-attr", // domAttr.set domAttr.get
8414 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
8415 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
8416 "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
8417 "dojo/_base/event", // event.stop
8418 "dojo/i18n", // i18n.getLocalization
8419 "dojo/_base/kernel", // kernel.deprecated
8420 "dojo/keys", // keys.ENTER keys.ESCAPE
8421 "dojo/_base/lang", // lang.getObject
8422 "dojo/sniff", // has("ie")
8426 "./_TemplatedMixin",
8427 "./_WidgetsInTemplateMixin",
8430 "./form/_TextBoxMixin",
8432 "dojo/text!./templates/InlineEditBox.html",
8433 "dojo/i18n!./nls/common"
8434 ], function(require
, array
, declare
, domAttr
, domClass
, domConstruct
, domStyle
, event
, i18n
, kernel
, keys
, lang
, has
, when
,
8435 fm
, _Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
, _Container
, Button
, _TextBoxMixin
, TextBox
, template
){
8438 // dijit/InlineEditBox
8440 var InlineEditor
= declare("dijit._InlineEditor", [_Widget
, _TemplatedMixin
, _WidgetsInTemplateMixin
], {
8442 // Internal widget used by InlineEditBox, displayed when in editing mode
8443 // to display the editor and maybe save/cancel buttons. Calling code should
8444 // connect to save/cancel methods to detect when editing is finished
8446 // Has mainly the same parameters as InlineEditBox, plus these values:
8449 // Set of CSS attributes of display node, to replicate in editor
8452 // Value as an HTML string or plain text string, depending on renderAsHTML flag
8454 templateString
: template
,
8456 postMixInProperties: function(){
8457 this.inherited(arguments
);
8458 this.messages
= i18n
.getLocalization("dijit", "common", this.lang
);
8459 array
.forEach(["buttonSave", "buttonCancel"], function(prop
){
8461 this[prop
] = this.messages
[prop
];
8466 buildRendering: function(){
8467 this.inherited(arguments
);
8469 // Create edit widget in place in the template
8470 // TODO: remove getObject() for 2.0
8471 var Cls
= typeof this.editor
== "string" ? (lang
.getObject(this.editor
) || require(this.editor
)) : this.editor
;
8473 // Copy the style from the source
8474 // Don't copy ALL properties though, just the necessary/applicable ones.
8475 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
8476 // is a relative value like 200%, rather than an absolute value like 24px, and
8477 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
8478 var srcStyle
= this.sourceStyle
,
8479 editStyle
= "line-height:" + srcStyle
.lineHeight
+ ";",
8480 destStyle
= domStyle
.getComputedStyle(this.domNode
);
8481 array
.forEach(["Weight", "Family", "Size", "Style"], function(prop
){
8482 var textStyle
= srcStyle
["font" + prop
],
8483 wrapperStyle
= destStyle
["font" + prop
];
8484 if(wrapperStyle
!= textStyle
){
8485 editStyle
+= "font-" + prop
+ ":" + srcStyle
["font" + prop
] + ";";
8488 array
.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop
){
8489 this.domNode
.style
[prop
] = srcStyle
[prop
];
8491 var width
= this.inlineEditBox
.width
;
8492 if(width
== "100%"){
8494 editStyle
+= "width:100%;";
8495 this.domNode
.style
.display
= "block";
8497 // inline-block mode
8498 editStyle
+= "width:" + (width
+ (Number(width
) == width
? "px" : "")) + ";";
8500 var editorParams
= lang
.delegate(this.inlineEditBox
.editorParams
, {
8504 textDir
: this.textDir
8506 editorParams
[ "displayedValue" in Cls
.prototype ? "displayedValue" : "value"] = this.value
;
8507 this.editWidget
= new Cls(editorParams
, this.editorPlaceholder
);
8509 if(this.inlineEditBox
.autoSave
){
8510 // Remove the save/cancel buttons since saving is done by simply tabbing away or
8511 // selecting a value from the drop down list
8512 domConstruct
.destroy(this.buttonContainer
);
8516 postCreate: function(){
8517 this.inherited(arguments
);
8519 var ew
= this.editWidget
;
8521 if(this.inlineEditBox
.autoSave
){
8522 // Selecting a value from a drop down list causes an onChange event and then we save
8523 this.connect(ew
, "onChange", "_onChange");
8525 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
8526 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
8527 // so this is the only way we can see the key press event.
8528 this.connect(ew
, "onKeyPress", "_onKeyPress");
8530 // If possible, enable/disable save button based on whether the user has changed the value
8531 if("intermediateChanges" in ew
){
8532 ew
.set("intermediateChanges", true);
8533 this.connect(ew
, "onChange", "_onIntermediateChange");
8534 this.saveButton
.set("disabled", true);
8539 startup: function(){
8540 this.editWidget
.startup();
8541 this.inherited(arguments
);
8544 _onIntermediateChange: function(/*===== val =====*/){
8546 // Called for editor widgets that support the intermediateChanges=true flag as a way
8547 // to detect when to enable/disabled the save button
8548 this.saveButton
.set("disabled", (this.getValue() == this._resetValue
) || !this.enableSave());
8551 destroy: function(){
8552 this.editWidget
.destroy(true); // let the parent wrapper widget clean up the DOM
8553 this.inherited(arguments
);
8556 getValue: function(){
8558 // Return the [display] value of the edit widget
8559 var ew
= this.editWidget
;
8560 return String(ew
.get("displayedValue" in ew
? "displayedValue" : "value"));
8563 _onKeyPress: function(e
){
8565 // Handler for keypress in the edit box in autoSave mode.
8567 // For autoSave widgets, if Esc/Enter, call cancel/save.
8571 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
){
8572 if(e
.altKey
|| e
.ctrlKey
){
8575 // If Enter/Esc pressed, treat as save/cancel.
8576 if(e
.charOrCode
== keys
.ESCAPE
){
8578 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
8579 }else if(e
.charOrCode
== keys
.ENTER
&& e
.target
.tagName
== "INPUT"){
8581 this._onChange(); // fire _onBlur and then save
8584 // _onBlur will handle TAB automatically by allowing
8585 // the TAB to change focus before we mess with the DOM: #6227
8586 // Expounding by request:
8587 // The current focus is on the edit widget input field.
8588 // save() will hide and destroy this widget.
8589 // We want the focus to jump from the currently hidden
8590 // displayNode, but since it's hidden, it's impossible to
8591 // unhide it, focus it, and then have the browser focus
8592 // away from it to the next focusable element since each
8593 // of these events is asynchronous and the focus-to-next-element
8594 // is already queued.
8595 // So we allow the browser time to unqueue the move-focus event
8596 // before we do all the hide/show stuff.
8600 _onBlur: function(){
8602 // Called when focus moves outside the editor
8606 this.inherited(arguments
);
8607 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
){
8608 if(this.getValue() == this._resetValue
){
8610 }else if(this.enableSave()){
8616 _onChange: function(){
8618 // Called when the underlying widget fires an onChange event,
8619 // such as when the user selects a value from the drop down list of a ComboBox,
8620 // which means that the user has finished entering the value and we should save.
8624 if(this.inlineEditBox
.autoSave
&& this.inlineEditBox
.editing
&& this.enableSave()){
8625 fm
.focus(this.inlineEditBox
.displayNode
); // fires _onBlur which will save the formatted value
8629 enableSave: function(){
8631 // User overridable function returning a Boolean to indicate
8632 // if the Save button should be enabled or not - usually due to invalid conditions
8635 return this.editWidget
.isValid
? this.editWidget
.isValid() : true;
8640 // Focus the edit widget.
8644 this.editWidget
.focus();
8646 if(this.editWidget
.focusNode
){
8647 // IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
8648 fm
._onFocusNode(this.editWidget
.focusNode
);
8650 if(this.editWidget
.focusNode
.tagName
== "INPUT"){
8651 this.defer(function(){
8652 _TextBoxMixin
.selectInputText(this.editWidget
.focusNode
);
8660 var InlineEditBox
= declare("dijit.InlineEditBox", _Widget
, {
8662 // An element with in-line edit capabilities
8665 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
8666 // when you click it, an editor shows up in place of the original
8667 // text. Optionally, Save and Cancel button are displayed below the edit widget.
8668 // When Save is clicked, the text is pulled from the edit
8669 // widget and redisplayed and the edit widget is again hidden.
8670 // By default a plain Textarea widget is used as the editor (or for
8671 // inline values a TextBox), but you can specify an editor such as
8672 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
8673 // An edit widget must support the following API to be used:
8675 // - displayedValue or value as initialization parameter,
8676 // and available through set('displayedValue') / set('value')
8678 // - DOM-node focusNode = node containing editable text
8680 // editing: [readonly] Boolean
8681 // Is the node currently in edit mode?
8684 // autoSave: Boolean
8685 // Changing the value automatically saves it; don't have to push save button
8686 // (and save button isn't even displayed)
8689 // buttonSave: String
8690 // Save button label
8693 // buttonCancel: String
8694 // Cancel button label
8697 // renderAsHtml: Boolean
8698 // Set this to true if the specified Editor's value should be interpreted as HTML
8699 // rather than plain text (ex: `dijit.Editor`)
8700 renderAsHtml
: false,
8702 // editor: String|Function
8703 // MID (ex: "dijit/form/TextBox") or constructor for editor widget
8706 // editorWrapper: String|Function
8707 // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
8709 editorWrapper
: InlineEditor
,
8711 // editorParams: Object
8712 // Set of parameters for editor, like {required: true}
8715 // disabled: Boolean
8716 // If true, clicking the InlineEditBox to edit it will have no effect.
8719 onChange: function(/*===== value =====*/){
8721 // Set this handler to be notified of changes to value.
8726 onCancel: function(){
8728 // Set this handler to be notified when editing is cancelled.
8734 // Width of editor. By default it's width=100% (ie, block mode).
8738 // The display value of the widget in read-only mode
8741 // noValueIndicator: [const] String
8742 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
8743 noValueIndicator
: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
8744 "<span style='font-family: wingdings; text-decoration: underline;'>    ✍    </span>" :
8745 "<span style='text-decoration: underline;'>    ✍    </span>", //   ==
8747 constructor: function(/*===== params, srcNodeRef =====*/){
8749 // Create the widget.
8750 // params: Object|null
8751 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
8752 // and functions, typically callbacks like onClick.
8753 // The hash can contain any of the widget's properties, excluding read-only properties.
8754 // srcNodeRef: DOMNode|String?
8755 // If a srcNodeRef (DOM node) is specified:
8757 // - use srcNodeRef.innerHTML as my value
8758 // - replace srcNodeRef with my generated DOM tree
8760 this.editorParams
= {};
8763 postMixInProperties: function(){
8764 this.inherited(arguments
);
8766 // save pointer to original source node, since Widget nulls-out srcNodeRef
8767 this.displayNode
= this.srcNodeRef
;
8769 // connect handlers to the display node
8771 ondijitclick
: "_onClick",
8772 onmouseover
: "_onMouseOver",
8773 onmouseout
: "_onMouseOut",
8774 onfocus
: "_onMouseOver",
8775 onblur
: "_onMouseOut"
8777 for(var name
in events
){
8778 this.connect(this.displayNode
, name
, events
[name
]);
8780 this.displayNode
.setAttribute("role", "button");
8781 if(!this.displayNode
.getAttribute("tabIndex")){
8782 this.displayNode
.setAttribute("tabIndex", 0);
8785 if(!this.value
&& !("value" in this.params
)){ // "" is a good value if specified directly so check params){
8786 this.value
= lang
.trim(this.renderAsHtml
? this.displayNode
.innerHTML
:
8787 (this.displayNode
.innerText
|| this.displayNode
.textContent
|| ""));
8790 this.displayNode
.innerHTML
= this.noValueIndicator
;
8793 domClass
.add(this.displayNode
, 'dijitInlineEditBoxDisplayMode');
8796 setDisabled: function(/*Boolean*/ disabled
){
8798 // Deprecated. Use set('disabled', ...) instead.
8801 kernel
.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
8802 this.set('disabled', disabled
);
8805 _setDisabledAttr: function(/*Boolean*/ disabled
){
8807 // Hook to make set("disabled", ...) work.
8808 // Set disabled state of widget.
8809 this.domNode
.setAttribute("aria-disabled", disabled
? "true" : "false");
8811 this.displayNode
.removeAttribute("tabIndex");
8813 this.displayNode
.setAttribute("tabIndex", 0);
8815 domClass
.toggle(this.displayNode
, "dijitInlineEditBoxDisplayModeDisabled", disabled
);
8816 this._set("disabled", disabled
);
8819 _onMouseOver: function(){
8821 // Handler for onmouseover and onfocus event.
8825 domClass
.add(this.displayNode
, "dijitInlineEditBoxDisplayModeHover");
8829 _onMouseOut: function(){
8831 // Handler for onmouseout and onblur event.
8834 domClass
.remove(this.displayNode
, "dijitInlineEditBoxDisplayModeHover");
8837 _onClick: function(/*Event*/ e
){
8839 // Handler for onclick event.
8850 // Since FF gets upset if you move a node while in an event handler for that node...
8856 // Display the editor widget in place of the original (read only) markup.
8860 if(this.disabled
|| this.editing
){
8863 this._set('editing', true);
8865 // save some display node values that can be restored later
8866 this._savedTabIndex
= domAttr
.get(this.displayNode
, "tabIndex") || "0";
8868 if(this.wrapperWidget
){
8869 var ew
= this.wrapperWidget
.editWidget
;
8870 ew
.set("displayedValue" in ew
? "displayedValue" : "value", this.value
);
8872 // Placeholder for edit widget
8873 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
8874 // when Calendar dropdown appears, which happens automatically on focus.
8875 var placeholder
= domConstruct
.create("span", null, this.domNode
, "before");
8877 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
8878 var Ewc
= typeof this.editorWrapper
== "string" ? lang
.getObject(this.editorWrapper
) : this.editorWrapper
;
8879 this.wrapperWidget
= new Ewc({
8881 buttonSave
: this.buttonSave
,
8882 buttonCancel
: this.buttonCancel
,
8885 tabIndex
: this._savedTabIndex
,
8886 editor
: this.editor
,
8887 inlineEditBox
: this,
8888 sourceStyle
: domStyle
.getComputedStyle(this.displayNode
),
8889 save
: lang
.hitch(this, "save"),
8890 cancel
: lang
.hitch(this, "cancel"),
8891 textDir
: this.textDir
8893 if(!this.wrapperWidget
._started
){
8894 this.wrapperWidget
.startup();
8900 var ww
= this.wrapperWidget
;
8902 // to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
8903 // and then when it's finished rendering, we switch from display mode to editor
8904 // position: absolute releases screen space allocated to the display node
8905 // opacity:0 is the same as visibility: hidden but is still focusable
8906 // visibility: hidden removes focus outline
8908 domClass
.add(this.displayNode
, "dijitOffScreen");
8909 domClass
.remove(ww
.domNode
, "dijitOffScreen");
8910 domStyle
.set(ww
.domNode
, { visibility
: "visible" });
8911 domAttr
.set(this.displayNode
, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
8913 // After edit widget has finished initializing (in particular need to wait for dijit.Editor),
8914 // or immediately if there is no onLoadDeferred Deferred,
8915 // replace the display widget with edit widget, leaving them both displayed for a brief time so that
8916 // focus can be shifted without incident.
8917 when(ww
.editWidget
.onLoadDeferred
, lang
.hitch(ww
, function(){
8918 this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
8919 this.focus(); // both nodes are showing, so we can switch focus safely
8920 this._resetValue
= this.getValue();
8925 _onBlur: function(){
8927 // Called when focus moves outside the InlineEditBox.
8928 // Performs garbage collection.
8932 this.inherited(arguments
);
8934 /* causes IE focus problems, see TooltipDialog_a11y.html...
8935 this.defer(function(){
8936 if(this.wrapperWidget){
8937 this.wrapperWidget.destroy();
8938 delete this.wrapperWidget;
8945 destroy: function(){
8946 if(this.wrapperWidget
&& !this.wrapperWidget
._destroyed
){
8947 this.wrapperWidget
.destroy();
8948 delete this.wrapperWidget
;
8950 this.inherited(arguments
);
8953 _showText: function(/*Boolean*/ focus
){
8955 // Revert to display mode, and optionally focus on display node
8959 var ww
= this.wrapperWidget
;
8960 domStyle
.set(ww
.domNode
, { visibility
: "hidden" }); // hide the editor from mouse/keyboard events
8961 domClass
.add(ww
.domNode
, "dijitOffScreen");
8962 domClass
.remove(this.displayNode
, "dijitOffScreen");
8963 domAttr
.set(this.displayNode
, "tabIndex", this._savedTabIndex
);
8965 fm
.focus(this.displayNode
);
8969 save: function(/*Boolean*/ focus
){
8971 // Save the contents of the editor and revert to display mode.
8973 // Focus on the display mode text
8977 if(this.disabled
|| !this.editing
){
8980 this._set('editing', false);
8982 var ww
= this.wrapperWidget
;
8983 var value
= ww
.getValue();
8984 this.set('value', value
); // display changed, formatted value
8986 this._showText(focus
); // set focus as needed
8989 setValue: function(/*String*/ val
){
8991 // Deprecated. Use set('value', ...) instead.
8994 kernel
.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
8995 return this.set("value", val
);
8998 _setValueAttr: function(/*String*/ val
){
9000 // Hook to make set("value", ...) work.
9001 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
9003 val
= lang
.trim(val
);
9004 var renderVal
= this.renderAsHtml
? val
: val
.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, ""
;").replace(/\n/g, "<br
>");
9005 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
9006 this._set("value
", val);
9009 // tell the world that we have changed
9010 this.defer(function(){
9012 }); // defer prevents browser freeze for long-running event handlers
9014 // contextual (auto) text direction depends on the text value
9015 if(this.textDir == "auto
"){
9016 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9020 getValue: function(){
9022 // Deprecated. Use get('value') instead.
9025 kernel.deprecated("dijit
.InlineEditBox
.getValue() is deprecated
. Use
get('value') instead
.", "", "2.0");
9026 return this.get("value
");
9029 cancel: function(/*Boolean*/ focus){
9031 // Revert to display mode, discarding any changes made in the editor
9035 if(this.disabled || !this.editing){
9038 this._set('editing', false);
9040 // tell the world that we have no changes
9041 this.defer("onCancel
"); // defer prevents browser freeze for long-running event handlers
9043 this._showText(focus);
9046 _setTextDirAttr: function(/*String*/ textDir){
9048 // Setter for textDir.
9050 // Users shouldn't call this function; they should be calling
9051 // set('textDir', value)
9054 if(!this._created || this.textDir != textDir){
9055 this._set("textDir
", textDir);
9056 this.applyTextDir(this.displayNode, this.displayNode.innerText);
9057 this.displayNode.align = this.dir == "rtl
" ? "right
" : "left
"; //fix the text alignment
9062 InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
9064 return InlineEditBox;
9067 'dojo/selector/acme':function(){
9068 define("dojo
/selector/acme", [
9069 "../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window
"
9070 ], function(dom, has, array, lang, win){
9073 // dojo/selector/acme
9076 acme architectural overview:
9078 acme is a relatively full-featured CSS3 query library. It is
9079 designed to take any valid CSS3 selector and return the nodes matching
9080 the selector. To do this quickly, it processes queries in several
9081 steps, applying caching where profitable.
9083 The steps (roughly in reverse order of the way they appear in the code):
9084 1.) check to see if we already have a "query dispatcher
"
9085 - if so, use that with the given parameterization. Skip to step 4.
9086 2.) attempt to determine which branch to dispatch the query to:
9087 - JS (optimized DOM iteration)
9088 - native (FF3.1+, Safari 3.1+, IE 8+)
9089 3.) tokenize and convert to executable "query dispatcher
"
9090 - this is where the lion's share of the complexity in the
9091 system lies. In the DOM version, the query dispatcher is
9092 assembled as a chain of "yes
/no
" test functions pertaining to
9093 a section of a simple query statement (".blah
:nth
-child(odd
)"
9094 but not "div div
", which is 2 simple statements). Individual
9095 statement dispatchers are cached (to prevent re-definition)
9096 as are entire dispatch chains (to make re-execution of the
9098 4.) the resulting query dispatcher is called in the passed scope
9099 (by default the top-level document)
9100 - for DOM queries, this results in a recursive, top-down
9101 evaluation of nodes based on each simple query section
9102 - for native implementations, this may mean working around spec
9104 5.) matched nodes are pruned to ensure they are unique (if necessary)
9108 ////////////////////////////////////////////////////////////////////////
9110 ////////////////////////////////////////////////////////////////////////
9112 // if you are extracting acme for use in your own system, you will
9113 // need to provide these methods and properties. No other porting should be
9114 // necessary, save for configuring the system to use a class other than
9115 // dojo/NodeList as the return instance instantiator
9116 var trim = lang.trim;
9117 var each = array.forEach;
9119 var getDoc = function(){ return win.doc; };
9120 // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
9121 var cssCaseBug = (getDoc().compatMode) == "BackCompat
";
9123 ////////////////////////////////////////////////////////////////////////
9125 ////////////////////////////////////////////////////////////////////////
9128 var specials = ">~+";
9130 // global thunk to determine whether we should treat the current query as
9131 // case sensitive or not. This switch is flipped by the query evaluator
9132 // based on the document passed as the context to search.
9133 var caseSensitive = false;
9136 var yesman = function(){ return true; };
9138 ////////////////////////////////////////////////////////////////////////
9140 ////////////////////////////////////////////////////////////////////////
9142 var getQueryParts = function(query){
9144 // state machine for query tokenization
9146 // instead of using a brittle and slow regex-based CSS parser,
9147 // acme implements an AST-style query representation. This
9148 // representation is only generated once per query. For example,
9149 // the same query run multiple times or under different root nodes
9150 // does not re-parse the selector expression but instead uses the
9151 // cached data structure. The state machine implemented here
9152 // terminates on the last " " (space) character and returns an
9153 // ordered array of query component structures (or "parts
"). Each
9154 // part represents an operator or a simple CSS filtering
9155 // expression. The structure for parts is documented in the code
9160 // this code is designed to run fast and compress well. Sacrifices
9161 // to readability and maintainability have been made. Your best
9162 // bet when hacking the tokenizer is to put The Donnas on *really*
9163 // loud (may we recommend their "Spend The Night
" release?) and
9164 // just assume you're gonna make mistakes. Keep the unit tests
9165 // open and run them frequently. Knowing is half the battle ;-)
9166 if(specials.indexOf(query.slice(-1)) >= 0){
9167 // if we end with a ">", "+", or "~", that means we're implicitly
9168 // searching all children, so make it explicit
9171 // if you have not provided a terminator, one will be provided for
9176 var ts = function(/*Integer*/ s, /*Integer*/ e){
9179 // take an index to start a string slice from and an end position
9180 // and return a trimmed copy of that sub-string
9181 return trim(query.slice(s, e));
9184 // the overall data graph of the full query, as represented by queryPart objects
9185 var queryParts = [];
9188 // state keeping vars
9189 var inBrackets = -1, inParens = -1, inMatchFor = -1,
9190 inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
9191 lc = "", cc = "", pStart;
9194 var x = 0, // index in the query
9196 currentPart = null, // data structure representing the entire clause
9197 _cp = null; // the current pseudo or attr matcher
9199 // several temporary variables are assigned to this structure during a
9200 // potential sub-expression match:
9202 // a string representing the current full attribute match in a
9203 // bracket expression
9205 // if there's an operator in a bracket expression, this is
9206 // used to keep track of it
9208 // the internals of parenthetical expression for a pseudo. for
9209 // :nth-child(2n+1), value might be "2n
+1"
9211 var endTag = function(){
9212 // called when the tokenizer hits the end of a particular tag name.
9213 // Re-sets state variables for tag matching and sets up the matcher
9214 // to handle the next type of token (tag or operator).
9216 var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
9217 currentPart[ (specials.indexOf(tv) < 0) ? "tag
" : "oper
" ] = tv;
9222 var endId = function(){
9223 // called when the tokenizer might be at the end of an ID portion of a match
9225 currentPart.id = ts(inId, x).replace(/\\/g, "");
9230 var endClass = function(){
9231 // called when the tokenizer might be at the end of a class name
9232 // match. CSS allows for multiple classes, so we augment the
9233 // current item with another class in its list
9235 currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
9240 var endAll = function(){
9241 // at the end of a simple fragment, so wall off the matches
9247 var endPart = function(){
9250 currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
9252 // hint to the selector engine to tell it whether or not it
9253 // needs to do any iteration. Many simple selectors don't, and
9254 // we can avoid significant construction-time work by advising
9255 // the system to skip them
9256 currentPart.loops = (
9257 currentPart.pseudos.length ||
9258 currentPart.attrs.length ||
9259 currentPart.classes.length );
9261 currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
9264 // otag/tag are hints to suggest to the system whether or not
9265 // it's an operator or a tag. We save a copy of otag since the
9266 // tag name is cast to upper-case in regular HTML matches. The
9267 // system has a global switch to figure out if the current
9268 // expression needs to be case sensitive or not and it will use
9269 // otag or tag accordingly
9270 currentPart.otag = currentPart.tag = (currentPart["oper
"]) ? null : (currentPart.tag || "*");
9272 if(currentPart.tag){
9273 // if we're in a case-insensitive HTML doc, we likely want
9274 // the toUpperCase when matching on element.tagName. If we
9275 // do it here, we can skip the string op per node
9277 currentPart.tag = currentPart.tag.toUpperCase();
9280 // add the part to the list
9281 if(queryParts.length && (queryParts[queryParts.length-1].oper)){
9282 // operators are always infix, so we remove them from the
9283 // list and attach them to the next match. The evaluator is
9284 // responsible for sorting out how to handle them.
9285 currentPart.infixOper = queryParts.pop();
9286 currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
9288 console.debug( "swapping out the infix
",
9289 currentPart.infixOper,
9290 "and attaching it to
",
9294 queryParts.push(currentPart);
9299 // iterate over the query, character by character, building up a
9300 // list of query part objects
9301 for(; lc=cc, cc=query.charAt(x), x < ql; x++){
9302 // cc: the current character in the match
9303 // lc: the last character (if any)
9305 // someone is trying to escape something, so don't try to match any
9306 // fragments. We assume we're inside a literal.
9307 if(lc == "\\"){ continue; }
9308 if(!currentPart){ // a part was just ended or none has yet been created
9309 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
9311 // rules describe full CSS sub-expressions, like:
9313 // .className:first-child
9315 // thinger > div.howdy[type=thinger]
9316 // the indidual components of the previous query would be
9317 // split into 3 parts that would be represented a structure like:
9320 // query: "thinger
",
9324 // query: "div
.howdy
[type
=thinger
]",
9325 // classes: ["howdy
"],
9333 query: null, // the full text of the part's rule
9334 pseudos: [], // CSS supports multiple pseud-class matches in a single rule
9335 attrs: [], // CSS supports multi-attribute match, so we need an array
9336 classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
9337 tag: null, // only one tag...
9338 oper: null, // ...or operator per component. Note that these wind up being exclusive.
9339 id: null, // the id component of a rule
9341 return caseSensitive ? this.otag : this.tag;
9345 // if we don't have a part, we assume we're going to start at
9346 // the beginning of a match, which should be a tag name. This
9347 // might fault a little later on, but we detect that and this
9348 // iteration will still be fine.
9352 // Skip processing all quoted characters.
9353 // If we are inside quoted text then currentQuoteChar stores the character that began the quote,
9354 // thus that character that will end it.
9355 if(currentQuoteChar){
9356 if(cc == currentQuoteChar){
9357 currentQuoteChar = null;
9360 }else if (cc == "'" || cc == '"'){
9361 currentQuoteChar = cc;
9365 if(inBrackets >= 0){
9366 // look for a the close first
9367 if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
9369 // no attribute match was previously begun, so we
9370 // assume this is an attribute existence match in the
9371 // form of [someAttributeName]
9372 _cp.attr = ts(inBrackets+1, x);
9374 // we had an attribute already, so we know that we're
9375 // matching some sort of value, as in [attrName=howdy]
9376 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
9378 var cmf = _cp.matchFor;
9380 // try to strip quotes from the matchFor value. We want
9381 // [attrName=howdy] to match the same
9382 // as [attrName = 'howdy' ]
9383 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
9384 _cp.matchFor = cmf.slice(1, -1);
9387 // remove backslash escapes from an attribute match, since DOM
9388 // querying will get attribute values without backslashes
9390 _cp.matchFor = _cp.matchFor.replace(/\\/g, "");
9393 // end the attribute by adding it to the list of attributes.
9394 currentPart.attrs.push(_cp);
9395 _cp = null; // necessary?
9396 inBrackets = inMatchFor = -1;
9397 }else if(cc == "="){
9398 // if the last char was an operator prefix, make sure we
9399 // record it along with the "=" operator.
9400 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
9401 _cp.type = addToCc+cc;
9402 _cp.attr = ts(inBrackets+1, x-addToCc.length);
9405 // now look for other clause parts
9406 }else if(inParens >= 0){
9407 // if we're in a parenthetical expression, we need to figure
9408 // out if it's attached to a pseudo-selector rule like
9412 _cp.value = ts(inParens+1, x);
9414 inPseudo = inParens = -1;
9416 }else if(cc == "#"){
9417 // start of an ID match
9420 }else if(cc == "."){
9421 // start of a class match
9424 }else if(cc == ":"){
9425 // start of a pseudo-selector match
9428 }else if(cc == "["){
9429 // start of an attribute match.
9432 // provide a new structure for the attribute match to fill-in
9435 attr: null, type: null, matchFor: null
9438 }else if(cc == "("){
9439 // we really only care if we've entered a parenthetical
9440 // expression if we're already inside a pseudo-selector match
9442 // provide a new structure for the pseudo match to fill-in
9444 name: ts(inPseudo+1, x),
9447 currentPart.pseudos.push(_cp);
9452 // if it's a space char and the last char is too, consume the
9453 // current one without doing more work
9463 ////////////////////////////////////////////////////////////////////////
9464 // DOM query infrastructure
9465 ////////////////////////////////////////////////////////////////////////
9467 var agree = function(first, second){
9468 // the basic building block of the yes/no chaining system. agree(f1,
9469 // f2) generates a new function which returns the boolean results of
9470 // both of the passed functions to a single logical-anded result. If
9471 // either are not passed, the other is used exclusively.
9472 if(!first){ return second; }
9473 if(!second){ return first; }
9476 return first.apply(window, arguments) && second.apply(window, arguments);
9480 var getArr = function(i, arr){
9481 // helps us avoid array alloc when we don't need it
9482 var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
9487 var _isElement = function(n){ return (1 == n.nodeType); };
9489 // FIXME: need to coalesce _getAttr with defaultGetter
9491 var _getAttr = function(elem, attr){
9492 if(!elem){ return blank; }
9493 if(attr == "class"){
9494 return elem.className || blank;
9497 return elem.htmlFor || blank;
9499 if(attr == "style
"){
9500 return elem.style.cssText || blank;
9502 return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
9506 "*=": function(attr, value){
9507 return function(elem){
9509 // an E element whose "foo
" attribute value contains
9510 // the substring "bar
"
9511 return (_getAttr(elem, attr).indexOf(value)>=0);
9514 "^=": function(attr, value){
9516 // an E element whose "foo
" attribute value begins exactly
9517 // with the string "bar
"
9518 return function(elem){
9519 return (_getAttr(elem, attr).indexOf(value)==0);
9522 "$=": function(attr, value){
9524 // an E element whose "foo
" attribute value ends exactly
9525 // with the string "bar
"
9526 return function(elem){
9527 var ea = " "+_getAttr(elem, attr);
9528 var lastIndex = ea.lastIndexOf(value);
9529 return lastIndex > -1 && (lastIndex==(ea.length-value.length));
9532 "~=": function(attr, value){
9534 // an E element whose "foo
" attribute value is a list of
9535 // space-separated values, one of which is exactly equal
9538 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
9539 var tval = " "+value+" ";
9540 return function(elem){
9541 var ea = " "+_getAttr(elem, attr)+" ";
9542 return (ea.indexOf(tval)>=0);
9545 "|=": function(attr, value){
9546 // E[hreflang|="en
"]
9547 // an E element whose "hreflang
" attribute has a
9548 // hyphen-separated list of values beginning (from the
9550 var valueDash = value+"-";
9551 return function(elem){
9552 var ea = _getAttr(elem, attr);
9555 (ea.indexOf(valueDash)==0)
9559 "=": function(attr, value){
9560 return function(elem){
9561 return (_getAttr(elem, attr) == value);
9566 // avoid testing for node type if we can. Defining this in the negative
9567 // here to avoid negation in the fast path.
9568 var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
9569 var _ns = !_noNES ? "nextElementSibling
" : "nextSibling
";
9570 var _ps = !_noNES ? "previousElementSibling
" : "previousSibling
";
9571 var _simpleNodeTest = (_noNES ? _isElement : yesman);
9573 var _lookLeft = function(node){
9575 while(node = node[_ps]){
9576 if(_simpleNodeTest(node)){ return false; }
9581 var _lookRight = function(node){
9583 while(node = node[_ns]){
9584 if(_simpleNodeTest(node)){ return false; }
9589 var getNodeIndex = function(node){
9590 var root = node.parentNode;
9591 root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
9593 tret = root.children || root.childNodes,
9594 ci = (node["_i
"]||node.getAttribute("_i
")||-1),
9595 cl = (root["_l
"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l
") : -1));
9597 if(!tret){ return -1; }
9598 var l = tret.length;
9600 // we calculate the parent length as a cheap way to invalidate the
9601 // cache. It's not 100% accurate, but it's much more honest than what
9602 // other libraries do
9603 if( cl == l && ci >= 0 && cl >= 0 ){
9604 // if it's legit, tag and release
9608 // else re-key things
9609 if(has("ie
") && typeof root.setAttribute !== "undefined"){
9610 root.setAttribute("_l
", l);
9615 for(var te = root["firstElementChild
"]||root["firstChild
"]; te; te = te[_ns]){
9616 if(_simpleNodeTest(te)){
9618 te.setAttribute("_i
", ++i);
9624 // shortcutting the return at this step in indexing works
9625 // very well for benchmarking but we avoid it here since
9626 // it leads to potential O(n^2) behavior in sequential
9627 // getNodexIndex operations on a previously un-indexed
9628 // parent. We may revisit this at a later time, but for
9629 // now we just want to get the right answer more often
9638 var isEven = function(elem){
9639 return !((getNodeIndex(elem)) % 2);
9642 var isOdd = function(elem){
9643 return ((getNodeIndex(elem)) % 2);
9647 "checked
": function(name, condition){
9648 return function(elem){
9649 return !!("checked
" in elem ? elem.checked : elem.selected);
9652 "disabled
": function(name, condition){
9653 return function(elem){
9654 return elem.disabled;
9657 "enabled
": function(name, condition){
9658 return function(elem){
9659 return !elem.disabled;
9662 "first
-child
": function(){ return _lookLeft; },
9663 "last
-child
": function(){ return _lookRight; },
9664 "only
-child
": function(name, condition){
9665 return function(node){
9666 return _lookLeft(node) && _lookRight(node);
9669 "empty
": function(name, condition){
9670 return function(elem){
9671 // DomQuery and jQuery get this wrong, oddly enough.
9672 // The CSS 3 selectors spec is pretty explicit about it, too.
9673 var cn = elem.childNodes;
9674 var cnl = elem.childNodes.length;
9675 // if(!cnl){ return true; }
9676 for(var x=cnl-1; x >= 0; x--){
9677 var nt = cn[x].nodeType;
9678 if((nt === 1)||(nt == 3)){ return false; }
9683 "contains
": function(name, condition){
9684 var cz = condition.charAt(0);
9685 if( cz == '"' || cz == "'" ){ //remove quote
9686 condition = condition.slice(1, -1);
9688 return function(elem){
9689 return (elem.innerHTML.indexOf(condition) >= 0);
9692 "not
": function(name, condition){
9693 var p = getQueryParts(condition)[0];
9694 var ignores = { el: 1 };
9698 if(!p.classes.length){
9699 ignores.classes = 1;
9701 var ntf = getSimpleFilterFunc(p, ignores);
9702 return function(elem){
9703 return (!ntf(elem));
9706 "nth
-child
": function(name, condition){
9708 // avoid re-defining function objects if we can
9709 if(condition == "odd
"){
9711 }else if(condition == "even
"){
9714 // FIXME: can we shorten this?
9715 if(condition.indexOf("n
") != -1){
9716 var tparts = condition.split("n
", 2);
9717 var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
9718 var idx = tparts[1] ? pi(tparts[1]) : 0;
9719 var lb = 0, ub = -1;
9722 idx = (idx % pred) && (pred + (idx % pred));
9725 lb = idx - idx % pred;
9731 // idx has to be greater than 0 when pred is negative;
9732 // shall we throw an error here?
9739 return function(elem){
9740 var i = getNodeIndex(elem);
9741 return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
9747 var ncount = pi(condition);
9748 return function(elem){
9749 return (getNodeIndex(elem) == ncount);
9754 var defaultGetter = (has("ie
") < 9 || has("ie
") == 9 && has("quirks
")) ? function(cond){
9755 var clc = cond.toLowerCase();
9756 if(clc == "class"){ cond = "className
"; }
9757 return function(elem){
9758 return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
9761 return function(elem){
9762 return (elem && elem.getAttribute && elem.hasAttribute(cond));
9766 var getSimpleFilterFunc = function(query, ignores){
9767 // generates a node tester function based on the passed query part. The
9768 // query part is one of the structures generated by the query parser
9769 // when it creates the query AST. The "ignores
" object specifies which
9770 // (if any) tests to skip, allowing the system to avoid duplicating
9771 // work where it may have already been taken into account by other
9772 // factors such as how the nodes to test were fetched in the first
9774 if(!query){ return yesman; }
9775 ignores = ignores||{};
9779 if(!("el
" in ignores)){
9780 ff = agree(ff, _isElement);
9783 if(!("tag
" in ignores)){
9784 if(query.tag != "*"){
9785 ff = agree(ff, function(elem){
9786 return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
9791 if(!("classes
" in ignores)){
9792 each(query.classes, function(cname, idx, arr){
9793 // get the class name
9795 var isWildcard = cname.charAt(cname.length-1) == "*";
9797 cname = cname.substr(0, cname.length-1);
9799 // I dislike the regex thing, even if memoized in a cache, but it's VERY short
9800 var re = new RegExp("(?:^|\\s
)" + cname + (isWildcard ? ".*" : "") + "(?:\\s
|$)");
9802 var re = new RegExp("(?:^|\\s
)" + cname + "(?:\\s
|$)");
9803 ff = agree(ff, function(elem){
9804 return re.test(elem.className);
9810 if(!("pseudos
" in ignores)){
9811 each(query.pseudos, function(pseudo){
9812 var pn = pseudo.name;
9814 ff = agree(ff, pseudos[pn](pn, pseudo.value));
9819 if(!("attrs
" in ignores)){
9820 each(query.attrs, function(attr){
9823 // type, attr, matchFor
9824 if(attr.type && attrs[attr.type]){
9825 matcher = attrs[attr.type](a, attr.matchFor);
9827 matcher = defaultGetter(a);
9830 ff = agree(ff, matcher);
9835 if(!("id
" in ignores)){
9837 ff = agree(ff, function(elem){
9838 return (!!elem && (elem.id == query.id));
9844 if(!("default" in ignores)){
9851 var _nextSibling = function(filterFunc){
9852 return function(node, ret, bag){
9853 while(node = node[_ns]){
9854 if(_noNES && (!_isElement(node))){ continue; }
9856 (!bag || _isUnique(node, bag)) &&
9867 var _nextSiblings = function(filterFunc){
9868 return function(root, ret, bag){
9871 if(_simpleNodeTest(te)){
9872 if(bag && !_isUnique(te, bag)){
9885 // get an array of child *elements*, skipping text and comment nodes
9886 var _childElements = function(filterFunc){
9887 filterFunc = filterFunc||yesman;
9888 return function(root, ret, bag){
9889 // get an array of child elements, skipping text and comment nodes
9890 var te, x = 0, tret = root.children || root.childNodes;
9891 while(te = tret[x++]){
9893 _simpleNodeTest(te) &&
9894 (!bag || _isUnique(te, bag)) &&
9904 // test to see if node is below root
9905 var _isDescendant = function(node, root){
9906 var pn = node.parentNode;
9916 var _getElementsFuncCache = {};
9918 var getElementsFunc = function(query){
9919 var retFunc = _getElementsFuncCache[query.query];
9920 // if we've got a cached dispatcher, just use that
9921 if(retFunc){ return retFunc; }
9922 // else, generate a new on
9925 // this function returns a function that searches for nodes and
9926 // filters them. The search may be specialized by infix operators
9927 // (">", "~", or "+") else it will default to searching all
9928 // descendants (the " " selector). Once a group of children is
9929 // found, a test function is applied to weed out the ones we
9930 // don't want. Many common cases can be fast-pathed. We spend a
9931 // lot of cycles to create a dispatcher that doesn't do more work
9932 // than necessary at any point since, unlike this function, the
9933 // dispatchers will be called every time. The logic of generating
9934 // efficient dispatchers looks like this in pseudo code:
9936 // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
9937 // if infixOperator == " ":
9939 // return def(root):
9940 // return d.byId(id, root);
9943 // return def(root):
9944 // return filter(d.byId(id, root));
9946 // elif cssClass && getElementsByClassName:
9947 // return def(root):
9948 // return filter(root.getElementsByClassName(cssClass));
9951 // return def(root):
9952 // return root.getElementsByTagName(tagName);
9955 // # search by tag name, then filter
9956 // return def(root):
9957 // return filter(root.getElementsByTagName(tagName||"*"));
9959 // elif infixOperator == ">":
9960 // # search direct children
9961 // return def(root):
9962 // return filter(root.children);
9964 // elif infixOperator == "+":
9965 // # search next sibling
9966 // return def(root):
9967 // return filter(root.nextElementSibling);
9969 // elif infixOperator == "~":
9970 // # search rightward siblings
9971 // return def(root):
9972 // return filter(nextSiblings(root));
9974 var io = query.infixOper;
9975 var oper = (io ? io.oper : "");
9976 // the default filter func which tests for all conditions in the query
9977 // part. This is potentially inefficient, so some optimized paths may
9978 // re-define it to test fewer things.
9979 var filterFunc = getSimpleFilterFunc(query, { el: 1 });
9981 var wildcardTag = ("*" == qt);
9982 var ecs = getDoc()["getElementsByClassName
"];
9985 // if there's no infix operator, then it's a descendant query. ID
9986 // and "elements by
class name
" variants can be accelerated so we
9987 // call them out explicitly:
9989 // testing shows that the overhead of yesman() is acceptable
9990 // and can save us some bytes vs. re-defining the function
9992 filterFunc = (!query.loops && wildcardTag) ?
9994 getSimpleFilterFunc(query, { el: 1, id: 1 });
9996 retFunc = function(root, arr){
9997 var te = dom.byId(query.id, (root.ownerDocument||root));
9998 if(!te || !filterFunc(te)){ return; }
9999 if(9 == root.nodeType){ // if root's a doc, we just return directly
10000 return getArr(te, arr);
10001 }else{ // otherwise check ancestry
10002 if(_isDescendant(te, root)){
10003 return getArr(te, arr);
10009 // isAlien check. Workaround for Prototype.js being totally evil/dumb.
10010 /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
10011 query.classes.length &&
10014 // it's a class-based query and we've got a fast way to run it.
10016 // ignore class and ID filters since we will have handled both
10017 filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
10018 var classesString = query.classes.join(" ");
10019 retFunc = function(root, arr, bag){
10020 var ret = getArr(0, arr), te, x=0;
10021 var tret = root.getElementsByClassName(classesString);
10022 while((te = tret[x++])){
10023 if(filterFunc(te, root) && _isUnique(te, bag)){
10030 }else if(!wildcardTag && !query.loops){
10031 // it's tag only. Fast-path it.
10032 retFunc = function(root, arr, bag){
10033 var ret = getArr(0, arr), te, x=0;
10034 var tag = query.getTag(),
10035 tret = tag ? root.getElementsByTagName(tag) : [];
10036 while((te = tret[x++])){
10037 if(_isUnique(te, bag)){
10044 // the common case:
10045 // a descendant selector without a fast path. By now it's got
10046 // to have a tag selector, even if it's just "*" so we query
10047 // by that and filter
10048 filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
10049 retFunc = function(root, arr, bag){
10050 var ret = getArr(0, arr), te, x=0;
10051 // we use getTag() to avoid case sensitivity issues
10052 var tag = query.getTag(),
10053 tret = tag ? root.getElementsByTagName(tag) : [];
10054 while((te = tret[x++])){
10055 if(filterFunc(te, root) && _isUnique(te, bag)){
10063 // the query is scoped in some way. Instead of querying by tag we
10064 // use some other collection to find candidate nodes
10065 var skipFilters = { el: 1 };
10067 skipFilters.tag = 1;
10069 filterFunc = getSimpleFilterFunc(query, skipFilters);
10071 retFunc = _nextSibling(filterFunc);
10072 }else if("~" == oper){
10073 retFunc = _nextSiblings(filterFunc);
10074 }else if(">" == oper){
10075 retFunc = _childElements(filterFunc);
10078 // cache it and return
10079 return _getElementsFuncCache[query.query] = retFunc;
10082 var filterDown = function(root, queryParts){
10084 // this is the guts of the DOM query system. It takes a list of
10085 // parsed query parts and a root and finds children which match
10086 // the selector represented by the parts
10087 var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
10089 for(var i = 0; i < qpl; i++){
10091 qp = queryParts[i];
10092 x = candidates.length - 1;
10094 // if we have more than one root at this level, provide a new
10095 // hash to use for checking group membership but tell the
10096 // system not to post-filter us since we will already have been
10097 // guaranteed to be unique
10101 var gef = getElementsFunc(qp);
10102 for(var j = 0; (te = candidates[j]); j++){
10103 // for every root, get the elements that match the descendant
10104 // selector, adding them to the "ret
" array and filtering them
10105 // via membership in this level's bag. If there are more query
10106 // parts, then this level's return will be used as the next
10107 // level's candidates
10110 if(!ret.length){ break; }
10116 ////////////////////////////////////////////////////////////////////////
10117 // the query runner
10118 ////////////////////////////////////////////////////////////////////////
10120 // these are the primary caches for full-query results. The query
10121 // dispatcher functions are generated then stored here for hash lookup in
10123 var _queryFuncCacheDOM = {},
10124 _queryFuncCacheQSA = {};
10126 // this is the second level of splitting, from full-length queries (e.g.,
10127 // "div
.foo
.bar
") into simple query expressions (e.g., ["div
.foo
",
10129 var getStepQueryFunc = function(query){
10130 var qparts = getQueryParts(trim(query));
10132 // if it's trivial, avoid iteration and zipping costs
10133 if(qparts.length == 1){
10134 // we optimize this case here to prevent dispatch further down the
10135 // chain, potentially slowing things down. We could more elegantly
10136 // handle this in filterDown(), but it's slower for simple things
10137 // that need to be fast (e.g., "#someId
").
10138 var tef = getElementsFunc(qparts[0]);
10139 return function(root){
10140 var r = tef(root, []);
10141 if(r){ r.nozip = true; }
10146 // otherwise, break it up and return a runner that iterates over the parts recursively
10147 return function(root){
10148 return filterDown(root, qparts);
10153 // * we can't trust QSA for anything but document-rooted queries, so
10154 // caching is split into DOM query evaluators and QSA query evaluators
10155 // * caching query results is dirty and leak-prone (or, at a minimum,
10156 // prone to unbounded growth). Other toolkits may go this route, but
10157 // they totally destroy their own ability to manage their memory
10158 // footprint. If we implement it, it should only ever be with a fixed
10159 // total element reference # limit and an LRU-style algorithm since JS
10160 // has no weakref support. Caching compiled query evaluators is also
10161 // potentially problematic, but even on large documents the size of the
10162 // query evaluators is often < 100 function objects per evaluator (and
10163 // LRU can be applied if it's ever shown to be an issue).
10164 // * since IE's QSA support is currently only for HTML documents and even
10165 // then only in IE 8's "standards mode
", we have to detect our dispatch
10166 // route at query time and keep 2 separate caches. Ugg.
10168 // we need to determine if we think we can run a given query via
10169 // querySelectorAll or if we'll need to fall back on DOM queries to get
10170 // there. We need a lot of information about the environment and the query
10171 // to make the determination (e.g. does it support QSA, does the query in
10172 // question work in the native QSA impl, etc.).
10174 // IE QSA queries may incorrectly include comment nodes, so we throw the
10175 // zipping function into "remove
" comments mode instead of the normal "skip
10176 // it" which every other QSA-clued browser enjoys
10177 var noZip
= has("ie") ? "commentStrip" : "nozip";
10179 var qsa
= "querySelectorAll";
10180 var qsaAvail
= !!getDoc()[qsa
];
10182 //Don't bother with n+3 type of matches, IE complains if we modify those.
10183 var infixSpaceRe
= /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
10184 var infixSpaceFunc = function(match
, pre
, ch
, post
){
10185 return ch
? (pre
? pre
+ " " : "") + ch
+ (post
? " " + post
: "") : /*n+3*/ match
;
10188 //Don't apply the infixSpaceRe to attribute value selectors
10189 var attRe
= /([^[]*)([^\]]*])?/g;
10190 var attFunc = function(match
, nonAtt
, att
){
10191 return nonAtt
.replace(infixSpaceRe
, infixSpaceFunc
) + (att
||"");
10193 var getQueryFunc = function(query
, forceDOM
){
10194 //Normalize query. The CSS3 selectors spec allows for omitting spaces around
10195 //infix operators, >, ~ and +
10196 //Do the work here since detection for spaces is used as a simple "not use QSA"
10198 query
= query
.replace(attRe
, attFunc
);
10201 // if we've got a cached variant and we think we can do it, run it!
10202 var qsaCached
= _queryFuncCacheQSA
[query
];
10203 if(qsaCached
&& !forceDOM
){ return qsaCached
; }
10206 // else if we've got a DOM cached variant, assume that we already know
10207 // all we need to and use it
10208 var domCached
= _queryFuncCacheDOM
[query
];
10209 if(domCached
){ return domCached
; }
10212 // today we're caching DOM and QSA branches separately so we
10213 // recalc useQSA every time. If we had a way to tag root+query
10214 // efficiently, we'd be in good shape to do a global cache.
10216 var qcz
= query
.charAt(0);
10217 var nospace
= (-1 == query
.indexOf(" "));
10219 // byId searches are wicked fast compared to QSA, even when filtering
10221 if( (query
.indexOf("#") >= 0) && (nospace
) ){
10226 qsaAvail
&& (!forceDOM
) &&
10227 // as per CSS 3, we can't currently start w/ combinator:
10228 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
10229 (specials
.indexOf(qcz
) == -1) &&
10230 // IE's QSA impl sucks on pseudos
10231 (!has("ie") || (query
.indexOf(":") == -1)) &&
10233 (!(cssCaseBug
&& (query
.indexOf(".") >= 0))) &&
10236 // need to tighten up browser rules on ":contains" and "|=" to
10237 // figure out which aren't good
10238 // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
10239 // elements, even though according to spec, selected options should
10240 // match :checked. So go nonQSA for it:
10241 // http://bugs.dojotoolkit.org/ticket/5179
10242 (query
.indexOf(":contains") == -1) && (query
.indexOf(":checked") == -1) &&
10243 (query
.indexOf("|=") == -1) // some browsers don't grok it
10247 // if we've got a descendant query (e.g., "> .thinger" instead of
10248 // just ".thinger") in a QSA-able doc, but are passed a child as a
10249 // root, it should be possible to give the item a synthetic ID and
10250 // trivially rewrite the query to the form "#synid > .thinger" to
10251 // use the QSA branch
10255 var tq
= (specials
.indexOf(query
.charAt(query
.length
-1)) >= 0) ?
10256 (query
+ " *") : query
;
10257 return _queryFuncCacheQSA
[query
] = function(root
){
10259 // the QSA system contains an egregious spec bug which
10260 // limits us, effectively, to only running QSA queries over
10261 // entire documents. See:
10262 // http://ejohn.org/blog/thoughts-on-queryselectorall/
10263 // despite this, we can also handle QSA runs on simple
10264 // selectors, but we don't want detection to be expensive
10265 // so we're just checking for the presence of a space char
10266 // right now. Not elegant, but it's cheaper than running
10267 // the query parser when we might not need to
10268 if(!((9 == root
.nodeType
) || nospace
)){ throw ""; }
10269 var r
= root
[qsa
](tq
);
10270 // skip expensive duplication checks and just wrap in a NodeList
10274 // else run the DOM branch on this query, ensuring that we
10275 // default that way in the future
10276 return getQueryFunc(query
, true)(root
);
10281 var parts
= query
.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
10282 return _queryFuncCacheDOM
[query
] = ((parts
.length
< 2) ?
10283 // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
10284 getStepQueryFunc(query
) :
10285 // if it *is* a complex query, break it up into its
10286 // constituent parts and return a dispatcher that will
10287 // merge the parts when run
10289 var pindex
= 0, // avoid array alloc for every invocation
10292 while((tp
= parts
[pindex
++])){
10293 ret
= ret
.concat(getStepQueryFunc(tp
)(root
));
10304 // this function is Moo inspired, but our own impl to deal correctly
10306 var _nodeUID
= has("ie") ? function(node
){
10308 // XML docs don't have uniqueID on their nodes
10309 return (node
.getAttribute("_uid") || node
.setAttribute("_uid", ++_zipIdx
) || _zipIdx
);
10312 return node
.uniqueID
;
10316 return (node
._uid
|| (node
._uid
= ++_zipIdx
));
10319 // determine if a node in is unique in a "bag". In this case we don't want
10320 // to flatten a list of unique items, but rather just tell if the item in
10321 // question is already in the bag. Normally we'd just use hash lookup to do
10322 // this for us but IE's DOM is busted so we can't really count on that. On
10323 // the upside, it gives us a built in unique ID function.
10324 var _isUnique = function(node
, bag
){
10325 if(!bag
){ return 1; }
10326 var id
= _nodeUID(node
);
10327 if(!bag
[id
]){ return bag
[id
] = 1; }
10331 // attempt to efficiently determine if an item in a list is a dupe,
10332 // returning a list of "uniques", hopefully in document order
10333 var _zipIdxName
= "_zipIdx";
10334 var _zip = function(arr
){
10335 if(arr
&& arr
.nozip
){
10339 if(!arr
|| !arr
.length
){ return ret
; }
10343 if(arr
.length
< 2){ return ret
; }
10347 // we have to fork here for IE and XML docs because we can't set
10348 // expandos on their nodes (apparently). *sigh*
10350 if(has("ie") && caseSensitive
){
10351 var szidx
= _zipIdx
+"";
10352 arr
[0].setAttribute(_zipIdxName
, szidx
);
10353 for(x
= 1; te
= arr
[x
]; x
++){
10354 if(arr
[x
].getAttribute(_zipIdxName
) != szidx
){
10357 te
.setAttribute(_zipIdxName
, szidx
);
10359 }else if(has("ie") && arr
.commentStrip
){
10361 for(x
= 1; te
= arr
[x
]; x
++){
10362 if(_isElement(te
)){
10366 }catch(e
){ /* squelch */ }
10368 if(arr
[0]){ arr
[0][_zipIdxName
] = _zipIdx
; }
10369 for(x
= 1; te
= arr
[x
]; x
++){
10370 if(arr
[x
][_zipIdxName
] != _zipIdx
){
10373 te
[_zipIdxName
] = _zipIdx
;
10379 // the main executor
10380 var query = function(/*String*/ query
, /*String|DOMNode?*/ root
){
10382 // Returns nodes which match the given CSS3 selector, searching the
10383 // entire document by default but optionally taking a node to scope
10384 // the search by. Returns an array.
10386 // dojo.query() is the swiss army knife of DOM node manipulation in
10387 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
10388 // "$" function, dojo.query provides robust, high-performance
10389 // CSS-based node selector support with the option of scoping searches
10390 // to a particular sub-tree of a document.
10392 // Supported Selectors:
10393 // --------------------
10395 // acme supports a rich set of CSS3 selectors, including:
10397 // - class selectors (e.g., `.foo`)
10398 // - node type selectors like `span`
10399 // - ` ` descendant selectors
10400 // - `>` child element selectors
10401 // - `#foo` style ID selectors
10402 // - `*` universal selector
10403 // - `~`, the preceded-by sibling selector
10404 // - `+`, the immediately preceded-by sibling selector
10405 // - attribute queries:
10406 // - `[foo]` attribute presence selector
10407 // - `[foo='bar']` attribute value exact match
10408 // - `[foo~='bar']` attribute value list item match
10409 // - `[foo^='bar']` attribute start match
10410 // - `[foo$='bar']` attribute end match
10411 // - `[foo*='bar']` attribute substring match
10412 // - `:first-child`, `:last-child`, and `:only-child` positional selectors
10413 // - `:empty` content emtpy selector
10414 // - `:checked` pseudo selector
10415 // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
10416 // - `:nth-child(even)`, `:nth-child(odd)` positional selectors
10417 // - `:not(...)` negation pseudo selectors
10419 // Any legal combination of these selectors will work with
10420 // `dojo.query()`, including compound selectors ("," delimited).
10421 // Very complex and useful searches can be constructed with this
10422 // palette of selectors and when combined with functions for
10423 // manipulation presented by dojo/NodeList, many types of DOM
10424 // manipulation operations become very straightforward.
10426 // Unsupported Selectors:
10427 // ----------------------
10429 // While dojo.query handles many CSS3 selectors, some fall outside of
10430 // what's reasonable for a programmatic node querying engine to
10431 // handle. Currently unsupported selectors include:
10433 // - namespace-differentiated selectors of any form
10434 // - all `::` pseduo-element selectors
10435 // - certain pseudo-selectors which don't get a lot of day-to-day use:
10436 // - `:root`, `:lang()`, `:target`, `:focus`
10437 // - all visual and state selectors:
10438 // - `:root`, `:active`, `:hover`, `:visited`, `:link`,
10439 // `:enabled`, `:disabled`
10440 // - `:*-of-type` pseudo selectors
10442 // dojo.query and XML Documents:
10443 // -----------------------------
10445 // `dojo.query` (as of dojo 1.2) supports searching XML documents
10446 // in a case-sensitive manner. If an HTML document is served with
10447 // a doctype that forces case-sensitivity (e.g., XHTML 1.1
10448 // Strict), dojo.query() will detect this and "do the right
10449 // thing". Case sensitivity is dependent upon the document being
10450 // searched and not the query used. It is therefore possible to
10451 // use case-sensitive queries on strict sub-documents (iframes,
10452 // etc.) or XML documents while still assuming case-insensitivity
10453 // for a host/root document.
10455 // Non-selector Queries:
10456 // ---------------------
10458 // If something other than a String is passed for the query,
10459 // `dojo.query` will return a new `dojo/NodeList` instance
10460 // constructed from that parameter alone and all further
10461 // processing will stop. This means that if you have a reference
10462 // to a node or NodeList, you can quickly construct a new NodeList
10463 // from the original by calling `dojo.query(node)` or
10464 // `dojo.query(list)`.
10467 // The CSS3 expression to match against. For details on the syntax of
10468 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
10470 // A DOMNode (or node id) to scope the search from. Optional.
10473 // search the entire document for elements with the class "foo":
10474 // | dojo.query(".foo");
10475 // these elements will match:
10476 // | <span class="foo"></span>
10477 // | <span class="foo bar"></span>
10478 // | <p class="thud foo"></p>
10480 // search the entire document for elements with the classes "foo" *and* "bar":
10481 // | dojo.query(".foo.bar");
10482 // these elements will match:
10483 // | <span class="foo bar"></span>
10484 // while these will not:
10485 // | <span class="foo"></span>
10486 // | <p class="thud foo"></p>
10488 // find `<span>` elements which are descendants of paragraphs and
10489 // which have a "highlighted" class:
10490 // | dojo.query("p span.highlighted");
10491 // the innermost span in this fragment matches:
10492 // | <p class="foo">
10494 // | <span class="highlighted foo bar">...</span>
10498 // set an "odd" class on all odd table rows inside of the table
10499 // `#tabular_data`, using the `>` (direct child) selector to avoid
10500 // affecting any nested tables:
10501 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
10503 // remove all elements with the class "error" from the document
10504 // and store them in a list:
10505 // | var errors = dojo.query(".error").orphan();
10507 // add an onclick handler to every submit button in the document
10508 // which causes the form to be sent via Ajax instead:
10509 // | dojo.query("input[type='submit']").onclick(function(e){
10510 // | dojo.stopEvent(e); // prevent sending the form
10511 // | var btn = e.target;
10512 // | dojo.xhrPost({
10513 // | form: btn.form,
10514 // | load: function(data){
10515 // | // replace the form with the response
10516 // | var div = dojo.doc.createElement("div");
10517 // | dojo.place(div, btn.form, "after");
10518 // | div.innerHTML = data;
10519 // | dojo.style(btn.form, "display", "none");
10524 root
= root
|| getDoc();
10526 // throw the big case sensitivity switch
10527 var od
= root
.ownerDocument
|| root
; // root is either Document or a node inside the document
10528 caseSensitive
= (od
.createElement("div").tagName
=== "div");
10531 // adding "true" as the 2nd argument to getQueryFunc is useful for
10532 // testing the DOM branch without worrying about the
10533 // behavior/performance of the QSA branch.
10534 var r
= getQueryFunc(query
)(root
);
10537 // need to investigate this branch WRT #8074 and #8075
10541 return _zip(r
); // dojo/NodeList
10543 query
.filter = function(/*Node[]*/ nodeList
, /*String*/ filter
, /*String|DOMNode?*/ root
){
10545 // function for filtering a NodeList based on a selector, optimized for simple selectors
10546 var tmpNodeList
= [],
10547 parts
= getQueryParts(filter
),
10549 (parts
.length
== 1 && !/[^\w#\.]/.test(filter
)) ?
10550 getSimpleFilterFunc(parts
[0]) :
10552 return array
.indexOf(query(filter
, dom
.byId(root
)), node
) != -1;
10554 for(var x
= 0, te
; te
= nodeList
[x
]; x
++){
10555 if(filterFunc(te
)){ tmpNodeList
.push(te
); }
10557 return tmpNodeList
;
10563 'dojo/dnd/autoscroll':function(){
10564 define("dojo/dnd/autoscroll", ["../_base/lang", "../sniff", "../_base/window", "../dom-geometry", "../dom-style", "../window"],
10565 function(lang
, has
, win
, domGeom
, domStyle
, winUtils
){
10568 // dojo/dnd/autoscroll
10572 // Used by dojo/dnd/Manager to scroll document or internal node when the user
10573 // drags near the edge of the viewport or a scrollable node
10575 lang
.setObject("dojo.dnd.autoscroll", exports
);
10577 exports
.getViewport
= winUtils
.getBox
;
10579 exports
.V_TRIGGER_AUTOSCROLL
= 32;
10580 exports
.H_TRIGGER_AUTOSCROLL
= 32;
10582 exports
.V_AUTOSCROLL_VALUE
= 16;
10583 exports
.H_AUTOSCROLL_VALUE
= 16;
10585 // These are set by autoScrollStart().
10586 // Set to default values in case autoScrollStart() isn't called. (back-compat, remove for 2.0)
10589 maxScrollTop
= Infinity
,
10590 maxScrollLeft
= Infinity
;
10592 exports
.autoScrollStart = function(d
){
10594 // Called at the start of a drag.
10596 // The document of the node being dragged.
10599 viewport
= winUtils
.getBox(doc
);
10601 // Save height/width of document at start of drag, before it gets distorted by a user dragging an avatar past
10602 // the document's edge
10603 var html
= win
.body(doc
).parentNode
;
10604 maxScrollTop
= Math
.max(html
.scrollHeight
- viewport
.h
, 0);
10605 maxScrollLeft
= Math
.max(html
.scrollWidth
- viewport
.w
, 0); // usually 0
10608 exports
.autoScroll = function(e
){
10610 // a handler for mousemove and touchmove events, which scrolls the window, if
10613 // mousemove/touchmove event
10615 // FIXME: needs more docs!
10616 var v
= viewport
|| winUtils
.getBox(doc
), // getBox() call for back-compat, in case autoScrollStart() wasn't called
10617 html
= win
.body(doc
).parentNode
,
10619 if(e
.clientX
< exports
.H_TRIGGER_AUTOSCROLL
){
10620 dx
= -exports
.H_AUTOSCROLL_VALUE
;
10621 }else if(e
.clientX
> v
.w
- exports
.H_TRIGGER_AUTOSCROLL
){
10622 dx
= Math
.min(exports
.H_AUTOSCROLL_VALUE
, maxScrollLeft
- html
.scrollLeft
); // don't scroll past edge of doc
10624 if(e
.clientY
< exports
.V_TRIGGER_AUTOSCROLL
){
10625 dy
= -exports
.V_AUTOSCROLL_VALUE
;
10626 }else if(e
.clientY
> v
.h
- exports
.V_TRIGGER_AUTOSCROLL
){
10627 dy
= Math
.min(exports
.V_AUTOSCROLL_VALUE
, maxScrollTop
- html
.scrollTop
); // don't scroll past edge of doc
10629 window
.scrollBy(dx
, dy
);
10632 exports
._validNodes
= {"div": 1, "p": 1, "td": 1};
10633 exports
._validOverflow
= {"auto": 1, "scroll": 1};
10635 exports
.autoScrollNodes = function(e
){
10637 // a handler for mousemove and touchmove events, which scrolls the first available
10638 // Dom element, it falls back to exports.autoScroll()
10640 // mousemove/touchmove event
10642 // FIXME: needs more docs!
10644 var b
, t
, w
, h
, rx
, ry
, dx
= 0, dy
= 0, oldLeft
, oldTop
;
10646 for(var n
= e
.target
; n
;){
10647 if(n
.nodeType
== 1 && (n
.tagName
.toLowerCase() in exports
._validNodes
)){
10648 var s
= domStyle
.getComputedStyle(n
),
10649 overflow
= (s
.overflow
.toLowerCase() in exports
._validOverflow
),
10650 overflowX
= (s
.overflowX
.toLowerCase() in exports
._validOverflow
),
10651 overflowY
= (s
.overflowY
.toLowerCase() in exports
._validOverflow
);
10652 if(overflow
|| overflowX
|| overflowY
){
10653 b
= domGeom
.getContentBox(n
, s
);
10654 t
= domGeom
.position(n
, true);
10657 if(overflow
|| overflowX
){
10658 w
= Math
.min(exports
.H_TRIGGER_AUTOSCROLL
, b
.w
/ 2);
10659 rx
= e
.pageX
- t
.x
;
10660 if(has("webkit") || has("opera")){
10661 // FIXME: this code should not be here, it should be taken into account
10662 // either by the event fixing code, or the domGeom.position()
10663 // FIXME: this code doesn't work on Opera 9.5 Beta
10664 rx
+= win
.body().scrollLeft
;
10667 if(rx
> 0 && rx
< b
.w
){
10670 }else if(rx
> b
.w
- w
){
10673 oldLeft
= n
.scrollLeft
;
10674 n
.scrollLeft
= n
.scrollLeft
+ dx
;
10678 if(overflow
|| overflowY
){
10679 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
10680 h
= Math
.min(exports
.V_TRIGGER_AUTOSCROLL
, b
.h
/ 2);
10681 ry
= e
.pageY
- t
.y
;
10682 if(has("webkit") || has("opera")){
10683 // FIXME: this code should not be here, it should be taken into account
10684 // either by the event fixing code, or the domGeom.position()
10685 // FIXME: this code doesn't work on Opera 9.5 Beta
10686 ry
+= win
.body().scrollTop
;
10689 if(ry
> 0 && ry
< b
.h
){
10692 }else if(ry
> b
.h
- h
){
10695 oldTop
= n
.scrollTop
;
10696 n
.scrollTop
= n
.scrollTop
+ dy
;
10699 if(dx
|| dy
){ return; }
10707 exports
.autoScroll(e
);
10715 'dijit/form/_RadioButtonMixin':function(){
10716 define("dijit/form/_RadioButtonMixin", [
10717 "dojo/_base/array", // array.forEach
10718 "dojo/_base/declare", // declare
10719 "dojo/dom-attr", // domAttr.set
10720 "dojo/_base/event", // event.stop
10721 "dojo/_base/lang", // lang.hitch
10722 "dojo/query", // query
10723 "../registry" // registry.getEnclosingWidget
10724 ], function(array
, declare
, domAttr
, event
, lang
, query
, registry
){
10727 // dijit/form/_RadioButtonMixin
10729 return declare("dijit.form._RadioButtonMixin", null, {
10731 // Mixin to provide widget functionality for an HTML radio button
10733 // type: [private] String
10734 // type attribute on `<input>` node.
10735 // Users should not change this value.
10738 _getRelatedWidgets: function(){
10739 // Private function needed to help iterate over all radio buttons in a group.
10741 query("input[type=radio]", this.focusNode
.form
|| this.ownerDocument
).forEach( // can't use name= since query doesn't support [] in the name
10742 lang
.hitch(this, function(inputNode
){
10743 if(inputNode
.name
== this.name
&& inputNode
.form
== this.focusNode
.form
){
10744 var widget
= registry
.getEnclosingWidget(inputNode
);
10754 _setCheckedAttr: function(/*Boolean*/ value
){
10755 // If I am being checked then have to deselect currently checked radio button
10756 this.inherited(arguments
);
10757 if(!this._created
){ return; }
10759 array
.forEach(this._getRelatedWidgets(), lang
.hitch(this, function(widget
){
10760 if(widget
!= this && widget
.checked
){
10761 widget
.set('checked', false);
10767 _getSubmitValue: function(/*String*/ value
){
10768 return value
=== null ? "on" : value
;
10771 _onClick: function(/*Event*/ e
){
10772 if(this.checked
|| this.disabled
){ // nothing to do
10776 if(this.readOnly
){ // ignored by some browsers so we have to resync the DOM elements with widget values
10778 array
.forEach(this._getRelatedWidgets(), lang
.hitch(this, function(widget
){
10779 domAttr
.set(this.focusNode
|| this.domNode
, 'checked', widget
.checked
);
10783 return this.inherited(arguments
);
10789 'dojo/data/ItemFileWriteStore':function(){
10790 define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/kernel",
10791 "./ItemFileReadStore", "../date/stamp"
10792 ], function(lang
, declare
, arrayUtil
, jsonUtil
, kernel
, ItemFileReadStore
, dateStamp
){
10795 // dojo/data/ItemFileWriteStore
10797 return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore
, {
10801 constructor: function(/* object */ keywordParameters
){
10802 // keywordParameters:
10803 // The structure of the typeMap object is as follows:
10805 // | type0: function || object,
10806 // | type1: function || object,
10808 // | typeN: function || object
10810 // Where if it is a function, it is assumed to be an object constructor that takes the
10811 // value of _value as the initialization parameters. It is serialized assuming object.toString()
10812 // serialization. If it is an object, then it is assumed
10813 // to be an object of general form:
10815 // | type: function, //constructor.
10816 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
10817 // | serialize: function(object) //The function that converts the object back into the proper file format form.
10820 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
10821 this._features
['dojo.data.api.Write'] = true;
10822 this._features
['dojo.data.api.Notification'] = true;
10824 // For keeping track of changes so that we can implement isDirty and revert
10831 if(!this._datatypeMap
['Date'].serialize
){
10832 this._datatypeMap
['Date'].serialize = function(obj
){
10833 return dateStamp
.toISOString(obj
, {zulu
:true});
10836 //Disable only if explicitly set to false.
10837 if(keywordParameters
&& (keywordParameters
.referenceIntegrity
=== false)){
10838 this.referenceIntegrity
= false;
10841 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
10842 this._saveInProgress
= false;
10845 referenceIntegrity
: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
10847 _assert: function(/* boolean */ condition
){
10849 throw new Error("assertion failed in ItemFileWriteStore");
10853 _getIdentifierAttribute: function(){
10854 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
10855 return this.getFeatures()['dojo.data.api.Identity'];
10859 /* dojo/data/api/Write */
10861 newItem: function(/* Object? */ keywordArgs
, /* Object? */ parentInfo
){
10863 // See dojo/data/api/Write.newItem()
10865 this._assert(!this._saveInProgress
);
10867 if(!this._loadFinished
){
10868 // We need to do this here so that we'll be able to find out what
10869 // identifierAttribute was specified in the data file.
10873 if(typeof keywordArgs
!= "object" && typeof keywordArgs
!= "undefined"){
10874 throw new Error("newItem() was passed something other than an object");
10876 var newIdentity
= null;
10877 var identifierAttribute
= this._getIdentifierAttribute();
10878 if(identifierAttribute
=== Number
){
10879 newIdentity
= this._arrayOfAllItems
.length
;
10881 newIdentity
= keywordArgs
[identifierAttribute
];
10882 if(typeof newIdentity
=== "undefined"){
10883 throw new Error("newItem() was not passed an identity for the new item");
10885 if(lang
.isArray(newIdentity
)){
10886 throw new Error("newItem() was not passed an single-valued identity");
10890 // make sure this identity is not already in use by another item, if identifiers were
10891 // defined in the file. Otherwise it would be the item count,
10892 // which should always be unique in this case.
10893 if(this._itemsByIdentity
){
10894 this._assert(typeof this._itemsByIdentity
[newIdentity
] === "undefined");
10896 this._assert(typeof this._pending
._newItems
[newIdentity
] === "undefined");
10897 this._assert(typeof this._pending
._deletedItems
[newIdentity
] === "undefined");
10900 newItem
[this._storeRefPropName
] = this;
10901 newItem
[this._itemNumPropName
] = this._arrayOfAllItems
.length
;
10902 if(this._itemsByIdentity
){
10903 this._itemsByIdentity
[newIdentity
] = newItem
;
10904 //We have to set the identifier now, otherwise we can't look it
10905 //up at calls to setValueorValues in parentInfo handling.
10906 newItem
[identifierAttribute
] = [newIdentity
];
10908 this._arrayOfAllItems
.push(newItem
);
10910 //We need to construct some data for the onNew call too...
10913 // Now we need to check to see where we want to assign this thingm if any.
10914 if(parentInfo
&& parentInfo
.parent
&& parentInfo
.attribute
){
10916 item
: parentInfo
.parent
,
10917 attribute
: parentInfo
.attribute
,
10918 oldValue
: undefined
10921 //See if it is multi-valued or not and handle appropriately
10922 //Generally, all attributes are multi-valued for this store
10923 //So, we only need to append if there are already values present.
10924 var values
= this.getValues(parentInfo
.parent
, parentInfo
.attribute
);
10925 if(values
&& values
.length
> 0){
10926 var tempValues
= values
.slice(0, values
.length
);
10927 if(values
.length
=== 1){
10928 pInfo
.oldValue
= values
[0];
10930 pInfo
.oldValue
= values
.slice(0, values
.length
);
10932 tempValues
.push(newItem
);
10933 this._setValueOrValues(parentInfo
.parent
, parentInfo
.attribute
, tempValues
, false);
10934 pInfo
.newValue
= this.getValues(parentInfo
.parent
, parentInfo
.attribute
);
10936 this._setValueOrValues(parentInfo
.parent
, parentInfo
.attribute
, newItem
, false);
10937 pInfo
.newValue
= newItem
;
10940 //Toplevel item, add to both top list as well as all list.
10941 newItem
[this._rootItemPropName
]=true;
10942 this._arrayOfTopLevelItems
.push(newItem
);
10945 this._pending
._newItems
[newIdentity
] = newItem
;
10947 //Clone over the properties to the new item
10948 for(var key
in keywordArgs
){
10949 if(key
=== this._storeRefPropName
|| key
=== this._itemNumPropName
){
10950 // Bummer, the user is trying to do something like
10951 // newItem({_S:"foo"}). Unfortunately, our superclass,
10952 // ItemFileReadStore, is already using _S in each of our items
10953 // to hold private info. To avoid a naming collision, we
10954 // need to move all our private info to some other property
10955 // of all the items/objects. So, we need to iterate over all
10956 // the items and do something like:
10957 // item.__S = item._S;
10958 // item._S = undefined;
10959 // But first we have to make sure the new "__S" variable is
10960 // not in use, which means we have to iterate over all the
10961 // items checking for that.
10962 throw new Error("encountered bug in ItemFileWriteStore.newItem");
10964 var value
= keywordArgs
[key
];
10965 if(!lang
.isArray(value
)){
10968 newItem
[key
] = value
;
10969 if(this.referenceIntegrity
){
10970 for(var i
= 0; i
< value
.length
; i
++){
10971 var val
= value
[i
];
10972 if(this.isItem(val
)){
10973 this._addReferenceToMap(val
, newItem
, key
);
10978 this.onNew(newItem
, pInfo
); // dojo/data/api/Notification call
10979 return newItem
; // item
10982 _removeArrayElement: function(/* Array */ array
, /* anything */ element
){
10983 var index
= arrayUtil
.indexOf(array
, element
);
10985 array
.splice(index
, 1);
10991 deleteItem: function(/* dojo/data/api/Item */ item){
10993 // See dojo/data/api/Write.deleteItem()
10994 this._assert(!this._saveInProgress);
10995 this._assertIsItem(item);
10997 // Remove this item from the _arrayOfAllItems, but leave a null value in place
10998 // of the item, so as not to change the length of the array, so that in newItem()
10999 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
11000 var indexInArrayOfAllItems = item[this._itemNumPropName];
11001 var identity = this.getIdentity(item);
11003 //If we have reference integrity on, we need to do reference cleanup for the deleted item
11004 if(this.referenceIntegrity){
11005 //First scan all the attributes of this items for references and clean them up in the map
11006 //As this item is going away, no need to track its references anymore.
11008 //Get the attributes list before we generate the backup so it
11009 //doesn't pollute the attributes list.
11010 var attributes = this.getAttributes(item);
11012 //Backup the map, we'll have to restore it potentially, in a revert.
11013 if(item[this._reverseRefMap]){
11014 item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
11017 //TODO: This causes a reversion problem. This list won't be restored on revert since it is
11018 //attached to the 'value'. item, not ours. Need to back tese up somehow too.
11019 //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
11020 //later. Or just record them and call _addReferenceToMap on them in revert.
11021 arrayUtil.forEach(attributes, function(attribute){
11022 arrayUtil.forEach(this.getValues(item, attribute), function(value){
11023 if(this.isItem(value)){
11024 //We have to back up all the references we had to others so they can be restored on a revert.
11025 if(!item["backupRefs_" + this._reverseRefMap]){
11026 item["backupRefs_" + this._reverseRefMap] = [];
11028 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
11029 this._removeReferenceFromMap(value, item, attribute);
11034 //Next, see if we have references to this item, if we do, we have to clean them up too.
11035 var references = item[this._reverseRefMap];
11037 //Look through all the items noted as references to clean them up.
11038 for(var itemId in references){
11039 var containingItem = null;
11040 if(this._itemsByIdentity){
11041 containingItem = this._itemsByIdentity[itemId];
11043 containingItem = this._arrayOfAllItems[itemId];
11045 //We have a reference to a containing item, now we have to process the
11046 //attributes and clear all references to the item being deleted.
11047 if(containingItem){
11048 for(var attribute in references[itemId]){
11049 var oldValues = this.getValues(containingItem, attribute) || [];
11050 var newValues = arrayUtil.filter(oldValues, function(possibleItem){
11051 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
11053 //Remove the note of the reference to the item and set the values on the modified attribute.
11054 this._removeReferenceFromMap(item, containingItem, attribute);
11055 if(newValues.length < oldValues.length){
11056 this._setValueOrValues(containingItem, attribute, newValues, true);
11064 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
11066 item[this._storeRefPropName] = null;
11067 if(this._itemsByIdentity){
11068 delete this._itemsByIdentity[identity];
11070 this._pending._deletedItems[identity] = item;
11072 //Remove from the toplevel items, if necessary...
11073 if(item[this._rootItemPropName]){
11074 this._removeArrayElement(this._arrayOfTopLevelItems, item);
11076 this.onDelete(item); // dojo/data/api/Notification call
11080 setValue: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* almost anything */ value
){
11082 // See dojo/data/api/Write.set()
11083 return this._setValueOrValues(item
, attribute
, value
, true); // boolean
11086 setValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* array */ values
){
11088 // See dojo/data/api/Write.setValues()
11089 return this._setValueOrValues(item
, attribute
, values
, true); // boolean
11092 unsetAttribute: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
){
11094 // See dojo/data/api/Write.unsetAttribute()
11095 return this._setValueOrValues(item
, attribute
, [], true);
11098 _setValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
, /* anything */ newValueOrValues
, /*boolean?*/ callOnSet
){
11099 this._assert(!this._saveInProgress
);
11101 // Check for valid arguments
11102 this._assertIsItem(item
);
11103 this._assert(lang
.isString(attribute
));
11104 this._assert(typeof newValueOrValues
!== "undefined");
11106 // Make sure the user isn't trying to change the item's identity
11107 var identifierAttribute
= this._getIdentifierAttribute();
11108 if(attribute
== identifierAttribute
){
11109 throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
11112 // To implement the Notification API, we need to make a note of what
11113 // the old attribute value was, so that we can pass that info when
11114 // we call the onSet method.
11115 var oldValueOrValues
= this._getValueOrValues(item
, attribute
);
11117 var identity
= this.getIdentity(item
);
11118 if(!this._pending
._modifiedItems
[identity
]){
11119 // Before we actually change the item, we make a copy of it to
11120 // record the original state, so that we'll be able to revert if
11121 // the revert method gets called. If the item has already been
11122 // modified then there's no need to do this now, since we already
11123 // have a record of the original state.
11124 var copyOfItemState
= {};
11125 for(var key
in item
){
11126 if((key
=== this._storeRefPropName
) || (key
=== this._itemNumPropName
) || (key
=== this._rootItemPropName
)){
11127 copyOfItemState
[key
] = item
[key
];
11128 }else if(key
=== this._reverseRefMap
){
11129 copyOfItemState
[key
] = lang
.clone(item
[key
]);
11131 copyOfItemState
[key
] = item
[key
].slice(0, item
[key
].length
);
11134 // Now mark the item as dirty, and save the copy of the original state
11135 this._pending
._modifiedItems
[identity
] = copyOfItemState
;
11138 // Okay, now we can actually change this attribute on the item
11139 var success
= false;
11141 if(lang
.isArray(newValueOrValues
) && newValueOrValues
.length
=== 0){
11143 // If we were passed an empty array as the value, that counts
11144 // as "unsetting" the attribute, so we need to remove this
11145 // attribute from the item.
11146 success
= delete item
[attribute
];
11147 newValueOrValues
= undefined; // used in the onSet Notification call below
11149 if(this.referenceIntegrity
&& oldValueOrValues
){
11150 var oldValues
= oldValueOrValues
;
11151 if(!lang
.isArray(oldValues
)){
11152 oldValues
= [oldValues
];
11154 for(var i
= 0; i
< oldValues
.length
; i
++){
11155 var value
= oldValues
[i
];
11156 if(this.isItem(value
)){
11157 this._removeReferenceFromMap(value
, item
, attribute
);
11163 if(lang
.isArray(newValueOrValues
)){
11164 // Unfortunately, it's not safe to just do this:
11165 // newValueArray = newValueOrValues;
11166 // Instead, we need to copy the array, which slice() does very nicely.
11167 // This is so that our internal data structure won't
11168 // get corrupted if the user mucks with the values array *after*
11169 // calling setValues().
11170 newValueArray
= newValueOrValues
.slice(0, newValueOrValues
.length
);
11172 newValueArray
= [newValueOrValues
];
11175 //We need to handle reference integrity if this is on.
11176 //In the case of set, we need to see if references were added or removed
11177 //and update the reference tracking map accordingly.
11178 if(this.referenceIntegrity
){
11179 if(oldValueOrValues
){
11180 var oldValues
= oldValueOrValues
;
11181 if(!lang
.isArray(oldValues
)){
11182 oldValues
= [oldValues
];
11184 //Use an associative map to determine what was added/removed from the list.
11185 //Should be O(n) performant. First look at all the old values and make a list of them
11186 //Then for any item not in the old list, we add it. If it was already present, we remove it.
11187 //Then we pass over the map and any references left it it need to be removed (IE, no match in
11188 //the new values list).
11190 arrayUtil
.forEach(oldValues
, function(possibleItem
){
11191 if(this.isItem(possibleItem
)){
11192 var id
= this.getIdentity(possibleItem
);
11193 map
[id
.toString()] = true;
11196 arrayUtil
.forEach(newValueArray
, function(possibleItem
){
11197 if(this.isItem(possibleItem
)){
11198 var id
= this.getIdentity(possibleItem
);
11199 if(map
[id
.toString()]){
11200 delete map
[id
.toString()];
11202 this._addReferenceToMap(possibleItem
, item
, attribute
);
11206 for(var rId
in map
){
11208 if(this._itemsByIdentity
){
11209 removedItem
= this._itemsByIdentity
[rId
];
11211 removedItem
= this._arrayOfAllItems
[rId
];
11213 this._removeReferenceFromMap(removedItem
, item
, attribute
);
11216 //Everything is new (no old values) so we have to just
11217 //insert all the references, if any.
11218 for(var i
= 0; i
< newValueArray
.length
; i
++){
11219 var value
= newValueArray
[i
];
11220 if(this.isItem(value
)){
11221 this._addReferenceToMap(value
, item
, attribute
);
11226 item
[attribute
] = newValueArray
;
11230 // Now we make the dojo/data/api/Notification call
11232 this.onSet(item
, attribute
, oldValueOrValues
, newValueOrValues
);
11234 return success
; // boolean
11237 _addReferenceToMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute
){
11239 // Method to add an reference map entry for an item and attribute.
11241 // Method to add an reference map entry for an item and attribute.
11243 // The item that is referenced.
11245 // The item that holds the new reference to refItem.
11247 // The attribute on parentItem that contains the new reference.
11249 var parentId
= this.getIdentity(parentItem
);
11250 var references
= refItem
[this._reverseRefMap
];
11253 references
= refItem
[this._reverseRefMap
] = {};
11255 var itemRef
= references
[parentId
];
11257 itemRef
= references
[parentId
] = {};
11259 itemRef
[attribute
] = true;
11262 _removeReferenceFromMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute
){
11264 // Method to remove an reference map entry for an item and attribute.
11266 // Method to remove an reference map entry for an item and attribute. This will
11267 // also perform cleanup on the map such that if there are no more references at all to
11268 // the item, its reference object and entry are removed.
11270 // The item that is referenced.
11272 // The item holding a reference to refItem.
11274 // The attribute on parentItem that contains the reference.
11275 var identity
= this.getIdentity(parentItem
);
11276 var references
= refItem
[this._reverseRefMap
];
11279 for(itemId
in references
){
11280 if(itemId
== identity
){
11281 delete references
[itemId
][attribute
];
11282 if(this._isEmpty(references
[itemId
])){
11283 delete references
[itemId
];
11287 if(this._isEmpty(references
)){
11288 delete refItem
[this._reverseRefMap
];
11293 _dumpReferenceMap: function(){
11295 // Function to dump the reverse reference map of all items in the store for debug purposes.
11297 // Function to dump the reverse reference map of all items in the store for debug purposes.
11299 for(i
= 0; i
< this._arrayOfAllItems
.length
; i
++){
11300 var item
= this._arrayOfAllItems
[i
];
11301 if(item
&& item
[this._reverseRefMap
]){
11302 console
.log("Item: [" + this.getIdentity(item
) + "] is referenced by: " + jsonUtil
.toJson(item
[this._reverseRefMap
]));
11307 _getValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute
){
11308 var valueOrValues
= undefined;
11309 if(this.hasAttribute(item
, attribute
)){
11310 var valueArray
= this.getValues(item
, attribute
);
11311 if(valueArray
.length
== 1){
11312 valueOrValues
= valueArray
[0];
11314 valueOrValues
= valueArray
;
11317 return valueOrValues
;
11320 _flatten: function(/* anything */ value
){
11321 if(this.isItem(value
)){
11322 // Given an item, return an serializable object that provides a
11323 // reference to the item.
11324 // For example, given kermit:
11325 // var kermit = store.newItem({id:2, name:"Kermit"});
11326 // we want to return
11328 return {_reference
: this.getIdentity(value
)};
11330 if(typeof value
=== "object"){
11331 for(var type
in this._datatypeMap
){
11332 var typeMap
= this._datatypeMap
[type
];
11333 if(lang
.isObject(typeMap
) && !lang
.isFunction(typeMap
)){
11334 if(value
instanceof typeMap
.type
){
11335 if(!typeMap
.serialize
){
11336 throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type
+ "]");
11338 return {_type
: type
, _value
: typeMap
.serialize(value
)};
11340 }else if(value
instanceof typeMap
){
11341 //SImple mapping, therefore, return as a toString serialization.
11342 return {_type
: type
, _value
: value
.toString()};
11350 _getNewFileContentString: function(){
11352 // Generate a string that can be saved to a file.
11353 // The result should look similar to:
11354 // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
11355 var serializableStructure
= {};
11357 var identifierAttribute
= this._getIdentifierAttribute();
11358 if(identifierAttribute
!== Number
){
11359 serializableStructure
.identifier
= identifierAttribute
;
11361 if(this._labelAttr
){
11362 serializableStructure
.label
= this._labelAttr
;
11364 serializableStructure
.items
= [];
11365 for(var i
= 0; i
< this._arrayOfAllItems
.length
; ++i
){
11366 var item
= this._arrayOfAllItems
[i
];
11368 var serializableItem
= {};
11369 for(var key
in item
){
11370 if(key
!== this._storeRefPropName
&& key
!== this._itemNumPropName
&& key
!== this._reverseRefMap
&& key
!== this._rootItemPropName
){
11371 var valueArray
= this.getValues(item
, key
);
11372 if(valueArray
.length
== 1){
11373 serializableItem
[key
] = this._flatten(valueArray
[0]);
11375 var serializableArray
= [];
11376 for(var j
= 0; j
< valueArray
.length
; ++j
){
11377 serializableArray
.push(this._flatten(valueArray
[j
]));
11378 serializableItem
[key
] = serializableArray
;
11383 serializableStructure
.items
.push(serializableItem
);
11386 var prettyPrint
= true;
11387 return jsonUtil
.toJson(serializableStructure
, prettyPrint
);
11390 _isEmpty: function(something
){
11392 // Function to determine if an array or object has no properties or values.
11394 // The array or object to examine.
11396 if(lang
.isObject(something
)){
11398 for(i
in something
){
11402 }else if(lang
.isArray(something
)){
11403 if(something
.length
> 0){
11407 return empty
; //boolean
11410 save: function(/* object */ keywordArgs
){
11412 // See dojo/data/api/Write.save()
11413 this._assert(!this._saveInProgress
);
11415 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
11416 this._saveInProgress
= true;
11419 var saveCompleteCallback = function(){
11426 self
._saveInProgress
= false; // must come after this._pending is cleared, but before any callbacks
11427 if(keywordArgs
&& keywordArgs
.onComplete
){
11428 var scope
= keywordArgs
.scope
|| kernel
.global
;
11429 keywordArgs
.onComplete
.call(scope
);
11432 var saveFailedCallback = function(err
){
11433 self
._saveInProgress
= false;
11434 if(keywordArgs
&& keywordArgs
.onError
){
11435 var scope
= keywordArgs
.scope
|| kernel
.global
;
11436 keywordArgs
.onError
.call(scope
, err
);
11440 if(this._saveEverything
){
11441 var newFileContentString
= this._getNewFileContentString();
11442 this._saveEverything(saveCompleteCallback
, saveFailedCallback
, newFileContentString
);
11444 if(this._saveCustom
){
11445 this._saveCustom(saveCompleteCallback
, saveFailedCallback
);
11447 if(!this._saveEverything
&& !this._saveCustom
){
11448 // Looks like there is no user-defined save-handler function.
11449 // That's fine, it just means the datastore is acting as a "mock-write"
11450 // store -- changes get saved in memory but don't get saved to disk.
11451 saveCompleteCallback();
11455 revert: function(){
11457 // See dojo/data/api/Write.revert()
11458 this._assert(!this._saveInProgress
);
11461 for(identity
in this._pending
._modifiedItems
){
11462 // find the original item and the modified item that replaced it
11463 var copyOfItemState
= this._pending
._modifiedItems
[identity
];
11464 var modifiedItem
= null;
11465 if(this._itemsByIdentity
){
11466 modifiedItem
= this._itemsByIdentity
[identity
];
11468 modifiedItem
= this._arrayOfAllItems
[identity
];
11471 // Restore the original item into a full-fledged item again, we want to try to
11472 // keep the same object instance as if we don't it, causes bugs like #9022.
11473 copyOfItemState
[this._storeRefPropName
] = this;
11474 for(var key
in modifiedItem
){
11475 delete modifiedItem
[key
];
11477 lang
.mixin(modifiedItem
, copyOfItemState
);
11480 for(identity
in this._pending
._deletedItems
){
11481 deletedItem
= this._pending
._deletedItems
[identity
];
11482 deletedItem
[this._storeRefPropName
] = this;
11483 var index
= deletedItem
[this._itemNumPropName
];
11485 //Restore the reverse refererence map, if any.
11486 if(deletedItem
["backup_" + this._reverseRefMap
]){
11487 deletedItem
[this._reverseRefMap
] = deletedItem
["backup_" + this._reverseRefMap
];
11488 delete deletedItem
["backup_" + this._reverseRefMap
];
11490 this._arrayOfAllItems
[index
] = deletedItem
;
11491 if(this._itemsByIdentity
){
11492 this._itemsByIdentity
[identity
] = deletedItem
;
11494 if(deletedItem
[this._rootItemPropName
]){
11495 this._arrayOfTopLevelItems
.push(deletedItem
);
11498 //We have to pass through it again and restore the reference maps after all the
11499 //undeletes have occurred.
11500 for(identity
in this._pending
._deletedItems
){
11501 deletedItem
= this._pending
._deletedItems
[identity
];
11502 if(deletedItem
["backupRefs_" + this._reverseRefMap
]){
11503 arrayUtil
.forEach(deletedItem
["backupRefs_" + this._reverseRefMap
], function(reference
){
11505 if(this._itemsByIdentity
){
11506 refItem
= this._itemsByIdentity
[reference
.id
];
11508 refItem
= this._arrayOfAllItems
[reference
.id
];
11510 this._addReferenceToMap(refItem
, deletedItem
, reference
.attr
);
11512 delete deletedItem
["backupRefs_" + this._reverseRefMap
];
11516 for(identity
in this._pending
._newItems
){
11517 var newItem
= this._pending
._newItems
[identity
];
11518 newItem
[this._storeRefPropName
] = null;
11519 // null out the new item, but don't change the array index so
11520 // so we can keep using _arrayOfAllItems.length.
11521 this._arrayOfAllItems
[newItem
[this._itemNumPropName
]] = null;
11522 if(newItem
[this._rootItemPropName
]){
11523 this._removeArrayElement(this._arrayOfTopLevelItems
, newItem
);
11525 if(this._itemsByIdentity
){
11526 delete this._itemsByIdentity
[identity
];
11535 return true; // boolean
11538 isDirty: function(/* item? */ item
){
11540 // See dojo/data/api/Write.isDirty()
11542 // return true if the item is dirty
11543 var identity
= this.getIdentity(item
);
11544 return new Boolean(this._pending
._newItems
[identity
] ||
11545 this._pending
._modifiedItems
[identity
] ||
11546 this._pending
._deletedItems
[identity
]).valueOf(); // boolean
11548 // return true if the store is dirty -- which means return true
11549 // if there are any new items, dirty items, or modified items
11550 return !this._isEmpty(this._pending
._newItems
) ||
11551 !this._isEmpty(this._pending
._modifiedItems
) ||
11552 !this._isEmpty(this._pending
._deletedItems
); // boolean
11556 /* dojo/data/api/Notification */
11558 onSet: function(/* dojo/data/api/Item */ item,
11559 /*attribute-name-string*/ attribute
,
11560 /*object|array*/ oldValue
,
11561 /*object|array*/ newValue
){
11563 // See dojo/data/api/Notification.onSet()
11565 // No need to do anything. This method is here just so that the
11566 // client code can connect observers to it.
11569 onNew: function(/* dojo/data/api/Item */ newItem, /*object?*/ parentInfo
){
11571 // See dojo/data/api/Notification.onNew()
11573 // No need to do anything. This method is here just so that the
11574 // client code can connect observers to it.
11577 onDelete: function(/* dojo/data/api/Item */ deletedItem){
11579 // See dojo/data/api/Notification.onDelete()
11581 // No need to do anything. This method is here just so that the
11582 // client code can connect observers to it.
11585 close: function(/* object? */ request
){
11587 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11589 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
11590 // If the store is still dirty (unsaved changes), then an error will be thrown instead of
11591 // clearing the internal state for reload from the url.
11593 //Clear if not dirty ... or throw an error
11594 if(this.clearOnClose
){
11595 if(!this.isDirty()){
11596 this.inherited(arguments
);
11598 //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
11599 throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
11608 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
11609 'dojo/dnd/TimedMoveable':function(){
11610 define("dojo/dnd/TimedMoveable", ["../_base/declare", "./Moveable" /*=====, "./Mover" =====*/], function(declare, Moveable /*=====, Mover =====*/){
11612 // dojo/dnd/TimedMoveable
11615 var __TimedMoveableArgs = declare([Moveable.__MoveableArgs], {
11617 // delay move by this number of ms,
11618 // accumulating position changes during the timeout
11623 // precalculate long expressions
11624 var oldOnMove
= Moveable
.prototype.onMove
;
11626 return declare("dojo.dnd.TimedMoveable", Moveable
, {
11628 // A specialized version of Moveable to support an FPS throttling.
11629 // This class puts an upper restriction on FPS, which may reduce
11630 // the CPU load. The additional parameter "timeout" regulates
11631 // the delay before actually moving the moveable object.
11633 // object attributes (for markup)
11634 timeout
: 40, // in ms, 40ms corresponds to 25 fps
11636 constructor: function(node
, params
){
11638 // an object that makes a node moveable with a timer
11639 // node: Node||String
11640 // a node (or node's id) to be moved
11641 // params: __TimedMoveableArgs
11642 // object with additional parameters.
11644 // sanitize parameters
11645 if(!params
){ params
= {}; }
11646 if(params
.timeout
&& typeof params
.timeout
== "number" && params
.timeout
>= 0){
11647 this.timeout
= params
.timeout
;
11651 onMoveStop: function(/*Mover*/ mover
){
11654 clearTimeout(mover
._timer
);
11655 // reflect the last received position
11656 oldOnMove
.call(this, mover
, mover
._leftTop
);
11658 Moveable
.prototype.onMoveStop
.apply(this, arguments
);
11660 onMove: function(/*Mover*/ mover
, /*Object*/ leftTop
){
11661 mover
._leftTop
= leftTop
;
11663 var _t
= this; // to avoid using dojo.hitch()
11664 mover
._timer
= setTimeout(function(){
11665 // we don't have any pending requests
11666 mover
._timer
= null;
11667 // reflect the last received position
11668 oldOnMove
.call(_t
, mover
, mover
._leftTop
);
11676 'dojo/NodeList-fx':function(){
11677 define("dojo/NodeList-fx", ["./query", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
11678 function(query
, lang
, connectLib
, baseFx
, coreFx
){
11681 // dojo/NodeList-fx
11686 // Adds dojo.fx animation support to dojo.query() by extending the NodeList class
11687 // with additional FX functions. NodeList is the array-like object used to hold query results.
11691 var NodeList
= query
.NodeList
;
11693 lang
.extend(NodeList
, {
11694 _anim: function(obj
, method
, args
){
11696 var a
= coreFx
.combine(
11697 this.map(function(item
){
11698 var tmpArgs
= { node
: item
};
11699 lang
.mixin(tmpArgs
, args
);
11700 return obj
[method
](tmpArgs
);
11703 return args
.auto
? a
.play() && this : a
; // dojo/_base/fx.Animation|dojo/NodeList
11706 wipeIn: function(args
){
11708 // wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
11711 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11712 // an `auto` parameter.
11714 // returns: dojo/_base/fx.Animation|dojo/NodeList
11715 // A special args member `auto` can be passed to automatically play the animation.
11716 // If args.auto is present, the original dojo/NodeList will be returned for further
11717 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11720 // Fade in all tables with class "blah":
11721 // | dojo.query("table.blah").wipeIn().play();
11724 // Utilizing `auto` to get the NodeList back:
11725 // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
11727 return this._anim(coreFx
, "wipeIn", args
); // dojo/_base/fx.Animation|dojo/NodeList
11730 wipeOut: function(args
){
11732 // wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
11735 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11736 // an `auto` parameter.
11738 // returns: dojo/_base/fx.Animation|dojo/NodeList
11739 // A special args member `auto` can be passed to automatically play the animation.
11740 // If args.auto is present, the original dojo/NodeList will be returned for further
11741 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11744 // Wipe out all tables with class "blah":
11745 // | dojo.query("table.blah").wipeOut().play();
11746 return this._anim(coreFx
, "wipeOut", args
); // dojo/_base/fx.Animation|dojo/NodeList
11749 slideTo: function(args
){
11751 // slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
11754 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11755 // an `auto` parameter.
11757 // returns: dojo/_base/fx.Animation|dojo/NodeList
11758 // A special args member `auto` can be passed to automatically play the animation.
11759 // If args.auto is present, the original dojo/NodeList will be returned for further
11760 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11763 // | Move all tables with class "blah" to 300/300:
11764 // | dojo.query("table.blah").slideTo({
11768 return this._anim(coreFx
, "slideTo", args
); // dojo/_base/fx.Animation|dojo/NodeList
11772 fadeIn: function(args
){
11774 // fade in all elements of this NodeList via `dojo.fadeIn`
11777 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11778 // an `auto` parameter.
11780 // returns: dojo/_base/fx.Animation|dojo/NodeList
11781 // A special args member `auto` can be passed to automatically play the animation.
11782 // If args.auto is present, the original dojo/NodeList will be returned for further
11783 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11786 // Fade in all tables with class "blah":
11787 // | dojo.query("table.blah").fadeIn().play();
11788 return this._anim(baseFx
, "fadeIn", args
); // dojo/_base/fx.Animation|dojo/NodeList
11791 fadeOut: function(args
){
11793 // fade out all elements of this NodeList via `dojo.fadeOut`
11796 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11797 // an `auto` parameter.
11799 // returns: dojo/_base/fx.Animation|dojo/NodeList
11800 // A special args member `auto` can be passed to automatically play the animation.
11801 // If args.auto is present, the original dojo/NodeList will be returned for further
11802 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11805 // Fade out all elements with class "zork":
11806 // | dojo.query(".zork").fadeOut().play();
11808 // Fade them on a delay and do something at the end:
11809 // | var fo = dojo.query(".zork").fadeOut();
11810 // | dojo.connect(fo, "onEnd", function(){ /*...*/ });
11814 // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
11816 return this._anim(baseFx
, "fadeOut", args
); // dojo/_base/fx.Animation|dojo/NodeList
11819 animateProperty: function(args
){
11821 // Animate all elements of this NodeList across the properties specified.
11822 // syntax identical to `dojo.animateProperty`
11825 // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
11826 // an `auto` parameter.
11828 // returns: dojo/_base/fx.Animation|dojo/NodeList
11829 // A special args member `auto` can be passed to automatically play the animation.
11830 // If args.auto is present, the original dojo/NodeList will be returned for further
11831 // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
11834 // | dojo.query(".zork").animateProperty({
11835 // | duration: 500,
11837 // | color: { start: "black", end: "white" },
11838 // | left: { end: 300 }
11843 // | dojo.query(".grue").animateProperty({
11848 // | }).onclick(handler);
11849 return this._anim(baseFx
, "animateProperty", args
); // dojo/_base/fx.Animation|dojo/NodeList
11852 anim: function( /*Object*/ properties
,
11853 /*Integer?*/ duration
,
11854 /*Function?*/ easing
,
11855 /*Function?*/ onEnd
,
11856 /*Integer?*/ delay
){
11858 // Animate one or more CSS properties for all nodes in this list.
11859 // The returned animation object will already be playing when it
11860 // is returned. See the docs for `dojo.anim` for full details.
11861 // properties: Object
11862 // the properties to animate. does NOT support the `auto` parameter like other
11863 // NodeList-fx methods.
11864 // duration: Integer?
11865 // Optional. The time to run the animations for
11866 // easing: Function?
11867 // Optional. The easing function to use.
11868 // onEnd: Function?
11869 // A function to be called when the animation ends
11871 // how long to delay playing the returned animation
11873 // Another way to fade out:
11874 // | dojo.query(".thinger").anim({ opacity: 0 });
11876 // animate all elements with the "thigner" class to a width of 500
11877 // pixels over half a second
11878 // | dojo.query(".thinger").anim({ width: 500 }, 700);
11879 var canim
= coreFx
.combine(
11880 this.map(function(item
){
11881 return baseFx
.animateProperty({
11883 properties
: properties
,
11884 duration
: duration
||350,
11890 connectLib
.connect(canim
, "onEnd", onEnd
);
11892 return canim
.play(delay
||0); // dojo/_base/fx.Animation
11900 'dijit/form/_ListMouseMixin':function(){
11901 define("dijit/form/_ListMouseMixin", [
11902 "dojo/_base/declare", // declare
11907 ], function(declare
, mouse
, on
, touch
, _ListBase
){
11910 // dijit/form/_ListMouseMixin
11912 return declare( "dijit.form._ListMouseMixin", _ListBase
, {
11914 // a Mixin to handle mouse or touch events for a focus-less menu
11915 // Abstract methods that must be defined externally:
11917 // - onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
11921 postCreate: function(){
11922 this.inherited(arguments
);
11924 this.own(on(this.domNode
, touch
.press
, function(evt
){ evt
.preventDefault(); })); // prevent focus shift on list scrollbar press
11926 this._listConnect(touch
.press
, "_onMouseDown");
11927 this._listConnect(touch
.release
, "_onMouseUp");
11928 this._listConnect(mouse
.enter
, "_onMouseOver");
11929 this._listConnect(mouse
.leave
, "_onMouseOut");
11932 _onMouseDown: function(/*Event*/ evt
, /*DomNode*/ target
){
11933 if(this._hoveredNode
){
11934 this.onUnhover(this._hoveredNode
);
11935 this._hoveredNode
= null;
11937 this._isDragging
= true;
11938 this._setSelectedAttr(target
);
11941 _onMouseUp: function(/*Event*/ evt
, /*DomNode*/ target
){
11942 this._isDragging
= false;
11943 var selectedNode
= this.selected
;
11944 var hoveredNode
= this._hoveredNode
;
11945 if(selectedNode
&& target
== selectedNode
){
11946 this.onClick(selectedNode
);
11947 }else if(hoveredNode
&& target
== hoveredNode
){ // drag to select
11948 this._setSelectedAttr(hoveredNode
);
11949 this.onClick(hoveredNode
);
11953 _onMouseOut: function(/*Event*/ evt
, /*DomNode*/ target
){
11954 if(this._hoveredNode
){
11955 this.onUnhover(this._hoveredNode
);
11956 this._hoveredNode
= null;
11958 if(this._isDragging
){
11959 this._cancelDrag
= (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
11963 _onMouseOver: function(/*Event*/ evt
, /*DomNode*/ target
){
11964 if(this._cancelDrag
){
11965 var time
= (new Date()).getTime();
11966 if(time
> this._cancelDrag
){
11967 this._isDragging
= false;
11969 this._cancelDrag
= null;
11971 this._hoveredNode
= target
;
11972 this.onHover(target
);
11973 if(this._isDragging
){
11974 this._setSelectedAttr(target
);
11982 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
11983 'dojo/cookie':function(){
11984 define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo
, regexp
){
11990 var __cookieProps = {
11991 // expires: Date|String|Number?
11992 // If a number, the number of days from today at which the cookie
11993 // will expire. If a date, the date past which the cookie will expire.
11994 // If expires is in the past, the cookie will be deleted.
11995 // If expires is omitted or is 0, the cookie will expire when the browser closes.
11997 // The path to use for the cookie.
11999 // The domain to use for the cookie.
12000 // secure: Boolean?
12001 // Whether to only send the cookie on secure connections
12006 dojo
.cookie = function(/*String*/name
, /*String?*/ value
, /*__cookieProps?*/ props
){
12008 // Get or set a cookie.
12010 // If one argument is passed, returns the value of the cookie
12011 // For two or more arguments, acts as a setter.
12013 // Name of the cookie
12015 // Value for the cookie
12017 // Properties for the cookie
12019 // set a cookie with the JSON-serialized contents of an object which
12020 // will expire 5 days from now:
12021 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12022 // | cookie("configObj", json.stringify(config, {expires: 5 }));
12026 // de-serialize a cookie back into a JavaScript object:
12027 // | require(["dojo/cookie", "dojo/json"], function(cookie, json){
12028 // | config = json.parse(cookie("configObj"));
12032 // delete a cookie:
12033 // | require(["dojo/cookie"], function(cookie){
12034 // | cookie("configObj", null, {expires: -1});
12036 var c
= document
.cookie
, ret
;
12037 if(arguments
.length
== 1){
12038 var matches
= c
.match(new RegExp("(?:^|; )" + regexp
.escapeString(name
) + "=([^;]*)"));
12039 ret
= matches
? decodeURIComponent(matches
[1]) : undefined;
12041 props
= props
|| {};
12042 // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
12043 var exp
= props
.expires
;
12044 if(typeof exp
== "number"){
12045 var d
= new Date();
12046 d
.setTime(d
.getTime() + exp
*24*60*60*1000);
12047 exp
= props
.expires
= d
;
12049 if(exp
&& exp
.toUTCString
){ props
.expires
= exp
.toUTCString(); }
12051 value
= encodeURIComponent(value
);
12052 var updatedCookie
= name
+ "=" + value
, propName
;
12053 for(propName
in props
){
12054 updatedCookie
+= "; " + propName
;
12055 var propValue
= props
[propName
];
12056 if(propValue
!== true){ updatedCookie
+= "=" + propValue
; }
12058 document
.cookie
= updatedCookie
;
12060 return ret
; // String|undefined
12063 dojo
.cookie
.isSupported = function(){
12065 // Use to determine if the current browser supports cookies or not.
12067 // Returns true if user allows cookies.
12068 // Returns false if user doesn't allow cookies.
12070 if(!("cookieEnabled" in navigator
)){
12071 this("__djCookieTest__", "CookiesAllowed");
12072 navigator
.cookieEnabled
= this("__djCookieTest__") == "CookiesAllowed";
12073 if(navigator
.cookieEnabled
){
12074 this("__djCookieTest__", "", {expires
: -1});
12077 return navigator
.cookieEnabled
;
12080 return dojo
.cookie
;
12084 'dojo/cache':function(){
12085 define("dojo/cache", ["./_base/kernel", "./text"], function(dojo
){
12089 // dojo.cache is defined in dojo/text
12094 'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"▼ \" 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",
12095 'dijit/ProgressBar':function(){
12097 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\"> </span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"}});
12098 define("dijit/ProgressBar", [
12099 "require", // require.toUrl
12100 "dojo/_base/declare", // declare
12101 "dojo/dom-class", // domClass.toggle
12102 "dojo/_base/lang", // lang.mixin
12103 "dojo/number", // number.format
12105 "./_TemplatedMixin",
12106 "dojo/text!./templates/ProgressBar.html"
12107 ], function(require
, declare
, domClass
, lang
, number
, _Widget
, _TemplatedMixin
, template
){
12110 // dijit/ProgressBar
12113 return declare("dijit.ProgressBar", [_Widget
, _TemplatedMixin
], {
12115 // A progress indication widget, showing the amount completed
12116 // (often the percentage completed) of a task.
12119 // | <div data-dojo-type="ProgressBar"
12121 // | value="..." maximum="...">
12124 // progress: [const] String (Percentage or Number)
12125 // Number or percentage indicating amount of task completed.
12126 // Deprecated. Use "value" instead.
12129 // value: String (Percentage or Number)
12130 // Number or percentage indicating amount of task completed.
12131 // With "%": percentage value, 0% <= progress <= 100%, or
12132 // without "%": absolute value, 0 <= progress <= maximum.
12133 // Infinity means that the progress bar is indeterminate.
12136 // maximum: [const] Float
12137 // Max sample number
12140 // places: [const] Number
12141 // Number of places to show in values; 0 by default
12144 // indeterminate: [const] Boolean
12145 // If false: show progress value (number or percentage).
12146 // If true: show that a process is underway but that the amount completed is unknown.
12147 // Deprecated. Use "value" instead.
12148 indeterminate
: false,
12151 // Label on progress bar. Defaults to percentage for determinate progress bar and
12152 // blank for indeterminate progress bar.
12156 // this is the field name (for a form) if set. This needs to be set if you want to use
12157 // this widget in a dijit/form/Form widget (such as dijit/Dialog)
12160 templateString
: template
,
12162 // _indeterminateHighContrastImagePath: [private] URL
12163 // URL to image to use for indeterminate progress bar when display is in high contrast mode
12164 _indeterminateHighContrastImagePath
:
12165 require
.toUrl("./themes/a11y/indeterminate_progress.gif"),
12167 postMixInProperties: function(){
12168 this.inherited(arguments
);
12170 // Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
12171 if(!(this.params
&& "value" in this.params
)){
12172 this.value
= this.indeterminate
? Infinity
: this.progress
;
12176 buildRendering: function(){
12177 this.inherited(arguments
);
12178 this.indeterminateHighContrastImage
.setAttribute("src",
12179 this._indeterminateHighContrastImagePath
.toString());
12183 update: function(/*Object?*/attributes
){
12185 // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
12186 // set("value", ...) rather than calling this method directly.
12188 // May provide progress and/or maximum properties on this parameter;
12189 // see attribute specs for details.
12191 // | myProgressBar.update({'indeterminate': true});
12192 // | myProgressBar.update({'progress': 80});
12193 // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
12197 // TODO: deprecate this method and use set() instead
12199 lang
.mixin(this, attributes
|| {});
12200 var tip
= this.internalProgress
, ap
= this.domNode
;
12202 if(this.indeterminate
){
12203 ap
.removeAttribute("aria-valuenow");
12205 if(String(this.progress
).indexOf("%") != -1){
12206 percent
= Math
.min(parseFloat(this.progress
)/100, 1);
12207 this.progress
= percent
* this.maximum
;
12209 this.progress
= Math
.min(this.progress
, this.maximum
);
12210 percent
= this.maximum
? this.progress
/ this.maximum
: 0;
12212 ap
.setAttribute("aria-valuenow", this.progress
);
12215 // Even indeterminate ProgressBars should have these attributes
12216 ap
.setAttribute("aria-describedby", this.labelNode
.id
);
12217 ap
.setAttribute("aria-valuemin", 0);
12218 ap
.setAttribute("aria-valuemax", this.maximum
);
12220 this.labelNode
.innerHTML
= this.report(percent
);
12222 domClass
.toggle(this.domNode
, "dijitProgressBarIndeterminate", this.indeterminate
);
12223 tip
.style
.width
= (percent
* 100) + "%";
12227 _setValueAttr: function(v
){
12228 this._set("value", v
);
12230 this.update({indeterminate
:true});
12232 this.update({indeterminate
:false, progress
:v
});
12236 _setLabelAttr: function(label
){
12237 this._set("label", label
);
12241 _setIndeterminateAttr: function(indeterminate
){
12242 // Deprecated, use set("value", ...) instead
12243 this.indeterminate
= indeterminate
;
12247 report: function(/*float*/percent
){
12249 // Generates message to show inside progress bar (normally indicating amount of task completed).
12250 // May be overridden.
12254 return this.label
? this.label
:
12255 (this.indeterminate
? " " : number
.format(percent
, { type
: "percent", places
: this.places
, locale
: this.lang
}));
12258 onChange: function(){
12260 // Callback fired when progress updates.
12269 'dijit/_base/popup':function(){
12270 define("dijit/_base/popup", [
12271 "dojo/dom-class", // domClass.contains
12272 "dojo/_base/window",
12274 "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
12275 ], function(domClass
, win
, popup
){
12278 // dijit/_base/popup
12283 // Deprecated. Old module for popups, new code should use dijit/popup directly.
12288 // Hack support for old API passing in node instead of a widget (to various methods)
12289 var origCreateWrapper
= popup
._createWrapper
;
12290 popup
._createWrapper = function(widget
){
12291 if(!widget
.declaredClass
){
12292 // make fake widget to pass to new API
12294 _popupWrapper
: (widget
.parentNode
&& domClass
.contains(widget
.parentNode
, "dijitPopup")) ?
12295 widget
.parentNode
: null,
12297 destroy: function(){},
12298 ownerDocument
: widget
.ownerDocument
,
12299 ownerDocumentBody
: win
.body(widget
.ownerDocument
)
12302 return origCreateWrapper
.call(this, widget
);
12305 // Support old format of orient parameter
12306 var origOpen
= popup
.open
;
12307 popup
.open = function(/*__OpenArgs*/ args
){
12308 // Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
12309 // Don't do conversion for:
12310 // - null parameter (that means to use the default positioning)
12311 // - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
12312 // - new format, ex: ["below", "above"]
12313 // - return value from deprecated dijit.getPopupAroundAlignment() method,
12314 // ex: ["below", "above"]
12315 if(args
.orient
&& typeof args
.orient
!= "string" && !("length" in args
.orient
)){
12317 for(var key
in args
.orient
){
12318 ary
.push({aroundCorner
: key
, corner
: args
.orient
[key
]});
12323 return origOpen
.call(this, args
);
12330 'dijit/ColorPalette':function(){
12332 'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
12333 define("dijit/ColorPalette", [
12334 "require", // require.toUrl
12335 "dojo/text!./templates/ColorPalette.html",
12336 "./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
12337 "./_TemplatedMixin",
12339 "./hccss", // has("highcontrast")
12340 "dojo/i18n", // i18n.getLocalization
12341 "dojo/_base/Color", // dojo.Color dojo.Color.named
12342 "dojo/_base/declare", // declare
12343 "dojo/dom-construct", // domConstruct.place
12344 "dojo/string", // string.substitute
12345 "dojo/i18n!dojo/nls/colors", // translations
12346 "dojo/colors" // extend dojo.Color w/names of other colors
12347 ], function(require
, template
, _Widget
, _TemplatedMixin
, _PaletteMixin
, has
, i18n
, Color
,
12348 declare
, domConstruct
, string
){
12351 // dijit/ColorPalette
12353 var ColorPalette
= declare("dijit.ColorPalette", [_Widget
, _TemplatedMixin
, _PaletteMixin
], {
12355 // A keyboard accessible color-picking widget
12357 // Grid showing various colors, so the user can pick a certain color.
12358 // Can be used standalone, or as a popup.
12361 // | <div data-dojo-type="dijit/ColorPalette"></div>
12364 // | var picker = new dijit.ColorPalette({ },srcNode);
12365 // | picker.startup();
12368 // palette: [const] String
12369 // Size of grid, either "7x10" or "3x4".
12372 // _palettes: [protected] Map
12373 // This represents the value of the colors.
12374 // The first level is a hashmap of the different palettes available.
12375 // The next two dimensions represent the columns and rows of colors.
12377 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
12378 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
12379 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
12380 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
12381 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
12382 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
12383 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
12385 "3x4": [["white", "lime", "green", "blue"],
12386 ["silver", "yellow", "fuchsia", "navy"],
12387 ["gray", "red", "purple", "black"]]
12390 // templateString: String
12391 // The template of this widget.
12392 templateString
: template
,
12394 baseClass
: "dijitColorPalette",
12396 _dyeFactory: function(value
, row
, col
, title
){
12397 // Overrides _PaletteMixin._dyeFactory().
12398 return new this._dyeClass(value
, row
, col
, title
);
12401 buildRendering: function(){
12402 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
12404 this.inherited(arguments
);
12406 // Creates customized constructor for dye class (color of a single cell) for
12407 // specified palette and high-contrast vs. normal mode. Used in _getDye().
12408 this._dyeClass
= declare(ColorPalette
._Color
, {
12409 palette
: this.palette
12412 // Creates <img> nodes in each cell of the template.
12413 this._preparePalette(
12414 this._palettes
[this.palette
],
12415 i18n
.getLocalization("dojo", "colors", this.lang
));
12419 ColorPalette
._Color
= declare("dijit._Color", Color
, {
12421 // Object associated with each cell in a ColorPalette palette.
12422 // Implements dijit/Dye.
12424 // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
12425 // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
12426 // for showing the color.
12428 "<span class='dijitInline dijitPaletteImg'>" +
12429 "<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
12432 // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
12433 // but scrolled and clipped to show the correct color only
12435 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
12436 "<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
12439 // _imagePaths: [protected] Map
12440 // This is stores the path to the palette images used for high-contrast mode display
12442 "7x10": require
.toUrl("./themes/a11y/colors7x10.png"),
12443 "3x4": require
.toUrl("./themes/a11y/colors3x4.png")
12446 constructor: function(alias
, row
, col
, title
){
12448 // Constructor for ColorPalette._Color
12450 // English name of the color.
12452 // Vertical position in grid.
12454 // Horizontal position in grid.
12456 // Localized name of the color.
12457 this._title
= title
;
12460 this.setColor(Color
.named
[alias
]);
12463 getValue: function(){
12465 // Note that although dijit._Color is initialized with a value like "white" getValue() always
12466 // returns a hex value
12467 return this.toHex();
12470 fillCell: function(/*DOMNode*/ cell
, /*String*/ blankGif
){
12471 var html
= string
.substitute(has("highcontrast") ? this.hcTemplate
: this.template
, {
12472 // substitution variables for normal mode
12473 color
: this.toHex(),
12474 blankGif
: blankGif
,
12476 title
: this._title
,
12478 // variables used for high contrast mode
12479 image
: this._imagePaths
[this.palette
].toString(),
12480 left
: this._col
* -20 - 5,
12481 top
: this._row
* -20 - 5,
12482 size
: this.palette
== "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
12485 domConstruct
.place(html
, cell
);
12490 return ColorPalette
;
12494 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">●</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
12495 'dojo/_base/url':function(){
12496 define("dojo/_base/url", ["./kernel"], function(dojo
){
12501 ore
= new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
12502 ire
= new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
12507 // resolve uri components relative to each other
12508 for(var i
= 1; i
<_a
.length
; i
++){
12509 if(!_a
[i
]){ continue; }
12511 // Safari doesn't support this.constructor so we have to be explicit
12512 // FIXME: Tracked (and fixed) in Webkit bug 3537.
12513 // http://bugs.webkit.org/show_bug.cgi?id=3537
12514 var relobj
= new _Url(_a
[i
]+""),
12515 uriobj
= new _Url(uri
[0]+"");
12518 relobj
.path
== "" &&
12520 !relobj
.authority
&&
12523 if(relobj
.fragment
!= n
){
12524 uriobj
.fragment
= relobj
.fragment
;
12527 }else if(!relobj
.scheme
){
12528 relobj
.scheme
= uriobj
.scheme
;
12530 if(!relobj
.authority
){
12531 relobj
.authority
= uriobj
.authority
;
12533 if(relobj
.path
.charAt(0) != "/"){
12534 var path
= uriobj
.path
.substring(0,
12535 uriobj
.path
.lastIndexOf("/") + 1) + relobj
.path
;
12537 var segs
= path
.split("/");
12538 for(var j
= 0; j
< segs
.length
; j
++){
12539 if(segs
[j
] == "."){
12540 // flatten "./" references
12541 if(j
== segs
.length
- 1){
12547 }else if(j
> 0 && !(j
== 1 && segs
[0] == "") &&
12548 segs
[j
] == ".." && segs
[j
-1] != ".."){
12549 // flatten "../" references
12550 if(j
== (segs
.length
- 1)){
12554 segs
.splice(j
- 1, 2);
12559 relobj
.path
= segs
.join("/");
12566 uri
.push(relobj
.scheme
, ":");
12568 if(relobj
.authority
){
12569 uri
.push("//", relobj
.authority
);
12571 uri
.push(relobj
.path
);
12573 uri
.push("?", relobj
.query
);
12575 if(relobj
.fragment
){
12576 uri
.push("#", relobj
.fragment
);
12580 this.uri
= uri
.join("");
12582 // break the uri into its main components
12583 var r
= this.uri
.match(ore
);
12585 this.scheme
= r
[2] || (r
[1] ? "" : n
);
12586 this.authority
= r
[4] || (r
[3] ? "" : n
);
12587 this.path
= r
[5]; // can never be undefined
12588 this.query
= r
[7] || (r
[6] ? "" : n
);
12589 this.fragment
= r
[9] || (r
[8] ? "" : n
);
12591 if(this.authority
!= n
){
12592 // server based naming authority
12593 r
= this.authority
.match(ire
);
12595 this.user
= r
[3] || n
;
12596 this.password
= r
[4] || n
;
12597 this.host
= r
[6] || r
[7]; // ipv6 || ipv4
12598 this.port
= r
[9] || n
;
12601 _Url
.prototype.toString = function(){ return this.uri
; };
12603 return dojo
._Url
= _Url
;
12607 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n",
12608 'dojo/text':function(){
12609 define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo
, require
, has
, xhr
){
12615 getText= function(url
, sync
, load
){
12616 xhr("GET", {url
: url
, sync
:!!sync
, load
: load
, headers
: dojo
.config
.textPluginHeaders
|| {}});
12619 // TODOC: only works for dojo AMD loader
12620 if(require
.getText
){
12621 getText
= require
.getText
;
12623 console
.error("dojo/text plugin failed to load because loader does not support getText");
12630 strip= function(text
){
12631 //Strips <?xml ...?> declarations so that external SVG and XML
12632 //documents can be added to a document without worry. Also, if the string
12633 //is an HTML document, only the part inside the body tag is returned.
12635 text
= text
.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
12636 var matches
= text
.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
12650 dojo
.cache = function(/*String||Object*/module
, /*String*/url
, /*String||Object?*/value
){
12652 // A getter and setter for storing the string content associated with the
12653 // module and url arguments.
12655 // If module is a string that contains slashes, then it is interpretted as a fully
12656 // resolved path (typically a result returned by require.toUrl), and url should not be
12657 // provided. This is the preferred signature. If module is a string that does not
12658 // contain slashes, then url must also be provided and module and url are used to
12659 // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
12660 // If value is specified, the cache value for the moduleUrl will be set to
12661 // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
12662 // in its internal cache and return that cached value for the URL. To clear
12663 // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
12664 // the URL contents, only modules on the same domain of the page can use this capability.
12665 // The build system can inline the cache values though, to allow for xdomain hosting.
12666 // module: String||Object
12667 // If a String with slashes, a fully resolved path; if a String without slashes, the
12668 // module name to use for the base part of the URL, similar to module argument
12669 // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
12670 // generates a valid path for the cache item. For example, a dojo._Url object.
12672 // The rest of the path to append to the path derived from the module argument. If
12673 // module is an object, then this second argument should be the "value" argument instead.
12674 // value: String||Object?
12675 // If a String, the value to use in the cache for the module/url combination.
12676 // If an Object, it can have two properties: value and sanitize. The value property
12677 // should be the value to use in the cache, and sanitize can be set to true or false,
12678 // to indicate if XML declarations should be removed from the value and if the HTML
12679 // inside a body tag in the value should be extracted as the real value. The value argument
12680 // or the value property on the value argument are usually only used by the build system
12681 // as it inlines cache content.
12683 // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
12684 // of call is used to avoid an issue with the build system erroneously trying to intern
12685 // this example. To get the build system to intern your dojo.cache calls, use the
12686 // "dojo.cache" style of call):
12687 // | //If template.html contains "<h1>Hello</h1>" that will be
12688 // | //the value for the text variable.
12689 // | var text = dojo["cache"]("my.module", "template.html");
12691 // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
12692 // (the dojo["cache"] style of call is used to avoid an issue with the build system
12693 // erroneously trying to intern this example. To get the build system to intern your
12694 // dojo.cache calls, use the "dojo.cache" style of call):
12695 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12696 // | //text variable will contain just "<h1>Hello</h1>".
12697 // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
12699 // Same example as previous, but demonstrates how an object can be passed in as
12700 // the first argument, then the value argument can then be the second argument.
12701 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12702 // | //text variable will contain just "<h1>Hello</h1>".
12703 // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
12705 // * (string string [value]) => (module, url, value)
12706 // * (object [value]) => (module, value), url defaults to ""
12708 // * if module is an object, then it must be convertable to a string
12709 // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
12710 // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
12712 if(typeof module
=="string"){
12713 if(/\//.test(module
)){
12714 // module is a version 1.7+ resolved path
12718 // module is a version 1.6- argument to dojo.moduleUrl
12719 key
= require
.toUrl(module
.replace(/\./g, "/") + (url
? ("/" + url
) : ""));
12726 val
= (value
!= undefined && typeof value
!= "string") ? value
.value
: value
,
12727 sanitize
= value
&& value
.sanitize
;
12729 if(typeof val
== "string"){
12730 //We have a string, set cache value
12731 theCache
[key
] = val
;
12732 return sanitize
? strip(val
) : val
;
12733 }else if(val
=== null){
12734 //Remove cached value
12735 delete theCache
[key
];
12738 //Allow cache values to be empty strings. If key property does
12739 //not exist, fetch it.
12740 if(!(key
in theCache
)){
12741 getText(key
, true, function(text
){
12742 theCache
[key
]= text
;
12745 return sanitize
? strip(theCache
[key
]) : theCache
[key
];
12751 // This module implements the dojo/text! plugin and the dojo.cache API.
12753 // We choose to include our own plugin to leverage functionality already contained in dojo
12754 // and thereby reduce the size of the plugin compared to various foreign loader implementations.
12755 // Also, this allows foreign AMD loaders to be used without their plugins.
12757 // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
12758 // loader. This feature is outside the scope of the CommonJS plugins specification.
12760 // the dojo/text caches it's own resources because of dojo.cache
12763 normalize: function(id
, toAbsMid
){
12764 // id is something like (path may be relative):
12766 // "path/to/text.html"
12767 // "path/to/text.html!strip"
12768 var parts
= id
.split("!"),
12770 return (/^\./.test(url
) ? toAbsMid(url
) : url
) + (parts
[1] ? "!" + parts
[1] : "");
12773 load: function(id
, require
, load
){
12775 // Path to the resource.
12776 // require: Function
12777 // Object that include the function toUrl with given id returns a valid URL from which to load the text.
12779 // Callback function which will be called, when the loading finished.
12781 // id is something like (path is always absolute):
12783 // "path/to/text.html"
12784 // "path/to/text.html!strip"
12786 parts
= id
.split("!"),
12787 stripFlag
= parts
.length
>1,
12789 url
= require
.toUrl(parts
[0]),
12790 requireCacheUrl
= "url:" + url
,
12792 finish = function(text
){
12793 load(stripFlag
? strip(text
) : text
);
12795 if(absMid
in theCache
){
12796 text
= theCache
[absMid
];
12797 }else if(requireCacheUrl
in require
.cache
){
12798 text
= require
.cache
[requireCacheUrl
];
12799 }else if(url
in theCache
){
12800 text
= theCache
[url
];
12802 if(text
===notFound
){
12804 pending
[url
].push(finish
);
12806 var pendingList
= pending
[url
] = [finish
];
12807 getText(url
, !require
.async
, function(text
){
12808 theCache
[absMid
]= theCache
[url
]= text
;
12809 for(var i
= 0; i
<pendingList
.length
;){
12810 pendingList
[i
++](text
);
12812 delete pending
[url
];
12825 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
12826 'dojo/uacss':function(){
12827 define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./sniff", "./_base/window"],
12828 function(geometry
, lang
, ready
, has
, baseWindow
){
12836 // Applies pre-set CSS classes to the top-level HTML node, based on:
12838 // - browser (ex: dj_ie)
12839 // - browser version (ex: dj_ie6)
12840 // - box model (ex: dj_contentBox)
12841 // - text direction (ex: dijitRtl)
12843 // In addition, browser, browser version, and box model are
12844 // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
12846 // Returns the has() method.
12851 html
= baseWindow
.doc
.documentElement
,
12853 opera
= has("opera"),
12856 boxModel
= geometry
.boxModel
.replace(/-/,''),
12859 "dj_quirks": has("quirks"),
12861 // NOTE: Opera not supported by dijit
12864 "dj_khtml": has("khtml"),
12866 "dj_webkit": has("webkit"),
12867 "dj_safari": has("safari"),
12868 "dj_chrome": has("chrome"),
12870 "dj_gecko": has("mozilla")
12871 }; // no dojo unsupported browsers
12874 classes
["dj_ie"] = true;
12875 classes
["dj_ie" + maj(ie
)] = true;
12876 classes
["dj_iequirks"] = has("quirks");
12879 classes
["dj_ff" + maj(ff
)] = true;
12882 classes
["dj_" + boxModel
] = true;
12884 // apply browser, browser version, and box model class names
12886 for(var clz
in classes
){
12888 classStr
+= clz
+ " ";
12891 html
.className
= lang
.trim(html
.className
+ " " + classStr
);
12893 // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
12894 // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
12895 // priority is 90 to run ahead of parser priority of 100
12896 ready(90, function(){
12897 if(!geometry
.isBodyLtr()){
12898 var rtlClassStr
= "dj_rtl dijitRtl " + classStr
.replace(/ /g
, "-rtl ");
12899 html
.className
= lang
.trim(html
.className
+ " " + rtlClassStr
+ "dj_rtl dijitRtl " + classStr
.replace(/ /g
, "-rtl "));
12906 'dijit/Tooltip':function(){
12908 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}});
12909 define("dijit/Tooltip", [
12910 "dojo/_base/array", // array.forEach array.indexOf array.map
12911 "dojo/_base/declare", // declare
12912 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
12913 "dojo/dom", // dom.byId
12914 "dojo/dom-class", // domClass.add
12915 "dojo/dom-geometry", // domGeometry.position
12916 "dojo/dom-style", // domStyle.set, domStyle.get
12917 "dojo/_base/lang", // lang.hitch lang.isArrayLike
12920 "dojo/sniff", // has("ie")
12921 "./_base/manager", // manager.defaultDuration
12924 "./_TemplatedMixin",
12925 "./BackgroundIframe",
12926 "dojo/text!./templates/Tooltip.html",
12927 "./main" // sets dijit.showTooltip etc. for back-compat
12928 ], function(array
, declare
, fx
, dom
, domClass
, domGeometry
, domStyle
, lang
, mouse
, on
, has
,
12929 manager
, place
, _Widget
, _TemplatedMixin
, BackgroundIframe
, template
, dijit
){
12935 // TODO: Tooltip should really share more positioning code with TooltipDialog, like:
12936 // - the orient() method
12937 // - the connector positioning code in show()
12938 // - the dijitTooltip[Dialog] class
12940 // The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
12941 // with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
12943 var MasterTooltip
= declare("dijit._MasterTooltip", [_Widget
, _TemplatedMixin
], {
12945 // Internal widget that holds the actual tooltip markup,
12946 // which occurs once per page.
12947 // Called by Tooltip widgets which are just containers to hold
12952 // duration: Integer
12953 // Milliseconds to fade in/fade out
12954 duration
: manager
.defaultDuration
,
12956 templateString
: template
,
12958 postCreate: function(){
12959 this.ownerDocumentBody
.appendChild(this.domNode
);
12961 this.bgIframe
= new BackgroundIframe(this.domNode
);
12963 // Setup fade-in and fade-out functions.
12964 this.fadeIn
= fx
.fadeIn({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onShow") });
12965 this.fadeOut
= fx
.fadeOut({ node
: this.domNode
, duration
: this.duration
, onEnd
: lang
.hitch(this, "_onHide") });
12968 show: function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
12970 // Display tooltip w/specified contents to right of specified node
12971 // (To left if there's no space on the right, or if rtl == true)
12972 // innerHTML: String
12973 // Contents of the tooltip
12974 // aroundNode: DomNode|dijit/place.__Rectangle
12975 // Specifies that tooltip should be next to this node / area
12976 // position: String[]?
12977 // List of positions to try to position tooltip (ex: ["right", "above"])
12979 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
12980 // means "rtl"; specifies GUI direction, not text direction.
12981 // textDir: String?
12982 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
12985 if(this.aroundNode
&& this.aroundNode
=== aroundNode
&& this.containerNode
.innerHTML
== innerHTML
){
12989 if(this.fadeOut
.status() == "playing"){
12990 // previous tooltip is being hidden; wait until the hide completes then show new one
12991 this._onDeck
=arguments
;
12994 this.containerNode
.innerHTML
=innerHTML
;
12997 this.set("textDir", textDir
);
13000 this.containerNode
.align
= rtl
? "right" : "left"; //fix the text alignment
13002 var pos
= place
.around(this.domNode
, aroundNode
,
13003 position
&& position
.length
? position
: Tooltip
.defaultPosition
, !rtl
, lang
.hitch(this, "orient"));
13005 // Position the tooltip connector for middle alignment.
13006 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
13007 var aroundNodeCoords
= pos
.aroundNodePos
;
13008 if(pos
.corner
.charAt(0) == 'M' && pos
.aroundCorner
.charAt(0) == 'M'){
13009 this.connectorNode
.style
.top
= aroundNodeCoords
.y
+ ((aroundNodeCoords
.h
- this.connectorNode
.offsetHeight
) >> 1) - pos
.y
+ "px";
13010 this.connectorNode
.style
.left
= "";
13011 }else if(pos
.corner
.charAt(1) == 'M' && pos
.aroundCorner
.charAt(1) == 'M'){
13012 this.connectorNode
.style
.left
= aroundNodeCoords
.x
+ ((aroundNodeCoords
.w
- this.connectorNode
.offsetWidth
) >> 1) - pos
.x
+ "px";
13014 // Not *-centered, but just above/below/after/before
13015 this.connectorNode
.style
.left
= "";
13016 this.connectorNode
.style
.top
= "";
13020 domStyle
.set(this.domNode
, "opacity", 0);
13021 this.fadeIn
.play();
13022 this.isShowingNow
= true;
13023 this.aroundNode
= aroundNode
;
13026 orient: function(/*DomNode*/ node
, /*String*/ aroundCorner
, /*String*/ tooltipCorner
, /*Object*/ spaceAvailable
, /*Object*/ aroundNodeCoords
){
13028 // Private function to set CSS for tooltip node based on which position it's in.
13029 // This is called by the dijit popup code. It will also reduce the tooltip's
13030 // width to whatever width is available
13034 this.connectorNode
.style
.top
= ""; //reset to default
13036 var heightAvailable
= spaceAvailable
.h
,
13037 widthAvailable
= spaceAvailable
.w
;
13039 node
.className
= "dijitTooltip " +
13041 "MR-ML": "dijitTooltipRight",
13042 "ML-MR": "dijitTooltipLeft",
13043 "TM-BM": "dijitTooltipAbove",
13044 "BM-TM": "dijitTooltipBelow",
13045 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
13046 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
13047 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
13048 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
13049 "BR-BL": "dijitTooltipRight",
13050 "BL-BR": "dijitTooltipLeft"
13051 }[aroundCorner
+ "-" + tooltipCorner
];
13053 // reset width; it may have been set by orient() on a previous tooltip show()
13054 this.domNode
.style
.width
= "auto";
13056 // Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
13057 // Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
13058 // negative number since that causes an exception on IE.
13059 var size
= domGeometry
.position(this.domNode
);
13060 if(has("ie") == 9){
13061 // workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
13065 var width
= Math
.min((Math
.max(widthAvailable
,1)), size
.w
);
13067 domGeometry
.setMarginBox(this.domNode
, {w
: width
});
13069 // Reposition the tooltip connector.
13070 if(tooltipCorner
.charAt(0) == 'B' && aroundCorner
.charAt(0) == 'B'){
13071 var bb
= domGeometry
.position(node
);
13072 var tooltipConnectorHeight
= this.connectorNode
.offsetHeight
;
13073 if(bb
.h
> heightAvailable
){
13074 // The tooltip starts at the top of the page and will extend past the aroundNode
13075 var aroundNodePlacement
= heightAvailable
- ((aroundNodeCoords
.h
+ tooltipConnectorHeight
) >> 1);
13076 this.connectorNode
.style
.top
= aroundNodePlacement
+ "px";
13077 this.connectorNode
.style
.bottom
= "";
13079 // Align center of connector with center of aroundNode, except don't let bottom
13080 // of connector extend below bottom of tooltip content, or top of connector
13081 // extend past top of tooltip content
13082 this.connectorNode
.style
.bottom
= Math
.min(
13083 Math
.max(aroundNodeCoords
.h
/2 - tooltipConnectorHeight/2, 0),
13084 bb
.h
- tooltipConnectorHeight
) + "px";
13085 this.connectorNode
.style
.top
= "";
13088 // reset the tooltip back to the defaults
13089 this.connectorNode
.style
.top
= "";
13090 this.connectorNode
.style
.bottom
= "";
13093 return Math
.max(0, size
.w
- widthAvailable
);
13096 _onShow: function(){
13098 // Called at end of fade-in operation
13102 // the arrow won't show up on a node w/an opacity filter
13103 this.domNode
.style
.filter
="";
13107 hide: function(aroundNode
){
13109 // Hide the tooltip
13111 if(this._onDeck
&& this._onDeck
[1] == aroundNode
){
13112 // this hide request is for a show() that hasn't even started yet;
13113 // just cancel the pending show()
13115 }else if(this.aroundNode
=== aroundNode
){
13116 // this hide request is for the currently displayed tooltip
13117 this.fadeIn
.stop();
13118 this.isShowingNow
= false;
13119 this.aroundNode
= null;
13120 this.fadeOut
.play();
13122 // just ignore the call, it's for a tooltip that has already been erased
13126 _onHide: function(){
13128 // Called at end of fade-out operation
13132 this.domNode
.style
.cssText
=""; // to position offscreen again
13133 this.containerNode
.innerHTML
="";
13135 // a show request has been queued up; do it now
13136 this.show
.apply(this, this._onDeck
);
13141 _setAutoTextDir: function(/*Object*/node
){
13143 // Resolve "auto" text direction for children nodes
13147 this.applyTextDir(node
, has("ie") ? node
.outerText
: node
.textContent
);
13148 array
.forEach(node
.children
, function(child
){this._setAutoTextDir(child
); }, this);
13151 _setTextDirAttr: function(/*String*/ textDir
){
13153 // Setter for textDir.
13155 // Users shouldn't call this function; they should be calling
13156 // set('textDir', value)
13160 this._set("textDir", textDir
);
13162 if (textDir
== "auto"){
13163 this._setAutoTextDir(this.containerNode
);
13165 this.containerNode
.dir
= this.textDir
;
13170 dijit
.showTooltip = function(innerHTML
, aroundNode
, position
, rtl
, textDir
){
13172 // Static method to display tooltip w/specified contents in specified position.
13173 // See description of dijit/Tooltip.defaultPosition for details on position parameter.
13174 // If position is not specified then dijit/Tooltip.defaultPosition is used.
13175 // innerHTML: String
13176 // Contents of the tooltip
13177 // aroundNode: place.__Rectangle
13178 // Specifies that tooltip should be next to this node / area
13179 // position: String[]?
13180 // List of positions to try to position tooltip (ex: ["right", "above"])
13182 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
13183 // means "rtl"; specifies GUI direction, not text direction.
13184 // textDir: String?
13185 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
13187 // After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
13188 // Possibly remove this in 2.0. Alternately, get before/after to work.
13190 position
= array
.map(position
, function(val
){
13191 return {after
: "after-centered", before
: "before-centered"}[val
] || val
;
13195 if(!Tooltip
._masterTT
){ dijit
._masterTT
= Tooltip
._masterTT
= new MasterTooltip(); }
13196 return Tooltip
._masterTT
.show(innerHTML
, aroundNode
, position
, rtl
, textDir
);
13199 dijit
.hideTooltip = function(aroundNode
){
13201 // Static method to hide the tooltip displayed via showTooltip()
13202 return Tooltip
._masterTT
&& Tooltip
._masterTT
.hide(aroundNode
);
13205 var Tooltip
= declare("dijit.Tooltip", _Widget
, {
13207 // Pops up a tooltip (a help message) when you hover over a node.
13208 // Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
13211 // Text to display in the tooltip.
13212 // Specified as innerHTML when creating the widget from markup.
13215 // showDelay: Integer
13216 // Number of milliseconds to wait after hovering over/focusing on the object, before
13217 // the tooltip is displayed.
13220 // connectId: String|String[]|DomNode|DomNode[]
13221 // Id of domNode(s) to attach the tooltip to.
13222 // When user hovers over specified dom node(s), the tooltip will appear.
13225 // position: String[]
13226 // See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
13229 // selector: String?
13230 // CSS expression to apply this Tooltip to descendants of connectIds, rather than to
13231 // the nodes specified by connectIds themselves. Useful for applying a Tooltip to
13232 // a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
13233 // Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
13235 // The application must require() an appropriate level of dojo/query to handle the selector.
13238 // TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
13239 // So, change connectId to a "", remove addTarget()/removeTarget(), etc.
13241 _setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId
){
13243 // Connect to specified node(s)
13245 // Remove connections to old nodes (if there are any)
13246 array
.forEach(this._connections
|| [], function(nested
){
13247 array
.forEach(nested
, function(handle
){ handle
.remove(); });
13250 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
13251 this._connectIds
= array
.filter(lang
.isArrayLike(newId
) ? newId
: (newId
? [newId
] : []),
13252 function(id
){ return dom
.byId(id
, this.ownerDocument
); }, this);
13254 // Make connections
13255 this._connections
= array
.map(this._connectIds
, function(id
){
13256 var node
= dom
.byId(id
, this.ownerDocument
),
13257 selector
= this.selector
,
13258 delegatedEvent
= selector
?
13259 function(eventType
){ return on
.selector(selector
, eventType
); } :
13260 function(eventType
){ return eventType
; },
13263 on(node
, delegatedEvent(mouse
.enter
), function(){
13264 self
._onHover(this);
13266 on(node
, delegatedEvent("focusin"), function(){
13267 self
._onHover(this);
13269 on(node
, delegatedEvent(mouse
.leave
), lang
.hitch(self
, "_onUnHover")),
13270 on(node
, delegatedEvent("focusout"), lang
.hitch(self
, "_onUnHover"))
13274 this._set("connectId", newId
);
13277 addTarget: function(/*OomNode|String*/ node
){
13279 // Attach tooltip to specified node if it's not already connected
13281 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13283 var id
= node
.id
|| node
;
13284 if(array
.indexOf(this._connectIds
, id
) == -1){
13285 this.set("connectId", this._connectIds
.concat(id
));
13289 removeTarget: function(/*DomNode|String*/ node
){
13291 // Detach tooltip from specified node
13293 // TODO: remove in 2.0 and just use set("connectId", ...) interface
13295 var id
= node
.id
|| node
, // map from DOMNode back to plain id string
13296 idx
= array
.indexOf(this._connectIds
, id
);
13298 // remove id (modifies original this._connectIds but that's OK in this case)
13299 this._connectIds
.splice(idx
, 1);
13300 this.set("connectId", this._connectIds
);
13304 buildRendering: function(){
13305 this.inherited(arguments
);
13306 domClass
.add(this.domNode
,"dijitTooltipData");
13309 startup: function(){
13310 this.inherited(arguments
);
13312 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
13313 // didn't exist during the widget's initialization, then connect now.
13314 var ids
= this.connectId
;
13315 array
.forEach(lang
.isArrayLike(ids
) ? ids
: [ids
], this.addTarget
, this);
13318 getContent: function(/*DomNode*/ node
){
13320 // User overridable function that return the text to display in the tooltip.
13323 return this.label
|| this.domNode
.innerHTML
;
13326 _onHover: function(/*DomNode*/ target
){
13328 // Despite the name of this method, it actually handles both hover and focus
13329 // events on the target node, setting a timer to show the tooltip.
13332 if(!this._showTimer
){
13333 this._showTimer
= this.defer(function(){ this.open(target
); }, this.showDelay
);
13337 _onUnHover: function(){
13339 // Despite the name of this method, it actually handles both mouseleave and blur
13340 // events on the target node, hiding the tooltip.
13344 if(this._showTimer
){
13345 this._showTimer
.remove();
13346 delete this._showTimer
;
13351 open: function(/*DomNode*/ target
){
13353 // Display the tooltip; usually not called directly.
13357 if(this._showTimer
){
13358 this._showTimer
.remove();
13359 delete this._showTimer
;
13362 var content
= this.getContent(target
);
13366 Tooltip
.show(content
, target
, this.position
, !this.isLeftToRight(), this.textDir
);
13368 this._connectNode
= target
; // _connectNode means "tooltip currently displayed for this node"
13369 this.onShow(target
, this.position
);
13374 // Hide the tooltip or cancel timer for show of tooltip
13378 if(this._connectNode
){
13379 // if tooltip is currently shown
13380 Tooltip
.hide(this._connectNode
);
13381 delete this._connectNode
;
13384 if(this._showTimer
){
13385 // if tooltip is scheduled to be shown (after a brief delay)
13386 this._showTimer
.remove();
13387 delete this._showTimer
;
13391 onShow: function(/*===== target, position =====*/){
13393 // Called when the tooltip is shown
13398 onHide: function(){
13400 // Called when the tooltip is hidden
13405 destroy: function(){
13408 // Remove connections manually since they aren't registered to be removed by _WidgetBase
13409 array
.forEach(this._connections
|| [], function(nested
){
13410 array
.forEach(nested
, function(handle
){ handle
.remove(); });
13413 this.inherited(arguments
);
13417 Tooltip
._MasterTooltip
= MasterTooltip
; // for monkey patching
13418 Tooltip
.show
= dijit
.showTooltip
; // export function through module return value
13419 Tooltip
.hide
= dijit
.hideTooltip
; // export function through module return value
13421 Tooltip
.defaultPosition
= ["after-centered", "before-centered"];
13424 lang.mixin(Tooltip, {
13425 // defaultPosition: String[]
13426 // This variable controls the position of tooltips, if the position is not specified to
13427 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
13428 // possible for `dijit/place.around()`. The recommended values are:
13430 // - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
13431 // in the case of RTL scripts like Hebrew and Arabic
13432 // - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
13433 // in the case of RTL scripts like Hebrew and Arabic
13434 // - above-centered: tooltip is centered above anchor node
13435 // - below-centered: tooltip is centered above anchor node
13437 // The list is positions is tried, in order, until a position is found where the tooltip fits
13438 // within the viewport.
13440 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
13441 // the screen so that there's no room above the target node. Nodes with drop downs, like
13442 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
13443 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
13444 // is only room below (or above) the target node, but not both.
13451 'dojo/string':function(){
13452 define("dojo/string", [
13453 "./_base/kernel", // kernel.global
13455 ], function(kernel
, lang
){
13462 // String utilities for Dojo
13464 lang
.setObject("dojo.string", string
);
13466 string
.rep = function(/*String*/str
, /*Integer*/num
){
13468 // Efficiently replicate a string `n` times.
13470 // the string to replicate
13472 // number of times to replicate the string
13474 if(num
<= 0 || !str
){ return ""; }
13481 if(!(num
>>= 1)){ break; }
13484 return buf
.join(""); // String
13487 string
.pad = function(/*String*/text
, /*Integer*/size
, /*String?*/ch
, /*Boolean?*/end
){
13489 // Pad a string to guarantee that it is at least `size` length by
13490 // filling with the character `ch` at either the start or end of the
13491 // string. Pads at the start, by default.
13493 // the string to pad
13495 // length to provide padding
13497 // character to pad, defaults to '0'
13499 // adds padding at the end if true, otherwise pads at start
13501 // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
13502 // | string.pad("Dojo", 10, "+", true);
13507 var out
= String(text
),
13508 pad
= string
.rep(ch
, Math
.ceil((size
- out
.length
) / ch
.length
));
13509 return end
? out
+ pad
: pad
+ out
; // String
13512 string
.substitute = function( /*String*/ template
,
13513 /*Object|Array*/map
,
13514 /*Function?*/ transform
,
13515 /*Object?*/ thisObject
){
13517 // Performs parameterized substitutions on a string. Throws an
13518 // exception if any parameter is unmatched.
13520 // a string with expressions in the form `${key}` to be replaced or
13521 // `${key:format}` which specifies a format function. keys are case-sensitive.
13523 // hash to search for substitutions
13525 // a function to process all parameters before substitution takes
13526 // place, e.g. mylib.encodeXML
13528 // where to look for optional format function; default to the global
13531 // Substitutes two expressions in a string from an Array or Object
13532 // | // returns "File 'foo.html' is not found in directory '/temp'."
13533 // | // by providing substitution data in an Array
13534 // | string.substitute(
13535 // | "File '${0}' is not found in directory '${1}'.",
13536 // | ["foo.html","/temp"]
13539 // | // also returns "File 'foo.html' is not found in directory '/temp'."
13540 // | // but provides substitution data in an Object structure. Dotted
13541 // | // notation may be used to traverse the structure.
13542 // | string.substitute(
13543 // | "File '${name}' is not found in directory '${info.dir}'.",
13544 // | { name: "foo.html", info: { dir: "/temp" } }
13547 // Use a transform function to modify the values:
13548 // | // returns "file 'foo.html' is not found in directory '/temp'."
13549 // | string.substitute(
13550 // | "${0} is not found in ${1}.",
13551 // | ["foo.html","/temp"],
13552 // | function(str){
13553 // | // try to figure out the type
13554 // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
13555 // | return prefix + " '" + str + "'";
13560 // | // returns "thinger -- howdy"
13561 // | string.substitute(
13562 // | "${0:postfix}", ["thinger"], null, {
13563 // | postfix: function(value, key){
13564 // | return value + " -- howdy";
13569 thisObject
= thisObject
|| kernel
.global
;
13570 transform
= transform
?
13571 lang
.hitch(thisObject
, transform
) : function(v
){ return v
; };
13573 return template
.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
13574 function(match
, key
, format
){
13575 var value
= lang
.getObject(key
, false, map
);
13577 value
= lang
.getObject(format
, false, thisObject
).call(thisObject
, value
, key
);
13579 return transform(value
, key
).toString();
13583 string
.trim
= String
.prototype.trim
?
13584 lang
.trim
: // aliasing to the native function
13586 str
= str
.replace(/^\s+/, '');
13587 for(var i
= str
.length
- 1; i
>= 0; i
--){
13588 if(/\S/.test(str
.charAt(i
))){
13589 str
= str
.substring(0, i
+ 1);
13597 string.trim = function(str){
13599 // Trims whitespace from both sides of the string
13601 // String to be trimmed
13603 // Returns the trimmed string
13605 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
13606 // The short yet performant version of this function is dojo.trim(),
13607 // which is part of Dojo base. Uses String.prototype.trim instead, if available.
13608 return ""; // String
13616 'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>",
13617 'dijit/dijit':function(){
13618 define("dijit/dijit", [
13623 "./_TemplatedMixin",
13625 "./layout/_LayoutWidget",
13626 "./form/_FormWidget",
13627 "./form/_FormValueWidget"
13628 ], function(dijit
){
13636 // A roll-up for common dijit methods
13637 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
13638 // And some other stuff that we tend to pull in all the time anyway
13646 'dijit/form/DropDownButton':function(){
13648 'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">▼</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n"}});
13649 define("dijit/form/DropDownButton", [
13650 "dojo/_base/declare", // declare
13651 "dojo/_base/lang", // hitch
13652 "dojo/query", // query
13653 "../registry", // registry.byNode
13654 "../popup", // dijit.popup2.hide
13658 "dojo/text!./templates/DropDownButton.html"
13659 ], function(declare
, lang
, query
, registry
, popup
, Button
, _Container
, _HasDropDown
, template
){
13662 // dijit/form/DropDownButton
13665 return declare("dijit.form.DropDownButton", [Button
, _Container
, _HasDropDown
], {
13667 // A button with a drop down
13670 // | <button data-dojo-type="dijit/form/DropDownButton">
13672 // | <div data-dojo-type="dijit/Menu">...</div>
13676 // | var button1 = new DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
13677 // | win.body().appendChild(button1);
13680 baseClass
: "dijitDropDownButton",
13682 templateString
: template
,
13684 _fillContent: function(){
13685 // Overrides Button._fillContent().
13687 // My inner HTML contains both the button contents and a drop down widget, like
13688 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
13689 // The first node is assumed to be the button content. The widget is the popup.
13691 if(this.srcNodeRef
){ // programatically created buttons might not define srcNodeRef
13692 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
13693 // content, not just nodes[0]
13694 var nodes
= query("*", this.srcNodeRef
);
13695 this.inherited(arguments
, [nodes
[0]]);
13697 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
13698 this.dropDownContainer
= this.srcNodeRef
;
13702 startup: function(){
13703 if(this._started
){ return; }
13705 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
13706 // make it invisible, and store a reference to pass to the popup code.
13707 if(!this.dropDown
&& this.dropDownContainer
){
13708 var dropDownNode
= query("[widgetId]", this.dropDownContainer
)[0];
13709 this.dropDown
= registry
.byNode(dropDownNode
);
13710 delete this.dropDownContainer
;
13713 popup
.hide(this.dropDown
);
13716 this.inherited(arguments
);
13719 isLoaded: function(){
13720 // Returns whether or not we are loaded - if our dropdown has an href,
13721 // then we want to check that.
13722 var dropDown
= this.dropDown
;
13723 return (!!dropDown
&& (!dropDown
.href
|| dropDown
.isLoaded
));
13726 loadDropDown: function(/*Function*/ callback
){
13727 // Default implementation assumes that drop down already exists,
13728 // but hasn't loaded it's data (ex: ContentPane w/href).
13729 // App must override if the drop down is lazy-created.
13730 var dropDown
= this.dropDown
;
13731 var handler
= dropDown
.on("load", lang
.hitch(this, function(){
13735 dropDown
.refresh(); // tell it to load
13738 isFocusable: function(){
13739 // Overridden so that focus is handled by the _HasDropDown mixin, not by
13740 // the _FormWidget mixin.
13741 return this.inherited(arguments
) && !this._mouseDown
;
13748 'dijit/form/_FormValueMixin':function(){
13749 define("dijit/form/_FormValueMixin", [
13750 "dojo/_base/declare", // declare
13751 "dojo/dom-attr", // domAttr.set
13752 "dojo/keys", // keys.ESCAPE
13753 "dojo/sniff", // has("ie"), has("quirks")
13754 "./_FormWidgetMixin"
13755 ], function(declare
, domAttr
, keys
, has
, _FormWidgetMixin
){
13758 // dijit/form/_FormValueMixin
13760 return declare("dijit.form._FormValueMixin", _FormWidgetMixin
, {
13762 // Mixin for widgets corresponding to native HTML elements such as `<input>` or `<select>`
13763 // that have user changeable values.
13765 // Each _FormValueMixin represents a single input value, and has a (possibly hidden) `<input>` element,
13766 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
13767 // works as expected.
13769 // readOnly: Boolean
13770 // Should this widget respond to user input?
13771 // In markup, this is specified as "readOnly".
13772 // Similar to disabled except readOnly form values are submitted.
13775 _setReadOnlyAttr: function(/*Boolean*/ value
){
13776 domAttr
.set(this.focusNode
, 'readOnly', value
);
13777 this._set("readOnly", value
);
13780 postCreate: function(){
13781 this.inherited(arguments
);
13783 if(has("ie")){ // IE won't stop the event with keypress
13784 this.connect(this.focusNode
|| this.domNode
, "onkeydown", this._onKeyDown
);
13786 // Update our reset value if it hasn't yet been set (because this.set()
13787 // is only called when there *is* a value)
13788 if(this._resetValue
=== undefined){
13789 this._lastValueReported
= this._resetValue
= this.value
;
13793 _setValueAttr: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
13795 // Hook so set('value', value) works.
13797 // Sets the value of the widget.
13798 // If the value has changed, then fire onChange event, unless priorityChange
13799 // is specified as null (or false?)
13800 this._handleOnChange(newValue
, priorityChange
);
13803 _handleOnChange: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
13805 // Called when the value of the widget has changed. Saves the new value in this.value,
13806 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
13807 this._set("value", newValue
);
13808 this.inherited(arguments
);
13813 // Restore the value to the last value passed to onChange
13814 this._setValueAttr(this._lastValueReported
, false);
13819 // Reset the widget's value to what it was at initialization time
13820 this._hasBeenBlurred
= false;
13821 this._setValueAttr(this._resetValue
, true);
13824 _onKeyDown: function(e
){
13825 if(e
.keyCode
== keys
.ESCAPE
&& !(e
.ctrlKey
|| e
.altKey
|| e
.metaKey
)){
13826 if(has("ie") < 9 || (has("ie") && has("quirks"))){
13827 e
.preventDefault(); // default behavior needs to be stopped here since keypress is too late
13828 var node
= e
.srcElement
,
13829 te
= node
.ownerDocument
.createEventObject();
13830 te
.keyCode
= keys
.ESCAPE
;
13831 te
.shiftKey
= e
.shiftKey
;
13832 node
.fireEvent('onkeypress', te
);
13840 'dijit/form/_FormWidgetMixin':function(){
13841 define("dijit/form/_FormWidgetMixin", [
13842 "dojo/_base/array", // array.forEach
13843 "dojo/_base/declare", // declare
13844 "dojo/dom-attr", // domAttr.set
13845 "dojo/dom-style", // domStyle.get
13846 "dojo/_base/lang", // lang.hitch lang.isArray
13847 "dojo/mouse", // mouse.isLeft
13848 "dojo/sniff", // has("webkit")
13849 "dojo/window", // winUtils.scrollIntoView
13850 "../a11y" // a11y.hasDefaultTabStop
13851 ], function(array
, declare
, domAttr
, domStyle
, lang
, mouse
, has
, winUtils
, a11y
){
13854 // dijit/form/_FormWidgetMixin
13856 return declare("dijit.form._FormWidgetMixin", null, {
13858 // Mixin for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
13859 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
13862 // Represents a single HTML element.
13863 // All these widgets should have these attributes just like native HTML input elements.
13864 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
13866 // They also share some common methods.
13868 // name: [const] String
13869 // Name used when submitting form; same as "name" attribute or plain HTML elements
13873 // Corresponds to the native HTML `<input>` element's attribute.
13877 // Corresponds to the native HTML `<input>` element's attribute.
13880 // type: [const] String
13881 // Corresponds to the native HTML `<input>` element's attribute.
13885 // Apply aria-label in markup to the widget's focusNode
13886 "aria-label": "focusNode",
13888 // tabIndex: String
13889 // Order fields are traversed when user hits the tab key
13891 _setTabIndexAttr
: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
13893 // disabled: Boolean
13894 // Should this widget respond to user input?
13895 // In markup, this is specified as "disabled='disabled'", or just "disabled".
13898 // intermediateChanges: Boolean
13899 // Fires onChange for each value change or only on demand
13900 intermediateChanges
: false,
13902 // scrollOnFocus: Boolean
13903 // On focus, should this widget scroll into view?
13904 scrollOnFocus
: true,
13906 // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
13907 // works with screen reader
13908 _setIdAttr
: "focusNode",
13910 _setDisabledAttr: function(/*Boolean*/ value
){
13911 this._set("disabled", value
);
13912 domAttr
.set(this.focusNode
, 'disabled', value
);
13913 if(this.valueNode
){
13914 domAttr
.set(this.valueNode
, 'disabled', value
);
13916 this.focusNode
.setAttribute("aria-disabled", value
? "true" : "false");
13919 // reset these, because after the domNode is disabled, we can no longer receive
13920 // mouse related events, see #4200
13921 this._set("hovering", false);
13922 this._set("active", false);
13924 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
13925 var attachPointNames
= "tabIndex" in this.attributeMap
? this.attributeMap
.tabIndex
:
13926 ("_setTabIndexAttr" in this) ? this._setTabIndexAttr
: "focusNode";
13927 array
.forEach(lang
.isArray(attachPointNames
) ? attachPointNames
: [attachPointNames
], function(attachPointName
){
13928 var node
= this[attachPointName
];
13929 // complex code because tabIndex=-1 on a <div> doesn't work on FF
13930 if(has("webkit") || a11y
.hasDefaultTabStop(node
)){ // see #11064 about webkit bug
13931 node
.setAttribute('tabIndex', "-1");
13933 node
.removeAttribute('tabIndex');
13937 if(this.tabIndex
!= ""){
13938 this.set('tabIndex', this.tabIndex
);
13943 _onFocus: function(/*String*/ by
){
13944 // If user clicks on the widget, even if the mouse is released outside of it,
13945 // this widget's focusNode should get focus (to mimic native browser hehavior).
13946 // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
13947 if(by
== "mouse" && this.isFocusable()){
13948 // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
13949 var focusConnector
= this.connect(this.focusNode
, "onfocus", function(){
13950 this.disconnect(mouseUpConnector
);
13951 this.disconnect(focusConnector
);
13953 // Set a global event to handle mouseup, so it fires properly
13954 // even if the cursor leaves this.domNode before the mouse up event.
13955 var mouseUpConnector
= this.connect(this.ownerDocumentBody
, "onmouseup", function(){
13956 this.disconnect(mouseUpConnector
);
13957 this.disconnect(focusConnector
);
13958 // if here, then the mousedown did not focus the focusNode as the default action
13964 if(this.scrollOnFocus
){
13965 this.defer(function(){ winUtils
.scrollIntoView(this.domNode
); }); // without defer, the input caret position can change on mouse click
13967 this.inherited(arguments
);
13970 isFocusable: function(){
13972 // Tells if this widget is focusable or not. Used internally by dijit.
13975 return !this.disabled
&& this.focusNode
&& (domStyle
.get(this.domNode
, "display") != "none");
13980 // Put focus on this widget
13981 if(!this.disabled
&& this.focusNode
.focus
){
13982 try{ this.focusNode
.focus(); }catch(e
){}/*squelch errors from hidden nodes*/
13986 compare: function(/*anything*/ val1
, /*anything*/ val2
){
13988 // Compare 2 values (as returned by get('value') for this widget).
13991 if(typeof val1
== "number" && typeof val2
== "number"){
13992 return (isNaN(val1
) && isNaN(val2
)) ? 0 : val1
- val2
;
13993 }else if(val1
> val2
){
13995 }else if(val1
< val2
){
14002 onChange: function(/*===== newValue =====*/){
14004 // Callback when this widget's value is changed.
14009 // _onChangeActive: [private] Boolean
14010 // Indicates that changes to the value should call onChange() callback.
14011 // This is false during widget initialization, to avoid calling onChange()
14012 // when the initial value is set.
14013 _onChangeActive
: false,
14015 _handleOnChange: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
14017 // Called when the value of the widget is set. Calls onChange() if appropriate
14021 // For a slider, for example, dragging the slider is priorityChange==false,
14022 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
14023 // onChange is only called form priorityChange=true events.
14026 if(this._lastValueReported
== undefined && (priorityChange
=== null || !this._onChangeActive
)){
14027 // this block executes not for a change, but during initialization,
14028 // and is used to store away the original value (or for ToggleButton, the original checked state)
14029 this._resetValue
= this._lastValueReported
= newValue
;
14031 this._pendingOnChange
= this._pendingOnChange
14032 || (typeof newValue
!= typeof this._lastValueReported
)
14033 || (this.compare(newValue
, this._lastValueReported
) != 0);
14034 if((this.intermediateChanges
|| priorityChange
|| priorityChange
=== undefined) && this._pendingOnChange
){
14035 this._lastValueReported
= newValue
;
14036 this._pendingOnChange
= false;
14037 if(this._onChangeActive
){
14038 if(this._onChangeHandle
){
14039 this._onChangeHandle
.remove();
14041 // defer allows hidden value processing to run and
14042 // also the onChange handler can safely adjust focus, etc
14043 this._onChangeHandle
= this.defer(
14045 this._onChangeHandle
= null;
14046 this.onChange(newValue
);
14047 }); // try to collapse multiple onChange's fired faster than can be processed
14052 create: function(){
14053 // Overrides _Widget.create()
14054 this.inherited(arguments
);
14055 this._onChangeActive
= true;
14058 destroy: function(){
14059 if(this._onChangeHandle
){ // destroy called before last onChange has fired
14060 this._onChangeHandle
.remove();
14061 this.onChange(this._lastValueReported
);
14063 this.inherited(arguments
);
14070 'dijit/a11yclick':function(){
14071 define("dijit/a11yclick", [
14073 "dojo/_base/array", // array.forEach
14074 "dojo/keys", // keys.ENTER keys.SPACE
14075 "dojo/_base/declare", // declare
14076 "dojo/has", // has("dom-addeventlistener")
14077 "dojo/_base/unload", // unload.addOnWindowUnload
14078 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
14079 ], function(on
, array
, keys
, declare
, has
, unload
, win
){
14084 // Keep track of where the last keydown event was, to help avoid generating
14085 // spurious ondijitclick events when:
14086 // 1. focus is on a <button> or <a>
14087 // 2. user presses then releases the ENTER key
14088 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
14089 // 4. onkeyup event fires, causing the ondijitclick handler to fire
14090 var lastKeyDownNode
= null;
14091 if(has("dom-addeventlistener")){
14092 win
.doc
.addEventListener('keydown', function(evt
){
14093 lastKeyDownNode
= evt
.target
;
14096 // Fallback path for IE6-8
14098 var keydownCallback = function(evt
){
14099 lastKeyDownNode
= evt
.srcElement
;
14101 win
.doc
.attachEvent('onkeydown', keydownCallback
);
14102 unload
.addOnWindowUnload(function(){
14103 win
.doc
.detachEvent('onkeydown', keydownCallback
);
14108 function clickKey(/*Event*/ e
){
14109 return (e
.keyCode
=== keys
.ENTER
|| e
.keyCode
=== keys
.SPACE
) &&
14110 !e
.ctrlKey
&& !e
.shiftKey
&& !e
.altKey
&& !e
.metaKey
;
14113 return function(node
, listener
){
14115 // Custom a11yclick (a.k.a. ondijitclick) event
14116 // which triggers on a mouse click, touch, or space/enter keyup.
14118 if(/input|button/i.test(node
.nodeName
)){
14119 // pass through, the browser already generates click event on SPACE/ENTER key
14120 return on(node
, "click", listener
);
14122 // Don't fire the click event unless both the keydown and keyup occur on this node.
14123 // Avoids problems where focus shifted to this node or away from the node on keydown,
14124 // either causing this node to process a stray keyup event, or causing another node
14125 // to get a stray keyup event.
14128 on(node
, "keydown", function(e
){
14129 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14131 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
14132 lastKeyDownNode
= e
.target
;
14134 // Prevent viewport scrolling on space key in IE<9.
14135 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
14136 e
.preventDefault();
14140 on(node
, "keyup", function(e
){
14141 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
14142 if(clickKey(e
) && e
.target
== lastKeyDownNode
){ // === breaks greasemonkey
14143 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
14144 lastKeyDownNode
= null;
14145 on
.emit(e
.target
, "click", {
14152 on(node
, "click", function(e
){
14153 // catch mouse clicks, plus the on.emit() calls from above and below
14154 listener
.call(this, e
);
14159 // touchstart-->touchend will automatically generate a click event, but there are problems
14160 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
14161 // if click doesn't fire naturally.
14165 on(node
, "touchend", function(e
){
14166 var target
= e
.target
;
14167 clickTimer
= setTimeout(function(){
14169 on
.emit(target
, "click", {
14175 on(node
, "click", function(e
){
14176 // If browser generates a click naturally, clear the timer to fire a synthetic click event
14178 clearTimeout(clickTimer
);
14181 // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
14182 // event <300ms after the touchend event, then clear the synthetic click timer, because user
14183 // is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
14184 // zoom level has changed.
14189 remove: function(){
14190 array
.forEach(handles
, function(h
){ h
.remove(); });
14192 clearTimeout(clickTimer
);
14204 'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\"> </span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
14205 'dijit/Destroyable':function(){
14206 define("dijit/Destroyable", [
14207 "dojo/_base/array", // array.forEach array.map
14209 "dojo/_base/declare"
14210 ], function(array
, aspect
, declare
){
14213 // dijit/Destroyable
14215 return declare("dijit.Destroyable", null, {
14217 // Mixin to track handles and release them when instance is destroyed.
14219 // Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
14220 // dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
14221 // Then call destroy() later to destroy this instance and release the resources.
14223 destroy: function(/*Boolean*/ preserveDom
){
14225 // Destroy this class, releasing any resources registered via own().
14226 this._destroyed
= true;
14231 // Track specified handles and remove/destroy them when this instance is destroyed, unless they were
14232 // already removed/destroyed manually.
14236 // The array of specified handles, so you can do for example:
14237 // | var handle = this.own(on(...))[0];
14239 array
.forEach(arguments
, function(handle
){
14240 var destroyMethodName
=
14241 "destroyRecursive" in handle
? "destroyRecursive" : // remove "destroyRecursive" for 2.0
14242 "destroy" in handle
? "destroy" :
14245 // When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
14246 // the handle will be destroyed before a subclass's destroy() method starts running, before it calls
14247 // this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
14248 // onDestroy() method and connect to that instead.
14249 var odh
= aspect
.before(this, "destroy", function(preserveDom
){
14250 handle
[destroyMethodName
](preserveDom
);
14253 // If handle is destroyed manually before this.destroy() is called, remove the listener set directly above.
14254 var hdh
= aspect
.after(handle
, destroyMethodName
, function(){
14260 return arguments
; // handle
14267 'dijit/layout/_ContentPaneResizeMixin':function(){
14268 define("dijit/layout/_ContentPaneResizeMixin", [
14269 "dojo/_base/array", // array.filter array.forEach
14270 "dojo/_base/declare", // declare
14271 "dojo/dom-class", // domClass.contains domClass.toggle
14272 "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
14274 "dojo/_base/lang", // lang.mixin
14275 "dojo/query", // query
14276 "dojo/sniff", // has("ie")
14277 "../registry", // registry.byId
14279 "./utils" // marginBox2contextBox
14280 ], function(array
, declare
, domClass
, domGeometry
, domStyle
, lang
, query
, has
,
14281 registry
, Viewport
, layoutUtils
){
14284 // dijit/layout/_ContentPaneResizeMixin
14287 return declare("dijit.layout._ContentPaneResizeMixin", null, {
14289 // Resize() functionality of ContentPane. If there's a single layout widget
14290 // child then it will call resize() with the same dimensions as the ContentPane.
14291 // Otherwise just calls resize on each child.
14293 // Also implements basic startup() functionality, where starting the parent
14294 // will start the children
14296 // doLayout: Boolean
14297 // - false - don't adjust size of children
14298 // - true - if there is a single visible child widget, set it's size to however big the ContentPane is
14301 // isLayoutContainer: [protected] Boolean
14302 // Indicates that this widget will call resize() on it's child widgets
14303 // when they become visible.
14304 isLayoutContainer
: true,
14306 startup: function(){
14308 // See `dijit/layout/_LayoutWidget.startup()` for description.
14309 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14312 if(this._started
){ return; }
14314 var parent
= this.getParent();
14315 this._childOfLayoutWidget
= parent
&& parent
.isLayoutContainer
;
14317 // I need to call resize() on my child/children (when I become visible), unless
14318 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
14319 this._needLayout
= !this._childOfLayoutWidget
;
14321 this.inherited(arguments
);
14323 if(this._isShown()){
14327 if(!this._childOfLayoutWidget
){
14328 // Since my parent isn't a layout container, and my style *may be* width=height=100%
14329 // or something similar (either set directly or via a CSS class),
14330 // monitor when viewport size changes so that I can re-layout.
14331 // This is more for subclasses of ContentPane than ContentPane itself, although it
14332 // could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size.
14333 this.own(Viewport
.on("resize", lang
.hitch(this, "resize")));
14337 _checkIfSingleChild: function(){
14339 // Test if we have exactly one visible widget as a child,
14340 // and if so assume that we are a container for that widget,
14341 // and should propagate startup() and resize() calls to it.
14342 // Skips over things like data stores since they aren't visible.
14344 var candidateWidgets
= [],
14345 otherVisibleNodes
= false;
14347 query("> *", this.containerNode
).some(function(node
){
14348 var widget
= registry
.byNode(node
);
14349 if(widget
&& widget
.resize
){
14350 candidateWidgets
.push(widget
);
14351 }else if(node
.offsetHeight
){
14352 otherVisibleNodes
= true;
14356 this._singleChild
= candidateWidgets
.length
== 1 && !otherVisibleNodes
?
14357 candidateWidgets
[0] : null;
14359 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
14360 domClass
.toggle(this.containerNode
, this.baseClass
+ "SingleChild", !!this._singleChild
);
14363 resize: function(changeSize
, resultSize
){
14365 // See `dijit/layout/_LayoutWidget.resize()` for description.
14366 // Although ContentPane doesn't extend _LayoutWidget, it does implement
14369 this._resizeCalled
= true;
14371 this._scheduleLayout(changeSize
, resultSize
);
14374 _scheduleLayout: function(changeSize
, resultSize
){
14376 // Resize myself, and call resize() on each of my child layout widgets, either now
14377 // (if I'm currently visible) or when I become visible
14378 if(this._isShown()){
14379 this._layout(changeSize
, resultSize
);
14381 this._needLayout
= true;
14382 this._changeSize
= changeSize
;
14383 this._resultSize
= resultSize
;
14387 _layout: function(changeSize
, resultSize
){
14389 // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
14390 // Also, since I am an isLayoutContainer widget, each of my children expects me to
14391 // call resize() or layout() on it.
14393 // Should be called on initialization and also whenever we get new content
14394 // (from an href, or from set('content', ...))... but deferred until
14395 // the ContentPane is visible
14397 delete this._needLayout
;
14399 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
14400 // never called directly, so resize() is our trigger to do the initial href download (see [20099]).
14401 // However, don't load href for closed TitlePanes.
14402 if(!this._wasShown
&& this.open
!== false){
14406 // Set margin box size, unless it wasn't specified, in which case use current size.
14408 domGeometry
.setMarginBox(this.domNode
, changeSize
);
14411 // Compute content box size of containerNode in case we [later] need to size our single child.
14412 var cn
= this.containerNode
;
14413 if(cn
=== this.domNode
){
14414 // If changeSize or resultSize was passed to this method and this.containerNode ==
14415 // this.domNode then we can compute the content-box size without querying the node,
14416 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
14417 var mb
= resultSize
|| {};
14418 lang
.mixin(mb
, changeSize
|| {}); // changeSize overrides resultSize
14419 if(!("h" in mb
) || !("w" in mb
)){
14420 mb
= lang
.mixin(domGeometry
.getMarginBox(cn
), mb
); // just use domGeometry.setMarginBox() to fill in missing values
14422 this._contentBox
= layoutUtils
.marginBox2contentBox(cn
, mb
);
14424 this._contentBox
= domGeometry
.getContentBox(cn
);
14427 this._layoutChildren();
14430 _layoutChildren: function(){
14431 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
14432 // of the ContentPane (rather than changing it through the set("content", ...) API.
14434 this._checkIfSingleChild();
14437 if(this._singleChild
&& this._singleChild
.resize
){
14438 var cb
= this._contentBox
|| domGeometry
.getContentBox(this.containerNode
);
14440 // note: if widget has padding this._contentBox will have l and t set,
14441 // but don't pass them to resize() or it will doubly-offset the child
14442 this._singleChild
.resize({w
: cb
.w
, h
: cb
.h
});
14444 // All my child widgets are independently sized (rather than matching my size),
14445 // but I still need to call resize() on each child to make it layout.
14446 array
.forEach(this.getChildren(), function(widget
){
14454 _isShown: function(){
14456 // Returns true if the content is currently shown.
14458 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
14459 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
14460 // tree every call, and at least solves the performance problem on page load by deferring loading
14461 // hidden ContentPanes until they are first shown
14463 if(this._childOfLayoutWidget
){
14464 // If we are TitlePane, etc - we return that only *IF* we've been resized
14465 if(this._resizeCalled
&& "open" in this){
14468 return this._resizeCalled
;
14469 }else if("open" in this){
14470 return this.open
; // for TitlePane, etc.
14472 var node
= this.domNode
, parent
= this.domNode
.parentNode
;
14473 return (node
.style
.display
!= 'none') && (node
.style
.visibility
!= 'hidden') && !domClass
.contains(node
, "dijitHidden") &&
14474 parent
&& parent
.style
&& (parent
.style
.display
!= 'none');
14478 _onShow: function(){
14480 // Called when the ContentPane is made visible
14482 // For a plain ContentPane, this is called on initialization, from startup().
14483 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
14484 // called whenever the pane is made visible.
14486 // Does layout/resize of child widget(s)
14488 // Need to keep track of whether ContentPane has been shown (which is different than
14489 // whether or not it's currently visible).
14490 this._wasShown
= true;
14492 if(this._needLayout
){
14493 // If a layout has been scheduled for when we become visible, do it now
14494 this._layout(this._changeSize
, this._resultSize
);
14497 this.inherited(arguments
);
14504 'dijit/WidgetSet':function(){
14505 define("dijit/WidgetSet", [
14506 "dojo/_base/array", // array.forEach array.map
14507 "dojo/_base/declare", // declare
14508 "dojo/_base/kernel", // kernel.global
14509 "./registry" // to add functions to dijit.registry
14510 ], function(array
, declare
, kernel
, registry
){
14515 var WidgetSet
= declare("dijit.WidgetSet", null, {
14517 // A set of widgets indexed by id.
14518 // Deprecated, will be removed in 2.0.
14521 // Create a small list of widgets:
14522 // | require(["dijit/WidgetSet", "dijit/registry"],
14523 // | function(WidgetSet, registry){
14524 // | var ws = new WidgetSet();
14525 // | ws.add(registry.byId("one"));
14526 // | ws.add(registry.byId("two"));
14527 // | // destroy both:
14528 // | ws.forEach(function(w){ w.destroy(); });
14531 constructor: function(){
14536 add: function(/*dijit/_WidgetBase*/ widget){
14538 // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
14540 // widget: dijit/_WidgetBase
14541 // Any dijit/_WidgetBase subclass.
14542 if(this._hash[widget.id]){
14543 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
14545 this._hash[widget.id] = widget;
14549 remove: function(/*String*/ id
){
14551 // Remove a widget from this WidgetSet. Does not destroy the widget; simply
14552 // removes the reference.
14553 if(this._hash
[id
]){
14554 delete this._hash
[id
];
14559 forEach: function(/*Function*/ func
, /* Object? */thisObj
){
14561 // Call specified function for each widget in this set.
14564 // A callback function to run for each item. Is passed the widget, the index
14565 // in the iteration, and the full hash, similar to `array.forEach`.
14568 // An optional scope parameter
14571 // Using the default `dijit.registry` instance:
14572 // | require(["dijit/WidgetSet", "dijit/registry"],
14573 // | function(WidgetSet, registry){
14574 // | registry.forEach(function(widget){
14575 // | console.log(widget.declaredClass);
14580 // Returns self, in order to allow for further chaining.
14582 thisObj
= thisObj
|| kernel
.global
;
14584 for(id
in this._hash
){
14585 func
.call(thisObj
, this._hash
[id
], i
++, this._hash
);
14587 return this; // dijit/WidgetSet
14590 filter: function(/*Function*/ filter
, /* Object? */thisObj
){
14592 // Filter down this WidgetSet to a smaller new WidgetSet
14593 // Works the same as `array.filter` and `NodeList.filter`
14596 // Callback function to test truthiness. Is passed the widget
14597 // reference and the pseudo-index in the object.
14599 // thisObj: Object?
14600 // Option scope to use for the filter function.
14603 // Arbitrary: select the odd widgets in this list
14607 // | require(["dijit/WidgetSet", "dijit/registry"],
14608 // | function(WidgetSet, registry){
14609 // | registry.filter(function(w, i){
14610 // | return i % 2 == 0;
14611 // | }).forEach(function(w){ /* odd ones */ });
14614 thisObj
= thisObj
|| kernel
.global
;
14615 var res
= new WidgetSet(), i
= 0, id
;
14616 for(id
in this._hash
){
14617 var w
= this._hash
[id
];
14618 if(filter
.call(thisObj
, w
, i
++, this._hash
)){
14622 return res
; // dijit/WidgetSet
14625 byId: function(/*String*/ id
){
14627 // Find a widget in this list by it's id.
14629 // Test if an id is in a particular WidgetSet
14630 // | require(["dijit/WidgetSet", "dijit/registry"],
14631 // | function(WidgetSet, registry){
14632 // | var ws = new WidgetSet();
14633 // | ws.add(registry.byId("bar"));
14634 // | var t = ws.byId("bar") // returns a widget
14635 // | var x = ws.byId("foo"); // returns undefined
14638 return this._hash
[id
]; // dijit/_WidgetBase
14641 byClass: function(/*String*/ cls
){
14643 // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
14646 // The Class to scan for. Full dot-notated string.
14649 // Find all `dijit.TitlePane`s in a page:
14650 // | require(["dijit/WidgetSet", "dijit/registry"],
14651 // | function(WidgetSet, registry){
14652 // | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
14655 var res
= new WidgetSet(), id
, widget
;
14656 for(id
in this._hash
){
14657 widget
= this._hash
[id
];
14658 if(widget
.declaredClass
== cls
){
14662 return res
; // dijit/WidgetSet
14665 toArray: function(){
14667 // Convert this WidgetSet into a true Array
14670 // Work with the widget .domNodes in a real Array
14671 // | require(["dijit/WidgetSet", "dijit/registry"],
14672 // | function(WidgetSet, registry){
14673 // | array.map(registry.toArray(), function(w){ return w.domNode; });
14678 for(var id
in this._hash
){
14679 ar
.push(this._hash
[id
]);
14681 return ar
; // dijit/_WidgetBase[]
14684 map: function(/* Function */func
, /* Object? */thisObj
){
14686 // Create a new Array from this WidgetSet, following the same rules as `array.map`
14688 // | require(["dijit/WidgetSet", "dijit/registry"],
14689 // | function(WidgetSet, registry){
14690 // | var nodes = registry.map(function(w){ return w.domNode; });
14694 // A new array of the returned values.
14695 return array
.map(this.toArray(), func
, thisObj
); // Array
14698 every: function(func
, thisObj
){
14700 // A synthetic clone of `array.every` acting explicitly on this WidgetSet
14703 // A callback function run for every widget in this list. Exits loop
14704 // when the first false return is encountered.
14706 // thisObj: Object?
14707 // Optional scope parameter to use for the callback
14709 thisObj
= thisObj
|| kernel
.global
;
14711 for(i
in this._hash
){
14712 if(!func
.call(thisObj
, this._hash
[i
], x
++, this._hash
)){
14713 return false; // Boolean
14716 return true; // Boolean
14719 some: function(func
, thisObj
){
14721 // A synthetic clone of `array.some` acting explicitly on this WidgetSet
14724 // A callback function run for every widget in this list. Exits loop
14725 // when the first true return is encountered.
14727 // thisObj: Object?
14728 // Optional scope parameter to use for the callback
14730 thisObj
= thisObj
|| kernel
.global
;
14732 for(i
in this._hash
){
14733 if(func
.call(thisObj
, this._hash
[i
], x
++, this._hash
)){
14734 return true; // Boolean
14737 return false; // Boolean
14742 // Add in 1.x compatibility methods to dijit/registry.
14743 // These functions won't show up in the API doc but since they are deprecated anyway,
14744 // that's probably for the best.
14745 array
.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func
){
14746 registry
[func
] = WidgetSet
.prototype[func
];
14754 'dojo/dnd/Moveable':function(){
14755 define("dojo/dnd/Moveable", [
14756 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang",
14757 "../dom", "../dom-class", "../Evented", "../on", "../topic", "../touch", "./common", "./Mover", "../_base/window"
14758 ], function(array
, declare
, event
, lang
, dom
, domClass
, Evented
, on
, topic
, touch
, dnd
, Mover
, win
){
14761 // dojo/dnd/Moveable
14764 var Moveable
= declare("dojo.dnd.Moveable", [Evented
], {
14766 // an object, which makes a node movable
14768 // object attributes (for markup)
14773 constructor: function(node
, params
){
14775 // a node (or node's id) to be moved
14776 // params: Moveable.__MoveableArgs?
14777 // optional parameters
14778 this.node
= dom
.byId(node
);
14779 if(!params
){ params
= {}; }
14780 this.handle
= params
.handle
? dom
.byId(params
.handle
) : null;
14781 if(!this.handle
){ this.handle
= this.node
; }
14782 this.delay
= params
.delay
> 0 ? params
.delay
: 0;
14783 this.skip
= params
.skip
;
14784 this.mover
= params
.mover
? params
.mover
: Mover
;
14786 on(this.handle
, touch
.press
, lang
.hitch(this, "onMouseDown")),
14787 // cancel text selection and text dragging
14788 on(this.handle
, "dragstart", lang
.hitch(this, "onSelectStart")),
14789 on(this.handle
, "selectstart", lang
.hitch(this, "onSelectStart"))
14794 markupFactory: function(params
, node
, Ctor
){
14795 return new Ctor(node
, params
);
14799 destroy: function(){
14801 // stops watching for possible move, deletes all references, so the object can be garbage-collected
14802 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
14803 this.events
= this.node
= this.handle
= null;
14806 // mouse event processors
14807 onMouseDown: function(e
){
14809 // event processor for onmousedown/ontouchstart, creates a Mover for the node
14811 // mouse/touch event
14812 if(this.skip
&& dnd
.isFormElement(e
)){ return; }
14815 on(this.handle
, touch
.move, lang
.hitch(this, "onMouseMove")),
14816 on(this.handle
, touch
.release
, lang
.hitch(this, "onMouseUp"))
14818 this._lastX
= e
.pageX
;
14819 this._lastY
= e
.pageY
;
14821 this.onDragDetected(e
);
14825 onMouseMove: function(e
){
14827 // event processor for onmousemove/ontouchmove, used only for delayed drags
14829 // mouse/touch event
14830 if(Math
.abs(e
.pageX
- this._lastX
) > this.delay
|| Math
.abs(e
.pageY
- this._lastY
) > this.delay
){
14832 this.onDragDetected(e
);
14836 onMouseUp: function(e
){
14838 // event processor for onmouseup, used only for delayed drags
14841 for(var i
= 0; i
< 2; ++i
){
14842 this.events
.pop().remove();
14846 onSelectStart: function(e
){
14848 // event processor for onselectevent and ondragevent
14851 if(!this.skip
|| !dnd
.isFormElement(e
)){
14857 onDragDetected: function(/*Event*/ e
){
14859 // called when the drag is detected;
14860 // responsible for creation of the mover
14861 new this.mover(this.node
, e
, this);
14863 onMoveStart: function(/*Mover*/ mover
){
14865 // called before every move operation
14866 topic
.publish("/dnd/move/start", mover
);
14867 domClass
.add(win
.body(), "dojoMove");
14868 domClass
.add(this.node
, "dojoMoveItem");
14870 onMoveStop: function(/*Mover*/ mover
){
14872 // called after every move operation
14873 topic
.publish("/dnd/move/stop", mover
);
14874 domClass
.remove(win
.body(), "dojoMove");
14875 domClass
.remove(this.node
, "dojoMoveItem");
14877 onFirstMove: function(/*===== mover, e =====*/){
14879 // called during the very first move notification;
14880 // can be used to initialize coordinates, can be overwritten.
14884 // default implementation does nothing
14886 onMove: function(mover
, leftTop
/*=====, e =====*/){
14888 // called during every move notification;
14889 // should actually move the node; can be overwritten.
14893 this.onMoving(mover
, leftTop
);
14894 var s
= mover
.node
.style
;
14895 s
.left
= leftTop
.l
+ "px";
14896 s
.top
= leftTop
.t
+ "px";
14897 this.onMoved(mover
, leftTop
);
14899 onMoving: function(/*===== mover, leftTop =====*/){
14901 // called before every incremental move; can be overwritten.
14905 // default implementation does nothing
14907 onMoved: function(/*===== mover, leftTop =====*/){
14909 // called after every incremental move; can be overwritten.
14913 // default implementation does nothing
14918 Moveable.__MoveableArgs = declare([], {
14919 // handle: Node||String
14920 // A node (or node's id), which is used as a mouse handle.
14921 // If omitted, the node itself is used as a handle.
14925 // delay move by this number of pixels
14929 // skip move of form elements
14933 // a constructor of custom Mover
14942 'dojo/store/util/SimpleQueryEngine':function(){
14943 define("dojo/store/util/SimpleQueryEngine", ["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil
/*=====, Store =====*/){
14946 // dojo/store/util/SimpleQueryEngine
14948 return function(query
, options
){
14950 // Simple query engine that matches using filter functions, named filter
14951 // functions or objects by name-value on a query object hash
14954 // The SimpleQueryEngine provides a way of getting a QueryResults through
14955 // the use of a simple object hash as a filter. The hash will be used to
14956 // match properties on data objects with the corresponding value given. In
14957 // other words, only exact matches will be returned.
14959 // This function can be used as a template for more complex query engines;
14960 // for example, an engine can be created that accepts an object hash that
14961 // contains filtering functions, or a string that gets evaluated, etc.
14963 // When creating a new dojo.store, simply set the store's queryEngine
14964 // field as a reference to this function.
14967 // An object hash with fields that may match fields of items in the store.
14968 // Values in the hash will be compared by normal == operator, but regular expressions
14969 // or any object that provides a test() method are also supported and can be
14970 // used to match strings by more complex expressions
14971 // (and then the regex's or object's test() method will be used to match values).
14973 // options: dojo/store/api/Store.QueryOptions?
14974 // An object that contains optional information such as sort, start, and count.
14976 // returns: Function
14977 // A function that caches the passed query under the field "matches". See any
14978 // of the "query" methods on dojo.stores.
14981 // Define a store with a reference to this engine, and set up a query method.
14983 // | var myStore = function(options){
14984 // | // ...more properties here
14985 // | this.queryEngine = SimpleQueryEngine;
14986 // | // define our query method
14987 // | this.query = function(query, options){
14988 // | return QueryResults(this.queryEngine(query, options)(this.data));
14992 // create our matching query function
14993 switch(typeof query
){
14995 throw new Error("Can not query with a " + typeof query
);
14996 case "object": case "undefined":
14997 var queryObject
= query
;
14998 query = function(object
){
14999 for(var key
in queryObject
){
15000 var required
= queryObject
[key
];
15001 if(required
&& required
.test
){
15002 // an object can provide a test method, which makes it work with regex
15003 if(!required
.test(object
[key
], object
)){
15006 }else if(required
!= object
[key
]){
15016 throw new Error("No filter function " + query
+ " was found in store");
15018 query
= this[query
];
15023 function execute(array
){
15024 // execute the whole query, first we filter
15025 var results
= arrayUtil
.filter(array
, query
);
15027 var sortSet
= options
&& options
.sort
;
15029 results
.sort(typeof sortSet
== "function" ? sortSet : function(a
, b
){
15030 for(var sort
, i
=0; sort
= sortSet
[i
]; i
++){
15031 var aValue
= a
[sort
.attribute
];
15032 var bValue
= b
[sort
.attribute
];
15033 if (aValue
!= bValue
){
15034 return !!sort
.descending
== (aValue
== null || aValue
> bValue
) ? -1 : 1;
15041 if(options
&& (options
.start
|| options
.count
)){
15042 var total
= results
.length
;
15043 results
= results
.slice(options
.start
|| 0, (options
.start
|| 0) + (options
.count
|| Infinity
));
15044 results
.total
= total
;
15048 execute
.matches
= query
;
15055 'dijit/typematic':function(){
15056 define("dijit/typematic", [
15057 "dojo/_base/array", // array.forEach
15058 "dojo/_base/connect", // connect.connect
15059 "dojo/_base/event", // event.stop
15060 "dojo/_base/kernel", // kernel.deprecated
15061 "dojo/_base/lang", // lang.mixin, lang.hitch
15063 "dojo/sniff", // has("ie")
15064 "./main" // setting dijit.typematic global
15065 ], function(array
, connect
, event
, kernel
, lang
, on
, has
, dijit
){
15070 var typematic
= (dijit
.typematic
= {
15072 // These functions are used to repetitively call a user specified callback
15073 // method when a specific key or mouse click over a specific DOM node is
15074 // held down for a specific amount of time.
15075 // Only 1 such event is allowed to occur on the browser page at 1 time.
15077 _fireEventAndReload: function(){
15078 this._timer
= null;
15079 this._callback(++this._count
, this._node
, this._evt
);
15081 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
15082 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
15083 this._currentTimeout
= Math
.max(
15084 this._currentTimeout
< 0 ? this._initialDelay
:
15085 (this._subsequentDelay
> 1 ? this._subsequentDelay
: Math
.round(this._currentTimeout
* this._subsequentDelay
)),
15087 this._timer
= setTimeout(lang
.hitch(this, "_fireEventAndReload"), this._currentTimeout
);
15090 trigger: function(/*Event*/ evt
, /*Object*/ _this
, /*DOMNode*/ node
, /*Function*/ callback
, /*Object*/ obj
, /*Number?*/ subsequentDelay
, /*Number?*/ initialDelay
, /*Number?*/ minDelay
){
15092 // Start a timed, repeating callback sequence.
15093 // If already started, the function call is ignored.
15094 // This method is not normally called by the user but can be
15095 // when the normal listener code is insufficient.
15097 // key or mouse event object to pass to the user callback
15099 // pointer to the user's widget space.
15101 // the DOM node object to pass the the callback function
15103 // function to call until the sequence is stopped called with 3 parameters:
15105 // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
15107 // the DOM node object passed in
15109 // key or mouse event object
15111 // user space object used to uniquely identify each typematic sequence
15112 // subsequentDelay:
15113 // if > 1, the number of milliseconds until the 3->n events occur
15114 // or else the fractional time multiplier for the next event's delay, default=0.9
15116 // the number of milliseconds until the 2nd event occurs, default=500ms
15118 // the maximum delay in milliseconds for event to fire, default=10ms
15119 if(obj
!= this._obj
){
15121 this._initialDelay
= initialDelay
|| 500;
15122 this._subsequentDelay
= subsequentDelay
|| 0.90;
15123 this._minDelay
= minDelay
|| 10;
15126 this._currentTimeout
= -1;
15128 this._callback
= lang
.hitch(_this
, callback
);
15129 this._evt
= { faux
: true };
15130 for(var attr
in evt
){
15131 if(attr
!= "layerX" && attr
!= "layerY"){ // prevent WebKit warnings
15133 if(typeof v
!= "function" && typeof v
!= "undefined"){ this._evt
[attr
] = v
}
15136 this._fireEventAndReload();
15142 // Stop an ongoing timed, repeating callback sequence.
15144 clearTimeout(this._timer
);
15145 this._timer
= null;
15148 this._callback(-1, this._node
, this._evt
);
15153 addKeyListener: function(/*DOMNode*/ node
, /*Object*/ keyObject
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15155 // Start listening for a specific typematic key.
15156 // See also the trigger method for other parameters.
15158 // an object defining the key to listen for:
15160 // - charOrCode: the printable character (string) or keyCode (number) to listen for.
15161 // - keyCode: (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
15162 // - charCode: (deprecated - use charOrCode) the charCode (number) to listen for.
15163 // - ctrlKey: desired ctrl key state to initiate the callback sequence:
15164 // - pressed (true)
15165 // - released (false)
15166 // - either (unspecified)
15167 // - altKey: same as ctrlKey but for the alt key
15168 // - shiftKey: same as ctrlKey but for the shift key
15170 // a connection handle
15172 if(keyObject
.keyCode
){
15173 keyObject
.charOrCode
= keyObject
.keyCode
;
15174 kernel
.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15175 }else if(keyObject
.charCode
){
15176 keyObject
.charOrCode
= String
.fromCharCode(keyObject
.charCode
);
15177 kernel
.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
15180 on(node
, connect
._keypress
, lang
.hitch(this, function(evt
){
15181 if(evt
.charOrCode
== keyObject
.charOrCode
&&
15182 (keyObject
.ctrlKey
=== undefined || keyObject
.ctrlKey
== evt
.ctrlKey
) &&
15183 (keyObject
.altKey
=== undefined || keyObject
.altKey
== evt
.altKey
) &&
15184 (keyObject
.metaKey
=== undefined || keyObject
.metaKey
== (evt
.metaKey
|| false)) && // IE doesn't even set metaKey
15185 (keyObject
.shiftKey
=== undefined || keyObject
.shiftKey
== evt
.shiftKey
)){
15187 typematic
.trigger(evt
, _this
, node
, callback
, keyObject
, subsequentDelay
, initialDelay
, minDelay
);
15188 }else if(typematic
._obj
== keyObject
){
15192 on(node
, "keyup", lang
.hitch(this, function(){
15193 if(typematic
._obj
== keyObject
){
15198 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15201 addMouseListener: function(/*DOMNode*/ node
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15203 // Start listening for a typematic mouse click.
15204 // See the trigger method for other parameters.
15206 // a connection handle
15208 on(node
, "mousedown", lang
.hitch(this, function(evt
){
15209 evt
.preventDefault();
15210 typematic
.trigger(evt
, _this
, node
, callback
, node
, subsequentDelay
, initialDelay
, minDelay
);
15212 on(node
, "mouseup", lang
.hitch(this, function(evt
){
15214 evt
.preventDefault();
15218 on(node
, "mouseout", lang
.hitch(this, function(evt
){
15220 evt
.preventDefault();
15224 on(node
, "dblclick", lang
.hitch(this, function(evt
){
15225 evt
.preventDefault();
15227 typematic
.trigger(evt
, _this
, node
, callback
, node
, subsequentDelay
, initialDelay
, minDelay
);
15228 setTimeout(lang
.hitch(this, typematic
.stop
), 50);
15232 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15235 addListener: function(/*Node*/ mouseNode
, /*Node*/ keyNode
, /*Object*/ keyObject
, /*Object*/ _this
, /*Function*/ callback
, /*Number*/ subsequentDelay
, /*Number*/ initialDelay
, /*Number?*/ minDelay
){
15237 // Start listening for a specific typematic key and mouseclick.
15238 // This is a thin wrapper to addKeyListener and addMouseListener.
15239 // See the addMouseListener and addKeyListener methods for other parameters.
15241 // the DOM node object to listen on for mouse events.
15243 // the DOM node object to listen on for key events.
15245 // a connection handle
15247 this.addKeyListener(keyNode
, keyObject
, _this
, callback
, subsequentDelay
, initialDelay
, minDelay
),
15248 this.addMouseListener(mouseNode
, _this
, callback
, subsequentDelay
, initialDelay
, minDelay
)
15250 return { remove: function(){ array
.forEach(handles
, function(h
){ h
.remove(); }); } };
15259 'dijit/MenuItem':function(){
15261 'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"}});
15262 define("dijit/MenuItem", [
15263 "dojo/_base/declare", // declare
15264 "dojo/dom", // dom.setSelectable
15265 "dojo/dom-attr", // domAttr.set
15266 "dojo/dom-class", // domClass.toggle
15267 "dojo/_base/kernel", // kernel.deprecated
15268 "dojo/sniff", // has("ie")
15270 "./_TemplatedMixin",
15272 "./_CssStateMixin",
15273 "dojo/text!./templates/MenuItem.html"
15274 ], function(declare
, dom
, domAttr
, domClass
, kernel
, has
,
15275 _Widget
, _TemplatedMixin
, _Contained
, _CssStateMixin
, template
){
15280 return declare("dijit.MenuItem",
15281 [_Widget
, _TemplatedMixin
, _Contained
, _CssStateMixin
],
15284 // A line item in a Menu Widget
15287 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
15288 templateString
: template
,
15290 baseClass
: "dijitMenuItem",
15295 _setLabelAttr: function(val
){
15296 this.containerNode
.innerHTML
= val
;
15297 this._set("label", val
);
15298 if(this.textDir
=== "auto"){
15299 this.applyTextDir(this.focusNode
, this.label
);
15303 // iconClass: String
15304 // Class to apply to DOMNode to make it display an icon.
15305 iconClass
: "dijitNoIcon",
15306 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
15308 // accelKey: String
15309 // Text for the accelerator (shortcut) key combination.
15310 // Note that although Menu can display accelerator keys there
15311 // is no infrastructure to actually catch and execute these
15315 // disabled: Boolean
15316 // If true, the menu item is disabled.
15317 // If false, the menu item is enabled.
15320 _fillContent: function(/*DomNode*/ source
){
15321 // If button label is specified as srcNodeRef.innerHTML rather than
15322 // this.params.label, handle it here.
15323 if(source
&& !("label" in this.params
)){
15324 this.set('label', source
.innerHTML
);
15328 buildRendering: function(){
15329 this.inherited(arguments
);
15330 var label
= this.id
+"_text";
15331 domAttr
.set(this.containerNode
, "id", label
);
15332 if(this.accelKeyNode
){
15333 domAttr
.set(this.accelKeyNode
, "id", this.id
+ "_accel");
15334 label
+= " " + this.id
+ "_accel";
15336 this.domNode
.setAttribute("aria-labelledby", label
);
15337 dom
.setSelectable(this.domNode
, false);
15340 onClick: function(/*Event*/){
15342 // User defined function to handle clicks
15349 // Focus on this MenuItem
15351 if(has("ie") == 8){
15352 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
15353 this.containerNode
.focus();
15355 this.focusNode
.focus();
15357 // this throws on IE (at least) in some scenarios
15361 _onFocus: function(){
15363 // This is called by the focus manager when focus
15364 // goes to this MenuItem or a child menu.
15367 this._setSelected(true);
15368 this.getParent()._onItemFocus(this);
15370 this.inherited(arguments
);
15373 _setSelected: function(selected
){
15375 // Indicate that this node is the currently selected one
15380 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
15381 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
15382 * That's not supposed to happen, but the problem is:
15383 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
15384 * points to the parent Menu, bypassing the parent MenuItem... thus the
15385 * MenuItem is not in the chain of active widgets and gets a premature call to
15389 domClass
.toggle(this.domNode
, "dijitMenuItemSelected", selected
);
15392 setLabel: function(/*String*/ content
){
15394 // Deprecated. Use set('label', ...) instead.
15397 kernel
.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
15398 this.set("label", content
);
15401 setDisabled: function(/*Boolean*/ disabled
){
15403 // Deprecated. Use set('disabled', bool) instead.
15406 kernel
.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
15407 this.set('disabled', disabled
);
15409 _setDisabledAttr: function(/*Boolean*/ value
){
15411 // Hook for attr('disabled', ...) to work.
15412 // Enable or disable this menu item.
15414 this.focusNode
.setAttribute('aria-disabled', value
? 'true' : 'false');
15415 this._set("disabled", value
);
15417 _setAccelKeyAttr: function(/*String*/ value
){
15419 // Hook for attr('accelKey', ...) to work.
15420 // Set accelKey on this menu item.
15422 this.accelKeyNode
.style
.display
=value
?"":"none";
15423 this.accelKeyNode
.innerHTML
=value
;
15424 //have to use colSpan to make it work in IE
15425 domAttr
.set(this.containerNode
,'colSpan',value
?"1":"2");
15427 this._set("accelKey", value
);
15429 _setTextDirAttr: function(/*String*/ textDir
){
15431 // Setter for textDir.
15433 // Users shouldn't call this function; they should be calling
15434 // set('textDir', value)
15438 // only if new textDir is different from the old one
15439 // and on widgets creation.
15440 if(!this._created
|| this.textDir
!= textDir
){
15441 this._set("textDir", textDir
);
15442 this.applyTextDir(this.focusNode
, this.label
);
15449 'dijit/layout/TabController':function(){
15451 'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n"}});
15452 define("dijit/layout/TabController", [
15453 "dojo/_base/declare", // declare
15454 "dojo/dom", // dom.setSelectable
15455 "dojo/dom-attr", // domAttr.attr
15456 "dojo/dom-class", // domClass.toggle
15457 "dojo/i18n", // i18n.getLocalization
15458 "dojo/_base/lang", // lang.hitch lang.trim
15459 "./StackController",
15463 "dojo/text!./templates/_TabButton.html",
15464 "dojo/i18n!../nls/common"
15465 ], function(declare
, dom
, domAttr
, domClass
, i18n
, lang
, StackController
, registry
, Menu
, MenuItem
, template
){
15468 // dijit/layout/TabController
15470 var TabButton
= declare("dijit.layout._TabButton", StackController
.StackButton
, {
15472 // A tab (the thing you click to select a pane).
15474 // Contains the title of the pane, and optionally a close-button to destroy the pane.
15475 // This is an internal widget and should not be instantiated directly.
15479 // baseClass: String
15480 // The CSS class applied to the domNode.
15481 baseClass
: "dijitTab",
15483 // Apply dijitTabCloseButtonHover when close button is hovered
15485 closeNode
: "dijitTabCloseButton"
15488 templateString
: template
,
15490 // Override _FormWidget.scrollOnFocus.
15491 // Don't scroll the whole tab container into view when the button is focused.
15492 scrollOnFocus
: false,
15494 buildRendering: function(){
15495 this.inherited(arguments
);
15497 dom
.setSelectable(this.containerNode
, false);
15500 startup: function(){
15501 this.inherited(arguments
);
15502 var n
= this.domNode
;
15504 // Required to give IE6 a kick, as it initially hides the
15505 // tabs until they are focused on.
15506 this.defer(function(){
15507 n
.className
= n
.className
;
15511 _setCloseButtonAttr: function(/*Boolean*/ disp
){
15513 // Hide/show close button
15514 this._set("closeButton", disp
);
15515 domClass
.toggle(this.domNode
, "dijitClosable", disp
);
15516 this.closeNode
.style
.display
= disp
? "" : "none";
15518 var _nlsResources
= i18n
.getLocalization("dijit", "common");
15519 if(this.closeNode
){
15520 domAttr
.set(this.closeNode
, "title", _nlsResources
.itemClose
);
15525 _setDisabledAttr: function(/*Boolean*/ disabled
){
15527 // Make tab selected/unselectable
15529 this.inherited(arguments
);
15531 // Don't show tooltip for close button when tab is disabled
15532 if(this.closeNode
){
15534 domAttr
.remove(this.closeNode
, "title");
15536 var _nlsResources
= i18n
.getLocalization("dijit", "common");
15537 domAttr
.set(this.closeNode
, "title", _nlsResources
.itemClose
);
15542 _setLabelAttr: function(/*String*/ content
){
15544 // Hook for set('label', ...) to work.
15546 // takes an HTML string.
15547 // Inherited ToggleButton implementation will Set the label (text) of the button;
15548 // Need to set the alt attribute of icon on tab buttons if no label displayed
15549 this.inherited(arguments
);
15550 if(!this.showLabel
&& !this.params
.title
){
15551 this.iconNode
.alt
= lang
.trim(this.containerNode
.innerText
|| this.containerNode
.textContent
|| '');
15556 var TabController
= declare("dijit.layout.TabController", StackController
, {
15558 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
15559 // Used internally by `dijit/layout/TabContainer`.
15561 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
15562 // TabController also monitors the TabContainer, and whenever a pane is
15563 // added or deleted updates itself accordingly.
15567 baseClass
: "dijitTabController",
15569 templateString
: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
15571 // tabPosition: String
15572 // Defines where tabs go relative to the content.
15573 // "top", "bottom", "left-h", "right-h"
15574 tabPosition
: "top",
15576 // buttonWidget: Constructor
15577 // The tab widget to create to correspond to each page
15578 buttonWidget
: TabButton
,
15580 // buttonWidgetCloseClass: String
15581 // Class of [x] close icon, used by event delegation code to tell when close button was clicked
15582 buttonWidgetCloseClass
: "dijitTabCloseButton",
15584 postCreate: function(){
15585 this.inherited(arguments
);
15587 // Setup a close menu to be shared between all the closable tabs (excluding disabled tabs)
15588 var closeMenu
= new Menu({
15589 id
: this.id
+"_Menu",
15590 ownerDocument
: this.ownerDocument
,
15593 textDir
: this.textDir
,
15594 targetNodeIds
: [this.domNode
],
15595 selector: function(node
){
15596 return domClass
.contains(node
, "dijitClosable") && !domClass
.contains(node
, "dijitTabDisabled");
15599 this.own(closeMenu
);
15601 var _nlsResources
= i18n
.getLocalization("dijit", "common"),
15603 closeMenu
.addChild(new MenuItem({
15604 label
: _nlsResources
.itemClose
,
15605 ownerDocument
: this.ownerDocument
,
15608 textDir
: this.textDir
,
15609 onClick: function(evt
){
15610 var button
= registry
.byNode(this.getParent().currentTarget
);
15611 controller
.onCloseButtonClick(button
.page
);
15617 TabController
.TabButton
= TabButton
; // for monkey patching
15619 return TabController
;
15623 'dijit/layout/_LayoutWidget':function(){
15624 define("dijit/layout/_LayoutWidget", [
15625 "dojo/_base/lang", // lang.mixin
15630 "dojo/_base/declare", // declare
15631 "dojo/dom-class", // domClass.add domClass.remove
15632 "dojo/dom-geometry", // domGeometry.marginBox
15633 "dojo/dom-style" // domStyle.getComputedStyle
15634 ], function(lang
, _Widget
, _Container
, _Contained
, Viewport
,
15635 declare
, domClass
, domGeometry
, domStyle
){
15638 // dijit/layout/_LayoutWidget
15641 return declare("dijit.layout._LayoutWidget", [_Widget
, _Container
, _Contained
], {
15643 // Base class for a _Container widget which is responsible for laying out its children.
15644 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
15646 // baseClass: [protected extension] String
15647 // This class name is applied to the widget's domNode
15648 // and also may be used to generate names for sub nodes,
15649 // for example dijitTabContainer-content.
15650 baseClass
: "dijitLayoutContainer",
15652 // isLayoutContainer: [protected] Boolean
15653 // Indicates that this widget is going to call resize() on its
15654 // children widgets, setting their size, when they become visible.
15655 isLayoutContainer
: true,
15657 buildRendering: function(){
15658 this.inherited(arguments
);
15659 domClass
.add(this.domNode
, "dijitContainer");
15662 startup: function(){
15664 // Called after all the widgets have been instantiated and their
15665 // dom nodes have been inserted somewhere under win.doc.body.
15667 // Widgets should override this method to do any initialization
15668 // dependent on other widgets existing, and then call
15669 // this superclass method to finish things off.
15671 // startup() in subclasses shouldn't do anything
15672 // size related because the size of the widget hasn't been set yet.
15674 if(this._started
){ return; }
15676 // Need to call inherited first - so that child widgets get started
15678 this.inherited(arguments
);
15680 // If I am a not being controlled by a parent layout widget...
15681 var parent
= this.getParent
&& this.getParent();
15682 if(!(parent
&& parent
.isLayoutContainer
)){
15683 // Do recursive sizing and layout of all my descendants
15684 // (passing in no argument to resize means that it has to glean the size itself)
15687 // Since my parent isn't a layout container, and my style *may be* width=height=100%
15688 // or something similar (either set directly or via a CSS class),
15689 // monitor when viewport size changes so that I can re-layout.
15690 this.own(Viewport
.on("resize", lang
.hitch(this, "resize")));
15694 resize: function(changeSize
, resultSize
){
15696 // Call this to resize a widget, or after its size has changed.
15698 // ####Change size mode:
15700 // When changeSize is specified, changes the marginBox of this widget
15701 // and forces it to re-layout its contents accordingly.
15702 // changeSize may specify height, width, or both.
15704 // If resultSize is specified it indicates the size the widget will
15705 // become after changeSize has been applied.
15707 // ####Notification mode:
15709 // When changeSize is null, indicates that the caller has already changed
15710 // the size of the widget, or perhaps it changed because the browser
15711 // window was resized. Tells widget to re-layout its contents accordingly.
15713 // If resultSize is also specified it indicates the size the widget has
15716 // In either mode, this method also:
15718 // 1. Sets this._borderBox and this._contentBox to the new size of
15719 // the widget. Queries the current domNode size if necessary.
15720 // 2. Calls layout() to resize contents (and maybe adjust child widgets).
15721 // changeSize: Object?
15722 // Sets the widget to this margin-box size and position.
15723 // May include any/all of the following properties:
15724 // | {w: int, h: int, l: int, t: int}
15725 // resultSize: Object?
15726 // The margin-box size of this widget after applying changeSize (if
15727 // changeSize is specified). If caller knows this size and
15728 // passes it in, we don't need to query the browser to get the size.
15729 // | {w: int, h: int}
15731 var node
= this.domNode
;
15733 // set margin box size, unless it wasn't specified, in which case use current size
15735 domGeometry
.setMarginBox(node
, changeSize
);
15738 // If either height or width wasn't specified by the user, then query node for it.
15739 // But note that setting the margin box and then immediately querying dimensions may return
15740 // inaccurate results, so try not to depend on it.
15741 var mb
= resultSize
|| {};
15742 lang
.mixin(mb
, changeSize
|| {}); // changeSize overrides resultSize
15743 if( !("h" in mb
) || !("w" in mb
) ){
15744 mb
= lang
.mixin(domGeometry
.getMarginBox(node
), mb
); // just use domGeometry.marginBox() to fill in missing values
15747 // Compute and save the size of my border box and content box
15748 // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
15749 var cs
= domStyle
.getComputedStyle(node
);
15750 var me
= domGeometry
.getMarginExtents(node
, cs
);
15751 var be
= domGeometry
.getBorderExtents(node
, cs
);
15752 var bb
= (this._borderBox
= {
15753 w
: mb
.w
- (me
.w
+ be
.w
),
15754 h
: mb
.h
- (me
.h
+ be
.h
)
15756 var pe
= domGeometry
.getPadExtents(node
, cs
);
15757 this._contentBox
= {
15758 l
: domStyle
.toPixelValue(node
, cs
.paddingLeft
),
15759 t
: domStyle
.toPixelValue(node
, cs
.paddingTop
),
15764 // Callback for widget to adjust size of its children
15768 layout: function(){
15770 // Widgets override this method to size and position their contents/children.
15771 // When this is called this._contentBox is guaranteed to be set (see resize()).
15773 // This is called after startup(), and also when the widget's size has been
15776 // protected extension
15779 _setupChild: function(/*dijit/_WidgetBase*/child){
15781 // Common setup for initial children and children which are added after startup
15783 // protected extension
15785 var cls = this.baseClass + "-child "
15786 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
15787 domClass.add(child.domNode, cls);
15790 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
15791 // Overrides _Container.addChild() to call _setupChild()
15792 this.inherited(arguments
);
15794 this._setupChild(child
);
15798 removeChild: function(/*dijit/_WidgetBase*/ child){
15799 // Overrides _Container.removeChild() to remove class added by _setupChild()
15800 var cls = this.baseClass + "-child"
15801 + (child.baseClass ?
15802 " " + this.baseClass + "-" + child.baseClass : "");
15803 domClass.remove(child.domNode, cls);
15805 this.inherited(arguments);
15811 'dijit/popup':function(){
15812 define("dijit/popup", [
15813 "dojo/_base/array", // array.forEach array.some
15815 "dojo/_base/connect", // connect._keypress
15816 "dojo/_base/declare", // declare
15817 "dojo/dom", // dom.isDescendant
15818 "dojo/dom-attr", // domAttr.set
15819 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
15820 "dojo/dom-geometry", // domGeometry.isBodyLtr
15821 "dojo/dom-style", // domStyle.set
15822 "dojo/_base/event", // event.stop
15824 "dojo/_base/lang", // lang.hitch
15826 "dojo/sniff", // has("ie") has("mozilla")
15828 "./BackgroundIframe",
15829 "./main" // dijit (defining dijit.popup to match API doc)
15830 ], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has,
15831 place, BackgroundIframe, dijit){
15839 // widget to display
15841 // the button etc. that is displaying this popup
15843 // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
15845 // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15847 // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15848 // orient: Object|String
15849 // When the around parameter is specified, orient should be a list of positions to try, ex:
15850 // | [ "below", "above" ]
15851 // For backwards compatibility it can also be an (ordered) hash of tuples of the form
15852 // (around-node-corner, popup-node-corner), ex:
15853 // | { "BL": "TL", "TL": "BL" }
15854 // where BL means "bottom left" and "TL" means "top left", etc.
15856 // dijit/popup.open() tries to position the popup according to each specified position, in order,
15857 // until the popup appears fully within the viewport.
15859 // The default value is ["below", "above"]
15861 // When an (x,y) position is specified rather than an around node, orient is either
15862 // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
15863 // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
15864 // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
15865 // and the top-right corner.
15866 // onCancel: Function
15867 // callback when user has canceled the popup by:
15869 // 1. hitting ESC or
15870 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
15871 // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
15872 // onClose: Function
15873 // callback whenever this popup is closed
15874 // onExecute: Function
15875 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
15876 // padding: place.__Position
15877 // adding a buffer around the opening position. This is only useful when around is not set.
15881 function destroyWrapper(){
15883 // Function to destroy wrapper when popup widget is destroyed.
15884 // Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
15885 if(this._popupWrapper
){
15886 domConstruct
.destroy(this._popupWrapper
);
15887 delete this._popupWrapper
;
15891 var PopupManager
= declare(null, {
15893 // Used to show drop downs (ex: the select list of a ComboBox)
15894 // or popups (ex: right-click context menus).
15896 // _stack: dijit/_WidgetBase[]
15897 // Stack of currently popped up widgets.
15898 // (someone opened _stack[0], and then it opened _stack[1], etc.)
15901 // _beginZIndex: Number
15902 // Z-index of the first popup. (If first popup opens other
15903 // popups they get a higher z-index.)
15904 _beginZIndex
: 1000,
15908 _createWrapper: function(/*Widget*/ widget
){
15910 // Initialization for widgets that will be used as popups.
15911 // Puts widget inside a wrapper DIV (if not already in one),
15912 // and returns pointer to that wrapper DIV.
15914 var wrapper
= widget
._popupWrapper
,
15915 node
= widget
.domNode
;
15918 // Create wrapper <div> for when this widget [in the future] will be used as a popup.
15919 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
15920 // to go wonky, see tests/robot/Toolbar.html to reproduce
15921 wrapper
= domConstruct
.create("div", {
15922 "class":"dijitPopup",
15923 style
:{ display
: "none"},
15924 role
: "presentation"
15925 }, widget
.ownerDocumentBody
);
15926 wrapper
.appendChild(node
);
15928 var s
= node
.style
;
15934 widget
._popupWrapper
= wrapper
;
15935 aspect
.after(widget
, "destroy", destroyWrapper
, true);
15941 moveOffScreen: function(/*Widget*/ widget
){
15943 // Moves the popup widget off-screen.
15944 // Do not use this method to hide popups when not in use, because
15945 // that will create an accessibility issue: the offscreen popup is
15946 // still in the tabbing order.
15948 // Create wrapper if not already there
15949 var wrapper
= this._createWrapper(widget
);
15951 domStyle
.set(wrapper
, {
15952 visibility
: "hidden",
15953 top
: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
15958 hide: function(/*Widget*/ widget
){
15960 // Hide this popup widget (until it is ready to be shown).
15961 // Initialization for widgets that will be used as popups
15963 // Also puts widget inside a wrapper DIV (if not already in one)
15965 // If popup widget needs to layout it should
15966 // do so when it is made visible, and popup._onShow() is called.
15968 // Create wrapper if not already there
15969 var wrapper
= this._createWrapper(widget
);
15971 domStyle
.set(wrapper
, "display", "none");
15974 getTopPopup: function(){
15976 // Compute the closest ancestor popup that's *not* a child of another popup.
15977 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
15978 var stack
= this._stack
;
15979 for(var pi
=stack
.length
-1; pi
> 0 && stack
[pi
].parent
=== stack
[pi
-1].widget
; pi
--){
15980 /* do nothing, just trying to get right value for pi */
15985 open: function(/*__OpenArgs*/ args
){
15987 // Popup the widget at the specified position
15990 // opening at the mouse position
15991 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
15994 // opening the widget as a dropdown
15995 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
15997 // Note that whatever widget called dijit/popup.open() should also listen to its own _onBlur callback
15998 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
16000 var stack
= this._stack
,
16001 widget
= args
.popup
,
16002 orient
= args
.orient
|| ["below", "below-alt", "above", "above-alt"],
16003 ltr
= args
.parent
? args
.parent
.isLeftToRight() : domGeometry
.isBodyLtr(widget
.ownerDocument
),
16004 around
= args
.around
,
16005 id
= (args
.around
&& args
.around
.id
) ? (args
.around
.id
+"_dropdown") : ("popup_"+this._idGen
++);
16007 // If we are opening a new popup that isn't a child of a currently opened popup, then
16008 // close currently opened popup(s). This should happen automatically when the old popups
16009 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
16010 while(stack
.length
&& (!args
.parent
|| !dom
.isDescendant(args
.parent
.domNode
, stack
[stack
.length
-1].widget
.domNode
))){
16011 this.close(stack
[stack
.length
-1].widget
);
16014 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
16015 var wrapper
= this._createWrapper(widget
);
16018 domAttr
.set(wrapper
, {
16021 zIndex
: this._beginZIndex
+ stack
.length
16023 "class": "dijitPopup " + (widget
.baseClass
|| widget
["class"] || "").split(" ")[0] +"Popup",
16024 dijitPopupParent
: args
.parent
? args
.parent
.id
: ""
16027 if(has("ie") || has("mozilla")){
16028 if(!widget
.bgIframe
){
16029 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
16030 widget
.bgIframe
= new BackgroundIframe(wrapper
);
16034 // position the wrapper node and make it visible
16035 var best
= around
?
16036 place
.around(wrapper
, around
, orient
, ltr
, widget
.orient
? lang
.hitch(widget
, "orient") : null) :
16037 place
.at(wrapper
, args
, orient
== 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args
.padding
);
16039 wrapper
.style
.display
= "";
16040 wrapper
.style
.visibility
= "visible";
16041 widget
.domNode
.style
.visibility
= "visible"; // counteract effects from _HasDropDown
16045 // provide default escape and tab key handling
16046 // (this will work for any widget, not just menu)
16047 handlers
.push(on(wrapper
, connect
._keypress
, lang
.hitch(this, function(evt
){
16048 if(evt
.charOrCode
== keys
.ESCAPE
&& args
.onCancel
){
16051 }else if(evt
.charOrCode
=== keys
.TAB
){
16053 var topPopup
= this.getTopPopup();
16054 if(topPopup
&& topPopup
.onCancel
){
16055 topPopup
.onCancel();
16060 // watch for cancel/execute events on the popup and notify the caller
16061 // (for a menu, "execute" means clicking an item)
16062 if(widget
.onCancel
&& args
.onCancel
){
16063 handlers
.push(widget
.on("cancel", args
.onCancel
));
16066 handlers
.push(widget
.on(widget
.onExecute
? "execute" : "change", lang
.hitch(this, function(){
16067 var topPopup
= this.getTopPopup();
16068 if(topPopup
&& topPopup
.onExecute
){
16069 topPopup
.onExecute();
16075 parent
: args
.parent
,
16076 onExecute
: args
.onExecute
,
16077 onCancel
: args
.onCancel
,
16078 onClose
: args
.onClose
,
16083 // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
16084 widget
.onOpen(best
);
16090 close: function(/*Widget?*/ popup
){
16092 // Close specified popup and any popups that it parented.
16093 // If no popup is specified, closes all popups.
16095 var stack
= this._stack
;
16097 // Basically work backwards from the top of the stack closing popups
16098 // until we hit the specified popup, but IIRC there was some issue where closing
16099 // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
16100 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
16101 // so the while condition is constructed defensively.
16102 while((popup
&& array
.some(stack
, function(elem
){return elem
.widget
== popup
;})) ||
16103 (!popup
&& stack
.length
)){
16104 var top
= stack
.pop(),
16105 widget
= top
.widget
,
16106 onClose
= top
.onClose
;
16108 if(widget
.onClose
){
16109 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
16114 while(h
= top
.handlers
.pop()){ h
.remove(); }
16116 // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
16117 if(widget
&& widget
.domNode
){
16128 return (dijit
.popup
= new PopupManager());
16132 'dijit/_base/manager':function(){
16133 define("dijit/_base/manager", [
16134 "dojo/_base/array",
16135 "dojo/_base/config", // defaultDuration
16138 "../main" // for setting exports to dijit namespace
16139 ], function(array
, config
, lang
, registry
, dijit
){
16142 // dijit/_base/manager
16146 // Deprecated. Shim to methods on registry, plus a few other declarations.
16147 // New code should access dijit/registry directly when possible.
16150 array
.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name
){
16151 exports
[name
] = registry
[name
];
16154 lang
.mixin(exports
, {
16155 // defaultDuration: Integer
16156 // The default fx.animation speed (in ms) to use for all Dijit
16157 // transitional fx.animations, unless otherwise specified
16158 // on a per-instance basis. Defaults to 200, overrided by
16159 // `djConfig.defaultDuration`
16160 defaultDuration
: config
["defaultDuration"] || 200
16163 lang
.mixin(dijit
, exports
);
16165 /*===== return exports; =====*/
16166 return dijit
; // for back compat :-(
16170 'dijit/layout/StackController':function(){
16171 define("dijit/layout/StackController", [
16172 "dojo/_base/array", // array.forEach array.indexOf array.map
16173 "dojo/_base/declare", // declare
16175 "dojo/_base/event", // event.stop
16176 "dojo/keys", // keys
16177 "dojo/_base/lang", // lang.getObject
16179 "../focus", // focus.focus()
16180 "../registry", // registry.byId
16182 "../_TemplatedMixin",
16184 "../form/ToggleButton",
16185 "dojo/i18n!../nls/common"
16186 ], function(array
, declare
, domClass
, event
, keys
, lang
, on
,
16187 focus
, registry
, _Widget
, _TemplatedMixin
, _Container
, ToggleButton
){
16190 // dijit/layout/StackController
16192 var StackButton
= declare("dijit.layout._StackButton", ToggleButton
, {
16194 // Internal widget used by StackContainer.
16196 // The button-like or tab-like object you click to select or delete a page
16200 // Override _FormWidget.tabIndex.
16201 // StackContainer buttons are not in the tab order by default.
16202 // Probably we should be calling this.startupKeyNavChildren() instead.
16205 // closeButton: Boolean
16206 // When true, display close button for this tab
16207 closeButton
: false,
16209 _aria_attr
: "aria-selected",
16211 buildRendering: function(/*Event*/ evt
){
16212 this.inherited(arguments
);
16213 (this.focusNode
|| this.domNode
).setAttribute("role", "tab");
16218 var StackController
= declare("dijit.layout.StackController", [_Widget
, _TemplatedMixin
, _Container
], {
16220 // Set of buttons to select a page in a `dijit/layout/StackContainer`
16222 // Monitors the specified StackContainer, and whenever a page is
16223 // added, deleted, or selected, updates itself accordingly.
16225 baseClass
: "dijitStackController",
16227 templateString
: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
16229 // containerId: [const] String
16230 // The id of the page container that I point to
16233 // buttonWidget: [const] Constructor
16234 // The button widget to create to correspond to each page
16235 buttonWidget
: StackButton
,
16237 // buttonWidgetCloseClass: String
16238 // CSS class of [x] close icon, used by event delegation code to tell when close button was clicked
16239 buttonWidgetCloseClass
: "dijitStackCloseButton",
16241 constructor: function(params
/*===== , srcNodeRef =====*/){
16243 // Create the widget.
16244 // params: Object|null
16245 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
16246 // and functions, typically callbacks like onClick.
16247 // The hash can contain any of the widget's properties, excluding read-only properties.
16248 // srcNodeRef: DOMNode|String?
16249 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
16251 this.pane2button
= {}; // mapping from pane id to buttons
16254 postCreate: function(){
16255 this.inherited(arguments
);
16257 // Listen to notifications from StackContainer.
16258 // TODO: do this through bubbled events instead of topics
16259 this.subscribe(this.containerId
+"-startup", "onStartup");
16260 this.subscribe(this.containerId
+"-addChild", "onAddChild");
16261 this.subscribe(this.containerId
+"-removeChild", "onRemoveChild");
16262 this.subscribe(this.containerId
+"-selectChild", "onSelectChild");
16263 this.subscribe(this.containerId
+"-containerKeyPress", "onContainerKeyPress");
16265 // Listen for click events to select or close tabs.
16266 // No need to worry about ENTER/SPACE key handling: tabs are selected via left/right arrow keys,
16267 // and closed via shift-F10 (to show the close menu).
16268 this.connect(this.containerNode
, 'click', function(evt
){
16269 var button
= registry
.getEnclosingWidget(evt
.target
);
16270 if(button
!= this.containerNode
&& !button
.disabled
&& button
.page
){
16271 for(var target
= evt
.target
; target
!== this.containerNode
; target
= target
.parentNode
){
16272 if(domClass
.contains(target
, this.buttonWidgetCloseClass
)){
16273 this.onCloseButtonClick(button
.page
);
16275 }else if(target
== button
.domNode
){
16276 this.onButtonClick(button
.page
);
16284 onStartup: function(/*Object*/ info
){
16286 // Called after StackContainer has finished initializing
16289 array
.forEach(info
.children
, this.onAddChild
, this);
16291 // Show button corresponding to selected pane (unless selected
16292 // is null because there are no panes)
16293 this.onSelectChild(info
.selected
);
16296 // Reflect events like page title changes to tab buttons
16297 var containerNode
= registry
.byId(this.containerId
).containerNode
,
16298 pane2button
= this.pane2button
,
16299 paneToButtonAttr
= {
16301 "showtitle": "showLabel",
16302 "iconclass": "iconClass",
16303 "closable": "closeButton",
16304 "tooltip": "title",
16305 "disabled": "disabled"
16307 connectFunc = function(attr
, buttonAttr
){
16308 return on(containerNode
, "attrmodified-" + attr
, function(evt
){
16309 var button
= pane2button
[evt
.detail
&& evt
.detail
.widget
&& evt
.detail
.widget
.id
];
16311 button
.set(buttonAttr
, evt
.detail
.newValue
);
16315 for(var attr
in paneToButtonAttr
){
16316 this.own(connectFunc(attr
, paneToButtonAttr
[attr
]));
16320 destroy: function(){
16321 // Since the buttons are internal to the StackController widget, destroy() should remove them, which is
16322 // done by calling onRemoveChild().
16323 for(var pane
in this.pane2button
){
16324 this.onRemoveChild(registry
.byId(pane
));
16327 // TODO: destroyRecursive() will call destroy() on each child button twice. Once from the above code,
16328 // and once because _WidgetBase.destroyDescendants() deletes anything inside of this.containerNode.
16329 // Probably shouldn't attach that DOMNode as this.containerNode.
16331 this.inherited(arguments
);
16334 onAddChild: function(/*dijit/_WidgetBase*/ page, /*Integer?*/ insertIndex
){
16336 // Called whenever a page is added to the container.
16337 // Create button corresponding to the page.
16341 // create an instance of the button widget
16342 // (remove typeof buttonWidget == string support in 2.0)
16343 var Cls
= lang
.isString(this.buttonWidget
) ? lang
.getObject(this.buttonWidget
) : this.buttonWidget
;
16344 var button
= new Cls({
16345 id
: this.id
+ "_" + page
.id
,
16346 name
: this.id
+ "_" + page
.id
,
16348 disabled
: page
.disabled
,
16349 ownerDocument
: this.ownerDocument
,
16352 textDir
: page
.textDir
,
16353 showLabel
: page
.showTitle
,
16354 iconClass
: page
.iconClass
,
16355 closeButton
: page
.closable
,
16356 title
: page
.tooltip
,
16360 this.addChild(button
, insertIndex
);
16361 this.pane2button
[page
.id
] = button
;
16362 page
.controlButton
= button
; // this value might be overwritten if two tabs point to same container
16363 if(!this._currentChild
){
16364 // If this is the first child then StackContainer will soon publish that it's selected,
16365 // but before that StackContainer calls layout(), and before layout() is called the
16366 // StackController needs to have the proper height... which means that the button needs
16367 // to be marked as selected now. See test_TabContainer_CSS.html for test.
16368 this.onSelectChild(page
);
16372 onRemoveChild: function(/*dijit/_WidgetBase*/ page){
16374 // Called whenever a page is removed from the container.
16375 // Remove the button corresponding to the page.
16379 if(this._currentChild === page){ this._currentChild = null; }
16381 var button = this.pane2button[page.id];
16383 this.removeChild(button);
16384 delete this.pane2button[page.id];
16387 delete page.controlButton;
16390 onSelectChild: function(/*dijit/_WidgetBase*/ page){
16392 // Called when a page has been selected in the StackContainer, either by me or by another StackController
16396 if(!page){ return; }
16398 if(this._currentChild){
16399 var oldButton=this.pane2button[this._currentChild.id];
16400 oldButton.set('checked', false);
16401 oldButton.focusNode.setAttribute("tabIndex", "-1");
16404 var newButton=this.pane2button[page.id];
16405 newButton.set('checked', true);
16406 this._currentChild = page;
16407 newButton.focusNode.setAttribute("tabIndex", "0");
16408 var container = registry.byId(this.containerId);
16409 container.containerNode.setAttribute("aria-labelledby", newButton.id);
16412 onButtonClick: function(/*dijit/_WidgetBase*/ page){
16414 // Called whenever one of my child buttons is pressed in an attempt to select a page
16418 var button = this.pane2button[page.id];
16420 // For TabContainer where the tabs are <span>, need to set focus explicitly when left/right arrow
16421 focus.focus(button.focusNode);
16423 if(this._currentChild && this._currentChild.id === page.id) {
16424 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
16425 button.set('checked', true);
16427 var container = registry.byId(this.containerId);
16428 container.selectChild(page);
16431 onCloseButtonClick: function(/*dijit/_WidgetBase*/ page){
16433 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
16437 var container = registry.byId(this.containerId);
16438 container.closeChild(page);
16439 if(this._currentChild){
16440 var b = this.pane2button[this._currentChild.id];
16442 focus.focus(b.focusNode || b.domNode);
16447 // TODO: this is a bit redundant with forward, back api in StackContainer
16448 adjacent: function(/*Boolean*/ forward
){
16450 // Helper for onkeypress to find next/previous button
16454 if(!this.isLeftToRight() && (!this.tabPosition
|| /top|bottom/.test(this.tabPosition
))){ forward
= !forward
; }
16455 // find currently focused button in children array
16456 var children
= this.getChildren();
16457 var idx
= array
.indexOf(children
, this.pane2button
[this._currentChild
.id
]),
16458 current
= children
[idx
];
16460 // Pick next/previous non-disabled button to focus on. If we get back to the original button it means
16461 // that all buttons must be disabled, so return current child to avoid an infinite loop.
16464 idx
= (idx
+ (forward
? 1 : children
.length
- 1)) % children
.length
;
16465 child
= children
[idx
];
16466 }while(child
.disabled
&& child
!= current
);
16468 return child
; // dijit/_WidgetBase
16471 onkeypress: function(/*Event*/ e
){
16473 // Handle keystrokes on the page list, for advancing to next/previous button
16474 // and closing the current page if the page is closable.
16478 if(this.disabled
|| e
.altKey
){ return; }
16479 var forward
= null;
16480 if(e
.ctrlKey
|| !e
._djpage
){
16481 switch(e
.charOrCode
){
16482 case keys
.LEFT_ARROW
:
16483 case keys
.UP_ARROW
:
16484 if(!e
._djpage
){ forward
= false; }
16487 if(e
.ctrlKey
){ forward
= false; }
16489 case keys
.RIGHT_ARROW
:
16490 case keys
.DOWN_ARROW
:
16491 if(!e
._djpage
){ forward
= true; }
16493 case keys
.PAGE_DOWN
:
16494 if(e
.ctrlKey
){ forward
= true; }
16497 // Navigate to first non-disabled child
16498 var children
= this.getChildren();
16499 for(var idx
= 0; idx
< children
.length
; idx
++){
16500 var child
= children
[idx
];
16501 if(!child
.disabled
){
16502 this.onButtonClick(child
.page
);
16509 // Navigate to last non-disabled child
16510 var children
= this.getChildren();
16511 for(var idx
= children
.length
-1; idx
>= 0; idx
--){
16512 var child
= children
[idx
];
16513 if(!child
.disabled
){
16514 this.onButtonClick(child
.page
);
16521 if(this._currentChild
.closable
){
16522 this.onCloseButtonClick(this._currentChild
);
16528 if(e
.charOrCode
=== keys
.TAB
){
16529 this.onButtonClick(this.adjacent(!e
.shiftKey
).page
);
16531 }else if(e
.charOrCode
== "w"){
16532 if(this._currentChild
.closable
){
16533 this.onCloseButtonClick(this._currentChild
);
16535 event
.stop(e
); // avoid browser tab closing.
16539 // handle next/previous page navigation (left/right arrow, etc.)
16540 if(forward
!== null){
16541 this.onButtonClick(this.adjacent(forward
).page
);
16547 onContainerKeyPress: function(/*Object*/ info
){
16549 // Called when there was a keypress on the container
16552 info
.e
._djpage
= info
.page
;
16553 this.onkeypress(info
.e
);
16557 StackController
.StackButton
= StackButton
; // for monkey patching
16559 return StackController
;
16563 'dojo/dnd/Mover':function(){
16564 define("dojo/dnd/Mover", [
16565 "../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window",
16566 "../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll"
16567 ], function(array
, declare
, event
, lang
, has
, win
, dom
, domGeom
, domStyle
, Evented
, on
, touch
, dnd
, autoscroll
){
16572 return declare("dojo.dnd.Mover", [Evented
], {
16574 // an object which makes a node follow the mouse, or touch-drag on touch devices.
16575 // Used as a default mover, and as a base class for custom movers.
16577 constructor: function(node
, e
, host
){
16579 // a node (or node's id) to be moved
16581 // a mouse event, which started the move;
16582 // only pageX and pageY properties are used
16584 // object which implements the functionality of the move,
16585 // and defines proper events (onMoveStart and onMoveStop)
16586 this.node
= dom
.byId(node
);
16587 this.marginBox
= {l
: e
.pageX
, t
: e
.pageY
};
16588 this.mouseButton
= e
.button
;
16589 var h
= (this.host
= host
), d
= node
.ownerDocument
;
16591 // At the start of a drag, onFirstMove is called, and then the following
16592 // listener is disconnected.
16593 on(d
, touch
.move, lang
.hitch(this, "onFirstMove")),
16595 // These are called continually during the drag
16596 on(d
, touch
.move, lang
.hitch(this, "onMouseMove")),
16598 // And these are called at the end of the drag
16599 on(d
, touch
.release
, lang
.hitch(this, "onMouseUp")),
16601 // cancel text selection and text dragging
16602 on(d
, "dragstart", event
.stop
),
16603 on(d
.body
, "selectstart", event
.stop
)
16606 // Tell autoscroll that a drag is starting
16607 autoscroll
.autoScrollStart(d
);
16609 // notify that the move has started
16610 if(h
&& h
.onMoveStart
){
16611 h
.onMoveStart(this);
16614 // mouse event processors
16615 onMouseMove: function(e
){
16617 // event processor for onmousemove/ontouchmove
16619 // mouse/touch event
16620 autoscroll
.autoScroll(e
);
16621 var m
= this.marginBox
;
16622 this.host
.onMove(this, {l
: m
.l
+ e
.pageX
, t
: m
.t
+ e
.pageY
}, e
);
16625 onMouseUp: function(e
){
16626 if(has("webkit") && has("mac") && this.mouseButton
== 2 ?
16627 e
.button
== 0 : this.mouseButton
== e
.button
){ // TODO Should condition be met for touch devices, too?
16633 onFirstMove: function(e
){
16635 // makes the node absolute; it is meant to be called only once.
16636 // relative and absolutely positioned nodes are assumed to use pixel units
16637 var s
= this.node
.style
, l
, t
, h
= this.host
;
16638 switch(s
.position
){
16641 // assume that left and top values are in pixels already
16642 l
= Math
.round(parseFloat(s
.left
)) || 0;
16643 t
= Math
.round(parseFloat(s
.top
)) || 0;
16646 s
.position
= "absolute"; // enforcing the absolute mode
16647 var m
= domGeom
.getMarginBox(this.node
);
16648 // event.pageX/pageY (which we used to generate the initial
16649 // margin box) includes padding and margin set on the body.
16650 // However, setting the node's position to absolute and then
16651 // doing domGeom.marginBox on it *doesn't* take that additional
16652 // space into account - so we need to subtract the combined
16653 // padding and margin. We use getComputedStyle and
16654 // _getMarginBox/_getContentBox to avoid the extra lookup of
16655 // the computed style.
16656 var b
= win
.doc
.body
;
16657 var bs
= domStyle
.getComputedStyle(b
);
16658 var bm
= domGeom
.getMarginBox(b
, bs
);
16659 var bc
= domGeom
.getContentBox(b
, bs
);
16660 l
= m
.l
- (bc
.l
- bm
.l
);
16661 t
= m
.t
- (bc
.t
- bm
.t
);
16664 this.marginBox
.l
= l
- this.marginBox
.l
;
16665 this.marginBox
.t
= t
- this.marginBox
.t
;
16666 if(h
&& h
.onFirstMove
){
16667 h
.onFirstMove(this, e
);
16670 // Disconnect touch.move that call this function
16671 this.events
.shift().remove();
16673 destroy: function(){
16675 // stops the move, deletes all references, so the object can be garbage-collected
16676 array
.forEach(this.events
, function(handle
){ handle
.remove(); });
16677 // undo global settings
16679 if(h
&& h
.onMoveStop
){
16680 h
.onMoveStop(this);
16683 this.events
= this.node
= this.host
= null;
16690 'dijit/layout/TabContainer':function(){
16691 define("dijit/layout/TabContainer", [
16692 "dojo/_base/lang", // lang.getObject
16693 "dojo/_base/declare", // declare
16694 "./_TabContainerBase",
16696 "./ScrollingTabController"
16697 ], function(lang
, declare
, _TabContainerBase
, TabController
, ScrollingTabController
){
16700 // dijit/layout/TabContainer
16703 return declare("dijit.layout.TabContainer", _TabContainerBase
, {
16705 // A Container with tabs to select each child (only one of which is displayed at a time).
16707 // A TabContainer is a container that has multiple panes, but shows only
16708 // one pane at a time. There are a set of tabs corresponding to each pane,
16709 // where each tab has the name (aka title) of the pane, and optionally a close button.
16711 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
16712 // children of a `TabContainer`.
16714 // useMenu: [const] Boolean
16715 // True if a menu should be used to select tabs when they are too
16716 // wide to fit the TabContainer, false otherwise.
16719 // useSlider: [const] Boolean
16720 // True if a slider should be used to select tabs when they are too
16721 // wide to fit the TabContainer, false otherwise.
16724 // controllerWidget: Class
16725 // An optional parameter to override the widget used to display the tab labels
16726 controllerWidget
: "",
16728 _makeController: function(/*DomNode*/ srcNode
){
16730 // Instantiate tablist controller widget and return reference to it.
16731 // Callback from _TabContainerBase.postCreate().
16733 // protected extension
16735 // "string" branch for back-compat, remove for 2.0
16736 var cls
= this.baseClass
+ "-tabs" + (this.doLayout
? "" : " dijitTabNoLayout"),
16737 TabController
= typeof this.controllerWidget
== "string" ? lang
.getObject(this.controllerWidget
) :
16738 this.controllerWidget
;
16740 return new TabController({
16741 id
: this.id
+ "_tablist",
16742 ownerDocument
: this.ownerDocument
,
16745 textDir
: this.textDir
,
16746 tabPosition
: this.tabPosition
,
16747 doLayout
: this.doLayout
,
16748 containerId
: this.id
,
16750 nested
: this.nested
,
16751 useMenu
: this.useMenu
,
16752 useSlider
: this.useSlider
,
16753 tabStripClass
: this.tabStrip
? this.baseClass
+ (this.tabStrip
? "":"No") + "Strip": null
16757 postMixInProperties: function(){
16758 this.inherited(arguments
);
16760 // Scrolling controller only works for horizontal non-nested tabs
16761 if(!this.controllerWidget
){
16762 this.controllerWidget
= (this.tabPosition
== "top" || this.tabPosition
== "bottom") && !this.nested
?
16763 ScrollingTabController
: TabController
;
16770 'dijit/BackgroundIframe':function(){
16771 define("dijit/BackgroundIframe", [
16772 "require", // require.toUrl
16773 "./main", // to export dijit.BackgroundIframe
16774 "dojo/_base/config",
16775 "dojo/dom-construct", // domConstruct.create
16776 "dojo/dom-style", // domStyle.set
16777 "dojo/_base/lang", // lang.extend lang.hitch
16779 "dojo/sniff", // has("ie"), has("mozilla"), has("quirks")
16780 "dojo/_base/window" // win.doc.createElement
16781 ], function(require
, dijit
, config
, domConstruct
, domStyle
, lang
, on
, has
, win
){
16784 // dijit/BackgroundIFrame
16786 // TODO: remove _frames, it isn't being used much, since popups never release their
16787 // iframes (see [22236])
16788 var _frames
= new function(){
16790 // cache of iframes
16794 this.pop = function(){
16797 iframe
= queue
.pop();
16798 iframe
.style
.display
="";
16801 var burl
= config
["dojoBlankHtmlUrl"] || require
.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
16802 var html
="<iframe src='" + burl
+ "' role='presentation'"
16803 + " style='position: absolute; left: 0px; top: 0px;"
16804 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
16805 iframe
= win
.doc
.createElement(html
);
16807 iframe
= domConstruct
.create("iframe");
16808 iframe
.src
= 'javascript:""';
16809 iframe
.className
= "dijitBackgroundIframe";
16810 iframe
.setAttribute("role", "presentation");
16811 domStyle
.set(iframe
, "opacity", 0.1);
16813 iframe
.tabIndex
= -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
16818 this.push = function(iframe
){
16819 iframe
.style
.display
="none";
16820 queue
.push(iframe
);
16825 dijit
.BackgroundIframe = function(/*DomNode*/ node
){
16827 // For IE/FF z-index schenanigans. id attribute is required.
16830 // new dijit.BackgroundIframe(node).
16832 // Makes a background iframe as a child of node, that fills
16833 // area (and position) of node
16835 if(!node
.id
){ throw new Error("no id"); }
16836 if(has("ie") || has("mozilla")){
16837 var iframe
= (this.iframe
= _frames
.pop());
16838 node
.appendChild(iframe
);
16839 if(has("ie")<7 || has("quirks")){
16841 this._conn
= on(node
, 'resize', lang
.hitch(this, function(){
16845 domStyle
.set(iframe
, {
16853 lang
.extend(dijit
.BackgroundIframe
, {
16854 resize: function(node
){
16856 // Resize the iframe so it's the same size as node.
16857 // Needed on IE6 and IE/quirks because height:100% doesn't work right.
16859 domStyle
.set(this.iframe
, {
16860 width
: node
.offsetWidth
+ 'px',
16861 height
: node
.offsetHeight
+ 'px'
16865 destroy: function(){
16867 // destroy the iframe
16869 this._conn
.remove();
16873 _frames
.push(this.iframe
);
16874 delete this.iframe
;
16879 return dijit
.BackgroundIframe
;
16883 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
16884 'dojo/dnd/Avatar':function(){
16885 define("dojo/dnd/Avatar", [
16886 "../_base/declare",
16891 "../dom-construct",
16894 ], function(declare
, win
, dom
, domAttr
, domClass
, domConstruct
, has
, query
){
16899 return declare("dojo.dnd.Avatar", null, {
16901 // Object that represents transferred DnD items visually
16903 // a DnD manager object
16905 constructor: function(manager
){
16906 this.manager
= manager
;
16911 construct: function(){
16913 // constructor function;
16914 // it is separate so it can be (dynamically) overwritten in case of need
16916 var a
= domConstruct
.create("table", {
16917 "class": "dojoDndAvatar",
16919 position
: "absolute",
16924 source
= this.manager
.source
, node
,
16925 b
= domConstruct
.create("tbody", null, a
),
16926 tr
= domConstruct
.create("tr", null, b
),
16927 td
= domConstruct
.create("td", null, tr
),
16928 k
= Math
.min(5, this.manager
.nodes
.length
), i
= 0;
16930 if(has("highcontrast")){
16931 domConstruct
.create("span", {
16933 innerHTML
: this.manager
.copy
? '+' : "<"
16936 domConstruct
.create("span", {
16937 innerHTML
: source
.generateText
? this._generateText() : ""
16940 // we have to set the opacity on IE only after the node is live
16942 "class": "dojoDndAvatarHeader",
16943 style
: {opacity
: 0.9}
16946 if(source
.creator
){
16947 // create an avatar representation of the node
16948 node
= source
._normalizedCreator(source
.getItem(this.manager
.nodes
[i
].id
).data
, "avatar").node
;
16950 // or just clone the node and hope it works
16951 node
= this.manager
.nodes
[i
].cloneNode(true);
16952 if(node
.tagName
.toLowerCase() == "tr"){
16953 // insert extra table nodes
16954 var table
= domConstruct
.create("table"),
16955 tbody
= domConstruct
.create("tbody", null, table
);
16956 tbody
.appendChild(node
);
16961 tr
= domConstruct
.create("tr", null, b
);
16962 td
= domConstruct
.create("td", null, tr
);
16963 td
.appendChild(node
);
16965 "class": "dojoDndAvatarItem",
16966 style
: {opacity
: (9 - i
) / 10}
16971 destroy: function(){
16973 // destructor for the avatar; called to remove all references so it can be garbage-collected
16974 domConstruct
.destroy(this.node
);
16977 update: function(){
16979 // updates the avatar to reflect the current DnD state
16980 domClass
.toggle(this.node
, "dojoDndAvatarCanDrop", this.manager
.canDropFlag
);
16981 if(has("highcontrast")){
16982 var icon
= dom
.byId("a11yIcon");
16983 var text
= '+'; // assume canDrop && copy
16984 if (this.manager
.canDropFlag
&& !this.manager
.copy
){
16985 text
= '< '; // canDrop && move
16986 }else if (!this.manager
.canDropFlag
&& !this.manager
.copy
){
16987 text
= "o"; //!canDrop && move
16988 }else if(!this.manager
.canDropFlag
){
16989 text
= 'x'; // !canDrop && copy
16991 icon
.innerHTML
=text
;
16994 query(("tr.dojoDndAvatarHeader td span" +(has("highcontrast") ? " span" : "")), this.node
).forEach(
16996 node
.innerHTML
= this.manager
.source
.generateText
? this._generateText() : "";
16999 _generateText: function(){
17001 // generates a proper text to reflect copying or moving of items
17002 return this.manager
.nodes
.length
.toString();
17009 'dijit/form/Button':function(){
17011 'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">●</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
17012 define("dijit/form/Button", [
17014 "dojo/_base/declare", // declare
17015 "dojo/dom-class", // domClass.toggle
17016 "dojo/has", // has("dijit-legacy-requires")
17017 "dojo/_base/kernel", // kernel.deprecated
17018 "dojo/_base/lang", // lang.trim
17022 "dojo/text!./templates/Button.html"
17023 ], function(require
, declare
, domClass
, has
, kernel
, lang
, ready
, _FormWidget
, _ButtonMixin
, template
){
17026 // dijit/form/Button
17028 // Back compat w/1.6, remove for 2.0
17029 if(has("dijit-legacy-requires")){
17030 ready(0, function(){
17031 var requires
= ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
17032 require(requires
); // use indirection so modules not rolled into a build
17036 return declare("dijit.form.Button", [_FormWidget
, _ButtonMixin
], {
17038 // Basically the same thing as a normal HTML button, but with special styling.
17040 // Buttons can display a label, an icon, or both.
17041 // A label should always be specified (through innerHTML) or the label
17042 // attribute. It can be hidden via showLabel=false.
17044 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
17047 // | var button1 = new Button({label: "hello world", onClick: foo});
17048 // | dojo.body().appendChild(button1.domNode);
17050 // showLabel: Boolean
17051 // Set this to true to hide the label text and display only the icon.
17052 // (If showLabel=false then iconClass must be specified.)
17053 // Especially useful for toolbars.
17054 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
17056 // The exception case is for computers in high-contrast mode, where the label
17057 // will still be displayed, since the icon doesn't appear.
17060 // iconClass: String
17061 // Class to apply to DOMNode in button to make it display an icon
17062 iconClass
: "dijitNoIcon",
17063 _setIconClassAttr
: { node
: "iconNode", type
: "class" },
17065 baseClass
: "dijitButton",
17067 templateString
: template
,
17069 // Map widget attributes to DOMNode attributes.
17070 _setValueAttr
: "valueNode",
17072 _onClick: function(/*Event*/ e
){
17074 // Internal function to handle click actions
17075 var ok
= this.inherited(arguments
);
17077 if(this.valueNode
){
17078 this.valueNode
.click();
17079 e
.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
17080 e
.stopPropagation(); // avoid two events bubbling from Button widget
17081 // leave ok = true so that subclasses can do what they need to do
17087 _fillContent: function(/*DomNode*/ source
){
17088 // Overrides _Templated._fillContent().
17089 // If button label is specified as srcNodeRef.innerHTML rather than
17090 // this.params.label, handle it here.
17091 // TODO: remove the method in 2.0, parser will do it all for me
17092 if(source
&& (!this.params
|| !("label" in this.params
))){
17093 var sourceLabel
= lang
.trim(source
.innerHTML
);
17095 this.label
= sourceLabel
; // _applyAttributes will be called after buildRendering completes to update the DOM
17100 _setShowLabelAttr: function(val
){
17101 if(this.containerNode
){
17102 domClass
.toggle(this.containerNode
, "dijitDisplayNone", !val
);
17104 this._set("showLabel", val
);
17107 setLabel: function(/*String*/ content
){
17109 // Deprecated. Use set('label', ...) instead.
17110 kernel
.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
17111 this.set("label", content
);
17114 _setLabelAttr: function(/*String*/ content
){
17116 // Hook for set('label', ...) to work.
17118 // Set the label (text) of the button; takes an HTML string.
17119 // If the label is hidden (showLabel=false) then and no title has
17120 // been specified, then label is also set as title attribute of icon.
17121 this.inherited(arguments
);
17122 if(!this.showLabel
&& !("title" in this.params
)){
17123 this.titleNode
.title
= lang
.trim(this.containerNode
.innerText
|| this.containerNode
.textContent
|| '');
17133 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n",
17134 'dojo/dnd/move':function(){
17135 define("dojo/dnd/move", [
17136 "../_base/declare",
17137 "../dom-geometry", "../dom-style",
17138 "./common", "./Mover", "./Moveable"
17139 ], function(declare
, domGeom
, domStyle
, dnd
, Mover
, Moveable
){
17145 var __constrainedMoveableArgs = declare([Moveable.__MoveableArgs], {
17146 // constraints: Function
17147 // Calculates a constraint box.
17148 // It is called in a context of the moveable object.
17149 constraints: function(){},
17152 // restrict move within boundaries.
17157 var constrainedMoveable
= declare("dojo.dnd.move.constrainedMoveable", Moveable
, {
17158 // object attributes (for markup)
17159 constraints: function(){},
17162 constructor: function(node
, params
){
17164 // an object that makes a node moveable
17166 // a node (or node's id) to be moved
17167 // params: __constrainedMoveableArgs?
17168 // an optional object with additional parameters;
17169 // the rest is passed to the base class
17170 if(!params
){ params
= {}; }
17171 this.constraints
= params
.constraints
;
17172 this.within
= params
.within
;
17174 onFirstMove: function(/*Mover*/ mover
){
17176 // called during the very first move notification;
17177 // can be used to initialize coordinates, can be overwritten.
17178 var c
= this.constraintBox
= this.constraints
.call(this, mover
);
17182 var mb
= domGeom
.getMarginSize(mover
.node
);
17187 onMove: function(/*Mover*/ mover
, /*Object*/ leftTop
){
17189 // called during every move notification;
17190 // should actually move the node; can be overwritten.
17191 var c
= this.constraintBox
, s
= mover
.node
.style
;
17192 this.onMoving(mover
, leftTop
);
17193 leftTop
.l
= leftTop
.l
< c
.l
? c
.l
: c
.r
< leftTop
.l
? c
.r
: leftTop
.l
;
17194 leftTop
.t
= leftTop
.t
< c
.t
? c
.t
: c
.b
< leftTop
.t
? c
.b
: leftTop
.t
;
17195 s
.left
= leftTop
.l
+ "px";
17196 s
.top
= leftTop
.t
+ "px";
17197 this.onMoved(mover
, leftTop
);
17202 var __boxConstrainedMoveableArgs = declare([__constrainedMoveableArgs], {
17204 // a constraint box
17209 var boxConstrainedMoveable
= declare("dojo.dnd.move.boxConstrainedMoveable", constrainedMoveable
, {
17211 // object attributes (for markup)
17214 constructor: function(node
, params
){
17216 // an object, which makes a node moveable
17218 // a node (or node's id) to be moved
17219 // params: __boxConstrainedMoveableArgs?
17220 // an optional object with parameters
17221 var box
= params
&& params
.box
;
17222 this.constraints = function(){ return box
; };
17227 var __parentConstrainedMoveableArgs = declare( [__constrainedMoveableArgs], {
17229 // A parent's area to restrict the move.
17230 // Can be "margin", "border", "padding", or "content".
17235 var parentConstrainedMoveable
= declare("dojo.dnd.move.parentConstrainedMoveable", constrainedMoveable
, {
17237 // object attributes (for markup)
17240 constructor: function(node
, params
){
17242 // an object, which makes a node moveable
17244 // a node (or node's id) to be moved
17245 // params: __parentConstrainedMoveableArgs?
17246 // an optional object with parameters
17247 var area
= params
&& params
.area
;
17248 this.constraints = function(){
17249 var n
= this.node
.parentNode
,
17250 s
= domStyle
.getComputedStyle(n
),
17251 mb
= domGeom
.getMarginBox(n
, s
);
17252 if(area
== "margin"){
17253 return mb
; // Object
17255 var t
= domGeom
.getMarginExtents(n
, s
);
17256 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17257 if(area
== "border"){
17258 return mb
; // Object
17260 t
= domGeom
.getBorderExtents(n
, s
);
17261 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17262 if(area
== "padding"){
17263 return mb
; // Object
17265 t
= domGeom
.getPadExtents(n
, s
);
17266 mb
.l
+= t
.l
, mb
.t
+= t
.t
, mb
.w
-= t
.w
, mb
.h
-= t
.h
;
17267 return mb
; // Object
17276 constrainedMoveable
: constrainedMoveable
,
17277 boxConstrainedMoveable
: boxConstrainedMoveable
,
17278 parentConstrainedMoveable
: parentConstrainedMoveable
17284 'dijit/_WidgetBase':function(){
17285 define("dijit/_WidgetBase", [
17286 "require", // require.toUrl
17287 "dojo/_base/array", // array.forEach array.map
17289 "dojo/_base/config", // config.blankGif
17290 "dojo/_base/connect", // connect.connect
17291 "dojo/_base/declare", // declare
17292 "dojo/dom", // dom.byId
17293 "dojo/dom-attr", // domAttr.set domAttr.remove
17294 "dojo/dom-class", // domClass.add domClass.replace
17295 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
17296 "dojo/dom-geometry", // isBodyLtr
17297 "dojo/dom-style", // domStyle.set, domStyle.get
17299 "dojo/_base/kernel",
17300 "dojo/_base/lang", // mixin(), isArray(), etc.
17303 "dojo/Stateful", // Stateful
17305 "dojo/_base/window", // win.doc, win.body()
17307 "./registry" // registry.getUniqueId(), registry.findWidgets()
17308 ], function(require
, array
, aspect
, config
, connect
, declare
,
17309 dom
, domAttr
, domClass
, domConstruct
, domGeometry
, domStyle
, has
, kernel
,
17310 lang
, on
, ready
, Stateful
, topic
, win
, Destroyable
, registry
){
17313 // dijit/_WidgetBase
17315 // Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
17316 has
.add("dijit-legacy-requires", !kernel
.isAsync
);
17318 // For back-compat, remove in 2.0.
17319 if(has("dijit-legacy-requires")){
17320 ready(0, function(){
17321 var requires
= ["dijit/_base/manager"];
17322 require(requires
); // use indirection so modules not rolled into a build
17326 // Nested hash listing attributes for each tag, all strings in lowercase.
17327 // ex: {"div": {"style": true, "tabindex" true}, "form": { ...
17329 function getAttrs(obj
){
17331 for(var attr
in obj
){
17332 ret
[attr
.toLowerCase()] = true;
17337 function nonEmptyAttrToDom(attr
){
17339 // Returns a setter function that copies the attribute to this.domNode,
17340 // or removes the attribute from this.domNode, depending on whether the
17341 // value is defined or not.
17342 return function(val
){
17343 domAttr
[val
? "set" : "remove"](this.domNode
, attr
, val
);
17344 this._set(attr
, val
);
17348 return declare("dijit._WidgetBase", [Stateful
, Destroyable
], {
17350 // Future base class for all Dijit widgets.
17352 // Future base class for all Dijit widgets.
17353 // _Widget extends this class adding support for various features needed by desktop.
17355 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
17356 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
17358 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
17359 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
17361 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
17363 // - DOM node attribute
17364 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
17365 // | _setFocusAttr: "focusNode" (shorthand)
17366 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
17367 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
17369 // - DOM node innerHTML
17370 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
17371 // Maps this.title to this.titleNode.innerHTML
17373 // - DOM node innerText
17374 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
17375 // Maps this.title to this.titleNode.innerText
17377 // - DOM node CSS class
17378 // | _setMyClassAttr: { node: "domNode", type: "class" }
17379 // Maps this.myClass to this.domNode.className
17381 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
17382 // formats of the above list.
17384 // If the custom setter is null, no action is performed other than saving the new value
17385 // in the widget (in this).
17387 // If no custom setter is defined for an attribute, then it will be copied
17388 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
17389 // That's only done though for attributes that match DOMNode attributes (title,
17390 // alt, aria-labelledby, etc.)
17392 // id: [const] String
17393 // A unique, opaque ID string that can be assigned by users or by the
17394 // system. If the developer passes an ID which is known not to be
17395 // unique, the specified ID is ignored and the system-generated ID is
17398 _setIdAttr
: "domNode", // to copy to this.domNode even for auto-generated id's
17400 // lang: [const] String
17401 // Rarely used. Overrides the default Dojo locale used to render this widget,
17402 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
17403 // Value must be among the list of locales specified during by the Dojo bootstrap,
17404 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
17406 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
17407 _setLangAttr
: nonEmptyAttrToDom("lang"),
17409 // dir: [const] String
17410 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
17411 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
17412 // default direction.
17414 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
17415 _setDirAttr
: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
17418 // Bi-directional support, the main variable which is responsible for the direction of the text.
17419 // The text direction can be different than the GUI direction by using this parameter in creation
17426 // 3. "auto" - contextual the direction of a text defined by first strong letter.
17428 // By default is as the page direction.
17432 // HTML class attribute
17434 _setClassAttr
: { node
: "domNode", type
: "class" },
17436 // style: String||Object
17437 // HTML style attributes as cssText string or name/value hash
17441 // HTML title attribute.
17443 // For form widgets this specifies a tooltip to display when hovering over
17444 // the widget (just like the native HTML title attribute).
17446 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
17447 // etc., it's used to specify the tab label, accordion pane title, etc.
17451 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
17452 // this specifies the tooltip to appear when the mouse is hovered over that text.
17455 // baseClass: [protected] String
17456 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
17460 // srcNodeRef: [readonly] DomNode
17461 // pointer to original DOM node
17464 // domNode: [readonly] DomNode
17465 // This is our visible representation of the widget! Other DOM
17466 // Nodes may by assigned to other properties, usually through the
17467 // template system's data-dojo-attach-point syntax, but the domNode
17468 // property is the canonical "top level" node in widget UI.
17471 // containerNode: [readonly] DomNode
17472 // Designates where children of the source DOM node will be placed.
17473 // "Children" in this case refers to both DOM nodes and widgets.
17474 // For example, for myWidget:
17476 // | <div data-dojo-type=myWidget>
17477 // | <b> here's a plain DOM node
17478 // | <span data-dojo-type=subWidget>and a widget</span>
17479 // | <i> and another plain DOM node </i>
17482 // containerNode would point to:
17484 // | <b> here's a plain DOM node
17485 // | <span data-dojo-type=subWidget>and a widget</span>
17486 // | <i> and another plain DOM node </i>
17488 // In templated widgets, "containerNode" is set via a
17489 // data-dojo-attach-point assignment.
17491 // containerNode must be defined for any widget that accepts innerHTML
17492 // (like ContentPane or BorderContainer or even Button), and conversely
17493 // is null for widgets that don't, like TextBox.
17494 containerNode
: null,
17496 // ownerDocument: [const] Document?
17497 // The document this widget belongs to. If not specified to constructor, will default to
17498 // srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
17499 ownerDocument
: null,
17500 _setOwnerDocumentAttr: function(val
){
17501 // this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
17502 this._set("ownerDocument", val
);
17506 // _started: [readonly] Boolean
17507 // startup() has completed.
17511 // attributeMap: [protected] Object
17512 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
17513 // for each XXX attribute to be mapped to the DOM.
17515 // attributeMap sets up a "binding" between attributes (aka properties)
17516 // of the widget and the widget's DOM.
17517 // Changes to widget attributes listed in attributeMap will be
17518 // reflected into the DOM.
17520 // For example, calling set('title', 'hello')
17521 // on a TitlePane will automatically cause the TitlePane's DOM to update
17522 // with the new title.
17524 // attributeMap is a hash where the key is an attribute of the widget,
17525 // and the value reflects a binding to a:
17527 // - DOM node attribute
17528 // | focus: {node: "focusNode", type: "attribute"}
17529 // Maps this.focus to this.focusNode.focus
17531 // - DOM node innerHTML
17532 // | title: { node: "titleNode", type: "innerHTML" }
17533 // Maps this.title to this.titleNode.innerHTML
17535 // - DOM node innerText
17536 // | title: { node: "titleNode", type: "innerText" }
17537 // Maps this.title to this.titleNode.innerText
17539 // - DOM node CSS class
17540 // | myClass: { node: "domNode", type: "class" }
17541 // Maps this.myClass to this.domNode.className
17543 // If the value is an array, then each element in the array matches one of the
17544 // formats of the above list.
17546 // There are also some shorthands for backwards compatibility:
17548 // - string --> { node: string, type: "attribute" }, for example:
17550 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
17552 // - "" --> { node: "domNode", type: "attribute" }
17555 // _blankGif: [protected] String
17556 // Path to a blank 1x1 image.
17557 // Used by `<img>` nodes in templates that really get their image via CSS background-image.
17558 _blankGif
: config
.blankGif
|| require
.toUrl("dojo/resources/blank.gif"),
17560 //////////// INITIALIZATION METHODS ///////////////////////////////////////
17563 constructor: function(params, srcNodeRef){
17565 // Create the widget.
17566 // params: Object|null
17567 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17568 // and functions, typically callbacks like onClick.
17569 // The hash can contain any of the widget's properties, excluding read-only properties.
17570 // srcNodeRef: DOMNode|String?
17571 // If a srcNodeRef (DOM node) is specified:
17573 // - use srcNodeRef.innerHTML as my contents
17574 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17575 // - otherwise, replace srcNodeRef with my generated DOM tree
17579 postscript: function(/*Object?*/params
, /*DomNode|String*/srcNodeRef
){
17581 // Kicks off widget instantiation. See create() for details.
17584 this.create(params
, srcNodeRef
);
17587 create: function(params
, srcNodeRef
){
17589 // Kick off the life-cycle of a widget
17591 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
17592 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
17593 // for a discussion of the widget creation lifecycle.
17595 // Of course, adventurous developers could override create entirely, but this should
17596 // only be done as a last resort.
17597 // params: Object|null
17598 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
17599 // and functions, typically callbacks like onClick.
17600 // The hash can contain any of the widget's properties, excluding read-only properties.
17601 // srcNodeRef: DOMNode|String?
17602 // If a srcNodeRef (DOM node) is specified:
17604 // - use srcNodeRef.innerHTML as my contents
17605 // - if this is a behavioral widget then apply behavior to that srcNodeRef
17606 // - otherwise, replace srcNodeRef with my generated DOM tree
17610 // store pointer to original DOM tree
17611 this.srcNodeRef
= dom
.byId(srcNodeRef
);
17613 // No longer used, remove for 2.0.
17614 this._connects
= [];
17615 this._supportingWidgets
= [];
17617 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
17618 if(this.srcNodeRef
&& (typeof this.srcNodeRef
.id
== "string")){ this.id
= this.srcNodeRef
.id
; }
17620 // mix in our passed parameters
17622 this.params
= params
;
17623 lang
.mixin(this, params
);
17625 this.postMixInProperties();
17627 // Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
17628 // Do this before buildRendering() because it might expect the id to be there.
17630 this.id
= registry
.getUniqueId(this.declaredClass
.replace(/\./g,"_"));
17632 // if params contains {id: undefined}, prevent _applyAttributes() from processing it
17633 delete this.params
.id
;
17637 // The document and <body> node this widget is associated with
17638 this.ownerDocument
= this.ownerDocument
|| (this.srcNodeRef
? this.srcNodeRef
.ownerDocument
: win
.doc
);
17639 this.ownerDocumentBody
= win
.body(this.ownerDocument
);
17641 registry
.add(this);
17643 this.buildRendering();
17645 var deleteSrcNodeRef
;
17648 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
17649 // Also calls custom setters for all attributes with custom setters.
17650 this._applyAttributes();
17652 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
17653 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
17654 // widget being attached to the DOM since it isn't when a widget is created programmatically like
17655 // new MyWidget({}). See #11635.
17656 var source
= this.srcNodeRef
;
17657 if(source
&& source
.parentNode
&& this.domNode
!== source
){
17658 source
.parentNode
.replaceChild(this.domNode
, source
);
17659 deleteSrcNodeRef
= true;
17662 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
17663 // assuming that dojo._scopeName even exists in 2.0
17664 this.domNode
.setAttribute("widgetId", this.id
);
17668 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
17669 // I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
17670 if(deleteSrcNodeRef
){
17671 delete this.srcNodeRef
;
17674 this._created
= true;
17677 _applyAttributes: function(){
17679 // Step during widget creation to copy widget attributes to the
17680 // DOM according to attributeMap and _setXXXAttr objects, and also to call
17681 // custom _setXXXAttr() methods.
17683 // Skips over blank/false attribute values, unless they were explicitly specified
17684 // as parameters to the widget, since those are the default anyway,
17685 // and setting tabIndex="" is different than not setting tabIndex at all.
17687 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
17688 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
17692 // Get list of attributes where this.set(name, value) will do something beyond
17693 // setting this[name] = value. Specifically, attributes that have:
17694 // - associated _setXXXAttr() method/hash/string/array
17695 // - entries in attributeMap (remove this for 2.0);
17696 var ctor
= this.constructor,
17697 list
= ctor
._setterAttrs
;
17699 list
= (ctor
._setterAttrs
= []);
17700 for(var attr
in this.attributeMap
){
17704 var proto
= ctor
.prototype;
17705 for(var fxName
in proto
){
17706 if(fxName
in this.attributeMap
){ continue; }
17707 var setterName
= "_set" + fxName
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); }) + "Attr";
17708 if(setterName
in proto
){
17714 // Call this.set() for each property that was either specified as parameter to constructor,
17715 // or is in the list found above. For correlated properties like value and displayedValue, the one
17716 // specified as a parameter should take precedence.
17717 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
17718 // NaN and thus is not ignored like a default value of "".
17720 // Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
17721 // Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
17723 for(var key
in this.params
|| {}){
17724 params
[key
] = this[key
];
17727 // Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
17728 array
.forEach(list
, function(attr
){
17729 if(attr
in params
){
17730 // skip this one, do it below
17731 }else if(this[attr
]){
17732 this.set(attr
, this[attr
]);
17736 // Step 3: Call set() for each property that was specified as parameter to constructor.
17737 // Use params hash created above to ignore side effects from step #2 above.
17738 for(key
in params
){
17739 this.set(key
, params
[key
]);
17743 postMixInProperties: function(){
17745 // Called after the parameters to the widget have been read-in,
17746 // but before the widget template is instantiated. Especially
17747 // useful to set properties that are referenced in the widget
17753 buildRendering: function(){
17755 // Construct the UI for this widget, setting this.domNode.
17756 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
17761 // Create root node if it wasn't created by _Templated
17762 this.domNode
= this.srcNodeRef
|| this.ownerDocument
.createElement("div");
17765 // baseClass is a single class name or occasionally a space-separated list of names.
17766 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
17767 // TODO: make baseClass custom setter
17768 if(this.baseClass
){
17769 var classes
= this.baseClass
.split(" ");
17770 if(!this.isLeftToRight()){
17771 classes
= classes
.concat( array
.map(classes
, function(name
){ return name
+"Rtl"; }));
17773 domClass
.add(this.domNode
, classes
);
17777 postCreate: function(){
17779 // Processing after the DOM fragment is created
17781 // Called after the DOM fragment has been created, but not necessarily
17782 // added to the document. Do not include any operations which rely on
17783 // node dimensions or placement.
17788 startup: function(){
17790 // Processing after the DOM fragment is added to the document
17792 // Called after a widget and its children have been created and added to the page,
17793 // and all related widgets have finished their create() cycle, up through postCreate().
17794 // This is useful for composite widgets that need to control or layout sub-widgets.
17795 // Many layout widgets can use this as a wiring phase.
17796 if(this._started
){ return; }
17797 this._started
= true;
17798 array
.forEach(this.getChildren(), function(obj
){
17799 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
17801 obj
._started
= true;
17806 //////////// DESTROY FUNCTIONS ////////////////////////////////
17808 destroyRecursive: function(/*Boolean?*/ preserveDom
){
17810 // Destroy this widget and its descendants
17812 // This is the generic "destructor" function that all widget users
17813 // should call to cleanly discard with a widget. Once a widget is
17814 // destroyed, it is removed from the manager object.
17816 // If true, this method will leave the original DOM structure
17817 // alone of descendant Widgets. Note: This will NOT work with
17818 // dijit._Templated widgets.
17820 this._beingDestroyed
= true;
17821 this.destroyDescendants(preserveDom
);
17822 this.destroy(preserveDom
);
17825 destroy: function(/*Boolean*/ preserveDom
){
17827 // Destroy this widget, but not its descendants.
17828 // This method will, however, destroy internal widgets such as those used within a template.
17829 // preserveDom: Boolean
17830 // If true, this method will leave the original DOM structure alone.
17831 // Note: This will not yet work with _Templated widgets
17833 this._beingDestroyed
= true;
17834 this.uninitialize();
17836 function destroy(w
){
17837 if(w
.destroyRecursive
){
17838 w
.destroyRecursive(preserveDom
);
17839 }else if(w
.destroy
){
17840 w
.destroy(preserveDom
);
17844 // Back-compat, remove for 2.0
17845 array
.forEach(this._connects
, lang
.hitch(this, "disconnect"));
17846 array
.forEach(this._supportingWidgets
, destroy
);
17848 // Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
17849 // here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
17851 array
.forEach(registry
.findWidgets(this.domNode
, this.containerNode
), destroy
);
17854 this.destroyRendering(preserveDom
);
17855 registry
.remove(this.id
);
17856 this._destroyed
= true;
17859 destroyRendering: function(/*Boolean?*/ preserveDom
){
17861 // Destroys the DOM nodes associated with this widget
17863 // If true, this method will leave the original DOM structure alone
17864 // during tear-down. Note: this will not work with _Templated
17870 this.bgIframe
.destroy(preserveDom
);
17871 delete this.bgIframe
;
17876 domAttr
.remove(this.domNode
, "widgetId");
17878 domConstruct
.destroy(this.domNode
);
17880 delete this.domNode
;
17883 if(this.srcNodeRef
){
17885 domConstruct
.destroy(this.srcNodeRef
);
17887 delete this.srcNodeRef
;
17891 destroyDescendants: function(/*Boolean?*/ preserveDom
){
17893 // Recursively destroy the children of this widget and their
17896 // If true, the preserveDom attribute is passed to all descendant
17897 // widget's .destroy() method. Not for use with _Templated
17900 // get all direct descendants and destroy them recursively
17901 array
.forEach(this.getChildren(), function(widget
){
17902 if(widget
.destroyRecursive
){
17903 widget
.destroyRecursive(preserveDom
);
17908 uninitialize: function(){
17910 // Deprecated. Override destroy() instead to implement custom widget tear-down
17917 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
17919 _setStyleAttr: function(/*String||Object*/ value
){
17921 // Sets the style attribute of the widget according to value,
17922 // which is either a hash like {height: "5px", width: "3px"}
17923 // or a plain string
17925 // Determines which node to set the style on based on style setting
17926 // in attributeMap.
17930 var mapNode
= this.domNode
;
17932 // Note: technically we should revert any style setting made in a previous call
17933 // to his method, but that's difficult to keep track of.
17935 if(lang
.isObject(value
)){
17936 domStyle
.set(mapNode
, value
);
17938 if(mapNode
.style
.cssText
){
17939 mapNode
.style
.cssText
+= "; " + value
;
17941 mapNode
.style
.cssText
= value
;
17945 this._set("style", value
);
17948 _attrToDom: function(/*String*/ attr
, /*String*/ value
, /*Object?*/ commands
){
17950 // Reflect a widget attribute (title, tabIndex, duration etc.) to
17951 // the widget DOM, as specified by commands parameter.
17952 // If commands isn't specified then it's looked up from attributeMap.
17953 // Note some attributes like "type"
17954 // cannot be processed this way as they are not mutable.
17956 // Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
17957 // to DOMNode inside the widget, or alternately pointing to a subwidget
17961 commands
= arguments
.length
>= 3 ? commands
: this.attributeMap
[attr
];
17963 array
.forEach(lang
.isArray(commands
) ? commands
: [commands
], function(command
){
17965 // Get target node and what we are doing to that node
17966 var mapNode
= this[command
.node
|| command
|| "domNode"]; // DOM node
17967 var type
= command
.type
|| "attribute"; // class, innerHTML, innerText, or attribute
17971 if(lang
.isFunction(value
)){ // functions execute in the context of the widget
17972 value
= lang
.hitch(this, value
);
17975 // Get the name of the DOM node attribute; usually it's the same
17976 // as the name of the attribute in the widget (attr), but can be overridden.
17977 // Also maps handler names to lowercase, like onSubmit --> onsubmit
17978 var attrName
= command
.attribute
? command
.attribute
:
17979 (/^on[A-Z][a-zA-Z]*$/.test(attr
) ? attr
.toLowerCase() : attr
);
17981 if(mapNode
.tagName
){
17982 // Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
17983 // method, but for consistency we still call domAttr
17984 domAttr
.set(mapNode
, attrName
, value
);
17986 // mapping to a sub-widget
17987 mapNode
.set(attrName
, value
);
17991 mapNode
.innerHTML
= "";
17992 mapNode
.appendChild(this.ownerDocument
.createTextNode(value
));
17995 mapNode
.innerHTML
= value
;
17998 domClass
.replace(mapNode
, value
, this[attr
]);
18004 get: function(name
){
18006 // Get a property from a widget.
18008 // The property to get.
18010 // Get a named property from a widget. The property may
18011 // potentially be retrieved via a getter method. If no getter is defined, this
18012 // just retrieves the object's property.
18014 // For example, if the widget has properties `foo` and `bar`
18015 // and a method named `_getFooAttr()`, calling:
18016 // `myWidget.get("foo")` would be equivalent to calling
18017 // `widget._getFooAttr()` and `myWidget.get("bar")`
18018 // would be equivalent to the expression
18020 var names
= this._getAttrNames(name
);
18021 return this[names
.g
] ? this[names
.g
]() : this[name
];
18024 set: function(name
, value
){
18026 // Set a property on a widget
18028 // The property to set.
18030 // The value to set in the property.
18032 // Sets named properties on a widget which may potentially be handled by a
18033 // setter in the widget.
18035 // For example, if the widget has properties `foo` and `bar`
18036 // and a method named `_setFooAttr()`, calling
18037 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
18038 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
18039 // would be equivalent to the statement `widget.bar = 3;`
18041 // set() may also be called with a hash of name/value pairs, ex:
18043 // | myWidget.set({
18048 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
18050 if(typeof name
=== "object"){
18051 for(var x
in name
){
18052 this.set(x
, name
[x
]);
18056 var names
= this._getAttrNames(name
),
18057 setter
= this[names
.s
];
18058 if(lang
.isFunction(setter
)){
18059 // use the explicit setter
18060 var result
= setter
.apply(this, Array
.prototype.slice
.call(arguments
, 1));
18062 // Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
18063 // Map according to:
18064 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
18065 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
18066 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
18067 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
18068 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
18069 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
18070 var defaultNode
= this.focusNode
&& !lang
.isFunction(this.focusNode
) ? "focusNode" : "domNode",
18071 tag
= this[defaultNode
].tagName
,
18072 attrsForTag
= tagAttrs
[tag
] || (tagAttrs
[tag
] = getAttrs(this[defaultNode
])),
18073 map
= name
in this.attributeMap
? this.attributeMap
[name
] :
18074 names
.s
in this ? this[names
.s
] :
18075 ((names
.l
in attrsForTag
&& typeof value
!= "function") ||
18076 /^aria-|^data-|^role$/.test(name
)) ? defaultNode
: null;
18078 this._attrToDom(name
, value
, map
);
18080 this._set(name
, value
);
18082 return result
|| this;
18085 _attrPairNames
: {}, // shared between all widgets
18086 _getAttrNames: function(name
){
18088 // Helper function for get() and set().
18089 // Caches attribute name values so we don't do the string ops every time.
18093 var apn
= this._attrPairNames
;
18094 if(apn
[name
]){ return apn
[name
]; }
18095 var uc
= name
.replace(/^[a-z]|-[a-zA-Z]/g, function(c
){ return c
.charAt(c
.length
-1).toUpperCase(); });
18096 return (apn
[name
] = {
18098 s
: "_set"+uc
+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
18099 g
: "_get"+uc
+"Attr",
18100 l
: uc
.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
18104 _set: function(/*String*/ name
, /*anything*/ value
){
18106 // Helper function to set new value for specified attribute, and call handlers
18107 // registered with watch() if the value has changed.
18108 var oldValue
= this[name
];
18109 this[name
] = value
;
18110 if(this._created
&& value
!== oldValue
){
18111 if(this._watchCallbacks
){
18112 this._watchCallbacks(name
, oldValue
, value
);
18114 this.emit("attrmodified-" + name
, {
18116 prevValue
: oldValue
,
18123 emit: function(/*String*/ type
, /*Object?*/ eventObj
, /*Array?*/ callbackArgs
){
18125 // Used by widgets to signal that a synthetic event occurred, ex:
18126 // | myWidget.emit("attrmodified-selectedChildWidget", {}).
18128 // Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
18129 // Also calls onType() method, if present, and returns value from that method.
18130 // By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
18131 // Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
18135 // Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
18136 // Also set pointer to widget, although since we can't add a pointer to the widget for native events
18137 // (see #14729), maybe we shouldn't do it here?
18138 eventObj
= eventObj
|| {};
18139 if(eventObj
.bubbles
=== undefined){ eventObj
.bubbles
= true; }
18140 if(eventObj
.cancelable
=== undefined){ eventObj
.cancelable
= true; }
18141 if(!eventObj
.detail
){ eventObj
.detail
= {}; }
18142 eventObj
.detail
.widget
= this;
18144 var ret
, callback
= this["on"+type
];
18146 ret
= callback
.apply(this, callbackArgs
? callbackArgs
: [eventObj
]);
18149 // Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
18150 if(this._started
&& !this._beingDestroyed
){
18151 on
.emit(this.domNode
, type
.toLowerCase(), eventObj
);
18157 on: function(/*String|Function*/ type
, /*Function*/ func
){
18159 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
18161 // Name of event (ex: "click") or extension event like touch.press.
18163 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
18164 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
18165 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
18167 // For backwards compatibility, if there's an onType() method in the widget then connect to that.
18169 var widgetMethod
= this._onMap(type
);
18171 return aspect
.after(this, widgetMethod
, func
, true);
18174 // Otherwise, just listen for the event on this.domNode.
18175 return this.own(on(this.domNode
, type
, func
))[0];
18178 _onMap: function(/*String|Function*/ type
){
18180 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
18181 // If type is a synthetic event like touch.press then returns undefined.
18182 var ctor
= this.constructor, map
= ctor
._onMap
;
18184 map
= (ctor
._onMap
= {});
18185 for(var attr
in ctor
.prototype){
18186 if(/^on/.test(attr
)){
18187 map
[attr
.replace(/^on/, "").toLowerCase()] = attr
;
18191 return map
[typeof type
== "string" && type
.toLowerCase()]; // String
18194 toString: function(){
18196 // Returns a string that represents the widget
18198 // When a widget is cast to a string, this method will be used to generate the
18199 // output. Currently, it does not implement any sort of reversible
18201 return '[Widget ' + this.declaredClass
+ ', ' + (this.id
|| 'NO ID') + ']'; // String
18204 getChildren: function(){
18206 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
18207 // Does not return nested widgets, nor widgets that are part of this widget's template.
18208 return this.containerNode
? registry
.findWidgets(this.containerNode
) : []; // dijit/_WidgetBase[]
18211 getParent: function(){
18213 // Returns the parent widget of this widget
18214 return registry
.getEnclosingWidget(this.domNode
.parentNode
);
18218 /*Object|null*/ obj
,
18219 /*String|Function*/ event
,
18220 /*String|Function*/ method
){
18222 // Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
18224 // Connects specified obj/event to specified method of this object
18225 // and registers for disconnect() on widget destroy.
18227 // Provide widget-specific analog to dojo.connect, except with the
18228 // implicit use of this widget as the target object.
18229 // Events connected with `this.connect` are disconnected upon
18232 // A handle that can be passed to `disconnect` in order to disconnect before
18233 // the widget is destroyed.
18235 // | var btn = new Button();
18236 // | // when foo.bar() is called, call the listener we're going to
18237 // | // provide in the scope of btn
18238 // | btn.connect(foo, "bar", function(){
18239 // | console.debug(this.toString());
18244 return this.own(connect
.connect(obj
, event
, this, method
))[0]; // handle
18247 disconnect: function(handle
){
18249 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18251 // Disconnects handle created by `connect`.
18258 subscribe: function(t
, method
){
18260 // Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
18262 // Subscribes to the specified topic and calls the specified method
18263 // of this object and registers for unsubscribe() on widget destroy.
18265 // Provide widget-specific analog to dojo.subscribe, except with the
18266 // implicit use of this widget as the target object.
18269 // method: Function
18272 // | var btn = new Button();
18273 // | // when /my/topic is published, this button changes its label to
18274 // | // be the parameter of the topic.
18275 // | btn.subscribe("/my/topic", function(v){
18276 // | this.set("label", v);
18280 return this.own(topic
.subscribe(t
, lang
.hitch(this, method
)))[0]; // handle
18283 unsubscribe: function(/*Object*/ handle
){
18285 // Deprecated, will be removed in 2.0, use handle.remove() instead.
18287 // Unsubscribes handle created by this.subscribe.
18288 // Also removes handle from this widget's list of subscriptions
18295 isLeftToRight: function(){
18297 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
18300 return this.dir
? (this.dir
== "ltr") : domGeometry
.isBodyLtr(this.ownerDocument
); //Boolean
18303 isFocusable: function(){
18305 // Return true if this widget can currently be focused
18306 // and false if not
18307 return this.focus
&& (domStyle
.get(this.domNode
, "display") != "none");
18310 placeAt: function(/* String|DomNode|_Widget */ reference
, /* String|Int? */ position
){
18312 // Place this widget somewhere in the DOM based
18313 // on standard domConstruct.place() conventions.
18315 // A convenience function provided in all _Widgets, providing a simple
18316 // shorthand mechanism to put an existing (or newly created) Widget
18317 // somewhere in the dom, and allow chaining.
18319 // Widget, DOMNode, or id of widget or DOMNode
18321 // If reference is a widget (or id of widget), and that widget has an ".addChild" method,
18322 // it will be called passing this widget instance into that method, supplying the optional
18323 // position index passed. In this case position (if specified) should be an integer.
18325 // If reference is a DOMNode (or id matching a DOMNode but not a widget),
18326 // the position argument can be a numeric index or a string
18327 // "first", "last", "before", or "after", same as dojo/dom-construct::place().
18328 // returns: dijit/_WidgetBase
18329 // Provides a useful return of the newly created dijit._Widget instance so you
18330 // can "chain" this function by instantiating, placing, then saving the return value
18333 // | // create a Button with no srcNodeRef, and place it in the body:
18334 // | var button = new Button({ label:"click" }).placeAt(win.body());
18335 // | // now, 'button' is still the widget reference to the newly created button
18336 // | button.on("click", function(e){ console.log('click'); }));
18338 // | // create a button out of a node with id="src" and append it to id="wrapper":
18339 // | var button = new Button({},"src").placeAt("wrapper");
18341 // | // place a new button as the first element of some div
18342 // | var button = new Button({ label:"click" }).placeAt("wrapper","first");
18344 // | // create a contentpane and add it to a TabContainer
18345 // | var tc = dijit.byId("myTabs");
18346 // | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
18348 var refWidget
= !reference
.tagName
&& registry
.byId(reference
);
18349 if(refWidget
&& refWidget
.addChild
&& (!position
|| typeof position
=== "number")){
18350 // Adding this to refWidget and can use refWidget.addChild() to handle everything.
18351 refWidget
.addChild(this, position
);
18353 // "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
18354 // target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
18355 // refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
18356 var ref
= refWidget
?
18357 (refWidget
.containerNode
&& !/after|before|replace/.test(position
||"") ?
18358 refWidget
.containerNode
: refWidget
.domNode
) : dom
.byId(reference
, this.ownerDocument
);
18359 domConstruct
.place(this.domNode
, ref
, position
);
18361 // Start this iff it has a parent widget that's already started.
18362 if(!this._started
&& (this.getParent() || {})._started
){
18369 getTextDir: function(/*String*/ text
,/*String*/ originalDir
){
18371 // Return direction of the text.
18372 // The function overridden in the _BidiSupport module,
18373 // its main purpose is to calculate the direction of the
18374 // text, if was defined by the programmer through textDir.
18377 return originalDir
;
18380 applyTextDir: function(/*===== element, text =====*/){
18382 // The function overridden in the _BidiSupport module,
18383 // originally used for setting element.dir according to this.textDir.
18384 // In this case does nothing.
18385 // element: DOMNode
18391 defer: function(fcn
, delay
){
18393 // Wrapper to setTimeout to avoid deferred functions executing
18394 // after the originating widget has been destroyed.
18395 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
18396 // fcn: function reference
18397 // delay: Optional number (defaults to 0)
18400 var timer
= setTimeout(lang
.hitch(this,
18403 if(!this._destroyed
){
18404 lang
.hitch(this, fcn
)();
18410 remove: function(){
18412 clearTimeout(timer
);
18415 return null; // so this works well: handle = handle.remove();
18424 'dijit/layout/_TabContainerBase':function(){
18426 'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}});
18427 define("dijit/layout/_TabContainerBase", [
18428 "dojo/text!./templates/TabContainer.html",
18429 "./StackContainer",
18430 "./utils", // marginBox2contextBox, layoutChildren
18431 "../_TemplatedMixin",
18432 "dojo/_base/declare", // declare
18433 "dojo/dom-class", // domClass.add
18434 "dojo/dom-geometry", // domGeometry.contentBox
18435 "dojo/dom-style" // domStyle.style
18436 ], function(template
, StackContainer
, layoutUtils
, _TemplatedMixin
, declare
, domClass
, domGeometry
, domStyle
){
18439 // dijit/layout/_TabContainerBase
18442 return declare("dijit.layout._TabContainerBase", [StackContainer
, _TemplatedMixin
], {
18444 // Abstract base class for TabContainer. Must define _makeController() to instantiate
18445 // and return the widget that displays the tab labels
18447 // A TabContainer is a container that has multiple panes, but shows only
18448 // one pane at a time. There are a set of tabs corresponding to each pane,
18449 // where each tab has the name (aka title) of the pane, and optionally a close button.
18451 // tabPosition: String
18452 // Defines where tabs go relative to tab content.
18453 // "top", "bottom", "left-h", "right-h"
18454 tabPosition
: "top",
18456 baseClass
: "dijitTabContainer",
18458 // tabStrip: [const] Boolean
18459 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
18460 // around the set of tabs. Not supported by claro theme.
18463 // nested: [const] Boolean
18464 // If true, use styling for a TabContainer nested inside another TabContainer.
18465 // For tundra etc., makes tabs look like links, and hides the outer
18466 // border since the outer TabContainer already has a border.
18469 templateString
: template
,
18471 postMixInProperties: function(){
18472 // set class name according to tab position, ex: dijitTabContainerTop
18473 this.baseClass
+= this.tabPosition
.charAt(0).toUpperCase() + this.tabPosition
.substr(1).replace(/-.*/, "");
18475 this.srcNodeRef
&& domStyle
.set(this.srcNodeRef
, "visibility", "hidden");
18477 this.inherited(arguments
);
18480 buildRendering: function(){
18481 this.inherited(arguments
);
18483 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
18484 this.tablist
= this._makeController(this.tablistNode
);
18486 if(!this.doLayout
){ domClass
.add(this.domNode
, "dijitTabContainerNoLayout"); }
18489 /* workaround IE's lack of support for "a > b" selectors by
18490 * tagging each node in the template.
18492 domClass
.add(this.domNode
, "dijitTabContainerNested");
18493 domClass
.add(this.tablist
.containerNode
, "dijitTabContainerTabListNested");
18494 domClass
.add(this.tablistSpacer
, "dijitTabContainerSpacerNested");
18495 domClass
.add(this.containerNode
, "dijitTabPaneWrapperNested");
18497 domClass
.add(this.domNode
, "tabStrip-" + (this.tabStrip
? "enabled" : "disabled"));
18501 _setupChild: function(/*dijit/_WidgetBase*/ tab){
18502 // Overrides StackContainer._setupChild().
18503 domClass.add(tab.domNode, "dijitTabPane");
18504 this.inherited(arguments);
18507 startup: function(){
18508 if(this._started){ return; }
18510 // wire up the tablist and its tabs
18511 this.tablist.startup();
18513 this.inherited(arguments);
18516 layout: function(){
18517 // Overrides StackContainer.layout().
18518 // Configure the content pane to take up all the space except for where the tabs are
18520 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
18522 var sc = this.selectedChildWidget;
18525 // position and size the titles and the container node
18526 var titleAlign = this.tabPosition.replace(/-h/, "");
18527 this.tablist.layoutAlign = titleAlign;
18528 var children = [this.tablist, {
18529 domNode: this.tablistSpacer,
18530 layoutAlign: titleAlign
18532 domNode: this.containerNode,
18533 layoutAlign: "client"
18535 layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
18537 // Compute size to make each of my children.
18538 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
18539 this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
18541 if(sc && sc.resize){
18542 sc.resize(this._containerContentBox);
18545 // just layout the tab controller, so it can position left/right buttons etc.
18546 if(this.tablist.resize){
18547 //make the tabs zero width so that they don't interfere with width calc, then reset
18548 var s = this.tablist.domNode.style;
18550 var width = domGeometry.getContentBox(this.domNode).w;
18552 this.tablist.resize({w: width});
18555 // and call resize() on the selected pane just to tell it that it's been made visible
18556 if(sc && sc.resize){
18562 destroy: function(){
18564 this.tablist.destroy();
18566 this.inherited(arguments);
18573 'dijit/form/Form':function(){
18574 define("dijit/form/Form", [
18575 "dojo/_base/declare", // declare
18576 "dojo/dom-attr", // domAttr.set
18577 "dojo/_base/event", // event.stop
18578 "dojo/_base/kernel", // kernel.deprecated
18579 "dojo/sniff", // has("ie")
18581 "../_TemplatedMixin",
18583 "../layout/_ContentPaneResizeMixin"
18584 ], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
18590 return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
18592 // Widget corresponding to HTML form tag, for validation and serialization
18595 // | <form data-dojo-type="dijit/form/Form" id="myForm">
18596 // | Name: <input type="text" name="name" />
18598 // | myObj = {name: "John Doe"};
18599 // | dijit.byId('myForm').set('value', myObj);
18601 // | myObj=dijit.byId('myForm').get('value');
18603 // HTML <FORM> attributes
18606 // Name of form for scripting.
18610 // Server-side form handler.
18614 // HTTP method used to submit the form, either "GET" or "POST".
18617 // encType: String?
18618 // Encoding type for the form, ex: application/x-www-form-urlencoded.
18621 // accept-charset: String?
18622 // List of supported charsets.
18623 "accept-charset": "",
18626 // List of MIME types for file upload.
18630 // Target frame for the document to be opened in.
18633 templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
18635 postMixInProperties: function(){
18636 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
18637 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
18638 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
18639 this.inherited(arguments);
18642 execute: function(/*Object*/ /*===== formContents =====*/){
18644 // Deprecated: use submit()
18649 onExecute: function(){
18651 // Deprecated: use onSubmit()
18656 _setEncTypeAttr: function(/*String*/ value
){
18657 this.encType
= value
;
18658 domAttr
.set(this.domNode
, "encType", value
);
18659 if(has("ie")){ this.domNode
.encoding
= value
; }
18662 reset: function(/*Event?*/ e
){
18664 // restores all widget values back to their init values,
18665 // calls onReset() which can cancel the reset by returning false
18667 // create fake event so we can know if preventDefault() is called
18669 returnValue
: true, // the IE way
18670 preventDefault: function(){ // not IE
18671 this.returnValue
= false;
18673 stopPropagation: function(){},
18674 currentTarget
: e
? e
.target
: this.domNode
,
18675 target
: e
? e
.target
: this.domNode
18677 // if return value is not exactly false, and haven't called preventDefault(), then reset
18678 if(!(this.onReset(faux
) === false) && faux
.returnValue
){
18679 this.inherited(arguments
, []);
18683 onReset: function(/*Event?*/ /*===== e =====*/){
18685 // Callback when user resets the form. This method is intended
18686 // to be over-ridden. When the `reset` method is called
18687 // programmatically, the return value from `onReset` is used
18688 // to compute whether or not resetting should proceed
18691 return true; // Boolean
18694 _onReset: function(e
){
18700 _onSubmit: function(e
){
18701 var fp
= this.constructor.prototype;
18702 // TODO: remove this if statement beginning with 2.0
18703 if(this.execute
!= fp
.execute
|| this.onExecute
!= fp
.onExecute
){
18704 kernel
.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
18706 this.execute(this.getValues());
18708 if(this.onSubmit(e
) === false){ // only exactly false stops submit
18713 onSubmit: function(/*Event?*/ /*===== e =====*/){
18715 // Callback when user submits the form.
18717 // This method is intended to be over-ridden, but by default it checks and
18718 // returns the validity of form elements. When the `submit`
18719 // method is called programmatically, the return value from
18720 // `onSubmit` is used to compute whether or not submission
18725 return this.isValid(); // Boolean
18728 submit: function(){
18730 // programmatically submit form if and only if the `onSubmit` returns true
18731 if(!(this.onSubmit() === false)){
18732 this.containerNode
.submit();
18739 'dojo/store/Memory':function(){
18740 define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
18741 function(declare
, QueryResults
, SimpleQueryEngine
/*=====, Store =====*/){
18744 // dojo/store/Memory
18746 // No base class, but for purposes of documentation, the base class is dojo/store/api/Store
18748 /*===== base = Store; =====*/
18750 return declare("dojo.store.Memory", base
, {
18752 // This is a basic in-memory object store. It implements dojo/store/api/Store.
18753 constructor: function(options
){
18755 // Creates a memory object store.
18756 // options: dojo/store/Memory
18757 // This provides any configuration information that will be mixed into the store.
18758 // This should generally include the data property to provide the starting set of data.
18759 for(var i
in options
){
18760 this[i
] = options
[i
];
18762 this.setData(this.data
|| []);
18765 // The array of all the objects in the memory store
18768 // idProperty: String
18769 // Indicates the property to use as the identity property. The values of this
18770 // property should be unique.
18774 // An index of data indices into the data array by id
18777 // queryEngine: Function
18778 // Defines the query engine to use for querying the data store
18779 queryEngine
: SimpleQueryEngine
,
18782 // Retrieves an object by its identity
18784 // The identity to use to lookup the object
18786 // The object in the store that matches the given id.
18787 return this.data
[this.index
[id
]];
18789 getIdentity: function(object
){
18791 // Returns an object's identity
18793 // The object to get the identity from
18795 return object
[this.idProperty
];
18797 put: function(object
, options
){
18799 // Stores an object
18801 // The object to store.
18802 // options: dojo/store/api/Store.PutDirectives?
18803 // Additional metadata for storing the data. Includes an "id"
18804 // property if a specific id is to be used.
18806 var data
= this.data
,
18807 index
= this.index
,
18808 idProperty
= this.idProperty
;
18809 var id
= object
[idProperty
] = (options
&& "id" in options
) ? options
.id
: idProperty
in object
? object
[idProperty
] : Math
.random();
18812 if(options
&& options
.overwrite
=== false){
18813 throw new Error("Object already exists");
18815 // replace the entry in data
18816 data
[index
[id
]] = object
;
18818 // add the new object
18819 index
[id
] = data
.push(object
) - 1;
18823 add: function(object
, options
){
18825 // Creates an object, throws an error if the object already exists
18827 // The object to store.
18828 // options: dojo/store/api/Store.PutDirectives?
18829 // Additional metadata for storing the data. Includes an "id"
18830 // property if a specific id is to be used.
18832 (options
= options
|| {}).overwrite
= false;
18833 // call put with overwrite being false
18834 return this.put(object
, options
);
18836 remove: function(id
){
18838 // Deletes an object by its identity
18840 // The identity to use to delete the object
18841 // returns: Boolean
18842 // Returns true if an object was removed, falsy (undefined) if no object matched the id
18843 var index
= this.index
;
18844 var data
= this.data
;
18846 data
.splice(index
[id
], 1);
18847 // now we have to reindex
18848 this.setData(data
);
18852 query: function(query
, options
){
18854 // Queries the store for objects.
18856 // The query to use for retrieving objects from the store.
18857 // options: dojo/store/api/Store.QueryOptions?
18858 // The optional arguments to apply to the resultset.
18859 // returns: dojo/store/api/Store.QueryResults
18860 // The results of the query, extended with iterative methods.
18863 // Given the following store:
18865 // | var store = new Memory({
18867 // | {id: 1, name: "one", prime: false },
18868 // | {id: 2, name: "two", even: true, prime: true},
18869 // | {id: 3, name: "three", prime: true},
18870 // | {id: 4, name: "four", even: true, prime: false},
18871 // | {id: 5, name: "five", prime: true}
18875 // ...find all items where "prime" is true:
18877 // | var results = store.query({ prime: true });
18879 // ...or find all items where "even" is true:
18881 // | var results = store.query({ even: true });
18882 return QueryResults(this.queryEngine(query
, options
)(this.data
));
18884 setData: function(data
){
18886 // Sets the given data as the source for this store, and indexes it
18888 // An array of objects to use as the source of data.
18890 // just for convenience with the data format IFRS expects
18891 this.idProperty
= data
.identifier
;
18892 data
= this.data
= data
.items
;
18897 for(var i
= 0, l
= data
.length
; i
< l
; i
++){
18898 this.index
[data
[i
][this.idProperty
]] = i
;
18906 'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n",
18907 'dijit/_base/sniff':function(){
18908 define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
18911 // dijit/_base/sniff
18916 // Deprecated, back compatibility module, new code should require dojo/uacss directly instead of this module.
18922 'dijit/Toolbar':function(){
18923 define("dijit/Toolbar", [
18925 "dojo/_base/declare", // declare
18927 "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
18930 "./_KeyNavContainer",
18931 "./_TemplatedMixin"
18932 ], function(require
, declare
, has
, keys
, ready
, _Widget
, _KeyNavContainer
, _TemplatedMixin
){
18938 // Back compat w/1.6, remove for 2.0
18939 if(has("dijit-legacy-requires")){
18940 ready(0, function(){
18941 var requires
= ["dijit/ToolbarSeparator"];
18942 require(requires
); // use indirection so modules not rolled into a build
18946 return declare("dijit.Toolbar", [_Widget
, _TemplatedMixin
, _KeyNavContainer
], {
18948 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
18951 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
18954 baseClass
: "dijitToolbar",
18956 postCreate: function(){
18957 this.inherited(arguments
);
18959 this.connectKeyNavHandlers(
18960 this.isLeftToRight() ? [keys
.LEFT_ARROW
] : [keys
.RIGHT_ARROW
],
18961 this.isLeftToRight() ? [keys
.RIGHT_ARROW
] : [keys
.LEFT_ARROW
]
18968 'dijit/layout/StackContainer':function(){
18969 define("dijit/layout/StackContainer", [
18970 "dojo/_base/array", // array.forEach array.indexOf array.some
18971 "dojo/cookie", // cookie
18972 "dojo/_base/declare", // declare
18973 "dojo/dom-class", // domClass.add domClass.replace
18974 "dojo/has", // has("dijit-legacy-requires")
18975 "dojo/_base/lang", // lang.extend
18977 "dojo/topic", // publish
18978 "../registry", // registry.byId
18981 "dojo/i18n!../nls/common"
18982 ], function(array
, cookie
, declare
, domClass
, has
, lang
, ready
, topic
,
18983 registry
, _WidgetBase
, _LayoutWidget
){
18986 // dijit/layout/StackContainer
18988 // Back compat w/1.6, remove for 2.0
18989 if(has("dijit-legacy-requires")){
18990 ready(0, function(){
18991 var requires
= ["dijit/layout/StackController"];
18992 require(requires
); // use indirection so modules not rolled into a build
18996 var StackContainer
= declare("dijit.layout.StackContainer", _LayoutWidget
, {
18998 // A container that has multiple children, but shows only
18999 // one child at a time
19002 // A container for widgets (ContentPanes, for example) That displays
19003 // only one Widget at a time.
19005 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
19007 // Can be base class for container, Wizard, Show, etc.
19009 // See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
19010 // children of a `StackContainer`.
19012 // doLayout: Boolean
19013 // If true, change the size of my currently displayed child to match my size
19016 // persist: Boolean
19017 // Remembers the selected child across sessions
19020 baseClass
: "dijitStackContainer",
19023 // selectedChildWidget: [readonly] dijit._Widget
19024 // References the currently selected child widget, if any.
19025 // Adjust selected child with selectChild() method.
19026 selectedChildWidget: null,
19029 buildRendering: function(){
19030 this.inherited(arguments
);
19031 domClass
.add(this.domNode
, "dijitLayoutContainer");
19032 this.containerNode
.setAttribute("role", "tabpanel");
19035 postCreate: function(){
19036 this.inherited(arguments
);
19037 this.connect(this.domNode
, "onkeypress", this._onKeyPress
);
19040 startup: function(){
19041 if(this._started
){ return; }
19043 var children
= this.getChildren();
19045 // Setup each page panel to be initially hidden
19046 array
.forEach(children
, this._setupChild
, this);
19048 // Figure out which child to initially display, defaulting to first one
19050 this.selectedChildWidget
= registry
.byId(cookie(this.id
+ "_selectedChild"));
19052 array
.some(children
, function(child
){
19053 if(child
.selected
){
19054 this.selectedChildWidget
= child
;
19056 return child
.selected
;
19059 var selected
= this.selectedChildWidget
;
19060 if(!selected
&& children
[0]){
19061 selected
= this.selectedChildWidget
= children
[0];
19062 selected
.selected
= true;
19065 // Publish information about myself so any StackControllers can initialize.
19066 // This needs to happen before this.inherited(arguments) so that for
19067 // TabContainer, this._contentBox doesn't include the space for the tab labels.
19068 topic
.publish(this.id
+"-startup", {children
: children
, selected
: selected
});
19070 // Startup each child widget, and do initial layout like setting this._contentBox,
19071 // then calls this.resize() which does the initial sizing on the selected child.
19072 this.inherited(arguments
);
19075 resize: function(){
19076 // Overrides _LayoutWidget.resize()
19077 // Resize is called when we are first made visible (it's called from startup()
19078 // if we are initially visible). If this is the first time we've been made
19079 // visible then show our first child.
19080 if(!this._hasBeenShown
){
19081 this._hasBeenShown
= true;
19082 var selected
= this.selectedChildWidget
;
19084 this._showChild(selected
);
19087 this.inherited(arguments
);
19090 _setupChild: function(/*dijit/_WidgetBase*/ child){
19091 // Overrides _LayoutWidget._setupChild()
19093 this.inherited(arguments);
19095 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
19097 // remove the title attribute so it doesn't show up when i hover
19099 child.domNode.title = "";
19102 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
19103 // Overrides _Container.addChild() to do layout and publish events
19105 this.inherited(arguments
);
19108 topic
.publish(this.id
+"-addChild", child
, insertIndex
); // publish
19110 // in case the tab titles have overflowed from one line to two lines
19111 // (or, if this if first child, from zero lines to one line)
19112 // TODO: w/ScrollingTabController this is no longer necessary, although
19113 // ScrollTabController.resize() does need to get called to show/hide
19114 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
19115 // If this is updated to not layout [except for initial child added / last child removed], update
19116 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
19119 // if this is the first child, then select it
19120 if(!this.selectedChildWidget
){
19121 this.selectChild(child
);
19126 removeChild: function(/*dijit/_WidgetBase*/ page){
19127 // Overrides _Container.removeChild() to do layout and publish events
19129 this.inherited(arguments);
19132 // this will notify any tablists to remove a button; do this first because it may affect sizing
19133 topic.publish(this.id + "-removeChild", page); // publish
19136 // If all our children are being destroyed than don't run the code below (to select another page),
19137 // because we are deleting every page one by one
19138 if(this._descendantsBeingDestroyed){ return; }
19140 // Select new page to display, also updating TabController to show the respective tab.
19141 // Do this before layout call because it can affect the height of the TabController.
19142 if(this.selectedChildWidget === page){
19143 this.selectedChildWidget = undefined;
19145 var children = this.getChildren();
19146 if(children.length){
19147 this.selectChild(children[0]);
19153 // In case the tab titles now take up one line instead of two lines
19154 // (note though that ScrollingTabController never overflows to multiple lines),
19155 // or the height has changed slightly because of addition/removal of tab which close icon
19160 selectChild: function(/*dijit/_WidgetBase|String*/ page, /*Boolean*/ animate
){
19162 // Show the given widget (which must be one of my children)
19164 // Reference to child widget or id of child widget
19166 page
= registry
.byId(page
);
19168 if(this.selectedChildWidget
!= page
){
19169 // Deselect old page and select new one
19170 var d
= this._transition(page
, this.selectedChildWidget
, animate
);
19171 this._set("selectedChildWidget", page
);
19172 topic
.publish(this.id
+"-selectChild", page
); // publish
19175 cookie(this.id
+ "_selectedChild", this.selectedChildWidget
.id
);
19179 return d
; // If child has an href, promise that fires when the child's href finishes loading
19182 _transition: function(newWidget
, oldWidget
/*===== , animate =====*/){
19184 // Hide the old widget and display the new widget.
19185 // Subclasses should override this.
19186 // newWidget: dijit/_WidgetBase
19187 // The newly selected widget.
19188 // oldWidget: dijit/_WidgetBase
19189 // The previously selected widget.
19190 // animate: Boolean
19191 // Used by AccordionContainer to turn on/off slide effect.
19193 // protected extension
19195 this._hideChild(oldWidget
);
19197 var d
= this._showChild(newWidget
);
19199 // Size the new widget, in case this is the first time it's being shown,
19200 // or I have been resized since the last time it was shown.
19201 // Note that page must be visible for resizing to work.
19202 if(newWidget
.resize
){
19204 newWidget
.resize(this._containerContentBox
|| this._contentBox
);
19206 // the child should pick it's own size but we still need to call resize()
19207 // (with no arguments) to let the widget lay itself out
19208 newWidget
.resize();
19212 return d
; // If child has an href, promise that fires when the child's href finishes loading
19215 _adjacent: function(/*Boolean*/ forward
){
19217 // Gets the next/previous child widget in this container from the current selection.
19219 // TODO: remove for 2.0 if this isn't being used. Otherwise, fix to skip disabled tabs.
19221 var children
= this.getChildren();
19222 var index
= array
.indexOf(children
, this.selectedChildWidget
);
19223 index
+= forward
? 1 : children
.length
- 1;
19224 return children
[ index
% children
.length
]; // dijit/_WidgetBase
19227 forward: function(){
19229 // Advance to next page.
19230 return this.selectChild(this._adjacent(true), true);
19235 // Go back to previous page.
19236 return this.selectChild(this._adjacent(false), true);
19239 _onKeyPress: function(e
){
19240 topic
.publish(this.id
+"-containerKeyPress", { e
: e
, page
: this}); // publish
19243 layout: function(){
19244 // Implement _LayoutWidget.layout() virtual method.
19245 var child
= this.selectedChildWidget
;
19246 if(child
&& child
.resize
){
19248 child
.resize(this._containerContentBox
|| this._contentBox
);
19255 _showChild: function(/*dijit/_WidgetBase*/ page){
19257 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
19258 // it can do any updates it needs regarding loading href's etc.
19260 // Promise that fires when page has finished showing, or true if there's no href
19261 var children = this.getChildren();
19262 page.isFirstChild = (page == children[0]);
19263 page.isLastChild = (page == children[children.length-1]);
19264 page._set("selected", true);
19266 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
19268 return (page._onShow && page._onShow()) || true;
19271 _hideChild: function(/*dijit/_WidgetBase*/ page){
19273 // Hide the specified child by changing it's CSS, and call _onHide() so
19275 page._set("selected", false);
19276 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
19278 page.onHide && page.onHide();
19281 closeChild: function(/*dijit/_WidgetBase*/ page){
19283 // Callback when user clicks the [X] to remove a page.
19284 // If onClose() returns true then remove and destroy the child.
19287 var remove = page.onClose(this, page);
19289 this.removeChild(page);
19290 // makes sure we can clean up executeScripts in ContentPane onUnLoad
19291 page.destroyRecursive();
19295 destroyDescendants: function(/*Boolean*/ preserveDom
){
19296 this._descendantsBeingDestroyed
= true;
19297 this.selectedChildWidget
= undefined;
19298 array
.forEach(this.getChildren(), function(child
){
19300 this.removeChild(child
);
19302 child
.destroyRecursive(preserveDom
);
19304 this._descendantsBeingDestroyed
= false;
19308 StackContainer
.ChildWidgetProperties
= {
19310 // These properties can be specified for the children of a StackContainer.
19312 // selected: Boolean
19313 // Specifies that this widget should be the initially displayed pane.
19314 // Note: to change the selected child use `dijit/layout/StackContainer.selectChild`
19317 // disabled: Boolean
19318 // Specifies that the button to select this pane should be disabled.
19319 // Doesn't affect programmatic selection of the pane, nor does it deselect the pane if it is currently selected.
19322 // closable: Boolean
19323 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
19326 // iconClass: String
19327 // CSS Class specifying icon to use in label associated with this pane.
19328 iconClass
: "dijitNoIcon",
19330 // showTitle: Boolean
19331 // When true, display title of this widget as tab label etc., rather than just using
19332 // icon specified in iconClass
19336 // Since any widget can be specified as a StackContainer child, mix them
19337 // into the base widget class. (This is a hack, but it's effective.)
19338 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
19339 lang
.extend(_WidgetBase
, /*===== {} || =====*/ StackContainer
.ChildWidgetProperties
);
19341 return StackContainer
;
19345 'dojo/regexp':function(){
19346 define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo
, lang
){
19353 // Regular expressions and Builder resources
19355 lang
.setObject("dojo.regexp", regexp
);
19357 regexp
.escapeString = function(/*String*/str
, /*String?*/except
){
19359 // Adds escape sequences for special characters in regular expressions
19361 // a String with special characters to be left unescaped
19363 return str
.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch
){
19364 if(except
&& except
.indexOf(ch
) != -1){
19371 regexp
.buildGroupRE = function(/*Object|Array*/arr
, /*Function*/re
, /*Boolean?*/nonCapture
){
19373 // Builds a regular expression that groups subexpressions
19375 // A utility function used by some of the RE generators. The
19376 // subexpressions are constructed by the function, re, in the second
19377 // parameter. re builds one subexpression for each elem in the array
19378 // a, in the first parameter. Returns a string for a regular
19379 // expression that groups all the subexpressions.
19381 // A single value or an array of values.
19383 // A function. Takes one parameter and converts it to a regular
19386 // If true, uses non-capturing match, otherwise matches are retained
19387 // by regular expression. Defaults to false
19389 // case 1: a is a single value.
19390 if(!(arr
instanceof Array
)){
19391 return re(arr
); // String
19394 // case 2: a is an array
19396 for(var i
= 0; i
< arr
.length
; i
++){
19397 // convert each elem to a RE
19398 b
.push(re(arr
[i
]));
19401 // join the REs as alternatives in a RE group.
19402 return regexp
.group(b
.join("|"), nonCapture
); // String
19405 regexp
.group = function(/*String*/expression
, /*Boolean?*/nonCapture
){
19407 // adds group match to expression
19409 // If true, uses non-capturing match, otherwise matches are retained
19410 // by regular expression.
19411 return "(" + (nonCapture
? "?:":"") + expression
+ ")"; // String
19418 'dijit/DropDownMenu':function(){
19420 'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
19421 define("dijit/DropDownMenu", [
19422 "dojo/_base/declare", // declare
19423 "dojo/_base/event", // event.stop
19424 "dojo/keys", // keys
19425 "dojo/text!./templates/Menu.html",
19426 "./_OnDijitClickMixin",
19428 ], function(declare
, event
, keys
, template
, _OnDijitClickMixin
, _MenuBase
){
19431 // dijit/DropDownMenu
19433 return declare("dijit.DropDownMenu", [_MenuBase
, _OnDijitClickMixin
], {
19435 // A menu, without features for context menu (Meaning, drop down menu)
19437 templateString
: template
,
19439 baseClass
: "dijitMenu",
19441 postCreate: function(){
19442 this.inherited(arguments
);
19443 var l
= this.isLeftToRight();
19444 this._openSubMenuKey
= l
? keys
.RIGHT_ARROW
: keys
.LEFT_ARROW
;
19445 this._closeSubMenuKey
= l
? keys
.LEFT_ARROW
: keys
.RIGHT_ARROW
;
19446 this.connectKeyNavHandlers([keys
.UP_ARROW
], [keys
.DOWN_ARROW
]);
19449 _onKeyPress: function(/*Event*/ evt
){
19451 // Handle keyboard based menu navigation.
19455 if(evt
.ctrlKey
|| evt
.altKey
){ return; }
19457 switch(evt
.charOrCode
){
19458 case this._openSubMenuKey
:
19459 this._moveToPopup(evt
);
19462 case this._closeSubMenuKey
:
19463 if(this.parentMenu
){
19464 if(this.parentMenu
._isMenuBar
){
19465 this.parentMenu
.focusPrev();
19467 this.onCancel(false);
19479 'dijit/form/_FormMixin':function(){
19480 define("dijit/form/_FormMixin", [
19481 "dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
19482 "dojo/_base/declare", // declare
19483 "dojo/_base/kernel", // kernel.deprecated
19484 "dojo/_base/lang", // lang.hitch lang.isArray
19486 "dojo/window" // winUtils.scrollIntoView
19487 ], function(array
, declare
, kernel
, lang
, on
, winUtils
){
19490 // dijit/form/_FormMixin
19492 return declare("dijit.form._FormMixin", null, {
19494 // Mixin for containers of form widgets (i.e. widgets that represent a single value
19495 // and can be children of a `<form>` node or `dijit/form/Form` widget)
19497 // Can extract all the form widgets
19498 // values and combine them into a single javascript object, or alternately
19499 // take such an object and set the values for all the contained
19504 // Name/value hash for each child widget with a name and value.
19505 // Child widgets without names are not part of the hash.
19507 // If there are multiple child widgets w/the same name, value is an array,
19508 // unless they are radio buttons in which case value is a scalar (since only
19509 // one radio button can be checked at a time).
19511 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
19514 // | { name: "John Smith", interests: ["sports", "movies"] }
19517 // state: [readonly] String
19518 // Will be "Error" if one or more of the child widgets has an invalid value,
19519 // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
19520 // which indicates that the form is ready to be submitted.
19525 // * better handling for arrays. Often form elements have names with [] like
19526 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
19529 _getDescendantFormWidgets: function(/*dijit/_WidgetBase[]?*/ children){
19531 // Returns all form widget descendants, searching through non-form child widgets like BorderContainer
19533 array.forEach(children || this.getChildren(), function(child){
19534 if("value" in child){
19537 res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
19544 array.forEach(this._getDescendantFormWidgets(), function(widget){
19551 validate: function(){
19553 // returns if the form is valid - same as isValid - but
19554 // provides a few additional (ui-specific) features:
19556 // 1. it will highlight any sub-widgets that are not valid
19557 // 2. it will call focus() on the first invalid sub-widget
19558 var didFocus = false;
19559 return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
19560 // Need to set this so that "required" widgets get their
19562 widget._hasBeenBlurred = true;
19563 var valid = widget.disabled || !widget.validate || widget.validate();
19564 if(!valid && !didFocus){
19565 // Set focus of the first non-valid widget
19566 winUtils.scrollIntoView(widget.containerNode || widget.domNode);
19571 }), function(item){ return item; });
19574 setValues: function(val){
19575 kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
19576 return this.set('value', val);
19578 _setValueAttr: function(/*Object*/ obj
){
19580 // Fill in form values from according to an Object (in the format returned by get('value'))
19582 // generate map from name --> [list of widgets with that name]
19584 array
.forEach(this._getDescendantFormWidgets(), function(widget
){
19585 if(!widget
.name
){ return; }
19586 var entry
= map
[widget
.name
] || (map
[widget
.name
] = [] );
19587 entry
.push(widget
);
19590 for(var name
in map
){
19591 if(!map
.hasOwnProperty(name
)){
19594 var widgets
= map
[name
], // array of widgets w/this name
19595 values
= lang
.getObject(name
, false, obj
); // list of values for those widgets
19597 if(values
=== undefined){
19600 if(!lang
.isArray(values
)){
19601 values
= [ values
];
19603 if(typeof widgets
[0].checked
== 'boolean'){
19604 // for checkbox/radio, values is a list of which widgets should be checked
19605 array
.forEach(widgets
, function(w
){
19606 w
.set('value', array
.indexOf(values
, w
.value
) != -1);
19608 }else if(widgets
[0].multiple
){
19609 // it takes an array (e.g. multi-select)
19610 widgets
[0].set('value', values
);
19612 // otherwise, values is a list of values to be assigned sequentially to each widget
19613 array
.forEach(widgets
, function(w
, i
){
19614 w
.set('value', values
[i
]);
19620 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
19622 array.forEach(this.containerNode.elements, function(element){
19623 if(element.name == ''){return}; // like "continue"
19624 var namePath = element.name.split(".");
19626 var name=namePath[namePath.length-1];
19627 for(var j=1,len2=namePath.length;j<len2;++j){
19628 var p=namePath[j - 1];
19629 // repeater support block
19630 var nameA=p.split("[");
19631 if(nameA.length > 1){
19632 if(typeof(myObj[nameA[0]]) == "undefined"){
19633 myObj[nameA[0]]=[ ];
19636 nameIndex=parseInt(nameA[1]);
19637 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
19638 myObj[nameA[0]][nameIndex] = { };
19640 myObj=myObj[nameA[0]][nameIndex];
19642 } // repeater support ends
19644 if(typeof(myObj[p]) == "undefined"){
19651 if(typeof(myObj) == "undefined"){
19652 return; // like "continue"
19654 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
19655 return; // like "continue"
19658 // TODO: widget values (just call set('value', ...) on the widget)
19660 // TODO: maybe should call dojo.getNodeProp() instead
19661 switch(element.type){
19663 element.checked = (name in myObj) &&
19664 array.some(myObj[name], function(val){ return val == element.value; });
19667 element.checked = (name in myObj) && myObj[name] == element.value;
19669 case "select-multiple":
19670 element.selectedIndex=-1;
19671 array.forEach(element.options, function(option){
19672 option.selected = array.some(myObj[name], function(val){ return option.value == val; });
19676 element.selectedIndex="0";
19677 array.forEach(element.options, function(option){
19678 option.selected = option.value == myObj[name];
19685 element.value = myObj[name] || "";
19691 // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
19692 // which I am monitoring.
19695 getValues: function(){
19696 kernel
.deprecated(this.declaredClass
+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
19697 return this.get('value');
19699 _getValueAttr: function(){
19701 // Returns Object representing form values. See description of `value` for details.
19704 // The value is updated into this.value every time a child has an onChange event,
19705 // so in the common case this function could just return this.value. However,
19706 // that wouldn't work when:
19708 // 1. User presses return key to submit a form. That doesn't fire an onchange event,
19709 // and even if it did it would come too late due to the defer(...) in _handleOnChange()
19711 // 2. app for some reason calls this.get("value") while the user is typing into a
19712 // form field. Not sure if that case needs to be supported or not.
19714 // get widget values
19716 array
.forEach(this._getDescendantFormWidgets(), function(widget
){
19717 var name
= widget
.name
;
19718 if(!name
|| widget
.disabled
){ return; }
19720 // Single value widget (checkbox, radio, or plain <input> type widget)
19721 var value
= widget
.get('value');
19723 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
19724 if(typeof widget
.checked
== 'boolean'){
19725 if(/Radio/.test(widget
.declaredClass
)){
19727 if(value
!== false){
19728 lang
.setObject(name
, value
, obj
);
19730 // give radio widgets a default of null
19731 value
= lang
.getObject(name
, false, obj
);
19732 if(value
=== undefined){
19733 lang
.setObject(name
, null, obj
);
19737 // checkbox/toggle button
19738 var ary
=lang
.getObject(name
, false, obj
);
19741 lang
.setObject(name
, ary
, obj
);
19743 if(value
!== false){
19748 var prev
=lang
.getObject(name
, false, obj
);
19749 if(typeof prev
!= "undefined"){
19750 if(lang
.isArray(prev
)){
19753 lang
.setObject(name
, [prev
, value
], obj
);
19757 lang
.setObject(name
, value
, obj
);
19763 * code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
19764 * but it doesn't understand [] notation, presumably)
19766 array.forEach(this.containerNode.elements, function(elm){
19768 return; // like "continue"
19770 var namePath = elm.name.split(".");
19772 var name=namePath[namePath.length-1];
19773 for(var j=1,len2=namePath.length;j<len2;++j){
19774 var nameIndex = null;
19775 var p=namePath[j - 1];
19776 var nameA=p.split("[");
19777 if(nameA.length > 1){
19778 if(typeof(myObj[nameA[0]]) == "undefined"){
19779 myObj[nameA[0]]=[ ];
19781 nameIndex=parseInt(nameA[1]);
19782 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
19783 myObj[nameA[0]][nameIndex] = { };
19785 }else if(typeof(myObj[nameA[0]]) == "undefined"){
19786 myObj[nameA[0]] = { }
19789 if(nameA.length == 1){
19790 myObj=myObj[nameA[0]];
19792 myObj=myObj[nameA[0]][nameIndex];
19796 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
19797 if(name == name.split("[")[0]){
19798 myObj[name]=elm.value;
19800 // can not set value when there is no name
19802 }else if(elm.type == "checkbox" && elm.checked){
19803 if(typeof(myObj[name]) == 'undefined'){
19806 myObj[name].push(elm.value);
19807 }else if(elm.type == "select-multiple"){
19808 if(typeof(myObj[name]) == 'undefined'){
19811 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
19812 if(elm.options[jdx].selected){
19813 myObj[name].push(elm.options[jdx].value);
19823 isValid: function(){
19825 // Returns true if all of the widgets are valid.
19826 // Deprecated, will be removed in 2.0. Use get("state") instead.
19828 return this.state
== "";
19831 onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
19833 // Stub function to connect to if you want to do something
19834 // (like disable/enable a submit button) when the valid
19835 // state changes on the form as a whole.
19837 // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
19840 _getState: function(){
19842 // Compute what this.state should be based on state of children
19843 var states
= array
.map(this._descendants
, function(w
){
19844 return w
.get("state") || "";
19847 return array
.indexOf(states
, "Error") >= 0 ? "Error" :
19848 array
.indexOf(states
, "Incomplete") >= 0 ? "Incomplete" : "";
19851 disconnectChildren: function(){
19853 // Deprecated method. Applications no longer need to call this. Remove for 2.0.
19856 connectChildren: function(/*Boolean*/ inStartup
){
19858 // You can call this function directly, ex. in the event that you
19859 // programmatically add a widget to the form *after* the form has been
19862 // TODO: rename for 2.0
19864 this._descendants
= this._getDescendantFormWidgets();
19866 // To get notifications from children they need to be started. Children didn't used to need to be started,
19867 // so for back-compat, start them here
19868 array
.forEach(this._descendants
, function(child
){
19869 if(!child
._started
){ child
.startup(); }
19873 this._onChildChange();
19877 _onChildChange: function(/*String*/ attr
){
19879 // Called when child's value or disabled state changes
19881 // The unit tests expect state update to be synchronous, so update it immediately.
19882 if(!attr
|| attr
== "state" || attr
== "disabled"){
19883 this._set("state", this._getState());
19886 // Use defer() to collapse value changes in multiple children into a single
19887 // update to my value. Multiple updates will occur on:
19890 // 3. user selecting a radio button (which will de-select another radio button,
19891 // causing two onChange events)
19892 if(!attr
|| attr
== "value" || attr
== "disabled" || attr
== "checked"){
19893 if(this._onChangeDelayTimer
){
19894 this._onChangeDelayTimer
.remove();
19896 this._onChangeDelayTimer
= this.defer(function(){
19897 delete this._onChangeDelayTimer
;
19898 this._set("value", this.get("value"));
19903 startup: function(){
19904 this.inherited(arguments
);
19906 // Set initial this.value and this.state. Don't emit watch() notifications.
19907 this._descendants
= this._getDescendantFormWidgets();
19908 this.value
= this.get("value");
19909 this.state
= this._getState();
19911 // Initialize value and valid/invalid state tracking.
19915 this.containerNode
,
19916 "attrmodified-state, attrmodified-disabled, attrmodified-value, attrmodified-checked",
19918 if(evt
.target
== self
.domNode
){
19919 return; // ignore events that I fire on myself because my children changed
19921 self
._onChildChange(evt
.type
.replace("attrmodified-", ""));
19926 // Make state change call onValidStateChange(), will be removed in 2.0
19927 this.watch("state", function(attr
, oldVal
, newVal
){ this.onValidStateChange(newVal
== ""); });
19930 destroy: function(){
19931 this.inherited(arguments
);
19938 'dojo/data/util/simpleFetch':function(){
19939 define("dojo/data/util/simpleFetch", ["../../_base/lang", "../../_base/kernel", "./sorter"],
19940 function(lang
, kernel
, sorter
){
19942 // dojo/data/util/simpleFetch
19944 // The simpleFetch mixin is designed to serve as a set of function(s) that can
19945 // be mixed into other datastore implementations to accelerate their development.
19947 var simpleFetch
= {};
19948 lang
.setObject("dojo.data.util.simpleFetch", simpleFetch
);
19950 simpleFetch
.errorHandler = function(/*Object*/ errorData
, /*Object*/ requestObject
){
19952 // The error handler when there is an error fetching items. This function should not be called
19953 // directly and is used by simpleFetch.fetch().
19954 if(requestObject
.onError
){
19955 var scope
= requestObject
.scope
|| kernel
.global
;
19956 requestObject
.onError
.call(scope
, errorData
, requestObject
);
19960 simpleFetch
.fetchHandler = function(/*Array*/ items
, /*Object*/ requestObject
){
19962 // The handler when items are sucessfully fetched. This function should not be called directly
19963 // and is used by simpleFetch.fetch().
19964 var oldAbortFunction
= requestObject
.abort
|| null,
19967 startIndex
= requestObject
.start
?requestObject
.start
: 0,
19968 endIndex
= (requestObject
.count
&& (requestObject
.count
!== Infinity
))?(startIndex
+ requestObject
.count
):items
.length
;
19970 requestObject
.abort = function(){
19972 if(oldAbortFunction
){
19973 oldAbortFunction
.call(requestObject
);
19977 var scope
= requestObject
.scope
|| kernel
.global
;
19978 if(!requestObject
.store
){
19979 requestObject
.store
= this;
19981 if(requestObject
.onBegin
){
19982 requestObject
.onBegin
.call(scope
, items
.length
, requestObject
);
19984 if(requestObject
.sort
){
19985 items
.sort(sorter
.createSortFunction(requestObject
.sort
, this));
19987 if(requestObject
.onItem
){
19988 for(var i
= startIndex
; (i
< items
.length
) && (i
< endIndex
); ++i
){
19989 var item
= items
[i
];
19991 requestObject
.onItem
.call(scope
, item
, requestObject
);
19995 if(requestObject
.onComplete
&& !aborted
){
19997 if(!requestObject
.onItem
){
19998 subset
= items
.slice(startIndex
, endIndex
);
20000 requestObject
.onComplete
.call(scope
, subset
, requestObject
);
20004 simpleFetch
.fetch = function(/* Object? */ request
){
20006 // The simpleFetch mixin is designed to serve as a set of function(s) that can
20007 // be mixed into other datastore implementations to accelerate their development.
20009 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
20010 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
20011 // is not designed to work for datastores that respond to a fetch() call by incrementally
20012 // loading items, or sequentially loading partial batches of the result
20013 // set. For datastores that mixin simpleFetch, simpleFetch
20014 // implements a fetch method that automatically handles eight of the fetch()
20015 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
20016 // The class mixing in simpleFetch should not implement fetch(),
20017 // but should instead implement a _fetchItems() method. The _fetchItems()
20018 // method takes three arguments, the keywordArgs object that was passed
20019 // to fetch(), a callback function to be called when the result array is
20020 // available, and an error callback to be called if something goes wrong.
20021 // The _fetchItems() method should ignore any keywordArgs parameters for
20022 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
20023 // The _fetchItems() method needs to correctly handle any other keywordArgs
20024 // parameters, including the query parameter and any optional parameters
20025 // (such as includeChildren). The _fetchItems() method should create an array of
20026 // result items and pass it to the fetchHandler along with the original request object --
20027 // or, the _fetchItems() method may, if it wants to, create an new request object
20028 // with other specifics about the request that are specific to the datastore and pass
20029 // that as the request object to the handler.
20031 // For more information on this specific function, see dojo/data/api/Read.fetch()
20034 // The keywordArgs parameter may either be an instance of
20035 // conforming to dojo/data/api/Request or may be a simple anonymous object
20036 // that may contain any of the following:
20038 // | query: query-object or query-string,
20039 // | queryOptions: object,
20040 // | onBegin: Function,
20041 // | onItem: Function,
20042 // | onComplete: Function,
20043 // | onError: Function,
20044 // | scope: object,
20049 // All implementations should accept keywordArgs objects with any of
20050 // the 9 standard properties: query, onBegin, onItem, onComplete, onError
20051 // scope, sort, start, and count. Some implementations may accept additional
20052 // properties in the keywordArgs object as valid parameters, such as
20053 // {includeOutliers:true}.
20055 // ####The *query* parameter
20057 // The query may be optional in some data store implementations.
20058 // The dojo/data/api/Read API does not specify the syntax or semantics
20059 // of the query itself -- each different data store implementation
20060 // may have its own notion of what a query should look like.
20061 // However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data
20062 // and dojox.data support an object structure query, where the object is a set of
20063 // name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the
20064 // dijit widgets, such as ComboBox assume this to be the case when working with a datastore
20065 // when they dynamically update the query. Therefore, for maximum compatibility with dijit
20066 // widgets the recommended query parameter is a key/value object. That does not mean that the
20067 // the datastore may not take alternative query forms, such as a simple string, a Date, a number,
20068 // or a mix of such. Ultimately, The dojo/data/api/Read API is agnostic about what the query
20071 // Further note: In general for query objects that accept strings as attribute
20072 // value matches, the store should also support basic filtering capability, such as *
20073 // (match any character) and ? (match single character). An example query that is a query object
20074 // would be like: { attrFoo: "value*"}. Which generally means match all items where they have
20075 // an attribute named attrFoo, with a value that starts with 'value'.
20077 // ####The *queryOptions* parameter
20079 // The queryOptions parameter is an optional parameter used to specify options that may modify
20080 // the query in some fashion, such as doing a case insensitive search, or doing a deep search
20081 // where all items in a hierarchical representation of data are scanned instead of just the root
20082 // items. It currently defines two options that all datastores should attempt to honor if possible:
20084 // | ignoreCase: boolean, // Whether or not the query should match case sensitively or not. Default behaviour is false.
20085 // | deep: boolean // Whether or not a fetch should do a deep search of items and all child
20086 // | // items instead of just root-level items in a datastore. Default is false.
20089 // ####The *onBegin* parameter.
20091 // function(size, request);
20092 // If an onBegin callback function is provided, the callback function
20093 // will be called just once, before the first onItem callback is called.
20094 // The onBegin callback function will be passed two arguments, the
20095 // the total number of items identified and the Request object. If the total number is
20096 // unknown, then size will be -1. Note that size is not necessarily the size of the
20097 // collection of items returned from the query, as the request may have specified to return only a
20098 // subset of the total set of items through the use of the start and count parameters.
20100 // ####The *onItem* parameter.
20102 // function(item, request);
20104 // If an onItem callback function is provided, the callback function
20105 // will be called as each item in the result is received. The callback
20106 // function will be passed two arguments: the item itself, and the
20109 // ####The *onComplete* parameter.
20111 // function(items, request);
20113 // If an onComplete callback function is provided, the callback function
20114 // will be called just once, after the last onItem callback is called.
20115 // Note that if the onItem callback is not present, then onComplete will be passed
20116 // an array containing all items which matched the query and the request object.
20117 // If the onItem callback is present, then onComplete is called as:
20118 // onComplete(null, request).
20120 // ####The *onError* parameter.
20122 // function(errorData, request);
20124 // If an onError callback function is provided, the callback function
20125 // will be called if there is any sort of error while attempting to
20126 // execute the query.
20127 // The onError callback function will be passed two arguments:
20128 // an Error object and the Request object.
20130 // ####The *scope* parameter.
20132 // If a scope object is provided, all of the callback functions (onItem,
20133 // onComplete, onError, etc) will be invoked in the context of the scope
20134 // object. In the body of the callback function, the value of the "this"
20135 // keyword will be the scope object. If no scope object is provided,
20136 // the callback functions will be called in the context of dojo.global().
20137 // For example, onItem.call(scope, item, request) vs.
20138 // onItem.call(dojo.global(), item, request)
20140 // ####The *start* parameter.
20142 // If a start parameter is specified, this is a indication to the datastore to
20143 // only start returning items once the start number of items have been located and
20144 // skipped. When this parameter is paired with 'count', the store should be able
20145 // to page across queries with millions of hits by only returning subsets of the
20146 // hits for each query
20148 // ####The *count* parameter.
20150 // If a count parameter is specified, this is a indication to the datastore to
20151 // only return up to that many items. This allows a fetch call that may have
20152 // millions of item matches to be paired down to something reasonable.
20154 // ####The *sort* parameter.
20156 // If a sort parameter is specified, this is a indication to the datastore to
20157 // sort the items in some manner before returning the items. The array is an array of
20158 // javascript objects that must conform to the following format to be applied to the
20159 // fetching of items:
20161 // | attribute: attribute || attribute-name-string,
20162 // | descending: true|false; // Optional. Default is false.
20164 // Note that when comparing attributes, if an item contains no value for the attribute
20165 // (undefined), then it the default ascending sort logic should push it to the bottom
20166 // of the list. In the descending order case, it such items should appear at the top of the list.
20168 request
= request
|| {};
20169 if(!request
.store
){
20170 request
.store
= this;
20173 this._fetchItems(request
, lang
.hitch(this, "fetchHandler"), lang
.hitch(this, "errorHandler"));
20174 return request
; // Object
20177 return simpleFetch
;
20181 'dijit/Menu':function(){
20182 define("dijit/Menu", [
20184 "dojo/_base/array", // array.forEach
20185 "dojo/_base/declare", // declare
20186 "dojo/_base/event", // event.stop
20187 "dojo/dom", // dom.byId dom.isDescendant
20188 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
20189 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
20190 "dojo/dom-style", // domStyle.getComputedStyle
20191 "dojo/keys", // keys.F10
20192 "dojo/_base/lang", // lang.hitch
20194 "dojo/sniff", // has("ie"), has("quirks")
20195 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames
20196 "dojo/window", // winUtils.get
20200 ], function(require
, array
, declare
, event
, dom
, domAttr
, domGeometry
, domStyle
, keys
, lang
, on
,
20201 has
, win
, winUtils
, pm
, DropDownMenu
, ready
){
20206 // Back compat w/1.6, remove for 2.0
20207 if(has("dijit-legacy-requires")){
20208 ready(0, function(){
20209 var requires
= ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
20210 require(requires
); // use indirection so modules not rolled into a build
20214 return declare("dijit.Menu", DropDownMenu
, {
20216 // A context menu you can assign to multiple elements
20218 constructor: function(/*===== params, srcNodeRef =====*/){
20220 // Create the widget.
20221 // params: Object|null
20222 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
20223 // and functions, typically callbacks like onClick.
20224 // The hash can contain any of the widget's properties, excluding read-only properties.
20225 // srcNodeRef: DOMNode|String?
20226 // If a srcNodeRef (DOM node) is specified:
20228 // - use srcNodeRef.innerHTML as my contents
20229 // - replace srcNodeRef with my generated DOM tree
20231 this._bindings
= [];
20234 // targetNodeIds: [const] String[]
20235 // Array of dom node ids of nodes to attach to.
20236 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
20239 // selector: String?
20240 // CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
20241 // the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
20242 // a range of rows in a table, tree, etc.
20244 // The application must require() an appropriate level of dojo/query to handle the selector.
20247 // TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
20248 // So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
20251 // currentTarget: [readonly] DOMNode
20252 // For context menus, set to the current node that the Menu is being displayed for.
20253 // Useful so that the menu actions can be tailored according to the node
20254 currentTarget: null,
20257 // contextMenuForWindow: [const] Boolean
20258 // If true, right clicking anywhere on the window will cause this context menu to open.
20259 // If false, must specify targetNodeIds.
20260 contextMenuForWindow
: false,
20262 // leftClickToOpen: [const] Boolean
20263 // If true, menu will open on left click instead of right click, similar to a file menu.
20264 leftClickToOpen
: false,
20266 // refocus: Boolean
20267 // When this menu closes, re-focus the element which had focus before it was opened.
20270 postCreate: function(){
20271 if(this.contextMenuForWindow
){
20272 this.bindDomNode(this.ownerDocumentBody
);
20274 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
20275 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
20276 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
20277 array
.forEach(this.targetNodeIds
, this.bindDomNode
, this);
20279 this.inherited(arguments
);
20282 // thanks burstlib!
20283 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el
){
20285 // Returns the window reference of the passed iframe
20288 return winUtils
.get(this._iframeContentDocument(iframe_el
)) ||
20289 // Moz. TODO: is this available when defaultView isn't?
20290 this._iframeContentDocument(iframe_el
)['__parent__'] ||
20291 (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
]) || null; // Window
20294 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el
){
20296 // Returns a reference to the document object inside iframe_el
20299 return iframe_el
.contentDocument
// W3
20300 || (iframe_el
.contentWindow
&& iframe_el
.contentWindow
.document
) // IE
20301 || (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
] && win
.doc
.frames
[iframe_el
.name
].document
)
20302 || null; // HTMLDocument
20305 bindDomNode: function(/*String|DomNode*/ node
){
20307 // Attach menu to given node
20308 node
= dom
.byId(node
, this.ownerDocument
);
20310 var cn
; // Connect node
20312 // Support context menus on iframes. Rather than binding to the iframe itself we need
20313 // to bind to the <body> node inside the iframe.
20314 if(node
.tagName
.toLowerCase() == "iframe"){
20316 window
= this._iframeContentWindow(iframe
);
20317 cn
= win
.body(window
.document
);
20319 // To capture these events at the top level, attach to <html>, not <body>.
20320 // Otherwise right-click context menu just doesn't work.
20321 cn
= (node
== win
.body(this.ownerDocument
) ? this.ownerDocument
.documentElement
: node
);
20325 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
20331 // Save info about binding in _bindings[], and make node itself record index(+1) into
20332 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
20333 // start with a number, which fails on FF/safari.
20334 domAttr
.set(node
, "_dijitMenu" + this.id
, this._bindings
.push(binding
));
20336 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
20337 // loading yet, in which case we need to wait for the onload event first, and then connect
20338 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
20339 // we need to monitor keyboard events in addition to the oncontextmenu event.
20340 var doConnects
= lang
.hitch(this, function(cn
){
20341 var selector
= this.selector
,
20342 delegatedEvent
= selector
?
20343 function(eventType
){ return on
.selector(selector
, eventType
); } :
20344 function(eventType
){ return eventType
; },
20347 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
20348 // rather than shift-F10?
20349 on(cn
, delegatedEvent(this.leftClickToOpen
? "click" : "contextmenu"), function(evt
){
20350 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
20352 self
._scheduleOpen(this, iframe
, {x
: evt
.pageX
, y
: evt
.pageY
});
20354 on(cn
, delegatedEvent("keydown"), function(evt
){
20355 if(evt
.shiftKey
&& evt
.keyCode
== keys
.F10
){
20357 self
._scheduleOpen(this, iframe
); // no coords - open near target node
20362 binding
.connects
= cn
? doConnects(cn
) : [];
20365 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
20366 // and every time the contents change.
20367 // Need to do this b/c we are actually binding to the iframe's <body> node.
20368 // Note: can't use connect.connect(), see #9609.
20370 binding
.onloadHandler
= lang
.hitch(this, function(){
20371 // want to remove old connections, but IE throws exceptions when trying to
20372 // access the <body> node because it's already gone, or at least in a state of limbo
20374 var window
= this._iframeContentWindow(iframe
);
20375 cn
= win
.body(window
.document
)
20376 binding
.connects
= doConnects(cn
);
20378 if(iframe
.addEventListener
){
20379 iframe
.addEventListener("load", binding
.onloadHandler
, false);
20381 iframe
.attachEvent("onload", binding
.onloadHandler
);
20386 unBindDomNode: function(/*String|DomNode*/ nodeName
){
20388 // Detach menu from given node
20392 node
= dom
.byId(nodeName
, this.ownerDocument
);
20394 // On IE the dom.byId() call will get an exception if the attach point was
20395 // the <body> node of an <iframe> that has since been reloaded (and thus the
20396 // <body> node is in a limbo state of destruction.
20400 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
20401 var attrName
= "_dijitMenu" + this.id
;
20402 if(node
&& domAttr
.has(node
, attrName
)){
20403 var bid
= domAttr
.get(node
, attrName
)-1, b
= this._bindings
[bid
], h
;
20404 while((h
= b
.connects
.pop())){
20408 // Remove listener for iframe onload events
20409 var iframe
= b
.iframe
;
20411 if(iframe
.removeEventListener
){
20412 iframe
.removeEventListener("load", b
.onloadHandler
, false);
20414 iframe
.detachEvent("onload", b
.onloadHandler
);
20418 domAttr
.remove(node
, attrName
);
20419 delete this._bindings
[bid
];
20423 _scheduleOpen: function(/*DomNode?*/ target
, /*DomNode?*/ iframe
, /*Object?*/ coords
){
20425 // Set timer to display myself. Using a timer rather than displaying immediately solves
20428 // 1. IE: without the delay, focus work in "open" causes the system
20429 // context menu to appear in spite of stopEvent.
20431 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
20432 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
20433 // oncontextmenu event.)
20435 if(!this._openTimer
){
20436 this._openTimer
= this.defer(function(){
20437 delete this._openTimer
;
20447 _openMyself: function(args
){
20449 // Internal function for opening myself when the user does a right-click or something similar.
20451 // This is an Object containing:
20453 // - target: The node that is being clicked
20454 // - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
20455 // - coords: Put menu at specified x/y position in viewport, or if iframe is
20456 // specified, then relative to iframe.
20458 // _openMyself() formerly took the event object, and since various code references
20459 // evt.target (after connecting to _openMyself()), using an Object for parameters
20460 // (so that old code still works).
20462 var target
= args
.target
,
20463 iframe
= args
.iframe
,
20464 coords
= args
.coords
;
20466 // To be used by MenuItem event handlers to tell which node the menu was opened on
20467 this.currentTarget
= target
;
20469 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
20470 // then near the node the menu is assigned to.
20473 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
20474 var ifc
= domGeometry
.position(iframe
, true),
20475 window
= this._iframeContentWindow(iframe
),
20476 scroll
= domGeometry
.docScroll(window
.document
);
20478 var cs
= domStyle
.getComputedStyle(iframe
),
20479 tp
= domStyle
.toPixelValue
,
20480 left
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingLeft
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderLeftWidth
) : 0),
20481 top
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingTop
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderTopWidth
) : 0);
20483 coords
.x
+= ifc
.x
+ left
- scroll
.x
;
20484 coords
.y
+= ifc
.y
+ top
- scroll
.y
;
20487 coords
= domGeometry
.position(target
, true);
20493 var prevFocusNode
= this._focusManager
.get("prevNode");
20494 var curFocusNode
= this._focusManager
.get("curNode");
20495 var savedFocusNode
= !curFocusNode
|| (dom
.isDescendant(curFocusNode
, this.domNode
)) ? prevFocusNode
: curFocusNode
;
20497 function closeAndRestoreFocus(){
20498 // user has clicked on a menu or popup
20499 if(self
.refocus
&& savedFocusNode
){
20500 savedFocusNode
.focus();
20508 onExecute
: closeAndRestoreFocus
,
20509 onCancel
: closeAndRestoreFocus
,
20510 orient
: this.isLeftToRight() ? 'L' : 'R'
20514 this._onBlur = function(){
20515 this.inherited('_onBlur', arguments
);
20516 // Usually the parent closes the child widget but if this is a context
20517 // menu then there is no parent
20519 // don't try to restore focus; user has clicked another part of the screen
20520 // and set focus there
20524 destroy: function(){
20525 array
.forEach(this._bindings
, function(b
){ if(b
){ this.unBindDomNode(b
.node
); } }, this);
20526 this.inherited(arguments
);
20533 'dijit/form/_CheckBoxMixin':function(){
20534 define("dijit/form/_CheckBoxMixin", [
20535 "dojo/_base/declare", // declare
20536 "dojo/dom-attr", // domAttr.set
20537 "dojo/_base/event" // event.stop
20538 ], function(declare
, domAttr
, event
){
20541 // dijit/form/_CheckBoxMixin
20543 return declare("dijit.form._CheckBoxMixin", null, {
20545 // Mixin to provide widget functionality corresponding to an HTML checkbox
20548 // User interacts with real html inputs.
20549 // On onclick (which occurs by mouse click, space-bar, or
20550 // using the arrow keys to switch the selected radio button),
20551 // we update the state of the checkbox/radio.
20554 // type: [private] String
20555 // type attribute on `<input>` node.
20556 // Overrides `dijit/form/Button.type`. Users should not change this value.
20560 // As an initialization parameter, equivalent to value field on normal checkbox
20561 // (if checked, the value is passed as the value when form is submitted).
20564 // readOnly: Boolean
20565 // Should this widget respond to user input?
20566 // In markup, this is specified as "readOnly".
20567 // Similar to disabled except readOnly form values are submitted.
20570 // aria-pressed for toggle buttons, and aria-checked for checkboxes
20571 _aria_attr
: "aria-checked",
20573 _setReadOnlyAttr: function(/*Boolean*/ value
){
20574 this._set("readOnly", value
);
20575 domAttr
.set(this.focusNode
, 'readOnly', value
);
20578 // Override dijit/form/Button._setLabelAttr() since we don't even have a containerNode.
20579 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox/layout/TabContainer
20580 _setLabelAttr
: undefined,
20582 _getSubmitValue: function(/*String*/ value
){
20583 return !value
&& value
!== 0 ? "on" : value
;
20586 _setValueAttr: function(newValue
){
20587 newValue
= this._getSubmitValue(newValue
); // "on" to match browser native behavior when value unspecified
20588 this._set("value", newValue
);
20589 domAttr
.set(this.focusNode
, "value", newValue
);
20593 this.inherited(arguments
);
20594 // Handle unlikely event that the <input type=checkbox> value attribute has changed
20595 this._set("value", this.params
.value
|| "on");
20596 domAttr
.set(this.focusNode
, 'value', this.value
);
20599 _onClick: function(/*Event*/ e
){
20601 // Internal function to handle click actions - need to check
20602 // readOnly, since button no longer does that check.
20607 return this.inherited(arguments
);
20613 'dijit/layout/ContentPane':function(){
20614 define("dijit/layout/ContentPane", [
20615 "dojo/_base/kernel", // kernel.deprecated
20616 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
20619 "./_ContentPaneResizeMixin",
20620 "dojo/string", // string.substitute
20621 "dojo/html", // html._ContentSetter
20622 "dojo/i18n!../nls/loading",
20623 "dojo/_base/array", // array.forEach
20624 "dojo/_base/declare", // declare
20625 "dojo/_base/Deferred", // Deferred
20626 "dojo/dom", // dom.byId
20627 "dojo/dom-attr", // domAttr.attr
20628 "dojo/dom-construct", // empty()
20629 "dojo/_base/xhr", // xhr.get
20630 "dojo/i18n", // i18n.getLocalization
20632 ], function(kernel
, lang
, _Widget
, _Container
, _ContentPaneResizeMixin
, string
, html
, nlsLoading
,
20633 array
, declare
, Deferred
, dom
, domAttr
, domConstruct
, xhr
, i18n
, when
){
20636 // dijit/layout/ContentPane
20639 return declare("dijit.layout.ContentPane", [_Widget
, _Container
, _ContentPaneResizeMixin
], {
20641 // A widget containing an HTML fragment, specified inline
20642 // or by uri. Fragment may include widgets.
20645 // This widget embeds a document fragment in the page, specified
20646 // either by uri, javascript generated markup or DOM reference.
20647 // Any widgets within this content are instantiated and managed,
20648 // but laid out according to the HTML structure. Unlike IFRAME,
20649 // ContentPane embeds a document fragment as would be found
20650 // inside the BODY tag of a full HTML document. It should not
20651 // contain the HTML, HEAD, or BODY tags.
20652 // For more advanced functionality with scripts and
20653 // stylesheets, see dojox/layout/ContentPane. This widget may be
20654 // used stand alone or as a base class for other widgets.
20655 // ContentPane is useful as a child of other layout containers
20656 // such as BorderContainer or TabContainer, but note that those
20657 // widgets can contain any widget as a child.
20660 // Some quick samples:
20661 // To change the innerHTML:
20662 // | cp.set('content', '<b>new content</b>')`
20663 // Or you can send it a NodeList:
20664 // | cp.set('content', dojo.query('div [class=selected]', userSelection))
20665 // To do an ajax update:
20666 // | cp.set('href', url)
20669 // The href of the content that displays now.
20670 // Set this at construction if you want to load data externally when the
20671 // pane is shown. (Set preload=true to load it immediately.)
20672 // Changing href after creation doesn't have any effect; Use set('href', ...);
20675 // content: String|DomNode|NodeList|dijit/_Widget
20676 // The innerHTML of the ContentPane.
20677 // Note that the initialization parameter / argument to set("content", ...)
20678 // can be a String, DomNode, Nodelist, or _Widget.
20681 // extractContent: Boolean
20682 // Extract visible content from inside of `<body> .... </body>`.
20683 // I.e., strip `<html>` and `<head>` (and it's contents) from the href
20684 extractContent
: false,
20686 // parseOnLoad: Boolean
20687 // Parse content and create the widgets, if any.
20690 // parserScope: String
20691 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
20692 // will search for data-dojo-type (or dojoType). For backwards compatibility
20693 // reasons defaults to dojo._scopeName (which is "dojo" except when
20694 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
20695 parserScope
: kernel
._scopeName
,
20697 // preventCache: Boolean
20698 // Prevent caching of data from href's by appending a timestamp to the href.
20699 preventCache
: false,
20701 // preload: Boolean
20702 // Force load of data on initialization even if pane is hidden.
20705 // refreshOnShow: Boolean
20706 // Refresh (re-download) content when pane goes from hidden to shown
20707 refreshOnShow
: false,
20709 // loadingMessage: String
20710 // Message that shows while downloading
20711 loadingMessage
: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
20713 // errorMessage: String
20714 // Message that shows if an error occurs
20715 errorMessage
: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
20717 // isLoaded: [readonly] Boolean
20718 // True if the ContentPane has data in it, either specified
20719 // during initialization (via href or inline content), or set
20720 // via set('content', ...) / set('href', ...)
20722 // False if it doesn't have any content, or if ContentPane is
20723 // still in the process of downloading href.
20726 baseClass
: "dijitContentPane",
20729 // ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
20730 // Function that should grab the content specified via href.
20731 ioMethod: dojo.xhrGet,
20735 // Parameters to pass to xhrGet() request, for example:
20736 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
20739 // onLoadDeferred: [readonly] dojo.Deferred
20740 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
20741 // Calling onLoadDeferred.then() registers your
20742 // callback to be called only once, when the prior set('href', ...) call or
20743 // the initial href parameter to the constructor finishes loading.
20745 // This is different than an onLoad() handler which gets called any time any href
20746 // or content is loaded.
20747 onLoadDeferred
: null,
20749 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
20750 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
20752 _setTitleAttr
: null,
20754 // Flag to parser that I'll parse my contents, so it shouldn't.
20757 // template: [private] Boolean
20758 // Flag from the parser that this ContentPane is inside a template
20759 // so the contents are pre-parsed.
20760 // TODO: this declaration can be commented out in 2.0
20763 create: function(params
, srcNodeRef
){
20764 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
20765 // processed in the same way as contents set via set("content", ...), calling the parser etc.
20766 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
20767 if((!params
|| !params
.template
) && srcNodeRef
&& !("href" in params
) && !("content" in params
)){
20768 srcNodeRef
= dom
.byId(srcNodeRef
);
20769 var df
= srcNodeRef
.ownerDocument
.createDocumentFragment();
20770 while(srcNodeRef
.firstChild
){
20771 df
.appendChild(srcNodeRef
.firstChild
);
20773 params
= lang
.delegate(params
, {content
: df
});
20775 this.inherited(arguments
, [params
, srcNodeRef
]);
20778 postMixInProperties: function(){
20779 this.inherited(arguments
);
20780 var messages
= i18n
.getLocalization("dijit", "loading", this.lang
);
20781 this.loadingMessage
= string
.substitute(this.loadingMessage
, messages
);
20782 this.errorMessage
= string
.substitute(this.errorMessage
, messages
);
20785 buildRendering: function(){
20786 this.inherited(arguments
);
20788 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
20789 // For subclasses of ContentPane that do have a template, does nothing.
20790 if(!this.containerNode
){
20791 this.containerNode
= this.domNode
;
20794 // remove the title attribute so it doesn't show up when hovering
20795 // over a node (TODO: remove in 2.0, no longer needed after #11490)
20796 this.domNode
.title
= "";
20798 if(!domAttr
.get(this.domNode
,"role")){
20799 this.domNode
.setAttribute("role", "group");
20803 startup: function(){
20805 // Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
20807 // This starts all the widgets
20808 this.inherited(arguments
);
20810 // And this catches stuff like dojo/dnd/Source
20811 if(this._contentSetter
){
20812 array
.forEach(this._contentSetter
.parseResults
, function(obj
){
20813 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
20815 obj
._started
= true;
20821 _startChildren: function(){
20823 // Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
20824 // itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
20826 // This starts all the widgets
20827 array
.forEach(this.getChildren(), function(obj
){
20828 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
20830 obj
._started
= true;
20834 // And this catches stuff like dojo/dnd/Source
20835 if(this._contentSetter
){
20836 array
.forEach(this._contentSetter
.parseResults
, function(obj
){
20837 if(!obj
._started
&& !obj
._destroyed
&& lang
.isFunction(obj
.startup
)){
20839 obj
._started
= true;
20845 setHref: function(/*String|Uri*/ href
){
20847 // Deprecated. Use set('href', ...) instead.
20848 kernel
.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
20849 return this.set("href", href
);
20851 _setHrefAttr: function(/*String|Uri*/ href
){
20853 // Hook so set("href", ...) works.
20855 // Reset the (external defined) content of this pane and replace with new url
20856 // Note: It delays the download until widget is shown if preload is false.
20858 // url to the page you want to get, must be within the same domain as your mainpage
20860 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
20863 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
20864 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
20866 this._set("href", href
);
20868 // _setHrefAttr() is called during creation and by the user, after creation.
20869 // Assuming preload == false, only in the second case do we actually load the URL;
20870 // otherwise it's done in startup(), and only if this widget is shown.
20871 if(this.preload
|| (this._created
&& this._isShown())){
20874 // Set flag to indicate that href needs to be loaded the next time the
20875 // ContentPane is made visible
20876 this._hrefChanged
= true;
20879 return this.onLoadDeferred
; // Deferred
20882 setContent: function(/*String|DomNode|Nodelist*/data
){
20884 // Deprecated. Use set('content', ...) instead.
20885 kernel
.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
20886 this.set("content", data
);
20888 _setContentAttr: function(/*String|DomNode|Nodelist*/data
){
20890 // Hook to make set("content", ...) work.
20891 // Replaces old content with data content, include style classes from old content
20893 // the new Content may be String, DomNode or NodeList
20895 // if data is a NodeList (or an array of nodes) nodes are copied
20896 // so you can import nodes from another document implicitly
20898 // clear href so we can't run refresh and clear content
20899 // refresh should only work if we downloaded the content
20900 this._set("href", "");
20902 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
20905 // Even though user is just setting content directly, still need to define an onLoadDeferred
20906 // because the _onLoadHandler() handler is still getting called from setContent()
20907 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
20909 // For back-compat reasons, call onLoad() for set('content', ...)
20910 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
20911 // or as initialization parameter (ie: new ContentPane({content: ...})
20912 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
20915 this._setContent(data
|| "");
20917 this._isDownloaded
= false; // mark that content is from a set('content') not a set('href')
20919 return this.onLoadDeferred
; // Deferred
20921 _getContentAttr: function(){
20923 // Hook to make get("content") work
20924 return this.containerNode
.innerHTML
;
20927 cancel: function(){
20929 // Cancels an in-flight download of content
20930 if(this._xhrDfd
&& (this._xhrDfd
.fired
== -1)){
20931 this._xhrDfd
.cancel();
20933 delete this._xhrDfd
; // garbage collect
20935 this.onLoadDeferred
= null;
20938 destroy: function(){
20940 this.inherited(arguments
);
20943 destroyRecursive: function(/*Boolean*/ preserveDom
){
20945 // Destroy the ContentPane and its contents
20947 // if we have multiple controllers destroying us, bail after the first
20948 if(this._beingDestroyed
){
20951 this.inherited(arguments
);
20954 _onShow: function(){
20956 // Called when the ContentPane is made visible
20958 // For a plain ContentPane, this is called on initialization, from startup().
20959 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
20960 // called whenever the pane is made visible.
20962 // Does necessary processing, including href download and layout/resize of
20965 this.inherited(arguments
);
20968 if(!this._xhrDfd
&& // if there's an href that isn't already being loaded
20969 (!this.isLoaded
|| this._hrefChanged
|| this.refreshOnShow
)
20971 return this.refresh(); // If child has an href, promise that fires when the load is complete
20976 refresh: function(){
20978 // [Re]download contents of href and display
20980 // 1. cancels any currently in-flight requests
20981 // 2. posts "loading..." message
20982 // 3. sends XHR to download new data
20984 // Cancel possible prior in-flight request
20987 this.onLoadDeferred
= new Deferred(lang
.hitch(this, "cancel"));
20988 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
20990 return this.onLoadDeferred
; // If child has an href, promise that fires when refresh is complete
20995 // Load/reload the href specified in this.href
20997 // display loading message
20998 this._setContent(this.onDownloadStart(), true);
21002 preventCache
: (this.preventCache
|| this.refreshOnShow
),
21006 if(lang
.isObject(this.ioArgs
)){
21007 lang
.mixin(getArgs
, this.ioArgs
);
21010 var hand
= (this._xhrDfd
= (this.ioMethod
|| xhr
.get)(getArgs
)),
21015 returnedHtml
= html
;
21017 self
._isDownloaded
= true;
21018 return self
._setContent(html
, false);
21020 self
._onError('Content', err
); // onContentError
21024 if(!hand
.canceled
){
21025 // show error message in the pane
21026 self
._onError('Download', err
); // onDownloadError
21028 delete self
._xhrDfd
;
21032 self
.onDownloadEnd();
21033 delete self
._xhrDfd
;
21034 return returnedHtml
;
21037 // Remove flag saying that a load is needed
21038 delete this._hrefChanged
;
21041 _onLoadHandler: function(data
){
21043 // This is called whenever new content is being loaded
21044 this._set("isLoaded", true);
21046 this.onLoadDeferred
.resolve(data
);
21048 console
.error('Error '+this.widgetId
+' running custom onLoad code: ' + e
.message
);
21052 _onUnloadHandler: function(){
21054 // This is called whenever the content is being unloaded
21055 this._set("isLoaded", false);
21059 console
.error('Error '+this.widgetId
+' running custom onUnload code: ' + e
.message
);
21063 destroyDescendants: function(/*Boolean*/ preserveDom
){
21065 // Destroy all the widgets inside the ContentPane and empty containerNode
21067 // Make sure we call onUnload (but only when the ContentPane has real content)
21069 this._onUnloadHandler();
21072 // Even if this.isLoaded == false there might still be a "Loading..." message
21073 // to erase, so continue...
21075 // For historical reasons we need to delete all widgets under this.containerNode,
21076 // even ones that the user has created manually.
21077 var setter
= this._contentSetter
;
21078 array
.forEach(this.getChildren(), function(widget
){
21079 if(widget
.destroyRecursive
){
21080 // All widgets will hit this branch
21081 widget
.destroyRecursive(preserveDom
);
21082 }else if(widget
.destroy
){
21083 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21084 widget
.destroy(preserveDom
);
21086 widget
._destroyed
= true;
21089 // Most of the widgets in setter.parseResults have already been destroyed, but
21090 // things like Menu that have been moved to <body> haven't yet
21091 array
.forEach(setter
.parseResults
, function(widget
){
21092 if(!widget
._destroyed
){
21093 if(widget
.destroyRecursive
){
21094 // All widgets will hit this branch
21095 widget
.destroyRecursive(preserveDom
);
21096 }else if(widget
.destroy
){
21097 // Things like dojo/dnd/Source have destroy(), not destroyRecursive()
21098 widget
.destroy(preserveDom
);
21100 widget
._destroyed
= true;
21103 delete setter
.parseResults
;
21106 // And then clear away all the DOM nodes
21108 domConstruct
.empty(this.containerNode
);
21111 // Delete any state information we have about current contents
21112 delete this._singleChild
;
21115 _setContent: function(/*String|DocumentFragment*/ cont
, /*Boolean*/ isFakeContent
){
21117 // Insert the content into the container node
21119 // Returns a Deferred promise that is resolved when the content is parsed.
21121 // first get rid of child widgets
21122 this.destroyDescendants();
21124 // html.set will take care of the rest of the details
21125 // we provide an override for the error handling to ensure the widget gets the errors
21126 // configure the setter instance with only the relevant widget instance properties
21127 // NOTE: unless we hook into attr, or provide property setters for each property,
21128 // we need to re-configure the ContentSetter with each use
21129 var setter
= this._contentSetter
;
21130 if(! (setter
&& setter
instanceof html
._ContentSetter
)){
21131 setter
= this._contentSetter
= new html
._ContentSetter({
21132 node
: this.containerNode
,
21133 _onError
: lang
.hitch(this, this._onError
),
21134 onContentError
: lang
.hitch(this, function(e
){
21135 // fires if a domfault occurs when we are appending this.errorMessage
21136 // like for instance if domNode is a UL and we try append a DIV
21137 var errMess
= this.onContentError(e
);
21139 this.containerNode
.innerHTML
= errMess
;
21141 console
.error('Fatal '+this.id
+' could not change content due to '+e
.message
, e
);
21148 var setterParams
= lang
.mixin({
21149 cleanContent
: this.cleanContent
,
21150 extractContent
: this.extractContent
,
21151 parseContent
: !cont
.domNode
&& this.parseOnLoad
,
21152 parserScope
: this.parserScope
,
21156 textDir
: this.textDir
21157 }, this._contentSetterParams
|| {});
21159 var p
= setter
.set( (lang
.isObject(cont
) && cont
.domNode
) ? cont
.domNode
: cont
, setterParams
);
21161 // dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
21162 // dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
21163 // So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
21165 return when(p
&& p
.then
? p
: setter
.parseDeferred
, function(){
21166 // setter params must be pulled afresh from the ContentPane each time
21167 delete self
._contentSetterParams
;
21169 if(!isFakeContent
){
21171 // Startup each top level child widget (and they will start their children, recursively)
21172 self
._startChildren();
21174 // Call resize() on each of my child layout widgets,
21175 // or resize() on my single child layout widget...
21176 // either now (if I'm currently visible) or when I become visible
21177 self
._scheduleLayout();
21179 self
._onLoadHandler(cont
);
21184 _onError: function(type
, err
, consoleText
){
21185 this.onLoadDeferred
.reject(err
);
21187 // shows user the string that is returned by on[type]Error
21188 // override on[type]Error and return your own string to customize
21189 var errText
= this['on' + type
+ 'Error'].call(this, err
);
21191 console
.error(consoleText
, err
);
21192 }else if(errText
){// a empty string won't change current content
21193 this._setContent(errText
, true);
21197 // EVENT's, should be overide-able
21198 onLoad: function(/*===== data =====*/){
21200 // Event hook, is called after everything is loaded and widgetified
21205 onUnload: function(){
21207 // Event hook, is called before old content is cleared
21212 onDownloadStart: function(){
21214 // Called before download starts.
21216 // The string returned by this function will be the html
21217 // that tells the user we are loading something.
21218 // Override with your own function if you want to change text.
21221 return this.loadingMessage
;
21224 onContentError: function(/*Error*/ /*===== error =====*/){
21226 // Called on DOM faults, require faults etc. in content.
21228 // In order to display an error message in the pane, return
21229 // the error message from this method, as an HTML string.
21231 // By default (if this method is not overriden), it returns
21232 // nothing, so the error message is just printed to the console.
21237 onDownloadError: function(/*Error*/ /*===== error =====*/){
21239 // Called when download error occurs.
21241 // In order to display an error message in the pane, return
21242 // the error message from this method, as an HTML string.
21244 // Default behavior (if this method is not overriden) is to display
21245 // the error message inside the pane.
21248 return this.errorMessage
;
21251 onDownloadEnd: function(){
21253 // Called when download is finished.
21262 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21263 'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
21264 'dijit/_KeyNavContainer':function(){
21265 define("dijit/_KeyNavContainer", [
21266 "dojo/_base/kernel", // kernel.deprecated
21269 "dojo/_base/array", // array.forEach
21270 "dojo/keys", // keys.END keys.HOME
21271 "dojo/_base/declare", // declare
21272 "dojo/_base/event", // event.stop
21273 "dojo/dom-attr", // domAttr.set
21274 "dojo/_base/lang" // lang.hitch
21275 ], function(kernel
, _Container
, _FocusMixin
, array
, keys
, declare
, event
, domAttr
, lang
){
21279 // dijit/_KeyNavContainer
21281 return declare("dijit._KeyNavContainer", [_FocusMixin
, _Container
], {
21283 // A _Container with keyboard navigation of its children.
21285 // To use this mixin, call connectKeyNavHandlers() in
21287 // It provides normalized keyboard and focusing code for Container
21291 // focusedChild: [protected] Widget
21292 // The currently focused child widget, or null if there isn't one
21293 focusedChild: null,
21296 // tabIndex: String
21297 // Tab index of the container; same as HTML tabIndex attribute.
21298 // Note then when user tabs into the container, focus is immediately
21299 // moved to the first item in the container.
21302 connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes
, /*keys[]*/ nextKeyCodes
){
21304 // Call in postCreate() to attach the keyboard handlers
21305 // to the container.
21306 // preKeyCodes: keys[]
21307 // Key codes for navigating to the previous child.
21308 // nextKeyCodes: keys[]
21309 // Key codes for navigating to the next child.
21313 // TODO: call this automatically from my own postCreate()
21315 var keyCodes
= (this._keyNavCodes
= {});
21316 var prev
= lang
.hitch(this, "focusPrev");
21317 var next
= lang
.hitch(this, "focusNext");
21318 array
.forEach(prevKeyCodes
, function(code
){ keyCodes
[code
] = prev
; });
21319 array
.forEach(nextKeyCodes
, function(code
){ keyCodes
[code
] = next
; });
21320 keyCodes
[keys
.HOME
] = lang
.hitch(this, "focusFirstChild");
21321 keyCodes
[keys
.END
] = lang
.hitch(this, "focusLastChild");
21322 this.connect(this.domNode
, "onkeypress", "_onContainerKeypress");
21323 this.connect(this.domNode
, "onfocus", "_onContainerFocus");
21326 startupKeyNavChildren: function(){
21327 kernel
.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
21330 startup: function(){
21331 this.inherited(arguments
);
21332 array
.forEach(this.getChildren(), lang
.hitch(this, "_startupChild"));
21335 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex
){
21336 this.inherited(arguments
);
21337 this._startupChild(widget
);
21342 // Default focus() implementation: focus the first child.
21343 this.focusFirstChild();
21346 focusFirstChild: function(){
21348 // Focus the first focusable child in the container.
21351 this.focusChild(this._getFirstFocusableChild());
21354 focusLastChild: function(){
21356 // Focus the last focusable child in the container.
21359 this.focusChild(this._getLastFocusableChild());
21362 focusNext: function(){
21364 // Focus the next widget
21367 this.focusChild(this._getNextFocusableChild(this.focusedChild
, 1));
21370 focusPrev: function(){
21372 // Focus the last focusable node in the previous widget
21373 // (ex: go to the ComboButton icon section rather than button section)
21376 this.focusChild(this._getNextFocusableChild(this.focusedChild
, -1), true);
21379 focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last
){
21381 // Focus specified child widget.
21383 // Reference to container's child widget
21385 // If true and if widget has multiple focusable nodes, focus the
21386 // last one instead of the first one
21390 if(!widget
){ return; }
21392 if(this.focusedChild
&& widget
!== this.focusedChild
){
21393 this._onChildBlur(this.focusedChild
); // used by _MenuBase
21395 widget
.set("tabIndex", this.tabIndex
); // for IE focus outline to appear, must set tabIndex before focs
21396 widget
.focus(last
? "end" : "start");
21397 this._set("focusedChild", widget
);
21400 _startupChild: function(/*dijit/_WidgetBase*/ widget){
21402 // Setup for each child widget
21404 // Sets tabIndex=-1 on each child, so that the tab key will
21405 // leave the container rather than visiting each child.
21409 widget.set("tabIndex", "-1");
21411 this.connect(widget, "_onFocus", function(){
21412 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
21413 widget.set("tabIndex", this.tabIndex);
21415 this.connect(widget, "_onBlur", function(){
21416 widget.set("tabIndex", "-1");
21420 _onContainerFocus: function(evt){
21422 // Handler for when the container gets focus
21424 // Initially the container itself has a tabIndex, but when it gets
21425 // focus, switch focus to first child...
21429 // Note that we can't use _onFocus() because switching focus from the
21430 // _onFocus() handler confuses the focus.js code
21431 // (because it causes _onFocusNode() to be called recursively)
21432 // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
21434 // Ignore spurious focus events:
21435 // 1. focus on a child widget bubbles on FF
21436 // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
21437 if(evt.target !== this.domNode || this.focusedChild){ return; }
21439 this.focusFirstChild();
21441 // and then set the container's tabIndex to -1,
21442 // (don't remove as that breaks Safari 4)
21443 // so that tab or shift-tab will go to the fields after/before
21444 // the container, rather than the container itself
21445 domAttr.set(this.domNode, "tabIndex", "-1");
21448 _onBlur: function(evt){
21449 // When focus is moved away the container, and its descendant (popup) widgets,
21450 // then restore the container's tabIndex so that user can tab to it again.
21451 // Note that using _onBlur() so that this doesn't happen when focus is shifted
21452 // to one of my child widgets (typically a popup)
21454 domAttr.set(this.domNode, "tabIndex", this.tabIndex);
21456 this.focusedChild = null;
21457 this.inherited(arguments);
21460 _onContainerKeypress: function(evt){
21462 // When a key is pressed, if it's an arrow key etc. then
21463 // it's handled here.
21466 if(evt.ctrlKey || evt.altKey){ return; }
21467 var func = this._keyNavCodes[evt.charOrCode];
21474 _onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
21476 // Called when focus leaves a child widget to go
21477 // to a sibling widget.
21478 // Used by MenuBase.js (TODO: move code there)
21483 _getFirstFocusableChild: function(){
21485 // Returns first child that can be focused
21486 return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
21489 _getLastFocusableChild: function(){
21491 // Returns last child that can be focused
21492 return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
21495 _getNextFocusableChild: function(child
, dir
){
21497 // Returns the next or previous focusable child, compared
21500 // The current widget
21505 child
= this._getSiblingOfChild(child
, dir
);
21507 var children
= this.getChildren();
21508 for(var i
=0; i
< children
.length
; i
++){
21510 child
= children
[(dir
>0) ? 0 : (children
.length
-1)];
21512 if(child
.isFocusable()){
21513 return child
; // dijit/_WidgetBase
21515 child
= this._getSiblingOfChild(child
, dir
);
21517 // no focusable child found
21518 return null; // dijit/_WidgetBase
21524 'dijit/layout/utils':function(){
21525 define("dijit/layout/utils", [
21526 "dojo/_base/array", // array.filter array.forEach
21527 "dojo/dom-class", // domClass.add domClass.remove
21528 "dojo/dom-geometry", // domGeometry.marginBox
21529 "dojo/dom-style", // domStyle.getComputedStyle
21530 "dojo/_base/lang", // lang.mixin
21531 "../main" // for exporting symbols to dijit, remove in 2.0
21532 ], function(array
, domClass
, domGeometry
, domStyle
, lang
, dijit
){
21535 // dijit/layout/utils
21537 var layout
= lang
.getObject("layout", true, dijit
);
21541 // marginBox2contentBox() and layoutChildren()
21545 layout
.marginBox2contentBox = function(/*DomNode*/ node
, /*Object*/ mb
){
21547 // Given the margin-box size of a node, return its content box size.
21548 // Functions like domGeometry.contentBox() but is more reliable since it doesn't have
21549 // to wait for the browser to compute sizes.
21550 var cs
= domStyle
.getComputedStyle(node
);
21551 var me
= domGeometry
.getMarginExtents(node
, cs
);
21552 var pb
= domGeometry
.getPadBorderExtents(node
, cs
);
21554 l
: domStyle
.toPixelValue(node
, cs
.paddingLeft
),
21555 t
: domStyle
.toPixelValue(node
, cs
.paddingTop
),
21556 w
: mb
.w
- (me
.w
+ pb
.w
),
21557 h
: mb
.h
- (me
.h
+ pb
.h
)
21561 function capitalize(word
){
21562 return word
.substring(0,1).toUpperCase() + word
.substring(1);
21565 function size(widget
, dim
){
21567 var newSize
= widget
.resize
? widget
.resize(dim
) : domGeometry
.setMarginBox(widget
.domNode
, dim
);
21569 // record child's size
21571 // if the child returned it's new size then use that
21572 lang
.mixin(widget
, newSize
);
21574 // otherwise, call getMarginBox(), but favor our own numbers when we have them.
21575 // the browser lies sometimes
21576 lang
.mixin(widget
, domGeometry
.getMarginBox(widget
.domNode
));
21577 lang
.mixin(widget
, dim
);
21581 layout
.layoutChildren = function(/*DomNode*/ container
, /*Object*/ dim
, /*Widget[]*/ children
,
21582 /*String?*/ changedRegionId
, /*Number?*/ changedRegionSize
){
21584 // Layout a bunch of child dom nodes within a parent dom node
21588 // {l, t, w, h} object specifying dimensions of container into which to place children
21590 // An array of Widgets or at least objects containing:
21592 // - domNode: pointer to DOM node to position
21593 // - region or layoutAlign: position to place DOM node
21594 // - resize(): (optional) method to set size of node
21595 // - id: (optional) Id of widgets, referenced from resize object, below.
21596 // changedRegionId:
21597 // If specified, the slider for the region with the specified id has been dragged, and thus
21598 // the region's height or width should be adjusted according to changedRegionSize
21599 // changedRegionSize:
21600 // See changedRegionId.
21602 // copy dim because we are going to modify it
21603 dim
= lang
.mixin({}, dim
);
21605 domClass
.add(container
, "dijitLayoutContainer");
21607 // Move "client" elements to the end of the array for layout. a11y dictates that the author
21608 // needs to be able to put them in the document in tab-order, but this algorithm requires that
21609 // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
21610 children
= array
.filter(children
, function(item
){ return item
.region
!= "center" && item
.layoutAlign
!= "client"; })
21611 .concat(array
.filter(children
, function(item
){ return item
.region
== "center" || item
.layoutAlign
== "client"; }));
21613 // set positions/sizes
21614 array
.forEach(children
, function(child
){
21615 var elm
= child
.domNode
,
21616 pos
= (child
.region
|| child
.layoutAlign
);
21618 throw new Error("No region setting for " + child
.id
)
21621 // set elem to upper left corner of unused space; may move it later
21622 var elmStyle
= elm
.style
;
21623 elmStyle
.left
= dim
.l
+"px";
21624 elmStyle
.top
= dim
.t
+"px";
21625 elmStyle
.position
= "absolute";
21627 domClass
.add(elm
, "dijitAlign" + capitalize(pos
));
21629 // Size adjustments to make to this child widget
21630 var sizeSetting
= {};
21632 // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
21633 // panes and width adjustment for left/right align panes.
21634 if(changedRegionId
&& changedRegionId
== child
.id
){
21635 sizeSetting
[child
.region
== "top" || child
.region
== "bottom" ? "h" : "w"] = changedRegionSize
;
21638 // set size && adjust record of remaining space.
21639 // note that setting the width of a <div> may affect its height.
21640 if(pos
== "top" || pos
== "bottom"){
21641 sizeSetting
.w
= dim
.w
;
21642 size(child
, sizeSetting
);
21647 elmStyle
.top
= dim
.t
+ dim
.h
+ "px";
21649 }else if(pos
== "left" || pos
== "right"){
21650 sizeSetting
.h
= dim
.h
;
21651 size(child
, sizeSetting
);
21656 elmStyle
.left
= dim
.l
+ dim
.w
+ "px";
21658 }else if(pos
== "client" || pos
== "center"){
21666 marginBox2contentBox
: layout
.marginBox2contentBox
,
21667 layoutChildren
: layout
.layoutChildren
21672 'dijit/_Contained':function(){
21673 define("dijit/_Contained", [
21674 "dojo/_base/declare", // declare
21675 "./registry" // registry.getEnclosingWidget(), registry.byNode()
21676 ], function(declare
, registry
){
21679 // dijit/_Contained
21681 return declare("dijit._Contained", null, {
21683 // Mixin for widgets that are children of a container widget
21686 // | // make a basic custom widget that knows about it's parents
21687 // | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
21689 _getSibling: function(/*String*/ which
){
21691 // Returns next or previous sibling
21693 // Either "next" or "previous"
21696 var node
= this.domNode
;
21698 node
= node
[which
+"Sibling"];
21699 }while(node
&& node
.nodeType
!= 1);
21700 return node
&& registry
.byNode(node
); // dijit/_WidgetBase
21703 getPreviousSibling: function(){
21705 // Returns null if this is the first child of the parent,
21706 // otherwise returns the next element sibling to the "left".
21708 return this._getSibling("previous"); // dijit/_WidgetBase
21711 getNextSibling: function(){
21713 // Returns null if this is the last child of the parent,
21714 // otherwise returns the next element sibling to the "right".
21716 return this._getSibling("next"); // dijit/_WidgetBase
21719 getIndexInParent: function(){
21721 // Returns the index of this widget within its container parent.
21722 // It returns -1 if the parent does not exist, or if the parent
21723 // is not a dijit._Container
21725 var p
= this.getParent();
21726 if(!p
|| !p
.getIndexOfChild
){
21729 return p
.getIndexOfChild(this); // int
21735 'dijit/form/DataList':function(){
21736 define("dijit/form/DataList", [
21737 "dojo/_base/declare", // declare
21738 "dojo/dom", // dom.byId
21739 "dojo/_base/lang", // lang.trim
21740 "dojo/query", // query
21741 "dojo/store/Memory",
21742 "../registry" // registry.add registry.remove
21743 ], function(declare
, dom
, lang
, query
, MemoryStore
, registry
){
21746 // dijit/form/DataList
21748 function toItem(/*DOMNode*/ option
){
21750 // Convert `<option>` node to hash
21753 value
: option
.value
,
21754 name
: lang
.trim(option
.innerText
|| option
.textContent
|| '')
21758 return declare("dijit.form.DataList", MemoryStore
, {
21760 // Inefficient but small data store specialized for inlined data via OPTION tags
21763 // Provides a store for inlined data like:
21766 // | <option value="AL">Alabama</option>
21769 constructor: function(params
, srcNodeRef
){
21771 // Create the widget.
21772 // params: Object|null
21773 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
21774 // and functions, typically callbacks like onClick.
21775 // The hash can contain any of the widget's properties, excluding read-only properties.
21776 // srcNodeRef: DOMNode|String
21777 // Attach widget to this DOM node.
21779 // store pointer to original DOM tree
21780 this.domNode
= dom
.byId(srcNodeRef
);
21782 lang
.mixin(this, params
);
21784 registry
.add(this); // add to registry so it can be easily found by id
21786 this.domNode
.style
.display
= "none";
21788 this.inherited(arguments
, [{
21789 data
: query("option", this.domNode
).map(toItem
)
21793 destroy: function(){
21794 registry
.remove(this.id
);
21797 fetchSelectedItem: function(){
21799 // Get the option marked as selected, like `<option selected>`.
21800 // Not part of dojo.data API.
21801 var option
= query("> option[selected]", this.domNode
)[0] || query("> option", this.domNode
)[0];
21802 return option
&& toItem(option
);
21808 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
21809 'dijit/form/CheckBox':function(){
21811 'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n"}});
21812 define("dijit/form/CheckBox", [
21814 "dojo/_base/declare", // declare
21815 "dojo/dom-attr", // domAttr.set
21816 "dojo/has", // has("dijit-legacy-requires")
21817 "dojo/query", // query
21820 "./_CheckBoxMixin",
21821 "dojo/text!./templates/CheckBox.html",
21822 "dojo/NodeList-dom" // NodeList.addClass/removeClass
21823 ], function(require
, declare
, domAttr
, has
, query
, ready
, ToggleButton
, _CheckBoxMixin
, template
){
21826 // dijit/form/CheckBox
21828 // Back compat w/1.6, remove for 2.0
21829 if(has("dijit-legacy-requires")){
21830 ready(0, function(){
21831 var requires
= ["dijit/form/RadioButton"];
21832 require(requires
); // use indirection so modules not rolled into a build
21836 return declare("dijit.form.CheckBox", [ToggleButton
, _CheckBoxMixin
], {
21838 // Same as an HTML checkbox, but with fancy styling.
21841 // User interacts with real html inputs.
21842 // On onclick (which occurs by mouse click, space-bar, or
21843 // using the arrow keys to switch the selected radio button),
21844 // we update the state of the checkbox/radio.
21846 // There are two modes:
21848 // 1. High contrast mode
21851 // In case 1, the regular html inputs are shown and used by the user.
21852 // In case 2, the regular html inputs are invisible but still used by
21853 // the user. They are turned quasi-invisible and overlay the background-image.
21855 templateString
: template
,
21857 baseClass
: "dijitCheckBox",
21859 _setValueAttr: function(/*String|Boolean*/ newValue
, /*Boolean*/ priorityChange
){
21861 // Handler for value= attribute to constructor, and also calls to
21862 // set('value', val).
21864 // During initialization, just saves as attribute to the `<input type=checkbox>`.
21866 // After initialization,
21867 // when passed a boolean, controls whether or not the CheckBox is checked.
21868 // If passed a string, changes the value attribute of the CheckBox (the one
21869 // specified as "value" when the CheckBox was constructed
21870 // (ex: `<input data-dojo-type="dijit/CheckBox" value="chicken">`).
21872 // `widget.set('value', string)` will check the checkbox and change the value to the
21873 // specified string.
21875 // `widget.set('value', boolean)` will change the checked state.
21877 if(typeof newValue
== "string"){
21878 this.inherited(arguments
);
21882 this.set('checked', newValue
, priorityChange
);
21885 _getValueAttr: function(){
21887 // Hook so get('value') works.
21889 // If the CheckBox is checked, returns the value attribute.
21890 // Otherwise returns false.
21891 return (this.checked
? this.value
: false);
21894 // Override behavior from Button, since we don't have an iconNode
21895 _setIconClassAttr
: null,
21897 postMixInProperties: function(){
21898 this.inherited(arguments
);
21900 // Need to set initial checked state as part of template, so that form submit works.
21901 // domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
21902 // to <body>, see #8666
21903 this.checkedAttrSetting
= this.checked
? "checked" : "";
21906 _fillContent: function(){
21907 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
21908 // since CheckBox doesn't even have a container
21911 _onFocus: function(){
21913 query("label[for='"+this.id
+"']").addClass("dijitFocusedLabel");
21915 this.inherited(arguments
);
21918 _onBlur: function(){
21920 query("label[for='"+this.id
+"']").removeClass("dijitFocusedLabel");
21922 this.inherited(arguments
);
21928 'dijit/tree/_dndSelector':function(){
21929 define("dijit/tree/_dndSelector", [
21930 "dojo/_base/array", // array.filter array.forEach array.map
21931 "dojo/_base/connect", // connect.isCopyKey
21932 "dojo/_base/declare", // declare
21933 "dojo/_base/Deferred", // Deferred
21934 "dojo/_base/kernel", // global
21935 "dojo/_base/lang", // lang.hitch
21936 "dojo/cookie", // cookie
21937 "dojo/mouse", // mouse.isLeft
21941 ], function(array
, connect
, declare
, Deferred
, kernel
, lang
, cookie
, mouse
, on
, touch
, _dndContainer
){
21944 // dijit/tree/_dndSelector
21947 return declare("dijit.tree._dndSelector", _dndContainer
, {
21949 // This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
21950 // It's based on `dojo/dnd/Selector`.
21955 // selection: Object
21956 // (id to DomNode) map for every TreeNode that's currently selected.
21957 // The DOMNode is the TreeNode.rowNode.
21961 constructor: function(){
21968 this.anchor
= null;
21970 if(!this.cookieName
&& this.tree
.id
){
21971 this.cookieName
= this.tree
.id
+ "SaveSelectedCookie";
21975 on(this.tree
.domNode
, touch
.press
, lang
.hitch(this,"onMouseDown")),
21976 on(this.tree
.domNode
, touch
.release
, lang
.hitch(this,"onMouseUp")),
21977 on(this.tree
.domNode
, touch
.move, lang
.hitch(this,"onMouseMove"))
21981 // singular: Boolean
21982 // Allows selection of only one element, if true.
21983 // Tree hasn't been tested in singular=true mode, unclear if it works.
21987 getSelectedTreeNodes: function(){
21989 // Returns a list of selected node(s).
21990 // Used by dndSource on the start of a drag.
21993 var nodes
=[], sel
= this.selection
;
21995 nodes
.push(sel
[i
]);
22000 selectNone: function(){
22002 // Unselects all items
22006 this.setSelection([]);
22007 return this; // self
22010 destroy: function(){
22012 // Prepares the object to be garbage-collected
22013 this.inherited(arguments
);
22014 this.selection
= this.anchor
= null;
22016 addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor
){
22018 // add node to current selection
22021 // isAnchor: Boolean
22022 // Whether the node should become anchor.
22024 this.setSelection(this.getSelectedTreeNodes().concat( [node
] ));
22025 if(isAnchor
){ this.anchor
= node
; }
22028 removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
22030 // remove node from current selection
22033 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
22036 isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
22038 // return true if node is currently selected
22040 // the node to check whether it's in the current selection
22042 return node.id && !!this.selection[node.id];
22044 setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
22046 // set the list of selected nodes to be exactly newSelection. All changes to the
22047 // selection should be passed through this function, which ensures that derived
22048 // attributes are kept up to date. Anchor will be deleted if it has been removed
22049 // from the selection, but no new anchor will be added by this function.
22050 // newSelection: Node[]
22051 // list of tree nodes to make selected
22052 var oldSelection = this.getSelectedTreeNodes();
22053 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
22054 node.setSelected(false);
22055 if(this.anchor == node){
22056 delete this.anchor;
22058 delete this.selection[node.id];
22060 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
22061 node.setSelected(true);
22062 this.selection[node.id] = node;
22064 this._updateSelectionProperties();
22066 _setDifference: function(xs,ys){
22068 // Returns a copy of xs which lacks any objects
22069 // occurring in ys. Checks for membership by
22070 // modifying and then reading the object, so it will
22071 // not properly handle sets of numbers or strings.
22073 array.forEach(ys, function(y){ y.__exclude__ = true; });
22074 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
22076 // clean up after ourselves.
22077 array.forEach(ys, function(y){ delete y['__exclude__'] });
22080 _updateSelectionProperties: function(){
22082 // Update the following tree properties from the current selection:
22083 // path[s], selectedItem[s], selectedNode[s]
22085 var selected = this.getSelectedTreeNodes();
22086 var paths = [], nodes = [], selects = [];
22087 array.forEach(selected, function(node){
22088 var ary = node.getTreePath(), model = this.tree.model;
22091 ary = array.map(ary, function(item){
22092 return model.getIdentity(item);
22094 selects.push(ary.join("/"))
22096 var items = array.map(nodes,function(node){ return node.item; });
22097 this.tree._set("paths", paths);
22098 this.tree._set("path", paths[0] || []);
22099 this.tree._set("selectedNodes", nodes);
22100 this.tree._set("selectedNode", nodes[0] || null);
22101 this.tree._set("selectedItems", items);
22102 this.tree._set("selectedItem", items[0] || null);
22103 if (this.tree.persist && selects.length > 0) {
22104 cookie(this.cookieName, selects.join(","), {expires:365});
22107 _getSavedPaths: function(){
22109 // Returns paths of nodes that were selected previously and saved in the cookie.
22111 var tree = this.tree;
22112 if(tree.persist && tree.dndController.cookieName){
22113 var oreo, paths = [];
22114 oreo = cookie(tree.dndController.cookieName);
22116 paths = array.map(oreo.split(","), function(path){
22117 return path.split("/");
22124 onMouseDown: function(e){
22126 // Event processor for onmousedown/ontouchstart
22128 // onmousedown/ontouchstart event
22132 // ignore click on expando node
22133 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
22135 if(mouse.isLeft(e)){
22136 // Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
22137 // for mobile because it will break things completely, see #15838.
22138 e.preventDefault();
22139 }else if(e.type != "touchstart"){
22140 // Ignore right click
22144 var treeNode = this.current,
22145 copy = connect.isCopyKey(e), id = treeNode.id;
22147 // if shift key is not pressed, and the node is already in the selection,
22148 // delay deselection until onmouseup so in the case of DND, deselection
22149 // will be canceled by onmousemove.
22150 if(!this.singular && !e.shiftKey && this.selection[id]){
22151 this._doDeselect = true;
22154 this._doDeselect = false;
22156 this.userSelect(treeNode, copy, e.shiftKey);
22159 onMouseUp: function(e){
22161 // Event processor for onmouseup/ontouchend
22163 // onmouseup/ontouchend event
22167 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
22168 // a already selected item (to deselect the item), or click on a not-yet selected item
22169 // (which should remove all current selection, and add the clicked item). This can not
22170 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
22171 // the deselection logic here, the user can drags an already selected item.
22172 if(!this._doDeselect){ return; }
22173 this._doDeselect = false;
22174 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
22176 onMouseMove: function(/*===== e =====*/){
22178 // event processor for onmousemove/ontouchmove
22180 // onmousemove/ontouchmove event
22181 this._doDeselect
= false;
22184 _compareNodes: function(n1
, n2
){
22189 if('sourceIndex' in document
.documentElement
){ //IE
22190 //TODO: does not yet work if n1 and/or n2 is a text node
22191 return n1
.sourceIndex
- n2
.sourceIndex
;
22192 }else if('compareDocumentPosition' in document
.documentElement
){ //FF, Opera
22193 return n1
.compareDocumentPosition(n2
) & 2 ? 1: -1;
22194 }else if(document
.createRange
){ //Webkit
22195 var r1
= doc
.createRange();
22196 r1
.setStartBefore(n1
);
22198 var r2
= doc
.createRange();
22199 r2
.setStartBefore(n2
);
22201 return r1
.compareBoundaryPoints(r1
.END_TO_END
, r2
);
22203 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
22207 userSelect: function(node
, multi
, range
){
22209 // Add or remove the given node from selection, responding
22210 // to a user action such as a click or keypress.
22212 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
22214 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
22219 if(this.anchor
== node
&& multi
){
22222 this.setSelection([node
]);
22223 this.anchor
= node
;
22226 if(range
&& this.anchor
){
22227 var cr
= this._compareNodes(this.anchor
.rowNode
, node
.rowNode
),
22228 begin
, end
, anchor
= this.anchor
;
22230 if(cr
< 0){ //current is after anchor
22233 }else{ //current is before anchor
22238 //add everything betweeen begin and end inclusively
22239 while(begin
!= end
){
22241 begin
= this.tree
._getNextNode(begin
);
22245 this.setSelection(nodes
);
22247 if( this.selection
[ node
.id
] && multi
){
22248 this.removeTreeNode( node
);
22250 this.addTreeNode(node
, true);
22252 this.setSelection([node
]);
22253 this.anchor
= node
;
22259 getItem: function(/*String*/ key
){
22261 // Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
22262 // Called by dojo/dnd/Source.checkAcceptance().
22266 var widget
= this.selection
[key
];
22270 }; // dojo/dnd/Container._Item
22273 forInSelectedItems: function(/*Function*/ f
, /*Object?*/ o
){
22275 // Iterates over selected items;
22276 // see `dojo/dnd/Container.forInItems()` for details
22277 o
= o
|| kernel
.global
;
22278 for(var id
in this.selection
){
22279 // console.log("selected item id: " + id);
22280 f
.call(o
, this.getItem(id
), id
, this);
22287 'dijit/_Container':function(){
22288 define("dijit/_Container", [
22289 "dojo/_base/array", // array.forEach array.indexOf
22290 "dojo/_base/declare", // declare
22291 "dojo/dom-construct" // domConstruct.place
22292 ], function(array
, declare
, domConstruct
){
22295 // dijit/_Container
22297 return declare("dijit._Container", null, {
22299 // Mixin for widgets that contain HTML and/or a set of widget children.
22301 buildRendering: function(){
22302 this.inherited(arguments
);
22303 if(!this.containerNode
){
22304 // all widgets with descendants must set containerNode
22305 this.containerNode
= this.domNode
;
22309 addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex
){
22311 // Makes the given widget a child of this widget.
22313 // Inserts specified child widget's dom node as a child of this widget's
22314 // container node, and possibly does other processing (such as layout).
22316 // Functionality is undefined if this widget contains anything besides
22317 // a list of child widgets (ie, if it contains arbitrary non-widget HTML).
22319 var refNode
= this.containerNode
;
22320 if(insertIndex
&& typeof insertIndex
== "number"){
22321 var children
= this.getChildren();
22322 if(children
&& children
.length
>= insertIndex
){
22323 refNode
= children
[insertIndex
-1].domNode
;
22324 insertIndex
= "after";
22327 domConstruct
.place(widget
.domNode
, refNode
, insertIndex
);
22329 // If I've been started but the child widget hasn't been started,
22330 // start it now. Make sure to do this after widget has been
22331 // inserted into the DOM tree, so it can see that it's being controlled by me,
22332 // so it doesn't try to size itself.
22333 if(this._started
&& !widget
._started
){
22338 removeChild: function(/*Widget|int*/ widget
){
22340 // Removes the passed widget instance from this widget but does
22341 // not destroy it. You can also pass in an integer indicating
22342 // the index within the container to remove (ie, removeChild(5) removes the sixth widget).
22344 if(typeof widget
== "number"){
22345 widget
= this.getChildren()[widget
];
22349 var node
= widget
.domNode
;
22350 if(node
&& node
.parentNode
){
22351 node
.parentNode
.removeChild(node
); // detach but don't destroy
22356 hasChildren: function(){
22358 // Returns true if widget has child widgets, i.e. if this.containerNode contains widgets.
22359 return this.getChildren().length
> 0; // Boolean
22362 _getSiblingOfChild: function(/*dijit/_WidgetBase*/ child, /*int*/ dir
){
22364 // Get the next or previous widget sibling of child
22366 // if 1, get the next sibling
22367 // if -1, get the previous sibling
22370 var children
= this.getChildren(),
22371 idx
= array
.indexOf(this.getChildren(), child
); // int
22372 return children
[idx
+ dir
];
22375 getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
22377 // Gets the index of the child in this container or -1 if not found
22378 return array.indexOf(this.getChildren(), child); // int
22384 'dojo/data/ItemFileReadStore':function(){
22385 define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
22386 "../Evented", "./util/filter", "./util/simpleFetch", "../date/stamp"
22387 ], function(kernel, lang, declare, array, xhr, Evented, filterUtil, simpleFetch, dateStamp){
22390 // dojo/data/ItemFileReadStore
22392 var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
22394 // The ItemFileReadStore implements the dojo/data/api/Read API and reads
22395 // data from JSON files that have contents in this format --
22397 // | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
22398 // | { name:'Fozzie Bear', wears:['hat', 'tie']},
22399 // | { name:'Miss Piggy', pets:'Foo-Foo'}
22401 // Note that it can also contain an 'identifier' property that specified which attribute on the items
22402 // in the array of items that acts as the unique identifier for that item.
22404 constructor: function(/* Object */ keywordParameters
){
22407 // keywordParameters:
22408 // {url: String} {data: jsonObject} {typeMap: object}
22409 // The structure of the typeMap object is as follows:
22411 // | type0: function || object,
22412 // | type1: function || object,
22414 // | typeN: function || object
22416 // Where if it is a function, it is assumed to be an object constructor that takes the
22417 // value of _value as the initialization parameters. If it is an object, then it is assumed
22418 // to be an object of general form:
22420 // | type: function, //constructor.
22421 // | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
22424 this._arrayOfAllItems
= [];
22425 this._arrayOfTopLevelItems
= [];
22426 this._loadFinished
= false;
22427 this._jsonFileUrl
= keywordParameters
.url
;
22428 this._ccUrl
= keywordParameters
.url
;
22429 this.url
= keywordParameters
.url
;
22430 this._jsonData
= keywordParameters
.data
;
22432 this._datatypeMap
= keywordParameters
.typeMap
|| {};
22433 if(!this._datatypeMap
['Date']){
22434 //If no default mapping for dates, then set this as default.
22435 //We use the dojo/date/stamp here because the ISO format is the 'dojo way'
22436 //of generically representing dates.
22437 this._datatypeMap
['Date'] = {
22439 deserialize: function(value
){
22440 return dateStamp
.fromISOString(value
);
22444 this._features
= {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
22445 this._itemsByIdentity
= null;
22446 this._storeRefPropName
= "_S"; // Default name for the store reference to attach to every item.
22447 this._itemNumPropName
= "_0"; // Default Item Id for isItem to attach to every item.
22448 this._rootItemPropName
= "_RI"; // Default Item Id for isItem to attach to every item.
22449 this._reverseRefMap
= "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
22450 this._loadInProgress
= false; //Got to track the initial load to prevent duelling loads of the dataset.
22451 this._queuedFetches
= [];
22452 if(keywordParameters
.urlPreventCache
!== undefined){
22453 this.urlPreventCache
= keywordParameters
.urlPreventCache
?true:false;
22455 if(keywordParameters
.hierarchical
!== undefined){
22456 this.hierarchical
= keywordParameters
.hierarchical
?true:false;
22458 if(keywordParameters
.clearOnClose
){
22459 this.clearOnClose
= true;
22461 if("failOk" in keywordParameters
){
22462 this.failOk
= keywordParameters
.failOk
?true:false;
22466 url
: "", // use "" rather than undefined for the benefit of the parser (#3539)
22468 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
22469 //when clearOnClose and close is used.
22472 data
: null, // define this so that the parser can populate it
22474 typeMap
: null, //Define so parser can populate.
22476 // clearOnClose: Boolean
22477 // Parameter to allow users to specify if a close call should force a reload or not.
22478 // By default, it retains the old behavior of not clearing if close is called. But
22479 // if set true, the store will be reset to default state. Note that by doing this,
22480 // all item handles will become invalid and a new fetch must be issued.
22481 clearOnClose
: false,
22483 // urlPreventCache: Boolean
22484 // Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
22485 // Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
22486 // Added for tracker: #6072
22487 urlPreventCache
: false,
22490 // Parameter for specifying that it is OK for the xhrGet call to fail silently.
22493 // hierarchical: Boolean
22494 // Parameter to indicate to process data from the url as hierarchical
22495 // (data items can contain other data items in js form). Default is true
22496 // for backwards compatibility. False means only root items are processed
22497 // as items, all child objects outside of type-mapped objects and those in
22498 // specific reference format, are left straight JS data objects.
22499 hierarchical
: true,
22501 _assertIsItem: function(/* dojo/data/api/Item */ item){
22503 // This function tests whether the item passed in is indeed an item in the store.
22505 // The item to test for being contained by the store.
22506 if(!this.isItem(item)){
22507 throw new Error(this.declaredClass + ": Invalid item argument.");
22511 _assertIsAttribute: function(/* attribute-name-string */ attribute
){
22513 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
22515 // The attribute to test for being contained by the store.
22516 if(typeof attribute
!== "string"){
22517 throw new Error(this.declaredClass
+ ": Invalid attribute argument.");
22521 getValue: function( /* dojo/data/api/Item */ item,
22522 /* attribute-name-string */ attribute
,
22523 /* value? */ defaultValue
){
22525 // See dojo/data/api/Read.getValue()
22526 var values
= this.getValues(item
, attribute
);
22527 return (values
.length
> 0)?values
[0]:defaultValue
; // mixed
22530 getValues: function(/* dojo/data/api/Item */ item,
22531 /* attribute-name-string */ attribute
){
22533 // See dojo/data/api/Read.getValues()
22535 this._assertIsItem(item
);
22536 this._assertIsAttribute(attribute
);
22537 // Clone it before returning. refs: #10474
22538 return (item
[attribute
] || []).slice(0); // Array
22541 getAttributes: function(/* dojo/data/api/Item */ item){
22543 // See dojo/data/api/Read.getAttributes()
22544 this._assertIsItem(item);
22545 var attributes = [];
22546 for(var key in item){
22547 // Save off only the real item attributes, not the special id marks for O(1) isItem.
22548 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
22549 attributes.push(key);
22552 return attributes; // Array
22555 hasAttribute: function( /* dojo/data/api/Item */ item,
22556 /* attribute-name-string */ attribute
){
22558 // See dojo/data/api/Read.hasAttribute()
22559 this._assertIsItem(item
);
22560 this._assertIsAttribute(attribute
);
22561 return (attribute
in item
);
22564 containsValue: function(/* dojo/data/api/Item */ item,
22565 /* attribute-name-string */ attribute
,
22566 /* anything */ value
){
22568 // See dojo/data/api/Read.containsValue()
22569 var regexp
= undefined;
22570 if(typeof value
=== "string"){
22571 regexp
= filterUtil
.patternToRegExp(value
, false);
22573 return this._containsValue(item
, attribute
, value
, regexp
); //boolean.
22576 _containsValue: function( /* dojo/data/api/Item */ item,
22577 /* attribute-name-string */ attribute
,
22578 /* anything */ value
,
22579 /* RegExp?*/ regexp
){
22581 // Internal function for looking at the values contained by the item.
22583 // Internal function for looking at the values contained by the item. This
22584 // function allows for denoting if the comparison should be case sensitive for
22585 // strings or not (for handling filtering cases where string case should not matter)
22587 // The data item to examine for attribute values.
22589 // The attribute to inspect.
22591 // The value to match.
22593 // Optional regular expression generated off value if value was of string type to handle wildcarding.
22594 // If present and attribute values are string, then it can be used for comparison instead of 'value'
22595 return array
.some(this.getValues(item
, attribute
), function(possibleValue
){
22596 if(possibleValue
!== null && !lang
.isObject(possibleValue
) && regexp
){
22597 if(possibleValue
.toString().match(regexp
)){
22598 return true; // Boolean
22600 }else if(value
=== possibleValue
){
22601 return true; // Boolean
22606 isItem: function(/* anything */ something
){
22608 // See dojo/data/api/Read.isItem()
22609 if(something
&& something
[this._storeRefPropName
] === this){
22610 if(this._arrayOfAllItems
[something
[this._itemNumPropName
]] === something
){
22614 return false; // Boolean
22617 isItemLoaded: function(/* anything */ something
){
22619 // See dojo/data/api/Read.isItemLoaded()
22620 return this.isItem(something
); //boolean
22623 loadItem: function(/* object */ keywordArgs
){
22625 // See dojo/data/api/Read.loadItem()
22626 this._assertIsItem(keywordArgs
.item
);
22629 getFeatures: function(){
22631 // See dojo/data/api/Read.getFeatures()
22632 return this._features
; //Object
22635 getLabel: function(/* dojo/data/api/Item */ item){
22637 // See dojo/data/api/Read.getLabel()
22638 if(this._labelAttr && this.isItem(item)){
22639 return this.getValue(item,this._labelAttr); //String
22641 return undefined; //undefined
22644 getLabelAttributes: function(/* dojo/data/api/Item */ item){
22646 // See dojo/data/api/Read.getLabelAttributes()
22647 if(this._labelAttr){
22648 return [this._labelAttr]; //array
22650 return null; //null
22653 filter: function(/* Object */ requestArgs
, /* item[] */ arrayOfItems
, /* Function */ findCallback
){
22655 // This method handles the basic filtering needs for ItemFile* based stores.
22659 if(requestArgs
.query
){
22661 ignoreCase
= requestArgs
.queryOptions
? requestArgs
.queryOptions
.ignoreCase
: false;
22663 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
22664 //same value for each item examined. Much more efficient.
22665 var regexpList
= {};
22666 for(key
in requestArgs
.query
){
22667 value
= requestArgs
.query
[key
];
22668 if(typeof value
=== "string"){
22669 regexpList
[key
] = filterUtil
.patternToRegExp(value
, ignoreCase
);
22670 }else if(value
instanceof RegExp
){
22671 regexpList
[key
] = value
;
22674 for(i
= 0; i
< arrayOfItems
.length
; ++i
){
22676 var candidateItem
= arrayOfItems
[i
];
22677 if(candidateItem
=== null){
22680 for(key
in requestArgs
.query
){
22681 value
= requestArgs
.query
[key
];
22682 if(!this._containsValue(candidateItem
, key
, value
, regexpList
[key
])){
22688 items
.push(candidateItem
);
22691 findCallback(items
, requestArgs
);
22693 // We want a copy to pass back in case the parent wishes to sort the array.
22694 // We shouldn't allow resort of the internal list, so that multiple callers
22695 // can get lists and sort without affecting each other. We also need to
22696 // filter out any null values that have been left as a result of deleteItem()
22697 // calls in ItemFileWriteStore.
22698 for(i
= 0; i
< arrayOfItems
.length
; ++i
){
22699 var item
= arrayOfItems
[i
];
22704 findCallback(items
, requestArgs
);
22708 _fetchItems: function( /* Object */ keywordArgs
,
22709 /* Function */ findCallback
,
22710 /* Function */ errorCallback
){
22712 // See dojo/data/util.simpleFetch.fetch()
22715 if(this._loadFinished
){
22716 this.filter(keywordArgs
, this._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
22718 //Do a check on the JsonFileUrl and crosscheck it.
22719 //If it doesn't match the cross-check, it needs to be updated
22720 //This allows for either url or _jsonFileUrl to he changed to
22721 //reset the store load location. Done this way for backwards
22722 //compatibility. People use _jsonFileUrl (even though officially
22724 if(this._jsonFileUrl
!== this._ccUrl
){
22725 kernel
.deprecated(this.declaredClass
+ ": ",
22726 "To change the url, set the url property of the store," +
22727 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
22728 this._ccUrl
= this._jsonFileUrl
;
22729 this.url
= this._jsonFileUrl
;
22730 }else if(this.url
!== this._ccUrl
){
22731 this._jsonFileUrl
= this.url
;
22732 this._ccUrl
= this.url
;
22735 //See if there was any forced reset of data.
22736 if(this.data
!= null){
22737 this._jsonData
= this.data
;
22741 if(this._jsonFileUrl
){
22742 //If fetches come in before the loading has finished, but while
22743 //a load is in progress, we have to defer the fetching to be
22744 //invoked in the callback.
22745 if(this._loadInProgress
){
22746 this._queuedFetches
.push({args
: keywordArgs
, filter
: lang
.hitch(self
, "filter"), findCallback
: lang
.hitch(self
, findCallback
)});
22748 this._loadInProgress
= true;
22750 url
: self
._jsonFileUrl
,
22751 handleAs
: "json-comment-optional",
22752 preventCache
: this.urlPreventCache
,
22753 failOk
: this.failOk
22755 var getHandler
= xhr
.get(getArgs
);
22756 getHandler
.addCallback(function(data
){
22758 self
._getItemsFromLoadedData(data
);
22759 self
._loadFinished
= true;
22760 self
._loadInProgress
= false;
22762 self
.filter(keywordArgs
, self
._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
22763 self
._handleQueuedFetches();
22765 self
._loadFinished
= true;
22766 self
._loadInProgress
= false;
22767 errorCallback(e
, keywordArgs
);
22770 getHandler
.addErrback(function(error
){
22771 self
._loadInProgress
= false;
22772 errorCallback(error
, keywordArgs
);
22775 //Wire up the cancel to abort of the request
22776 //This call cancel on the deferred if it hasn't been called
22777 //yet and then will chain to the simple abort of the
22778 //simpleFetch keywordArgs
22779 var oldAbort
= null;
22780 if(keywordArgs
.abort
){
22781 oldAbort
= keywordArgs
.abort
;
22783 keywordArgs
.abort = function(){
22784 var df
= getHandler
;
22785 if(df
&& df
.fired
=== -1){
22790 oldAbort
.call(keywordArgs
);
22794 }else if(this._jsonData
){
22796 this._loadFinished
= true;
22797 this._getItemsFromLoadedData(this._jsonData
);
22798 this._jsonData
= null;
22799 self
.filter(keywordArgs
, this._getItemsArray(keywordArgs
.queryOptions
), findCallback
);
22801 errorCallback(e
, keywordArgs
);
22804 errorCallback(new Error(this.declaredClass
+ ": No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs
);
22809 _handleQueuedFetches: function(){
22811 // Internal function to execute delayed request in the store.
22813 //Execute any deferred fetches now.
22814 if(this._queuedFetches
.length
> 0){
22815 for(var i
= 0; i
< this._queuedFetches
.length
; i
++){
22816 var fData
= this._queuedFetches
[i
],
22817 delayedQuery
= fData
.args
,
22818 delayedFilter
= fData
.filter
,
22819 delayedFindCallback
= fData
.findCallback
;
22821 delayedFilter(delayedQuery
, this._getItemsArray(delayedQuery
.queryOptions
), delayedFindCallback
);
22823 this.fetchItemByIdentity(delayedQuery
);
22826 this._queuedFetches
= [];
22830 _getItemsArray: function(/*object?*/queryOptions
){
22832 // Internal function to determine which list of items to search over.
22833 // queryOptions: The query options parameter, if any.
22834 if(queryOptions
&& queryOptions
.deep
){
22835 return this._arrayOfAllItems
;
22837 return this._arrayOfTopLevelItems
;
22840 close: function(/*dojo/data/api/Request|Object?*/ request){
22842 // See dojo/data/api/Read.close()
22843 if(this.clearOnClose &&
22844 this._loadFinished &&
22845 !this._loadInProgress){
22846 //Reset all internalsback to default state. This will force a reload
22847 //on next fetch. This also checks that the data or url param was set
22848 //so that the store knows it can get data. Without one of those being set,
22849 //the next fetch will trigger an error.
22851 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
22852 (this.url == "" || this.url == null)
22853 ) && this.data == null){
22854 console.debug(this.declaredClass + ": WARNING! Data reload " +
22855 " information has not been provided." +
22856 " Please set 'url' or 'data' to the appropriate value before" +
22857 " the next fetch");
22859 this._arrayOfAllItems = [];
22860 this._arrayOfTopLevelItems = [];
22861 this._loadFinished = false;
22862 this._itemsByIdentity = null;
22863 this._loadInProgress = false;
22864 this._queuedFetches = [];
22868 _getItemsFromLoadedData: function(/* Object */ dataObject
){
22870 // Function to parse the loaded data into item format and build the internal items array.
22872 // Function to parse the loaded data into item format and build the internal items array.
22874 // The JS data object containing the raw data to convery into item format.
22876 // Array of items in store item format.
22878 // First, we define a couple little utility functions...
22879 var addingArrays
= false,
22882 function valueIsAnItem(/* anything */ aValue
){
22884 // Given any sort of value that could be in the raw json data,
22885 // return true if we should interpret the value as being an
22886 // item itself, rather than a literal value or a reference.
22888 // | false == valueIsAnItem("Kermit");
22889 // | false == valueIsAnItem(42);
22890 // | false == valueIsAnItem(new Date());
22891 // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
22892 // | false == valueIsAnItem({_reference:'Kermit'});
22893 // | true == valueIsAnItem({name:'Kermit', color:'green'});
22894 // | true == valueIsAnItem({iggy:'pop'});
22895 // | true == valueIsAnItem({foo:42});
22896 return (aValue
!== null) &&
22897 (typeof aValue
=== "object") &&
22898 (!lang
.isArray(aValue
) || addingArrays
) &&
22899 (!lang
.isFunction(aValue
)) &&
22900 (aValue
.constructor == Object
|| lang
.isArray(aValue
)) &&
22901 (typeof aValue
._reference
=== "undefined") &&
22902 (typeof aValue
._type
=== "undefined") &&
22903 (typeof aValue
._value
=== "undefined") &&
22907 function addItemAndSubItemsToArrayOfAllItems(/* dojo/data/api/Item */ anItem){
22908 self._arrayOfAllItems.push(anItem);
22909 for(var attribute in anItem){
22910 var valueForAttribute = anItem[attribute];
22911 if(valueForAttribute){
22912 if(lang.isArray(valueForAttribute)){
22913 var valueArray = valueForAttribute;
22914 for(var k = 0; k < valueArray.length; ++k){
22915 var singleValue = valueArray[k];
22916 if(valueIsAnItem(singleValue)){
22917 addItemAndSubItemsToArrayOfAllItems(singleValue);
22921 if(valueIsAnItem(valueForAttribute)){
22922 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
22929 this._labelAttr = dataObject.label;
22931 // We need to do some transformations to convert the data structure
22932 // that we read from the file into a format that will be convenient
22933 // to work with in memory.
22935 // Step 1: Walk through the object hierarchy and build a list of all items
22938 this._arrayOfAllItems = [];
22939 this._arrayOfTopLevelItems = dataObject.items;
22941 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
22942 item = this._arrayOfTopLevelItems[i];
22943 if(lang.isArray(item)){
22944 addingArrays = true;
22946 addItemAndSubItemsToArrayOfAllItems(item);
22947 item[this._rootItemPropName]=true;
22950 // Step 2: Walk through all the attribute values of all the items,
22951 // and replace single values with arrays. For example, we change this:
22952 // { name:'Miss Piggy', pets:'Foo-Foo'}
22954 // { name:['Miss Piggy'], pets:['Foo-Foo']}
22956 // We also store the attribute names so we can validate our store
22957 // reference and item id special properties for the O(1) isItem
22958 var allAttributeNames = {},
22961 for(i = 0; i < this._arrayOfAllItems.length; ++i){
22962 item = this._arrayOfAllItems[i];
22964 if(key !== this._rootItemPropName){
22965 var value = item[key];
22966 if(value !== null){
22967 if(!lang.isArray(value)){
22968 item[key] = [value];
22971 item[key] = [null];
22974 allAttributeNames[key]=key;
22978 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
22979 // This should go really fast, it will generally never even run the loop.
22980 while(allAttributeNames[this._storeRefPropName]){
22981 this._storeRefPropName += "_";
22983 while(allAttributeNames[this._itemNumPropName]){
22984 this._itemNumPropName += "_";
22986 while(allAttributeNames[this._reverseRefMap]){
22987 this._reverseRefMap += "_";
22990 // Step 4: Some data files specify an optional 'identifier', which is
22991 // the name of an attribute that holds the identity of each item.
22992 // If this data file specified an identifier attribute, then build a
22993 // hash table of items keyed by the identity of the items.
22996 var identifier = dataObject.identifier;
22998 this._itemsByIdentity = {};
22999 this._features['dojo.data.api.Identity'] = identifier;
23000 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23001 item = this._arrayOfAllItems[i];
23002 arrayOfValues = item[identifier];
23003 var identity = arrayOfValues[0];
23004 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
23005 this._itemsByIdentity[identity] = item;
23007 if(this._jsonFileUrl){
23008 throw new Error(this.declaredClass + ": The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23009 }else if(this._jsonData){
23010 throw new Error(this.declaredClass + ": The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
23015 this._features['dojo.data.api.Identity'] = Number;
23018 // Step 5: Walk through all the items, and set each item's properties
23019 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
23020 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23021 item = this._arrayOfAllItems[i];
23022 item[this._storeRefPropName] = this;
23023 item[this._itemNumPropName] = i;
23026 // Step 6: We walk through all the attribute values of all the items,
23027 // looking for type/value literals and item-references.
23029 // We replace item-references with pointers to items. For example, we change:
23030 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23032 // { name:['Kermit'], friends:[miss_piggy] }
23033 // (where miss_piggy is the object representing the 'Miss Piggy' item).
23035 // We replace type/value pairs with typed-literals. For example, we change:
23036 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
23038 // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
23040 // We also generate the associate map for all items for the O(1) isItem function.
23041 for(i = 0; i < this._arrayOfAllItems.length; ++i){
23042 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23044 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
23045 for(var j = 0; j < arrayOfValues.length; ++j){
23046 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
23047 if(value !== null && typeof value == "object"){
23048 if(("_type" in value) && ("_value" in value)){
23049 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
23050 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
23052 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
23053 }else if(lang.isFunction(mappingObj)){
23054 arrayOfValues[j] = new mappingObj(value._value);
23055 }else if(lang.isFunction(mappingObj.deserialize)){
23056 arrayOfValues[j] = mappingObj.deserialize(value._value);
23058 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
23061 if(value._reference){
23062 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
23063 if(!lang.isObject(referenceDescription)){
23064 // example: 'Miss Piggy'
23065 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
23066 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
23068 // example: {name:'Miss Piggy'}
23069 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
23070 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
23071 var candidateItem = this._arrayOfAllItems[k],
23073 for(var refKey in referenceDescription){
23074 if(candidateItem[refKey] != referenceDescription[refKey]){
23079 arrayOfValues[j] = candidateItem;
23083 if(this.referenceIntegrity){
23084 var refItem = arrayOfValues[j];
23085 if(this.isItem(refItem)){
23086 this._addReferenceToMap(refItem, item, key);
23089 }else if(this.isItem(value)){
23090 //It's a child item (not one referenced through _reference).
23091 //We need to treat this as a referenced item, so it can be cleaned up
23092 //in a write store easily.
23093 if(this.referenceIntegrity){
23094 this._addReferenceToMap(value, item, key);
23103 _addReferenceToMap: function(/*item*/ refItem
, /*item*/ parentItem
, /*string*/ attribute
){
23105 // Method to add an reference map entry for an item and attribute.
23107 // Method to add an reference map entry for an item and attribute.
23109 // The item that is referenced.
23111 // The item that holds the new reference to refItem.
23113 // The attribute on parentItem that contains the new reference.
23115 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
23118 getIdentity: function(/* dojo/data/api/Item */ item){
23120 // See dojo/data/api/Identity.getIdentity()
23121 var identifier = this._features['dojo.data.api.Identity'];
23122 if(identifier === Number){
23123 return item[this._itemNumPropName]; // Number
23125 var arrayOfValues = item[identifier];
23127 return arrayOfValues[0]; // Object|String
23130 return null; // null
23133 fetchItemByIdentity: function(/* Object */ keywordArgs
){
23135 // See dojo/data/api/Identity.fetchItemByIdentity()
23137 // Hasn't loaded yet, we have to trigger the load.
23140 if(!this._loadFinished
){
23142 //Do a check on the JsonFileUrl and crosscheck it.
23143 //If it doesn't match the cross-check, it needs to be updated
23144 //This allows for either url or _jsonFileUrl to he changed to
23145 //reset the store load location. Done this way for backwards
23146 //compatibility. People use _jsonFileUrl (even though officially
23148 if(this._jsonFileUrl
!== this._ccUrl
){
23149 kernel
.deprecated(this.declaredClass
+ ": ",
23150 "To change the url, set the url property of the store," +
23151 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23152 this._ccUrl
= this._jsonFileUrl
;
23153 this.url
= this._jsonFileUrl
;
23154 }else if(this.url
!== this._ccUrl
){
23155 this._jsonFileUrl
= this.url
;
23156 this._ccUrl
= this.url
;
23159 //See if there was any forced reset of data.
23160 if(this.data
!= null && this._jsonData
== null){
23161 this._jsonData
= this.data
;
23165 if(this._jsonFileUrl
){
23167 if(this._loadInProgress
){
23168 this._queuedFetches
.push({args
: keywordArgs
});
23170 this._loadInProgress
= true;
23172 url
: self
._jsonFileUrl
,
23173 handleAs
: "json-comment-optional",
23174 preventCache
: this.urlPreventCache
,
23175 failOk
: this.failOk
23177 var getHandler
= xhr
.get(getArgs
);
23178 getHandler
.addCallback(function(data
){
23179 var scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23181 self
._getItemsFromLoadedData(data
);
23182 self
._loadFinished
= true;
23183 self
._loadInProgress
= false;
23184 item
= self
._getItemByIdentity(keywordArgs
.identity
);
23185 if(keywordArgs
.onItem
){
23186 keywordArgs
.onItem
.call(scope
, item
);
23188 self
._handleQueuedFetches();
23190 self
._loadInProgress
= false;
23191 if(keywordArgs
.onError
){
23192 keywordArgs
.onError
.call(scope
, error
);
23196 getHandler
.addErrback(function(error
){
23197 self
._loadInProgress
= false;
23198 if(keywordArgs
.onError
){
23199 var scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23200 keywordArgs
.onError
.call(scope
, error
);
23205 }else if(this._jsonData
){
23206 // Passed in data, no need to xhr.
23207 self
._getItemsFromLoadedData(self
._jsonData
);
23208 self
._jsonData
= null;
23209 self
._loadFinished
= true;
23210 item
= self
._getItemByIdentity(keywordArgs
.identity
);
23211 if(keywordArgs
.onItem
){
23212 scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23213 keywordArgs
.onItem
.call(scope
, item
);
23217 // Already loaded. We can just look it up and call back.
23218 item
= this._getItemByIdentity(keywordArgs
.identity
);
23219 if(keywordArgs
.onItem
){
23220 scope
= keywordArgs
.scope
?keywordArgs
.scope
:kernel
.global
;
23221 keywordArgs
.onItem
.call(scope
, item
);
23226 _getItemByIdentity: function(/* Object */ identity
){
23228 // Internal function to look an item up by its identity map.
23230 if(this._itemsByIdentity
){
23231 // If this map is defined, we need to just try to get it. If it fails
23232 // the item does not exist.
23233 if(Object
.hasOwnProperty
.call(this._itemsByIdentity
, identity
)){
23234 item
= this._itemsByIdentity
[identity
];
23236 }else if (Object
.hasOwnProperty
.call(this._arrayOfAllItems
, identity
)){
23237 item
= this._arrayOfAllItems
[identity
];
23239 if(item
=== undefined){
23242 return item
; // Object
23245 getIdentityAttributes: function(/* dojo/data/api/Item */ item){
23247 // See dojo/data/api/Identity.getIdentityAttributes()
23249 var identifier = this._features['dojo.data.api.Identity'];
23250 if(identifier === Number){
23251 // If (identifier === Number) it means getIdentity() just returns
23252 // an integer item-number for each item. The dojo/data/api/Identity
23253 // spec says we need to return null if the identity is not composed
23255 return null; // null
23257 return [identifier]; // Array
23261 _forceLoad: function(){
23263 // Internal function to force a load of the store if it hasn't occurred yet. This is required
23264 // for specific functions to work properly.
23266 //Do a check on the JsonFileUrl and crosscheck it.
23267 //If it doesn't match the cross-check, it needs to be updated
23268 //This allows for either url or _jsonFileUrl to he changed to
23269 //reset the store load location. Done this way for backwards
23270 //compatibility. People use _jsonFileUrl (even though officially
23272 if(this._jsonFileUrl !== this._ccUrl){
23273 kernel.deprecated(this.declaredClass + ": ",
23274 "To change the url, set the url property of the store," +
23275 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
23276 this._ccUrl = this._jsonFileUrl;
23277 this.url = this._jsonFileUrl;
23278 }else if(this.url !== this._ccUrl){
23279 this._jsonFileUrl = this.url;
23280 this._ccUrl = this.url;
23283 //See if there was any forced reset of data.
23284 if(this.data != null){
23285 this._jsonData = this.data;
23289 if(this._jsonFileUrl){
23291 url: this._jsonFileUrl,
23292 handleAs: "json-comment-optional",
23293 preventCache: this.urlPreventCache,
23294 failOk: this.failOk,
23297 var getHandler = xhr.get(getArgs);
23298 getHandler.addCallback(function(data){
23300 //Check to be sure there wasn't another load going on concurrently
23301 //So we don't clobber data that comes in on it. If there is a load going on
23302 //then do not save this data. It will potentially clobber current data.
23303 //We mainly wanted to sync/wait here.
23304 //TODO: Revisit the loading scheme of this store to improve multi-initial
23305 //request handling.
23306 if(self._loadInProgress !== true && !self._loadFinished){
23307 self._getItemsFromLoadedData(data);
23308 self._loadFinished = true;
23309 }else if(self._loadInProgress){
23310 //Okay, we hit an error state we can't recover from. A forced load occurred
23311 //while an async load was occurring. Since we cannot block at this point, the best
23312 //that can be managed is to throw an error.
23313 throw new Error(this.declaredClass + ": Unable to perform a synchronous load, an async load is in progress.");
23320 getHandler.addErrback(function(error){
23323 }else if(this._jsonData){
23324 self._getItemsFromLoadedData(self._jsonData);
23325 self._jsonData = null;
23326 self._loadFinished = true;
23330 //Mix in the simple fetch implementation to this class.
23331 lang.extend(ItemFileReadStore,simpleFetch);
23333 return ItemFileReadStore;
23338 'dojo/html':function(){
23339 define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
23340 function(kernel, lang, darray, declare, dom, domConstruct, parser){
23348 lang.setObject("dojo.html", html);
23350 // the parser might be needed..
23352 // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
23355 html._secureForInnerHtml = function(/*String*/ cont
){
23357 // removes !DOCTYPE and title elements from the html string.
23359 // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
23360 // must go into head, so we need to cut out those tags
23362 // An html string for insertion into the dom
23364 return cont
.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
23367 html
._emptyNode
= domConstruct
.empty
;
23369 dojo.html._emptyNode = function(node){
23371 // Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
23374 // the parent element
23378 html
._setNodeContent = function(/*DomNode*/ node
, /*String|DomNode|NodeList*/ cont
){
23380 // inserts the given content into the given node
23382 // the parent element
23384 // the content to be set on the parent element.
23385 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23388 domConstruct
.empty(node
);
23391 if(typeof cont
== "string"){
23392 cont
= domConstruct
.toDom(cont
, node
.ownerDocument
);
23394 if(!cont
.nodeType
&& lang
.isArrayLike(cont
)){
23395 // handle as enumerable, but it may shrink as we enumerate it
23396 for(var startlen
=cont
.length
, i
=0; i
<cont
.length
; i
=startlen
==cont
.length
? i
+1 : 0){
23397 domConstruct
.place( cont
[i
], node
, "last");
23400 // pass nodes, documentFragments and unknowns through to dojo.place
23401 domConstruct
.place(cont
, node
, "last");
23409 // we wrap up the content-setting operation in a object
23410 html
._ContentSetter
= declare("dojo.html._ContentSetter", null,
23412 // node: DomNode|String
23413 // An node which will be the parent element that we set content into
23416 // content: String|DomNode|DomNode[]
23417 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
23421 // Usually only used internally, and auto-generated with each instance
23424 // cleanContent: Boolean
23425 // Should the content be treated as a full html document,
23426 // and the real content stripped of <html>, <body> wrapper before injection
23427 cleanContent
: false,
23429 // extractContent: Boolean
23430 // Should the content be treated as a full html document,
23431 // and the real content stripped of `<html> <body>` wrapper before injection
23432 extractContent
: false,
23434 // parseContent: Boolean
23435 // Should the node by passed to the parser after the new content is set
23436 parseContent
: false,
23438 // parserScope: String
23439 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
23440 // will search for data-dojo-type (or dojoType). For backwards compatibility
23441 // reasons defaults to dojo._scopeName (which is "dojo" except when
23442 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
23443 parserScope
: kernel
._scopeName
,
23445 // startup: Boolean
23446 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
23449 // lifecycle methods
23450 constructor: function(/*Object*/ params
, /*String|DomNode*/ node
){
23452 // Provides a configurable, extensible object to wrap the setting on content on a node
23453 // call the set() method to actually set the content..
23455 // the original params are mixed directly into the instance "this"
23456 lang
.mixin(this, params
|| {});
23458 // give precedence to params.node vs. the node argument
23459 // and ensure its a node, not an id string
23460 node
= this.node
= dom
.byId( this.node
|| node
);
23465 (node
) ? node
.id
|| node
.tagName
: "",
23470 set: function(/* String|DomNode|NodeList? */ cont
, /*Object?*/ params
){
23472 // front-end to the set-content sequence
23474 // An html string, node or enumerable list of nodes for insertion into the dom
23475 // If not provided, the object's content property will be used
23476 if(undefined !== cont
){
23477 this.content
= cont
;
23479 // in the re-use scenario, set needs to be able to mixin new configuration
23481 this._mixin(params
);
23487 var ret
= this.onEnd();
23489 if(ret
&& ret
.then
){
23490 // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
23493 // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
23494 // return a Deferred like above.
23499 setContent: function(){
23501 // sets the content on the node
23503 var node
= this.node
;
23506 throw new Error(this.declaredClass
+ ": setContent given no node");
23509 node
= html
._setNodeContent(node
, this.content
);
23511 // check if a domfault occurs when we are appending this.errorMessage
23512 // like for instance if domNode is a UL and we try append a DIV
23514 // FIXME: need to allow the user to provide a content error message string
23515 var errMess
= this.onContentError(e
);
23517 node
.innerHTML
= errMess
;
23519 console
.error('Fatal ' + this.declaredClass
+ '.setContent could not change content due to '+e
.message
, e
);
23522 // always put back the node for the next method
23523 this.node
= node
; // DomNode
23528 // cleanly empty out existing content
23530 // If there is a parse in progress, cancel it.
23531 if(this.parseDeferred
){
23532 if(!this.parseDeferred
.isResolved()){
23533 this.parseDeferred
.cancel();
23535 delete this.parseDeferred
;
23538 // destroy any widgets from a previous run
23539 // NOTE: if you don't want this you'll need to empty
23540 // the parseResults array property yourself to avoid bad things happening
23541 if(this.parseResults
&& this.parseResults
.length
){
23542 darray
.forEach(this.parseResults
, function(w
){
23547 delete this.parseResults
;
23549 // this is fast, but if you know its already empty or safe, you could
23550 // override empty to skip this step
23551 domConstruct
.empty(this.node
);
23554 onBegin: function(){
23556 // Called after instantiation, but before set();
23557 // It allows modification of any of the object properties -
23558 // including the node and content provided - before the set operation actually takes place
23559 // This default implementation checks for cleanContent and extractContent flags to
23560 // optionally pre-process html string content
23561 var cont
= this.content
;
23563 if(lang
.isString(cont
)){
23564 if(this.cleanContent
){
23565 cont
= html
._secureForInnerHtml(cont
);
23568 if(this.extractContent
){
23569 var match
= cont
.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
23570 if(match
){ cont
= match
[1]; }
23574 // clean out the node and any cruft associated with it - like widgets
23577 this.content
= cont
;
23578 return this.node
; // DomNode
23583 // Called after set(), when the new content has been pushed into the node
23584 // It provides an opportunity for post-processing before handing back the node to the caller
23585 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
23586 if(this.parseContent
){
23587 // populates this.parseResults and this.parseDeferred if you need those..
23590 return this.node
; // DomNode
23591 // TODO: for 2.0 return a Promise indicating that the parse completed.
23594 tearDown: function(){
23596 // manually reset the Setter instance if its being re-used for example for another set()
23598 // tearDown() is not called automatically.
23599 // In normal use, the Setter instance properties are simply allowed to fall out of scope
23600 // but the tearDown method can be called to explicitly reset this instance.
23601 delete this.parseResults
;
23602 delete this.parseDeferred
;
23604 delete this.content
;
23607 onContentError: function(err
){
23608 return "Error occurred setting content: " + err
;
23611 onExecError: function(err
){
23612 return "Error occurred executing scripts: " + err
;
23615 _mixin: function(params
){
23616 // mix properties/methods into the instance
23617 // TODO: the intention with tearDown is to put the Setter's state
23618 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
23619 // so we could do something here to move the original properties aside for later restoration
23620 var empty
= {}, key
;
23621 for(key
in params
){
23622 if(key
in empty
){ continue; }
23623 // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
23624 // .. but history shows we'll almost always guess wrong
23625 this[key
] = params
[key
];
23628 _parse: function(){
23630 // runs the dojo parser over the node contents, storing any results in this.parseResults
23631 // and the parse promise in this.parseDeferred
23632 // Any errors resulting from parsing are passed to _onError for handling
23634 var rootNode
= this.node
;
23636 // store the results (widgets, whatever) for potential retrieval
23637 var inherited
= {};
23638 darray
.forEach(["dir", "lang", "textDir"], function(name
){
23640 inherited
[name
] = this[name
];
23644 this.parseDeferred
= parser
.parse({
23645 rootNode
: rootNode
,
23646 noStart
: !this.startup
,
23647 inherited
: inherited
,
23648 scope
: this.parserScope
23649 }).then(function(results
){
23650 return self
.parseResults
= results
;
23653 this._onError('Content', e
, "Error parsing in _ContentSetter#"+this.id
);
23657 _onError: function(type
, err
, consoleText
){
23659 // shows user the string that is returned by on[type]Error
23660 // override/implement on[type]Error and return your own string to customize
23661 var errText
= this['on' + type
+ 'Error'].call(this, err
);
23663 console
.error(consoleText
, err
);
23664 }else if(errText
){ // a empty string won't change current content
23665 html
._setNodeContent(this.node
, errText
, true);
23668 }); // end declare()
23670 html
.set = function(/*DomNode*/ node
, /*String|DomNode|NodeList*/ cont
, /*Object?*/ params
){
23672 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
23673 // may be a better choice for simple HTML insertion.
23675 // Unless you need to use the params capabilities of this method, you should use
23676 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
23677 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
23678 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
23679 // or the other capabilities as defined by the params object for this method.
23681 // the parent element that will receive the content
23683 // the content to be set on the parent element.
23684 // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
23686 // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
23688 // A safe string/node/nodelist content replacement/injection with hooks for extension
23690 // | html.set(node, "some string");
23691 // | html.set(node, contentNode, {options});
23692 // | html.set(node, myNode.childNodes, {options});
23693 if(undefined == cont
){
23694 console
.warn("dojo.html.set: no cont argument provided, using empty string");
23699 return html
._setNodeContent(node
, cont
, true);
23701 // more options but slower
23702 // note the arguments are reversed in order, to match the convention for instantiation via the parser
23703 var op
= new html
._ContentSetter(lang
.mixin(
23705 { content
: cont
, node
: node
}
23715 'dijit/_PaletteMixin':function(){
23716 define("dijit/_PaletteMixin", [
23717 "dojo/_base/declare", // declare
23718 "dojo/dom-attr", // domAttr.set
23719 "dojo/dom-class", // domClass.add domClass.remove
23720 "dojo/dom-construct", // domConstruct.create domConstruct.place
23721 "dojo/_base/event", // event.stop
23722 "dojo/keys", // keys
23723 "dojo/_base/lang", // lang.getObject
23724 "./_CssStateMixin",
23727 ], function(declare
, domAttr
, domClass
, domConstruct
, event
, keys
, lang
, _CssStateMixin
, focus
, typematic
){
23730 // dijit/_PaletteMixin
23732 return declare("dijit._PaletteMixin", [_CssStateMixin
], {
23734 // A keyboard accessible palette, for picking a color/emoticon/etc.
23736 // A mixin for a grid showing various entities, so the user can pick a certain entity.
23738 // defaultTimeout: Number
23739 // Number of milliseconds before a held key or button becomes typematic
23740 defaultTimeout
: 500,
23742 // timeoutChangeRate: Number
23743 // Fraction of time used to change the typematic timer between events
23744 // 1.0 means that each typematic event fires at defaultTimeout intervals
23745 // Less than 1.0 means that each typematic event fires at an increasing faster rate
23746 timeoutChangeRate
: 0.90,
23749 // Currently selected color/emoticon/etc.
23752 // _selectedCell: [private] Integer
23753 // Index of the currently selected cell. Initially, none selected
23757 // _currentFocus: [private] DomNode
23758 // The currently focused cell (if the palette itself has focus), or otherwise
23759 // the cell to be focused when the palette itself gets focus.
23760 // Different from value, which represents the selected (i.e. clicked) cell.
23761 _currentFocus: null,
23765 // _xDim: [protected] Integer
23766 // This is the number of cells horizontally across.
23771 // _yDim: [protected] Integer
23772 // This is the number of cells vertically down.
23776 // tabIndex: String
23777 // Widget tab index.
23780 // cellClass: [protected] String
23781 // CSS class applied to each cell in the palette
23782 cellClass
: "dijitPaletteCell",
23784 // dyeClass: [protected] Constructor
23785 // Constructor for Object created for each cell of the palette.
23786 // dyeClass should implements dijit.Dye interface
23790 // Localized summary for the palette table
23792 _setSummaryAttr
: "paletteTableNode",
23794 _dyeFactory: function(value
/*===== , row, col, title =====*/){
23796 // Return instance of dijit.Dye for specified cell of palette
23800 // Remove string support for 2.0
23801 var dyeClassObj
= typeof this.dyeClass
== "string" ? lang
.getObject(this.dyeClass
) : this.dyeClass
;
23802 return new dyeClassObj(value
);
23805 _preparePalette: function(choices
, titles
) {
23807 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
23809 // choices: String[][]
23810 // id's for each cell of the palette, used to create Dye JS object for each cell
23811 // titles: String[]
23812 // Localized tooltip for each cell
23815 var url
= this._blankGif
;
23817 this.connect(this.gridNode
, "ondijitclick", "_onCellClick");
23819 for(var row
=0; row
< choices
.length
; row
++){
23820 var rowNode
= domConstruct
.create("tr", {tabIndex
: "-1"}, this.gridNode
);
23821 for(var col
=0; col
< choices
[row
].length
; col
++){
23822 var value
= choices
[row
][col
];
23824 var cellObject
= this._dyeFactory(value
, row
, col
, titles
[value
]);
23826 var cellNode
= domConstruct
.create("td", {
23827 "class": this.cellClass
,
23829 title
: titles
[value
],
23833 // prepare cell inner structure
23834 cellObject
.fillCell(cellNode
, url
);
23836 cellNode
.idx
= this._cells
.length
;
23838 // save cell info into _cells
23839 this._cells
.push({node
:cellNode
, dye
:cellObject
});
23843 this._xDim
= choices
[0].length
;
23844 this._yDim
= choices
.length
;
23846 // Now set all events
23847 // The palette itself is navigated to with the tab key on the keyboard
23848 // Keyboard navigation within the Palette is with the arrow keys
23849 // Spacebar selects the cell.
23850 // For the up key the index is changed by negative the x dimension.
23852 var keyIncrementMap
= {
23853 UP_ARROW
: -this._xDim
,
23854 // The down key the index is increase by the x dimension.
23855 DOWN_ARROW
: this._xDim
,
23856 // Right and left move the index by 1.
23857 RIGHT_ARROW
: this.isLeftToRight() ? 1 : -1,
23858 LEFT_ARROW
: this.isLeftToRight() ? -1 : 1
23860 for(var key
in keyIncrementMap
){
23862 typematic
.addKeyListener(
23864 {charOrCode
:keys
[key
], ctrlKey
:false, altKey
:false, shiftKey
:false},
23867 var increment
= keyIncrementMap
[key
];
23868 return function(count
){ this._navigateByKey(increment
, count
); };
23870 this.timeoutChangeRate
,
23871 this.defaultTimeout
23877 postCreate: function(){
23878 this.inherited(arguments
);
23880 // Set initial navigable node.
23881 this._setCurrent(this._cells
[0].node
);
23886 // Focus this widget. Puts focus on the most recently focused cell.
23888 // The cell already has tabIndex set, just need to set CSS and focus it
23889 focus
.focus(this._currentFocus
);
23892 _onCellClick: function(/*Event*/ evt
){
23894 // Handler for click, enter key & space key. Selects the cell.
23900 var target
= evt
.target
;
23902 // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
23903 while(target
.tagName
!= "TD"){
23904 if(!target
.parentNode
|| target
== this.gridNode
){ // probably can never happen, but just in case
23907 target
= target
.parentNode
;
23910 var value
= this._getDye(target
).getValue();
23912 // First focus the clicked cell, and then send onChange() notification.
23913 // onChange() (via _setValueAttr) must be after the focus call, because
23914 // it may trigger a refocus to somewhere else (like the Editor content area), and that
23915 // second focus should win.
23916 this._setCurrent(target
);
23917 focus
.focus(target
);
23918 this._setValueAttr(value
, true);
23923 _setCurrent: function(/*DomNode*/ node
){
23925 // Sets which node is the focused cell.
23927 // At any point in time there's exactly one
23928 // cell with tabIndex != -1. If focus is inside the palette then
23929 // focus is on that cell.
23931 // After calling this method, arrow key handlers and mouse click handlers
23932 // should focus the cell in a setTimeout().
23935 if("_currentFocus" in this){
23936 // Remove tabIndex on old cell
23937 domAttr
.set(this._currentFocus
, "tabIndex", "-1");
23940 // Set tabIndex of new cell
23941 this._currentFocus
= node
;
23943 domAttr
.set(node
, "tabIndex", this.tabIndex
);
23947 _setValueAttr: function(value
, priorityChange
){
23949 // This selects a cell. It triggers the onChange event.
23951 // Value of the cell to select
23954 // priorityChange: Boolean?
23955 // Optional parameter used to tell the select whether or not to fire
23958 // clear old selected cell
23959 if(this._selectedCell
>= 0){
23960 domClass
.remove(this._cells
[this._selectedCell
].node
, this.cellClass
+ "Selected");
23962 this._selectedCell
= -1;
23964 // search for cell matching specified value
23966 for(var i
= 0; i
< this._cells
.length
; i
++){
23967 if(value
== this._cells
[i
].dye
.getValue()){
23968 this._selectedCell
= i
;
23969 domClass
.add(this._cells
[i
].node
, this.cellClass
+ "Selected");
23975 // record new value, or null if no matching cell
23976 this._set("value", this._selectedCell
>= 0 ? value
: null);
23978 if(priorityChange
|| priorityChange
=== undefined){
23979 this.onChange(value
);
23983 onChange: function(/*===== value =====*/){
23985 // Callback when a cell is selected.
23987 // Value corresponding to cell.
23990 _navigateByKey: function(increment
, typeCount
){
23992 // This is the callback for typematic.
23993 // It changes the focus and the highlighed cell.
23995 // How much the key is navigated.
23997 // How many times typematic has fired.
24001 // typecount == -1 means the key is released.
24002 if(typeCount
== -1){ return; }
24004 var newFocusIndex
= this._currentFocus
.idx
+ increment
;
24005 if(newFocusIndex
< this._cells
.length
&& newFocusIndex
> -1){
24006 var focusNode
= this._cells
[newFocusIndex
].node
;
24007 this._setCurrent(focusNode
);
24009 // Actually focus the node, for the benefit of screen readers.
24010 // Use defer because IE doesn't like changing focus inside of an event handler
24011 this.defer(lang
.hitch(focus
, "focus", focusNode
));
24015 _getDye: function(/*DomNode*/ cell
){
24017 // Get JS object for given cell DOMNode
24019 return this._cells
[cell
.idx
].dye
;
24024 declare("dijit.Dye",
24028 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
24030 constructor: function(alias, row, col){
24032 // Initialize according to value or alias like "white"
24036 getValue: function(){
24038 // Return "value" of cell; meaning of "value" varies by subclass.
24040 // For example color hex value, emoticon ascii value etc, entity hex value.
24043 fillCell: function(cell, blankGif){
24045 // Add cell DOMNode inner structure
24047 // The surrounding cell
24048 // blankGif: String
24049 // URL for blank cell image
24058 'dijit/form/ValidationTextBox':function(){
24060 'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
24061 define("dijit/form/ValidationTextBox", [
24062 "dojo/_base/declare", // declare
24063 "dojo/_base/kernel", // kernel.deprecated
24064 "dojo/i18n", // i18n.getLocalization
24067 "dojo/text!./templates/ValidationTextBox.html",
24068 "dojo/i18n!./nls/validate"
24069 ], function(declare
, kernel
, i18n
, TextBox
, Tooltip
, template
){
24072 // dijit/form/ValidationTextBox
24076 var __Constraints = {
24078 // locale used for validation, picks up value from this widget's lang attribute
24079 // _flags_: anything
24080 // various flags passed to pattern function
24084 var ValidationTextBox
;
24085 return ValidationTextBox
= declare("dijit.form.ValidationTextBox", TextBox
, {
24087 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
24089 templateString
: template
,
24091 // required: Boolean
24092 // User is required to enter data into this field.
24095 // promptMessage: String
24096 // If defined, display this hint string immediately on focus to the textbox, if empty.
24097 // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
24098 // Think of this like a tooltip that tells the user what to do, not an error message
24099 // that tells the user what they've done wrong.
24101 // Message disappears when user starts typing.
24104 // invalidMessage: String
24105 // The message to display if value is invalid.
24106 // The translated string value is read from the message file by default.
24107 // Set to "" to use the promptMessage instead.
24108 invalidMessage
: "$_unset_$",
24110 // missingMessage: String
24111 // The message to display if value is empty and the field is required.
24112 // The translated string value is read from the message file by default.
24113 // Set to "" to use the invalidMessage instead.
24114 missingMessage
: "$_unset_$",
24117 // Currently error/prompt message.
24118 // When using the default tooltip implementation, this will only be
24119 // displayed when the field is focused.
24122 // constraints: __Constraints
24123 // user-defined object needed to pass parameters to the validator functions
24126 // pattern: [extension protected] String|Function(constraints) returning a string.
24127 // This defines the regular expression used to validate the input.
24128 // Do not add leading ^ or $ characters since the widget adds these.
24129 // A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
24130 // set('pattern', String|Function).
24133 // regExp: Deprecated [extension protected] String. Use "pattern" instead.
24136 regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
24138 // Deprecated. Use set('pattern', Function) instead.
24141 // state: [readonly] String
24142 // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
24145 // tooltipPosition: String[]
24146 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
24147 tooltipPosition
: [],
24149 _deprecateRegExp: function(attr
, value
){
24150 if(value
!= ValidationTextBox
.prototype[attr
]){
24151 kernel
.deprecated("ValidationTextBox id="+this.id
+", set('" + attr
+ "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0");
24152 this.set('pattern', value
);
24155 _setRegExpGenAttr: function(/*Function*/ newFcn
){
24156 this._deprecateRegExp("regExpGen", newFcn
);
24157 this.regExpGen
= this._getPatternAttr
; // backward compat with this.regExpGen(this.constraints)
24159 _setRegExpAttr: function(/*String*/ value
){
24160 this._deprecateRegExp("regExp", value
);
24163 _setValueAttr: function(){
24165 // Hook so set('value', ...) works.
24166 this.inherited(arguments
);
24167 this.validate(this.focused
);
24170 validator: function(/*anything*/ value
, /*__Constraints*/ constraints
){
24172 // Overridable function used to validate the text input against the regular expression.
24175 return (new RegExp("^(?:" + this._getPatternAttr(constraints
) + ")"+(this.required
?"":"?")+"$")).test(value
) &&
24176 (!this.required
|| !this._isEmpty(value
)) &&
24177 (this._isEmpty(value
) || this.parse(value
, constraints
) !== undefined); // Boolean
24180 _isValidSubset: function(){
24182 // Returns true if the value is either already valid or could be made valid by appending characters.
24183 // This is used for validation while the user [may be] still typing.
24184 return this.textbox
.value
.search(this._partialre
) == 0;
24187 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
24189 // Tests if value is valid.
24190 // Can override with your own routine in a subclass.
24193 return this.validator(this.textbox
.value
, this.constraints
);
24196 _isEmpty: function(value
){
24198 // Checks for whitespace
24199 return (this.trim
? /^\s*$/ : /^$/).test(value
); // Boolean
24202 getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24204 // Return an error message to show if appropriate
24207 var invalid
= this.invalidMessage
== "$_unset_$" ? this.messages
.invalidMessage
:
24208 !this.invalidMessage
? this.promptMessage
: this.invalidMessage
;
24209 var missing
= this.missingMessage
== "$_unset_$" ? this.messages
.missingMessage
:
24210 !this.missingMessage
? invalid
: this.missingMessage
;
24211 return (this.required
&& this._isEmpty(this.textbox
.value
)) ? missing
: invalid
; // String
24214 getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
24216 // Return a hint message to show when widget is first focused
24219 return this.promptMessage
; // String
24222 _maskValidSubsetError
: true,
24223 validate: function(/*Boolean*/ isFocused
){
24225 // Called by oninit, onblur, and onkeypress.
24227 // Show missing or invalid messages if appropriate, and highlight textbox field.
24231 var isValid
= this.disabled
|| this.isValid(isFocused
);
24232 if(isValid
){ this._maskValidSubsetError
= true; }
24233 var isEmpty
= this._isEmpty(this.textbox
.value
);
24234 var isValidSubset
= !isValid
&& isFocused
&& this._isValidSubset();
24235 this._set("state", isValid
? "" : (((((!this._hasBeenBlurred
|| isFocused
) && isEmpty
) || isValidSubset
) && (this._maskValidSubsetError
|| (isValidSubset
&& !this._hasBeenBlurred
&& isFocused
))) ? "Incomplete" : "Error"));
24236 this.focusNode
.setAttribute("aria-invalid", isValid
? "false" : "true");
24238 if(this.state
== "Error"){
24239 this._maskValidSubsetError
= isFocused
&& isValidSubset
; // we want the error to show up after a blur and refocus
24240 message
= this.getErrorMessage(isFocused
);
24241 }else if(this.state
== "Incomplete"){
24242 message
= this.getPromptMessage(isFocused
); // show the prompt whenever the value is not yet complete
24243 this._maskValidSubsetError
= !this._hasBeenBlurred
|| isFocused
; // no Incomplete warnings while focused
24245 message
= this.getPromptMessage(isFocused
); // show the prompt whenever there's no error and no text
24247 this.set("message", message
);
24252 displayMessage: function(/*String*/ message
){
24254 // Overridable method to display validation errors/hints.
24255 // By default uses a tooltip.
24258 if(message
&& this.focused
){
24259 Tooltip
.show(message
, this.domNode
, this.tooltipPosition
, !this.isLeftToRight());
24261 Tooltip
.hide(this.domNode
);
24265 _refreshState: function(){
24266 // Overrides TextBox._refreshState()
24268 this.validate(this.focused
);
24270 this.inherited(arguments
);
24273 //////////// INITIALIZATION METHODS ///////////////////////////////////////
24275 constructor: function(params
/*===== , srcNodeRef =====*/){
24277 // Create the widget.
24278 // params: Object|null
24279 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
24280 // and functions, typically callbacks like onClick.
24281 // The hash can contain any of the widget's properties, excluding read-only properties.
24282 // srcNodeRef: DOMNode|String?
24283 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
24285 this.constraints
= {};
24286 this.baseClass
+= ' dijitValidationTextBox';
24289 startup: function(){
24290 this.inherited(arguments
);
24291 this._refreshState(); // after all _set* methods have run
24294 _setConstraintsAttr: function(/*__Constraints*/ constraints
){
24295 if(!constraints
.locale
&& this.lang
){
24296 constraints
.locale
= this.lang
;
24298 this._set("constraints", constraints
);
24299 this._refreshState();
24302 _setPatternAttr: function(/*String|Function*/ pattern
){
24303 this._set("pattern", pattern
); // don't set on INPUT to avoid native HTML5 validation
24306 _getPatternAttr: function(/*__Constraints*/ constraints
){
24308 // Hook to get the current regExp and to compute the partial validation RE.
24309 var p
= this.pattern
;
24310 var type
= (typeof p
).toLowerCase();
24311 if(type
== "function"){
24312 p
= this.pattern(constraints
|| this.constraints
);
24314 if(p
!= this._lastRegExp
){
24315 var partialre
= "";
24316 this._lastRegExp
= p
;
24317 // parse the regexp and produce a new regexp that matches valid subsets
24318 // if the regexp is .* then there's no use in matching subsets since everything is valid
24320 p
.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
24322 switch(re
.charAt(0)){
24334 partialre
+= "|$)";
24337 partialre
+= "(?:"+re
+"|$)";
24342 try{ // this is needed for now since the above regexp parsing needs more test verification
24343 "".search(partialre
);
24344 }catch(e
){ // should never be here unless the original RE is bad or the parsing is bad
24345 partialre
= this.pattern
;
24346 console
.warn('RegExp error in ' + this.declaredClass
+ ': ' + this.pattern
);
24347 } // should never be here unless the original RE is bad or the parsing is bad
24348 this._partialre
= "^(?:" + partialre
+ ")$";
24353 postMixInProperties: function(){
24354 this.inherited(arguments
);
24355 this.messages
= i18n
.getLocalization("dijit.form", "validate", this.lang
);
24356 this._setConstraintsAttr(this.constraints
); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
24359 _setDisabledAttr: function(/*Boolean*/ value
){
24360 this.inherited(arguments
); // call FormValueWidget._setDisabledAttr()
24361 this._refreshState();
24364 _setRequiredAttr: function(/*Boolean*/ value
){
24365 this._set("required", value
);
24366 this.focusNode
.setAttribute("aria-required", value
);
24367 this._refreshState();
24370 _setMessageAttr: function(/*String*/ message
){
24371 this._set("message", message
);
24372 this.displayMessage(message
);
24376 // Overrides dijit/form/TextBox.reset() by also
24377 // hiding errors about partial matches
24378 this._maskValidSubsetError
= true;
24379 this.inherited(arguments
);
24382 _onBlur: function(){
24383 // the message still exists but for back-compat, and to erase the tooltip
24384 // (if the message is being displayed as a tooltip), call displayMessage('')
24385 this.displayMessage('');
24387 this.inherited(arguments
);
24393 'dijit/_base/typematic':function(){
24394 define("dijit/_base/typematic", ["../typematic"], function(){
24399 // Deprecated, for back-compat, just loads top level module
24406 'dijit/layout/BorderContainer':function(){
24407 define("dijit/layout/BorderContainer", [
24408 "dojo/_base/array", // array.filter array.forEach array.map
24409 "dojo/cookie", // cookie
24410 "dojo/_base/declare", // declare
24411 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
24412 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
24413 "dojo/dom-geometry", // domGeometry.marginBox
24414 "dojo/dom-style", // domStyle.style
24415 "dojo/_base/event", // event.stop
24417 "dojo/_base/lang", // lang.getObject lang.hitch
24422 "../_TemplatedMixin",
24424 "./utils" // layoutUtils.layoutChildren
24425 ], function(array
, cookie
, declare
, domClass
, domConstruct
, domGeometry
, domStyle
, event
, keys
, lang
, on
, touch
,
24426 _WidgetBase
, _Widget
, _TemplatedMixin
, _LayoutWidget
, layoutUtils
){
24429 // dijit/layout/BorderContainer
24431 var _Splitter
= declare("dijit.layout._Splitter", [_Widget
, _TemplatedMixin
],
24434 // A draggable spacer between two items in a `dijit/layout/BorderContainer`.
24436 // This is instantiated by `dijit/layout/BorderContainer`. Users should not
24437 // create it directly.
24442 // container: [const] dijit/layout/BorderContainer
24443 // Pointer to the parent BorderContainer
24446 // child: [const] dijit/layout/_LayoutWidget
24447 // Pointer to the pane associated with this splitter
24450 // region: [const] String
24451 // Region of pane associated with this splitter.
24452 // "top", "bottom", "left", "right".
24456 // live: [const] Boolean
24457 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
24458 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
24461 templateString
: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
24463 constructor: function(){
24464 this._handlers
= [];
24467 postMixInProperties: function(){
24468 this.inherited(arguments
);
24470 this.horizontal
= /top|bottom/.test(this.region
);
24471 this._factor
= /top|left/.test(this.region
) ? 1 : -1;
24472 this._cookieName
= this.container
.id
+ "_" + this.region
;
24475 buildRendering: function(){
24476 this.inherited(arguments
);
24478 domClass
.add(this.domNode
, "dijitSplitter" + (this.horizontal
? "H" : "V"));
24480 if(this.container
.persist
){
24481 // restore old size
24482 var persistSize
= cookie(this._cookieName
);
24484 this.child
.domNode
.style
[this.horizontal
? "height" : "width"] = persistSize
;
24489 _computeMaxSize: function(){
24491 // Return the maximum size that my corresponding pane can be set to
24493 var dim
= this.horizontal
? 'h' : 'w',
24494 childSize
= domGeometry
.getMarginBox(this.child
.domNode
)[dim
],
24495 center
= array
.filter(this.container
.getChildren(), function(child
){ return child
.region
== "center";})[0],
24496 spaceAvailable
= domGeometry
.getMarginBox(center
.domNode
)[dim
]; // can expand until center is crushed to 0
24498 return Math
.min(this.child
.maxSize
, childSize
+ spaceAvailable
);
24501 _startDrag: function(e
){
24503 this.cover
= domConstruct
.place("<div class=dijitSplitterCover></div>", this.child
.domNode
, "after");
24505 domClass
.add(this.cover
, "dijitSplitterCoverActive");
24507 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
24508 if(this.fake
){ domConstruct
.destroy(this.fake
); }
24509 if(!(this._resize
= this.live
)){ //TODO: disable live for IE6?
24510 // create fake splitter to display at old position while we drag
24511 (this.fake
= this.domNode
.cloneNode(true)).removeAttribute("id");
24512 domClass
.add(this.domNode
, "dijitSplitterShadow");
24513 domConstruct
.place(this.fake
, this.domNode
, "after");
24515 domClass
.add(this.domNode
, "dijitSplitterActive dijitSplitter" + (this.horizontal
? "H" : "V") + "Active");
24517 domClass
.remove(this.fake
, "dijitSplitterHover dijitSplitter" + (this.horizontal
? "H" : "V") + "Hover");
24520 //Performance: load data info local vars for onmousevent function closure
24521 var factor
= this._factor
,
24522 isHorizontal
= this.horizontal
,
24523 axis
= isHorizontal
? "pageY" : "pageX",
24524 pageStart
= e
[axis
],
24525 splitterStyle
= this.domNode
.style
,
24526 dim
= isHorizontal
? 'h' : 'w',
24527 childStart
= domGeometry
.getMarginBox(this.child
.domNode
)[dim
],
24528 max
= this._computeMaxSize(),
24529 min
= this.child
.minSize
|| 20,
24530 region
= this.region
,
24531 splitterAttr
= region
== "top" || region
== "bottom" ? "top" : "left", // style attribute of splitter to adjust
24532 splitterStart
= parseInt(splitterStyle
[splitterAttr
], 10),
24533 resize
= this._resize
,
24534 layoutFunc
= lang
.hitch(this.container
, "_layoutChildren", this.child
.id
),
24535 de
= this.ownerDocument
;
24537 this._handlers
= this._handlers
.concat([
24538 on(de
, touch
.move, this._drag = function(e
, forceResize
){
24539 var delta
= e
[axis
] - pageStart
,
24540 childSize
= factor
* delta
+ childStart
,
24541 boundChildSize
= Math
.max(Math
.min(childSize
, max
), min
);
24543 if(resize
|| forceResize
){
24544 layoutFunc(boundChildSize
);
24546 // TODO: setting style directly (usually) sets content box size, need to set margin box size
24547 splitterStyle
[splitterAttr
] = delta
+ splitterStart
+ factor
*(boundChildSize
- childSize
) + "px";
24549 on(de
, "dragstart", event
.stop
),
24550 on(this.ownerDocumentBody
, "selectstart", event
.stop
),
24551 on(de
, touch
.release
, lang
.hitch(this, "_stopDrag"))
24556 _onMouse: function(e
){
24558 // Handler for onmouseenter / onmouseleave events
24559 var o
= (e
.type
== "mouseover" || e
.type
== "mouseenter");
24560 domClass
.toggle(this.domNode
, "dijitSplitterHover", o
);
24561 domClass
.toggle(this.domNode
, "dijitSplitter" + (this.horizontal
? "H" : "V") + "Hover", o
);
24564 _stopDrag: function(e
){
24567 domClass
.remove(this.cover
, "dijitSplitterCoverActive");
24569 if(this.fake
){ domConstruct
.destroy(this.fake
); }
24570 domClass
.remove(this.domNode
, "dijitSplitterActive dijitSplitter"
24571 + (this.horizontal
? "H" : "V") + "Active dijitSplitterShadow");
24572 this._drag(e
); //TODO: redundant with onmousemove?
24573 this._drag(e
, true);
24575 this._cleanupHandlers();
24579 if(this.container
.persist
){
24580 cookie(this._cookieName
, this.child
.domNode
.style
[this.horizontal
? "height" : "width"], {expires
:365});
24584 _cleanupHandlers: function(){
24586 while(h
= this._handlers
.pop()){ h
.remove(); }
24589 _onKeyPress: function(/*Event*/ e
){
24590 // should we apply typematic to this?
24591 this._resize
= true;
24592 var horizontal
= this.horizontal
;
24594 switch(e
.charOrCode
){
24595 case horizontal
? keys
.UP_ARROW
: keys
.LEFT_ARROW
:
24598 case horizontal
? keys
.DOWN_ARROW
: keys
.RIGHT_ARROW
:
24601 // this.inherited(arguments);
24604 var childSize
= domGeometry
.getMarginSize(this.child
.domNode
)[ horizontal
? 'h' : 'w' ] + this._factor
* tick
;
24605 this.container
._layoutChildren(this.child
.id
, Math
.max(Math
.min(childSize
, this._computeMaxSize()), this.child
.minSize
));
24609 destroy: function(){
24610 this._cleanupHandlers();
24612 delete this.container
;
24615 this.inherited(arguments
);
24619 var _Gutter
= declare("dijit.layout._Gutter", [_Widget
, _TemplatedMixin
],
24622 // Just a spacer div to separate side pane from center pane.
24623 // Basically a trick to lookup the gutter/splitter width from the theme.
24625 // Instantiated by `dijit/layout/BorderContainer`. Users should not
24626 // create directly.
24630 templateString
: '<div class="dijitGutter" role="presentation"></div>',
24632 postMixInProperties: function(){
24633 this.inherited(arguments
);
24634 this.horizontal
= /top|bottom/.test(this.region
);
24637 buildRendering: function(){
24638 this.inherited(arguments
);
24639 domClass
.add(this.domNode
, "dijitGutter" + (this.horizontal
? "H" : "V"));
24643 var BorderContainer
= declare("dijit.layout.BorderContainer", _LayoutWidget
, {
24645 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
24647 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
24648 // that contains a child widget marked region="center" and optionally children widgets marked
24649 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
24650 // Children along the edges will be laid out according to width or height dimensions and may
24651 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
24652 // space is designated for the center region.
24654 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
24655 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
24656 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
24657 // "left" and "right" except that they will be reversed in right-to-left environments.
24659 // For complex layouts, multiple children can be specified for a single region. In this case, the
24660 // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
24661 // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
24662 // instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
24664 // See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on
24665 // children of a `BorderContainer`.
24667 // | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
24668 // | style="width: 400px; height: 300px;">
24669 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div>
24670 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
24671 // | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div>
24675 // Which design is used for the layout:
24677 // - "headline" (default) where the top and bottom extend the full width of the container
24678 // - "sidebar" where the left and right sides extend from top to bottom.
24679 design
: "headline",
24681 // gutters: [const] Boolean
24682 // Give each pane a border and margin.
24683 // Margin determined by domNode.paddingLeft.
24684 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
24687 // liveSplitters: [const] Boolean
24688 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
24689 liveSplitters
: true,
24691 // persist: Boolean
24692 // Save splitter positions in a cookie.
24695 baseClass
: "dijitBorderContainer",
24697 // _splitterClass: Function||String
24698 // Optional hook to override the default Splitter widget used by BorderContainer
24699 _splitterClass
: _Splitter
,
24701 postMixInProperties: function(){
24702 // change class name to indicate that BorderContainer is being used purely for
24703 // layout (like LayoutContainer) rather than for pretty formatting.
24705 this.baseClass
+= "NoGutter";
24707 this.inherited(arguments
);
24710 startup: function(){
24711 if(this._started
){ return; }
24712 array
.forEach(this.getChildren(), this._setupChild
, this);
24713 this.inherited(arguments
);
24716 _setupChild: function(/*dijit/_WidgetBase*/ child){
24717 // Override _LayoutWidget._setupChild().
24719 var region = child.region;
24721 this.inherited(arguments);
24723 domClass.add(child.domNode, this.baseClass+"Pane");
24725 var ltr = this.isLeftToRight();
24726 if(region == "leading"){ region = ltr ? "left" : "right"; }
24727 if(region == "trailing"){ region = ltr ? "right" : "left"; }
24729 // Create draggable splitter for resizing pane,
24730 // or alternately if splitter=false but BorderContainer.gutters=true then
24731 // insert dummy div just for spacing
24732 if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
24733 var _Splitter = child.splitter ? this._splitterClass : _Gutter;
24734 if(lang.isString(_Splitter)){
24735 _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
24737 var splitter = new _Splitter({
24738 id: child.id + "_splitter",
24742 live: this.liveSplitters
24744 splitter.isSplitter = true;
24745 child._splitterWidget = splitter;
24747 domConstruct.place(splitter.domNode, child.domNode, "after");
24749 // Splitters aren't added as Contained children, so we need to call startup explicitly
24750 splitter.startup();
24752 child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
24756 layout: function(){
24757 // Implement _LayoutWidget.layout() virtual method.
24758 this._layoutChildren();
24761 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex
){
24762 // Override _LayoutWidget.addChild().
24763 this.inherited(arguments
);
24765 this.layout(); //OPT
24769 removeChild: function(/*dijit/_WidgetBase*/ child){
24770 // Override _LayoutWidget.removeChild().
24772 var region = child.region;
24773 var splitter = child._splitterWidget;
24775 splitter.destroy();
24776 delete child._splitterWidget;
24778 this.inherited(arguments);
24781 this._layoutChildren();
24783 // Clean up whatever style changes we made to the child pane.
24784 // Unclear how height and width should be handled.
24785 domClass.remove(child.domNode, this.baseClass+"Pane");
24786 domStyle.set(child.domNode, {
24793 domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
24796 getChildren: function(){
24797 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
24798 return array.filter(this.inherited(arguments), function(widget){
24799 return !widget.isSplitter;
24803 // TODO: remove in 2.0
24804 getSplitter: function(/*String*/region
){
24806 // Returns the widget responsible for rendering the splitter associated with region
24809 return array
.filter(this.getChildren(), function(child
){
24810 return child
.region
== region
;
24811 })[0]._splitterWidget
;
24814 resize: function(newSize
, currentSize
){
24815 // Overrides _LayoutWidget.resize().
24817 // resetting potential padding to 0px to provide support for 100% width/height + padding
24818 // TODO: this hack doesn't respect the box model and is a temporary fix
24819 if(!this.cs
|| !this.pe
){
24820 var node
= this.domNode
;
24821 this.cs
= domStyle
.getComputedStyle(node
);
24822 this.pe
= domGeometry
.getPadExtents(node
, this.cs
);
24823 this.pe
.r
= domStyle
.toPixelValue(node
, this.cs
.paddingRight
);
24824 this.pe
.b
= domStyle
.toPixelValue(node
, this.cs
.paddingBottom
);
24826 domStyle
.set(node
, "padding", "0px");
24829 this.inherited(arguments
);
24832 _layoutChildren: function(/*String?*/ changedChildId
, /*Number?*/ changedChildSize
){
24834 // This is the main routine for setting size/position of each child.
24836 // With no arguments, measures the height of top/bottom panes, the width
24837 // of left/right panes, and then sizes all panes accordingly.
24839 // With changedRegion specified (as "left", "top", "bottom", or "right"),
24840 // it changes that region's width/height to changedRegionSize and
24841 // then resizes other regions that were affected.
24843 // Id of the child which should be resized because splitter was dragged.
24844 // changedChildSize:
24845 // The new width/height (in pixels) to make specified child
24847 if(!this._borderBox
|| !this._borderBox
.h
){
24848 // We are currently hidden, or we haven't been sized by our parent yet.
24849 // Abort. Someone will resize us later.
24853 // Generate list of wrappers of my children in the order that I want layoutChildren()
24854 // to process them (i.e. from the outside to the inside)
24855 var wrappers
= array
.map(this.getChildren(), function(child
, idx
){
24859 child
.region
== "center" ? Infinity
: 0,
24860 child
.layoutPriority
,
24861 (this.design
== "sidebar" ? 1 : -1) * (/top|bottom/.test(child
.region
) ? 1 : -1),
24866 wrappers
.sort(function(a
, b
){
24867 var aw
= a
.weight
, bw
= b
.weight
;
24868 for(var i
=0; i
<aw
.length
; i
++){
24869 if(aw
[i
] != bw
[i
]){
24870 return aw
[i
] - bw
[i
];
24876 // Make new list, combining the externally specified children with splitters and gutters
24877 var childrenAndSplitters
= [];
24878 array
.forEach(wrappers
, function(wrapper
){
24879 var pane
= wrapper
.pane
;
24880 childrenAndSplitters
.push(pane
);
24881 if(pane
._splitterWidget
){
24882 childrenAndSplitters
.push(pane
._splitterWidget
);
24886 // Compute the box in which to lay out my children
24890 w
: this._borderBox
.w
- this.pe
.w
,
24891 h
: this._borderBox
.h
- this.pe
.h
24894 // Layout the children, possibly changing size due to a splitter drag
24895 layoutUtils
.layoutChildren(this.domNode
, dim
, childrenAndSplitters
,
24896 changedChildId
, changedChildSize
);
24899 destroyRecursive: function(){
24900 // Destroy splitters first, while getChildren() still works
24901 array
.forEach(this.getChildren(), function(child
){
24902 var splitter
= child
._splitterWidget
;
24904 splitter
.destroy();
24906 delete child
._splitterWidget
;
24909 // Then destroy the real children, and myself
24910 this.inherited(arguments
);
24914 BorderContainer
.ChildWidgetProperties
= {
24916 // These properties can be specified for the children of a BorderContainer.
24918 // region: [const] String
24919 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
24920 // See the `dijit/layout/BorderContainer` description for details.
24923 // layoutPriority: [const] Number
24924 // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
24925 // between children with a lower layoutPriority.
24928 // splitter: [const] Boolean
24929 // Parameter for children where region != "center".
24930 // If true, enables user to resize the widget by putting a draggable splitter between
24931 // this widget and the region=center widget.
24934 // minSize: [const] Number
24935 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
24938 // maxSize: [const] Number
24939 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
24943 // Since any widget can be specified as a LayoutContainer child, mix it
24944 // into the base widget class. (This is a hack, but it's effective.)
24945 // This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
24946 lang
.extend(_WidgetBase
, /*===== {} || =====*/ BorderContainer
.ChildWidgetProperties
);
24948 // For monkey patching
24949 BorderContainer
._Splitter
= _Splitter
;
24950 BorderContainer
._Gutter
= _Gutter
;
24952 return BorderContainer
;
24956 'dijit/_base':function(){
24957 define("dijit/_base", [
24959 "./a11y", // used to be in dijit/_base/manager
24960 "./WidgetSet", // used to be in dijit/_base/manager
24967 "./_base/typematic",
24970 ], function(dijit
){
24978 // Includes all the modules in dijit/_base
24982 return dijit
._base
;
24986 'dojo/window':function(){
24987 define("dojo/window", ["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
24988 function(lang
, has
, baseWindow
, dom
, geom
, style
){
24997 getBox: function(/*Document?*/ doc
){
24999 // Returns the dimensions and scroll position of the viewable area of a browser window
25001 doc
= doc
|| baseWindow
.doc
;
25004 scrollRoot
= (doc
.compatMode
== 'BackCompat') ? baseWindow
.body(doc
) : doc
.documentElement
,
25005 // get scroll position
25006 scroll
= geom
.docScroll(doc
), // scrollRoot.scrollTop/Left should work
25009 if(has("touch")){ // if(scrollbars not supported)
25010 var uiWindow
= window
.get(doc
); // use UI window, not dojo.global window
25011 // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
25012 w
= uiWindow
.innerWidth
|| scrollRoot
.clientWidth
; // || scrollRoot.clientXXX probably never evaluated
25013 h
= uiWindow
.innerHeight
|| scrollRoot
.clientHeight
;
25015 // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
25016 // uiWindow.innerWidth/Height includes the scrollbar and cannot be used
25017 w
= scrollRoot
.clientWidth
;
25018 h
= scrollRoot
.clientHeight
;
25028 get: function(/*Document*/ doc
){
25030 // Get window object associated with document doc.
25032 // The document to get the associated window for.
25034 // In some IE versions (at least 6.0), document.parentWindow does not return a
25035 // reference to the real window object (maybe a copy), so we must fix it as well
25036 // We use IE specific execScript to attach the real window reference to
25037 // document._parentWindow for later use
25038 if(has("ie") && window
!== document
.parentWindow
){
25040 In IE 6, only the variable "window" can be used to connect events (others
25041 may be only copies).
25043 doc
.parentWindow
.execScript("document._parentWindow = window;", "Javascript");
25044 //to prevent memory leak, unset it after use
25045 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
25046 var win
= doc
._parentWindow
;
25047 doc
._parentWindow
= null;
25048 return win
; // Window
25051 return doc
.parentWindow
|| doc
.defaultView
; // Window
25054 scrollIntoView: function(/*DomNode*/ node
, /*Object?*/ pos
){
25056 // Scroll the passed node into view, if it is not already.
25058 // don't rely on node.scrollIntoView working just because the function is there
25060 try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
25061 node
= dom
.byId(node
);
25062 var doc
= node
.ownerDocument
|| baseWindow
.doc
, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined?
25063 body
= baseWindow
.body(doc
),
25064 html
= doc
.documentElement
|| body
.parentNode
,
25065 isIE
= has("ie"), isWK
= has("webkit");
25066 // if an untested browser, then use the native method
25067 if((!(has("mozilla") || isIE
|| isWK
|| has("opera")) || node
== body
|| node
== html
) && (typeof node
.scrollIntoView
!= "undefined")){
25068 node
.scrollIntoView(false); // short-circuit to native if possible
25071 var backCompat
= doc
.compatMode
== 'BackCompat',
25072 clientAreaRoot
= (isIE
>= 9 && "frameElement" in node
.ownerDocument
.parentWindow
)
25073 ? ((html
.clientHeight
> 0 && html
.clientWidth
> 0 && (body
.clientHeight
== 0 || body
.clientWidth
== 0 || body
.clientHeight
> html
.clientHeight
|| body
.clientWidth
> html
.clientWidth
)) ? html
: body
)
25074 : (backCompat
? body
: html
),
25075 scrollRoot
= isWK
? body
: clientAreaRoot
,
25076 rootWidth
= clientAreaRoot
.clientWidth
,
25077 rootHeight
= clientAreaRoot
.clientHeight
,
25078 rtl
= !geom
.isBodyLtr(doc
),
25079 nodePos
= pos
|| geom
.position(node
),
25080 el
= node
.parentNode
,
25081 isFixed = function(el
){
25082 return ((isIE
<= 6 || (isIE
&& backCompat
))? false : (style
.get(el
, 'position').toLowerCase() == "fixed"));
25084 if(isFixed(node
)){ return; } // nothing to do
25087 if(el
== body
){ el
= scrollRoot
; }
25088 var elPos
= geom
.position(el
),
25089 fixedPos
= isFixed(el
);
25091 if(el
== scrollRoot
){
25092 elPos
.w
= rootWidth
; elPos
.h
= rootHeight
;
25093 if(scrollRoot
== html
&& isIE
&& rtl
){ elPos
.x
+= scrollRoot
.offsetWidth
-elPos
.w
; } // IE workaround where scrollbar causes negative x
25094 if(elPos
.x
< 0 || !isIE
){ elPos
.x
= 0; } // IE can have values > 0
25095 if(elPos
.y
< 0 || !isIE
){ elPos
.y
= 0; }
25097 var pb
= geom
.getPadBorderExtents(el
);
25098 elPos
.w
-= pb
.w
; elPos
.h
-= pb
.h
; elPos
.x
+= pb
.l
; elPos
.y
+= pb
.t
;
25099 var clientSize
= el
.clientWidth
,
25100 scrollBarSize
= elPos
.w
- clientSize
;
25101 if(clientSize
> 0 && scrollBarSize
> 0){
25102 elPos
.w
= clientSize
;
25103 elPos
.x
+= (rtl
&& (isIE
|| el
.clientLeft
> pb
.l
/*Chrome*/)) ? scrollBarSize
: 0;
25105 clientSize
= el
.clientHeight
;
25106 scrollBarSize
= elPos
.h
- clientSize
;
25107 if(clientSize
> 0 && scrollBarSize
> 0){
25108 elPos
.h
= clientSize
;
25111 if(fixedPos
){ // bounded by viewport, not parents
25113 elPos
.h
+= elPos
.y
; elPos
.y
= 0;
25116 elPos
.w
+= elPos
.x
; elPos
.x
= 0;
25118 if(elPos
.y
+ elPos
.h
> rootHeight
){
25119 elPos
.h
= rootHeight
- elPos
.y
;
25121 if(elPos
.x
+ elPos
.w
> rootWidth
){
25122 elPos
.w
= rootWidth
- elPos
.x
;
25125 // calculate overflow in all 4 directions
25126 var l
= nodePos
.x
- elPos
.x
, // beyond left: < 0
25127 t
= nodePos
.y
- Math
.max(elPos
.y
, 0), // beyond top: < 0
25128 r
= l
+ nodePos
.w
- elPos
.w
, // beyond right: > 0
25129 bot
= t
+ nodePos
.h
- elPos
.h
; // beyond bottom: > 0
25131 var s
= Math
[l
< 0? "max" : "min"](l
, r
);
25132 if(rtl
&& ((isIE
== 8 && !backCompat
) || isIE
>= 9)){ s
= -s
; }
25133 nodePos
.x
+= el
.scrollLeft
;
25134 el
.scrollLeft
+= s
;
25135 nodePos
.x
-= el
.scrollLeft
;
25138 nodePos
.y
+= el
.scrollTop
;
25139 el
.scrollTop
+= Math
[t
< 0? "max" : "min"](t
, bot
);
25140 nodePos
.y
-= el
.scrollTop
;
25142 el
= (el
!= scrollRoot
) && !fixedPos
&& el
.parentNode
;
25145 console
.error('scrollIntoView: ' + error
);
25146 node
.scrollIntoView(false);
25151 1 && lang
.setObject("dojo.window", window
);
25157 'dojo/number':function(){
25158 define("dojo/number", [/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
25159 function(/*===== declare, =====*/ lang
, i18n
, nlsNumber
, dstring
, dregexp
){
25166 // localized formatting and parsing routines for Number
25168 lang
.setObject("dojo.number", number
);
25171 number.__FormatOptions = declare(null, {
25172 // pattern: String?
25173 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25174 // with this string. Default value is based on locale. Overriding this property will defeat
25175 // localization. Literal characters in patterns are not supported.
25177 // choose a format type based on the locale from the following:
25178 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25180 // fixed number of decimal places to show. This overrides any
25181 // information in the provided pattern.
25183 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25184 // means do not round.
25186 // override the locale used to determine formatting rules
25187 // fractional: Boolean?
25188 // If false, show no decimal places, overriding places and pattern settings.
25192 number
.format = function(/*Number*/ value
, /*number.__FormatOptions?*/ options
){
25194 // Format a Number as a String, using locale-specific settings
25196 // Create a string from a Number using a known localized pattern.
25197 // Formatting patterns appropriate to the locale are chosen from the
25198 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
25200 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
25202 // the number to be formatted
25204 options
= lang
.mixin({}, options
|| {});
25205 var locale
= i18n
.normalizeLocale(options
.locale
),
25206 bundle
= i18n
.getLocalization("dojo.cldr", "number", locale
);
25207 options
.customs
= bundle
;
25208 var pattern
= options
.pattern
|| bundle
[(options
.type
|| "decimal") + "Format"];
25209 if(isNaN(value
) || Math
.abs(value
) == Infinity
){ return null; } // null
25210 return number
._applyPattern(value
, pattern
, options
); // String
25213 //number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
25214 number
._numberPatternRE
= /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
25216 number
._applyPattern = function(/*Number*/ value
, /*String*/ pattern
, /*number.__FormatOptions?*/ options
){
25218 // Apply pattern to format value as a string using options. Gives no
25219 // consideration to local customs.
25221 // the number to be formatted.
25223 // a pattern string as described by
25224 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25225 // options: number.__FormatOptions?
25226 // _applyPattern is usually called via `dojo/number.format()` which
25227 // populates an extra property in the options parameter, "customs".
25228 // The customs object specifies group and decimal parameters if set.
25230 //TODO: support escapes
25231 options
= options
|| {};
25232 var group
= options
.customs
.group
,
25233 decimal = options
.customs
.decimal,
25234 patternList
= pattern
.split(';'),
25235 positivePattern
= patternList
[0];
25236 pattern
= patternList
[(value
< 0) ? 1 : 0] || ("-" + positivePattern
);
25238 //TODO: only test against unescaped
25239 if(pattern
.indexOf('%') != -1){
25241 }else if(pattern
.indexOf('\u2030') != -1){
25242 value
*= 1000; // per mille
25243 }else if(pattern
.indexOf('\u00a4') != -1){
25244 group
= options
.customs
.currencyGroup
|| group
;//mixins instead?
25245 decimal = options
.customs
.currencyDecimal
|| decimal;// Should these be mixins instead?
25246 pattern
= pattern
.replace(/\u00a4{1,3}/, function(match
){
25247 var prop
= ["symbol", "currency", "displayName"][match
.length
-1];
25248 return options
[prop
] || options
.currency
|| "";
25250 }else if(pattern
.indexOf('E') != -1){
25251 throw new Error("exponential notation not supported");
25254 //TODO: support @ sig figs?
25255 var numberPatternRE
= number
._numberPatternRE
;
25256 var numberPattern
= positivePattern
.match(numberPatternRE
);
25257 if(!numberPattern
){
25258 throw new Error("unable to find a number expression in pattern: "+pattern
);
25260 if(options
.fractional
=== false){ options
.places
= 0; }
25261 return pattern
.replace(numberPatternRE
,
25262 number
._formatAbsolute(value
, numberPattern
[0], {decimal: decimal, group
: group
, places
: options
.places
, round
: options
.round
}));
25265 number
.round = function(/*Number*/ value
, /*Number?*/ places
, /*Number?*/ increment
){
25267 // Rounds to the nearest value with the given number of decimal places, away from zero
25269 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
25270 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
25271 // fractional increments also, such as the nearest quarter.
25272 // NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround.
25274 // The number to round
25276 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
25277 // Must be non-negative.
25279 // Rounds next place to nearest value of increment/10. 10 by default.
25281 // | >>> number.round(-0.5)
25283 // | >>> number.round(162.295, 2)
25284 // | 162.29 // note floating point error. Should be 162.3
25285 // | >>> number.round(10.71, 0, 2.5)
25287 var factor
= 10 / (increment
|| 10);
25288 return (factor
* +value
).toFixed(places
) / factor
; // Number
25291 if((0.9).toFixed() == 0){
25292 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
25293 // is just after the rounding place and is >=5
25294 var round
= number
.round
;
25295 number
.round = function(v
, p
, m
){
25296 var d
= Math
.pow(10, -p
|| 0), a
= Math
.abs(v
);
25301 if(a
< 0.5 || a
>= 0.95){
25305 return round(v
, p
, m
) + (v
> 0 ? d
: -d
);
25308 // Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above.
25309 /*===== number.round = round; =====*/
25313 number.__FormatAbsoluteOptions = declare(null, {
25314 // decimal: String?
25315 // the decimal separator
25317 // the group separator
25318 // places: Number|String?
25319 // number of decimal places. the range "n,m" will format to m places.
25321 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
25322 // means don't round.
25326 number
._formatAbsolute = function(/*Number*/ value
, /*String*/ pattern
, /*number.__FormatAbsoluteOptions?*/ options
){
25328 // Apply numeric pattern to absolute value using options. Gives no
25329 // consideration to local customs.
25331 // the number to be formatted, ignores sign
25333 // the number portion of a pattern (e.g. `#,##0.00`)
25334 options
= options
|| {};
25335 if(options
.places
=== true){options
.places
=0;}
25336 if(options
.places
=== Infinity
){options
.places
=6;} // avoid a loop; pick a limit
25338 var patternParts
= pattern
.split("."),
25339 comma
= typeof options
.places
== "string" && options
.places
.indexOf(","),
25340 maxPlaces
= options
.places
;
25342 maxPlaces
= options
.places
.substring(comma
+ 1);
25343 }else if(!(maxPlaces
>= 0)){
25344 maxPlaces
= (patternParts
[1] || []).length
;
25346 if(!(options
.round
< 0)){
25347 value
= number
.round(value
, maxPlaces
, options
.round
);
25350 var valueParts
= String(Math
.abs(value
)).split("."),
25351 fractional
= valueParts
[1] || "";
25352 if(patternParts
[1] || options
.places
){
25354 options
.places
= options
.places
.substring(0, comma
);
25356 // Pad fractional with trailing zeros
25357 var pad
= options
.places
!== undefined ? options
.places
: (patternParts
[1] && patternParts
[1].lastIndexOf("0") + 1);
25358 if(pad
> fractional
.length
){
25359 valueParts
[1] = dstring
.pad(fractional
, pad
, '0', true);
25362 // Truncate fractional
25363 if(maxPlaces
< fractional
.length
){
25364 valueParts
[1] = fractional
.substr(0, maxPlaces
);
25367 if(valueParts
[1]){ valueParts
.pop(); }
25370 // Pad whole with leading zeros
25371 var patternDigits
= patternParts
[0].replace(',', '');
25372 pad
= patternDigits
.indexOf("0");
25374 pad
= patternDigits
.length
- pad
;
25375 if(pad
> valueParts
[0].length
){
25376 valueParts
[0] = dstring
.pad(valueParts
[0], pad
);
25380 if(patternDigits
.indexOf("#") == -1){
25381 valueParts
[0] = valueParts
[0].substr(valueParts
[0].length
- pad
);
25385 // Add group separators
25386 var index
= patternParts
[0].lastIndexOf(','),
25387 groupSize
, groupSize2
;
25389 groupSize
= patternParts
[0].length
- index
- 1;
25390 var remainder
= patternParts
[0].substr(0, index
);
25391 index
= remainder
.lastIndexOf(',');
25393 groupSize2
= remainder
.length
- index
- 1;
25397 for(var whole
= valueParts
[0]; whole
;){
25398 var off
= whole
.length
- groupSize
;
25399 pieces
.push((off
> 0) ? whole
.substr(off
) : whole
);
25400 whole
= (off
> 0) ? whole
.slice(0, off
) : "";
25402 groupSize
= groupSize2
;
25406 valueParts
[0] = pieces
.reverse().join(options
.group
|| ",");
25408 return valueParts
.join(options
.decimal || ".");
25412 number.__RegexpOptions = declare(null, {
25413 // pattern: String?
25414 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25415 // with this string. Default value is based on locale. Overriding this property will defeat
25418 // choose a format type based on the locale from the following:
25419 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25421 // override the locale used to determine formatting rules
25422 // strict: Boolean?
25423 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25424 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25425 // places: Number|String?
25426 // number of decimal places to accept: Infinity, a positive number, or
25427 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
25430 number
.regexp = function(/*number.__RegexpOptions?*/ options
){
25432 // Builds the regular needed to parse a number
25434 // Returns regular expression with positive and negative match, group
25435 // and decimal separators
25436 return number
._parseInfo(options
).regexp
; // String
25439 number
._parseInfo = function(/*Object?*/ options
){
25440 options
= options
|| {};
25441 var locale
= i18n
.normalizeLocale(options
.locale
),
25442 bundle
= i18n
.getLocalization("dojo.cldr", "number", locale
),
25443 pattern
= options
.pattern
|| bundle
[(options
.type
|| "decimal") + "Format"],
25445 group
= bundle
.group
,
25446 decimal = bundle
.decimal,
25449 if(pattern
.indexOf('%') != -1){
25451 }else if(pattern
.indexOf('\u2030') != -1){
25452 factor
/= 1000; // per mille
25454 var isCurrency
= pattern
.indexOf('\u00a4') != -1;
25456 group
= bundle
.currencyGroup
|| group
;
25457 decimal = bundle
.currencyDecimal
|| decimal;
25461 //TODO: handle quoted escapes
25462 var patternList
= pattern
.split(';');
25463 if(patternList
.length
== 1){
25464 patternList
.push("-" + patternList
[0]);
25467 var re
= dregexp
.buildGroupRE(patternList
, function(pattern
){
25468 pattern
= "(?:"+dregexp
.escapeString(pattern
, '.')+")";
25469 return pattern
.replace(number
._numberPatternRE
, function(format
){
25472 separator
: options
.strict
? group
: [group
,""],
25473 fractional
: options
.fractional
,
25478 parts
= format
.split('.'),
25479 places
= options
.places
;
25481 // special condition for percent (factor != 1)
25482 // allow decimal places even if not specified in pattern
25483 if(parts
.length
== 1 && factor
!= 1){
25486 if(parts
.length
== 1 || places
=== 0){
25487 flags
.fractional
= false;
25489 if(places
=== undefined){ places
= options
.pattern
? parts
[1].lastIndexOf('0') + 1 : Infinity
; }
25490 if(places
&& options
.fractional
== undefined){flags
.fractional
= true;} // required fractional, unless otherwise specified
25491 if(!options
.places
&& (places
< parts
[1].length
)){ places
+= "," + parts
[1].length
; }
25492 flags
.places
= places
;
25494 var groups
= parts
[0].split(',');
25495 if(groups
.length
> 1){
25496 flags
.groupSize
= groups
.pop().length
;
25497 if(groups
.length
> 1){
25498 flags
.groupSize2
= groups
.pop().length
;
25501 return "("+number
._realNumberRegexp(flags
)+")";
25506 // substitute the currency symbol for the placeholder in the pattern
25507 re
= re
.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match
, before
, target
, after
){
25508 var prop
= ["symbol", "currency", "displayName"][target
.length
-1],
25509 symbol
= dregexp
.escapeString(options
[prop
] || options
.currency
|| "");
25510 before
= before
? "[\\s\\xa0]" : "";
25511 after
= after
? "[\\s\\xa0]" : "";
25512 if(!options
.strict
){
25513 if(before
){before
+= "*";}
25514 if(after
){after
+= "*";}
25515 return "(?:"+before
+symbol
+after
+")?";
25517 return before
+symbol
+after
;
25521 //TODO: substitute localized sign/percent/permille/etc.?
25523 // normalize whitespace and return
25524 return {regexp
: re
.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group
: group
, decimal: decimal, factor
: factor
}; // Object
25528 number.__ParseOptions = declare(null, {
25529 // pattern: String?
25530 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25531 // with this string. Default value is based on locale. Overriding this property will defeat
25532 // localization. Literal characters in patterns are not supported.
25534 // choose a format type based on the locale from the following:
25535 // decimal, scientific (not yet supported), percent, currency. decimal by default.
25537 // override the locale used to determine formatting rules
25538 // strict: Boolean?
25539 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
25540 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
25541 // fractional: Boolean|Array?
25542 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
25543 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
25546 number
.parse = function(/*String*/ expression
, /*number.__ParseOptions?*/ options
){
25548 // Convert a properly formatted string to a primitive Number, using
25549 // locale-specific settings.
25551 // Create a Number from a string using a known localized pattern.
25552 // Formatting patterns are chosen appropriate to the locale
25553 // and follow the syntax described by
25554 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
25555 // Note that literal characters in patterns are not supported.
25557 // A string representation of a Number
25558 var info
= number
._parseInfo(options
),
25559 results
= (new RegExp("^"+info
.regexp
+"$")).exec(expression
);
25563 var absoluteMatch
= results
[1]; // match for the positive expression
25568 // matched the negative pattern
25569 absoluteMatch
=results
[2];
25573 // Transform it to something Javascript can parse as a number. Normalize
25574 // decimal point and strip out group separators or alternate forms of whitespace
25575 absoluteMatch
= absoluteMatch
.
25576 replace(new RegExp("["+info
.group
+ "\\s\\xa0"+"]", "g"), "").
25577 replace(info
.decimal, ".");
25578 // Adjust for negative sign, percent, etc. as necessary
25579 return absoluteMatch
* info
.factor
; //Number
25583 number.__RealNumberRegexpFlags = declare(null, {
25585 // The integer number of decimal places or a range given as "n,m". If
25586 // not given, the decimal part is optional and the number of places is
25588 // decimal: String?
25589 // A string for the character used as the decimal point. Default
25591 // fractional: Boolean|Array?
25592 // Whether decimal places are used. Can be true, false, or [true,
25593 // false]. Default is [true, false] which means optional.
25594 // exponent: Boolean|Array?
25595 // Express in exponential notation. Can be true, false, or [true,
25596 // false]. Default is [true, false], (i.e. will match if the
25597 // exponential part is present are not).
25598 // eSigned: Boolean|Array?
25599 // The leading plus-or-minus sign on the exponent. Can be true,
25600 // false, or [true, false]. Default is [true, false], (i.e. will
25601 // match if it is signed or unsigned). flags in regexp.integer can be
25606 number
._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags
){
25608 // Builds a regular expression to match a real number in exponential
25611 // assign default values to missing parameters
25612 flags
= flags
|| {};
25613 //TODO: use mixin instead?
25614 if(!("places" in flags
)){ flags
.places
= Infinity
; }
25615 if(typeof flags
.decimal != "string"){ flags
.decimal = "."; }
25616 if(!("fractional" in flags
) || /^0/.test(flags
.places
)){ flags
.fractional
= [true, false]; }
25617 if(!("exponent" in flags
)){ flags
.exponent
= [true, false]; }
25618 if(!("eSigned" in flags
)){ flags
.eSigned
= [true, false]; }
25620 var integerRE
= number
._integerRegexp(flags
),
25621 decimalRE
= dregexp
.buildGroupRE(flags
.fractional
,
25624 if(q
&& (flags
.places
!==0)){
25625 re
= "\\" + flags
.decimal;
25626 if(flags
.places
== Infinity
){
25627 re
= "(?:" + re
+ "\\d+)?";
25629 re
+= "\\d{" + flags
.places
+ "}";
25637 var exponentRE
= dregexp
.buildGroupRE(flags
.exponent
,
25639 if(q
){ return "([eE]" + number
._integerRegexp({ signed
: flags
.eSigned
}) + ")"; }
25644 var realRE
= integerRE
+ decimalRE
;
25645 // allow for decimals without integers, e.g. .25
25646 if(decimalRE
){realRE
= "(?:(?:"+ realRE
+ ")|(?:" + decimalRE
+ "))";}
25647 return realRE
+ exponentRE
; // String
25651 number.__IntegerRegexpFlags = declare(null, {
25652 // signed: Boolean?
25653 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
25654 // Default is `[true, false]`, (i.e. will match if it is signed
25656 // separator: String?
25657 // The character used as the thousands separator. Default is no
25658 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
25659 // makes ',' optional.
25660 // groupSize: Number?
25661 // group size between separators
25662 // groupSize2: Number?
25663 // second grouping, where separators 2..n have a different interval than the first separator (for India)
25667 number
._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags
){
25669 // Builds a regular expression that matches an integer
25671 // assign default values to missing parameters
25672 flags
= flags
|| {};
25673 if(!("signed" in flags
)){ flags
.signed
= [true, false]; }
25674 if(!("separator" in flags
)){
25675 flags
.separator
= "";
25676 }else if(!("groupSize" in flags
)){
25677 flags
.groupSize
= 3;
25680 var signRE
= dregexp
.buildGroupRE(flags
.signed
,
25681 function(q
){ return q
? "[-+]" : ""; },
25685 var numberRE
= dregexp
.buildGroupRE(flags
.separator
,
25691 sep
= dregexp
.escapeString(sep
);
25692 if(sep
== " "){ sep
= "\\s"; }
25693 else if(sep
== "\xa0"){ sep
= "\\s\\xa0"; }
25695 var grp
= flags
.groupSize
, grp2
= flags
.groupSize2
;
25696 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
25698 var grp2RE
= "(?:0|[1-9]\\d{0," + (grp2
-1) + "}(?:[" + sep
+ "]\\d{" + grp2
+ "})*[" + sep
+ "]\\d{" + grp
+ "})";
25699 return ((grp
-grp2
) > 0) ? "(?:" + grp2RE
+ "|(?:0|[1-9]\\d{0," + (grp
-1) + "}))" : grp2RE
;
25701 return "(?:0|[1-9]\\d{0," + (grp
-1) + "}(?:[" + sep
+ "]\\d{" + grp
+ "})*)";
25706 return signRE
+ numberRE
; // String
25713 'dijit/_FocusMixin':function(){
25714 define("dijit/_FocusMixin", [
25717 "dojo/_base/declare", // declare
25718 "dojo/_base/lang" // lang.extend
25719 ], function(focus
, _WidgetBase
, declare
, lang
){
25722 // dijit/_FocusMixin
25724 // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
25725 // to be last in the inheritance chain, so mixin to _WidgetBase.
25726 lang
.extend(_WidgetBase
, {
25727 // focused: [readonly] Boolean
25728 // This widget or a widget it contains has focus, or is "active" because
25729 // it was recently clicked.
25732 onFocus: function(){
25734 // Called when the widget becomes "active" because
25735 // it or a widget inside of it either has focus, or has recently
25741 onBlur: function(){
25743 // Called when the widget stops being "active" because
25744 // focus moved to something outside of it, or the user
25745 // clicked somewhere outside of it, or the widget was
25751 _onFocus: function(){
25753 // This is where widgets do processing for when they are active,
25754 // such as changing CSS classes. See onFocus() for more details.
25760 _onBlur: function(){
25762 // This is where widgets do processing for when they stop being active,
25763 // such as changing CSS classes. See onBlur() for more details.
25770 return declare("dijit._FocusMixin", null, {
25772 // Mixin to widget to provide _onFocus() and _onBlur() methods that
25773 // fire when a widget or its descendants get/lose focus
25775 // flag that I want _onFocus()/_onBlur() notifications from focus manager
25776 _focusManager
: focus
25782 'dojo/data/util/filter':function(){
25783 define("dojo/data/util/filter", ["../../_base/lang"], function(lang
){
25785 // dojo/data/util/filter
25790 lang
.setObject("dojo.data.util.filter", filter
);
25792 filter
.patternToRegExp = function(/*String*/pattern
, /*boolean?*/ ignoreCase
){
25794 // Helper function to convert a simple pattern to a regular expression for matching.
25796 // Returns a regular expression object that conforms to the defined conversion rules.
25799 // - ca* -> /^ca.*$/
25800 // - *ca* -> /^.*ca.*$/
25801 // - *c\*a* -> /^.*c\*a.*$/
25802 // - *c\*a?* -> /^.*c\*a..*$/
25806 // A simple matching pattern to convert that follows basic rules:
25808 // - * Means match anything, so ca* means match anything starting with ca
25809 // - ? Means match single character. So, b?b will match to bob and bab, and so on.
25810 // - \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
25812 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
25813 // represented by \\ to be treated as an ordinary \ character instead of an escape.
25815 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
25816 // By default, it is assumed case sensitive.
25820 for(var i
= 0; i
< pattern
.length
; i
++){
25821 c
= pattern
.charAt(i
);
25826 rxp
+= pattern
.charAt(i
);
25829 rxp
+= ".*"; break;
25844 rxp
+= "\\"; //fallthrough
25851 return new RegExp(rxp
,"mi"); //RegExp
25853 return new RegExp(rxp
,"m"); //RegExp
25862 'dijit/_WidgetsInTemplateMixin':function(){
25863 define("dijit/_WidgetsInTemplateMixin", [
25864 "dojo/_base/array", // array.forEach
25865 "dojo/_base/declare", // declare
25866 "dojo/parser" // parser.parse
25867 ], function(array
, declare
, parser
){
25870 // dijit/_WidgetsInTemplateMixin
25872 return declare("dijit._WidgetsInTemplateMixin", null, {
25874 // Mixin to supplement _TemplatedMixin when template contains widgets
25876 // _earlyTemplatedStartup: Boolean
25877 // A fallback to preserve the 1.0 - 1.3 behavior of children in
25878 // templates having their startup called before the parent widget
25879 // fires postCreate. Defaults to 'false', causing child widgets to
25880 // have their .startup() called immediately before a parent widget
25881 // .startup(), but always after the parent .postCreate(). Set to
25882 // 'true' to re-enable to previous, arguably broken, behavior.
25883 _earlyTemplatedStartup
: false,
25885 // widgetsInTemplate: [protected] Boolean
25886 // Should we parse the template to find widgets that might be
25887 // declared in markup inside it? (Remove for 2.0 and assume true)
25888 widgetsInTemplate
: true,
25890 _beforeFillContent: function(){
25891 if(this.widgetsInTemplate
){
25892 // Before copying over content, instantiate widgets in template
25893 var node
= this.domNode
;
25895 var cw
= (this._startupWidgets
= parser
.parse(node
, {
25896 noStart
: !this._earlyTemplatedStartup
,
25898 inherited
: {dir
: this.dir
, lang
: this.lang
, textDir
: this.textDir
},
25899 propsThis
: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
25900 scope
: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
25903 if(!cw
.isFulfilled()){
25904 throw new Error(this.declaredClass
+ ": parser returned unfilled promise (probably waiting for module auto-load), " +
25905 "unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.");
25908 // _WidgetBase::destroy() will destroy any supporting widgets under this.domNode.
25909 // If we wanted to, we could call this.own() on anything in this._startupWidgets that was moved outside
25910 // of this.domNode (like Dialog, which is moved to <body>).
25912 this._attachTemplateNodes(cw
, function(n
,p
){
25918 startup: function(){
25919 array
.forEach(this._startupWidgets
, function(w
){
25920 if(w
&& !w
._started
&& w
.startup
){
25924 this.inherited(arguments
);
25930 'dojo/fx/Toggler':function(){
25931 define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
25932 function(lang
, declare
, baseFx
, connectUtil
){
25936 return declare("dojo.fx.Toggler", null, {
25938 // A simple `dojo.Animation` toggler API.
25940 // class constructor for an animation toggler. It accepts a packed
25941 // set of arguments about what type of animation to use in each
25942 // direction, duration, etc. All available members are mixed into
25943 // these animations from the constructor (for example, `node`,
25944 // `showDuration`, `hideDuration`).
25946 // | var t = new dojo/fx/Toggler({
25947 // | node: "nodeId",
25948 // | showDuration: 500,
25949 // | // hideDuration will default to "200"
25950 // | showFunc: dojo/fx/wipeIn,
25951 // | // hideFunc will default to "fadeOut"
25953 // | t.show(100); // delay showing for 100ms
25954 // | // ...time passes...
25958 // the node to target for the showing and hiding animations
25961 // showFunc: Function
25962 // The function that returns the `dojo.Animation` to show the node
25963 showFunc
: baseFx
.fadeIn
,
25965 // hideFunc: Function
25966 // The function that returns the `dojo.Animation` to hide the node
25967 hideFunc
: baseFx
.fadeOut
,
25970 // Time in milliseconds to run the show Animation
25974 // Time in milliseconds to run the hide Animation
25977 // FIXME: need a policy for where the toggler should "be" the next
25978 // time show/hide are called if we're stopped somewhere in the
25980 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
25981 // each animation individually.
25982 // FIXME: also would be nice to have events from the animations exposed/bridged
25995 constructor: function(args
){
25998 lang
.mixin(_t
, args
);
25999 _t
.node
= args
.node
;
26000 _t
._showArgs
= lang
.mixin({}, args
);
26001 _t
._showArgs
.node
= _t
.node
;
26002 _t
._showArgs
.duration
= _t
.showDuration
;
26003 _t
.showAnim
= _t
.showFunc(_t
._showArgs
);
26005 _t
._hideArgs
= lang
.mixin({}, args
);
26006 _t
._hideArgs
.node
= _t
.node
;
26007 _t
._hideArgs
.duration
= _t
.hideDuration
;
26008 _t
.hideAnim
= _t
.hideFunc(_t
._hideArgs
);
26010 connectUtil
.connect(_t
.showAnim
, "beforeBegin", lang
.hitch(_t
.hideAnim
, "stop", true));
26011 connectUtil
.connect(_t
.hideAnim
, "beforeBegin", lang
.hitch(_t
.showAnim
, "stop", true));
26014 show: function(delay
){
26016 // Toggle the node to showing
26018 // Amount of time to stall playing the show animation
26019 return this.showAnim
.play(delay
|| 0);
26022 hide: function(delay
){
26024 // Toggle the node to hidden
26026 // Amount of time to stall playing the hide animation
26027 return this.hideAnim
.play(delay
|| 0);
26034 'dijit/form/FilteringSelect':function(){
26035 define("dijit/form/FilteringSelect", [
26036 "dojo/data/util/filter", // filter.patternToRegExp
26037 "dojo/_base/declare", // declare
26038 "dojo/_base/lang", // lang.mixin
26042 ], function(filter
, declare
, lang
, when
, MappedTextBox
, ComboBoxMixin
){
26045 // dijit/form/FilteringSelect
26047 return declare("dijit.form.FilteringSelect", [MappedTextBox
, ComboBoxMixin
], {
26049 // An enhanced version of the HTML SELECT tag, populated dynamically
26052 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
26053 // very nicely with very large data sets because it can load and page data as needed.
26054 // It also resembles ComboBox, but does not allow values outside of the provided ones.
26055 // If OPTION tags are used as the data provider via markup, then the
26056 // OPTION tag's child text node is used as the displayed value when selected
26057 // while the OPTION tag's value attribute is used as the widget value on form submit.
26058 // To set the default value when using OPTION tags, specify the selected
26059 // attribute on 1 of the child OPTION tags.
26061 // Similar features:
26063 // - There is a drop down list of possible values.
26064 // - You can only enter a value from the drop down list. (You can't
26065 // enter an arbitrary value.)
26066 // - The value submitted with the form is the hidden value (ex: CA),
26067 // not the displayed value a.k.a. label (ex: California)
26069 // Enhancements over plain HTML version:
26071 // - If you type in some text then it will filter down the list of
26072 // possible values in the drop down list.
26073 // - List can be specified either as a static list or via a javascript
26074 // function (that can get the list from a server)
26076 // required: Boolean
26077 // True (default) if user is required to enter a value into this field.
26080 _lastDisplayedValue
: "",
26082 _isValidSubset: function(){
26083 return this._opened
;
26086 isValid: function(){
26087 // Overrides ValidationTextBox.isValid()
26088 return !!this.item
|| (!this.required
&& this.get('displayedValue') == ""); // #5974
26091 _refreshState: function(){
26092 if(!this.searchTimer
){ // state will be refreshed after results are returned
26093 this.inherited(arguments
);
26097 _callbackSetLabel: function(
26100 /*Object*/ options
,
26101 /*Boolean?*/ priorityChange
){
26103 // Callback from dojo.store after lookup of user entered value finishes
26105 // setValue does a synchronous lookup,
26106 // so it calls _callbackSetLabel directly,
26107 // and so does not pass dataObject
26108 // still need to test against _lastQuery in case it came too late
26109 if((query
&& query
[this.searchAttr
] !== this._lastQuery
) || (!query
&& result
.length
&& this.store
.getIdentity(result
[0]) != this._lastQuery
)){
26112 if(!result
.length
){
26113 //#3268: don't modify display value on bad input
26114 //#3285: change CSS to indicate error
26115 this.set("value", '', priorityChange
|| (priorityChange
=== undefined && !this.focused
), this.textbox
.value
, null);
26117 this.set('item', result
[0], priorityChange
);
26121 _openResultList: function(/*Object*/ results
, /*Object*/ query
, /*Object*/ options
){
26122 // Callback when a data store query completes.
26123 // Overrides ComboBox._openResultList()
26125 // #3285: tap into search callback to see if user's query resembles a match
26126 if(query
[this.searchAttr
] !== this._lastQuery
){
26129 this.inherited(arguments
);
26131 if(this.item
=== undefined){ // item == undefined for keyboard search
26132 // If the search returned no items that means that the user typed
26133 // in something invalid (and they can't make it valid by typing more characters),
26134 // so flag the FilteringSelect as being in an invalid state
26135 this.validate(true);
26139 _getValueAttr: function(){
26141 // Hook for get('value') to work.
26143 // don't get the textbox value but rather the previously set hidden value.
26144 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
26145 return this.valueNode
.value
;
26148 _getValueField: function(){
26149 // Overrides ComboBox._getValueField()
26153 _setValueAttr: function(/*String*/ value
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
, /*item?*/ item
){
26155 // Hook so set('value', value) works.
26157 // Sets the value of the select.
26158 // Also sets the label to the corresponding value by reverse lookup.
26159 if(!this._onChangeActive
){ priorityChange
= null; }
26161 if(item
=== undefined){
26162 if(value
=== null || value
=== ''){
26164 if(!lang
.isString(displayedValue
)){
26165 this._setDisplayedValueAttr(displayedValue
||'', priorityChange
);
26171 this._lastQuery
= value
;
26172 when(this.store
.get(value
), function(item
){
26173 self
._callbackSetLabel(item
? [item
] : [], undefined, undefined, priorityChange
);
26176 this.valueNode
.value
= value
;
26177 this.inherited(arguments
);
26181 _setItemAttr: function(/*item*/ item
, /*Boolean?*/ priorityChange
, /*String?*/ displayedValue
){
26183 // Set the displayed valued in the input box, and the hidden value
26184 // that gets submitted, based on a dojo.data store item.
26186 // Users shouldn't call this function; they should be calling
26187 // set('item', value)
26190 this.inherited(arguments
);
26191 this._lastDisplayedValue
= this.textbox
.value
;
26194 _getDisplayQueryString: function(/*String*/ text
){
26195 return text
.replace(/([\\\*\?])/g, "\\$1");
26198 _setDisplayedValueAttr: function(/*String*/ label
, /*Boolean?*/ priorityChange
){
26200 // Hook so set('displayedValue', label) works.
26202 // Sets textbox to display label. Also performs reverse lookup
26203 // to set the hidden value. label should corresponding to item.searchAttr.
26205 if(label
== null){ label
= ''; }
26207 // This is called at initialization along with every custom setter.
26208 // Usually (or always?) the call can be ignored. If it needs to be
26209 // processed then at least make sure that the XHR request doesn't trigger an onChange()
26210 // event, even if it returns after creation has finished
26211 if(!this._created
){
26212 if(!("displayedValue" in this.params
)){
26215 priorityChange
= false;
26218 // Do a reverse lookup to map the specified displayedValue to the hidden value.
26219 // Note that if there's a custom labelFunc() this code
26221 this.closeDropDown();
26222 var query
= lang
.clone(this.query
); // #6196: populate query with user-specifics
26225 var qs
= this._getDisplayQueryString(label
), q
;
26226 if(this.store
._oldAPI
){
26227 // remove this branch for 2.0
26230 // Query on searchAttr is a regex for benefit of dojo/store/Memory,
26231 // but with a toString() method to help dojo/store/JsonRest.
26232 // Search string like "Co*" converted to regex like /^Co.*$/i.
26233 q
= filter
.patternToRegExp(qs
, this.ignoreCase
);
26234 q
.toString = function(){ return qs
; };
26236 this._lastQuery
= query
[this.searchAttr
] = q
;
26238 // If the label is not valid, the callback will never set it,
26239 // so the last valid value will get the warning textbox. Set the
26240 // textbox value now so that the impending warning will make
26241 // sense to the user
26242 this.textbox
.value
= label
;
26243 this._lastDisplayedValue
= label
;
26244 this._set("displayedValue", label
); // for watch("displayedValue") notification
26247 ignoreCase
: this.ignoreCase
,
26250 lang
.mixin(options
, this.fetchProperties
);
26251 this._fetchHandle
= this.store
.query(query
, options
);
26252 when(this._fetchHandle
, function(result
){
26253 _this
._fetchHandle
= null;
26254 _this
._callbackSetLabel(result
|| [], query
, options
, priorityChange
);
26256 _this
._fetchHandle
= null;
26257 if(!_this
._cancelingQuery
){ // don't treat canceled query as an error
26258 console
.error('dijit.form.FilteringSelect: ' + err
.toString());
26265 this.set('displayedValue', this._lastDisplayedValue
);
26271 'dojo/data/util/sorter':function(){
26272 define("dojo/data/util/sorter", ["../../_base/lang"], function(lang
){
26274 // dojo/data/util/sorter
26279 lang
.setObject("dojo.data.util.sorter", sorter
);
26281 sorter
.basicComparator = function( /*anything*/ a
,
26284 // Basic comparison function that compares if an item is greater or less than another item
26286 // returns 1 if a > b, -1 if a < b, 0 if equal.
26287 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
26288 // And compared to each other, null is equivalent to undefined.
26290 //null is a problematic compare, so if null, we set to undefined.
26291 //Makes the check logic simple, compact, and consistent
26292 //And (null == undefined) === true, so the check later against null
26293 //works for undefined and is less bytes.
26303 }else if(a
> b
|| a
== null){
26306 return r
; //int {-1,0,1}
26309 sorter
.createSortFunction = function( /* attributes[] */sortSpec
, /*dojo/data/api/Read*/ store){
26311 // Helper function to generate the sorting function based off the list of sort attributes.
26313 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
26314 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
26315 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
26316 // Returns the sorting function for this particular list of attributes and sorting directions.
26318 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
26319 // The objects should be formatted as follows:
26321 // | attribute: "attributeName-string" || attribute,
26322 // | descending: true|false; // Default is false.
26325 // The datastore object to look up item values from.
26327 var sortFunctions=[];
26329 function createSortFunction(attr, dir, comp, s){
26330 //Passing in comp and s (comparator and store), makes this
26331 //function much faster.
26332 return function(itemA, itemB){
26333 var a = s.getValue(itemA, attr);
26334 var b = s.getValue(itemB, attr);
26335 return dir * comp(a,b); //int
26339 var map = store.comparatorMap;
26340 var bc = sorter.basicComparator;
26341 for(var i = 0; i < sortSpec.length; i++){
26342 sortAttribute = sortSpec[i];
26343 var attr = sortAttribute.attribute;
26345 var dir = (sortAttribute.descending) ? -1 : 1;
26348 if(typeof attr !== "string" && ("toString" in attr)){
26349 attr = attr.toString();
26351 comp = map[attr] || bc;
26353 sortFunctions.push(createSortFunction(attr,
26354 dir, comp, store));
26357 return function(rowA, rowB){
26359 while(i < sortFunctions.length){
26360 var ret = sortFunctions[i++](rowA, rowB);
26373 'dijit/form/_ButtonMixin':function(){
26374 define("dijit/form/_ButtonMixin", [
26375 "dojo/_base/declare", // declare
26376 "dojo/dom", // dom.setSelectable
26377 "dojo/_base/event", // event.stop
26378 "../registry" // registry.byNode
26379 ], function(declare, dom, event, registry){
26382 // dijit/form/_ButtonMixin
26384 return declare("dijit.form._ButtonMixin", null, {
26386 // A mixin to add a thin standard API wrapper to a normal HTML button
26388 // A label should always be specified (through innerHTML) or the label attribute.
26392 // - focusNode (required): this node receives focus
26393 // - valueNode (optional): this node's value gets submitted with FORM elements
26394 // - containerNode (optional): this node gets the innerHTML assignment for label
26396 // | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
26398 // | var button1 = new Button({label: "hello world", onClick: foo});
26399 // | dojo.body().appendChild(button1.domNode);
26401 // label: HTML String
26402 // Content to display in button.
26405 // type: [const] String
26406 // Type of button (submit, reset, button, checkbox, radio)
26409 _onClick: function(/*Event*/ e
){
26411 // Internal function to handle click actions
26416 var preventDefault
= this.onClick(e
) === false; // user click actions
26417 if(!preventDefault
&& this.type
== "submit" && !(this.valueNode
||this.focusNode
).form
){ // see if a non-form widget needs to be signalled
26418 for(var node
=this.domNode
; node
.parentNode
; node
=node
.parentNode
){
26419 var widget
=registry
.byNode(node
);
26420 if(widget
&& typeof widget
._onSubmit
== "function"){
26421 widget
._onSubmit(e
);
26422 preventDefault
= true;
26427 if(preventDefault
){
26428 e
.preventDefault();
26430 return !preventDefault
;
26433 postCreate: function(){
26434 this.inherited(arguments
);
26435 dom
.setSelectable(this.focusNode
, false);
26438 onClick: function(/*Event*/ /*===== e =====*/){
26440 // Callback for when button is clicked.
26441 // If type="submit", return true to perform submit, or false to cancel it.
26444 return true; // Boolean
26447 _setLabelAttr: function(/*String*/ content
){
26449 // Hook for set('label', ...) to work.
26451 // Set the label (text) of the button; takes an HTML string.
26452 this._set("label", content
);
26453 (this.containerNode
||this.focusNode
).innerHTML
= content
;
26460 'dojo/colors':function(){
26461 define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo
, lang
, Color
, ArrayUtil
){
26468 // Color utilities, extending Base dojo.Color
26473 lang
.setObject("dojo.colors", ColorExt
);
26475 //TODO: this module appears to break naming conventions
26477 // this is a standard conversion prescribed by the CSS3 Color Module
26478 var hue2rgb = function(m1
, m2
, h
){
26482 if(h6
< 1){ return m1
+ (m2
- m1
) * h6
; }
26483 if(2 * h
< 1){ return m2
; }
26484 if(3 * h
< 2){ return m1
+ (m2
- m1
) * (2 / 3 - h
) * 6; }
26487 // Override base Color.fromRgb with the impl in this module
26488 dojo
.colorFromRgb
= Color
.fromRgb = function(/*String*/ color
, /*dojo/_base/Color?*/ obj
){
26490 // get rgb(a) array from css-style color declarations
26492 // this function can handle all 4 CSS3 Color Module formats: rgb,
26493 // rgba, hsl, hsla, including rgb(a) with percentage values.
26494 var m
= color
.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
26496 var c
= m
[2].split(/\s*,\s*/), l
= c
.length
, t
= m
[1], a
;
26497 if((t
== "rgb" && l
== 3) || (t
== "rgba" && l
== 4)){
26499 if(r
.charAt(r
.length
- 1) == "%"){
26500 // 3 rgb percentage values
26501 a
= ArrayUtil
.map(c
, function(x
){
26502 return parseFloat(x
) * 2.56;
26504 if(l
== 4){ a
[3] = c
[3]; }
26505 return Color
.fromArray(a
, obj
); // dojo/_base/Color
26507 return Color
.fromArray(c
, obj
); // dojo/_base/Color
26509 if((t
== "hsl" && l
== 3) || (t
== "hsla" && l
== 4)){
26510 // normalize hsl values
26511 var H
= ((parseFloat(c
[0]) % 360) + 360) % 360 / 360,
26512 S
= parseFloat(c
[1]) / 100,
26513 L
= parseFloat(c
[2]) / 100,
26514 // calculate rgb according to the algorithm
26515 // recommended by the CSS3 Color Module
26516 m2
= L
<= 0.5 ? L
* (S
+ 1) : L
+ S
- L
* S
,
26519 hue2rgb(m1
, m2
, H
+ 1 / 3) * 256,
26520 hue2rgb(m1
, m2
, H
) * 256,
26521 hue2rgb(m1
, m2
, H
- 1 / 3) * 256,
26524 if(l
== 4){ a
[3] = c
[3]; }
26525 return Color
.fromArray(a
, obj
); // dojo/_base/Color
26528 return null; // dojo/_base/Color
26531 var confine = function(c
, low
, high
){
26533 // sanitize a color component by making sure it is a number,
26534 // and clamping it to valid values
26536 return isNaN(c
) ? high
: c
< low
? low
: c
> high
? high
: c
; // Number
26539 Color
.prototype.sanitize = function(){
26541 // makes sure that the object has correct attributes
26543 t
.r
= Math
.round(confine(t
.r
, 0, 255));
26544 t
.g
= Math
.round(confine(t
.g
, 0, 255));
26545 t
.b
= Math
.round(confine(t
.b
, 0, 255));
26546 t
.a
= confine(t
.a
, 0, 1);
26547 return this; // dojo/_base/Color
26550 ColorExt
.makeGrey
= Color
.makeGrey = function(/*Number*/ g
, /*Number?*/ a
){
26552 // creates a greyscale color with an optional alpha
26553 return Color
.fromArray([g
, g
, g
, a
]); // dojo/_base/Color
26556 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
26557 lang
.mixin(Color
.named
, {
26558 "aliceblue": [240,248,255],
26559 "antiquewhite": [250,235,215],
26560 "aquamarine": [127,255,212],
26561 "azure": [240,255,255],
26562 "beige": [245,245,220],
26563 "bisque": [255,228,196],
26564 "blanchedalmond": [255,235,205],
26565 "blueviolet": [138,43,226],
26566 "brown": [165,42,42],
26567 "burlywood": [222,184,135],
26568 "cadetblue": [95,158,160],
26569 "chartreuse": [127,255,0],
26570 "chocolate": [210,105,30],
26571 "coral": [255,127,80],
26572 "cornflowerblue": [100,149,237],
26573 "cornsilk": [255,248,220],
26574 "crimson": [220,20,60],
26575 "cyan": [0,255,255],
26576 "darkblue": [0,0,139],
26577 "darkcyan": [0,139,139],
26578 "darkgoldenrod": [184,134,11],
26579 "darkgray": [169,169,169],
26580 "darkgreen": [0,100,0],
26581 "darkgrey": [169,169,169],
26582 "darkkhaki": [189,183,107],
26583 "darkmagenta": [139,0,139],
26584 "darkolivegreen": [85,107,47],
26585 "darkorange": [255,140,0],
26586 "darkorchid": [153,50,204],
26587 "darkred": [139,0,0],
26588 "darksalmon": [233,150,122],
26589 "darkseagreen": [143,188,143],
26590 "darkslateblue": [72,61,139],
26591 "darkslategray": [47,79,79],
26592 "darkslategrey": [47,79,79],
26593 "darkturquoise": [0,206,209],
26594 "darkviolet": [148,0,211],
26595 "deeppink": [255,20,147],
26596 "deepskyblue": [0,191,255],
26597 "dimgray": [105,105,105],
26598 "dimgrey": [105,105,105],
26599 "dodgerblue": [30,144,255],
26600 "firebrick": [178,34,34],
26601 "floralwhite": [255,250,240],
26602 "forestgreen": [34,139,34],
26603 "gainsboro": [220,220,220],
26604 "ghostwhite": [248,248,255],
26605 "gold": [255,215,0],
26606 "goldenrod": [218,165,32],
26607 "greenyellow": [173,255,47],
26608 "grey": [128,128,128],
26609 "honeydew": [240,255,240],
26610 "hotpink": [255,105,180],
26611 "indianred": [205,92,92],
26612 "indigo": [75,0,130],
26613 "ivory": [255,255,240],
26614 "khaki": [240,230,140],
26615 "lavender": [230,230,250],
26616 "lavenderblush": [255,240,245],
26617 "lawngreen": [124,252,0],
26618 "lemonchiffon": [255,250,205],
26619 "lightblue": [173,216,230],
26620 "lightcoral": [240,128,128],
26621 "lightcyan": [224,255,255],
26622 "lightgoldenrodyellow": [250,250,210],
26623 "lightgray": [211,211,211],
26624 "lightgreen": [144,238,144],
26625 "lightgrey": [211,211,211],
26626 "lightpink": [255,182,193],
26627 "lightsalmon": [255,160,122],
26628 "lightseagreen": [32,178,170],
26629 "lightskyblue": [135,206,250],
26630 "lightslategray": [119,136,153],
26631 "lightslategrey": [119,136,153],
26632 "lightsteelblue": [176,196,222],
26633 "lightyellow": [255,255,224],
26634 "limegreen": [50,205,50],
26635 "linen": [250,240,230],
26636 "magenta": [255,0,255],
26637 "mediumaquamarine": [102,205,170],
26638 "mediumblue": [0,0,205],
26639 "mediumorchid": [186,85,211],
26640 "mediumpurple": [147,112,219],
26641 "mediumseagreen": [60,179,113],
26642 "mediumslateblue": [123,104,238],
26643 "mediumspringgreen": [0,250,154],
26644 "mediumturquoise": [72,209,204],
26645 "mediumvioletred": [199,21,133],
26646 "midnightblue": [25,25,112],
26647 "mintcream": [245,255,250],
26648 "mistyrose": [255,228,225],
26649 "moccasin": [255,228,181],
26650 "navajowhite": [255,222,173],
26651 "oldlace": [253,245,230],
26652 "olivedrab": [107,142,35],
26653 "orange": [255,165,0],
26654 "orangered": [255,69,0],
26655 "orchid": [218,112,214],
26656 "palegoldenrod": [238,232,170],
26657 "palegreen": [152,251,152],
26658 "paleturquoise": [175,238,238],
26659 "palevioletred": [219,112,147],
26660 "papayawhip": [255,239,213],
26661 "peachpuff": [255,218,185],
26662 "peru": [205,133,63],
26663 "pink": [255,192,203],
26664 "plum": [221,160,221],
26665 "powderblue": [176,224,230],
26666 "rosybrown": [188,143,143],
26667 "royalblue": [65,105,225],
26668 "saddlebrown": [139,69,19],
26669 "salmon": [250,128,114],
26670 "sandybrown": [244,164,96],
26671 "seagreen": [46,139,87],
26672 "seashell": [255,245,238],
26673 "sienna": [160,82,45],
26674 "skyblue": [135,206,235],
26675 "slateblue": [106,90,205],
26676 "slategray": [112,128,144],
26677 "slategrey": [112,128,144],
26678 "snow": [255,250,250],
26679 "springgreen": [0,255,127],
26680 "steelblue": [70,130,180],
26681 "tan": [210,180,140],
26682 "thistle": [216,191,216],
26683 "tomato": [255,99,71],
26684 "turquoise": [64,224,208],
26685 "violet": [238,130,238],
26686 "wheat": [245,222,179],
26687 "whitesmoke": [245,245,245],
26688 "yellowgreen": [154,205,50]
26691 return Color
; // TODO: return ColorExt, not Color
26695 'dijit/registry':function(){
26696 define("dijit/registry", [
26697 "dojo/_base/array", // array.forEach array.map
26698 "dojo/sniff", // has("ie")
26699 "dojo/_base/unload", // unload.addOnWindowUnload
26700 "dojo/_base/window", // win.body
26701 "./main" // dijit._scopeName
26702 ], function(array
, has
, unload
, win
, dijit
){
26707 var _widgetTypeCtr
= {}, hash
= {};
26711 // Registry of existing widget on page, plus some utility methods.
26714 // Number of registered widgets
26717 add: function(widget
){
26719 // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
26720 // widget: dijit/_WidgetBase
26721 // Any dijit/_WidgetBase subclass.
26722 if(hash
[widget
.id
]){
26723 throw new Error("Tried to register widget with id==" + widget
.id
+ " but that id is already registered");
26725 hash
[widget
.id
] = widget
;
26729 remove: function(/*String*/ id
){
26731 // Remove a widget from the registry. Does not destroy the widget; simply
26732 // removes the reference.
26739 byId: function(/*String|Widget*/ id
){
26741 // Find a widget by it's id.
26742 // If passed a widget then just returns the widget.
26743 return typeof id
== "string" ? hash
[id
] : id
; // dijit/_WidgetBase
26746 byNode: function(/*DOMNode*/ node
){
26748 // Returns the widget corresponding to the given DOMNode
26749 return hash
[node
.getAttribute("widgetId")]; // dijit/_WidgetBase
26752 toArray: function(){
26754 // Convert registry into a true Array
26757 // Work with the widget .domNodes in a real Array
26758 // | array.map(registry.toArray(), function(w){ return w.domNode; });
26761 for(var id
in hash
){
26764 return ar
; // dijit/_WidgetBase[]
26767 getUniqueId: function(/*String*/widgetType
){
26769 // Generates a unique id for a given widgetType
26773 id
= widgetType
+ "_" +
26774 (widgetType
in _widgetTypeCtr
?
26775 ++_widgetTypeCtr
[widgetType
] : _widgetTypeCtr
[widgetType
] = 0);
26777 return dijit
._scopeName
== "dijit" ? id
: dijit
._scopeName
+ "_" + id
; // String
26780 findWidgets: function(root
, skipNode
){
26782 // Search subtree under root returning widgets found.
26783 // Doesn't search for nested widgets (ie, widgets inside other widgets).
26785 // Node to search under.
26786 // skipNode: DOMNode
26787 // If specified, don't search beneath this node (usually containerNode).
26791 function getChildrenHelper(root
){
26792 for(var node
= root
.firstChild
; node
; node
= node
.nextSibling
){
26793 if(node
.nodeType
== 1){
26794 var widgetId
= node
.getAttribute("widgetId");
26796 var widget
= hash
[widgetId
];
26797 if(widget
){ // may be null on page w/multiple dojo's loaded
26798 outAry
.push(widget
);
26800 }else if(node
!== skipNode
){
26801 getChildrenHelper(node
);
26807 getChildrenHelper(root
);
26811 _destroyAll: function(){
26813 // Code to destroy all widgets and do other cleanup on page unload
26815 // Clean up focus manager lingering references to widgets and nodes
26816 dijit
._curFocus
= null;
26817 dijit
._prevFocus
= null;
26818 dijit
._activeStack
= [];
26820 // Destroy all the widgets, top down
26821 array
.forEach(registry
.findWidgets(win
.body()), function(widget
){
26822 // Avoid double destroy of widgets like Menu that are attached to <body>
26823 // even though they are logically children of other widgets.
26824 if(!widget
._destroyed
){
26825 if(widget
.destroyRecursive
){
26826 widget
.destroyRecursive();
26827 }else if(widget
.destroy
){
26834 getEnclosingWidget: function(/*DOMNode*/ node
){
26836 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
26837 // the node is not contained within the DOM tree of any widget
26839 var id
= node
.nodeType
== 1 && node
.getAttribute("widgetId");
26843 node
= node
.parentNode
;
26848 // In case someone needs to access hash.
26849 // Actually, this is accessed from WidgetSet back-compatibility code
26853 dijit
.registry
= registry
;
26859 'dijit/tree/_dndContainer':function(){
26860 define("dijit/tree/_dndContainer", [
26861 "dojo/aspect", // aspect.after
26862 "dojo/_base/declare", // declare
26863 "dojo/dom-class", // domClass.add domClass.remove domClass.replace
26864 "dojo/_base/event", // event.stop
26865 "dojo/_base/lang", // lang.mixin lang.hitch
26868 ], function(aspect
, declare
,domClass
, event
, lang
, on
, touch
){
26871 // dijit/tree/_dndContainer
26876 // A dict of parameters for Tree source configuration.
26877 // isSource: Boolean?
26878 // Can be used as a DnD source. Defaults to true.
26879 // accept: String[]
26880 // List of accepted types (text strings) for a target; defaults to
26881 // ["text", "treeNode"]
26882 // copyOnly: Boolean?
26883 // Copy items, if true, use a state of Ctrl key otherwise,
26884 // dragThreshold: Number
26885 // The move delay in pixels before detecting a drag; 0 by default
26886 // betweenThreshold: Integer
26887 // Distance from upper/lower edge of node to allow drop to reorder nodes
26891 return declare("dijit.tree._dndContainer", null, {
26894 // This is a base class for `dijit/tree/_dndSelector`, and isn't meant to be used directly.
26895 // It's modeled after `dojo/dnd/Container`.
26900 // current: DomNode
26901 // The currently hovered TreeNode.rowNode (which is the DOM node
26902 // associated w/a given node in the tree, excluding it's descendants)
26906 constructor: function(tree
, params
){
26908 // A constructor of the Container
26910 // Node or node's id to build the container on
26912 // A dict of parameters, which gets mixed into the object
26916 this.node
= tree
.domNode
; // TODO: rename; it's not a TreeNode but the whole Tree
26917 lang
.mixin(this, params
);
26919 // class-specific variables
26920 this.current
= null; // current TreeNode's DOM node
26923 this.containerState
= "";
26924 domClass
.add(this.node
, "dojoDndContainer");
26928 // Mouse (or touch) enter/leave on Tree itself
26929 on(this.node
, touch
.enter
, lang
.hitch(this, "onOverEvent")),
26930 on(this.node
, touch
.leave
, lang
.hitch(this, "onOutEvent")),
26932 // switching between TreeNodes
26933 aspect
.after(this.tree
, "_onNodeMouseEnter", lang
.hitch(this, "onMouseOver"), true),
26934 aspect
.after(this.tree
, "_onNodeMouseLeave", lang
.hitch(this, "onMouseOut"), true),
26936 // cancel text selection and text dragging
26937 on(this.node
, "dragstart", lang
.hitch(event
, "stop")),
26938 on(this.node
, "selectstart", lang
.hitch(event
, "stop"))
26942 destroy: function(){
26944 // Prepares this object to be garbage-collected
26947 while(h
= this.events
.pop()){ h
.remove(); }
26949 // this.clearItems();
26950 this.node
= this.parent
= null;
26954 onMouseOver: function(widget
/*===== , evt =====*/){
26956 // Called when mouse is moved over a TreeNode
26957 // widget: TreeNode
26961 this.current
= widget
;
26964 onMouseOut: function(/*===== widget, evt =====*/){
26966 // Called when mouse is moved away from a TreeNode
26967 // widget: TreeNode
26971 this.current
= null;
26974 _changeState: function(type
, newState
){
26976 // Changes a named state to new state value
26978 // A name of the state to change
26979 // newState: String
26981 var prefix
= "dojoDnd" + type
;
26982 var state
= type
.toLowerCase() + "State";
26983 //domClass.replace(this.node, prefix + newState, prefix + this[state]);
26984 domClass
.replace(this.node
, prefix
+ newState
, prefix
+ this[state
]);
26985 this[state
] = newState
;
26988 _addItemClass: function(node
, type
){
26990 // Adds a class with prefix "dojoDndItem"
26994 // A variable suffix for a class name
26995 domClass
.add(node
, "dojoDndItem" + type
);
26998 _removeItemClass: function(node
, type
){
27000 // Removes a class with prefix "dojoDndItem"
27004 // A variable suffix for a class name
27005 domClass
.remove(node
, "dojoDndItem" + type
);
27008 onOverEvent: function(){
27010 // This function is called once, when mouse is over our container
27013 this._changeState("Container", "Over");
27016 onOutEvent: function(){
27018 // This function is called once, when mouse is out of our container
27021 this._changeState("Container", "");
27027 'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n",
27028 'dijit/_base/wai':function(){
27029 define("dijit/_base/wai", [
27030 "dojo/dom-attr", // domAttr.attr
27031 "dojo/_base/lang", // lang.mixin
27032 "../main", // export symbols to dijit
27033 "../hccss" // not using this module directly, but loading it sets CSS flag on <html>
27034 ], function(domAttr
, lang
, dijit
){
27041 // Deprecated methods for setting/getting wai roles and states.
27042 // New code should call setAttribute()/getAttribute() directly.
27044 // Also loads hccss to apply dj_a11y class to root node if machine is in high-contrast mode.
27046 hasWaiRole: function(/*Element*/ elem
, /*String?*/ role
){
27048 // Determines if an element has a particular role.
27050 // True if elem has the specific role attribute and false if not.
27051 // For backwards compatibility if role parameter not provided,
27052 // returns true if has a role
27053 var waiRole
= this.getWaiRole(elem
);
27054 return role
? (waiRole
.indexOf(role
) > -1) : (waiRole
.length
> 0);
27057 getWaiRole: function(/*Element*/ elem
){
27059 // Gets the role for an element (which should be a wai role).
27061 // The role of elem or an empty string if elem
27062 // does not have a role.
27063 return lang
.trim((domAttr
.get(elem
, "role") || "").replace("wairole:",""));
27066 setWaiRole: function(/*Element*/ elem
, /*String*/ role
){
27068 // Sets the role on an element.
27070 // Replace existing role attribute with new role.
27072 domAttr
.set(elem
, "role", role
);
27075 removeWaiRole: function(/*Element*/ elem
, /*String*/ role
){
27077 // Removes the specified role from an element.
27078 // Removes role attribute if no specific role provided (for backwards compat.)
27080 var roleValue
= domAttr
.get(elem
, "role");
27081 if(!roleValue
){ return; }
27083 var t
= lang
.trim((" " + roleValue
+ " ").replace(" " + role
+ " ", " "));
27084 domAttr
.set(elem
, "role", t
);
27086 elem
.removeAttribute("role");
27090 hasWaiState: function(/*Element*/ elem
, /*String*/ state
){
27092 // Determines if an element has a given state.
27094 // Checks for an attribute called "aria-"+state.
27096 // true if elem has a value for the given state and
27097 // false if it does not.
27099 return elem
.hasAttribute
? elem
.hasAttribute("aria-"+state
) : !!elem
.getAttribute("aria-"+state
);
27102 getWaiState: function(/*Element*/ elem
, /*String*/ state
){
27104 // Gets the value of a state on an element.
27106 // Checks for an attribute called "aria-"+state.
27108 // The value of the requested state on elem
27109 // or an empty string if elem has no value for state.
27111 return elem
.getAttribute("aria-"+state
) || "";
27114 setWaiState: function(/*Element*/ elem
, /*String*/ state
, /*String*/ value
){
27116 // Sets a state on an element.
27118 // Sets an attribute called "aria-"+state.
27120 elem
.setAttribute("aria-"+state
, value
);
27123 removeWaiState: function(/*Element*/ elem
, /*String*/ state
){
27125 // Removes a state from an element.
27127 // Sets an attribute called "aria-"+state.
27129 elem
.removeAttribute("aria-"+state
);
27133 lang
.mixin(dijit
, exports
);
27135 /*===== return exports; =====*/
27136 return dijit
; // for back compat :-(
27140 'dijit/form/_FormSelectWidget':function(){
27141 define("dijit/form/_FormSelectWidget", [
27142 "dojo/_base/array", // array.filter array.forEach array.map array.some
27143 "dojo/_base/Deferred",
27144 "dojo/aspect", // aspect.after
27145 "dojo/data/util/sorter", // util.sorter.createSortFunction
27146 "dojo/_base/declare", // declare
27147 "dojo/dom", // dom.setSelectable
27148 "dojo/dom-class", // domClass.toggle
27149 "dojo/_base/kernel", // _scopeName
27150 "dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
27151 "dojo/query", // query
27153 "dojo/store/util/QueryResults",
27154 "./_FormValueWidget"
27155 ], function(array
, Deferred
, aspect
, sorter
, declare
, dom
, domClass
, kernel
, lang
, query
, when
,
27156 QueryResults
, _FormValueWidget
){
27159 // dijit/form/_FormSelectWidget
27162 var __SelectOption = {
27164 // The value of the option. Setting to empty (or missing) will
27165 // place a separator at that location
27167 // The label for our option. It can contain html tags.
27168 // selected: Boolean
27169 // Whether or not we are a selected option
27170 // disabled: Boolean
27171 // Whether or not this specific option is disabled
27175 var _FormSelectWidget
= declare("dijit.form._FormSelectWidget", _FormValueWidget
, {
27177 // Extends _FormValueWidget in order to provide "select-specific"
27178 // values - i.e., those values that are unique to `<select>` elements.
27179 // This also provides the mechanism for reading the elements from
27180 // a store, if desired.
27182 // multiple: [const] Boolean
27183 // Whether or not we are multi-valued
27186 // options: __SelectOption[]
27187 // The set of options for our select item. Roughly corresponds to
27188 // the html `<option>` tag.
27191 // store: dojo/store/api/Store
27192 // A store to use for getting our list of options - rather than reading them
27193 // from the `<option>` html tags. Should support getIdentity().
27194 // For back-compat store can also be a dojo/data/api/Identity.
27198 // A query to use when fetching items from our store
27201 // queryOptions: object
27202 // Query options to use when fetching from the store
27203 queryOptions
: null,
27205 // labelAttr: String?
27206 // The entries in the drop down list come from this attribute in the dojo.store items.
27207 // If ``store`` is set, labelAttr must be set too, unless store is an old-style
27208 // dojo.data store rather than a new dojo/store.
27211 // onFetch: Function
27212 // A callback to do with an onFetch - but before any items are actually
27213 // iterated over (i.e. to filter even further what you want to add)
27216 // sortByLabel: Boolean
27217 // Flag to sort the options returned from a store by the label of
27222 // loadChildrenOnOpen: Boolean
27223 // By default loadChildren is called when the items are fetched from the
27224 // store. This property allows delaying loadChildren (and the creation
27225 // of the options/menuitems) until the user clicks the button to open the
27227 loadChildrenOnOpen
: false,
27229 // onLoadDeferred: [readonly] dojo.Deferred
27230 // This is the `dojo.Deferred` returned by setStore().
27231 // Calling onLoadDeferred.then() registers your
27232 // callback to be called only once, when the prior setStore completes.
27233 onLoadDeferred
: null,
27235 getOptions: function(/*anything*/ valueOrIdx
){
27237 // Returns a given option (or options).
27239 // If passed in as a string, that string is used to look up the option
27240 // in the array of options - based on the value property.
27241 // (See dijit/form/_FormSelectWidget.__SelectOption).
27243 // If passed in a number, then the option with the given index (0-based)
27244 // within this select will be returned.
27246 // If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
27247 // returned if and only if it exists within this select.
27249 // If passed an array, then an array will be returned with each element
27250 // in the array being looked up.
27252 // If not passed a value, then all options will be returned
27255 // The option corresponding with the given value or index. null
27256 // is returned if any of the following are true:
27258 // - A string value is passed in which doesn't exist
27259 // - An index is passed in which is outside the bounds of the array of options
27260 // - A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
27262 // NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
27263 // if the value property matches - NOT if the exact option exists
27264 // NOTE: if passing in an array, null elements will be placed in the returned
27265 // array when a value is not found.
27266 var lookupValue
= valueOrIdx
, opts
= this.options
|| [], l
= opts
.length
;
27268 if(lookupValue
=== undefined){
27269 return opts
; // __SelectOption[]
27271 if(lang
.isArray(lookupValue
)){
27272 return array
.map(lookupValue
, "return this.getOptions(item);", this); // __SelectOption[]
27274 if(lang
.isObject(valueOrIdx
)){
27275 // We were passed an option - so see if it's in our array (directly),
27276 // and if it's not, try and find it by value.
27277 if(!array
.some(this.options
, function(o
, idx
){
27278 if(o
=== lookupValue
||
27279 (o
.value
&& o
.value
=== lookupValue
.value
)){
27288 if(typeof lookupValue
== "string"){
27289 for(var i
=0; i
<l
; i
++){
27290 if(opts
[i
].value
=== lookupValue
){
27296 if(typeof lookupValue
== "number" && lookupValue
>= 0 && lookupValue
< l
){
27297 return this.options
[lookupValue
]; // __SelectOption
27299 return null; // null
27302 addOption: function(/*__SelectOption|__SelectOption[]*/ option
){
27304 // Adds an option or options to the end of the select. If value
27305 // of the option is empty or missing, a separator is created instead.
27306 // Passing in an array of options will yield slightly better performance
27307 // since the children are only loaded once.
27308 if(!lang
.isArray(option
)){ option
= [option
]; }
27309 array
.forEach(option
, function(i
){
27310 if(i
&& lang
.isObject(i
)){
27311 this.options
.push(i
);
27314 this._loadChildren();
27317 removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx
){
27319 // Removes the given option or options. You can remove by string
27320 // (in which case the value is removed), number (in which case the
27321 // index in the options array is removed), or select option (in
27322 // which case, the select option with a matching value is removed).
27323 // You can also pass in an array of those values for a slightly
27324 // better performance since the children are only loaded once.
27325 if(!lang
.isArray(valueOrIdx
)){ valueOrIdx
= [valueOrIdx
]; }
27326 var oldOpts
= this.getOptions(valueOrIdx
);
27327 array
.forEach(oldOpts
, function(i
){
27328 // We can get null back in our array - if our option was not found. In
27329 // that case, we don't want to blow up...
27331 this.options
= array
.filter(this.options
, function(node
){
27332 return (node
.value
!== i
.value
|| node
.label
!== i
.label
);
27334 this._removeOptionItem(i
);
27337 this._loadChildren();
27340 updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption
){
27342 // Updates the values of the given option. The option to update
27343 // is matched based on the value of the entered option. Passing
27344 // in an array of new options will yield better performance since
27345 // the children will only be loaded once.
27346 if(!lang
.isArray(newOption
)){ newOption
= [newOption
]; }
27347 array
.forEach(newOption
, function(i
){
27348 var oldOpt
= this.getOptions(i
), k
;
27350 for(k
in i
){ oldOpt
[k
] = i
[k
]; }
27353 this._loadChildren();
27356 setStore: function(store
,
27360 // Sets the store you would like to use with this select widget.
27361 // The selected value is the value of the new store to set. This
27362 // function returns the original store, in case you want to reuse
27363 // it or something.
27364 // store: dojo/store/api/Store
27365 // The dojo.store you would like to use - it MUST implement getIdentity()
27366 // and MAY implement observe().
27367 // For backwards-compatibility this can also be a data.data store, in which case
27368 // it MUST implement dojo/data/api/Identity,
27369 // and MAY implement dojo/data/api/Notification.
27370 // selectedValue: anything?
27371 // The value that this widget should set itself to *after* the store
27373 // fetchArgs: Object?
27374 // Hash of parameters to set filter on store, etc.
27376 // - query: new value for Select.query,
27377 // - queryOptions: new value for Select.queryOptions,
27378 // - onFetch: callback function for each item in data (Deprecated)
27379 var oStore
= this.store
;
27380 fetchArgs
= fetchArgs
|| {};
27382 if(oStore
!== store
){
27383 // Our store has changed, so cancel any listeners on old store (remove for 2.0)
27385 while((h
= this._notifyConnections
.pop())){ h
.remove(); }
27387 // For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
27389 lang
.mixin(store
, {
27393 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
27394 // Like dojo.store.DataStore.get() except returns native item.
27395 var deferred
= new Deferred();
27396 this.fetchItemByIdentity({
27398 onItem: function(object
){
27399 deferred
.resolve(object
);
27401 onError: function(error
){
27402 deferred
.reject(error
);
27405 return deferred
.promise
;
27407 query: function(query
, options
){
27409 // Queries the store for objects. Like dojo/store/DataStore.query()
27410 // except returned Deferred contains array of native items.
27411 var deferred
= new Deferred(function(){ if(fetchHandle
.abort
){ fetchHandle
.abort(); } } );
27412 deferred
.total
= new Deferred();
27413 var fetchHandle
= this.fetch(lang
.mixin({
27415 onBegin: function(count
){
27416 deferred
.total
.resolve(count
);
27418 onComplete: function(results
){
27419 deferred
.resolve(results
);
27421 onError: function(error
){
27422 deferred
.reject(error
);
27425 return new QueryResults(deferred
);
27429 if(store
.getFeatures()["dojo.data.api.Notification"]){
27430 this._notifyConnections
= [
27431 aspect
.after(store
, "onNew", lang
.hitch(this, "_onNewItem"), true),
27432 aspect
.after(store
, "onDelete", lang
.hitch(this, "_onDeleteItem"), true),
27433 aspect
.after(store
, "onSet", lang
.hitch(this, "_onSetItem"), true)
27437 this._set("store", store
); // Our store has changed, so update our notifications
27440 // Remove existing options (if there are any)
27441 if(this.options
&& this.options
.length
){
27442 this.removeOption(this.options
);
27445 // Cancel listener for updates to old store
27446 if(this._queryRes
&& this._queryRes
.close
){
27447 this._queryRes
.close();
27450 // If user has specified new query and query options along with this new store, then use them.
27451 if(fetchArgs
.query
){
27452 this._set("query", fetchArgs
.query
);
27453 this._set("queryOptions", fetchArgs
.queryOptions
);
27456 // Add our new options
27458 this._loadingStore
= true;
27459 this.onLoadDeferred
= new Deferred();
27462 // Save result in this._queryRes so we can cancel the listeners we register below
27463 this._queryRes
= store
.query(this.query
, this.queryOptions
);
27464 when(this._queryRes
, lang
.hitch(this, function(items
){
27466 if(this.sortByLabel
&& !fetchArgs
.sort
&& items
.length
){
27467 if(items
[0].getValue
){
27468 // Old dojo.data API to access items, remove for 2.0
27469 items
.sort(sorter
.createSortFunction([{
27470 attribute
: store
.getLabelAttributes(items
[0])[0]
27473 var labelAttr
= this.labelAttr
;
27474 items
.sort(function(a
, b
){
27475 return a
[labelAttr
] > b
[labelAttr
] ? 1 : b
[labelAttr
] > a
[labelAttr
] ? -1 : 0;
27480 if(fetchArgs
.onFetch
){
27481 items
= fetchArgs
.onFetch
.call(this, items
, fetchArgs
);
27484 // TODO: Add these guys as a batch, instead of separately
27485 array
.forEach(items
, function(i
){
27486 this._addOptionForItem(i
);
27489 // Register listener for store updates
27490 if(this._queryRes
.observe
){
27491 this._queryRes
.observe(lang
.hitch(this, function(object
, deletedFrom
, insertedInto
){
27492 if(deletedFrom
== insertedInto
){
27493 this._onSetItem(object
);
27495 if(deletedFrom
!= -1){
27496 this._onDeleteItem(object
);
27498 if(insertedInto
!= -1){
27499 this._onNewItem(object
);
27505 // Set our value (which might be undefined), and then tweak
27506 // it to send a change event with the real value
27507 this._loadingStore
= false;
27508 this.set("value", "_pendingValue" in this ? this._pendingValue
: selectedValue
);
27509 delete this._pendingValue
;
27511 if(!this.loadChildrenOnOpen
){
27512 this._loadChildren();
27514 this._pseudoLoadChildren(items
);
27516 this.onLoadDeferred
.resolve(true);
27519 console
.error('dijit.form.Select: ' + err
.toString());
27520 this.onLoadDeferred
.reject(err
);
27523 return oStore
; // dojo/data/api/Identity
27526 // TODO: implement set() and watch() for store and query, although not sure how to handle
27527 // setting them individually rather than together (as in setStore() above)
27529 _setValueAttr: function(/*anything*/ newValue
, /*Boolean?*/ priorityChange
){
27531 // set the value of the widget.
27532 // If a string is passed, then we set our value from looking it up.
27533 if(!this._onChangeActive
){ priorityChange
= null; }
27534 if(this._loadingStore
){
27535 // Our store is loading - so save our value, and we'll set it when
27537 this._pendingValue
= newValue
;
27540 var opts
= this.getOptions() || [];
27541 if(!lang
.isArray(newValue
)){
27542 newValue
= [newValue
];
27544 array
.forEach(newValue
, function(i
, idx
){
27545 if(!lang
.isObject(i
)){
27548 if(typeof i
=== "string"){
27549 newValue
[idx
] = array
.filter(opts
, function(node
){
27550 return node
.value
=== i
;
27551 })[0] || {value
: "", label
: ""};
27555 // Make sure some sane default is set
27556 newValue
= array
.filter(newValue
, function(i
){ return i
&& i
.value
; });
27557 if(!this.multiple
&& (!newValue
[0] || !newValue
[0].value
) && opts
.length
){
27558 newValue
[0] = opts
[0];
27560 array
.forEach(opts
, function(i
){
27561 i
.selected
= array
.some(newValue
, function(v
){ return v
.value
=== i
.value
; });
27563 var val
= array
.map(newValue
, function(i
){ return i
.value
; }),
27564 disp
= array
.map(newValue
, function(i
){ return i
.label
; });
27566 if(typeof val
== "undefined" || typeof val
[0] == "undefined"){ return; } // not fully initialized yet or a failed value lookup
27567 this._setDisplay(this.multiple
? disp
: disp
[0]);
27568 this.inherited(arguments
, [ this.multiple
? val
: val
[0], priorityChange
]);
27569 this._updateSelection();
27572 _getDisplayedValueAttr: function(){
27574 // returns the displayed value of the widget
27575 var val
= this.get("value");
27576 if(!lang
.isArray(val
)){
27579 var ret
= array
.map(this.getOptions(val
), function(v
){
27580 if(v
&& "label" in v
){
27587 return this.multiple
? ret
: ret
[0];
27590 _loadChildren: function(){
27592 // Loads the children represented by this widget's options.
27593 // reset the menu to make it populatable on the next click
27594 if(this._loadingStore
){ return; }
27595 array
.forEach(this._getChildren(), function(child
){
27596 child
.destroyRecursive();
27598 // Add each menu item
27599 array
.forEach(this.options
, this._addOptionItem
, this);
27602 this._updateSelection();
27605 _updateSelection: function(){
27607 // Sets the "selected" class on the item for styling purposes
27608 this._set("value", this._getValueFromOpts());
27609 var val
= this.value
;
27610 if(!lang
.isArray(val
)){
27614 array
.forEach(this._getChildren(), function(child
){
27615 var isSelected
= array
.some(val
, function(v
){
27616 return child
.option
&& (v
=== child
.option
.value
);
27618 domClass
.toggle(child
.domNode
, this.baseClass
.replace(/\s+|$/g, "SelectedOption "), isSelected
);
27619 child
.domNode
.setAttribute("aria-selected", isSelected
? "true" : "false");
27624 _getValueFromOpts: function(){
27626 // Returns the value of the widget by reading the options for
27627 // the selected flag
27628 var opts
= this.getOptions() || [];
27629 if(!this.multiple
&& opts
.length
){
27630 // Mirror what a select does - choose the first one
27631 var opt
= array
.filter(opts
, function(i
){
27634 if(opt
&& opt
.value
){
27637 opts
[0].selected
= true;
27638 return opts
[0].value
;
27640 }else if(this.multiple
){
27641 // Set value to be the sum of all selected
27642 return array
.map(array
.filter(opts
, function(i
){
27651 // Internal functions to call when we have store notifications come in
27652 _onNewItem: function(/*item*/ item
, /*Object?*/ parentInfo
){
27653 if(!parentInfo
|| !parentInfo
.parent
){
27654 // Only add it if we are top-level
27655 this._addOptionForItem(item
);
27658 _onDeleteItem: function(/*item*/ item
){
27659 var store
= this.store
;
27660 this.removeOption(store
.getIdentity(item
));
27662 _onSetItem: function(/*item*/ item
){
27663 this.updateOption(this._getOptionObjForItem(item
));
27666 _getOptionObjForItem: function(item
){
27668 // Returns an option object based off the given item. The "value"
27669 // of the option item will be the identity of the item, the "label"
27670 // of the option will be the label of the item.
27672 // remove getLabel() call for 2.0 (it's to support the old dojo.data API)
27673 var store
= this.store
,
27674 label
= (this.labelAttr
&& this.labelAttr
in item
) ? item
[this.labelAttr
] : store
.getLabel(item
),
27675 value
= (label
? store
.getIdentity(item
) : null);
27676 return {value
: value
, label
: label
, item
: item
}; // __SelectOption
27679 _addOptionForItem: function(/*item*/ item
){
27681 // Creates (and adds) the option for the given item
27682 var store
= this.store
;
27683 if(store
.isItemLoaded
&& !store
.isItemLoaded(item
)){
27684 // We are not loaded - so let's load it and add later.
27685 // Remove for 2.0 (it's the old dojo.data API)
27686 store
.loadItem({item
: item
, onItem: function(i
){
27687 this._addOptionForItem(i
);
27692 var newOpt
= this._getOptionObjForItem(item
);
27693 this.addOption(newOpt
);
27696 constructor: function(params
/*===== , srcNodeRef =====*/){
27698 // Create the widget.
27699 // params: Object|null
27700 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
27701 // and functions, typically callbacks like onClick.
27702 // The hash can contain any of the widget's properties, excluding read-only properties.
27703 // srcNodeRef: DOMNode|String?
27704 // If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
27706 // Saves off our value, if we have an initial one set so we
27707 // can use it if we have a store as well (see startup())
27708 this._oValue
= (params
|| {}).value
|| null;
27709 this._notifyConnections
= []; // remove for 2.0
27712 buildRendering: function(){
27713 this.inherited(arguments
);
27714 dom
.setSelectable(this.focusNode
, false);
27717 _fillContent: function(){
27719 // Loads our options and sets up our dropdown correctly. We
27720 // don't want any content, so we don't call any inherit chain
27725 ? query("> *", this.srcNodeRef
).map(
27727 if(node
.getAttribute("type") === "separator"){
27728 return { value
: "", label
: "", selected
: false, disabled
: false };
27731 value
: (node
.getAttribute("data-" + kernel
._scopeName
+ "-value") || node
.getAttribute("value")),
27732 label
: String(node
.innerHTML
),
27733 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
27734 // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
27735 // decide before 1.6
27736 selected
: node
.getAttribute("selected") || false,
27737 disabled
: node
.getAttribute("disabled") || false
27744 this._set("value", this._getValueFromOpts());
27745 }else if(this.multiple
&& typeof this.value
== "string"){
27746 this._set("value", this.value
.split(","));
27750 postCreate: function(){
27752 // sets up our event handling that we need for functioning
27754 this.inherited(arguments
);
27756 // Make our event connections for updating state
27757 this.connect(this, "onChange", "_updateSelection");
27759 // moved from startup
27760 // Connects in our store, if we have one defined
27761 var store
= this.store
;
27762 if(store
&& (store
.getIdentity
|| store
.getFeatures()["dojo.data.api.Identity"])){
27763 // Temporarily set our store to null so that it will get set
27764 // and connected appropriately
27766 this.setStore(store
, this._oValue
);
27770 startup: function(){
27772 this._loadChildren();
27773 this.inherited(arguments
);
27776 destroy: function(){
27778 // Clean up our connections
27781 while((h
= this._notifyConnections
.pop())){ h
.remove(); }
27783 // Cancel listener for store updates
27784 if(this._queryRes
&& this._queryRes
.close
){
27785 this._queryRes
.close();
27788 this.inherited(arguments
);
27791 _addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
27793 // User-overridable function which, for the given option, adds an
27794 // item to the select. If the option doesn't have a value, then a
27795 // separator is added in that place. Make sure to store the option
27796 // in the created option widget.
27799 _removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
27801 // User-overridable function which, for the given option, removes
27802 // its item from the select.
27805 _setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
27807 // Overridable function which will set the display for the
27808 // widget. newDisplay is either a string (in the case of
27809 // single selects) or array of strings (in the case of multi-selects)
27812 _getChildren: function(){
27814 // Overridable function to return the children that this widget contains.
27818 _getSelectedOptionsAttr: function(){
27820 // hooks into this.attr to provide a mechanism for getting the
27821 // option items for the current value of the widget.
27822 return this.getOptions(this.get("value"));
27825 _pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
27827 // a function that will "fake" loading children, if needed, and
27828 // if we have set to not load children until the widget opens.
27830 // An array of items that will be loaded, when needed
27833 onSetStore: function(){
27835 // a function that can be connected to in order to receive a
27836 // notification that the store has finished loading and all options
27837 // from that store are available
27842 _FormSelectWidget.__SelectOption = __SelectOption;
27845 return _FormSelectWidget
;
27850 'dijit/form/Select':function(){
27852 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" 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"}});
27853 define("dijit/form/Select", [
27854 "dojo/_base/array", // array.forEach
27855 "dojo/_base/declare", // declare
27856 "dojo/dom-attr", // domAttr.set
27857 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
27858 "dojo/dom-geometry", // domGeometry.setMarginBox
27859 "dojo/_base/event", // event.stop
27860 "dojo/i18n", // i18n.getLocalization
27861 "dojo/_base/lang", // lang.hitch
27862 "dojo/sniff", // has("ie")
27863 "./_FormSelectWidget",
27867 "../MenuSeparator",
27869 "dojo/text!./templates/Select.html",
27870 "dojo/i18n!./nls/validate"
27871 ], function(array
, declare
, domAttr
, domClass
, domGeometry
, event
, i18n
, lang
, has
,
27872 _FormSelectWidget
, _HasDropDown
, Menu
, MenuItem
, MenuSeparator
, Tooltip
, template
){
27875 // dijit/form/Select
27878 var _SelectMenu
= declare("dijit.form._SelectMenu", Menu
, {
27880 // An internally-used menu for dropdown that allows us a vertical scrollbar
27882 // Override Menu.autoFocus setting so that opening a Select highlights the current value.
27885 buildRendering: function(){
27887 // Stub in our own changes, so that our domNode is not a table
27888 // otherwise, we won't respond correctly to heights/overflows
27889 this.inherited(arguments
);
27890 var o
= (this.menuTableNode
= this.domNode
);
27891 var n
= (this.domNode
= this.ownerDocument
.createElement("div"));
27892 n
.style
.cssText
= "overflow-x: hidden; overflow-y: scroll";
27894 o
.parentNode
.replaceChild(n
, o
);
27896 domClass
.remove(o
, "dijitMenuTable");
27897 n
.className
= o
.className
+ " dijitSelectMenu";
27898 o
.className
= "dijitReset dijitMenuTable";
27899 o
.setAttribute("role", "listbox");
27900 n
.setAttribute("role", "presentation");
27904 postCreate: function(){
27906 // stop mousemove from selecting text on IE to be consistent with other browsers
27908 this.inherited(arguments
);
27910 this.connect(this.domNode
, "onselectstart", event
.stop
);
27916 // Overridden so that the previously selected value will be focused instead of only the first item
27918 val
= this.parentWidget
.value
;
27919 if(lang
.isArray(val
)){
27920 val
= val
[val
.length
-1];
27922 if(val
){ // if focus selected
27923 array
.forEach(this.parentWidget
._getChildren(), function(child
){
27924 if(child
.option
&& (val
=== child
.option
.value
)){ // find menu item widget with this value
27926 this.focusChild(child
, false); // focus previous selection
27931 this.inherited(arguments
); // focus first item by default
27935 resize: function(/*Object*/ mb
){
27937 // Overridden so that we are able to handle resizing our
27938 // internal widget. Note that this is not a "full" resize
27939 // implementation - it only works correctly if you pass it a
27943 // The margin box to set this dropdown to.
27945 domGeometry
.setMarginBox(this.domNode
, mb
);
27947 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
27948 // 100% is safer than a pixel value because there may be a scroll bar with
27949 // browser/OS specific width.
27950 this.menuTableNode
.style
.width
= "100%";
27956 var Select
= declare("dijit.form.Select", [_FormSelectWidget
, _HasDropDown
], {
27958 // This is a "styleable" select box - it is basically a DropDownButton which
27959 // can take a `<select>` as its input.
27961 baseClass
: "dijitSelect dijitValidationTextBox",
27963 templateString
: template
,
27965 _buttonInputDisabled
: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
27967 // required: Boolean
27968 // Can be true or false, default is false.
27971 // state: [readonly] String
27972 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
27976 // Currently displayed error/prompt message
27979 // tooltipPosition: String[]
27980 // See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
27981 tooltipPosition
: [],
27983 // emptyLabel: string
27984 // What to display in an "empty" dropdown
27985 emptyLabel
: " ", //
27987 // _isLoaded: Boolean
27988 // Whether or not we have been loaded
27991 // _childrenLoaded: Boolean
27992 // Whether or not our children have been loaded
27993 _childrenLoaded
: false,
27995 _fillContent: function(){
27997 // Set the value to be the first, or the selected index
27998 this.inherited(arguments
);
27999 // set value from selected option
28000 if(this.options
.length
&& !this.value
&& this.srcNodeRef
){
28001 var si
= this.srcNodeRef
.selectedIndex
|| 0; // || 0 needed for when srcNodeRef is not a SELECT
28002 this.value
= this.options
[si
>= 0 ? si
: 0].value
;
28004 // Create the dropDown widget
28005 this.dropDown
= new _SelectMenu({ id
: this.id
+ "_menu", parentWidget
: this });
28006 domClass
.add(this.dropDown
.domNode
, this.baseClass
.replace(/\s+|$/g, "Menu "));
28009 _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option
){
28011 // For the given option, return the menu item that should be
28012 // used to display it. This can be overridden as needed
28013 if(!option
.value
&& !option
.label
){
28014 // We are a separator (no label set for it)
28015 return new MenuSeparator({ownerDocument
: this.ownerDocument
});
28017 // Just a regular menu option
28018 var click
= lang
.hitch(this, "_setValueAttr", option
);
28019 var item
= new MenuItem({
28021 label
: option
.label
|| this.emptyLabel
,
28023 ownerDocument
: this.ownerDocument
,
28025 disabled
: option
.disabled
|| false
28027 item
.focusNode
.setAttribute("role", "option");
28032 _addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option
){
28034 // For the given option, add an option to our dropdown.
28035 // If the option doesn't have a value, then a separator is added
28038 this.dropDown
.addChild(this._getMenuItemForOption(option
));
28042 _getChildren: function(){
28043 if(!this.dropDown
){
28046 return this.dropDown
.getChildren();
28049 _loadChildren: function(/*Boolean*/ loadMenuItems
){
28051 // Resets the menu and the length attribute of the button - and
28052 // ensures that the label is appropriately set.
28053 // loadMenuItems: Boolean
28054 // actually loads the child menu items - we only do this when we are
28055 // populating for showing the dropdown.
28057 if(loadMenuItems
=== true){
28058 // this.inherited destroys this.dropDown's child widgets (MenuItems).
28059 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
28060 // issues later in _setSelected). (see #10296)
28062 delete this.dropDown
.focusedChild
;
28064 if(this.options
.length
){
28065 this.inherited(arguments
);
28067 // Drop down menu is blank but add one blank entry just so something appears on the screen
28068 // to let users know that they are no choices (mimicing native select behavior)
28069 array
.forEach(this._getChildren(), function(child
){ child
.destroyRecursive(); });
28070 var item
= new MenuItem({
28071 ownerDocument
: this.ownerDocument
,
28072 label
: this.emptyLabel
28074 this.dropDown
.addChild(item
);
28077 this._updateSelection();
28080 this._isLoaded
= false;
28081 this._childrenLoaded
= true;
28083 if(!this._loadingStore
){
28084 // Don't call this if we are loading - since we will handle it later
28085 this._setValueAttr(this.value
, false);
28089 _refreshState: function(){
28091 this.validate(this.focused
);
28095 startup: function(){
28096 this.inherited(arguments
);
28097 this._refreshState(); // after all _set* methods have run
28100 _setValueAttr: function(value
){
28101 this.inherited(arguments
);
28102 domAttr
.set(this.valueNode
, "value", this.get("value"));
28103 this._refreshState(); // to update this.state
28106 _setDisabledAttr: function(/*Boolean*/ value
){
28107 this.inherited(arguments
);
28108 this._refreshState(); // to update this.state
28111 _setRequiredAttr: function(/*Boolean*/ value
){
28112 this._set("required", value
);
28113 this.focusNode
.setAttribute("aria-required", value
);
28114 this._refreshState(); // to update this.state
28117 _setOptionsAttr: function(/*Array*/ options
){
28118 this._isLoaded
= false;
28119 this._set('options', options
);
28122 _setDisplay: function(/*String*/ newDisplay
){
28124 // sets the display for the given value (or values)
28125 var lbl
= newDisplay
|| this.emptyLabel
;
28126 this.containerNode
.innerHTML
= '<span role="option" class="dijitReset dijitInline ' + this.baseClass
.replace(/\s+|$/g, "Label ")+'">' + lbl
+ '</span>';
28129 validate: function(/*Boolean*/ isFocused
){
28131 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
28133 // Show missing or invalid messages if appropriate, and highlight textbox field.
28134 // Used when a select is initially set to no value and the user is required to
28137 var isValid
= this.disabled
|| this.isValid(isFocused
);
28138 this._set("state", isValid
? "" : (this._hasBeenBlurred
? "Error" : "Incomplete"));
28139 this.focusNode
.setAttribute("aria-invalid", isValid
? "false" : "true");
28140 var message
= isValid
? "" : this._missingMsg
;
28141 if(message
&& this.focused
&& this._hasBeenBlurred
){
28142 Tooltip
.show(message
, this.domNode
, this.tooltipPosition
, !this.isLeftToRight());
28144 Tooltip
.hide(this.domNode
);
28146 this._set("message", message
);
28150 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
28152 // Whether or not this is a valid value. The only way a Select
28153 // can be invalid is when it's required but nothing is selected.
28154 return (!this.required
|| this.value
=== 0 || !(/^\s*$/.test(this.value
|| ""))); // handle value is null or undefined
28159 // Overridden so that the state will be cleared.
28160 this.inherited(arguments
);
28161 Tooltip
.hide(this.domNode
);
28162 this._refreshState(); // to update this.state
28165 postMixInProperties: function(){
28167 // set the missing message
28168 this.inherited(arguments
);
28169 this._missingMsg
= i18n
.getLocalization("dijit.form", "validate", this.lang
).missingMessage
;
28172 postCreate: function(){
28174 // stop mousemove from selecting text on IE to be consistent with other browsers
28176 this.inherited(arguments
);
28178 this.connect(this.domNode
, "onselectstart", event
.stop
);
28179 this.domNode
.setAttribute("aria-expanded", "false");
28182 // IE INPUT tag fontFamily has to be set directly using STYLE
28183 // the defer gives IE a chance to render the TextBox and to deal with font inheritance
28184 this.defer(function(){
28186 var s
= domStyle
.getComputedStyle(this.domNode
); // can throw an exception if widget is immediately destroyed
28188 var ff
= s
.fontFamily
;
28190 var inputs
= this.domNode
.getElementsByTagName("INPUT");
28192 for(var i
=0; i
< inputs
.length
; i
++){
28193 inputs
[i
].style
.fontFamily
= ff
;
28198 }catch(e
){/*when used in a Dialog, and this is called before the dialog is
28199 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
28204 _setStyleAttr: function(/*String||Object*/ value
){
28205 this.inherited(arguments
);
28206 domClass
.toggle(this.domNode
, this.baseClass
.replace(/\s+|$/g, "FixedWidth "), !!this.domNode
.style
.width
);
28209 isLoaded: function(){
28210 return this._isLoaded
;
28213 loadDropDown: function(/*Function*/ loadCallback
){
28215 // populates the menu
28216 this._loadChildren(true);
28217 this._isLoaded
= true;
28221 closeDropDown: function(){
28222 // overriding _HasDropDown.closeDropDown()
28223 this.inherited(arguments
);
28225 if(this.dropDown
&& this.dropDown
.menuTableNode
){
28226 // Erase possible width: 100% setting from _SelectMenu.resize().
28227 // Leaving it would interfere with the next openDropDown() call, which
28228 // queries the natural size of the drop down.
28229 this.dropDown
.menuTableNode
.style
.width
= "";
28233 destroy: function(preserveDom
){
28234 if(this.dropDown
&& !this.dropDown
._destroyed
){
28235 this.dropDown
.destroyRecursive(preserveDom
);
28236 delete this.dropDown
;
28238 this.inherited(arguments
);
28241 _onFocus: function(){
28242 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
28243 this.inherited(arguments
);
28246 _onBlur: function(){
28247 Tooltip
.hide(this.domNode
);
28248 this.inherited(arguments
);
28249 this.validate(false);
28253 Select
._Menu
= _SelectMenu
; // for monkey patching
28259 'dojo/store/util/QueryResults':function(){
28260 define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
28261 ], function(array
, lang
, Deferred
){
28264 // dojo/store/util/QueryResults
28266 var QueryResults = function(results
){
28268 // A function that wraps the results of a store query with additional
28271 // QueryResults is a basic wrapper that allows for array-like iteration
28272 // over any kind of returned data from a query. While the simplest store
28273 // will return a plain array of data, other stores may return deferreds or
28274 // promises; this wrapper makes sure that *all* results can be treated
28277 // Additional methods include `forEach`, `filter` and `map`.
28278 // results: Array|dojo/promise/Promise
28279 // The result set as an array, or a promise for an array.
28281 // An array-like object that can be used for iterating over.
28283 // Query a store and iterate over the results.
28285 // | store.query({ prime: true }).forEach(function(item){
28286 // | // do something
28292 // if it is a promise it may be frozen
28294 results
= lang
.delegate(results
);
28296 function addIterativeMethod(method
){
28297 if(!results
[method
]){
28298 results
[method
] = function(){
28299 var args
= arguments
;
28300 return Deferred
.when(results
, function(results
){
28301 Array
.prototype.unshift
.call(args
, results
);
28302 return QueryResults(array
[method
].apply(array
, args
));
28307 addIterativeMethod("forEach");
28308 addIterativeMethod("filter");
28309 addIterativeMethod("map");
28310 if(!results
.total
){
28311 results
.total
= Deferred
.when(results
, function(results
){
28312 return results
.length
;
28315 return results
; // Object
28318 lang
.setObject("dojo.store.util.QueryResults", QueryResults
);
28320 return QueryResults
;
28325 'dijit/form/_ListBase':function(){
28326 define("dijit/form/_ListBase", [
28327 "dojo/_base/declare", // declare
28329 "dojo/window" // winUtils.scrollIntoView
28330 ], function(declare
, on
, winUtils
){
28333 // dijit/form/_ListBase
28335 return declare( "dijit.form._ListBase", null, {
28337 // Focus-less menu to handle UI events consistently
28338 // Abstract methods that must be defined externally:
28340 // - onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
28341 // - onDeselect: cancels onSelect
28345 // selected: DOMNode
28346 // currently selected node
28349 _listConnect: function(/*String|Function*/ eventType
, /*String*/ callbackFuncName
){
28351 // Connects 'containerNode' to specified method of this object
28352 // and automatically registers for 'disconnect' on widget destroy.
28354 // Provide widget-specific analog to 'connect'.
28355 // The callback function is called with the normal event object,
28356 // but also a second parameter is passed that indicates which list item
28357 // actually received the event.
28359 // A handle that can be passed to `disconnect` in order to disconnect
28360 // before the widget is destroyed.
28365 return self
.own(on(self
.containerNode
,
28367 function(eventTarget
, selector
, target
){
28368 return eventTarget
.parentNode
== target
;
28373 evt
.preventDefault();
28374 self
[callbackFuncName
](evt
, this);
28379 selectFirstNode: function(){
28381 // Select the first displayed item in the list.
28382 var first
= this.containerNode
.firstChild
;
28383 while(first
&& first
.style
.display
== "none"){
28384 first
= first
.nextSibling
;
28386 this._setSelectedAttr(first
);
28389 selectLastNode: function(){
28391 // Select the last displayed item in the list
28392 var last
= this.containerNode
.lastChild
;
28393 while(last
&& last
.style
.display
== "none"){
28394 last
= last
.previousSibling
;
28396 this._setSelectedAttr(last
);
28399 selectNextNode: function(){
28401 // Select the item just below the current selection.
28402 // If nothing selected, select first node.
28403 var selectedNode
= this.selected
;
28405 this.selectFirstNode();
28407 var next
= selectedNode
.nextSibling
;
28408 while(next
&& next
.style
.display
== "none"){
28409 next
= next
.nextSibling
;
28412 this.selectFirstNode();
28414 this._setSelectedAttr(next
);
28419 selectPreviousNode: function(){
28421 // Select the item just above the current selection.
28422 // If nothing selected, select last node (if
28423 // you select Previous and try to keep scrolling up the list).
28424 var selectedNode
= this.selected
;
28426 this.selectLastNode();
28428 var prev
= selectedNode
.previousSibling
;
28429 while(prev
&& prev
.style
.display
== "none"){
28430 prev
= prev
.previousSibling
;
28433 this.selectLastNode();
28435 this._setSelectedAttr(prev
);
28440 _setSelectedAttr: function(/*DomNode*/ node
){
28442 // Does the actual select.
28443 if(this.selected
!= node
){
28444 var selectedNode
= this.selected
;
28446 this.onDeselect(selectedNode
);
28447 this.selected
= null;
28450 this.selected
= node
;
28451 winUtils
.scrollIntoView(node
);
28452 this.onSelect(node
);
28455 this.onSelect(node
);
28463 'dijit/form/_FormWidget':function(){
28464 define("dijit/form/_FormWidget", [
28465 "dojo/_base/declare", // declare
28466 "dojo/has", // has("dijit-legacy-requires")
28467 "dojo/_base/kernel", // kernel.deprecated
28470 "../_CssStateMixin",
28471 "../_TemplatedMixin",
28472 "./_FormWidgetMixin"
28473 ], function(declare
, has
, kernel
, ready
, _Widget
, _CssStateMixin
, _TemplatedMixin
, _FormWidgetMixin
){
28477 // dijit/form/_FormWidget
28479 // Back compat w/1.6, remove for 2.0
28480 if(has("dijit-legacy-requires")){
28481 ready(0, function(){
28482 var requires
= ["dijit/form/_FormValueWidget"];
28483 require(requires
); // use indirection so modules not rolled into a build
28487 return declare("dijit.form._FormWidget", [_Widget
, _TemplatedMixin
, _CssStateMixin
, _FormWidgetMixin
], {
28489 // Base class for widgets corresponding to native HTML elements such as `<checkbox>` or `<button>`,
28490 // which can be children of a `<form>` node or a `dijit/form/Form` widget.
28493 // Represents a single HTML element.
28494 // All these widgets should have these attributes just like native HTML input elements.
28495 // You can set them during widget construction or afterwards, via `dijit/_WidgetBase.set()`.
28497 // They also share some common methods.
28499 setDisabled: function(/*Boolean*/ disabled
){
28501 // Deprecated. Use set('disabled', ...) instead.
28502 kernel
.deprecated("setDisabled("+disabled
+") is deprecated. Use set('disabled',"+disabled
+") instead.", "", "2.0");
28503 this.set('disabled', disabled
);
28506 setValue: function(/*String*/ value
){
28508 // Deprecated. Use set('value', ...) instead.
28509 kernel
.deprecated("dijit.form._FormWidget:setValue("+value
+") is deprecated. Use set('value',"+value
+") instead.", "", "2.0");
28510 this.set('value', value
);
28513 getValue: function(){
28515 // Deprecated. Use get('value') instead.
28516 kernel
.deprecated(this.declaredClass
+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
28517 return this.get('value');
28520 postMixInProperties: function(){
28521 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
28522 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
28523 // Regarding escaping, see heading "Attribute values" in
28524 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
28525 this.nameAttrSetting
= this.name
? ('name="' + this.name
.replace(/"/g, ""
;") + '"') : '';
28526 this.inherited(arguments);
28529 // Override automatic assigning type --> focusNode, it causes exception on IE.
28530 // Instead, type must be specified as ${type} in the template, as part of the original DOM
28537 'dojo
/DeferredList
':function(){
28538 define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
28540 // dojo/DeferredList
28543 dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
28545 // Deprecated, use dojo/promise/all instead.
28546 // Provides event handling for a group of Deferred objects.
28548 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
28549 // this new deferred will typically have its callback fired when all of the deferreds in
28550 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
28551 // fireOnOneErrback, will fire before all the deferreds as appropriate
28553 // The list of deferreds to be synchronizied with this DeferredList
28554 // fireOnOneCallback:
28555 // Will cause the DeferredLists callback to be fired as soon as any
28556 // of the deferreds in its list have been fired instead of waiting until
28557 // the entire list has finished
28558 // fireonOneErrback:
28559 // Will cause the errback to fire upon any of the deferreds errback
28561 // A deferred canceller function, see dojo.Deferred
28562 var resultList = [];
28563 Deferred.call(this);
28565 if(list.length === 0 && !fireOnOneCallback){
28566 this.resolve([0, []]);
28569 darray.forEach(list, function(item, i){
28570 item.then(function(result){
28571 if(fireOnOneCallback){
28572 self.resolve([i, result]);
28574 addResult(true, result);
28577 if(fireOnOneErrback){
28578 self.reject(error);
28580 addResult(false, error);
28587 function addResult(succeeded, result){
28588 resultList[i] = [succeeded, result];
28590 if(finished === list.length){
28591 self.resolve(resultList);
28597 dojo.DeferredList.prototype = new Deferred();
28599 dojo.DeferredList.prototype.gatherResults = function(deferredList){
28601 // Gathers the results of the deferreds for packaging
28602 // as the parameters to the Deferred Lists' callback
28603 // deferredList: dojo/DeferredList
28604 // The deferred list from which this function gathers results.
28605 // returns: dojo/DeferredList
28606 // The newly created deferred list which packs results as
28607 // parameters to its callback.
28609 var d
= new dojo
.DeferredList(deferredList
, false, true, false);
28610 d
.addCallback(function(results
){
28612 darray
.forEach(results
, function(result
){
28613 ret
.push(result
[1]);
28620 return dojo
.DeferredList
;
28624 'dojo/dnd/common':function(){
28625 define("dojo/dnd/common", ["../_base/connect", "../_base/kernel", "../_base/lang", "../dom"],
28626 function(connect
, kernel
, lang
, dom
){
28631 var exports
= lang
.getObject("dojo.dnd", true);
28633 // TODO: for 2.0, replace line above with this code.
28640 exports
.getCopyKeyState
= connect
.isCopyKey
;
28642 exports
._uniqueId
= 0;
28643 exports
.getUniqueId = function(){
28645 // returns a unique string for use with any DOM element
28648 id
= kernel
._scopeName
+ "Unique" + (++exports
._uniqueId
);
28649 }while(dom
.byId(id
));
28653 exports
._empty
= {};
28655 exports
.isFormElement = function(/*Event*/ e
){
28657 // returns true if user clicked on a form element
28659 if(t
.nodeType
== 3 /*TEXT_NODE*/){
28662 return " button textarea input select option ".indexOf(" " + t
.tagName
.toLowerCase() + " ") >= 0; // Boolean
28669 'dijit/Viewport':function(){
28670 define("dijit/Viewport", [
28675 "dojo/_base/window", // global
28676 "dojo/window" // getBox()
28677 ], function(Evented
, on
, ready
, has
, win
, winUtils
){
28685 // Utility singleton to watch for viewport resizes, avoiding duplicate notifications
28686 // which can lead to infinite loops.
28688 // Usage: Viewport.on("resize", myCallback).
28690 // myCallback() is called without arguments in case it's _WidgetBase.resize(),
28691 // which would interpret the argument as the size to make the widget.
28695 var Viewport
= new Evented();
28697 ready(200, function(){
28698 var oldBox
= winUtils
.getBox();
28699 Viewport
._rlh
= on(win
.global
, "resize", function(){
28700 var newBox
= winUtils
.getBox();
28701 if(oldBox
.h
== newBox
.h
&& oldBox
.w
== newBox
.w
){ return; }
28703 Viewport
.emit("resize");
28706 // Also catch zoom changes on IE8, since they don't naturally generate resize events
28707 if(has("ie") == 8){
28708 var deviceXDPI
= screen
.deviceXDPI
;
28709 setInterval(function(){
28710 if(screen
.deviceXDPI
!= deviceXDPI
){
28711 deviceXDPI
= screen
.deviceXDPI
;
28712 Viewport
.emit("resize");
28722 'dijit/_base/place':function(){
28723 define("dijit/_base/place", [
28724 "dojo/_base/array", // array.forEach
28725 "dojo/_base/lang", // lang.isArray, lang.mixin
28726 "dojo/window", // windowUtils.getBox
28728 "../main" // export to dijit namespace
28729 ], function(array
, lang
, windowUtils
, place
, dijit
){
28732 // dijit/_base/place
28737 // Deprecated back compatibility module, new code should use dijit/place directly instead of using this module.
28740 exports
.getViewport = function(){
28742 // Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
28743 // New code should use windowUtils.getBox()
28745 return windowUtils
.getBox();
28748 exports
.placeOnScreen
= place
.at
;
28750 exports
.placeOnScreenAroundElement = function(node
, aroundNode
, aroundCorners
, layoutNode
){
28752 // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
28753 // for the "around" argument and finds a proper processor to place a node.
28754 // Deprecated, new code should use dijit/place.around() instead.
28756 // Convert old style {"BL": "TL", "BR": "TR"} type argument
28757 // to style needed by dijit.place code:
28759 // {aroundCorner: "BL", corner: "TL" },
28760 // {aroundCorner: "BR", corner: "TR" }
28763 if(lang
.isArray(aroundCorners
)){
28764 positions
= aroundCorners
;
28767 for(var key
in aroundCorners
){
28768 positions
.push({aroundCorner
: key
, corner
: aroundCorners
[key
]});
28772 return place
.around(node
, aroundNode
, positions
, true, layoutNode
);
28775 exports
.placeOnScreenAroundNode
= exports
.placeOnScreenAroundElement
;
28777 exports.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
28779 // Position node adjacent or kitty-corner to aroundNode
28780 // such that it's fully visible in viewport.
28781 // Deprecated, new code should use dijit/place.around() instead.
28785 exports
.placeOnScreenAroundRectangle
= exports
.placeOnScreenAroundElement
;
28787 exports.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, layoutNode){
28789 // Like dijit.placeOnScreenAroundNode(), except that the "around"
28790 // parameter is an arbitrary rectangle on the screen (x, y, width, height)
28791 // instead of a dom node.
28792 // Deprecated, new code should use dijit/place.around() instead.
28796 exports
.getPopupAroundAlignment = function(/*Array*/ position
, /*Boolean*/ leftToRight
){
28798 // Deprecated method, unneeded when using dijit/place directly.
28799 // Transforms the passed array of preferred positions into a format suitable for
28800 // passing as the aroundCorners argument to dijit/place.placeOnScreenAroundElement.
28801 // position: String[]
28802 // This variable controls the position of the drop down.
28803 // It's an array of strings with the following values:
28805 // - before: places drop down to the left of the target node/widget, or to the right in
28806 // the case of RTL scripts like Hebrew and Arabic
28807 // - after: places drop down to the right of the target node/widget, or to the left in
28808 // the case of RTL scripts like Hebrew and Arabic
28809 // - above: drop down goes above target node
28810 // - below: drop down goes below target node
28812 // The list is positions is tried, in order, until a position is found where the drop down fits
28813 // within the viewport.
28814 // leftToRight: Boolean
28815 // Whether the popup will be displaying in leftToRight mode.
28818 array
.forEach(position
, function(pos
){
28819 var ltr
= leftToRight
;
28822 align
[leftToRight
? "BR" : "BL"] = leftToRight
? "BL" : "BR";
28825 align
[leftToRight
? "BL" : "BR"] = leftToRight
? "BR" : "BL";
28831 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
28832 align
[ltr
? "BL" : "BR"] = ltr
? "TL" : "TR";
28833 align
[ltr
? "BR" : "BL"] = ltr
? "TR" : "TL";
28840 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
28841 align
[ltr
? "TL" : "TR"] = ltr
? "BL" : "BR";
28842 align
[ltr
? "TR" : "TL"] = ltr
? "BR" : "BL";
28849 lang
.mixin(dijit
, exports
);
28851 /*===== return exports; =====*/
28852 return dijit
; // for back compat :-(
28856 'dijit/MenuSeparator':function(){
28858 'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>"}});
28859 define("dijit/MenuSeparator", [
28860 "dojo/_base/declare", // declare
28861 "dojo/dom", // dom.setSelectable
28863 "./_TemplatedMixin",
28865 "dojo/text!./templates/MenuSeparator.html"
28866 ], function(declare
, dom
, _WidgetBase
, _TemplatedMixin
, _Contained
, template
){
28869 // dijit/MenuSeparator
28871 return declare("dijit.MenuSeparator", [_WidgetBase
, _TemplatedMixin
, _Contained
], {
28873 // A line between two menu items
28875 templateString
: template
,
28877 buildRendering: function(){
28878 this.inherited(arguments
);
28879 dom
.setSelectable(this.domNode
, false);
28882 isFocusable: function(){
28884 // Override to always return false
28888 return false; // Boolean
28894 'dijit/form/_ComboBoxMenu':function(){
28895 define("dijit/form/_ComboBoxMenu", [
28896 "dojo/_base/declare", // declare
28897 "dojo/dom-class", // domClass.add domClass.remove
28898 "dojo/dom-style", // domStyle.get
28899 "dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
28901 "../_TemplatedMixin",
28902 "./_ComboBoxMenuMixin",
28903 "./_ListMouseMixin"
28904 ], function(declare
, domClass
, domStyle
, keys
,
28905 _WidgetBase
, _TemplatedMixin
, _ComboBoxMenuMixin
, _ListMouseMixin
){
28909 // dijit/form/_ComboBoxMenu
28911 return declare("dijit.form._ComboBoxMenu",[_WidgetBase
, _TemplatedMixin
, _ListMouseMixin
, _ComboBoxMenuMixin
], {
28913 // Focus-less menu for internal use in `dijit/form/ComboBox`
28914 // Abstract methods that must be defined externally:
28916 // - onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
28917 // - onPage: next(1) or previous(-1) button pressed
28921 templateString
: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;' role='listbox'>"
28922 +"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
28923 +"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
28926 baseClass
: "dijitComboBoxMenu",
28928 postCreate: function(){
28929 this.inherited(arguments
);
28930 if(!this.isLeftToRight()){
28931 domClass
.add(this.previousButton
, "dijitMenuItemRtl");
28932 domClass
.add(this.nextButton
, "dijitMenuItemRtl");
28936 _createMenuItem: function(){
28937 // note: not using domConstruct.create() because need to specify document
28938 var item
= this.ownerDocument
.createElement("div");
28939 item
.className
= "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl");
28940 item
.setAttribute("role", "option");
28944 onHover: function(/*DomNode*/ node
){
28947 domClass
.add(node
, "dijitMenuItemHover");
28950 onUnhover: function(/*DomNode*/ node
){
28952 // Remove hover CSS
28953 domClass
.remove(node
, "dijitMenuItemHover");
28956 onSelect: function(/*DomNode*/ node
){
28958 // Add selected CSS
28959 domClass
.add(node
, "dijitMenuItemSelected");
28962 onDeselect: function(/*DomNode*/ node
){
28964 // Remove selected CSS
28965 domClass
.remove(node
, "dijitMenuItemSelected");
28968 _page: function(/*Boolean*/ up
){
28970 // Handles page-up and page-down keypresses
28972 var scrollamount
= 0;
28973 var oldscroll
= this.domNode
.scrollTop
;
28974 var height
= domStyle
.get(this.domNode
, "height");
28975 // if no item is highlighted, highlight the first option
28976 if(!this.getHighlightedOption()){
28977 this.selectNextNode();
28979 while(scrollamount
<height
){
28980 var highlighted_option
= this.getHighlightedOption();
28982 // stop at option 1
28983 if(!highlighted_option
.previousSibling
||
28984 highlighted_option
.previousSibling
.style
.display
== "none"){
28987 this.selectPreviousNode();
28989 // stop at last option
28990 if(!highlighted_option
.nextSibling
||
28991 highlighted_option
.nextSibling
.style
.display
== "none"){
28994 this.selectNextNode();
28997 var newscroll
= this.domNode
.scrollTop
;
28998 scrollamount
+= (newscroll
-oldscroll
)*(up
? -1:1);
28999 oldscroll
= newscroll
;
29003 handleKey: function(evt
){
29005 // Handle keystroke event forwarded from ComboBox, returning false if it's
29006 // a keystroke I recognize and process, true otherwise.
29007 switch(evt
.keyCode
){
29008 case keys
.DOWN_ARROW
:
29009 this.selectNextNode();
29011 case keys
.PAGE_DOWN
:
29014 case keys
.UP_ARROW
:
29015 this.selectPreviousNode();
29028 'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">▼</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>",
29029 'dijit/Dialog':function(){
29031 'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
29032 define("dijit/Dialog", [
29034 "dojo/_base/array", // array.forEach array.indexOf array.map
29035 "dojo/_base/connect", // connect._keypress
29036 "dojo/_base/declare", // declare
29037 "dojo/_base/Deferred", // Deferred
29038 "dojo/dom", // dom.isDescendant
29039 "dojo/dom-class", // domClass.add domClass.contains
29040 "dojo/dom-geometry", // domGeometry.position
29041 "dojo/dom-style", // domStyle.set
29042 "dojo/_base/event", // event.stop
29043 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
29044 "dojo/i18n", // i18n.getLocalization
29046 "dojo/_base/lang", // lang.mixin lang.hitch
29049 "dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
29050 "dojo/window", // winUtils.getBox, winUtils.get
29051 "dojo/dnd/Moveable", // Moveable
29052 "dojo/dnd/TimedMoveable", // TimedMoveable
29054 "./_base/manager", // manager.defaultDuration
29056 "./_TemplatedMixin",
29057 "./_CssStateMixin",
29058 "./form/_FormMixin",
29060 "./DialogUnderlay",
29061 "./layout/ContentPane",
29062 "dojo/text!./templates/Dialog.html",
29063 "./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
29064 "dojo/i18n!./nls/common"
29065 ], function(require
, array
, connect
, declare
, Deferred
,
29066 dom
, domClass
, domGeometry
, domStyle
, event
, fx
, i18n
, keys
, lang
, on
, ready
, has
, winUtils
,
29067 Moveable
, TimedMoveable
, focus
, manager
, _Widget
, _TemplatedMixin
, _CssStateMixin
, _FormMixin
, _DialogMixin
,
29068 DialogUnderlay
, ContentPane
, template
, dijit
){
29074 dijit._underlay = function(kwArgs){
29076 // A shared instance of a `dijit.DialogUnderlay`
29079 // A shared instance of a `dijit.DialogUnderlay` created and
29080 // used by `dijit.Dialog`, though never created until some Dialog
29081 // or subclass thereof is shown.
29085 var _DialogBase
= declare("dijit._DialogBase", [_TemplatedMixin
, _FormMixin
, _DialogMixin
, _CssStateMixin
], {
29086 templateString
: template
,
29088 baseClass
: "dijitDialog",
29091 closeButtonNode
: "dijitDialogCloseIcon"
29094 // Map widget attributes to DOMNode attributes.
29096 { node
: "titleNode", type
: "innerHTML" },
29097 { node
: "titleBar", type
: "attribute" }
29100 // open: [readonly] Boolean
29101 // True if Dialog is currently displayed on screen.
29104 // duration: Integer
29105 // The time in milliseconds it takes the dialog to fade in and out
29106 duration
: manager
.defaultDuration
,
29108 // refocus: Boolean
29109 // A Toggle to modify the default focus behavior of a Dialog, which
29110 // is to re-focus the element which had focus before being opened.
29111 // False will disable refocusing. Default: true
29114 // autofocus: Boolean
29115 // A Toggle to modify the default focus behavior of a Dialog, which
29116 // is to focus on the first dialog element after opening the dialog.
29117 // False will disable autofocusing. Default: true
29120 // _firstFocusItem: [private readonly] DomNode
29121 // The pointer to the first focusable node in the dialog.
29122 // Set by `dijit/_DialogMixin._getFocusItems()`.
29123 _firstFocusItem
: null,
29125 // _lastFocusItem: [private readonly] DomNode
29126 // The pointer to which node has focus prior to our dialog.
29127 // Set by `dijit/_DialogMixin._getFocusItems()`.
29128 _lastFocusItem
: null,
29130 // doLayout: [protected] Boolean
29131 // Don't change this parameter from the default value.
29132 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
29133 // is never a child of a layout container, nor can you specify the size of
29134 // Dialog in order to control the size of an inner widget.
29137 // draggable: Boolean
29138 // Toggles the moveable aspect of the Dialog. If true, Dialog
29139 // can be dragged by it's title. If false it will remain centered
29140 // in the viewport.
29143 _setDraggableAttr: function(/*Boolean*/ val
){
29144 // Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
29145 // as that prevents text select on modern browsers (#14452)
29146 this._set("draggable", val
);
29149 // aria-describedby: String
29150 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
29151 // be the id of the container element of text that describes the dialog purpose (usually
29152 // the first text in the dialog).
29153 // | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
29154 // | <div id="intro">Introductory text</div>
29155 // | <div>rest of dialog contents</div>
29157 "aria-describedby": "",
29159 // maxRatio: Number
29160 // Maximum size to allow the dialog to expand to, relative to viewport size
29163 postMixInProperties: function(){
29164 var _nlsResources
= i18n
.getLocalization("dijit", "common");
29165 lang
.mixin(this, _nlsResources
);
29166 this.inherited(arguments
);
29169 postCreate: function(){
29170 domStyle
.set(this.domNode
, {
29172 position
:"absolute"
29174 this.ownerDocumentBody
.appendChild(this.domNode
);
29176 this.inherited(arguments
);
29178 this.connect(this, "onExecute", "hide");
29179 this.connect(this, "onCancel", "hide");
29180 this._modalconnects
= [];
29183 onLoad: function(){
29185 // Called when data has been loaded from an href.
29186 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
29187 // but should *not* be overridden.
29191 // when href is specified we need to reposition the dialog after the data is loaded
29192 // and find the focusable elements
29194 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
29195 this._getFocusItems(this.domNode
);
29196 focus
.focus(this._firstFocusItem
);
29198 this.inherited(arguments
);
29201 _onBlur: function(by
){
29202 this.inherited(arguments
);
29204 // If focus was accidentally removed from the dialog, such as if the user clicked a blank
29205 // area of the screen, or clicked the browser's address bar and then tabbed into the page,
29206 // then refocus. Won't do anything if focus was removed because the Dialog was closed, or
29207 // because a new Dialog popped up on top of the old one.
29208 var refocus
= lang
.hitch(this, function(){
29209 if(this.open
&& !this._destroyed
&& DialogLevelManager
.isTop(this)){
29210 this._getFocusItems(this.domNode
);
29211 focus
.focus(this._firstFocusItem
);
29215 // wait for mouse up, and then refocus dialog; otherwise doesn't work
29216 on
.once(this.ownerDocument
, "mouseup", refocus
);
29222 _endDrag: function(){
29224 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
29225 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
29226 var nodePosition
= domGeometry
.position(this.domNode
),
29227 viewport
= winUtils
.getBox(this.ownerDocument
);
29228 nodePosition
.y
= Math
.min(Math
.max(nodePosition
.y
, 0), (viewport
.h
- nodePosition
.h
));
29229 nodePosition
.x
= Math
.min(Math
.max(nodePosition
.x
, 0), (viewport
.w
- nodePosition
.w
));
29230 this._relativePosition
= nodePosition
;
29234 _setup: function(){
29236 // Stuff we need to do before showing the Dialog for the first
29237 // time (but we defer it until right beforehand, for
29238 // performance reasons).
29242 var node
= this.domNode
;
29244 if(this.titleBar
&& this.draggable
){
29245 this._moveable
= new ((has("ie") == 6) ? TimedMoveable
// prevent overload, see #5285
29246 : Moveable
)(node
, { handle
: this.titleBar
});
29247 this.connect(this._moveable
, "onMoveStop", "_endDrag");
29249 domClass
.add(node
,"dijitDialogFixed");
29252 this.underlayAttrs
= {
29254 "class": array
.map(this["class"].split(/\s/), function(s
){ return s
+"_underlay"; }).join(" "),
29255 ownerDocument
: this.ownerDocument
29261 // If necessary, shrink dialog contents so dialog fits in viewport
29265 this._checkIfSingleChild();
29267 // If we resized the dialog contents earlier, reset them back to original size, so
29268 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
29269 // Need to do this before the domGeometry.position(this.domNode) call below.
29270 if(this._singleChild
){
29271 if(typeof this._singleChildOriginalStyle
!= "undefined"){
29272 this._singleChild
.domNode
.style
.cssText
= this._singleChildOriginalStyle
;
29273 delete this._singleChildOriginalStyle
;
29276 domStyle
.set(this.containerNode
, {
29282 var bb
= domGeometry
.position(this.domNode
);
29284 // Get viewport size but then reduce it by a bit; Dialog should always have some space around it
29285 // to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
29286 var viewport
= winUtils
.getBox(this.ownerDocument
);
29287 viewport
.w
*= this.maxRatio
;
29288 viewport
.h
*= this.maxRatio
;
29290 if(bb
.w
>= viewport
.w
|| bb
.h
>= viewport
.h
){
29291 // Reduce size of dialog contents so that dialog fits in viewport
29293 var containerSize
= domGeometry
.position(this.containerNode
),
29294 w
= Math
.min(bb
.w
, viewport
.w
) - (bb
.w
- containerSize
.w
),
29295 h
= Math
.min(bb
.h
, viewport
.h
) - (bb
.h
- containerSize
.h
);
29297 if(this._singleChild
&& this._singleChild
.resize
){
29298 if(typeof this._singleChildOriginalStyle
== "undefined"){
29299 this._singleChildOriginalStyle
= this._singleChild
.domNode
.style
.cssText
;
29301 this._singleChild
.resize({w
: w
, h
: h
});
29303 domStyle
.set(this.containerNode
, {
29307 position
: "relative" // workaround IE bug moving scrollbar or dragging dialog
29311 if(this._singleChild
&& this._singleChild
.resize
){
29312 this._singleChild
.resize();
29317 _position: function(){
29319 // Position modal dialog in the viewport. If no relative offset
29320 // in the viewport has been determined (by dragging, for instance),
29321 // center the node. Otherwise, use the Dialog's stored relative offset,
29322 // and position the node to top: left: values based on the viewport.
29323 if(!domClass
.contains(this.ownerDocumentBody
, "dojoMove")){ // don't do anything if called during auto-scroll
29324 var node
= this.domNode
,
29325 viewport
= winUtils
.getBox(this.ownerDocument
),
29326 p
= this._relativePosition
,
29327 bb
= p
? null : domGeometry
.position(node
),
29328 l
= Math
.floor(viewport
.l
+ (p
? p
.x
: (viewport
.w
- bb
.w
) / 2)),
29329 t
= Math
.floor(viewport
.t
+ (p
? p
.y
: (viewport
.h
- bb
.h
) / 2))
29331 domStyle
.set(node
,{
29338 _onKey: function(/*Event*/ evt
){
29340 // Handles the keyboard events for accessibility reasons
29344 if(evt
.charOrCode
){
29345 var node
= evt
.target
;
29346 if(evt
.charOrCode
=== keys
.TAB
){
29347 this._getFocusItems(this.domNode
);
29349 var singleFocusItem
= (this._firstFocusItem
== this._lastFocusItem
);
29350 // see if we are shift-tabbing from first focusable item on dialog
29351 if(node
== this._firstFocusItem
&& evt
.shiftKey
&& evt
.charOrCode
=== keys
.TAB
){
29352 if(!singleFocusItem
){
29353 focus
.focus(this._lastFocusItem
); // send focus to last item in dialog
29356 }else if(node
== this._lastFocusItem
&& evt
.charOrCode
=== keys
.TAB
&& !evt
.shiftKey
){
29357 if(!singleFocusItem
){
29358 focus
.focus(this._firstFocusItem
); // send focus to first item in dialog
29362 // see if the key is for the dialog
29364 if(node
== this.domNode
|| domClass
.contains(node
, "dijitPopup")){
29365 if(evt
.charOrCode
== keys
.ESCAPE
){
29368 return; // just let it go
29371 node
= node
.parentNode
;
29373 // this key is for the disabled document window
29374 if(evt
.charOrCode
!== keys
.TAB
){ // allow tabbing into the dialog for a11y
29376 // opera won't tab to a div
29377 }else if(!has("opera")){
29379 this._firstFocusItem
.focus();
29380 }catch(e
){ /*squelch*/ }
29388 // Display the dialog
29389 // returns: dojo/_base/Deferred
29390 // Deferred object that resolves when the display animation is complete
29392 if(this.open
){ return; }
29394 if(!this._started
){
29398 // first time we show the dialog, there's some initialization stuff to do
29399 if(!this._alreadyInitialized
){
29401 this._alreadyInitialized
=true;
29404 if(this._fadeOutDeferred
){
29405 this._fadeOutDeferred
.cancel();
29408 // Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
29409 var win
= winUtils
.get(this.ownerDocument
);
29410 this._modalconnects
.push(on(win
, "scroll", lang
.hitch(this, "resize")));
29412 this._modalconnects
.push(on(this.domNode
, connect
._keypress
, lang
.hitch(this, "_onKey")));
29414 domStyle
.set(this.domNode
, {
29419 this._set("open", true);
29420 this._onShow(); // lazy load trigger
29425 // fade-in Animation object, setup below
29428 this._fadeInDeferred
= new Deferred(lang
.hitch(this, function(){
29430 delete this._fadeInDeferred
;
29433 fadeIn
= fx
.fadeIn({
29434 node
: this.domNode
,
29435 duration
: this.duration
,
29436 beforeBegin
: lang
.hitch(this, function(){
29437 DialogLevelManager
.show(this, this.underlayAttrs
);
29439 onEnd
: lang
.hitch(this, function(){
29440 if(this.autofocus
&& DialogLevelManager
.isTop(this)){
29441 // find focusable items each time dialog is shown since if dialog contains a widget the
29442 // first focusable items can change
29443 this._getFocusItems(this.domNode
);
29444 focus
.focus(this._firstFocusItem
);
29446 this._fadeInDeferred
.resolve(true);
29447 delete this._fadeInDeferred
;
29451 return this._fadeInDeferred
;
29457 // returns: dojo/_base/Deferred
29458 // Deferred object that resolves when the hide animation is complete
29460 // If we haven't been initialized yet then we aren't showing and we can just return.
29461 // Likewise if we are already hidden, or are currently fading out.
29462 if(!this._alreadyInitialized
|| !this.open
){
29465 if(this._fadeInDeferred
){
29466 this._fadeInDeferred
.cancel();
29469 // fade-in Animation object, setup below
29472 this._fadeOutDeferred
= new Deferred(lang
.hitch(this, function(){
29474 delete this._fadeOutDeferred
;
29476 // fire onHide when the promise resolves.
29477 this._fadeOutDeferred
.then(lang
.hitch(this, 'onHide'));
29479 fadeOut
= fx
.fadeOut({
29480 node
: this.domNode
,
29481 duration
: this.duration
,
29482 onEnd
: lang
.hitch(this, function(){
29483 this.domNode
.style
.display
= "none";
29484 DialogLevelManager
.hide(this);
29485 this._fadeOutDeferred
.resolve(true);
29486 delete this._fadeOutDeferred
;
29490 if(this._scrollConnected
){
29491 this._scrollConnected
= false;
29494 while(h
= this._modalconnects
.pop()){
29498 if(this._relativePosition
){
29499 delete this._relativePosition
;
29501 this._set("open", false);
29503 return this._fadeOutDeferred
;
29506 resize: function(){
29508 // Called when viewport scrolled or size changed. Position the Dialog and the underlay.
29511 if(this.domNode
.style
.display
!= "none"){
29512 if(DialogUnderlay
._singleton
){ // avoid race condition during show()
29513 DialogUnderlay
._singleton
.layout();
29520 destroy: function(){
29521 if(this._fadeInDeferred
){
29522 this._fadeInDeferred
.cancel();
29524 if(this._fadeOutDeferred
){
29525 this._fadeOutDeferred
.cancel();
29527 if(this._moveable
){
29528 this._moveable
.destroy();
29531 while(h
= this._modalconnects
.pop()){
29535 DialogLevelManager
.hide(this);
29537 this.inherited(arguments
);
29541 var Dialog
= declare("dijit.Dialog", [ContentPane
, _DialogBase
], {
29543 // A modal dialog Widget.
29545 // Pops up a modal dialog window, blocking access to the screen
29546 // and also graying out the screen Dialog is extended from
29547 // ContentPane so it supports all the same parameters (href, etc.).
29549 // | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
29551 // | var foo = new Dialog({ title: "test dialog", content: "test content" };
29552 // | foo.placeAt(win.body());
29553 // | foo.startup();
29555 Dialog
._DialogBase
= _DialogBase
; // for monkey patching and dojox/widget/DialogSimple
29557 var DialogLevelManager
= Dialog
._DialogLevelManager
= {
29559 // Controls the various active "levels" on the page, starting with the
29560 // stuff initially visible on the page (at z-index 0), and then having an entry for
29561 // each Dialog shown.
29565 show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs
){
29567 // Call right before fade-in animation for new dialog.
29568 // Saves current focus, displays/adjusts underlay for new dialog,
29569 // and sets the z-index of the dialog itself.
29571 // New dialog will be displayed on top of all currently displayed dialogs.
29573 // Caller is responsible for setting focus in new dialog after the fade-in
29574 // animation completes.
29576 // Save current focus
29577 ds
[ds
.length
-1].focus
= focus
.curNode
;
29579 // Display the underlay, or if already displayed then adjust for this new dialog
29580 // TODO: one underlay per document (based on dialog.ownerDocument)
29581 var underlay
= DialogUnderlay
._singleton
;
29582 if(!underlay
|| underlay
._destroyed
){
29583 underlay
= dijit
._underlay
= DialogUnderlay
._singleton
= new DialogUnderlay(underlayAttrs
);
29585 underlay
.set(dialog
.underlayAttrs
);
29588 // Set z-index a bit above previous dialog
29589 var zIndex
= ds
[ds
.length
-1].dialog
? ds
[ds
.length
-1].zIndex
+ 2 : Dialog
._DialogLevelManager
._beginZIndex
;
29590 if(ds
.length
== 1){ // first dialog
29593 domStyle
.set(DialogUnderlay
._singleton
.domNode
, 'zIndex', zIndex
- 1);
29596 domStyle
.set(dialog
.domNode
, 'zIndex', zIndex
);
29598 ds
.push({dialog
: dialog
, underlayAttrs
: underlayAttrs
, zIndex
: zIndex
});
29601 hide: function(/*dijit/_WidgetBase*/ dialog){
29603 // Called when the specified dialog is hidden/destroyed, after the fade-out
29604 // animation ends, in order to reset page focus, fix the underlay, etc.
29605 // If the specified dialog isn't open then does nothing.
29607 // Caller is responsible for either setting display:none on the dialog domNode,
29608 // or calling dijit/popup.hide(), or removing it from the page DOM.
29610 if(ds[ds.length-1].dialog == dialog){
29611 // Removing the top (or only) dialog in the stack, return focus
29612 // to previous dialog
29616 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
29618 // Adjust underlay, unless the underlay widget has already been destroyed
29619 // because we are being called during page unload (when all widgets are destroyed)
29620 if(!DialogUnderlay._singleton._destroyed){
29621 if(ds.length == 1){
29622 // Returning to original page. Hide the underlay.
29623 DialogUnderlay._singleton.hide();
29625 // Popping back to previous dialog, adjust underlay.
29626 domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
29627 DialogUnderlay._singleton.set(pd.underlayAttrs);
29632 if(dialog.refocus){
29633 // If we are returning control to a previous dialog but for some reason
29634 // that dialog didn't have a focused field, set focus to first focusable item.
29635 // This situation could happen if two dialogs appeared at nearly the same time,
29636 // since a dialog doesn't set it's focus until the fade-in is finished.
29637 var focus = pd.focus;
29638 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
29639 pd.dialog._getFocusItems(pd.dialog.domNode);
29640 focus = pd.dialog._firstFocusItem;
29644 // Refocus the button that spawned the Dialog. This will fail in corner cases including
29645 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
29646 // before this code runs. (#15058)
29653 // Removing a dialog out of order (#9944, #10705).
29654 // Don't need to mess with underlay or z-index or anything.
29655 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
29662 isTop: function(/*dijit/_WidgetBase*/ dialog){
29664 // Returns true if specified Dialog is the top in the task
29665 return ds[ds.length-1].dialog == dialog;
29669 // Stack representing the various active "levels" on the page, starting with the
29670 // stuff initially visible on the page (at z-index 0), and then having an entry for
29671 // each Dialog shown.
29672 // Each element in stack has form {
29673 // dialog: dialogWidget,
29674 // focus: returnFromGetFocus(),
29675 // underlayAttrs: attributes to set on underlay (when this widget is active)
29677 var ds = Dialog._dialogStack = [
29678 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
29681 // Back compat w/1.6, remove for 2.0
29682 if(has("dijit-legacy-requires")){
29683 ready(0, function(){
29684 var requires = ["dijit/TooltipDialog"];
29685 require(requires); // use indirection so modules not rolled into a build
29693 'dijit/_base/focus':function(){
29694 define("dijit/_base/focus", [
29695 "dojo/_base/array", // array.forEach
29696 "dojo/dom", // dom.isDescendant
29697 "dojo/_base/lang", // lang.isArray
29698 "dojo/topic", // publish
29699 "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
29701 "../main" // for exporting symbols to dijit
29702 ], function(array, dom, lang, topic, win, focus, dijit){
29705 // dijit/_base/focus
29709 // Deprecated module to monitor currently focused node and stack of currently focused widgets.
29710 // New code should access dijit/focus directly.
29712 // _curFocus: DomNode
29713 // Currently focused item on screen
29716 // _prevFocus: DomNode
29717 // Previously focused item on screen
29720 isCollapsed: function(){
29722 // Returns true if there is no text selected
29723 return dijit.getBookmark().isCollapsed;
29726 getBookmark: function(){
29728 // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
29729 var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
29731 if(win.global.getSelection){
29732 //W3C Range API for selections.
29733 sel = win.global.getSelection();
29735 if(sel.isCollapsed){
29736 tg = cf? cf.tagName : "";
29738 //Create a fake rangelike item to restore selections.
29739 tg = tg.toLowerCase();
29740 if(tg == "textarea" ||
29741 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
29743 start: cf.selectionStart,
29744 end: cf.selectionEnd,
29748 return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
29751 bm = {isCollapsed:true};
29752 if(sel.rangeCount){
29753 bm.mark = sel.getRangeAt(0).cloneRange();
29756 rg = sel.getRangeAt(0);
29757 bm = {isCollapsed: false, mark: rg.cloneRange()};
29761 // If the current focus was a input of some sort and no selection, don't bother saving
29762 // a native bookmark. This is because it causes issues with dialog/page selection restore.
29763 // So, we need to create psuedo bookmarks to work with.
29764 tg = cf ? cf.tagName : "";
29765 tg = tg.toLowerCase();
29766 if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
29767 if(sel.type && sel.type.toLowerCase() == "none"){
29773 rg = sel.createRange();
29775 isCollapsed: rg.text && rg.text.length?false:true,
29785 //'IE' way for selections.
29787 // createRange() throws exception when dojo in iframe
29788 //and nothing selected, see #9632
29789 rg = sel.createRange();
29790 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
29792 bm.isCollapsed = true;
29795 if(sel.type.toUpperCase() == 'CONTROL'){
29798 var i=0,len=rg.length;
29800 bm.mark.push(rg.item(i++));
29803 bm.isCollapsed = true;
29807 bm.mark = rg.getBookmark();
29810 console.warn("No idea how to store the current selection for this browser!");
29812 return bm; // Object
29815 moveToBookmark: function(/*Object*/ bookmark
){
29817 // Moves current selection to a bookmark
29819 // This should be a returned object from dijit.getBookmark()
29821 var _doc
= win
.doc
,
29822 mark
= bookmark
.mark
;
29824 if(win
.global
.getSelection
){
29825 //W3C Rangi API (FF, WebKit, Opera, etc)
29826 var sel
= win
.global
.getSelection();
29827 if(sel
&& sel
.removeAllRanges
){
29830 n
.selectionStart
= mark
.start
;
29831 n
.selectionEnd
= mark
.end
;
29833 sel
.removeAllRanges();
29834 sel
.addRange(mark
);
29837 console
.warn("No idea how to restore selection for this browser!");
29839 }else if(_doc
.selection
&& mark
){
29844 }else if(lang
.isArray(mark
)){
29845 rg
= _doc
.body
.createControlRange();
29846 //rg.addElement does not have call/apply method, so can not call it directly
29847 //rg is not available in "range.addElement(item)", so can't use that either
29848 array
.forEach(mark
, function(n
){
29852 rg
= _doc
.body
.createTextRange();
29853 rg
.moveToBookmark(mark
);
29860 getFocus: function(/*Widget?*/ menu
, /*Window?*/ openedForWindow
){
29862 // Called as getFocus(), this returns an Object showing the current focus
29863 // and selected text.
29865 // Called as getFocus(widget), where widget is a (widget representing) a button
29866 // that was just pressed, it returns where focus was before that button
29867 // was pressed. (Pressing the button may have either shifted focus to the button,
29868 // or removed focus altogether.) In this case the selected text is not returned,
29869 // since it can't be accurately determined.
29871 // menu: dijit/_WidgetBase|{domNode: DomNode} structure
29872 // The button that was just pressed. If focus has disappeared or moved
29873 // to this button, returns the previous focus. In this case the bookmark
29874 // information is already lost, and null is returned.
29876 // openedForWindow:
29877 // iframe in which menu was opened
29880 // A handle to restore focus/selection, to be passed to `dijit.focus`
29881 var node
= !focus
.curNode
|| (menu
&& dom
.isDescendant(focus
.curNode
, menu
.domNode
)) ? dijit
._prevFocus
: focus
.curNode
;
29884 bookmark
: node
&& (node
== focus
.curNode
) && win
.withGlobal(openedForWindow
|| win
.global
, dijit
.getBookmark
),
29885 openedForWindow
: openedForWindow
29889 // _activeStack: dijit/_WidgetBase[]
29890 // List of currently active widgets (focused widget and it's ancestors)
29893 registerIframe: function(/*DomNode*/ iframe
){
29895 // Registers listeners on the specified iframe so that any click
29896 // or focus event on that iframe (or anything in it) is reported
29897 // as a focus/click event on the `<iframe>` itself.
29899 // Currently only used by editor.
29901 // Handle to pass to unregisterIframe()
29902 return focus
.registerIframe(iframe
);
29905 unregisterIframe: function(/*Object*/ handle
){
29907 // Unregisters listeners on the specified iframe created by registerIframe.
29908 // After calling be sure to delete or null out the handle itself.
29910 // Handle returned by registerIframe()
29912 handle
&& handle
.remove();
29915 registerWin: function(/*Window?*/targetWindow
, /*DomNode?*/ effectiveNode
){
29917 // Registers listeners on the specified window (either the main
29918 // window or an iframe's window) to detect when the user has clicked somewhere
29919 // or focused somewhere.
29921 // Users should call registerIframe() instead of this method.
29923 // If specified this is the window associated with the iframe,
29924 // i.e. iframe.contentWindow.
29926 // If specified, report any focus events inside targetWindow as
29927 // an event on effectiveNode, rather than on evt.target.
29929 // Handle to pass to unregisterWin()
29931 return focus
.registerWin(targetWindow
, effectiveNode
);
29934 unregisterWin: function(/*Handle*/ handle
){
29936 // Unregisters listeners on the specified window (either the main
29937 // window or an iframe's window) according to handle returned from registerWin().
29938 // After calling be sure to delete or null out the handle itself.
29940 handle
&& handle
.remove();
29944 // Override focus singleton's focus function so that dijit.focus()
29945 // has backwards compatible behavior of restoring selection (although
29946 // probably no one is using that).
29947 focus
.focus = function(/*Object|DomNode */ handle
){
29949 // Sets the focused node and the selection according to argument.
29950 // To set focus to an iframe's content, pass in the iframe itself.
29952 // object returned by get(), or a DomNode
29954 if(!handle
){ return; }
29956 var node
= "node" in handle
? handle
.node
: handle
, // because handle is either DomNode or a composite object
29957 bookmark
= handle
.bookmark
,
29958 openedForWindow
= handle
.openedForWindow
,
29959 collapsed
= bookmark
? bookmark
.isCollapsed
: false;
29962 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
29963 // but we need to set focus to iframe.contentWindow
29965 var focusNode
= (node
.tagName
.toLowerCase() == "iframe") ? node
.contentWindow
: node
;
29966 if(focusNode
&& focusNode
.focus
){
29968 // Gecko throws sometimes if setting focus is impossible,
29969 // node not displayed or something like that
29971 }catch(e
){/*quiet*/}
29973 focus
._onFocusNode(node
);
29976 // set the selection
29977 // do not need to restore if current selection is not empty
29978 // (use keyboard to select a menu item) or if previous selection was collapsed
29979 // as it may cause focus shift (Esp in IE).
29980 if(bookmark
&& win
.withGlobal(openedForWindow
|| win
.global
, dijit
.isCollapsed
) && !collapsed
){
29981 if(openedForWindow
){
29982 openedForWindow
.focus();
29985 win
.withGlobal(openedForWindow
|| win
.global
, dijit
.moveToBookmark
, null, [bookmark
]);
29987 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
29992 // For back compatibility, monitor changes to focused node and active widget stack,
29993 // publishing events and copying changes from focus manager variables into dijit (top level) variables
29994 focus.watch("curNode", function(name, oldVal, newVal){
29995 dijit._curFocus = newVal;
29996 dijit._prevFocus = oldVal;
29998 topic.publish("focusNode", newVal); // publish
30001 focus.watch("activeStack", function(name, oldVal, newVal){
30002 dijit._activeStack = newVal;
30005 focus.on("widget-blur", function(widget, by){
30006 topic.publish("widgetBlur", widget, by); // publish
30008 focus.on("widget-focus", function(widget, by){
30009 topic.publish("widgetFocus", widget, by); // publish
30012 lang.mixin(dijit, exports);
30014 /*===== return exports; =====*/
30015 return dijit
; // for back compat :-(
30019 'dijit/tree/dndSource':function(){
30020 define("dijit/tree/dndSource", [
30021 "dojo/_base/array", // array.forEach array.indexOf array.map
30022 "dojo/_base/connect", // isCopyKey
30023 "dojo/_base/declare", // declare
30024 "dojo/dom-class", // domClass.add
30025 "dojo/dom-geometry", // domGeometry.position
30026 "dojo/_base/lang", // lang.mixin lang.hitch
30027 "dojo/on", // subscribe
30030 "dojo/dnd/Manager", // DNDManager.manager
30032 ], function(array
, connect
, declare
, domClass
, domGeometry
, lang
, on
, touch
, topic
, DNDManager
, _dndSelector
){
30035 // dijit/tree/dndSource
30037 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30042 // New item to be added to the Tree, like:
30050 var dndSource
= declare("dijit.tree.dndSource", _dndSelector
, {
30052 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
30054 // isSource: Boolean
30055 // Can be used as a DnD source.
30058 // accept: String[]
30059 // List of accepted types (text strings) for the Tree; defaults to
30061 accept
: ["text", "treeNode"],
30063 // copyOnly: [private] Boolean
30064 // Copy items, if true, use a state of Ctrl key otherwise
30067 // dragThreshold: Number
30068 // The move delay in pixels before detecting a drag; 5 by default
30071 // betweenThreshold: Integer
30072 // Distance from upper/lower edge of node to allow drop to reorder nodes
30073 betweenThreshold
: 0,
30075 // Flag used by Avatar.js to signal to generate text node when dragging
30076 generateText
: true,
30078 constructor: function(/*dijit/Tree*/ tree, /*dijit/tree/dndSource*/ params
){
30080 // a constructor of the Tree DnD Source
30083 if(!params
){ params
= {}; }
30084 lang
.mixin(this, params
);
30085 var type
= params
.accept
instanceof Array
? params
.accept
: ["text", "treeNode"];
30086 this.accept
= null;
30089 for(var i
= 0; i
< type
.length
; ++i
){
30090 this.accept
[type
[i
]] = 1;
30094 // class-specific variables
30095 this.isDragging
= false;
30096 this.mouseDown
= false;
30097 this.targetAnchor
= null; // DOMNode corresponding to the currently moused over TreeNode
30098 this.targetBox
= null; // coordinates of this.targetAnchor
30099 this.dropPosition
= ""; // whether mouse is over/after/before this.targetAnchor
30104 this.sourceState
= "";
30106 domClass
.add(this.node
, "dojoDndSource");
30108 this.targetState
= "";
30110 domClass
.add(this.node
, "dojoDndTarget");
30115 topic
.subscribe("/dnd/source/over", lang
.hitch(this, "onDndSourceOver")),
30116 topic
.subscribe("/dnd/start", lang
.hitch(this, "onDndStart")),
30117 topic
.subscribe("/dnd/drop", lang
.hitch(this, "onDndDrop")),
30118 topic
.subscribe("/dnd/cancel", lang
.hitch(this, "onDndCancel"))
30123 checkAcceptance: function(/*===== source, nodes =====*/){
30125 // Checks if the target can accept nodes from this source
30126 // source: dijit/tree/dndSource
30127 // The source which provides items
30128 // nodes: DOMNode[]
30129 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
30130 // source is a dijit/Tree.
30133 return true; // Boolean
30136 copyState: function(keyPressed
){
30138 // Returns true, if we need to copy items, false to move.
30139 // It is separated to be overwritten dynamically, if needed.
30140 // keyPressed: Boolean
30141 // The "copy" control key was pressed
30144 return this.copyOnly
|| keyPressed
; // Boolean
30146 destroy: function(){
30148 // Prepares the object to be garbage-collected.
30149 this.inherited(arguments
);
30151 while(h
= this.topics
.pop()){ h
.remove(); }
30152 this.targetAnchor
= null;
30155 _onDragMouse: function(e
, firstTime
){
30157 // Helper method for processing onmousemove/onmouseover events while drag is in progress.
30158 // Keeps track of current drop target.
30160 // The mousemove event.
30161 // firstTime: Boolean?
30162 // If this flag is set, this is the first mouse move event of the drag, so call m.canDrop() etc.
30163 // even if newTarget == null because the user quickly dragged a node in the Tree to a position
30164 // over Tree.containerNode but not over any TreeNode (#7971)
30166 var m
= DNDManager
.manager(),
30167 oldTarget
= this.targetAnchor
, // the TreeNode corresponding to TreeNode mouse was previously over
30168 newTarget
= this.current
, // TreeNode corresponding to TreeNode mouse is currently over
30169 oldDropPosition
= this.dropPosition
; // the previous drop position (over/before/after)
30171 // calculate if user is indicating to drop the dragged node before, after, or over
30172 // (i.e., to become a child of) the target node
30173 var newDropPosition
= "Over";
30174 if(newTarget
&& this.betweenThreshold
> 0){
30175 // If mouse is over a new TreeNode, then get new TreeNode's position and size
30176 if(!this.targetBox
|| oldTarget
!= newTarget
){
30177 this.targetBox
= domGeometry
.position(newTarget
.rowNode
, true);
30179 if((e
.pageY
- this.targetBox
.y
) <= this.betweenThreshold
){
30180 newDropPosition
= "Before";
30181 }else if((e
.pageY
- this.targetBox
.y
) >= (this.targetBox
.h
- this.betweenThreshold
)){
30182 newDropPosition
= "After";
30186 if(firstTime
|| newTarget
!= oldTarget
|| newDropPosition
!= oldDropPosition
){
30188 this._removeItemClass(oldTarget
.rowNode
, oldDropPosition
);
30191 this._addItemClass(newTarget
.rowNode
, newDropPosition
);
30194 // Check if it's ok to drop the dragged node on/before/after the target node.
30197 }else if(newTarget
== this.tree
.rootNode
&& newDropPosition
!= "Over"){
30198 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
30201 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
30202 var sameId
= false;
30203 if(m
.source
== this){
30204 for(var dragId
in this.selection
){
30205 var dragNode
= this.selection
[dragId
];
30206 if(dragNode
.item
=== newTarget
.item
){
30214 }else if(this.checkItemAcceptance(newTarget
.rowNode
, m
.source
, newDropPosition
.toLowerCase())
30215 && !this._isParentChildDrop(m
.source
, newTarget
.rowNode
)){
30222 this.targetAnchor
= newTarget
;
30223 this.dropPosition
= newDropPosition
;
30227 onMouseMove: function(e
){
30229 // Called for any onmousemove/ontouchmove events over the Tree
30231 // onmousemouse/ontouchmove event
30234 if(this.isDragging
&& this.targetState
== "Disabled"){ return; }
30235 this.inherited(arguments
);
30236 var m
= DNDManager
.manager();
30237 if(this.isDragging
){
30238 this._onDragMouse(e
);
30240 if(this.mouseDown
&& this.isSource
&&
30241 (Math
.abs(e
.pageX
-this._lastX
)>=this.dragThreshold
|| Math
.abs(e
.pageY
-this._lastY
)>=this.dragThreshold
)){
30242 var nodes
= this.getSelectedTreeNodes();
30244 if(nodes
.length
> 1){
30245 //filter out all selected items which has one of their ancestor selected as well
30246 var seen
= this.selection
, i
= 0, r
= [], n
, p
;
30247 nextitem
: while((n
= nodes
[i
++])){
30248 for(p
= n
.getParent(); p
&& p
!== this.tree
; p
= p
.getParent()){
30249 if(seen
[p
.id
]){ //parent is already selected, skip this node
30253 //this node does not have any ancestors selected, add it
30258 nodes
= array
.map(nodes
, function(n
){return n
.domNode
});
30259 m
.startDrag(this, nodes
, this.copyState(connect
.isCopyKey(e
)));
30260 this._onDragMouse(e
, true); // because this may be the only mousemove event we get before the drop
30266 onMouseDown: function(e
){
30268 // Event processor for onmousedown/ontouchstart
30270 // onmousedown/ontouchend event
30273 this.mouseDown
= true;
30274 this.mouseButton
= e
.button
;
30275 this._lastX
= e
.pageX
;
30276 this._lastY
= e
.pageY
;
30277 this.inherited(arguments
);
30280 onMouseUp: function(e
){
30282 // Event processor for onmouseup/ontouchend
30284 // onmouseup/ontouchend event
30287 if(this.mouseDown
){
30288 this.mouseDown
= false;
30289 this.inherited(arguments
);
30293 onMouseOut: function(){
30295 // Event processor for when mouse is moved away from a TreeNode
30298 this.inherited(arguments
);
30299 this._unmarkTargetAnchor();
30302 checkItemAcceptance: function(/*===== target, source, position =====*/){
30304 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
30306 // In the base case, this is called to check if target can become a child of source.
30307 // When betweenThreshold is set, position="before" or "after" means that we
30308 // are asking if the source node can be dropped before/after the target node.
30310 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
30311 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
30312 // source: dijit/tree/dndSource
30313 // The (set of) nodes we are dropping
30314 // position: String
30315 // "over", "before", or "after"
30321 // topic event processors
30322 onDndSourceOver: function(source
){
30324 // Topic event processor for /dnd/source/over, called when detected a current source.
30326 // The dijit/tree/dndSource / dojo/dnd/Source which has the mouse over it
30329 if(this != source
){
30330 this.mouseDown
= false;
30331 this._unmarkTargetAnchor();
30332 }else if(this.isDragging
){
30333 var m
= DNDManager
.manager();
30337 onDndStart: function(source
, nodes
, copy
){
30339 // Topic event processor for /dnd/start, called to initiate the DnD operation
30341 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30342 // nodes: DomNode[]
30343 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30345 // Copy items, if true, move items otherwise
30350 this._changeState("Source", this == source
? (copy
? "Copied" : "Moved") : "");
30352 var accepted
= this.checkAcceptance(source
, nodes
);
30354 this._changeState("Target", accepted
? "" : "Disabled");
30356 if(this == source
){
30357 DNDManager
.manager().overSource(this);
30360 this.isDragging
= true;
30363 itemCreator: function(nodes
/*===== , target, source =====*/){
30365 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
30366 // dropped onto the tree. Developer must override this method to enable
30367 // dropping from external sources onto this Tree, unless the Tree.model's items
30368 // happen to look like {id: 123, name: "Apple" } with no other attributes.
30370 // For each node in nodes[], which came from source, create a hash of name/value
30371 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
30372 // nodes: DomNode[]
30374 // source: dojo/dnd/Source
30375 // returns: __Item[]
30376 // Array of name/value hashes for each new item to be added to the Tree
30380 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
30381 // make signature itemCreator(sourceItem, node, target) (or similar).
30383 return array
.map(nodes
, function(node
){
30386 "name": node
.textContent
|| node
.innerText
|| ""
30391 onDndDrop: function(source
, nodes
, copy
){
30393 // Topic event processor for /dnd/drop, called to finish the DnD operation.
30395 // Updates data store items according to where node was dragged from and dropped
30396 // to. The tree will then respond to those data store updates and redraw itself.
30398 // The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
30399 // nodes: DomNode[]
30400 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
30402 // Copy items, if true, move items otherwise
30405 if(this.containerState
== "Over"){
30406 var tree
= this.tree
,
30407 model
= tree
.model
,
30408 target
= this.targetAnchor
;
30410 this.isDragging
= false;
30412 // Compute the new parent item
30415 var before
; // drop source before (aka previous sibling) of target
30416 newParentItem
= (target
&& target
.item
) || tree
.item
;
30417 if(this.dropPosition
== "Before" || this.dropPosition
== "After"){
30418 // TODO: if there is no parent item then disallow the drop.
30419 // Actually this should be checked during onMouseMove too, to make the drag icon red.
30420 newParentItem
= (target
.getParent() && target
.getParent().item
) || tree
.item
;
30421 // Compute the insert index for reordering
30422 insertIndex
= target
.getIndexInParent();
30423 if(this.dropPosition
== "After"){
30424 insertIndex
= target
.getIndexInParent() + 1;
30425 before
= target
.getNextSibling() && target
.getNextSibling().item
;
30427 before
= target
.item
;
30430 newParentItem
= (target
&& target
.item
) || tree
.item
;
30433 // If necessary, use this variable to hold array of hashes to pass to model.newItem()
30434 // (one entry in the array for each dragged node).
30435 var newItemsParams
;
30437 array
.forEach(nodes
, function(node
, idx
){
30438 // dojo/dnd/Item representing the thing being dropped.
30439 // Don't confuse the use of item here (meaning a DnD item) with the
30440 // uses below where item means dojo.data item.
30441 var sourceItem
= source
.getItem(node
.id
);
30443 // Information that's available if the source is another Tree
30444 // (possibly but not necessarily this tree, possibly but not
30445 // necessarily the same model as this Tree)
30446 if(array
.indexOf(sourceItem
.type
, "treeNode") != -1){
30447 var childTreeNode
= sourceItem
.data
,
30448 childItem
= childTreeNode
.item
,
30449 oldParentItem
= childTreeNode
.getParent().item
;
30452 if(source
== this){
30453 // This is a node from my own tree, and we are moving it, not copying.
30454 // Remove item from old parent's children attribute.
30455 // TODO: dijit/tree/dndSelector should implement deleteSelectedNodes()
30456 // and this code should go there.
30458 if(typeof insertIndex
== "number"){
30459 if(newParentItem
== oldParentItem
&& childTreeNode
.getIndexInParent() < insertIndex
){
30463 model
.pasteItem(childItem
, oldParentItem
, newParentItem
, copy
, insertIndex
, before
);
30464 }else if(model
.isItem(childItem
)){
30465 // Item from same model
30466 // (maybe we should only do this branch if the source is a tree?)
30467 model
.pasteItem(childItem
, oldParentItem
, newParentItem
, copy
, insertIndex
, before
);
30469 // Get the hash to pass to model.newItem(). A single call to
30470 // itemCreator() returns an array of hashes, one for each drag source node.
30471 if(!newItemsParams
){
30472 newItemsParams
= this.itemCreator(nodes
, target
.rowNode
, source
);
30475 // Create new item in the tree, based on the drag source.
30476 model
.newItem(newItemsParams
[idx
], newParentItem
, insertIndex
, before
);
30480 // Expand the target node (if it's currently collapsed) so the user can see
30481 // where their node was dropped. In particular since that node is still selected.
30482 this.tree
._expandNode(target
);
30484 this.onDndCancel();
30487 onDndCancel: function(){
30489 // Topic event processor for /dnd/cancel, called to cancel the DnD operation
30492 this._unmarkTargetAnchor();
30493 this.isDragging
= false;
30494 this.mouseDown
= false;
30495 delete this.mouseButton
;
30496 this._changeState("Source", "");
30497 this._changeState("Target", "");
30500 // When focus moves in/out of the entire Tree
30501 onOverEvent: function(){
30503 // This method is called when mouse is moved over our container (like onmouseenter)
30506 this.inherited(arguments
);
30507 DNDManager
.manager().overSource(this);
30509 onOutEvent: function(){
30511 // This method is called when mouse is moved out of our container (like onmouseleave)
30514 this._unmarkTargetAnchor();
30515 var m
= DNDManager
.manager();
30516 if(this.isDragging
){
30521 this.inherited(arguments
);
30524 _isParentChildDrop: function(source
, targetRow
){
30526 // Checks whether the dragged items are parent rows in the tree which are being
30527 // dragged into their own children.
30530 // The DragSource object.
30533 // The tree row onto which the dragged nodes are being dropped.
30538 // If the dragged object is not coming from the tree this widget belongs to,
30539 // it cannot be invalid.
30540 if(!source
.tree
|| source
.tree
!= this.tree
){
30545 var root
= source
.tree
.domNode
;
30546 var ids
= source
.selection
;
30548 var node
= targetRow
.parentNode
;
30550 // Iterate up the DOM hierarchy from the target drop row,
30551 // checking of any of the dragged nodes have the same ID.
30552 while(node
!= root
&& !ids
[node
.id
]){
30553 node
= node
.parentNode
;
30556 return node
.id
&& ids
[node
.id
];
30559 _unmarkTargetAnchor: function(){
30561 // Removes hover class of the current target anchor
30564 if(!this.targetAnchor
){ return; }
30565 this._removeItemClass(this.targetAnchor
.rowNode
, this.dropPosition
);
30566 this.targetAnchor
= null;
30567 this.targetBox
= null;
30568 this.dropPosition
= null;
30571 _markDndStatus: function(copy
){
30573 // Changes source's state based on "copy" status
30574 this._changeState("Source", copy
? "Copied" : "Moved");
30579 dndSource.__Item = __Item;
30586 'dijit/a11y':function(){
30587 define("dijit/a11y", [
30588 "dojo/_base/array", // array.forEach array.map
30589 "dojo/_base/config", // defaultDuration
30590 "dojo/_base/declare", // declare
30591 "dojo/dom", // dom.byId
30592 "dojo/dom-attr", // domAttr.attr domAttr.has
30593 "dojo/dom-style", // style.style
30594 "dojo/sniff", // has("ie")
30595 "./main" // for exporting methods to dijit namespace
30596 ], function(array
, config
, declare
, dom
, domAttr
, domStyle
, has
, dijit
){
30601 var shown
= (dijit
._isElementShown = function(/*Element*/ elem
){
30602 var s
= domStyle
.get(elem
);
30603 return (s
.visibility
!= "hidden")
30604 && (s
.visibility
!= "collapsed")
30605 && (s
.display
!= "none")
30606 && (domAttr
.get(elem
, "type") != "hidden");
30609 dijit
.hasDefaultTabStop = function(/*Element*/ elem
){
30611 // Tests if element is tab-navigable even without an explicit tabIndex setting
30613 // No explicit tabIndex setting, need to investigate node type
30614 switch(elem
.nodeName
.toLowerCase()){
30616 // An <a> w/out a tabindex is only navigable if it has an href
30617 return domAttr
.has(elem
, "href");
30624 // These are navigable by default
30627 // If it's an editor <iframe> then it's tab navigable.
30631 var contentDocument
= elem
.contentDocument
;
30632 if("designMode" in contentDocument
&& contentDocument
.designMode
== "on"){
30635 body
= contentDocument
.body
;
30637 // contentWindow.document isn't accessible within IE7/8
30638 // if the iframe.src points to a foreign url and this
30639 // page contains an element, that could get focus
30641 body
= elem
.contentWindow
.document
.body
;
30646 return body
&& (body
.contentEditable
== 'true' ||
30647 (body
.firstChild
&& body
.firstChild
.contentEditable
== 'true'));
30649 return elem
.contentEditable
== 'true';
30653 var isTabNavigable
= (dijit
.isTabNavigable = function(/*Element*/ elem
){
30655 // Tests if an element is tab-navigable
30657 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
30658 if(domAttr
.get(elem
, "disabled")){
30660 }else if(domAttr
.has(elem
, "tabIndex")){
30661 // Explicit tab index setting
30662 return domAttr
.get(elem
, "tabIndex") >= 0; // boolean
30664 // No explicit tabIndex setting, so depends on node type
30665 return dijit
.hasDefaultTabStop(elem
);
30669 dijit
._getTabNavigable = function(/*DOMNode*/ root
){
30671 // Finds descendants of the specified root node.
30673 // Finds the following descendants of the specified root node:
30675 // - the first tab-navigable element in document order
30676 // without a tabIndex or with tabIndex="0"
30677 // - the last tab-navigable element in document order
30678 // without a tabIndex or with tabIndex="0"
30679 // - the first element in document order with the lowest
30680 // positive tabIndex value
30681 // - the last element in document order with the highest
30682 // positive tabIndex value
30683 var first
, last
, lowest
, lowestTabindex
, highest
, highestTabindex
, radioSelected
= {};
30685 function radioName(node
){
30686 // If this element is part of a radio button group, return the name for that group.
30687 return node
&& node
.tagName
.toLowerCase() == "input" &&
30688 node
.type
&& node
.type
.toLowerCase() == "radio" &&
30689 node
.name
&& node
.name
.toLowerCase();
30692 var walkTree = function(/*DOMNode*/ parent
){
30693 for(var child
= parent
.firstChild
; child
; child
= child
.nextSibling
){
30694 // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
30695 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
30696 if(child
.nodeType
!= 1 || (has("ie") <= 9 && child
.scopeName
!== "HTML") || !shown(child
)){
30700 if(isTabNavigable(child
)){
30701 var tabindex
= +domAttr
.get(child
, "tabIndex"); // + to convert string --> number
30702 if(!domAttr
.has(child
, "tabIndex") || tabindex
== 0){
30707 }else if(tabindex
> 0){
30708 if(!lowest
|| tabindex
< lowestTabindex
){
30709 lowestTabindex
= tabindex
;
30712 if(!highest
|| tabindex
>= highestTabindex
){
30713 highestTabindex
= tabindex
;
30717 var rn
= radioName(child
);
30718 if(domAttr
.get(child
, "checked") && rn
){
30719 radioSelected
[rn
] = child
;
30722 if(child
.nodeName
.toUpperCase() != 'SELECT'){
30731 // substitute checked radio button for unchecked one, if there is a checked one with the same name.
30732 return radioSelected
[radioName(node
)] || node
;
30735 return { first
: rs(first
), last
: rs(last
), lowest
: rs(lowest
), highest
: rs(highest
) };
30737 dijit
.getFirstInTabbingOrder = function(/*String|DOMNode*/ root
, /*Document?*/ doc
){
30739 // Finds the descendant of the specified root node
30740 // that is first in the tabbing order
30741 var elems
= dijit
._getTabNavigable(dom
.byId(root
, doc
));
30742 return elems
.lowest
? elems
.lowest
: elems
.first
; // DomNode
30745 dijit
.getLastInTabbingOrder = function(/*String|DOMNode*/ root
, /*Document?*/ doc
){
30747 // Finds the descendant of the specified root node
30748 // that is last in the tabbing order
30749 var elems
= dijit
._getTabNavigable(dom
.byId(root
, doc
));
30750 return elems
.last
? elems
.last
: elems
.highest
; // DomNode
30755 // Accessibility utility functions (keyboard, tab stops, etc.)
30757 hasDefaultTabStop
: dijit
.hasDefaultTabStop
,
30758 isTabNavigable
: dijit
.isTabNavigable
,
30759 _getTabNavigable
: dijit
._getTabNavigable
,
30760 getFirstInTabbingOrder
: dijit
.getFirstInTabbingOrder
,
30761 getLastInTabbingOrder
: dijit
.getLastInTabbingOrder
30766 'dijit/form/_ToggleButtonMixin':function(){
30767 define("dijit/form/_ToggleButtonMixin", [
30768 "dojo/_base/declare", // declare
30769 "dojo/dom-attr" // domAttr.set
30770 ], function(declare
, domAttr
){
30773 // dijit/form/_ToggleButtonMixin
30775 return declare("dijit.form._ToggleButtonMixin", null, {
30777 // A mixin to provide functionality to allow a button that can be in two states (checked or not).
30779 // checked: Boolean
30780 // Corresponds to the native HTML `<input>` element's attribute.
30781 // In markup, specified as "checked='checked'" or just "checked".
30782 // True if the button is depressed, or the checkbox is checked,
30783 // or the radio button is selected, etc.
30786 // aria-pressed for toggle buttons, and aria-checked for checkboxes
30787 _aria_attr
: "aria-pressed",
30789 _onClick: function(/*Event*/ evt
){
30790 var original
= this.checked
;
30791 this._set('checked', !original
); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
30792 var ret
= this.inherited(arguments
); // the user could reset the value here
30793 this.set('checked', ret
? this.checked
: original
); // officially set the toggled or user value, or reset it back
30797 _setCheckedAttr: function(/*Boolean*/ value
, /*Boolean?*/ priorityChange
){
30798 this._set("checked", value
);
30799 var node
= this.focusNode
|| this.domNode
;
30800 domAttr
.set(node
, "checked", !!value
); // "mixed" -> true
30802 node
.setAttribute("checked", "");
30804 node
.removeAttribute("checked");
30806 node
.setAttribute(this._aria_attr
, String(value
)); // aria values should be strings
30807 this._handleOnChange(value
, priorityChange
);
30812 // Reset the widget's value to what it was at initialization time
30814 this._hasBeenBlurred
= false;
30816 // set checked state to original setting
30817 this.set('checked', this.params
.checked
|| false);
30824 'dijit/_Widget':function(){
30825 define("dijit/_Widget", [
30826 "dojo/aspect", // aspect.around
30827 "dojo/_base/config", // config.isDebug
30828 "dojo/_base/connect", // connect.connect
30829 "dojo/_base/declare", // declare
30831 "dojo/_base/kernel", // kernel.deprecated
30832 "dojo/_base/lang", // lang.hitch
30835 "./registry", // registry.byNode
30837 "./_OnDijitClickMixin",
30839 "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
30840 "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
30841 ], function(aspect
, config
, connect
, declare
, has
, kernel
, lang
, query
, ready
,
30842 registry
, _WidgetBase
, _OnDijitClickMixin
, _FocusMixin
){
30849 function connectToDomNode(){
30851 // If user connects to a widget method === this function, then they will
30852 // instead actually be connecting the equivalent event on this.domNode
30855 // Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
30856 function aroundAdvice(originalConnect
){
30857 return function(obj
, event
, scope
, method
){
30858 if(obj
&& typeof event
== "string" && obj
[event
] == connectToDomNode
){
30859 return obj
.on(event
.substring(2).toLowerCase(), lang
.hitch(scope
, method
));
30861 return originalConnect
.apply(connect
, arguments
);
30864 aspect
.around(connect
, "connect", aroundAdvice
);
30865 if(kernel
.connect
){
30866 aspect
.around(kernel
, "connect", aroundAdvice
);
30869 var _Widget
= declare("dijit._Widget", [_WidgetBase
, _OnDijitClickMixin
, _FocusMixin
], {
30871 // Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
30873 // Old Base class for Dijit widgets.
30875 // Extends _WidgetBase, adding support for:
30877 // - declaratively/programatically specifying widget initialization parameters like
30878 // onMouseMove="foo" that call foo when this.domNode gets a mousemove event
30880 // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
30881 // - focus related functions:
30882 // In particular, the onFocus()/onBlur() callbacks. Driven internally by
30883 // dijit/_base/focus.js.
30884 // - deprecated methods
30885 // - onShow(), onHide(), onClose()
30887 // Also, by loading code in dijit/_base, turns on:
30889 // - browser sniffing (putting browser class like `dj_ie` on `<html>` node)
30890 // - high contrast mode sniffing (add `dijit_a11y` class to `<body>` if machine is in high contrast mode)
30893 ////////////////// DEFERRED CONNECTS ///////////////////
30895 onClick
: connectToDomNode
,
30897 onClick: function(event){
30899 // Connect to this function to receive notifications of mouse click events.
30906 onDblClick
: connectToDomNode
,
30908 onDblClick: function(event){
30910 // Connect to this function to receive notifications of mouse double click events.
30917 onKeyDown
: connectToDomNode
,
30919 onKeyDown: function(event){
30921 // Connect to this function to receive notifications of keys being pressed down.
30928 onKeyPress
: connectToDomNode
,
30930 onKeyPress: function(event){
30932 // Connect to this function to receive notifications of printable keys being typed.
30939 onKeyUp
: connectToDomNode
,
30941 onKeyUp: function(event){
30943 // Connect to this function to receive notifications of keys being released.
30950 onMouseDown
: connectToDomNode
,
30952 onMouseDown: function(event){
30954 // Connect to this function to receive notifications of when the mouse button is pressed down.
30961 onMouseMove
: connectToDomNode
,
30963 onMouseMove: function(event){
30965 // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
30972 onMouseOut
: connectToDomNode
,
30974 onMouseOut: function(event){
30976 // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
30983 onMouseOver
: connectToDomNode
,
30985 onMouseOver: function(event){
30987 // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
30994 onMouseLeave
: connectToDomNode
,
30996 onMouseLeave: function(event){
30998 // Connect to this function to receive notifications of when the mouse moves off of this widget.
31005 onMouseEnter
: connectToDomNode
,
31007 onMouseEnter: function(event){
31009 // Connect to this function to receive notifications of when the mouse moves onto this widget.
31016 onMouseUp
: connectToDomNode
,
31018 onMouseUp: function(event){
31020 // Connect to this function to receive notifications of when the mouse button is released.
31028 constructor: function(params
/*===== ,srcNodeRef =====*/){
31030 // Create the widget.
31031 // params: Object|null
31032 // Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
31033 // and functions, typically callbacks like onClick.
31034 // The hash can contain any of the widget's properties, excluding read-only properties.
31035 // srcNodeRef: DOMNode|String?
31036 // If a srcNodeRef (DOM node) is specified:
31038 // - use srcNodeRef.innerHTML as my contents
31039 // - if this is a behavioral widget then apply behavior to that srcNodeRef
31040 // - otherwise, replace srcNodeRef with my generated DOM tree
31042 // extract parameters like onMouseMove that should connect directly to this.domNode
31043 this._toConnect
= {};
31044 for(var name
in params
){
31045 if(this[name
] === connectToDomNode
){
31046 this._toConnect
[name
.replace(/^on/, "").toLowerCase()] = params
[name
];
31047 delete params
[name
];
31052 postCreate: function(){
31053 this.inherited(arguments
);
31055 // perform connection from this.domNode to user specified handlers (ex: onMouseMove)
31056 for(var name
in this._toConnect
){
31057 this.on(name
, this._toConnect
[name
]);
31059 delete this._toConnect
;
31062 on: function(/*String|Function*/ type
, /*Function*/ func
){
31063 if(this[this._onMap(type
)] === connectToDomNode
){
31064 // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
31065 // normalization of onkeypress/onkeydown to behave like firefox, etc.
31066 // Also, need to specify context as "this" rather than the default context of the DOMNode
31068 return connect
.connect(this.domNode
, type
.toLowerCase(), this, func
);
31070 return this.inherited(arguments
);
31073 _setFocusedAttr: function(val
){
31074 // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
31075 // (but since it's a private variable we aren't required to keep supporting it).
31076 this._focused
= val
;
31077 this._set("focused", val
);
31080 ////////////////// DEPRECATED METHODS ///////////////////
31082 setAttribute: function(/*String*/ attr
, /*anything*/ value
){
31084 // Deprecated. Use set() instead.
31087 kernel
.deprecated(this.declaredClass
+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
31088 this.set(attr
, value
);
31091 attr: function(/*String|Object*/name
, /*Object?*/value
){
31093 // Set or get properties on a widget instance.
31095 // The property to get or set. If an object is passed here and not
31096 // a string, its keys are used as names of attributes to be set
31097 // and the value of the object as values to set in the widget.
31099 // Optional. If provided, attr() operates as a setter. If omitted,
31100 // the current value of the named property is returned.
31102 // This method is deprecated, use get() or set() directly.
31104 // Print deprecation warning but only once per calling function
31105 if(config
.isDebug
){
31106 var alreadyCalledHash
= arguments
.callee
._ach
|| (arguments
.callee
._ach
= {}),
31107 caller
= (arguments
.callee
.caller
|| "unknown caller").toString();
31108 if(!alreadyCalledHash
[caller
]){
31109 kernel
.deprecated(this.declaredClass
+ "::attr() is deprecated. Use get() or set() instead, called from " +
31110 caller
, "", "2.0");
31111 alreadyCalledHash
[caller
] = true;
31115 var args
= arguments
.length
;
31116 if(args
>= 2 || typeof name
=== "object"){ // setter
31117 return this.set.apply(this, arguments
);
31119 return this.get(name
);
31123 getDescendants: function(){
31125 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
31126 // This method should generally be avoided as it returns widgets declared in templates, which are
31127 // supposed to be internal/hidden, but it's left here for back-compat reasons.
31129 kernel
.deprecated(this.declaredClass
+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
31130 return this.containerNode
? query('[widgetId]', this.containerNode
).map(registry
.byNode
) : []; // dijit/_WidgetBase[]
31133 ////////////////// MISCELLANEOUS METHODS ///////////////////
31135 _onShow: function(){
31137 // Internal method called when this widget is made visible.
31138 // See `onShow` for details.
31142 onShow: function(){
31144 // Called when this widget becomes the selected pane in a
31145 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31146 // `dijit/layout/AccordionContainer`, etc.
31148 // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31153 onHide: function(){
31155 // Called when another widget becomes the selected pane in a
31156 // `dijit/layout/TabContainer`, `dijit/layout/StackContainer`,
31157 // `dijit/layout/AccordionContainer`, etc.
31159 // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
31164 onClose: function(){
31166 // Called when this widget is being displayed as a popup (ex: a Calendar popped
31167 // up from a DateTextBox), and it is hidden.
31168 // This is called from the dijit.popup code, and should not be called directly.
31170 // Also used as a parameter for children of `dijit/layout/StackContainer` or subclasses.
31171 // Callback if a user tries to close the child. Child will be closed if this function returns true.
31175 return true; // Boolean
31179 // For back-compat, remove in 2.0.
31180 if(has("dijit-legacy-requires")){
31181 ready(0, function(){
31182 var requires
= ["dijit/_base"];
31183 require(requires
); // use indirection so modules not rolled into a build
31190 'dojo/touch':function(){
31191 define("dojo/touch", ["./_base/kernel", "./aspect", "./dom", "./on", "./has", "./mouse", "./ready", "./_base/window"],
31192 function(dojo
, aspect
, dom
, on
, has
, mouse
, ready
, win
){
31197 var hasTouch
= has("touch");
31199 // TODO: get iOS version from dojo/sniff after #15827 is fixed
31202 var ua
= navigator
.userAgent
;
31203 var v
= ua
.match(/OS ([\d_]+)/) ? RegExp
.$1 : "1";
31204 var os
= parseFloat(v
.replace(/_/, '.').replace(/_
/g
, ''));
31208 var touchmove
, hoveredNode
;
31212 // Keep track of currently hovered node
31213 hoveredNode
= win
.body(); // currently hovered node
31215 win
.doc
.addEventListener("touchstart", function(evt
){
31216 // Precede touchstart event with touch.over event. DnD depends on this.
31217 // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
31218 // and to ensure this code runs even if the listener on the node does event.stop().
31219 var oldNode
= hoveredNode
;
31220 hoveredNode
= evt
.target
;
31221 on
.emit(oldNode
, "dojotouchout", {
31223 relatedTarget
: hoveredNode
,
31226 on
.emit(hoveredNode
, "dojotouchover", {
31227 target
: hoveredNode
,
31228 relatedTarget
: oldNode
,
31233 // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
31234 on(win
.doc
, "touchmove", function(evt
){
31235 var newNode
= win
.doc
.elementFromPoint(
31236 evt
.pageX
- (ios4
? 0 : win
.global
.pageXOffset
), // iOS 4 expects page coords
31237 evt
.pageY
- (ios4
? 0 : win
.global
.pageYOffset
)
31239 if(newNode
&& hoveredNode
!== newNode
){
31240 // touch out on the old node
31241 on
.emit(hoveredNode
, "dojotouchout", {
31242 target
: hoveredNode
,
31243 relatedTarget
: newNode
,
31247 // touchover on the new node
31248 on
.emit(newNode
, "dojotouchover", {
31250 relatedTarget
: hoveredNode
,
31254 hoveredNode
= newNode
;
31259 // Define synthetic touch.move event that unlike the native touchmove, fires for the node the finger is
31260 // currently dragging over rather than the node where the touch started.
31261 touchmove = function(node
, listener
){
31262 return on(win
.doc
, "touchmove", function(evt
){
31263 if(node
=== win
.doc
|| dom
.isDescendant(hoveredNode
, node
)){
31264 evt
.target
= hoveredNode
;
31265 listener
.call(this, evt
);
31272 function _handle(type
){
31274 // press | move | release | cancel
31276 return function(node
, listener
){//called by on(), see dojo.on
31277 return on(node
, type
, listener
);
31281 //device neutral events - touch.press|move|release|cancel/over/out
31283 press
: _handle(hasTouch
? "touchstart": "mousedown"),
31284 move: hasTouch
? touchmove
:_handle("mousemove"),
31285 release
: _handle(hasTouch
? "touchend": "mouseup"),
31286 cancel
: hasTouch
? _handle("touchcancel") : mouse
.leave
,
31287 over
: _handle(hasTouch
? "dojotouchover": "mouseover"),
31288 out
: _handle(hasTouch
? "dojotouchout": "mouseout"),
31289 enter
: mouse
._eventHandler(hasTouch
? "dojotouchover" : "mouseover"),
31290 leave
: mouse
._eventHandler(hasTouch
? "dojotouchout" : "mouseout")
31295 // This module provides unified touch event handlers by exporting
31296 // press, move, release and cancel which can also run well on desktop.
31297 // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
31300 // Used with dojo.on
31301 // | define(["dojo/on", "dojo/touch"], function(on, touch){
31302 // | on(node, touch.press, function(e){});
31303 // | on(node, touch.move, function(e){});
31304 // | on(node, touch.release, function(e){});
31305 // | on(node, touch.cancel, function(e){});
31307 // Used with touch.* directly
31308 // | touch.press(node, function(e){});
31309 // | touch.move(node, function(e){});
31310 // | touch.release(node, function(e){});
31311 // | touch.cancel(node, function(e){});
31313 press: function(node, listener){
31315 // Register a listener to 'touchstart'|'mousedown' for the given node
31317 // Target node to listen to
31318 // listener: Function
31319 // Callback function
31321 // A handle which will be used to remove the listener by handle.remove()
31323 move: function(node, listener){
31325 // Register a listener to 'touchmove'|'mousemove' for the given node
31327 // Target node to listen to
31328 // listener: Function
31329 // Callback function
31331 // A handle which will be used to remove the listener by handle.remove()
31333 release: function(node, listener){
31335 // Register a listener to 'touchend'|'mouseup' for the given node
31337 // Target node to listen to
31338 // listener: Function
31339 // Callback function
31341 // A handle which will be used to remove the listener by handle.remove()
31343 cancel: function(node, listener){
31345 // Register a listener to 'touchcancel'|'mouseleave' for the given node
31347 // Target node to listen to
31348 // listener: Function
31349 // Callback function
31351 // A handle which will be used to remove the listener by handle.remove()
31353 over: function(node, listener){
31355 // Register a listener to 'mouseover' or touch equivalent for the given node
31357 // Target node to listen to
31358 // listener: Function
31359 // Callback function
31361 // A handle which will be used to remove the listener by handle.remove()
31363 out: function(node, listener){
31365 // Register a listener to 'mouseout' or touch equivalent for the given node
31367 // Target node to listen to
31368 // listener: Function
31369 // Callback function
31371 // A handle which will be used to remove the listener by handle.remove()
31373 enter: function(node, listener){
31375 // Register a listener to mouse.enter or touch equivalent for the given node
31377 // Target node to listen to
31378 // listener: Function
31379 // Callback function
31381 // A handle which will be used to remove the listener by handle.remove()
31383 leave: function(node, listener){
31385 // Register a listener to mouse.leave or touch equivalent for the given node
31387 // Target node to listen to
31388 // listener: Function
31389 // Callback function
31391 // A handle which will be used to remove the listener by handle.remove()
31396 1 && (dojo
.touch
= touch
);
31402 'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"Χ \" 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",
31403 'dojo/fx':function(){
31404 define("dojo/fx", [
31415 "require" // for context sensitive loading of Toggler
31416 ], function(lang
, Evented
, dojo
, arrayUtil
, connect
, baseFx
, dom
, domStyle
, geom
, ready
, require
){
31421 // For back-compat, remove in 2.0.
31423 ready(0, function(){
31424 var requires
= ["./fx/Toggler"];
31425 require(requires
); // use indirection so modules not rolled into a build
31429 var coreFx
= dojo
.fx
= {
31431 // Effects library on top of Base animations
31435 _fire: function(evt
, args
){
31437 this[evt
].apply(this, args
||[]);
31443 var _chain = function(animations
){
31445 this._animations
= animations
||[];
31446 this._current
= this._onAnimateCtx
= this._onEndCtx
= null;
31449 arrayUtil
.forEach(this._animations
, function(a
){
31450 this.duration
+= a
.duration
;
31451 if(a
.delay
){ this.duration
+= a
.delay
; }
31454 _chain
.prototype = new Evented();
31455 lang
.extend(_chain
, {
31456 _onAnimate: function(){
31457 this._fire("onAnimate", arguments
);
31459 _onEnd: function(){
31460 connect
.disconnect(this._onAnimateCtx
);
31461 connect
.disconnect(this._onEndCtx
);
31462 this._onAnimateCtx
= this._onEndCtx
= null;
31463 if(this._index
+ 1 == this._animations
.length
){
31464 this._fire("onEnd");
31466 // switch animations
31467 this._current
= this._animations
[++this._index
];
31468 this._onAnimateCtx
= connect
.connect(this._current
, "onAnimate", this, "_onAnimate");
31469 this._onEndCtx
= connect
.connect(this._current
, "onEnd", this, "_onEnd");
31470 this._current
.play(0, true);
31473 play: function(/*int?*/ delay
, /*Boolean?*/ gotoStart
){
31474 if(!this._current
){ this._current
= this._animations
[this._index
= 0]; }
31475 if(!gotoStart
&& this._current
.status() == "playing"){ return this; }
31476 var beforeBegin
= connect
.connect(this._current
, "beforeBegin", this, function(){
31477 this._fire("beforeBegin");
31479 onBegin
= connect
.connect(this._current
, "onBegin", this, function(arg
){
31480 this._fire("onBegin", arguments
);
31482 onPlay
= connect
.connect(this._current
, "onPlay", this, function(arg
){
31483 this._fire("onPlay", arguments
);
31484 connect
.disconnect(beforeBegin
);
31485 connect
.disconnect(onBegin
);
31486 connect
.disconnect(onPlay
);
31488 if(this._onAnimateCtx
){
31489 connect
.disconnect(this._onAnimateCtx
);
31491 this._onAnimateCtx
= connect
.connect(this._current
, "onAnimate", this, "_onAnimate");
31492 if(this._onEndCtx
){
31493 connect
.disconnect(this._onEndCtx
);
31495 this._onEndCtx
= connect
.connect(this._current
, "onEnd", this, "_onEnd");
31496 this._current
.play
.apply(this._current
, arguments
);
31501 var e
= connect
.connect(this._current
, "onPause", this, function(arg
){
31502 this._fire("onPause", arguments
);
31503 connect
.disconnect(e
);
31505 this._current
.pause();
31509 gotoPercent: function(/*Decimal*/percent
, /*Boolean?*/ andPlay
){
31511 var offset
= this.duration
* percent
;
31512 this._current
= null;
31513 arrayUtil
.some(this._animations
, function(a
){
31514 if(a
.duration
<= offset
){
31518 offset
-= a
.duration
;
31522 this._current
.gotoPercent(offset
/ this._current
.duration
, andPlay
);
31526 stop: function(/*boolean?*/ gotoEnd
){
31529 for(; this._index
+ 1 < this._animations
.length
; ++this._index
){
31530 this._animations
[this._index
].stop(true);
31532 this._current
= this._animations
[this._index
];
31534 var e
= connect
.connect(this._current
, "onStop", this, function(arg
){
31535 this._fire("onStop", arguments
);
31536 connect
.disconnect(e
);
31538 this._current
.stop();
31542 status: function(){
31543 return this._current
? this._current
.status() : "stopped";
31545 destroy: function(){
31546 if(this._onAnimateCtx
){ connect
.disconnect(this._onAnimateCtx
); }
31547 if(this._onEndCtx
){ connect
.disconnect(this._onEndCtx
); }
31550 lang
.extend(_chain
, _baseObj
);
31552 coreFx
.chain = function(/*dojo/_base/fx.Animation[]*/ animations
){
31554 // Chain a list of `dojo.Animation`s to run in sequence
31557 // Return a `dojo.Animation` which will play all passed
31558 // `dojo.Animation` instances in sequence, firing its own
31559 // synthesized events simulating a single animation. (eg:
31560 // onEnd of this animation means the end of the chain,
31561 // not the individual animations within)
31564 // Once `node` is faded out, fade in `otherNode`
31566 // | dojo.fadeIn({ node:node }),
31567 // | dojo.fadeOut({ node:otherNode })
31570 return new _chain(animations
); // dojo/_base/fx.Animation
31573 var _combine = function(animations
){
31574 this._animations
= animations
||[];
31575 this._connects
= [];
31576 this._finished
= 0;
31579 arrayUtil
.forEach(animations
, function(a
){
31580 var duration
= a
.duration
;
31581 if(a
.delay
){ duration
+= a
.delay
; }
31582 if(this.duration
< duration
){ this.duration
= duration
; }
31583 this._connects
.push(connect
.connect(a
, "onEnd", this, "_onEnd"));
31586 this._pseudoAnimation
= new baseFx
.Animation({curve
: [0, 1], duration
: this.duration
});
31588 arrayUtil
.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
31590 self
._connects
.push(connect
.connect(self
._pseudoAnimation
, evt
,
31591 function(){ self
._fire(evt
, arguments
); }
31596 lang
.extend(_combine
, {
31597 _doAction: function(action
, args
){
31598 arrayUtil
.forEach(this._animations
, function(a
){
31599 a
[action
].apply(a
, args
);
31603 _onEnd: function(){
31604 if(++this._finished
> this._animations
.length
){
31605 this._fire("onEnd");
31608 _call: function(action
, args
){
31609 var t
= this._pseudoAnimation
;
31610 t
[action
].apply(t
, args
);
31612 play: function(/*int?*/ delay
, /*Boolean?*/ gotoStart
){
31613 this._finished
= 0;
31614 this._doAction("play", arguments
);
31615 this._call("play", arguments
);
31619 this._doAction("pause", arguments
);
31620 this._call("pause", arguments
);
31623 gotoPercent: function(/*Decimal*/percent
, /*Boolean?*/ andPlay
){
31624 var ms
= this.duration
* percent
;
31625 arrayUtil
.forEach(this._animations
, function(a
){
31626 a
.gotoPercent(a
.duration
< ms
? 1 : (ms
/ a
.duration
), andPlay
);
31628 this._call("gotoPercent", arguments
);
31631 stop: function(/*boolean?*/ gotoEnd
){
31632 this._doAction("stop", arguments
);
31633 this._call("stop", arguments
);
31636 status: function(){
31637 return this._pseudoAnimation
.status();
31639 destroy: function(){
31640 arrayUtil
.forEach(this._connects
, connect
.disconnect
);
31643 lang
.extend(_combine
, _baseObj
);
31645 coreFx
.combine = function(/*dojo/_base/fx.Animation[]*/ animations
){
31647 // Combine a list of `dojo.Animation`s to run in parallel
31650 // Combine an array of `dojo.Animation`s to run in parallel,
31651 // providing a new `dojo.Animation` instance encompasing each
31652 // animation, firing standard animation events.
31655 // Fade out `node` while fading in `otherNode` simultaneously
31657 // | dojo.fadeIn({ node:node }),
31658 // | dojo.fadeOut({ node:otherNode })
31662 // When the longest animation ends, execute a function:
31663 // | var anim = fx.combine([
31664 // | dojo.fadeIn({ node: n, duration:700 }),
31665 // | dojo.fadeOut({ node: otherNode, duration: 300 })
31667 // | dojo.connect(anim, "onEnd", function(){
31668 // | // overall animation is done.
31670 // | anim.play(); // play the animation
31672 return new _combine(animations
); // dojo/_base/fx.Animation
31675 coreFx
.wipeIn = function(/*Object*/ args
){
31677 // Expand a node to it's natural height.
31680 // Returns an animation that will expand the
31681 // node defined in 'args' object from it's current height to
31682 // it's natural height (with no scrollbar).
31683 // Node must have no margin/border/padding.
31686 // A hash-map of standard `dojo.Animation` constructor properties
31687 // (such as easing: node: duration: and so on)
31693 var node
= args
.node
= dom
.byId(args
.node
), s
= node
.style
, o
;
31695 var anim
= baseFx
.animateProperty(lang
.mixin({
31698 // wrapped in functions so we wait till the last second to query (in case value has changed)
31700 // start at current [computed] height, but use 1px rather than 0
31701 // because 0 causes IE to display the whole panel
31703 s
.overflow
= "hidden";
31704 if(s
.visibility
== "hidden" || s
.display
== "none"){
31710 var height
= domStyle
.get(node
, "height");
31711 return Math
.max(height
, 1);
31715 return node
.scrollHeight
;
31721 var fini = function(){
31725 connect
.connect(anim
, "onStop", fini
);
31726 connect
.connect(anim
, "onEnd", fini
);
31728 return anim
; // dojo/_base/fx.Animation
31731 coreFx
.wipeOut = function(/*Object*/ args
){
31733 // Shrink a node to nothing and hide it.
31736 // Returns an animation that will shrink node defined in "args"
31737 // from it's current height to 1px, and then hide it.
31740 // A hash-map of standard `dojo.Animation` constructor properties
31741 // (such as easing: node: duration: and so on)
31744 // | fx.wipeOut({ node:"someId" }).play()
31746 var node
= args
.node
= dom
.byId(args
.node
), s
= node
.style
, o
;
31748 var anim
= baseFx
.animateProperty(lang
.mixin({
31751 end
: 1 // 0 causes IE to display the whole panel
31756 connect
.connect(anim
, "beforeBegin", function(){
31758 s
.overflow
= "hidden";
31761 var fini = function(){
31764 s
.display
= "none";
31766 connect
.connect(anim
, "onStop", fini
);
31767 connect
.connect(anim
, "onEnd", fini
);
31769 return anim
; // dojo/_base/fx.Animation
31772 coreFx
.slideTo = function(/*Object*/ args
){
31774 // Slide a node to a new top/left position
31777 // Returns an animation that will slide "node"
31778 // defined in args Object from its current position to
31779 // the position defined by (args.left, args.top).
31782 // A hash-map of standard `dojo.Animation` constructor properties
31783 // (such as easing: node: duration: and so on). Special args members
31784 // are `top` and `left`, which indicate the new position to slide to.
31787 // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
31789 var node
= args
.node
= dom
.byId(args
.node
),
31790 top
= null, left
= null;
31792 var init
= (function(n
){
31794 var cs
= domStyle
.getComputedStyle(n
);
31795 var pos
= cs
.position
;
31796 top
= (pos
== 'absolute' ? n
.offsetTop
: parseInt(cs
.top
) || 0);
31797 left
= (pos
== 'absolute' ? n
.offsetLeft
: parseInt(cs
.left
) || 0);
31798 if(pos
!= 'absolute' && pos
!= 'relative'){
31799 var ret
= geom
.position(n
, true);
31802 n
.style
.position
="absolute";
31803 n
.style
.top
=top
+"px";
31804 n
.style
.left
=left
+"px";
31810 var anim
= baseFx
.animateProperty(lang
.mixin({
31812 top
: args
.top
|| 0,
31813 left
: args
.left
|| 0
31816 connect
.connect(anim
, "beforeBegin", anim
, init
);
31818 return anim
; // dojo/_base/fx.Animation
31825 'dijit/_DialogMixin':function(){
31826 define("dijit/_DialogMixin", [
31827 "dojo/_base/declare", // declare
31828 "./a11y" // _getTabNavigable
31829 ], function(declare
, a11y
){
31832 // dijit/_DialogMixin
31834 return declare("dijit._DialogMixin", null, {
31836 // This provides functions useful to Dialog and TooltipDialog
31838 execute: function(/*Object*/ /*===== formContents =====*/){
31840 // Callback when the user hits the submit button.
31841 // Override this method to handle Dialog execution.
31843 // After the user has pressed the submit button, the Dialog
31844 // first calls onExecute() to notify the container to hide the
31845 // dialog and restore focus to wherever it used to be.
31847 // *Then* this method is called.
31852 onCancel: function(){
31854 // Called when user has pressed the Dialog's cancel button, to notify container.
31856 // Developer shouldn't override or connect to this method;
31857 // it's a private communication device between the TooltipDialog
31858 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
31863 onExecute: function(){
31865 // Called when user has pressed the dialog's OK button, to notify container.
31867 // Developer shouldn't override or connect to this method;
31868 // it's a private communication device between the TooltipDialog
31869 // and the thing that opened it (ex: `dijit/form/DropDownButton`)
31874 _onSubmit: function(){
31876 // Callback when user hits submit button
31879 this.onExecute(); // notify container that we are about to execute
31880 this.execute(this.get('value'));
31883 _getFocusItems: function(){
31885 // Finds focusable items in dialog,
31886 // and sets this._firstFocusItem and this._lastFocusItem
31890 var elems
= a11y
._getTabNavigable(this.containerNode
);
31891 this._firstFocusItem
= elems
.lowest
|| elems
.first
|| this.closeButtonNode
|| this.domNode
;
31892 this._lastFocusItem
= elems
.last
|| elems
.highest
|| this._firstFocusItem
;
31898 'dijit/Tree':function(){
31900 'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
31901 'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});
31902 define("dijit/Tree", [
31903 "dojo/_base/array", // array.filter array.forEach array.map
31904 "dojo/_base/connect", // connect.isCopyKey()
31905 "dojo/cookie", // cookie
31906 "dojo/_base/declare", // declare
31907 "dojo/Deferred", // Deferred
31908 "dojo/DeferredList", // DeferredList
31909 "dojo/dom", // dom.isDescendant
31910 "dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
31911 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
31912 "dojo/dom-style",// domStyle.set
31913 "dojo/_base/event", // event.stop
31914 "dojo/errors/create", // createError
31915 "dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
31916 "dojo/_base/kernel", // kernel.deprecated
31917 "dojo/keys", // arrows etc.
31918 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
31919 "dojo/on", // on(), on.selector()
31924 "./registry", // registry.byNode(), registry.getEnclosingWidget()
31925 "./_base/manager", // manager.defaultDuration
31927 "./_TemplatedMixin",
31930 "./_CssStateMixin",
31931 "dojo/text!./templates/TreeNode.html",
31932 "dojo/text!./templates/Tree.html",
31933 "./tree/TreeStoreModel",
31934 "./tree/ForestStoreModel",
31935 "./tree/_dndSelector"
31936 ], function(array
, connect
, cookie
, declare
, Deferred
, DeferredList
,
31937 dom
, domClass
, domGeometry
, domStyle
, event
, createError
, fxUtils
, kernel
, keys
, lang
, on
, topic
, touch
, when
,
31938 focus
, registry
, manager
, _Widget
, _TemplatedMixin
, _Container
, _Contained
, _CssStateMixin
,
31939 treeNodeTemplate
, treeTemplate
, TreeStoreModel
, ForestStoreModel
, _dndSelector
){
31944 // Back-compat shim
31945 Deferred
= declare(Deferred
, {
31946 addCallback: function(callback
){ this.then(callback
); },
31947 addErrback: function(errback
){ this.then(null, errback
); }
31950 var TreeNode
= declare(
31952 [_Widget
, _TemplatedMixin
, _Container
, _Contained
, _CssStateMixin
],
31955 // Single node within a tree. This class is used internally
31956 // by Tree and should not be accessed directly.
31960 // item: [const] Item
31961 // the dojo.data entry this tree represents
31964 // isTreeNode: [protected] Boolean
31965 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
31966 // should not be accessed directly.
31970 // Text of this tree node
31972 _setLabelAttr
: {node
: "labelNode", type
: "innerText"},
31974 // isExpandable: [private] Boolean
31975 // This node has children, so show the expando node (+ sign)
31976 isExpandable
: null,
31978 // isExpanded: [readonly] Boolean
31979 // This node is currently expanded (ie, opened)
31982 // state: [private] String
31983 // Dynamic loading-related stuff.
31984 // When an empty folder node appears, it is "UNCHECKED" first,
31985 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
31986 state
: "UNCHECKED",
31988 templateString
: treeNodeTemplate
,
31990 baseClass
: "dijitTreeNode",
31992 // For hover effect for tree node, and focus effect for label
31994 rowNode
: "dijitTreeRow"
31997 // Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
31998 _setTooltipAttr
: {node
: "rowNode", type
: "attribute", attribute
: "title"},
32000 buildRendering: function(){
32001 this.inherited(arguments
);
32003 // set expand icon for leaf
32004 this._setExpando();
32006 // set icon and label class based on item
32007 this._updateItemClasses(this.item
);
32009 if(this.isExpandable
){
32010 this.labelNode
.setAttribute("aria-expanded", this.isExpanded
);
32013 //aria-selected should be false on all selectable elements.
32014 this.setSelected(false);
32017 _setIndentAttr: function(indent
){
32019 // Tell this node how many levels it should be indented
32021 // 0 for top level nodes, 1 for their children, 2 for their
32022 // grandchildren, etc.
32024 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
32025 var pixels
= (Math
.max(indent
, 0) * this.tree
._nodePixelIndent
) + "px";
32027 domStyle
.set(this.domNode
, "backgroundPosition", pixels
+ " 0px"); // TODOC: what is this for???
32028 domStyle
.set(this.indentNode
, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels
);
32030 array
.forEach(this.getChildren(), function(child
){
32031 child
.set("indent", indent
+1);
32034 this._set("indent", indent
);
32037 markProcessing: function(){
32039 // Visually denote that tree is loading data, etc.
32042 this.state
= "LOADING";
32043 this._setExpando(true);
32046 unmarkProcessing: function(){
32048 // Clear markup from markProcessing() call
32051 this._setExpando(false);
32054 _updateItemClasses: function(item
){
32056 // Set appropriate CSS classes for icon and label dom node
32057 // (used to allow for item updates to change respective CSS)
32060 var tree
= this.tree
, model
= tree
.model
;
32061 if(tree
._v10Compat
&& item
=== model
.root
){
32062 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
32065 this._applyClassAndStyle(item
, "icon", "Icon");
32066 this._applyClassAndStyle(item
, "label", "Label");
32067 this._applyClassAndStyle(item
, "row", "Row");
32069 this.tree
._startPaint(true); // signifies paint started and finished (synchronously)
32072 _applyClassAndStyle: function(item
, lower
, upper
){
32074 // Set the appropriate CSS classes and styles for labels, icons and rows.
32080 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
32083 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
32088 var clsName
= "_" + lower
+ "Class";
32089 var nodeName
= lower
+ "Node";
32090 var oldCls
= this[clsName
];
32092 this[clsName
] = this.tree
["get" + upper
+ "Class"](item
, this.isExpanded
);
32093 domClass
.replace(this[nodeName
], this[clsName
] || "", oldCls
|| "");
32095 domStyle
.set(this[nodeName
], this.tree
["get" + upper
+ "Style"](item
, this.isExpanded
) || {});
32098 _updateLayout: function(){
32100 // Set appropriate CSS classes for this.domNode
32103 var parent
= this.getParent();
32104 if(!parent
|| !parent
.rowNode
|| parent
.rowNode
.style
.display
== "none"){
32105 /* if we are hiding the root node then make every first level child look like a root node */
32106 domClass
.add(this.domNode
, "dijitTreeIsRoot");
32108 domClass
.toggle(this.domNode
, "dijitTreeIsLast", !this.getNextSibling());
32112 _setExpando: function(/*Boolean*/ processing
){
32114 // Set the right image for the expando node
32118 var styles
= ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
32119 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
32120 _a11yStates
= ["*","-","+","*"],
32121 idx
= processing
? 0 : (this.isExpandable
? (this.isExpanded
? 1 : 2) : 3);
32123 // apply the appropriate class to the expando node
32124 domClass
.replace(this.expandoNode
, styles
[idx
], styles
);
32126 // provide a non-image based indicator for images-off mode
32127 this.expandoNodeText
.innerHTML
= _a11yStates
[idx
];
32131 expand: function(){
32133 // Show my children
32135 // Deferred that fires when expansion is complete
32137 // If there's already an expand in progress or we are already expanded, just return
32138 if(this._expandDeferred
){
32139 return this._expandDeferred
; // dojo/_base/Deferred
32142 // cancel in progress collapse operation
32143 if(this._collapseDeferred
){
32144 this._collapseDeferred
.cancel();
32145 delete this._collapseDeferred
;
32148 // All the state information for when a node is expanded, maybe this should be
32149 // set when the animation completes instead
32150 this.isExpanded
= true;
32151 this.labelNode
.setAttribute("aria-expanded", "true");
32152 if(this.tree
.showRoot
|| this !== this.tree
.rootNode
){
32153 this.containerNode
.setAttribute("role", "group");
32155 domClass
.add(this.contentNode
,'dijitTreeContentExpanded');
32156 this._setExpando();
32157 this._updateItemClasses(this.item
);
32159 if(this == this.tree
.rootNode
&& this.tree
.showRoot
){
32160 this.tree
.domNode
.setAttribute("aria-expanded", "true");
32164 wipeIn
= fxUtils
.wipeIn({
32165 node
: this.containerNode
,
32166 duration
: manager
.defaultDuration
,
32172 // Deferred that fires when expand is complete
32173 def
= (this._expandDeferred
= new Deferred(function(){
32180 return def
; // dojo/_base/Deferred
32183 collapse: function(){
32185 // Collapse this node (if it's expanded)
32187 if(this._collapseDeferred
){
32188 // Node is already collapsed, or there's a collapse in progress, just return that Deferred
32189 return this._collapseDeferred
;
32192 // cancel in progress expand operation
32193 if(this._expandDeferred
){
32194 this._expandDeferred
.cancel();
32195 delete this._expandDeferred
;
32198 this.isExpanded
= false;
32199 this.labelNode
.setAttribute("aria-expanded", "false");
32200 if(this == this.tree
.rootNode
&& this.tree
.showRoot
){
32201 this.tree
.domNode
.setAttribute("aria-expanded", "false");
32203 domClass
.remove(this.contentNode
,'dijitTreeContentExpanded');
32204 this._setExpando();
32205 this._updateItemClasses(this.item
);
32208 wipeOut
= fxUtils
.wipeOut({
32209 node
: this.containerNode
,
32210 duration
: manager
.defaultDuration
,
32216 // Deferred that fires when expand is complete
32217 def
= (this._collapseDeferred
= new Deferred(function(){
32224 return def
; // dojo/_base/Deferred
32228 // Levels from this node to the root node
32231 setChildItems: function(/* Object[] */ items
){
32233 // Sets the child items of this node, removing/adding nodes
32234 // from current children to match specified items[] array.
32235 // Also, if this.persist == true, expands any children that were previously
32238 // Deferred object that fires after all previously opened children
32239 // have been expanded again (or fires instantly if there are no such children).
32241 var tree
= this.tree
,
32242 model
= tree
.model
,
32243 defs
= []; // list of deferreds that need to fire before I am complete
32246 // Orphan all my existing children.
32247 // If items contains some of the same items as before then we will reattach them.
32248 // Don't call this.removeChild() because that will collapse the tree etc.
32249 var oldChildren
= this.getChildren();
32250 array
.forEach(oldChildren
, function(child
){
32251 _Container
.prototype.removeChild
.call(this, child
);
32254 // All the old children of this TreeNode are subject for destruction if
32255 // 1) they aren't listed in the new children array (items)
32256 // 2) they aren't immediately adopted by another node (DnD)
32257 this.defer(function(){
32258 array
.forEach(oldChildren
, function(node
){
32259 if(!node
._destroyed
&& !node
.getParent()){
32260 // If node is in selection then remove it.
32261 tree
.dndController
.removeTreeNode(node
);
32263 // Deregister mapping from item id --> this node
32264 var id
= model
.getIdentity(node
.item
),
32265 ary
= tree
._itemNodesMap
[id
];
32266 if(ary
.length
== 1){
32267 delete tree
._itemNodesMap
[id
];
32269 var index
= array
.indexOf(ary
, node
);
32271 ary
.splice(index
, 1);
32275 // And finally we can destroy the node
32276 node
.destroyRecursive();
32281 this.state
= "LOADED";
32283 if(items
&& items
.length
> 0){
32284 this.isExpandable
= true;
32286 // Create _TreeNode widget for each specified tree node, unless one already
32287 // exists and isn't being used (presumably it's from a DnD move and was recently
32289 array
.forEach(items
, function(item
){ // MARKER: REUSE NODE
32290 var id
= model
.getIdentity(item
),
32291 existingNodes
= tree
._itemNodesMap
[id
],
32294 for(var i
=0;i
<existingNodes
.length
;i
++){
32295 if(existingNodes
[i
] && !existingNodes
[i
].getParent()){
32296 node
= existingNodes
[i
];
32297 node
.set('indent', this.indent
+1);
32303 node
= this.tree
._createTreeNode({
32306 isExpandable
: model
.mayHaveChildren(item
),
32307 label
: tree
.getLabel(item
),
32308 tooltip
: tree
.getTooltip(item
),
32309 ownerDocument
: tree
.ownerDocument
,
32312 textDir
: tree
.textDir
,
32313 indent
: this.indent
+ 1
32316 existingNodes
.push(node
);
32318 tree
._itemNodesMap
[id
] = [node
];
32321 this.addChild(node
);
32323 // If node was previously opened then open it again now (this may trigger
32324 // more data store accesses, recursively)
32325 if(this.tree
.autoExpand
|| this.tree
._state(node
)){
32326 defs
.push(tree
._expandNode(node
));
32330 // note that updateLayout() needs to be called on each child after
32331 // _all_ the children exist
32332 array
.forEach(this.getChildren(), function(child
){
32333 child
._updateLayout();
32336 this.isExpandable
=false;
32339 if(this._setExpando
){
32340 // change expando to/from dot or + icon, as appropriate
32341 this._setExpando(false);
32344 // Set leaf icon or folder icon, as appropriate
32345 this._updateItemClasses(this.item
);
32347 // On initial tree show, make the selected TreeNode as either the root node of the tree,
32348 // or the first child, if the root node is hidden
32349 if(this == tree
.rootNode
){
32350 var fc
= this.tree
.showRoot
? this : this.getChildren()[0];
32352 fc
.setFocusable(true);
32353 tree
.lastFocused
= fc
;
32355 // fallback: no nodes in tree so focus on Tree <div> itself
32356 tree
.domNode
.setAttribute("tabIndex", "0");
32360 var def
= new DeferredList(defs
);
32361 this.tree
._startPaint(def
); // to reset TreeNode widths after an item is added/removed from the Tree
32362 return def
; // dojo/_base/Deferred
32365 getTreePath: function(){
32368 while(node
&& node
!== this.tree
.rootNode
){
32369 path
.unshift(node
.item
);
32370 node
= node
.getParent();
32372 path
.unshift(this.tree
.rootNode
.item
);
32377 getIdentity: function(){
32378 return this.tree
.model
.getIdentity(this.item
);
32381 removeChild: function(/* treeNode */ node
){
32382 this.inherited(arguments
);
32384 var children
= this.getChildren();
32385 if(children
.length
== 0){
32386 this.isExpandable
= false;
32390 array
.forEach(children
, function(child
){
32391 child
._updateLayout();
32395 makeExpandable: function(){
32397 // if this node wasn't already showing the expando node,
32398 // turn it into one and call _setExpando()
32400 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
32402 this.isExpandable
= true;
32403 this._setExpando(false);
32406 setSelected: function(/*Boolean*/ selected
){
32408 // A Tree has a (single) currently selected node.
32409 // Mark that this node is/isn't that currently selected node.
32411 // In particular, setting a node as selected involves setting tabIndex
32412 // so that when user tabs to the tree, focus will go to that node (only).
32413 this.labelNode
.setAttribute("aria-selected", selected
? "true" : "false");
32414 domClass
.toggle(this.rowNode
, "dijitTreeRowSelected", selected
);
32417 setFocusable: function(/*Boolean*/ selected
){
32419 // A Tree has a (single) node that's focusable.
32420 // Mark that this node is/isn't that currently focsuable node.
32422 // In particular, setting a node as selected involves setting tabIndex
32423 // so that when user tabs to the tree, focus will go to that node (only).
32425 this.labelNode
.setAttribute("tabIndex", selected
? "0" : "-1");
32429 _setTextDirAttr: function(textDir
){
32430 if(textDir
&&((this.textDir
!= textDir
) || !this._created
)){
32431 this._set("textDir", textDir
);
32432 this.applyTextDir(this.labelNode
, this.labelNode
.innerText
|| this.labelNode
.textContent
|| "");
32433 array
.forEach(this.getChildren(), function(childNode
){
32434 childNode
.set("textDir", textDir
);
32440 var Tree
= declare("dijit.Tree", [_Widget
, _TemplatedMixin
], {
32442 // This widget displays hierarchical data from a store.
32444 // store: [deprecated] String|dojo/data/Store
32445 // Deprecated. Use "model" parameter instead.
32446 // The store to get data to display in the tree.
32449 // model: dijit/tree/model
32450 // Interface to read tree data, get notifications of changes to tree data,
32451 // and for handling drop operations (i.e drag and drop onto the tree)
32454 // query: [deprecated] anything
32455 // Deprecated. User should specify query to the model directly instead.
32456 // Specifies datastore query to return the root item or top items for the tree.
32459 // label: [deprecated] String
32460 // Deprecated. Use dijit/tree/ForestStoreModel directly instead.
32461 // Used in conjunction with query parameter.
32462 // If a query is specified (rather than a root node id), and a label is also specified,
32463 // then a fake root node is created and displayed, with this label.
32466 // showRoot: [const] Boolean
32467 // Should the root node be displayed, or hidden?
32470 // childrenAttr: [deprecated] String[]
32471 // Deprecated. This information should be specified in the model.
32472 // One ore more attributes that holds children of a tree node
32473 childrenAttr
: ["children"],
32475 // paths: String[][] or Item[][]
32476 // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
32477 // Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
32478 // returns a Deferred to indicate when the set is complete.
32481 // path: String[] or Item[]
32482 // Backward compatible singular variant of paths.
32485 // selectedItems: [readonly] Item[]
32486 // The currently selected items in this tree.
32487 // This property can only be set (via set('selectedItems', ...)) when that item is already
32488 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
32489 // Should generally use `paths` attribute to set the selected items instead.
32490 selectedItems
: null,
32492 // selectedItem: [readonly] Item
32493 // Backward compatible singular variant of selectedItems.
32494 selectedItem
: null,
32496 // openOnClick: Boolean
32497 // If true, clicking a folder node's label will open it, rather than calling onClick()
32498 openOnClick
: false,
32500 // openOnDblClick: Boolean
32501 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
32502 openOnDblClick
: false,
32504 templateString
: treeTemplate
,
32506 // persist: Boolean
32507 // Enables/disables use of cookies for state saving.
32510 // autoExpand: Boolean
32511 // Fully expand the tree on load. Overrides `persist`.
32514 // dndController: [protected] Function|String
32515 // Class to use as as the dnd controller. Specifying this class enables DnD.
32516 // Generally you should specify this as dijit/tree/dndSource.
32517 // Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
32518 dndController
: _dndSelector
,
32520 // parameters to pull off of the tree and pass on to the dndController as its params
32521 dndParams
: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
32523 //declare the above items so they can be pulled from the tree's markup
32525 // onDndDrop: [protected] Function
32526 // Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
32527 // Generally this doesn't need to be set.
32532 itemCreator: function(nodes, target, source){
32534 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
32535 // dropped onto the tree. Developer must override this method to enable
32536 // dropping from external sources onto this Tree, unless the Tree.model's items
32537 // happen to look like {id: 123, name: "Apple" } with no other attributes.
32539 // For each node in nodes[], which came from source, create a hash of name/value
32540 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
32541 // nodes: DomNode[]
32542 // The DOMNodes dragged from the source container
32544 // The target TreeNode.rowNode
32545 // source: dojo/dnd/Source
32546 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
32547 // returns: Object[]
32548 // Array of name/value hashes for each new item to be added to the Tree, like:
32550 // | { id: 123, label: "apple", foo: "bar" },
32551 // | { id: 456, label: "pear", zaz: "bam" }
32559 // onDndCancel: [protected] Function
32560 // Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
32561 // Generally this doesn't need to be set.
32565 checkAcceptance: function(source, nodes){
32567 // Checks if the Tree itself can accept nodes from this source
32568 // source: dijit/tree/dndSource
32569 // The source which provides items
32570 // nodes: DOMNode[]
32571 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
32572 // source is a dijit/Tree.
32575 return true; // Boolean
32578 checkAcceptance
: null,
32581 checkItemAcceptance: function(target, source, position){
32583 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
32585 // In the base case, this is called to check if target can become a child of source.
32586 // When betweenThreshold is set, position="before" or "after" means that we
32587 // are asking if the source node can be dropped before/after the target node.
32589 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
32590 // Use registry.getEnclosingWidget(target) to get the TreeNode.
32591 // source: dijit/tree/dndSource
32592 // The (set of) nodes we are dropping
32593 // position: String
32594 // "over", "before", or "after"
32597 return true; // Boolean
32600 checkItemAcceptance
: null,
32602 // dragThreshold: Integer
32603 // Number of pixels mouse moves before it's considered the start of a drag operation
32606 // betweenThreshold: Integer
32607 // Set to a positive value to allow drag and drop "between" nodes.
32609 // If during DnD mouse is over a (target) node but less than betweenThreshold
32610 // pixels from the bottom edge, dropping the the dragged node will make it
32611 // the next sibling of the target node, rather than the child.
32613 // Similarly, if mouse is over a target node but less that betweenThreshold
32614 // pixels from the top edge, dropping the dragged node will make it
32615 // the target node's previous sibling rather than the target node's child.
32616 betweenThreshold
: 0,
32618 // _nodePixelIndent: Integer
32619 // Number of pixels to indent tree nodes (relative to parent node).
32620 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
32621 // and calling resize() or startup() on tree after it's in the DOM.
32622 _nodePixelIndent
: 19,
32624 _publish: function(/*String*/ topicName
, /*Object*/ message
){
32626 // Publish a message for this widget/topic
32627 topic
.publish(this.id
, lang
.mixin({tree
: this, event
: topicName
}, message
|| {})); // publish
32630 postMixInProperties: function(){
32633 if(this.autoExpand
){
32634 // There's little point in saving opened/closed state of nodes for a Tree
32635 // that initially opens all it's nodes.
32636 this.persist
= false;
32639 this._itemNodesMap
= {};
32641 if(!this.cookieName
&& this.id
){
32642 this.cookieName
= this.id
+ "SaveStateCookie";
32645 // Deferred that fires when all the children have loaded.
32646 this.expandChildrenDeferred
= new Deferred();
32648 // Deferred that fires when all pending operations complete.
32649 this.pendingCommandsDeferred
= this.expandChildrenDeferred
;
32651 this.inherited(arguments
);
32654 postCreate: function(){
32657 // Catch events on TreeNodes
32660 on(this.domNode
, on
.selector(".dijitTreeNode", touch
.enter
), function(evt
){
32661 self
._onNodeMouseEnter(registry
.byNode(this), evt
);
32663 on(this.domNode
, on
.selector(".dijitTreeNode", touch
.leave
), function(evt
){
32664 self
._onNodeMouseLeave(registry
.byNode(this), evt
);
32666 on(this.domNode
, on
.selector(".dijitTreeNode", "click"), function(evt
){
32667 self
._onClick(registry
.byNode(this), evt
);
32669 on(this.domNode
, on
.selector(".dijitTreeNode", "dblclick"), function(evt
){
32670 self
._onDblClick(registry
.byNode(this), evt
);
32672 on(this.domNode
, on
.selector(".dijitTreeNode", "keypress"), function(evt
){
32673 self
._onKeyPress(registry
.byNode(this), evt
);
32675 on(this.domNode
, on
.selector(".dijitTreeNode", "keydown"), function(evt
){
32676 self
._onKeyDown(registry
.byNode(this), evt
);
32678 on(this.domNode
, on
.selector(".dijitTreeRow", "focusin"), function(evt
){
32679 self
._onNodeFocus(registry
.getEnclosingWidget(this), evt
);
32683 // Create glue between store and Tree, if not specified directly by user
32685 this._store2model();
32688 // monitor changes to items
32689 this.connect(this.model
, "onChange", "_onItemChange");
32690 this.connect(this.model
, "onChildrenChange", "_onItemChildrenChange");
32691 this.connect(this.model
, "onDelete", "_onItemDelete");
32693 this.inherited(arguments
);
32695 if(this.dndController
){
32696 if(lang
.isString(this.dndController
)){
32697 this.dndController
= lang
.getObject(this.dndController
);
32700 for(var i
=0; i
<this.dndParams
.length
;i
++){
32701 if(this[this.dndParams
[i
]]){
32702 params
[this.dndParams
[i
]] = this[this.dndParams
[i
]];
32705 this.dndController
= new this.dndController(this, params
);
32710 // If no path was specified to the constructor, use path saved in cookie
32711 if(!this.params
.path
&& !this.params
.paths
&& this.persist
){
32712 this.set("paths", this.dndController
._getSavedPaths());
32715 // onLoadDeferred should fire when all commands that are part of initialization have completed.
32716 // It will include all the set("paths", ...) commands that happen during initialization.
32717 this.onLoadDeferred
= this.pendingCommandsDeferred
;
32719 this.onLoadDeferred
.then(lang
.hitch(this, "onLoad"));
32722 _store2model: function(){
32724 // User specified a store&query rather than model, so create model from store/query
32725 this._v10Compat
= true;
32726 kernel
.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
32728 var modelParams
= {
32729 id
: this.id
+ "_ForestStoreModel",
32732 childrenAttrs
: this.childrenAttr
32735 // Only override the model's mayHaveChildren() method if the user has specified an override
32736 if(this.params
.mayHaveChildren
){
32737 modelParams
.mayHaveChildren
= lang
.hitch(this, "mayHaveChildren");
32740 if(this.params
.getItemChildren
){
32741 modelParams
.getChildren
= lang
.hitch(this, function(item
, onComplete
, onError
){
32742 this.getItemChildren((this._v10Compat
&& item
=== this.model
.root
) ? null : item
, onComplete
, onError
);
32745 this.model
= new ForestStoreModel(modelParams
);
32747 // For backwards compatibility, the visibility of the root node is controlled by
32748 // whether or not the user has specified a label
32749 this.showRoot
= Boolean(this.label
);
32752 onLoad: function(){
32754 // Called when tree finishes loading and expanding.
32756 // If persist == true the loading may encompass many levels of fetches
32757 // from the data store, each asynchronous. Waits for all to finish.
32764 // Initial load of the tree.
32765 // Load root node (possibly hidden) and it's children.
32766 this.model
.getRoot(
32767 lang
.hitch(this, function(item
){
32768 var rn
= (this.rootNode
= this.tree
._createTreeNode({
32771 isExpandable
: true,
32772 label
: this.label
|| this.getLabel(item
),
32773 textDir
: this.textDir
,
32774 indent
: this.showRoot
? 0 : -1
32777 if(!this.showRoot
){
32778 rn
.rowNode
.style
.display
="none";
32779 // if root is not visible, move tree role to the invisible
32780 // root node's containerNode, see #12135
32781 this.domNode
.setAttribute("role", "presentation");
32782 this.domNode
.removeAttribute("aria-expanded");
32783 this.domNode
.removeAttribute("aria-multiselectable");
32785 rn
.labelNode
.setAttribute("role", "presentation");
32786 rn
.containerNode
.setAttribute("role", "tree");
32787 rn
.containerNode
.setAttribute("aria-expanded","true");
32788 rn
.containerNode
.setAttribute("aria-multiselectable", !this.dndController
.singular
);
32790 this.domNode
.setAttribute("aria-multiselectable", !this.dndController
.singular
);
32793 this.domNode
.appendChild(rn
.domNode
);
32794 var identity
= this.model
.getIdentity(item
);
32795 if(this._itemNodesMap
[identity
]){
32796 this._itemNodesMap
[identity
].push(rn
);
32798 this._itemNodesMap
[identity
] = [rn
];
32801 rn
._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
32803 // Load top level children, and if persist==true, all nodes that were previously opened
32804 this._expandNode(rn
).then(lang
.hitch(this, function(){
32805 // Then, select the nodes that were selected last time, or
32806 // the ones specified by params.paths[].
32808 this.expandChildrenDeferred
.resolve(true);
32811 lang
.hitch(this, function(err
){
32812 console
.error(this, ": error loading root: ", err
);
32817 getNodesByItem: function(/*Item or id*/ item
){
32819 // Returns all tree nodes that refer to an item
32821 // Array of tree nodes that refer to passed item
32823 if(!item
){ return []; }
32824 var identity
= lang
.isString(item
) ? item
: this.model
.getIdentity(item
);
32825 // return a copy so widget don't get messed up by changes to returned array
32826 return [].concat(this._itemNodesMap
[identity
]);
32829 _setSelectedItemAttr: function(/*Item or id*/ item
){
32830 this.set('selectedItems', [item
]);
32833 _setSelectedItemsAttr: function(/*Items or ids*/ items
){
32835 // Select tree nodes related to passed items.
32836 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
32837 // behavior is undefined. Use set('paths', ...) instead.
32839 return this.pendingCommandsDeferred
= this.pendingCommandsDeferred
.then( lang
.hitch(this, function(){
32840 var identities
= array
.map(items
, function(item
){
32841 return (!item
|| lang
.isString(item
)) ? item
: tree
.model
.getIdentity(item
);
32844 array
.forEach(identities
, function(id
){
32845 nodes
= nodes
.concat(tree
._itemNodesMap
[id
] || []);
32847 this.set('selectedNodes', nodes
);
32851 _setPathAttr: function(/*Item[]|String[]*/ path
){
32853 // Singular variant of _setPathsAttr
32855 return this.set("paths", [path
]);
32857 // Empty list is interpreted as "select nothing"
32858 return this.set("paths", []);
32862 _setPathsAttr: function(/*Item[][]|String[][]*/ paths
){
32864 // Select the tree nodes identified by passed paths.
32866 // Array of arrays of items or item id's
32868 // Deferred to indicate when the set is complete
32872 // Let any previous set("path", ...) commands complete before this one starts.
32873 return this.pendingCommandsDeferred
= this.pendingCommandsDeferred
.then(function(){
32874 // We may need to wait for some nodes to expand, so setting
32875 // each path will involve a Deferred. We bring those deferreds
32876 // together with a DeferredList.
32877 return new DeferredList(array
.map(paths
, function(path
){
32878 var d
= new Deferred();
32880 // normalize path to use identity
32881 path
= array
.map(path
, function(item
){
32882 return lang
.isString(item
) ? item
: tree
.model
.getIdentity(item
);
32886 // Wait for the tree to load, if it hasn't already.
32887 selectPath(path
, [tree
.rootNode
], d
);
32889 d
.reject(new Tree
.PathError("Empty path"));
32895 function selectPath(path
, nodes
, def
){
32896 // Traverse path; the next path component should be among "nodes".
32897 var nextPath
= path
.shift();
32898 var nextNode
= array
.filter(nodes
, function(node
){
32899 return node
.getIdentity() == nextPath
;
32903 tree
._expandNode(nextNode
).then(function(){ selectPath(path
, nextNode
.getChildren(), def
); });
32905 // Successfully reached the end of this path
32906 def
.resolve(nextNode
);
32909 def
.reject(new Tree
.PathError("Could not expand path at " + nextPath
));
32913 function setNodes(newNodes
){
32914 // After all expansion is finished, set the selection to
32915 // the set of nodes successfully found.
32916 tree
.set("selectedNodes", array
.map(
32917 array
.filter(newNodes
,function(x
){return x
[0];}),
32918 function(x
){return x
[1];}));
32922 _setSelectedNodeAttr: function(node
){
32923 this.set('selectedNodes', [node
]);
32925 _setSelectedNodesAttr: function(nodes
){
32927 // Marks the specified TreeNodes as selected.
32928 // nodes: TreeNode[]
32929 // TreeNodes to mark.
32930 this.dndController
.setSelection(nodes
);
32934 expandAll: function(){
32936 // Expand all nodes in the tree
32938 // Deferred that fires when all nodes have expanded
32942 function expand(node
){
32943 var def
= new dojo
.Deferred();
32946 _this
._expandNode(node
).then(function(){
32947 // When node has expanded, call expand() recursively on each non-leaf child
32948 var childBranches
= array
.filter(node
.getChildren() || [], function(node
){
32949 return node
.isExpandable
;
32951 defs
= array
.map(childBranches
, expand
);
32953 // And when all those recursive calls finish, signal that I'm finished
32954 new dojo
.DeferredList(defs
).then(function(){
32962 return expand(this.rootNode
);
32965 collapseAll: function(){
32967 // Collapse all nodes in the tree
32969 // Deferred that fires when all nodes have collapsed
32973 function collapse(node
){
32974 var def
= new dojo
.Deferred();
32975 def
.label
= "collapseAllDeferred";
32977 // Collapse children first
32978 var childBranches
= array
.filter(node
.getChildren() || [], function(node
){
32979 return node
.isExpandable
;
32981 defs
= array
.map(childBranches
, collapse
);
32983 // And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
32984 // in which case collapseAll() is finished
32985 new dojo
.DeferredList(defs
).then(function(){
32986 if(!node
.isExpanded
|| (node
== _this
.rootNode
&& !_this
.showRoot
)){
32989 _this
._collapseNode(node
).then(function(){
32990 // When node has collapsed, signal that call is finished
33000 return collapse(this.rootNode
);
33003 ////////////// Data store related functions //////////////////////
33004 // These just get passed to the model; they are here for back-compat
33006 mayHaveChildren: function(/*dojo/data/Item*/ /*===== item =====*/){
33008 // Deprecated. This should be specified on the model itself.
33010 // Overridable function to tell if an item has or may have children.
33011 // Controls whether or not +/- expando icon is shown.
33012 // (For efficiency reasons we may not want to check if an element actually
33013 // has children until user clicks the expando node)
33018 getItemChildren: function(/*===== parentItem, onComplete =====*/){
33020 // Deprecated. This should be specified on the model itself.
33022 // Overridable function that return array of child items of given parent item,
33023 // or if parentItem==null then return top items in tree
33028 ///////////////////////////////////////////////////////
33029 // Functions for converting an item to a TreeNode
33030 getLabel: function(/*dojo/data/Item*/ item
){
33032 // Overridable function to get the label for a tree node (given the item)
33035 return this.model
.getLabel(item
); // String
33038 getIconClass: function(/*dojo/data/Item*/ item
, /*Boolean*/ opened
){
33040 // Overridable function to return CSS class name to display icon
33043 return (!item
|| this.model
.mayHaveChildren(item
)) ? (opened
? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
33046 getLabelClass: function(/*===== item, opened =====*/){
33048 // Overridable function to return CSS class name to display label
33049 // item: dojo/data/Item
33057 getRowClass: function(/*===== item, opened =====*/){
33059 // Overridable function to return CSS class name to display row
33060 // item: dojo/data/Item
33068 getIconStyle: function(/*===== item, opened =====*/){
33070 // Overridable function to return CSS styles to display icon
33071 // item: dojo/data/Item
33074 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
33079 getLabelStyle: function(/*===== item, opened =====*/){
33081 // Overridable function to return CSS styles to display label
33082 // item: dojo/data/Item
33085 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
33090 getRowStyle: function(/*===== item, opened =====*/){
33092 // Overridable function to return CSS styles to display row
33093 // item: dojo/data/Item
33096 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
33101 getTooltip: function(/*dojo/data/Item*/ /*===== item =====*/){
33103 // Overridable function to get the tooltip for a tree node (given the item)
33106 return ""; // String
33109 /////////// Keyboard and Mouse handlers ////////////////////
33111 _onKeyPress: function(/*TreeNode*/ treeNode
, /*Event*/ e
){
33113 // Handles keystrokes for printable keys, doing search navigation
33115 if(e
.charCode
<= 32){
33116 // Avoid duplicate events on firefox (this is an arrow key that will be handled by keydown handler)
33120 if(!e
.altKey
&& !e
.ctrlKey
&& !e
.shiftKey
&& !e
.metaKey
){
33121 var c
= String
.fromCharCode(e
.charCode
);
33122 this._onLetterKeyNav( { node
: treeNode
, key
: c
.toLowerCase() } );
33127 _onKeyDown: function(/*TreeNode*/ treeNode
, /*Event*/ e
){
33129 // Handles arrow, space, and enter keys
33131 var key
= e
.keyCode
;
33133 var map
= this._keyHandlerMap
;
33135 // Setup table mapping keys to events.
33136 // On WebKit based browsers, the combination ctrl-enter does not get passed through. To allow accessible
33137 // multi-select on those browsers, the space key is also used for selection.
33138 // Therefore, also allow space key for keyboard "click" operation.
33140 map
[keys
.ENTER
] = map
[keys
.SPACE
] = map
[" "] = "_onEnterKey";
33141 map
[this.isLeftToRight() ? keys
.LEFT_ARROW
: keys
.RIGHT_ARROW
] = "_onLeftArrow";
33142 map
[this.isLeftToRight() ? keys
.RIGHT_ARROW
: keys
.LEFT_ARROW
] = "_onRightArrow";
33143 map
[keys
.UP_ARROW
] = "_onUpArrow";
33144 map
[keys
.DOWN_ARROW
] = "_onDownArrow";
33145 map
[keys
.HOME
] = "_onHomeKey";
33146 map
[keys
.END
] = "_onEndKey";
33147 this._keyHandlerMap
= map
;
33150 if(this._keyHandlerMap
[key
]){
33151 // clear record of recent printables (being saved for multi-char letter navigation),
33152 // because "a", down-arrow, "b" shouldn't search for "ab"
33153 if(this._curSearch
){
33154 this._curSearch
.timer
.remove();
33155 delete this._curSearch
;
33158 this[this._keyHandlerMap
[key
]]( { node
: treeNode
, item
: treeNode
.item
, evt
: e
} );
33163 _onEnterKey: function(/*Object*/ message
){
33164 this._publish("execute", { item
: message
.item
, node
: message
.node
} );
33165 this.dndController
.userSelect(message
.node
, connect
.isCopyKey( message
.evt
), message
.evt
.shiftKey
);
33166 this.onClick(message
.item
, message
.node
, message
.evt
);
33169 _onDownArrow: function(/*Object*/ message
){
33171 // down arrow pressed; get next visible node, set focus there
33172 var node
= this._getNextNode(message
.node
);
33173 if(node
&& node
.isTreeNode
){
33174 this.focusNode(node
);
33178 _onUpArrow: function(/*Object*/ message
){
33180 // Up arrow pressed; move to previous visible node
33182 var node
= message
.node
;
33184 // if younger siblings
33185 var previousSibling
= node
.getPreviousSibling();
33186 if(previousSibling
){
33187 node
= previousSibling
;
33188 // if the previous node is expanded, dive in deep
33189 while(node
.isExpandable
&& node
.isExpanded
&& node
.hasChildren()){
33190 // move to the last child
33191 var children
= node
.getChildren();
33192 node
= children
[children
.length
-1];
33195 // if this is the first child, return the parent
33196 // unless the parent is the root of a tree with a hidden root
33197 var parent
= node
.getParent();
33198 if(!(!this.showRoot
&& parent
=== this.rootNode
)){
33203 if(node
&& node
.isTreeNode
){
33204 this.focusNode(node
);
33208 _onRightArrow: function(/*Object*/ message
){
33210 // Right arrow pressed; go to child node
33211 var node
= message
.node
;
33213 // if not expanded, expand, else move to 1st child
33214 if(node
.isExpandable
&& !node
.isExpanded
){
33215 this._expandNode(node
);
33216 }else if(node
.hasChildren()){
33217 node
= node
.getChildren()[0];
33218 if(node
&& node
.isTreeNode
){
33219 this.focusNode(node
);
33224 _onLeftArrow: function(/*Object*/ message
){
33226 // Left arrow pressed.
33227 // If not collapsed, collapse, else move to parent.
33229 var node
= message
.node
;
33231 if(node
.isExpandable
&& node
.isExpanded
){
33232 this._collapseNode(node
);
33234 var parent
= node
.getParent();
33235 if(parent
&& parent
.isTreeNode
&& !(!this.showRoot
&& parent
=== this.rootNode
)){
33236 this.focusNode(parent
);
33241 _onHomeKey: function(){
33243 // Home key pressed; get first visible node, and set focus there
33244 var node
= this._getRootOrFirstNode();
33246 this.focusNode(node
);
33250 _onEndKey: function(){
33252 // End key pressed; go to last visible node.
33254 var node
= this.rootNode
;
33255 while(node
.isExpanded
){
33256 var c
= node
.getChildren();
33257 node
= c
[c
.length
- 1];
33260 if(node
&& node
.isTreeNode
){
33261 this.focusNode(node
);
33265 // multiCharSearchDuration: Number
33266 // If multiple characters are typed where each keystroke happens within
33267 // multiCharSearchDuration of the previous keystroke,
33268 // search for nodes matching all the keystrokes.
33270 // For example, typing "ab" will search for entries starting with
33271 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
33272 multiCharSearchDuration
: 250,
33274 _onLetterKeyNav: function(message
){
33276 // Called when user presses a prinatable key; search for node starting with recently typed letters.
33278 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
33280 // Branch depending on whether this key starts a new search, or modifies an existing search
33281 var cs
= this._curSearch
;
33283 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
33284 // 'b', so we want to search for nodes starting w/"ab".
33285 cs
.pattern
= cs
.pattern
+ message
.key
;
33288 // We are starting a new search
33289 cs
= this._curSearch
= {
33290 pattern
: message
.key
,
33291 startNode
: message
.node
33295 // set/reset timer to forget recent keystrokes
33296 cs
.timer
= this.defer(function(){
33297 delete this._curSearch
;
33298 }, this.multiCharSearchDuration
);
33300 // Navigate to TreeNode matching keystrokes [entered so far].
33301 var node
= cs
.startNode
;
33303 node
= this._getNextNode(node
);
33304 //check for last node, jump to first node if necessary
33306 node
= this._getRootOrFirstNode();
33308 }while(node
!== cs
.startNode
&& (node
.label
.toLowerCase().substr(0, cs
.pattern
.length
) != cs
.pattern
));
33309 if(node
&& node
.isTreeNode
){
33310 // no need to set focus if back where we started
33311 if(node
!== cs
.startNode
){
33312 this.focusNode(node
);
33317 isExpandoNode: function(node
, widget
){
33319 // check whether a dom node is the expandoNode for a particular TreeNode widget
33320 return dom
.isDescendant(node
, widget
.expandoNode
) || dom
.isDescendant(node
, widget
.expandoNodeText
);
33323 _onClick: function(/*TreeNode*/ nodeWidget
, /*Event*/ e
){
33325 // Translates click events into commands for the controller to process
33327 var domElement
= e
.target
,
33328 isExpandoClick
= this.isExpandoNode(domElement
, nodeWidget
);
33330 if( (this.openOnClick
&& nodeWidget
.isExpandable
) || isExpandoClick
){
33331 // expando node was clicked, or label of a folder node was clicked; open it
33332 if(nodeWidget
.isExpandable
){
33333 this._onExpandoClick({node
:nodeWidget
});
33336 this._publish("execute", { item
: nodeWidget
.item
, node
: nodeWidget
, evt
: e
} );
33337 this.onClick(nodeWidget
.item
, nodeWidget
, e
);
33338 this.focusNode(nodeWidget
);
33342 _onDblClick: function(/*TreeNode*/ nodeWidget
, /*Event*/ e
){
33344 // Translates double-click events into commands for the controller to process
33346 var domElement
= e
.target
,
33347 isExpandoClick
= (domElement
== nodeWidget
.expandoNode
|| domElement
== nodeWidget
.expandoNodeText
);
33349 if( (this.openOnDblClick
&& nodeWidget
.isExpandable
) ||isExpandoClick
){
33350 // expando node was clicked, or label of a folder node was clicked; open it
33351 if(nodeWidget
.isExpandable
){
33352 this._onExpandoClick({node
:nodeWidget
});
33355 this._publish("execute", { item
: nodeWidget
.item
, node
: nodeWidget
, evt
: e
} );
33356 this.onDblClick(nodeWidget
.item
, nodeWidget
, e
);
33357 this.focusNode(nodeWidget
);
33362 _onExpandoClick: function(/*Object*/ message
){
33364 // User clicked the +/- icon; expand or collapse my children.
33365 var node
= message
.node
;
33367 // If we are collapsing, we might be hiding the currently focused node.
33368 // Also, clicking the expando node might have erased focus from the current node.
33369 // For simplicity's sake just focus on the node with the expando.
33370 this.focusNode(node
);
33372 if(node
.isExpanded
){
33373 this._collapseNode(node
);
33375 this._expandNode(node
);
33379 onClick: function(/*===== item, node, evt =====*/){
33381 // Callback when a tree node is clicked
33383 // Object from the dojo/store corresponding to this TreeNode
33385 // The TreeNode itself
33391 onDblClick: function(/*===== item, node, evt =====*/){
33393 // Callback when a tree node is double-clicked
33395 // Object from the dojo/store corresponding to this TreeNode
33397 // The TreeNode itself
33403 onOpen: function(/*===== item, node =====*/){
33405 // Callback when a node is opened
33406 // item: dojo/data/Item
33411 onClose: function(/*===== item, node =====*/){
33413 // Callback when a node is closed
33415 // Object from the dojo/store corresponding to this TreeNode
33417 // The TreeNode itself
33422 _getNextNode: function(node
){
33424 // Get next visible node
33426 if(node
.isExpandable
&& node
.isExpanded
&& node
.hasChildren()){
33427 // if this is an expanded node, get the first child
33428 return node
.getChildren()[0]; // TreeNode
33430 // find a parent node with a sibling
33431 while(node
&& node
.isTreeNode
){
33432 var returnNode
= node
.getNextSibling();
33434 return returnNode
; // TreeNode
33436 node
= node
.getParent();
33442 _getRootOrFirstNode: function(){
33444 // Get first visible node
33445 return this.showRoot
? this.rootNode
: this.rootNode
.getChildren()[0];
33448 _collapseNode: function(/*TreeNode*/ node
){
33450 // Called when the user has requested to collapse the node
33452 // Deferred that fires when the node is closed
33454 if(node
._expandNodeDeferred
){
33455 delete node
._expandNodeDeferred
;
33458 if(node
.state
== "LOADING"){
33459 // ignore clicks while we are in the process of loading data
33463 if(node
.isExpanded
){
33464 var ret
= node
.collapse();
33466 this.onClose(node
.item
, node
);
33467 this._state(node
, false);
33469 this._startPaint(ret
); // after this finishes, need to reset widths of TreeNodes
33475 _expandNode: function(/*TreeNode*/ node
){
33477 // Called when the user has requested to expand the node
33479 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
33480 // that were previously opened too
33482 // Signal that this call is complete
33483 var def
= new Deferred();
33485 if(node
._expandNodeDeferred
){
33486 // there's already an expand in progress, or completed, so just return
33487 return node
._expandNodeDeferred
; // dojo/_base/Deferred
33490 var model
= this.model
,
33494 // Load data if it's not already loaded
33495 if(!node
._loadDeferred
){
33496 // need to load all the children before expanding
33497 node
.markProcessing();
33499 // Setup deferred to signal when the load and expand are finished.
33500 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
33501 node
._loadDeferred
= new Deferred();
33503 // Get the children
33507 node
.unmarkProcessing();
33509 // Display the children and also start expanding any children that were previously expanded
33510 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
33511 node
.setChildItems(items
).then(function(){
33512 node
._loadDeferred
.resolve(items
);
33516 console
.error(_this
, ": error loading " + node
.label
+ " children: ", err
);
33517 node
._loadDeferred
.reject(err
);
33522 // Expand the node after data has loaded
33523 node
._loadDeferred
.then(lang
.hitch(this, function(){
33524 node
.expand().then(function(){
33525 def
.resolve(true); // signal that this _expandNode() call is complete
33528 // seems like these should be inside of then(), but left here for back-compat about
33529 // when this.isOpen flag gets set (ie, at the beginning of the animation)
33530 this.onOpen(node
.item
, node
);
33531 this._state(node
, true);
33534 this._startPaint(def
); // after this finishes, need to reset widths of TreeNodes
33536 return def
; // dojo/_base/Deferred
33539 ////////////////// Miscellaneous functions ////////////////
33541 focusNode: function(/* _tree.Node */ node
){
33543 // Focus on the specified node (which must be visible)
33547 // set focus so that the label will be voiced using screen readers
33548 focus
.focus(node
.labelNode
);
33551 _onNodeFocus: function(/*dijit/_WidgetBase*/ node){
33553 // Called when a TreeNode gets focus, either by user clicking
33554 // it, or programatically by arrow key handling code.
33556 // It marks that the current node is the selected one, and the previously
33557 // selected node no longer is.
33559 if(node && node != this.lastFocused){
33560 if(this.lastFocused && !this.lastFocused._destroyed){
33561 // mark that the previously focsable node is no longer focusable
33562 this.lastFocused.setFocusable(false);
33565 // mark that the new node is the currently selected one
33566 node.setFocusable(true);
33567 this.lastFocused = node;
33571 _onNodeMouseEnter: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
33573 // Called when mouse is over a node (onmouseenter event),
33574 // this is monitored by the DND code
33577 _onNodeMouseLeave: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
33579 // Called when mouse leaves a node (onmouseleave event),
33580 // this is monitored by the DND code
33583 //////////////// Events from the model //////////////////////////
33585 _onItemChange: function(/*Item*/ item
){
33587 // Processes notification of a change to an item's scalar values like label
33588 var model
= this.model
,
33589 identity
= model
.getIdentity(item
),
33590 nodes
= this._itemNodesMap
[identity
];
33593 var label
= this.getLabel(item
),
33594 tooltip
= this.getTooltip(item
);
33595 array
.forEach(nodes
, function(node
){
33597 item
: item
, // theoretically could be new JS Object representing same item
33601 node
._updateItemClasses(item
);
33606 _onItemChildrenChange: function(/*dojo/data/Item*/ parent
, /*dojo/data/Item[]*/ newChildrenList
){
33608 // Processes notification of a change to an item's children
33609 var model
= this.model
,
33610 identity
= model
.getIdentity(parent
),
33611 parentNodes
= this._itemNodesMap
[identity
];
33614 array
.forEach(parentNodes
,function(parentNode
){
33615 parentNode
.setChildItems(newChildrenList
);
33620 _onItemDelete: function(/*Item*/ item
){
33622 // Processes notification of a deletion of an item.
33623 // Not called from new dojo.store interface but there's cleanup code in setChildItems() instead.
33625 var model
= this.model
,
33626 identity
= model
.getIdentity(item
),
33627 nodes
= this._itemNodesMap
[identity
];
33630 array
.forEach(nodes
,function(node
){
33631 // Remove node from set of selected nodes (if it's selected)
33632 this.dndController
.removeTreeNode(node
);
33634 var parent
= node
.getParent();
33636 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
33637 parent
.removeChild(node
);
33639 node
.destroyRecursive();
33641 delete this._itemNodesMap
[identity
];
33645 /////////////// Miscellaneous funcs
33647 _initState: function(){
33649 // Load in which nodes should be opened automatically
33650 this._openedNodes
= {};
33651 if(this.persist
&& this.cookieName
){
33652 var oreo
= cookie(this.cookieName
);
33654 array
.forEach(oreo
.split(','), function(item
){
33655 this._openedNodes
[item
] = true;
33660 _state: function(node
, expanded
){
33662 // Query or set expanded state for an node
33666 var path
= array
.map(node
.getTreePath(), function(item
){
33667 return this.model
.getIdentity(item
);
33668 }, this).join("/");
33669 if(arguments
.length
=== 1){
33670 return this._openedNodes
[path
];
33673 this._openedNodes
[path
] = true;
33675 delete this._openedNodes
[path
];
33677 if(this.persist
&& this.cookieName
){
33679 for(var id
in this._openedNodes
){
33682 cookie(this.cookieName
, ary
.join(","), {expires
:365});
33687 destroy: function(){
33688 if(this._curSearch
){
33689 this._curSearch
.timer
.remove();
33690 delete this._curSearch
;
33693 this.rootNode
.destroyRecursive();
33695 if(this.dndController
&& !lang
.isString(this.dndController
)){
33696 this.dndController
.destroy();
33698 this.rootNode
= null;
33699 this.inherited(arguments
);
33702 destroyRecursive: function(){
33703 // A tree is treated as a leaf, not as a node with children (like a grid),
33704 // but defining destroyRecursive for back-compat.
33708 resize: function(changeSize
){
33710 domGeometry
.setMarginBox(this.domNode
, changeSize
);
33713 // The main JS sizing involved w/tree is the indentation, which is specified
33714 // in CSS and read in through this dummy indentDetector node (tree must be
33715 // visible and attached to the DOM to read this).
33716 // If the Tree is hidden domGeometry.position(this.tree.indentDetector).w will return 0, in which case just
33717 // keep the default value.
33718 this._nodePixelIndent
= domGeometry
.position(this.tree
.indentDetector
).w
|| this._nodePixelIndent
;
33720 // resize() may be called before this.rootNode is created, so wait until it's available
33721 this.expandChildrenDeferred
.then(lang
.hitch(this, function(){
33722 // If tree has already loaded, then reset indent for all the nodes
33723 this.rootNode
.set('indent', this.showRoot
? 0 : -1);
33725 // Also, adjust widths of all rows to match width of Tree
33726 this._adjustWidths();
33730 _outstandingPaintOperations
: 0,
33731 _startPaint: function(/*Promise|Boolean*/ p
){
33733 // Called at the start of an operation that will change what's displayed.
33735 // Promise that tells when the operation will complete. Alternately, if it's just a Boolean, it signifies
33736 // that the operation was synchronous, and already completed.
33738 this._outstandingPaintOperations
++;
33739 if(this._adjustWidthsTimer
){
33740 this._adjustWidthsTimer
.remove();
33741 delete this._adjustWidthsTimer
;
33744 var oc
= lang
.hitch(this, function(){
33745 this._outstandingPaintOperations
--;
33747 if(this._outstandingPaintOperations
<= 0 && !this._adjustWidthsTimer
&& this._started
){
33748 // Use defer() to avoid a width adjustment when another operation will immediately follow,
33749 // such as a sequence of opening a node, then it's children, then it's grandchildren, etc.
33750 this._adjustWidthsTimer
= this.defer("_adjustWidths");
33756 _adjustWidths: function(){
33758 // Get width of widest TreeNode, or the width of the Tree itself, whichever is greater,
33759 // and then set all TreeNodes to that width, so that selection/hover highlighting
33760 // extends to the edge of the Tree (#13141)
33762 if(this._adjustWidthsTimer
){
33763 this._adjustWidthsTimer
.remove();
33764 delete this._adjustWidthsTimer
;
33769 function collect(/*TreeNode*/ parent
){
33770 var node
= parent
.rowNode
;
33771 node
.style
.width
= "auto"; // erase setting from previous run
33772 maxWidth
= Math
.max(maxWidth
, node
.clientWidth
);
33774 if(parent
.isExpanded
){
33775 array
.forEach(parent
.getChildren(), collect
);
33778 collect(this.rootNode
);
33779 maxWidth
= Math
.max(maxWidth
, domGeometry
.getContentBox(this.domNode
).w
); // do after node.style.width="auto"
33780 array
.forEach(nodes
, function(node
){
33781 node
.style
.width
= maxWidth
+ "px"; // assumes no horizontal padding, border, or margin on rowNode
33785 _createTreeNode: function(/*Object*/ args
){
33787 // creates a TreeNode
33789 // Developers can override this method to define their own TreeNode class;
33790 // However it will probably be removed in a future release in favor of a way
33791 // of just specifying a widget for the label, rather than one that contains
33792 // the children too.
33793 return new TreeNode(args
);
33796 _setTextDirAttr: function(textDir
){
33797 if(textDir
&& this.textDir
!= textDir
){
33798 this._set("textDir",textDir
);
33799 this.rootNode
.set("textDir", textDir
);
33804 Tree
.PathError
= createError("TreePathError");
33805 Tree
._TreeNode
= TreeNode
; // for monkey patching or creating subclasses of TreeNode
33811 'dijit/form/_FormValueWidget':function(){
33812 define("dijit/form/_FormValueWidget", [
33813 "dojo/_base/declare", // declare
33814 "dojo/sniff", // has("ie")
33816 "./_FormValueMixin"
33817 ], function(declare
, has
, _FormWidget
, _FormValueMixin
){
33820 // dijit/form/_FormValueWidget
33822 return declare("dijit.form._FormValueWidget", [_FormWidget
, _FormValueMixin
],
33825 // Base class for widgets corresponding to native HTML elements such as `<input>` or `<select>`
33826 // that have user changeable values.
33828 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) `<input>` element,
33829 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
33830 // works as expected.
33832 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
33833 // directly in the template as read by the parser in order to function. IE is known to specifically
33834 // require the 'name' attribute at element creation time. See #8484, #8660.
33836 _layoutHackIE7: function(){
33838 // Work around table sizing bugs on IE7 by forcing redraw
33840 if(has("ie") == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
33841 var domNode
= this.domNode
;
33842 var parent
= domNode
.parentNode
;
33843 var pingNode
= domNode
.firstChild
|| domNode
; // target node most unlikely to have a custom filter
33844 var origFilter
= pingNode
.style
.filter
; // save custom filter, most likely nothing
33846 while(parent
&& parent
.clientHeight
== 0){ // search for parents that haven't rendered yet
33848 var disconnectHandle
= _this
.connect(parent
, "onscroll",
33850 _this
.disconnect(disconnectHandle
); // only call once
33851 pingNode
.style
.filter
= (new Date()).getMilliseconds(); // set to anything that's unique
33852 _this
.defer(function(){ pingNode
.style
.filter
= origFilter
; }); // restore custom filter, if any
33856 parent
= parent
.parentNode
;
33865 '*now':function(r
){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de","el","en-gb","en-us","es-es","fi-fi","fr-fr","he-il","hu","it-it","ja-jp","ko-kr","nl-nl","nb","pl","pt-br","pt-pt","ru","sk","sl","sv","th","tr","zh-tw","zh-cn","ROOT"]']);}
33867 define("dojo/tt-rss-layer", [], 1);