]> git.wh0rd.org - tt-rss.git/blame - lib/dojo/tt-rss-layer.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dojo / tt-rss-layer.js.uncompressed.js
CommitLineData
1354d172
AD
1require({cache:{
2'dijit/form/TextBox':function(){
3require({cache:{
4'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
5define("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/_base/sniff", // has("ie") has("mozilla")
12 "dojo/_base/window", // win.doc.selection.createRange
13 "./_FormValueWidget",
14 "./_TextBoxMixin",
15 "dojo/text!./templates/TextBox.html",
16 ".." // to export dijit._setSelectionRange, remove in 2.0
17], function(declare, domConstruct, domStyle, kernel, lang, has, win,
18 _FormValueWidget, _TextBoxMixin, template, dijit){
a089699c 19
1354d172
AD
20/*=====
21 var _FormValueWidget = dijit.form._FormValueWidget;
22 var _TextBoxMixin = dijit.form._TextBoxMixin;
23=====*/
a089699c 24
1354d172
AD
25 // module:
26 // dijit/form/TextBox
27 // summary:
28 // A base class for textbox form inputs
a089699c 29
1354d172
AD
30 var TextBox = declare(/*====="dijit.form.TextBox", =====*/ [_FormValueWidget, _TextBoxMixin], {
31 // summary:
32 // A base class for textbox form inputs
a089699c 33
1354d172
AD
34 templateString: template,
35 _singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
81bea17a 36
1354d172 37 _buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
a089699c 38
1354d172 39 baseClass: "dijitTextBox",
a089699c 40
1354d172
AD
41 postMixInProperties: function(){
42 var type = this.type.toLowerCase();
43 if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
44 this.templateString = this._singleNodeTemplate;
45 }
46 this.inherited(arguments);
47 },
a089699c 48
1354d172
AD
49 _onInput: function(e){
50 this.inherited(arguments);
51 if(this.intermediateChanges){ // _TextBoxMixin uses onInput
52 var _this = this;
53 // the setTimeout allows the key to post to the widget input box
54 setTimeout(function(){ _this._handleOnChange(_this.get('value'), false); }, 0);
55 }
56 },
a089699c 57
1354d172
AD
58 _setPlaceHolderAttr: function(v){
59 this._set("placeHolder", v);
60 if(!this._phspan){
61 this._attachPoints.push('_phspan');
62 // dijitInputField class gives placeHolder same padding as the input field
63 // parent node already has dijitInputField class but it doesn't affect this <span>
64 // since it's position: absolute.
65 this._phspan = domConstruct.create('span',{className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
66 }
67 this._phspan.innerHTML="";
68 this._phspan.appendChild(document.createTextNode(v));
69 this._updatePlaceHolder();
70 },
a089699c 71
1354d172
AD
72 _updatePlaceHolder: function(){
73 if(this._phspan){
74 this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
75 }
76 },
a089699c 77
1354d172
AD
78 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
79 this.inherited(arguments);
80 this._updatePlaceHolder();
81 },
a089699c 82
1354d172
AD
83 getDisplayedValue: function(){
84 // summary:
85 // Deprecated. Use get('displayedValue') instead.
86 // tags:
87 // deprecated
88 kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use set('displayedValue') instead.", "", "2.0");
89 return this.get('displayedValue');
90 },
a089699c 91
1354d172
AD
92 setDisplayedValue: function(/*String*/ value){
93 // summary:
94 // Deprecated. Use set('displayedValue', ...) instead.
95 // tags:
96 // deprecated
97 kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
98 this.set('displayedValue', value);
99 },
a089699c 100
1354d172
AD
101 _onBlur: function(e){
102 if(this.disabled){ return; }
103 this.inherited(arguments);
104 this._updatePlaceHolder();
105 },
a089699c 106
1354d172
AD
107 _onFocus: function(/*String*/ by){
108 if(this.disabled || this.readOnly){ return; }
109 this.inherited(arguments);
110 this._updatePlaceHolder();
a089699c 111 }
1354d172 112 });
a089699c 113
1354d172
AD
114 if(has("ie")){
115 TextBox = declare(/*===== "dijit.form.TextBox.IEMixin", =====*/ TextBox, {
116 declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
a089699c 117
1354d172
AD
118 _isTextSelected: function(){
119 var range = win.doc.selection.createRange();
120 var parent = range.parentElement();
121 return parent == this.textbox && range.text.length == 0;
122 },
123
124 postCreate: function(){
125 this.inherited(arguments);
126 // IE INPUT tag fontFamily has to be set directly using STYLE
127 // the setTimeout gives IE a chance to render the TextBox and to deal with font inheritance
128 setTimeout(lang.hitch(this, function(){
129 try{
130 var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
131 if(s){
132 var ff = s.fontFamily;
133 if(ff){
134 var inputs = this.domNode.getElementsByTagName("INPUT");
135 if(inputs){
136 for(var i=0; i < inputs.length; i++){
137 inputs[i].style.fontFamily = ff;
138 }
139 }
140 }
141 }
142 }catch(e){/*when used in a Dialog, and this is called before the dialog is
143 shown, s.fontFamily would trigger "Invalid Argument" error.*/}
144 }), 0);
145 }
146 });
a089699c 147
1354d172
AD
148 // Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
149 dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
150 if(element.createTextRange){
151 var r = element.createTextRange();
152 r.collapse(true);
153 r.moveStart("character", -99999); // move to 0
154 r.moveStart("character", start); // delta from 0 is the correct position
155 r.moveEnd("character", stop-start);
156 r.select();
157 }
158 }
159 }else if(has("mozilla")){
160 TextBox = declare(/*===== "dijit.form.TextBox.MozMixin", =====*/TextBox, {
161 declaredClass: "dijit.form.TextBox", // for user code referencing declaredClass
a089699c 162
1354d172
AD
163 _onBlur: function(e){
164 this.inherited(arguments);
165 if(this.selectOnClick){
166 // clear selection so that the next mouse click doesn't reselect
167 this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
168 }
169 }
170 });
171 }else{
172 TextBox.prototype.declaredClass = "dijit.form.TextBox";
173 }
174 lang.setObject("dijit.form.TextBox", TextBox); // don't do direct assignment, it confuses API doc parser
81bea17a 175
1354d172
AD
176 return TextBox;
177});
a089699c 178
1354d172
AD
179},
180'dijit/_base/scroll':function(){
181define("dijit/_base/scroll", [
182 "dojo/window", // windowUtils.scrollIntoView
183 ".." // export symbol to dijit
184], function(windowUtils, dijit){
185 // module:
186 // dijit/_base/scroll
81bea17a 187 // summary:
1354d172 188 // Back compatibility module, new code should use windowUtils directly instead of using this module.
a089699c 189
1354d172 190 dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
a089699c 191 // summary:
1354d172
AD
192 // Scroll the passed node into view, if it is not already.
193 // Deprecated, use `windowUtils.scrollIntoView` instead.
a089699c 194
1354d172 195 windowUtils.scrollIntoView(node, pos);
a089699c 196 };
1354d172 197});
a089699c 198
1354d172
AD
199},
200'dijit/_TemplatedMixin':function(){
201define("dijit/_TemplatedMixin", [
202 "dojo/_base/lang", // lang.getObject
203 "dojo/touch",
204 "./_WidgetBase",
205 "dojo/string", // string.substitute string.trim
206 "dojo/cache", // dojo.cache
207 "dojo/_base/array", // array.forEach
208 "dojo/_base/declare", // declare
209 "dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
210 "dojo/_base/sniff", // has("ie")
211 "dojo/_base/unload", // unload.addOnWindowUnload
212 "dojo/_base/window" // win.doc
213], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload, win) {
a089699c 214
1354d172
AD
215/*=====
216 var _WidgetBase = dijit._WidgetBase;
217=====*/
81bea17a 218
1354d172
AD
219 // module:
220 // dijit/_TemplatedMixin
221 // summary:
222 // Mixin for widgets that are instantiated from a template
a089699c 223
1354d172 224 var _TemplatedMixin = declare("dijit._TemplatedMixin", null, {
81bea17a 225 // summary:
1354d172 226 // Mixin for widgets that are instantiated from a template
a089699c 227
1354d172
AD
228 // templateString: [protected] String
229 // A string that represents the widget template.
230 // Use in conjunction with dojo.cache() to load from a file.
231 templateString: null,
81bea17a 232
1354d172
AD
233 // templatePath: [protected deprecated] String
234 // Path to template (HTML file) for this widget relative to dojo.baseUrl.
235 // Deprecated: use templateString with require([... "dojo/text!..."], ...) instead
236 templatePath: null,
81bea17a 237
1354d172
AD
238 // skipNodeCache: [protected] Boolean
239 // If using a cached widget template nodes poses issues for a
240 // particular widget class, it can set this property to ensure
241 // that its template is always re-built from a string
242 _skipNodeCache: false,
81bea17a 243
1354d172
AD
244 // _earlyTemplatedStartup: Boolean
245 // A fallback to preserve the 1.0 - 1.3 behavior of children in
246 // templates having their startup called before the parent widget
247 // fires postCreate. Defaults to 'false', causing child widgets to
248 // have their .startup() called immediately before a parent widget
249 // .startup(), but always after the parent .postCreate(). Set to
250 // 'true' to re-enable to previous, arguably broken, behavior.
251 _earlyTemplatedStartup: false,
a089699c 252
1354d172
AD
253/*=====
254 // _attachPoints: [private] String[]
255 // List of widget attribute names associated with data-dojo-attach-point=... in the
256 // template, ex: ["containerNode", "labelNode"]
257 _attachPoints: [],
258 =====*/
a089699c 259
1354d172
AD
260/*=====
261 // _attachEvents: [private] Handle[]
262 // List of connections associated with data-dojo-attach-event=... in the
263 // template
264 _attachEvents: [],
265 =====*/
266
267 constructor: function(){
268 this._attachPoints = [];
269 this._attachEvents = [];
270 },
271
272 _stringRepl: function(tmpl){
273 // summary:
274 // Does substitution of ${foo} type properties in template string
275 // tags:
276 // private
277 var className = this.declaredClass, _this = this;
278 // Cache contains a string because we need to do property replacement
279 // do the property replacement
280 return string.substitute(tmpl, this, function(value, key){
281 if(key.charAt(0) == '!'){ value = lang.getObject(key.substr(1), false, _this); }
282 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
283 if(value == null){ return ""; }
284
285 // Substitution keys beginning with ! will skip the transform step,
286 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
287 return key.charAt(0) == "!" ? value :
288 // Safer substitution, see heading "Attribute values" in
289 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
290 value.toString().replace(/"/g,"&quot;"); //TODO: add &amp? use encodeXML method?
291 }, this);
292 },
293
294 buildRendering: function(){
295 // summary:
296 // Construct the UI for this widget from a template, setting this.domNode.
297 // tags:
298 // protected
299
300 if(!this.templateString){
301 this.templateString = cache(this.templatePath, {sanitize: true});
a089699c 302 }
1354d172
AD
303
304 // Lookup cached version of template, and download to cache if it
305 // isn't there already. Returns either a DomNode or a string, depending on
306 // whether or not the template contains ${foo} replacement parameters.
307 var cached = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache);
308
309 var node;
310 if(lang.isString(cached)){
311 node = domConstruct.toDom(this._stringRepl(cached));
312 if(node.nodeType != 1){
313 // Flag common problems such as templates with multiple top level nodes (nodeType == 11)
314 throw new Error("Invalid template: " + cached);
81bea17a 315 }
1354d172
AD
316 }else{
317 // if it's a node, all we have to do is clone it
318 node = cached.cloneNode(true);
319 }
a089699c 320
1354d172
AD
321 this.domNode = node;
322
323 // Call down to _Widget.buildRendering() to get base classes assigned
324 // TODO: change the baseClass assignment to _setBaseClassAttr
325 this.inherited(arguments);
326
327 // recurse through the node, looking for, and attaching to, our
328 // attachment points and events, which should be defined on the template node.
329 this._attachTemplateNodes(node, function(n,p){ return n.getAttribute(p); });
330
331 this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
332
333 this._fillContent(this.srcNodeRef);
334 },
335
336 _beforeFillContent: function(){
337 },
338
339 _fillContent: function(/*DomNode*/ source){
340 // summary:
341 // Relocate source contents to templated container node.
342 // this.containerNode must be able to receive children, or exceptions will be thrown.
343 // tags:
344 // protected
345 var dest = this.containerNode;
346 if(source && dest){
347 while(source.hasChildNodes()){
348 dest.appendChild(source.firstChild);
a089699c 349 }
1354d172
AD
350 }
351 },
352
353 _attachTemplateNodes: function(rootNode, getAttrFunc){
354 // summary:
355 // Iterate through the template and attach functions and nodes accordingly.
356 // Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
357 // etc. for those widgets.
358 // description:
359 // Map widget properties and functions to the handlers specified in
360 // the dom node and it's descendants. This function iterates over all
361 // nodes and looks for these properties:
362 // * dojoAttachPoint/data-dojo-attach-point
363 // * dojoAttachEvent/data-dojo-attach-event
364 // rootNode: DomNode|Widget[]
365 // the node to search for properties. All children will be searched.
366 // getAttrFunc: Function
367 // a function which will be used to obtain property for a given
368 // DomNode/Widget
369 // tags:
370 // private
371
372 var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
373 var x = lang.isArray(rootNode) ? 0 : -1;
374 for(; x<nodes.length; x++){
375 var baseNode = (x == -1) ? rootNode : nodes[x];
376 if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
377 continue;
81bea17a 378 }
1354d172
AD
379 // Process data-dojo-attach-point
380 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
381 if(attachPoint){
382 var point, points = attachPoint.split(/\s*,\s*/);
383 while((point = points.shift())){
384 if(lang.isArray(this[point])){
385 this[point].push(baseNode);
386 }else{
387 this[point]=baseNode;
388 }
389 this._attachPoints.push(point);
81bea17a 390 }
a089699c 391 }
a089699c 392
1354d172
AD
393 // Process data-dojo-attach-event
394 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
395 if(attachEvent){
396 // NOTE: we want to support attributes that have the form
397 // "domEvent: nativeEvent; ..."
398 var event, events = attachEvent.split(/\s*,\s*/);
399 var trim = lang.trim;
400 while((event = events.shift())){
401 if(event){
402 var thisFunc = null;
403 if(event.indexOf(":") != -1){
404 // oh, if only JS had tuple assignment
405 var funcNameArr = event.split(":");
406 event = trim(funcNameArr[0]);
407 thisFunc = trim(funcNameArr[1]);
408 }else{
409 event = trim(event);
410 }
411 if(!thisFunc){
412 thisFunc = event;
413 }
414 // Map "press", "move" and "release" to keys.touch, keys.move, keys.release
415 this._attachEvents.push(this.connect(baseNode, touch[event] || event, thisFunc));
416 }
a089699c 417 }
a089699c 418 }
1354d172
AD
419 }
420 },
a089699c 421
1354d172
AD
422 destroyRendering: function(){
423 // Delete all attach points to prevent IE6 memory leaks.
424 array.forEach(this._attachPoints, function(point){
425 delete this[point];
426 }, this);
427 this._attachPoints = [];
a089699c 428
1354d172
AD
429 // And same for event handlers
430 array.forEach(this._attachEvents, this.disconnect, this);
431 this._attachEvents = [];
a089699c 432
1354d172
AD
433 this.inherited(arguments);
434 }
435 });
a089699c 436
1354d172
AD
437 // key is templateString; object is either string or DOM tree
438 _TemplatedMixin._templateCache = {};
439
440 _TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString){
441 // summary:
442 // Static method to get a template based on the templatePath or
443 // templateString key
444 // templateString: String
445 // The template
446 // alwaysUseString: Boolean
447 // Don't cache the DOM tree for this template, even if it doesn't have any variables
448 // returns: Mixed
449 // Either string (if there are ${} variables that need to be replaced) or just
450 // a DOM tree (if the node can be cloned directly)
451
452 // is it already cached?
453 var tmplts = _TemplatedMixin._templateCache;
454 var key = templateString;
455 var cached = tmplts[key];
456 if(cached){
457 try{
458 // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value
459 if(!cached.ownerDocument || cached.ownerDocument == win.doc){
460 // string or node of the same document
461 return cached;
a089699c 462 }
1354d172
AD
463 }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
464 domConstruct.destroy(cached);
465 }
466
467 templateString = string.trim(templateString);
468
469 if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
470 // there are variables in the template so all we can do is cache the string
471 return (tmplts[key] = templateString); //String
472 }else{
473 // there are no variables in the template so we can cache the DOM tree
474 var node = domConstruct.toDom(templateString);
475 if(node.nodeType != 1){
476 throw new Error("Invalid template: " + templateString);
477 }
478 return (tmplts[key] = node); //Node
a089699c 479 }
a089699c
AD
480 };
481
1354d172
AD
482 if(has("ie")){
483 unload.addOnWindowUnload(function(){
484 var cache = _TemplatedMixin._templateCache;
485 for(var key in cache){
486 var value = cache[key];
487 if(typeof value == "object"){ // value is either a string or a DOM node template
488 domConstruct.destroy(value);
a089699c 489 }
1354d172
AD
490 delete cache[key];
491 }
492 });
493 }
a089699c 494
1354d172
AD
495 // These arguments can be specified for widgets which are used in templates.
496 // Since any widget can be specified as sub widgets in template, mix it
497 // into the base widget class. (This is a hack, but it's effective.)
498 lang.extend(_WidgetBase,{
499 dojoAttachEvent: "",
500 dojoAttachPoint: ""
501 });
a089699c 502
1354d172
AD
503 return _TemplatedMixin;
504});
a089699c 505
1354d172
AD
506},
507'dijit/_CssStateMixin':function(){
508define("dijit/_CssStateMixin", [
509 "dojo/touch",
510 "dojo/_base/array", // array.forEach array.map
511 "dojo/_base/declare", // declare
512 "dojo/dom-class", // domClass.toggle
513 "dojo/_base/lang", // lang.hitch
514 "dojo/_base/window" // win.body
515], function(touch, array, declare, domClass, lang, win){
516
517// module:
518// dijit/_CssStateMixin
519// summary:
520// Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
521// state changes, and also higher-level state changes such becoming disabled or selected.
81bea17a 522
1354d172
AD
523return declare("dijit._CssStateMixin", [], {
524 // summary:
525 // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus
526 // state changes, and also higher-level state changes such becoming disabled or selected.
527 //
528 // description:
529 // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically
530 // maintain CSS classes on the widget root node (this.domNode) depending on hover,
531 // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes
532 // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it.
533 //
534 // It also sets CSS like dijitButtonDisabled based on widget semantic state.
535 //
536 // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons
537 // within the widget).
a089699c 538
1354d172
AD
539 // cssStateNodes: [protected] Object
540 // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus
541 //.
542 // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names
543 // (like "dijitUpArrowButton"). Example:
544 // | {
545 // | "upArrowButton": "dijitUpArrowButton",
546 // | "downArrowButton": "dijitDownArrowButton"
547 // | }
548 // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it
549 // is hovered, etc.
550 cssStateNodes: {},
a089699c 551
1354d172
AD
552 // hovering: [readonly] Boolean
553 // True if cursor is over this widget
554 hovering: false,
a089699c 555
1354d172
AD
556 // active: [readonly] Boolean
557 // True if mouse was pressed while over this widget, and hasn't been released yet
558 active: false,
a089699c 559
1354d172
AD
560 _applyAttributes: function(){
561 // This code would typically be in postCreate(), but putting in _applyAttributes() for
562 // performance: so the class changes happen before DOM is inserted into the document.
563 // Change back to postCreate() in 2.0. See #11635.
a089699c 564
1354d172 565 this.inherited(arguments);
a089699c 566
1354d172
AD
567 // Automatically monitor mouse events (essentially :hover and :active) on this.domNode
568 array.forEach(["onmouseenter", "onmouseleave", touch.press], function(e){
569 this.connect(this.domNode, e, "_cssMouseEvent");
570 }, this);
a089699c 571
1354d172
AD
572 // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
573 array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active"], function(attr){
574 this.watch(attr, lang.hitch(this, "_setStateClass"));
575 }, this);
81bea17a 576
1354d172
AD
577 // Events on sub nodes within the widget
578 for(var ap in this.cssStateNodes){
579 this._trackMouseState(this[ap], this.cssStateNodes[ap]);
580 }
581 // Set state initially; there's probably no hover/active/focus state but widget might be
582 // disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
583 this._setStateClass();
584 },
a089699c 585
1354d172
AD
586 _cssMouseEvent: function(/*Event*/ event){
587 // summary:
588 // Sets hovering and active properties depending on mouse state,
589 // which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
a089699c 590
1354d172
AD
591 if(!this.disabled){
592 switch(event.type){
593 case "mouseenter":
594 case "mouseover": // generated on non-IE browsers even though we connected to mouseenter
595 this._set("hovering", true);
596 this._set("active", this._mouseDown);
597 break;
a089699c 598
1354d172
AD
599 case "mouseleave":
600 case "mouseout": // generated on non-IE browsers even though we connected to mouseleave
601 this._set("hovering", false);
602 this._set("active", false);
603 break;
a089699c 604
1354d172
AD
605 case "mousedown":
606 case "touchpress":
607 this._set("active", true);
608 this._mouseDown = true;
609 // Set a global event to handle mouseup, so it fires properly
610 // even if the cursor leaves this.domNode before the mouse up event.
611 // Alternately could set active=false on mouseout.
612 var mouseUpConnector = this.connect(win.body(), touch.release, function(){
613 this._mouseDown = false;
614 this._set("active", false);
615 this.disconnect(mouseUpConnector);
616 });
617 break;
618 }
619 }
620 },
a089699c 621
1354d172
AD
622 _setStateClass: function(){
623 // summary:
624 // Update the visual state of the widget by setting the css classes on this.domNode
625 // (or this.stateNode if defined) by combining this.baseClass with
626 // various suffixes that represent the current widget state(s).
627 //
628 // description:
629 // In the case where a widget has multiple
630 // states, it sets the class based on all possible
631 // combinations. For example, an invalid form widget that is being hovered
632 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
633 //
634 // The widget may have one or more of the following states, determined
635 // by this.state, this.checked, this.valid, and this.selected:
636 // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
637 // - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
638 // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
639 // - Selected - ex: currently selected tab will have this.selected==true
640 //
641 // In addition, it may have one or more of the following states,
642 // based on this.disabled and flags set in _onMouse (this.active, this.hovering) and from focus manager (this.focused):
643 // - Disabled - if the widget is disabled
644 // - Active - if the mouse (or space/enter key?) is being pressed down
645 // - Focused - if the widget has focus
646 // - Hover - if the mouse is over the widget
a089699c 647
1354d172
AD
648 // Compute new set of classes
649 var newStateClasses = this.baseClass.split(" ");
a089699c 650
1354d172
AD
651 function multiply(modifier){
652 newStateClasses = newStateClasses.concat(array.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier);
a089699c 653 }
a089699c 654
1354d172
AD
655 if(!this.isLeftToRight()){
656 // For RTL mode we need to set an addition class like dijitTextBoxRtl.
657 multiply("Rtl");
81bea17a 658 }
a089699c 659
1354d172
AD
660 var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
661 if(this.checked){
662 multiply(checkedState);
663 }
664 if(this.state){
665 multiply(this.state);
666 }
667 if(this.selected){
668 multiply("Selected");
669 }
a089699c 670
1354d172
AD
671 if(this.disabled){
672 multiply("Disabled");
673 }else if(this.readOnly){
674 multiply("ReadOnly");
675 }else{
676 if(this.active){
677 multiply("Active");
678 }else if(this.hovering){
679 multiply("Hover");
680 }
681 }
a089699c 682
1354d172
AD
683 if(this.focused){
684 multiply("Focused");
685 }
81bea17a 686
1354d172
AD
687 // Remove old state classes and add new ones.
688 // For performance concerns we only write into domNode.className once.
689 var tn = this.stateNode || this.domNode,
690 classHash = {}; // set of all classes (state and otherwise) for node
a089699c 691
1354d172 692 array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
a089699c 693
1354d172
AD
694 if("_stateClasses" in this){
695 array.forEach(this._stateClasses, function(c){ delete classHash[c]; });
a089699c 696 }
a089699c 697
1354d172
AD
698 array.forEach(newStateClasses, function(c){ classHash[c] = true; });
699
700 var newClasses = [];
701 for(var c in classHash){
702 newClasses.push(c);
a089699c 703 }
1354d172
AD
704 tn.className = newClasses.join(" ");
705
706 this._stateClasses = newStateClasses;
a089699c
AD
707 },
708
1354d172 709 _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){
a089699c 710 // summary:
1354d172
AD
711 // Track mouse/focus events on specified node and set CSS class on that node to indicate
712 // current state. Usually not called directly, but via cssStateNodes attribute.
713 // description:
714 // Given class=foo, will set the following CSS class on the node
715 // - fooActive: if the user is currently pressing down the mouse button while over the node
716 // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button
717 // - fooFocus: if the node is focused
a089699c 718 //
1354d172
AD
719 // Note that it won't set any classes if the widget is disabled.
720 // node: DomNode
721 // Should be a sub-node of the widget, not the top node (this.domNode), since the top node
722 // is handled specially and automatically just by mixing in this class.
723 // clazz: String
724 // CSS class name (ex: dijitSliderUpArrow).
a089699c 725
1354d172
AD
726 // Current state of node (initially false)
727 // NB: setting specifically to false because domClass.toggle() needs true boolean as third arg
728 var hovering=false, active=false, focused=false;
a089699c 729
1354d172
AD
730 var self = this,
731 cn = lang.hitch(this, "connect", node);
a089699c 732
1354d172
AD
733 function setClass(){
734 var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly);
735 domClass.toggle(node, clazz+"Hover", hovering && !active && !disabled);
736 domClass.toggle(node, clazz+"Active", active && !disabled);
737 domClass.toggle(node, clazz+"Focused", focused && !disabled);
a089699c 738 }
a089699c 739
1354d172
AD
740 // Mouse
741 cn("onmouseenter", function(){
742 hovering = true;
743 setClass();
744 });
745 cn("onmouseleave", function(){
746 hovering = false;
747 active = false;
748 setClass();
749 });
750 cn(touch.press, function(){
751 active = true;
752 setClass();
753 });
754 cn(touch.release, function(){
755 active = false;
756 setClass();
757 });
a089699c 758
1354d172
AD
759 // Focus
760 cn("onfocus", function(){
761 focused = true;
762 setClass();
763 });
764 cn("onblur", function(){
765 focused = false;
766 setClass();
767 });
a089699c 768
1354d172
AD
769 // Just in case widget is enabled/disabled while it has focus/hover/active state.
770 // Maybe this is overkill.
771 this.watch("disabled", setClass);
772 this.watch("readOnly", setClass);
773 }
774});
775});
a089699c 776
a089699c 777},
1354d172
AD
778'dijit/DialogUnderlay':function(){
779define("dijit/DialogUnderlay", [
780 "dojo/_base/declare", // declare
781 "dojo/dom-attr", // domAttr.set
782 "dojo/_base/window", // win.body
783 "dojo/window", // winUtils.getBox
784 "./_Widget",
785 "./_TemplatedMixin",
786 "./BackgroundIframe"
787], function(declare, domAttr, win, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
a089699c 788
1354d172
AD
789/*=====
790 var _Widget = dijit._Widget;
791 var _TemplatedMixin = dijit._TemplatedMixin;
792=====*/
a089699c 793
1354d172
AD
794 // module:
795 // dijit/DialogUnderlay
796 // summary:
797 // The component that blocks the screen behind a `dijit.Dialog`
a089699c 798
1354d172 799 return declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
a089699c 800 // summary:
1354d172 801 // The component that blocks the screen behind a `dijit.Dialog`
a089699c 802 //
1354d172
AD
803 // description:
804 // A component used to block input behind a `dijit.Dialog`. Only a single
805 // instance of this widget is created by `dijit.Dialog`, and saved as
806 // a reference to be shared between all Dialogs as `dijit._underlay`
a089699c 807 //
1354d172
AD
808 // The underlay itself can be styled based on and id:
809 // | #myDialog_underlay { background-color:red; }
a089699c 810 //
1354d172
AD
811 // In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
812 // suffixed with _underlay.
a089699c 813
1354d172
AD
814 // Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
815 // Inner div has opacity specified in CSS file.
816 templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
a089699c 817
1354d172 818 // Parameters on creation or updatable later
a089699c 819
1354d172
AD
820 // dialogId: String
821 // Id of the dialog.... DialogUnderlay's id is based on this id
822 dialogId: "",
a089699c 823
1354d172
AD
824 // class: String
825 // This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
826 "class": "",
a089699c 827
1354d172
AD
828 _setDialogIdAttr: function(id){
829 domAttr.set(this.node, "id", id + "_underlay");
830 this._set("dialogId", id);
831 },
a089699c 832
1354d172
AD
833 _setClassAttr: function(clazz){
834 this.node.className = "dijitDialogUnderlay " + clazz;
835 this._set("class", clazz);
836 },
a089699c 837
1354d172
AD
838 postCreate: function(){
839 // summary:
840 // Append the underlay to the body
841 win.body().appendChild(this.domNode);
842 },
a089699c 843
1354d172
AD
844 layout: function(){
845 // summary:
846 // Sets the background to the size of the viewport
847 //
848 // description:
849 // Sets the background to the size of the viewport (rather than the size
850 // of the document) since we need to cover the whole browser window, even
851 // if the document is only a few lines long.
852 // tags:
853 // private
a089699c 854
1354d172
AD
855 var is = this.node.style,
856 os = this.domNode.style;
a089699c 857
1354d172
AD
858 // hide the background temporarily, so that the background itself isn't
859 // causing scrollbars to appear (might happen when user shrinks browser
860 // window and then we are called to resize)
861 os.display = "none";
a089699c 862
1354d172
AD
863 // then resize and show
864 var viewport = winUtils.getBox();
865 os.top = viewport.t + "px";
866 os.left = viewport.l + "px";
867 is.width = viewport.w + "px";
868 is.height = viewport.h + "px";
869 os.display = "block";
870 },
a089699c 871
1354d172
AD
872 show: function(){
873 // summary:
874 // Show the dialog underlay
875 this.domNode.style.display = "block";
876 this.layout();
877 this.bgIframe = new BackgroundIframe(this.domNode);
878 },
879
880 hide: function(){
881 // summary:
882 // Hides the dialog underlay
883 this.bgIframe.destroy();
884 delete this.bgIframe;
885 this.domNode.style.display = "none";
81bea17a 886 }
a089699c 887 });
1354d172 888});
a089699c 889
1354d172
AD
890},
891'dijit/layout/ScrollingTabController':function(){
892require({cache:{
893'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
894'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>"}});
895define("dijit/layout/ScrollingTabController", [
896 "dojo/_base/array", // array.forEach
897 "dojo/_base/declare", // declare
898 "dojo/dom-class", // domClass.add domClass.contains
899 "dojo/dom-geometry", // domGeometry.contentBox
900 "dojo/dom-style", // domStyle.style
901 "dojo/_base/fx", // Animation
902 "dojo/_base/lang", // lang.hitch
903 "dojo/query", // query
904 "dojo/_base/sniff", // has("ie"), has("webkit"), has("quirks")
905 "../registry", // registry.byId()
906 "dojo/text!./templates/ScrollingTabController.html",
907 "dojo/text!./templates/_ScrollingTabControllerButton.html",
908 "./TabController",
909 "./utils", // marginBox2contextBox, layoutChildren
910 "../_WidgetsInTemplateMixin",
911 "../Menu",
912 "../MenuItem",
913 "../form/Button",
914 "../_HasDropDown",
915 "dojo/NodeList-dom" // NodeList.style
916], function(array, declare, domClass, domGeometry, domStyle, fx, lang, query, has,
917 registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
918 Menu, MenuItem, Button, _HasDropDown){
a089699c 919
1354d172
AD
920/*=====
921var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
922var Menu = dijit.Menu;
923var _HasDropDown = dijit._HasDropDown;
924var TabController = dijit.layout.TabController;
925=====*/
a089699c 926
a089699c 927
1354d172
AD
928// module:
929// dijit/layout/ScrollingTabController
930// summary:
931// Set of tabs with left/right arrow keys and a menu to switch between tabs not
932// all fitting on a single row.
a089699c 933
81bea17a 934
1354d172
AD
935var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
936 // summary:
937 // Set of tabs with left/right arrow keys and a menu to switch between tabs not
938 // all fitting on a single row.
939 // Works only for horizontal tabs (either above or below the content, not to the left
940 // or right).
941 // tags:
942 // private
a089699c 943
1354d172 944 baseClass: "dijitTabController dijitScrollingTabController",
a089699c 945
1354d172 946 templateString: tabControllerTemplate,
a089699c 947
1354d172
AD
948 // useMenu: [const] Boolean
949 // True if a menu should be used to select tabs when they are too
950 // wide to fit the TabContainer, false otherwise.
951 useMenu: true,
a089699c 952
1354d172
AD
953 // useSlider: [const] Boolean
954 // True if a slider should be used to select tabs when they are too
955 // wide to fit the TabContainer, false otherwise.
956 useSlider: true,
a089699c 957
1354d172
AD
958 // tabStripClass: [const] String
959 // The css class to apply to the tab strip, if it is visible.
960 tabStripClass: "",
a089699c 961
1354d172 962 widgetsInTemplate: true,
a089699c 963
1354d172
AD
964 // _minScroll: Number
965 // The distance in pixels from the edge of the tab strip which,
966 // if a scroll animation is less than, forces the scroll to
967 // go all the way to the left/right.
968 _minScroll: 5,
a089699c 969
1354d172
AD
970 // Override default behavior mapping class to DOMNode
971 _setClassAttr: { node: "containerNode", type: "class" },
a089699c 972
1354d172
AD
973 buildRendering: function(){
974 this.inherited(arguments);
975 var n = this.domNode;
a089699c 976
1354d172
AD
977 this.scrollNode = this.tablistWrapper;
978 this._initButtons();
a089699c 979
1354d172
AD
980 if(!this.tabStripClass){
981 this.tabStripClass = "dijitTabContainer" +
982 this.tabPosition.charAt(0).toUpperCase() +
983 this.tabPosition.substr(1).replace(/-.*/, "") +
984 "None";
985 domClass.add(n, "tabStrip-disabled")
986 }
a089699c 987
1354d172 988 domClass.add(this.tablistWrapper, this.tabStripClass);
a089699c
AD
989 },
990
1354d172
AD
991 onStartup: function(){
992 this.inherited(arguments);
a089699c 993
1354d172
AD
994 // TabController is hidden until it finishes drawing, to give
995 // a less visually jumpy instantiation. When it's finished, set visibility to ""
996 // to that the tabs are hidden/shown depending on the container's visibility setting.
997 domStyle.set(this.domNode, "visibility", "");
998 this._postStartup = true;
a089699c
AD
999 },
1000
1354d172
AD
1001 onAddChild: function(page, insertIndex){
1002 this.inherited(arguments);
a089699c 1003
1354d172
AD
1004 // changes to the tab button label or iconClass will have changed the width of the
1005 // buttons, so do a resize
1006 array.forEach(["label", "iconClass"], function(attr){
1007 this.pane2watches[page.id].push(
1008 this.pane2button[page.id].watch(attr, lang.hitch(this, function(){
1009 if(this._postStartup && this._dim){
1010 this.resize(this._dim);
a089699c 1011 }
1354d172
AD
1012 }))
1013 );
1014 }, this);
a089699c 1015
1354d172
AD
1016 // Increment the width of the wrapper when a tab is added
1017 // This makes sure that the buttons never wrap.
1018 // The value 200 is chosen as it should be bigger than most
1019 // Tab button widths.
1020 domStyle.set(this.containerNode, "width",
1021 (domStyle.get(this.containerNode, "width") + 200) + "px");
1022 },
a089699c 1023
1354d172
AD
1024 onRemoveChild: function(page, insertIndex){
1025 // null out _selectedTab because we are about to delete that dom node
1026 var button = this.pane2button[page.id];
1027 if(this._selectedTab === button.domNode){
1028 this._selectedTab = null;
a089699c 1029 }
1354d172
AD
1030
1031 this.inherited(arguments);
a089699c
AD
1032 },
1033
1354d172 1034 _initButtons: function(){
a089699c 1035 // summary:
1354d172
AD
1036 // Creates the buttons used to scroll to view tabs that
1037 // may not be visible if the TabContainer is too narrow.
a089699c 1038
1354d172
AD
1039 // Make a list of the buttons to display when the tab labels become
1040 // wider than the TabContainer, and hide the other buttons.
1041 // Also gets the total width of the displayed buttons.
1042 this._btnWidth = 0;
1043 this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){
1044 if((this.useMenu && btn == this._menuBtn.domNode) ||
1045 (this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
1046 this._btnWidth += domGeometry.getMarginSize(btn).w;
1047 return true;
1048 }else{
1049 domStyle.set(btn, "display", "none");
1050 return false;
1051 }
1052 }, this);
a089699c
AD
1053 },
1054
1354d172
AD
1055 _getTabsWidth: function(){
1056 var children = this.getChildren();
1057 if(children.length){
1058 var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
1059 rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
1060 return rightTab.offsetLeft + domStyle.get(rightTab, "width") - leftTab.offsetLeft;
1061 }else{
1062 return 0;
a089699c 1063 }
1354d172 1064 },
a089699c 1065
1354d172
AD
1066 _enableBtn: function(width){
1067 // summary:
1068 // Determines if the tabs are wider than the width of the TabContainer, and
1069 // thus that we need to display left/right/menu navigation buttons.
1070 var tabsWidth = this._getTabsWidth();
1071 width = width || domStyle.get(this.scrollNode, "width");
1072 return tabsWidth > 0 && width < tabsWidth;
a089699c
AD
1073 },
1074
1354d172 1075 resize: function(dim){
a089699c 1076 // summary:
1354d172
AD
1077 // Hides or displays the buttons used to scroll the tab list and launch the menu
1078 // that selects tabs.
a089699c 1079
1354d172
AD
1080 // Save the dimensions to be used when a child is renamed.
1081 this._dim = dim;
a089699c 1082
1354d172
AD
1083 // Set my height to be my natural height (tall enough for one row of tab labels),
1084 // and my content-box width based on margin-box width specified in dim parameter.
1085 // But first reset scrollNode.height in case it was set by layoutChildren() call
1086 // in a previous run of this method.
1087 this.scrollNode.style.height = "auto";
1088 var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
1089 cb.h = this.scrollNode.offsetHeight;
1090 domGeometry.setContentSize(this.domNode, cb);
a089699c 1091
1354d172
AD
1092 // Show/hide the left/right/menu navigation buttons depending on whether or not they
1093 // are needed.
1094 var enable = this._enableBtn(this._contentBox.w);
1095 this._buttons.style("display", enable ? "" : "none");
a089699c 1096
1354d172
AD
1097 // Position and size the navigation buttons and the tablist
1098 this._leftBtn.layoutAlign = "left";
1099 this._rightBtn.layoutAlign = "right";
1100 this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
1101 layoutUtils.layoutChildren(this.domNode, this._contentBox,
1102 [this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
a089699c 1103
1354d172
AD
1104 // set proper scroll so that selected tab is visible
1105 if(this._selectedTab){
1106 if(this._anim && this._anim.status() == "playing"){
1107 this._anim.stop();
1108 }
1109 this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab());
a089699c
AD
1110 }
1111
1354d172
AD
1112 // Enable/disabled left right buttons depending on whether or not user can scroll to left or right
1113 this._setButtonClass(this._getScroll());
a089699c 1114
1354d172 1115 this._postResize = true;
a089699c 1116
1354d172
AD
1117 // Return my size so layoutChildren() can use it.
1118 // Also avoids IE9 layout glitch on browser resize when scroll buttons present
1119 return {h: this._contentBox.h, w: dim.w};
a089699c
AD
1120 },
1121
1354d172 1122 _getScroll: function(){
a089699c 1123 // summary:
1354d172
AD
1124 // Returns the current scroll of the tabs where 0 means
1125 // "scrolled all the way to the left" and some positive number, based on #
1126 // of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
1127 return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft :
1128 domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width")
1129 + (has("ie") == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
1130 },
a089699c 1131
1354d172
AD
1132 _convertToScrollLeft: function(val){
1133 // summary:
1134 // Given a scroll value where 0 means "scrolled all the way to the left"
1135 // and some positive number, based on # of pixels of possible scroll (ex: 1000)
1136 // means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
1137 // to achieve that scroll.
1138 //
1139 // This method is to adjust for RTL funniness in various browsers and versions.
1140 if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
1141 return val;
1142 }else{
1143 var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width");
1144 return (has("ie") == 8 ? -1 : 1) * (val - maxScroll);
a089699c 1145 }
1354d172 1146 },
a089699c 1147
1354d172
AD
1148 onSelectChild: function(/*dijit._Widget*/ page){
1149 // summary:
1150 // Smoothly scrolls to a tab when it is selected.
a089699c 1151
1354d172
AD
1152 var tab = this.pane2button[page.id];
1153 if(!tab || !page){return;}
a089699c 1154
1354d172 1155 var node = tab.domNode;
81bea17a 1156
1354d172
AD
1157 // Save the selection
1158 if(node != this._selectedTab){
1159 this._selectedTab = node;
a089699c 1160
1354d172
AD
1161 // Scroll to the selected tab, except on startup, when scrolling is handled in resize()
1162 if(this._postResize){
1163 var sl = this._getScroll();
a089699c 1164
1354d172
AD
1165 if(sl > node.offsetLeft ||
1166 sl + domStyle.get(this.scrollNode, "width") <
1167 node.offsetLeft + domStyle.get(node, "width")){
1168 this.createSmoothScroll().play();
a089699c
AD
1169 }
1170 }
1171 }
a089699c 1172
1354d172
AD
1173 this.inherited(arguments);
1174 },
a089699c 1175
1354d172
AD
1176 _getScrollBounds: function(){
1177 // summary:
1178 // Returns the minimum and maximum scroll setting to show the leftmost and rightmost
1179 // tabs (respectively)
1180 var children = this.getChildren(),
1181 scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
1182 containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
1183 maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
1184 tabsWidth = this._getTabsWidth();
1185
1186 if(children.length && tabsWidth > scrollNodeWidth){
1187 // Scrolling should happen
1188 return {
1189 min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
1190 max: this.isLeftToRight() ?
1191 (children[children.length-1].domNode.offsetLeft + domStyle.get(children[children.length-1].domNode, "width")) - scrollNodeWidth :
1192 maxPossibleScroll
1193 };
1194 }else{
1195 // No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
1196 var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
1197 return {
1198 min: onlyScrollPosition,
1199 max: onlyScrollPosition
1200 };
a089699c 1201 }
1354d172 1202 },
a089699c 1203
1354d172
AD
1204 _getScrollForSelectedTab: function(){
1205 // summary:
1206 // Returns the scroll value setting so that the selected tab
1207 // will appear in the center
1208 var w = this.scrollNode,
1209 n = this._selectedTab,
1210 scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
1211 scrollBounds = this._getScrollBounds();
a089699c 1212
1354d172
AD
1213 // TODO: scroll minimal amount (to either right or left) so that
1214 // selected tab is fully visible, and just return if it's already visible?
1215 var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
1216 pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
a089699c 1217
1354d172
AD
1218 // TODO:
1219 // If scrolling close to the left side or right side, scroll
1220 // all the way to the left or right. See this._minScroll.
1221 // (But need to make sure that doesn't scroll the tab out of view...)
1222 return pos;
1223 },
a089699c 1224
1354d172
AD
1225 createSmoothScroll: function(x){
1226 // summary:
1227 // Creates a dojo._Animation object that smoothly scrolls the tab list
1228 // either to a fixed horizontal pixel value, or to the selected tab.
1229 // description:
1230 // If an number argument is passed to the function, that horizontal
1231 // pixel position is scrolled to. Otherwise the currently selected
1232 // tab is scrolled to.
1233 // x: Integer?
1234 // An optional pixel value to scroll to, indicating distance from left.
a089699c 1235
1354d172
AD
1236 // Calculate position to scroll to
1237 if(arguments.length > 0){
1238 // position specified by caller, just make sure it's within bounds
1239 var scrollBounds = this._getScrollBounds();
1240 x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
1241 }else{
1242 // scroll to center the current tab
1243 x = this._getScrollForSelectedTab();
1244 }
a089699c 1245
1354d172
AD
1246 if(this._anim && this._anim.status() == "playing"){
1247 this._anim.stop();
1248 }
a089699c 1249
1354d172
AD
1250 var self = this,
1251 w = this.scrollNode,
1252 anim = new fx.Animation({
1253 beforeBegin: function(){
1254 if(this.curve){ delete this.curve; }
1255 var oldS = w.scrollLeft,
1256 newS = self._convertToScrollLeft(x);
1257 anim.curve = new fx._Line(oldS, newS);
1258 },
1259 onAnimate: function(val){
1260 w.scrollLeft = val;
1261 }
1262 });
1263 this._anim = anim;
a089699c 1264
1354d172
AD
1265 // Disable/enable left/right buttons according to new scroll position
1266 this._setButtonClass(x);
a089699c 1267
1354d172
AD
1268 return anim; // dojo._Animation
1269 },
a089699c 1270
1354d172
AD
1271 _getBtnNode: function(/*Event*/ e){
1272 // summary:
1273 // Gets a button DOM node from a mouse click event.
1274 // e:
1275 // The mouse click event.
1276 var n = e.target;
1277 while(n && !domClass.contains(n, "tabStripButton")){
1278 n = n.parentNode;
1279 }
1280 return n;
1281 },
a089699c 1282
1354d172
AD
1283 doSlideRight: function(/*Event*/ e){
1284 // summary:
1285 // Scrolls the menu to the right.
1286 // e:
1287 // The mouse click event.
1288 this.doSlide(1, this._getBtnNode(e));
1289 },
a089699c 1290
1354d172
AD
1291 doSlideLeft: function(/*Event*/ e){
1292 // summary:
1293 // Scrolls the menu to the left.
1294 // e:
1295 // The mouse click event.
1296 this.doSlide(-1,this._getBtnNode(e));
1297 },
a089699c 1298
1354d172
AD
1299 doSlide: function(/*Number*/ direction, /*DomNode*/ node){
1300 // summary:
1301 // Scrolls the tab list to the left or right by 75% of the widget width.
1302 // direction:
1303 // If the direction is 1, the widget scrolls to the right, if it is
1304 // -1, it scrolls to the left.
81bea17a 1305
1354d172 1306 if(node && domClass.contains(node, "dijitTabDisabled")){return;}
a089699c 1307
1354d172
AD
1308 var sWidth = domStyle.get(this.scrollNode, "width");
1309 var d = (sWidth * 0.75) * direction;
a089699c 1310
1354d172 1311 var to = this._getScroll() + d;
81bea17a 1312
1354d172 1313 this._setButtonClass(to);
a089699c 1314
1354d172
AD
1315 this.createSmoothScroll(to).play();
1316 },
a089699c 1317
1354d172
AD
1318 _setButtonClass: function(/*Number*/ scroll){
1319 // summary:
1320 // Disables the left scroll button if the tabs are scrolled all the way to the left,
1321 // or the right scroll button in the opposite case.
1322 // scroll: Integer
1323 // amount of horizontal scroll
a089699c 1324
1354d172
AD
1325 var scrollBounds = this._getScrollBounds();
1326 this._leftBtn.set("disabled", scroll <= scrollBounds.min);
1327 this._rightBtn.set("disabled", scroll >= scrollBounds.max);
1328 }
1329});
a089699c 1330
a089699c 1331
1354d172
AD
1332var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
1333 baseClass: "dijitTab tabStripButton",
a089699c 1334
1354d172 1335 templateString: buttonTemplate,
a089699c 1336
1354d172
AD
1337 // Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
1338 // able to tab to the left/right/menu buttons
1339 tabIndex: "",
a089699c 1340
1354d172
AD
1341 // Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
1342 // either (this override avoids focus() call in FormWidget.js)
1343 isFocusable: function(){ return false; }
1344});
1345/*=====
1346ScrollingTabControllerButtonMixin = dijit.layout._ScrollingTabControllerButtonMixin;
1347=====*/
a089699c 1348
1354d172
AD
1349// Class used in template
1350declare("dijit.layout._ScrollingTabControllerButton",
1351 [Button, ScrollingTabControllerButtonMixin]);
a089699c 1352
1354d172
AD
1353// Class used in template
1354declare(
1355 "dijit.layout._ScrollingTabControllerMenuButton",
1356 [Button, _HasDropDown, ScrollingTabControllerButtonMixin],
1357{
1358 // id of the TabContainer itself
1359 containerId: "",
a089699c 1360
1354d172
AD
1361 // -1 so user can't tab into the button, but so that button can still be focused programatically.
1362 // Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
1363 tabIndex: "-1",
a089699c 1364
1354d172
AD
1365 isLoaded: function(){
1366 // recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
1367 return false;
1368 },
a089699c 1369
1354d172
AD
1370 loadDropDown: function(callback){
1371 this.dropDown = new Menu({
1372 id: this.containerId + "_menu",
1373 dir: this.dir,
1374 lang: this.lang,
1375 textDir: this.textDir
a089699c 1376 });
1354d172
AD
1377 var container = registry.byId(this.containerId);
1378 array.forEach(container.getChildren(), function(page){
1379 var menuItem = new MenuItem({
1380 id: page.id + "_stcMi",
1381 label: page.title,
1382 iconClass: page.iconClass,
1383 dir: page.dir,
1384 lang: page.lang,
1385 textDir: page.textDir,
1386 onClick: function(){
1387 container.selectChild(page);
1388 }
1389 });
1390 this.dropDown.addChild(menuItem);
1391 }, this);
1392 callback();
a089699c 1393 },
a089699c 1394
1354d172
AD
1395 closeDropDown: function(/*Boolean*/ focus){
1396 this.inherited(arguments);
1397 if(this.dropDown){
1398 this.dropDown.destroyRecursive();
1399 delete this.dropDown;
a089699c 1400 }
1354d172
AD
1401 }
1402});
a089699c 1403
1354d172
AD
1404return ScrollingTabController;
1405});
a089699c 1406
1354d172
AD
1407},
1408'dijit/place':function(){
1409define("dijit/place", [
1410 "dojo/_base/array", // array.forEach array.map array.some
1411 "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
1412 "dojo/dom-style", // domStyle.getComputedStyle
1413 "dojo/_base/kernel", // kernel.deprecated
1414 "dojo/_base/window", // win.body
1415 "dojo/window", // winUtils.getBox
1416 "." // dijit (defining dijit.place to match API doc)
1417], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
1418
1419 // module:
1420 // dijit/place
1421 // summary:
1422 // Code to place a popup relative to another node
1423
1424
1425 function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){
1426 // summary:
1427 // Given a list of spots to put node, put it at the first spot where it fits,
1428 // of if it doesn't fit anywhere then the place with the least overflow
1429 // choices: Array
1430 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
1431 // Above example says to put the top-left corner of the node at (10,20)
1432 // layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
1433 // for things like tooltip, they are displayed differently (and have different dimensions)
1434 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1435 // It also passes in the available size for the popup, which is useful for tooltips to
1436 // tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
1437 // how much the popup had to be modified to fit into the available space. This is used to determine
1438 // what the best placement is.
1439 // aroundNodeCoords: Object
1440 // Size of aroundNode, ex: {w: 200, h: 50}
1441
1442 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
1443 // viewport over document
1444 var view = winUtils.getBox();
1445
1446 // This won't work if the node is inside a <div style="position: relative">,
1447 // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
1448 // and also it might get cutoff)
1449 if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
1450 win.body().appendChild(node);
1451 }
1452
1453 var best = null;
1454 array.some(choices, function(choice){
1455 var corner = choice.corner;
1456 var pos = choice.pos;
1457 var overflow = 0;
1458
1459 // calculate amount of space available given specified position of node
1460 var spaceAvailable = {
1461 w: {
1462 'L': view.l + view.w - pos.x,
1463 'R': pos.x - view.l,
1464 'M': view.w
1465 }[corner.charAt(1)],
1466 h: {
1467 'T': view.t + view.h - pos.y,
1468 'B': pos.y - view.t,
1469 'M': view.h
1470 }[corner.charAt(0)]
1471 };
a089699c 1472
1354d172
AD
1473 // configure node to be displayed in given position relative to button
1474 // (need to do this in order to get an accurate size for the node, because
1475 // a tooltip's size changes based on position, due to triangle)
1476 if(layoutNode){
1477 var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
1478 overflow = typeof res == "undefined" ? 0 : res;
1479 }
a089699c 1480
1354d172
AD
1481 // get node's size
1482 var style = node.style;
1483 var oldDisplay = style.display;
1484 var oldVis = style.visibility;
1485 if(style.display == "none"){
1486 style.visibility = "hidden";
1487 style.display = "";
1488 }
1489 var mb = domGeometry. getMarginBox(node);
1490 style.display = oldDisplay;
1491 style.visibility = oldVis;
a089699c 1492
1354d172
AD
1493 // coordinates and size of node with specified corner placed at pos,
1494 // and clipped by viewport
1495 var
1496 startXpos = {
1497 'L': pos.x,
1498 'R': pos.x - mb.w,
1499 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (mb.w >> 1)) - mb.w) // M orientation is more flexible
1500 }[corner.charAt(1)],
1501 startYpos = {
1502 'T': pos.y,
1503 'B': pos.y - mb.h,
1504 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (mb.h >> 1)) - mb.h)
1505 }[corner.charAt(0)],
1506 startX = Math.max(view.l, startXpos),
1507 startY = Math.max(view.t, startYpos),
1508 endX = Math.min(view.l + view.w, startXpos + mb.w),
1509 endY = Math.min(view.t + view.h, startYpos + mb.h),
1510 width = endX - startX,
1511 height = endY - startY;
1512
1513 overflow += (mb.w - width) + (mb.h - height);
1514
1515 if(best == null || overflow < best.overflow){
1516 best = {
1517 corner: corner,
1518 aroundCorner: choice.aroundCorner,
1519 x: startX,
1520 y: startY,
1521 w: width,
1522 h: height,
1523 overflow: overflow,
1524 spaceAvailable: spaceAvailable
1525 };
1526 }
a089699c 1527
1354d172
AD
1528 return !overflow;
1529 });
a089699c 1530
1354d172
AD
1531 // In case the best position is not the last one we checked, need to call
1532 // layoutNode() again.
1533 if(best.overflow && layoutNode){
1534 layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
1535 }
a089699c 1536
1354d172
AD
1537 // And then position the node. Do this last, after the layoutNode() above
1538 // has sized the node, due to browser quirks when the viewport is scrolled
1539 // (specifically that a Tooltip will shrink to fit as though the window was
1540 // scrolled to the left).
1541 //
1542 // In RTL mode, set style.right rather than style.left so in the common case,
1543 // window resizes move the popup along with the aroundNode.
1544 var l = domGeometry.isBodyLtr(),
1545 s = node.style;
1546 s.top = best.y + "px";
1547 s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
1548 s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
a089699c 1549
1354d172
AD
1550 return best;
1551 }
a089699c 1552
1354d172
AD
1553 /*=====
1554 dijit.place.__Position = function(){
1555 // x: Integer
1556 // horizontal coordinate in pixels, relative to document body
1557 // y: Integer
1558 // vertical coordinate in pixels, relative to document body
1559
1560 this.x = x;
1561 this.y = y;
1562 };
1563 =====*/
a089699c 1564
1354d172
AD
1565 /*=====
1566 dijit.place.__Rectangle = function(){
1567 // x: Integer
1568 // horizontal offset in pixels, relative to document body
1569 // y: Integer
1570 // vertical offset in pixels, relative to document body
1571 // w: Integer
1572 // width in pixels. Can also be specified as "width" for backwards-compatibility.
1573 // h: Integer
1574 // height in pixels. Can also be specified as "height" from backwards-compatibility.
1575
1576 this.x = x;
1577 this.y = y;
1578 this.w = w;
1579 this.h = h;
1580 };
1581 =====*/
a089699c 1582
1354d172
AD
1583 return (dijit.place = {
1584 // summary:
1585 // Code to place a DOMNode relative to another DOMNode.
1586 // Load using require(["dijit/place"], function(place){ ... }).
81bea17a 1587
1354d172
AD
1588 at: function(node, pos, corners, padding){
1589 // summary:
1590 // Positions one of the node's corners at specified position
1591 // such that node is fully visible in viewport.
1592 // description:
1593 // NOTE: node is assumed to be absolutely or relatively positioned.
1594 // node: DOMNode
1595 // The node to position
1596 // pos: dijit.place.__Position
1597 // Object like {x: 10, y: 20}
1598 // corners: String[]
1599 // Array of Strings representing order to try corners in, like ["TR", "BL"].
1600 // 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.
1607 // example:
1608 // Try to place node's top right corner at (10,20).
1609 // If that makes node go (partially) off screen, then try placing
1610 // bottom left corner at (10,20).
1611 // | place(node, {x: 10, y: 20}, ["TR", "BL"])
1612 var choices = array.map(corners, function(corner){
1613 var c = { corner: corner, pos: {x:pos.x,y:pos.y} };
1614 if(padding){
1615 c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x;
1616 c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y;
1617 }
1618 return c;
1619 });
a089699c 1620
1354d172
AD
1621 return _place(node, choices);
1622 },
a089699c 1623
1354d172
AD
1624 around: function(
1625 /*DomNode*/ node,
1626 /*DomNode || dijit.place.__Rectangle*/ anchor,
1627 /*String[]*/ positions,
1628 /*Boolean*/ leftToRight,
1629 /*Function?*/ layoutNode){
a089699c 1630
1354d172
AD
1631 // summary:
1632 // Position node adjacent or kitty-corner to anchor
1633 // such that it's fully visible in viewport.
1634 //
1635 // description:
1636 // Place node such that corner of node touches a corner of
1637 // aroundNode, and that node is fully visible.
1638 //
1639 // anchor:
1640 // Either a DOMNode or a __Rectangle (object with x, y, width, height).
1641 //
1642 // positions:
1643 // Ordered list of positions to try matching up.
1644 // * before: places drop down to the left of the anchor node/widget, or to the right in the case
1645 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1646 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1647 // * after: places drop down to the right of the anchor node/widget, or to the left in the case
1648 // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
1649 // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
1650 // * before-centered: centers drop down to the left of the anchor node/widget, or to the right
1651 // in the case of RTL scripts like Hebrew and Arabic
1652 // * after-centered: centers drop down to the right of the anchor node/widget, or to the left
1653 // in the case of RTL scripts like Hebrew and Arabic
1654 // * above-centered: drop down is centered above anchor node
1655 // * above: drop down goes above anchor node, left sides aligned
1656 // * above-alt: drop down goes above anchor node, right sides aligned
1657 // * below-centered: drop down is centered above anchor node
1658 // * below: drop down goes below anchor node
1659 // * below-alt: drop down goes below anchor node, right sides aligned
1660 //
1661 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
1662 // For things like tooltip, they are displayed differently (and have different dimensions)
1663 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
1664 //
1665 // leftToRight:
1666 // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
1667 // positions slightly.
1668 //
1669 // example:
1670 // | placeAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
1671 // This will try to position node such that node's top-left corner is at the same position
1672 // as the bottom left corner of the aroundNode (ie, put node below
1673 // aroundNode, with left edges aligned). If that fails it will try to put
1674 // the bottom-right corner of node where the top right corner of aroundNode is
1675 // (ie, put node above aroundNode, with right edges aligned)
1676 //
a089699c 1677
1354d172
AD
1678 // if around is a DOMNode (or DOMNode id), convert to coordinates
1679 var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor)
1680 ? domGeometry.position(anchor, true)
1681 : anchor;
1682
1683 // Adjust anchor positioning for the case that a parent node has overflw hidden, therefore cuasing the anchor not to be completely visible
1684 if(anchor.parentNode){
1685 var parent = anchor.parentNode;
1686 while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
1687 var parentPos = domGeometry.position(parent, true);
1688 var parentStyleOverflow = domStyle.getComputedStyle(parent).overflow;
1689 if(parentStyleOverflow == "hidden" || parentStyleOverflow == "auto" || parentStyleOverflow == "scroll"){
1690 var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h);
1691 var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w);
1692 aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x);
1693 aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y);
1694 aroundNodePos.h = bottomYCoord - aroundNodePos.y;
1695 aroundNodePos.w = rightXCoord - aroundNodePos.x;
1696 }
1697 parent = parent.parentNode;
1698 }
1699 }
1700
1701 var x = aroundNodePos.x,
1702 y = aroundNodePos.y,
1703 width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width),
1704 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);
1705
1706 // Convert positions arguments into choices argument for _place()
1707 var choices = [];
1708 function push(aroundCorner, corner){
1709 choices.push({
1710 aroundCorner: aroundCorner,
1711 corner: corner,
1712 pos: {
1713 x: {
1714 'L': x,
1715 'R': x + width,
1716 'M': x + (width >> 1)
1717 }[aroundCorner.charAt(1)],
1718 y: {
1719 'T': y,
1720 'B': y + height,
1721 'M': y + (height >> 1)
1722 }[aroundCorner.charAt(0)]
1723 }
1724 })
1725 }
1726 array.forEach(positions, function(pos){
1727 var ltr = leftToRight;
1728 switch(pos){
1729 case "above-centered":
1730 push("TM", "BM");
1731 break;
1732 case "below-centered":
1733 push("BM", "TM");
1734 break;
1735 case "after-centered":
1736 ltr = !ltr;
1737 // fall through
1738 case "before-centered":
1739 push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
1740 break;
1741 case "after":
1742 ltr = !ltr;
1743 // fall through
1744 case "before":
1745 push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
1746 push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
1747 break;
1748 case "below-alt":
1749 ltr = !ltr;
1750 // fall through
1751 case "below":
1752 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1753 push(ltr ? "BL" : "BR", ltr ? "TL" : "TR");
1754 push(ltr ? "BR" : "BL", ltr ? "TR" : "TL");
1755 break;
1756 case "above-alt":
1757 ltr = !ltr;
1758 // fall through
1759 case "above":
1760 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
1761 push(ltr ? "TL" : "TR", ltr ? "BL" : "BR");
1762 push(ltr ? "TR" : "TL", ltr ? "BR" : "BL");
1763 break;
1764 default:
1765 // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
1766 // Not meant to be used directly.
1767 push(pos.aroundCorner, pos.corner);
1768 }
1769 });
81bea17a 1770
1354d172
AD
1771 var position = _place(node, choices, layoutNode, {w: width, h: height});
1772 position.aroundNodePos = aroundNodePos;
81bea17a 1773
1354d172 1774 return position;
a089699c 1775 }
1354d172
AD
1776 });
1777});
a089699c 1778
1354d172
AD
1779},
1780'dijit/_HasDropDown':function(){
1781define("dijit/_HasDropDown", [
1782 "dojo/_base/declare", // declare
1783 "dojo/_base/Deferred",
1784 "dojo/_base/event", // event.stop
1785 "dojo/dom", // dom.isDescendant
1786 "dojo/dom-attr", // domAttr.set
1787 "dojo/dom-class", // domClass.add domClass.contains domClass.remove
1788 "dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
1789 "dojo/dom-style", // domStyle.set
1790 "dojo/has",
1791 "dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
1792 "dojo/_base/lang", // lang.hitch lang.isFunction
1793 "dojo/touch",
1794 "dojo/_base/window", // win.doc
1795 "dojo/window", // winUtils.getBox
1796 "./registry", // registry.byNode()
1797 "./focus",
1798 "./popup",
1799 "./_FocusMixin"
1800], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, touch,
1801 win, winUtils, registry, focus, popup, _FocusMixin){
a089699c 1802
1354d172
AD
1803/*=====
1804 var _FocusMixin = dijit._FocusMixin;
1805=====*/
a089699c 1806
1354d172
AD
1807 // module:
1808 // dijit/_HasDropDown
1809 // summary:
1810 // Mixin for widgets that need drop down ability.
81bea17a 1811
1354d172
AD
1812 return declare("dijit._HasDropDown", _FocusMixin, {
1813 // summary:
1814 // Mixin for widgets that need drop down ability.
81bea17a 1815
1354d172
AD
1816 // _buttonNode: [protected] DomNode
1817 // The button/icon/node to click to display the drop down.
1818 // Can be set via a data-dojo-attach-point assignment.
1819 // If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
1820 _buttonNode: null,
a089699c 1821
1354d172
AD
1822 // _arrowWrapperNode: [protected] DomNode
1823 // Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
1824 // on where the drop down is set to be positioned.
1825 // Can be set via a data-dojo-attach-point assignment.
1826 // If missing, then _buttonNode will be used.
1827 _arrowWrapperNode: null,
a089699c 1828
1354d172
AD
1829 // _popupStateNode: [protected] DomNode
1830 // The node to set the popupActive class on.
1831 // Can be set via a data-dojo-attach-point assignment.
1832 // If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
1833 _popupStateNode: null,
a089699c 1834
1354d172
AD
1835 // _aroundNode: [protected] DomNode
1836 // The node to display the popup around.
1837 // Can be set via a data-dojo-attach-point assignment.
1838 // If missing, then domNode will be used.
1839 _aroundNode: null,
a089699c 1840
1354d172
AD
1841 // dropDown: [protected] Widget
1842 // The widget to display as a popup. This widget *must* be
1843 // defined before the startup function is called.
1844 dropDown: null,
a089699c 1845
1354d172
AD
1846 // autoWidth: [protected] Boolean
1847 // Set to true to make the drop down at least as wide as this
1848 // widget. Set to false if the drop down should just be its
1849 // default width
1850 autoWidth: true,
a089699c 1851
1354d172
AD
1852 // forceWidth: [protected] Boolean
1853 // Set to true to make the drop down exactly as wide as this
1854 // widget. Overrides autoWidth.
1855 forceWidth: false,
a089699c 1856
1354d172
AD
1857 // maxHeight: [protected] Integer
1858 // The max height for our dropdown.
1859 // Any dropdown taller than this will have scrollbars.
1860 // Set to 0 for no max height, or -1 to limit height to available space in viewport
1861 maxHeight: 0,
a089699c 1862
1354d172
AD
1863 // dropDownPosition: [const] String[]
1864 // This variable controls the position of the drop down.
1865 // It's an array of strings with the following values:
1866 //
1867 // * before: places drop down to the left of the target node/widget, or to the right in
1868 // the case of RTL scripts like Hebrew and Arabic
1869 // * after: places drop down to the right of the target node/widget, or to the left in
1870 // the case of RTL scripts like Hebrew and Arabic
1871 // * above: drop down goes above target node
1872 // * below: drop down goes below target node
1873 //
1874 // The list is positions is tried, in order, until a position is found where the drop down fits
1875 // within the viewport.
1876 //
1877 dropDownPosition: ["below","above"],
a089699c 1878
1354d172
AD
1879 // _stopClickEvents: Boolean
1880 // When set to false, the click events will not be stopped, in
1881 // case you want to use them in your subwidget
1882 _stopClickEvents: true,
a089699c 1883
1354d172
AD
1884 _onDropDownMouseDown: function(/*Event*/ e){
1885 // summary:
1886 // Callback when the user mousedown's on the arrow icon
1887 if(this.disabled || this.readOnly){ return; }
a089699c 1888
1354d172
AD
1889 // Prevent default to stop things like text selection, but don't stop propogation, so that:
1890 // 1. TimeTextBox etc. can focusthe <input> on mousedown
1891 // 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
1892 // 3. user defined onMouseDown handler fires
1893 e.preventDefault();
a089699c 1894
1354d172 1895 this._docHandler = this.connect(win.doc, touch.release, "_onDropDownMouseUp");
a089699c 1896
1354d172
AD
1897 this.toggleDropDown();
1898 },
a089699c 1899
1354d172
AD
1900 _onDropDownMouseUp: function(/*Event?*/ e){
1901 // summary:
1902 // Callback when the user lifts their mouse after mouse down on the arrow icon.
1903 // If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
1904 // drop down widget. If the event is missing, then we are not
1905 // a mouseup event.
1906 //
1907 // This is useful for the common mouse movement pattern
1908 // with native browser <select> nodes:
1909 // 1. mouse down on the select node (probably on the arrow)
1910 // 2. move mouse to a menu item while holding down the mouse button
1911 // 3. mouse up. this selects the menu item as though the user had clicked it.
1912 if(e && this._docHandler){
1913 this.disconnect(this._docHandler);
a089699c 1914 }
1354d172 1915 var dropDown = this.dropDown, overMenu = false;
a089699c 1916
1354d172
AD
1917 if(e && this._opened){
1918 // This code deals with the corner-case when the drop down covers the original widget,
1919 // because it's so large. In that case mouse-up shouldn't select a value from the menu.
1920 // Find out if our target is somewhere in our dropdown widget,
1921 // but not over our _buttonNode (the clickable node)
1922 var c = domGeometry.position(this._buttonNode, true);
1923 if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
1924 !(e.pageY >= c.y && e.pageY <= c.y + c.h)){
1925 var t = e.target;
1926 while(t && !overMenu){
1927 if(domClass.contains(t, "dijitPopup")){
1928 overMenu = true;
1929 }else{
1930 t = t.parentNode;
1931 }
1932 }
1933 if(overMenu){
1934 t = e.target;
1935 if(dropDown.onItemClick){
1936 var menuItem;
1937 while(t && !(menuItem = registry.byNode(t))){
1938 t = t.parentNode;
1939 }
1940 if(menuItem && menuItem.onClick && menuItem.getParent){
1941 menuItem.getParent().onItemClick(menuItem, e);
1942 }
1943 }
1944 return;
1945 }
1946 }
a089699c 1947 }
1354d172
AD
1948 if(this._opened){
1949 if(dropDown.focus && dropDown.autoFocus !== false){
1950 // Focus the dropdown widget - do it on a delay so that we
1951 // don't steal our own focus.
1952 window.setTimeout(lang.hitch(dropDown, "focus"), 1);
1953 }
1954 }else{
1955 // The drop down arrow icon probably can't receive focus, but widget itself should get focus.
1956 // setTimeout() needed to make it work on IE (test DateTextBox)
1957 setTimeout(lang.hitch(this, "focus"), 0);
a089699c 1958 }
a089699c 1959
1354d172
AD
1960 if(has("ios")){
1961 this._justGotMouseUp = true;
1962 setTimeout(lang.hitch(this, function(){
1963 this._justGotMouseUp = false;
1964 }), 0);
1965 }
1966 },
81bea17a 1967
1354d172
AD
1968 _onDropDownClick: function(/*Event*/ e){
1969 if(has("ios") && !this._justGotMouseUp){
1970 // This branch fires on iPhone for ComboBox, because the button node is an <input> and doesn't
1971 // generate touchstart/touchend events. Pretend we just got a mouse down / mouse up.
1972 // The if(has("ios") is necessary since IE and desktop safari get spurious onclick events
1973 // when there are nested tables (specifically, clicking on a table that holds a dijit.form.Select,
1974 // but not on the Select itself, causes an onclick event on the Select)
1975 this._onDropDownMouseDown(e);
1976 this._onDropDownMouseUp(e);
1977 }
a089699c 1978
1354d172
AD
1979 // The drop down was already opened on mousedown/keydown; just need to call stopEvent().
1980 if(this._stopClickEvents){
1981 event.stop(e);
a089699c 1982 }
1354d172 1983 },
a089699c 1984
1354d172
AD
1985 buildRendering: function(){
1986 this.inherited(arguments);
a089699c 1987
1354d172
AD
1988 this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
1989 this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
a089699c 1990
1354d172
AD
1991 // Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
1992 // based on where drop down will normally appear
1993 var defaultPos = {
1994 "after" : this.isLeftToRight() ? "Right" : "Left",
1995 "before" : this.isLeftToRight() ? "Left" : "Right",
1996 "above" : "Up",
1997 "below" : "Down",
1998 "left" : "Left",
1999 "right" : "Right"
2000 }[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
2001 domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
2002 },
a089699c 2003
1354d172
AD
2004 postCreate: function(){
2005 // summary:
2006 // set up nodes and connect our mouse and keypress events
a089699c 2007
1354d172 2008 this.inherited(arguments);
a089699c 2009
1354d172
AD
2010 this.connect(this._buttonNode, touch.press, "_onDropDownMouseDown");
2011 this.connect(this._buttonNode, "onclick", "_onDropDownClick");
2012 this.connect(this.focusNode, "onkeypress", "_onKey");
2013 this.connect(this.focusNode, "onkeyup", "_onKeyUp");
2014 },
a089699c 2015
1354d172
AD
2016 destroy: function(){
2017 if(this.dropDown){
2018 // Destroy the drop down, unless it's already been destroyed. This can happen because
2019 // the drop down is a direct child of <body> even though it's logically my child.
2020 if(!this.dropDown._destroyed){
2021 this.dropDown.destroyRecursive();
2022 }
2023 delete this.dropDown;
2024 }
2025 this.inherited(arguments);
2026 },
a089699c 2027
1354d172
AD
2028 _onKey: function(/*Event*/ e){
2029 // summary:
2030 // Callback when the user presses a key while focused on the button node
a089699c 2031
1354d172 2032 if(this.disabled || this.readOnly){ return; }
a089699c 2033
1354d172
AD
2034 var d = this.dropDown, target = e.target;
2035 if(d && this._opened && d.handleKey){
2036 if(d.handleKey(e) === false){
2037 /* false return code means that the drop down handled the key */
2038 event.stop(e);
2039 return;
2040 }
2041 }
2042 if(d && this._opened && e.charOrCode == keys.ESCAPE){
2043 this.closeDropDown();
2044 event.stop(e);
2045 }else if(!this._opened &&
2046 (e.charOrCode == keys.DOWN_ARROW ||
2047 ( (e.charOrCode == keys.ENTER || e.charOrCode == " ") &&
2048 //ignore enter and space if the event is for a text input
2049 ((target.tagName || "").toLowerCase() !== 'input' ||
2050 (target.type && target.type.toLowerCase() !== 'text'))))){
2051 // Toggle the drop down, but wait until keyup so that the drop down doesn't
2052 // get a stray keyup event, or in the case of key-repeat (because user held
2053 // down key for too long), stray keydown events
2054 this._toggleOnKeyUp = true;
2055 event.stop(e);
2056 }
2057 },
a089699c 2058
1354d172
AD
2059 _onKeyUp: function(){
2060 if(this._toggleOnKeyUp){
2061 delete this._toggleOnKeyUp;
2062 this.toggleDropDown();
2063 var d = this.dropDown; // drop down may not exist until toggleDropDown() call
2064 if(d && d.focus){
2065 setTimeout(lang.hitch(d, "focus"), 1);
2066 }
2067 }
2068 },
a089699c 2069
1354d172
AD
2070 _onBlur: function(){
2071 // summary:
2072 // Called magically when focus has shifted away from this widget and it's dropdown
81bea17a 2073
1354d172
AD
2074 // Don't focus on button if the user has explicitly focused on something else (happens
2075 // when user clicks another control causing the current popup to close)..
2076 // But if focus is inside of the drop down then reset focus to me, because IE doesn't like
2077 // it when you display:none a node with focus.
2078 var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode);
a089699c 2079
1354d172 2080 this.closeDropDown(focusMe);
a089699c 2081
1354d172
AD
2082 this.inherited(arguments);
2083 },
a089699c 2084
1354d172
AD
2085 isLoaded: function(){
2086 // summary:
2087 // Returns true if the dropdown exists and it's data is loaded. This can
2088 // be overridden in order to force a call to loadDropDown().
2089 // tags:
2090 // protected
a089699c 2091
1354d172
AD
2092 return true;
2093 },
a089699c 2094
1354d172
AD
2095 loadDropDown: function(/*Function*/ loadCallback){
2096 // summary:
2097 // Creates the drop down if it doesn't exist, loads the data
2098 // if there's an href and it hasn't been loaded yet, and then calls
2099 // the given callback.
2100 // tags:
2101 // protected
a089699c 2102
1354d172
AD
2103 // TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
2104 loadCallback();
2105 },
a089699c 2106
1354d172
AD
2107 loadAndOpenDropDown: function(){
2108 // summary:
2109 // Creates the drop down if it doesn't exist, loads the data
2110 // if there's an href and it hasn't been loaded yet, and
2111 // then opens the drop down. This is basically a callback when the
2112 // user presses the down arrow button to open the drop down.
2113 // returns: Deferred
2114 // Deferred for the drop down widget that
2115 // fires when drop down is created and loaded
2116 // tags:
2117 // protected
2118 var d = new Deferred(),
2119 afterLoad = lang.hitch(this, function(){
2120 this.openDropDown();
2121 d.resolve(this.dropDown);
2122 });
2123 if(!this.isLoaded()){
2124 this.loadDropDown(afterLoad);
2125 }else{
2126 afterLoad();
2127 }
2128 return d;
2129 },
a089699c 2130
1354d172
AD
2131 toggleDropDown: function(){
2132 // summary:
2133 // Callback when the user presses the down arrow button or presses
2134 // the down arrow key to open/close the drop down.
2135 // Toggle the drop-down widget; if it is up, close it, if not, open it
2136 // tags:
2137 // protected
a089699c 2138
1354d172
AD
2139 if(this.disabled || this.readOnly){ return; }
2140 if(!this._opened){
2141 this.loadAndOpenDropDown();
2142 }else{
2143 this.closeDropDown();
2144 }
2145 },
a089699c 2146
1354d172
AD
2147 openDropDown: function(){
2148 // summary:
2149 // Opens the dropdown for this widget. To be called only when this.dropDown
2150 // has been created and is ready to display (ie, it's data is loaded).
2151 // returns:
2152 // return value of dijit.popup.open()
2153 // tags:
2154 // protected
a089699c 2155
1354d172
AD
2156 var dropDown = this.dropDown,
2157 ddNode = dropDown.domNode,
2158 aroundNode = this._aroundNode || this.domNode,
2159 self = this;
81bea17a 2160
1354d172 2161 // Prepare our popup's height and honor maxHeight if it exists.
81bea17a 2162
1354d172
AD
2163 // TODO: isn't maxHeight dependent on the return value from dijit.popup.open(),
2164 // ie, dependent on how much space is available (BK)
81bea17a 2165
1354d172
AD
2166 if(!this._preparedNode){
2167 this._preparedNode = true;
2168 // Check if we have explicitly set width and height on the dropdown widget dom node
2169 if(ddNode.style.width){
2170 this._explicitDDWidth = true;
2171 }
2172 if(ddNode.style.height){
2173 this._explicitDDHeight = true;
2174 }
2175 }
a089699c 2176
1354d172
AD
2177 // Code for resizing dropdown (height limitation, or increasing width to match my width)
2178 if(this.maxHeight || this.forceWidth || this.autoWidth){
2179 var myStyle = {
2180 display: "",
2181 visibility: "hidden"
2182 };
2183 if(!this._explicitDDWidth){
2184 myStyle.width = "";
2185 }
2186 if(!this._explicitDDHeight){
2187 myStyle.height = "";
2188 }
2189 domStyle.set(ddNode, myStyle);
a089699c 2190
1354d172
AD
2191 // Figure out maximum height allowed (if there is a height restriction)
2192 var maxHeight = this.maxHeight;
2193 if(maxHeight == -1){
2194 // limit height to space available in viewport either above or below my domNode
2195 // (whichever side has more room)
2196 var viewport = winUtils.getBox(),
2197 position = domGeometry.position(aroundNode, false);
2198 maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
2199 }
a089699c 2200
1354d172
AD
2201 // Attach dropDown to DOM and make make visibility:hidden rather than display:none
2202 // so we call startup() and also get the size
2203 popup.moveOffScreen(dropDown);
81bea17a 2204
1354d172
AD
2205 if(dropDown.startup && !dropDown._started){
2206 dropDown.startup(); // this has to be done after being added to the DOM
2207 }
2208 // Get size of drop down, and determine if vertical scroll bar needed
2209 var mb = domGeometry.getMarginSize(ddNode);
2210 var overHeight = (maxHeight && mb.h > maxHeight);
2211 domStyle.set(ddNode, {
2212 overflowX: "hidden",
2213 overflowY: overHeight ? "auto" : "hidden"
2214 });
2215 if(overHeight){
2216 mb.h = maxHeight;
2217 if("w" in mb){
2218 mb.w += 16; // room for vertical scrollbar
2219 }
2220 }else{
2221 delete mb.h;
2222 }
a089699c 2223
1354d172
AD
2224 // Adjust dropdown width to match or be larger than my width
2225 if(this.forceWidth){
2226 mb.w = aroundNode.offsetWidth;
2227 }else if(this.autoWidth){
2228 mb.w = Math.max(mb.w, aroundNode.offsetWidth);
2229 }else{
2230 delete mb.w;
2231 }
a089699c 2232
1354d172
AD
2233 // And finally, resize the dropdown to calculated height and width
2234 if(lang.isFunction(dropDown.resize)){
2235 dropDown.resize(mb);
2236 }else{
2237 domGeometry.setMarginBox(ddNode, mb);
2238 }
2239 }
a089699c 2240
1354d172
AD
2241 var retVal = popup.open({
2242 parent: this,
2243 popup: dropDown,
2244 around: aroundNode,
2245 orient: this.dropDownPosition,
2246 onExecute: function(){
2247 self.closeDropDown(true);
2248 },
2249 onCancel: function(){
2250 self.closeDropDown(true);
2251 },
2252 onClose: function(){
2253 domAttr.set(self._popupStateNode, "popupActive", false);
2254 domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
2255 self._opened = false;
a089699c 2256 }
1354d172
AD
2257 });
2258 domAttr.set(this._popupStateNode, "popupActive", "true");
2259 domClass.add(self._popupStateNode, "dijitHasDropDownOpen");
2260 this._opened=true;
a089699c 2261
1354d172
AD
2262 // TODO: set this.checked and call setStateClass(), to affect button look while drop down is shown
2263 return retVal;
2264 },
a089699c 2265
1354d172
AD
2266 closeDropDown: function(/*Boolean*/ focus){
2267 // summary:
2268 // Closes the drop down on this widget
2269 // focus:
2270 // If true, refocuses the button widget
2271 // tags:
2272 // protected
a089699c 2273
1354d172
AD
2274 if(this._opened){
2275 if(focus){ this.focus(); }
2276 popup.close(this.dropDown);
2277 this._opened = false;
a089699c
AD
2278 }
2279 }
a089699c 2280
1354d172
AD
2281 });
2282});
a089699c 2283
1354d172
AD
2284},
2285'dijit/tree/TreeStoreModel':function(){
2286define("dijit/tree/TreeStoreModel", [
2287 "dojo/_base/array", // array.filter array.forEach array.indexOf array.some
2288 "dojo/aspect", // aspect.after
2289 "dojo/_base/declare", // declare
2290 "dojo/_base/json", // json.stringify
2291 "dojo/_base/lang" // lang.hitch
2292], function(array, aspect, declare, json, lang){
2293
2294 // module:
2295 // dijit/tree/TreeStoreModel
2296 // summary:
2297 // Implements dijit.Tree.model connecting to a dojo.data store with a single
2298 // root item.
a089699c 2299
1354d172 2300 return declare("dijit.tree.TreeStoreModel", null, {
a089699c 2301 // summary:
1354d172
AD
2302 // Implements dijit.Tree.model connecting to a dojo.data store with a single
2303 // root item. Any methods passed into the constructor will override
2304 // the ones defined here.
a089699c 2305
1354d172
AD
2306 // store: dojo.data.Store
2307 // Underlying store
2308 store: null,
a089699c 2309
1354d172
AD
2310 // childrenAttrs: String[]
2311 // One or more attribute names (attributes in the dojo.data item) that specify that item's children
2312 childrenAttrs: ["children"],
a089699c 2313
1354d172
AD
2314 // newItemIdAttr: String
2315 // Name of attribute in the Object passed to newItem() that specifies the id.
2316 //
2317 // If newItemIdAttr is set then it's used when newItem() is called to see if an
2318 // item with the same id already exists, and if so just links to the old item
2319 // (so that the old item ends up with two parents).
2320 //
2321 // Setting this to null or "" will make every drop create a new item.
2322 newItemIdAttr: "id",
a089699c 2323
1354d172
AD
2324 // labelAttr: String
2325 // If specified, get label for tree node from this attribute, rather
2326 // than by calling store.getLabel()
2327 labelAttr: "",
a089699c 2328
1354d172
AD
2329 // root: [readonly] dojo.data.Item
2330 // Pointer to the root item (read only, not a parameter)
2331 root: null,
a089699c 2332
1354d172
AD
2333 // query: anything
2334 // Specifies datastore query to return the root item for the tree.
2335 // Must only return a single item. Alternately can just pass in pointer
2336 // to root item.
2337 // example:
2338 // | {id:'ROOT'}
2339 query: null,
a089699c 2340
1354d172
AD
2341 // deferItemLoadingUntilExpand: Boolean
2342 // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
2343 // until they are expanded. This allows for lazying loading where only one
2344 // loadItem (and generally one network call, consequently) per expansion
2345 // (rather than one for each child).
2346 // This relies on partial loading of the children items; each children item of a
2347 // fully loaded item should contain the label and info about having children.
2348 deferItemLoadingUntilExpand: false,
a089699c 2349
1354d172
AD
2350 constructor: function(/* Object */ args){
2351 // summary:
2352 // Passed the arguments listed above (store, etc)
2353 // tags:
2354 // private
a089699c 2355
1354d172 2356 lang.mixin(this, args);
a089699c 2357
1354d172 2358 this.connects = [];
a089699c 2359
1354d172
AD
2360 var store = this.store;
2361 if(!store.getFeatures()['dojo.data.api.Identity']){
2362 throw new Error("dijit.Tree: store must support dojo.data.Identity");
2363 }
a089699c 2364
1354d172
AD
2365 // if the store supports Notification, subscribe to the notification events
2366 if(store.getFeatures()['dojo.data.api.Notification']){
2367 this.connects = this.connects.concat([
2368 aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true),
2369 aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true),
2370 aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true)
2371 ]);
2372 }
2373 },
a089699c 2374
1354d172
AD
2375 destroy: function(){
2376 var h;
2377 while(h = this.connects.pop()){ h.remove(); }
2378 // TODO: should cancel any in-progress processing of getRoot(), getChildren()
2379 },
a089699c 2380
1354d172
AD
2381 // =======================================================================
2382 // Methods for traversing hierarchy
a089699c 2383
1354d172
AD
2384 getRoot: function(onItem, onError){
2385 // summary:
2386 // Calls onItem with the root item for the tree, possibly a fabricated item.
2387 // Calls onError on error.
2388 if(this.root){
2389 onItem(this.root);
2390 }else{
2391 this.store.fetch({
2392 query: this.query,
2393 onComplete: lang.hitch(this, function(items){
2394 if(items.length != 1){
2395 throw new Error(this.declaredClass + ": query " + json.stringify(this.query) + " returned " + items.length +
2396 " items, but must return exactly one item");
2397 }
2398 this.root = items[0];
2399 onItem(this.root);
2400 }),
2401 onError: onError
2402 });
2403 }
2404 },
a089699c 2405
1354d172
AD
2406 mayHaveChildren: function(/*dojo.data.Item*/ item){
2407 // summary:
2408 // Tells if an item has or may have children. Implementing logic here
2409 // avoids showing +/- expando icon for nodes that we know don't have children.
2410 // (For efficiency reasons we may not want to check if an element actually
2411 // has children until user clicks the expando node)
2412 return array.some(this.childrenAttrs, function(attr){
2413 return this.store.hasAttribute(item, attr);
2414 }, this);
2415 },
a089699c 2416
1354d172
AD
2417 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
2418 // summary:
2419 // Calls onComplete() with array of child items of given parent item, all loaded.
a089699c 2420
1354d172
AD
2421 var store = this.store;
2422 if(!store.isItemLoaded(parentItem)){
2423 // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
2424 // mode, so we will load it and just return the children (without loading each
2425 // child item)
2426 var getChildren = lang.hitch(this, arguments.callee);
2427 store.loadItem({
2428 item: parentItem,
2429 onItem: function(parentItem){
2430 getChildren(parentItem, onComplete, onError);
2431 },
2432 onError: onError
2433 });
2434 return;
2435 }
2436 // get children of specified item
2437 var childItems = [];
2438 for(var i=0; i<this.childrenAttrs.length; i++){
2439 var vals = store.getValues(parentItem, this.childrenAttrs[i]);
2440 childItems = childItems.concat(vals);
2441 }
a089699c 2442
1354d172
AD
2443 // count how many items need to be loaded
2444 var _waitCount = 0;
2445 if(!this.deferItemLoadingUntilExpand){
2446 array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
2447 }
a089699c 2448
1354d172
AD
2449 if(_waitCount == 0){
2450 // all items are already loaded (or we aren't loading them). proceed...
2451 onComplete(childItems);
2452 }else{
2453 // still waiting for some or all of the items to load
2454 array.forEach(childItems, function(item, idx){
2455 if(!store.isItemLoaded(item)){
2456 store.loadItem({
2457 item: item,
2458 onItem: function(item){
2459 childItems[idx] = item;
2460 if(--_waitCount == 0){
2461 // all nodes have been loaded, send them to the tree
2462 onComplete(childItems);
2463 }
2464 },
2465 onError: onError
2466 });
2467 }
2468 });
2469 }
2470 },
a089699c 2471
1354d172
AD
2472 // =======================================================================
2473 // Inspecting items
a089699c 2474
1354d172
AD
2475 isItem: function(/* anything */ something){
2476 return this.store.isItem(something); // Boolean
2477 },
a089699c 2478
1354d172
AD
2479 fetchItemByIdentity: function(/* object */ keywordArgs){
2480 this.store.fetchItemByIdentity(keywordArgs);
2481 },
a089699c 2482
1354d172
AD
2483 getIdentity: function(/* item */ item){
2484 return this.store.getIdentity(item); // Object
2485 },
a089699c 2486
1354d172
AD
2487 getLabel: function(/*dojo.data.Item*/ item){
2488 // summary:
2489 // Get the label for an item
2490 if(this.labelAttr){
2491 return this.store.getValue(item,this.labelAttr); // String
2492 }else{
2493 return this.store.getLabel(item); // String
81bea17a 2494 }
1354d172
AD
2495 },
2496
2497 // =======================================================================
2498 // Write interface
2499
2500 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
2501 // summary:
2502 // Creates a new item. See `dojo.data.api.Write` for details on args.
2503 // Used in drag & drop when item from external source dropped onto tree.
2504 // description:
2505 // Developers will need to override this method if new items get added
2506 // to parents with multiple children attributes, in order to define which
2507 // children attribute points to the new item.
2508
2509 var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
2510
2511 if(this.newItemIdAttr && args[this.newItemIdAttr]){
2512 // Maybe there's already a corresponding item in the store; if so, reuse it.
2513 this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
2514 if(item){
2515 // There's already a matching item in store, use it
2516 this.pasteItem(item, null, parent, true, insertIndex);
2517 }else{
2518 // Create new item in the tree, based on the drag source.
2519 LnewItem=this.store.newItem(args, pInfo);
2520 if(LnewItem && (insertIndex!=undefined)){
2521 // Move new item to desired position
2522 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
81bea17a
AD
2523 }
2524 }
1354d172
AD
2525 }});
2526 }else{
2527 // [as far as we know] there is no id so we must assume this is a new item
2528 LnewItem=this.store.newItem(args, pInfo);
2529 if(LnewItem && (insertIndex!=undefined)){
2530 // Move new item to desired position
2531 this.pasteItem(LnewItem, parent, parent, false, insertIndex);
81bea17a 2532 }
81bea17a 2533 }
1354d172 2534 },
81bea17a 2535
1354d172
AD
2536 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
2537 // summary:
2538 // Move or copy an item from one parent item to another.
2539 // Used in drag & drop
2540 var store = this.store,
2541 parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
a089699c 2542
1354d172
AD
2543 // remove child from source item, and record the attribute that child occurred in
2544 if(oldParentItem){
2545 array.forEach(this.childrenAttrs, function(attr){
2546 if(store.containsValue(oldParentItem, attr, childItem)){
2547 if(!bCopy){
2548 var values = array.filter(store.getValues(oldParentItem, attr), function(x){
2549 return x != childItem;
2550 });
2551 store.setValues(oldParentItem, attr, values);
2552 }
2553 parentAttr = attr;
2554 }
2555 });
2556 }
81bea17a 2557
1354d172
AD
2558 // modify target item's children attribute to include this item
2559 if(newParentItem){
2560 if(typeof insertIndex == "number"){
2561 // call slice() to avoid modifying the original array, confusing the data store
2562 var childItems = store.getValues(newParentItem, parentAttr).slice();
2563 childItems.splice(insertIndex, 0, childItem);
2564 store.setValues(newParentItem, parentAttr, childItems);
2565 }else{
2566 store.setValues(newParentItem, parentAttr,
2567 store.getValues(newParentItem, parentAttr).concat(childItem));
2568 }
2569 }
2570 },
81bea17a 2571
1354d172
AD
2572 // =======================================================================
2573 // Callbacks
a089699c 2574
1354d172
AD
2575 onChange: function(/*dojo.data.Item*/ /*===== item =====*/){
2576 // summary:
2577 // Callback whenever an item has changed, so that Tree
2578 // can update the label, icon, etc. Note that changes
2579 // to an item's children or parent(s) will trigger an
2580 // onChildrenChange() so you can ignore those changes here.
2581 // tags:
2582 // callback
2583 },
a089699c 2584
1354d172
AD
2585 onChildrenChange: function(/*===== parent, newChildrenList =====*/){
2586 // summary:
2587 // Callback to do notifications about new, updated, or deleted items.
2588 // parent: dojo.data.Item
2589 // newChildrenList: dojo.data.Item[]
2590 // tags:
2591 // callback
2592 },
81bea17a 2593
1354d172
AD
2594 onDelete: function(/*dojo.data.Item*/ /*===== item =====*/){
2595 // summary:
2596 // Callback when an item has been deleted.
2597 // description:
2598 // Note that there will also be an onChildrenChange() callback for the parent
2599 // of this item.
2600 // tags:
2601 // callback
2602 },
a089699c 2603
1354d172
AD
2604 // =======================================================================
2605 // Events from data store
a089699c 2606
1354d172
AD
2607 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
2608 // summary:
2609 // Handler for when new items appear in the store, either from a drop operation
2610 // or some other way. Updates the tree view (if necessary).
2611 // description:
2612 // If the new item is a child of an existing item,
2613 // calls onChildrenChange() with the new list of children
2614 // for that existing item.
2615 //
2616 // tags:
2617 // extension
a089699c 2618
1354d172
AD
2619 // We only care about the new item if it has a parent that corresponds to a TreeNode
2620 // we are currently displaying
2621 if(!parentInfo){
2622 return;
2623 }
a089699c 2624
1354d172
AD
2625 // Call onChildrenChange() on parent (ie, existing) item with new list of children
2626 // In the common case, the new list of children is simply parentInfo.newValue or
2627 // [ parentInfo.newValue ], although if items in the store has multiple
2628 // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
2629 // so call getChildren() to be sure to get right answer.
2630 this.getChildren(parentInfo.item, lang.hitch(this, function(children){
2631 this.onChildrenChange(parentInfo.item, children);
2632 }));
2633 },
a089699c 2634
1354d172
AD
2635 onDeleteItem: function(/*Object*/ item){
2636 // summary:
2637 // Handler for delete notifications from underlying store
2638 this.onDelete(item);
2639 },
a089699c 2640
1354d172
AD
2641 onSetItem: function(item, attribute /*===== , oldValue, newValue =====*/){
2642 // summary:
2643 // Updates the tree view according to changes in the data store.
2644 // description:
2645 // Handles updates to an item's children by calling onChildrenChange(), and
2646 // other updates to an item by calling onChange().
2647 //
2648 // See `onNewItem` for more details on handling updates to an item's children.
2649 // item: Item
2650 // attribute: attribute-name-string
2651 // oldValue: object | array
2652 // newValue: object | array
2653 // tags:
2654 // extension
a089699c 2655
1354d172
AD
2656 if(array.indexOf(this.childrenAttrs, attribute) != -1){
2657 // item's children list changed
2658 this.getChildren(item, lang.hitch(this, function(children){
2659 // See comments in onNewItem() about calling getChildren()
2660 this.onChildrenChange(item, children);
2661 }));
2662 }else{
2663 // item's label/icon/etc. changed.
2664 this.onChange(item);
2665 }
2666 }
2667 });
2668});
a089699c 2669
1354d172
AD
2670},
2671'dijit/_MenuBase':function(){
2672define("dijit/_MenuBase", [
2673 "./popup",
2674 "dojo/window",
2675 "./_Widget",
2676 "./_KeyNavContainer",
2677 "./_TemplatedMixin",
2678 "dojo/_base/declare", // declare
2679 "dojo/dom", // dom.isDescendant domClass.replace
2680 "dojo/dom-attr",
2681 "dojo/dom-class", // domClass.replace
2682 "dojo/_base/lang", // lang.hitch
2683 "dojo/_base/array" // array.indexOf
2684], function(pm, winUtils, _Widget, _KeyNavContainer, _TemplatedMixin,
2685 declare, dom, domAttr, domClass, lang, array){
a089699c
AD
2686
2687/*=====
1354d172
AD
2688 var _Widget = dijit._Widget;
2689 var _TemplatedMixin = dijit._TemplatedMixin;
2690 var _KeyNavContainer = dijit._KeyNavContainer;
a089699c
AD
2691=====*/
2692
1354d172
AD
2693// module:
2694// dijit/_MenuBase
2695// summary:
2696// Base class for Menu and MenuBar
a089699c 2697
1354d172
AD
2698return declare("dijit._MenuBase",
2699 [_Widget, _TemplatedMixin, _KeyNavContainer],
2700{
2701 // summary:
2702 // Base class for Menu and MenuBar
a089699c 2703
1354d172
AD
2704 // parentMenu: [readonly] Widget
2705 // pointer to menu that displayed me
2706 parentMenu: null,
81bea17a 2707
1354d172
AD
2708 // popupDelay: Integer
2709 // number of milliseconds before hovering (without clicking) causes the popup to automatically open.
2710 popupDelay: 500,
2711
2712 onExecute: function(){
a089699c 2713 // summary:
1354d172
AD
2714 // Attach point for notification about when a menu item has been executed.
2715 // This is an internal mechanism used for Menus to signal to their parent to
2716 // close them, because they are about to execute the onClick handler. In
2717 // general developers should not attach to or override this method.
a089699c 2718 // tags:
1354d172 2719 // protected
a089699c
AD
2720 },
2721
1354d172 2722 onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
a089699c 2723 // summary:
1354d172
AD
2724 // Attach point for notification about when the user cancels the current menu
2725 // This is an internal mechanism used for Menus to signal to their parent to
2726 // close them. In general developers should not attach to or override this method.
a089699c 2727 // tags:
1354d172
AD
2728 // protected
2729 },
a089699c 2730
1354d172
AD
2731 _moveToPopup: function(/*Event*/ evt){
2732 // summary:
2733 // This handles the right arrow key (left arrow key on RTL systems),
2734 // which will either open a submenu, or move to the next item in the
2735 // ancestor MenuBar
2736 // tags:
2737 // private
a089699c 2738
1354d172
AD
2739 if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
2740 this.focusedChild._onClick(evt);
2741 }else{
2742 var topMenu = this._getTopMenu();
2743 if(topMenu && topMenu._isMenuBar){
2744 topMenu.focusNext();
2745 }
2746 }
2747 },
a089699c 2748
1354d172
AD
2749 _onPopupHover: function(/*Event*/ /*===== evt =====*/){
2750 // summary:
2751 // This handler is called when the mouse moves over the popup.
2752 // tags:
2753 // private
a089699c 2754
1354d172
AD
2755 // if the mouse hovers over a menu popup that is in pending-close state,
2756 // then stop the close operation.
2757 // This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
2758 if(this.currentPopup && this.currentPopup._pendingClose_timer){
2759 var parentMenu = this.currentPopup.parentMenu;
2760 // highlight the parent menu item pointing to this popup
2761 if(parentMenu.focusedChild){
2762 parentMenu.focusedChild._setSelected(false);
2763 }
2764 parentMenu.focusedChild = this.currentPopup.from_item;
2765 parentMenu.focusedChild._setSelected(true);
2766 // cancel the pending close
2767 this._stopPendingCloseTimer(this.currentPopup);
a089699c 2768 }
1354d172 2769 },
a089699c 2770
1354d172
AD
2771 onItemHover: function(/*MenuItem*/ item){
2772 // summary:
2773 // Called when cursor is over a MenuItem.
2774 // tags:
2775 // protected
a089699c 2776
1354d172
AD
2777 // Don't do anything unless user has "activated" the menu by:
2778 // 1) clicking it
2779 // 2) opening it from a parent menu (which automatically focuses it)
2780 if(this.isActive){
2781 this.focusChild(item);
2782 if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
2783 this.hover_timer = setTimeout(lang.hitch(this, "_openPopup"), this.popupDelay);
a089699c 2784 }
a089699c 2785 }
1354d172
AD
2786 // if the user is mixing mouse and keyboard navigation,
2787 // then the menu may not be active but a menu item has focus,
2788 // but it's not the item that the mouse just hovered over.
2789 // To avoid both keyboard and mouse selections, use the latest.
2790 if(this.focusedChild){
2791 this.focusChild(item);
a089699c 2792 }
1354d172 2793 this._hoveredChild = item;
a089699c
AD
2794 },
2795
1354d172 2796 _onChildBlur: function(item){
a089699c 2797 // summary:
1354d172
AD
2798 // Called when a child MenuItem becomes inactive because focus
2799 // has been removed from the MenuItem *and* it's descendant menus.
a089699c
AD
2800 // tags:
2801 // private
1354d172
AD
2802 this._stopPopupTimer();
2803 item._setSelected(false);
2804 // Close all popups that are open and descendants of this menu
2805 var itemPopup = item.popup;
2806 if(itemPopup){
2807 this._stopPendingCloseTimer(itemPopup);
2808 itemPopup._pendingClose_timer = setTimeout(function(){
2809 itemPopup._pendingClose_timer = null;
2810 if(itemPopup.parentMenu){
2811 itemPopup.parentMenu.currentPopup = null;
81bea17a 2812 }
1354d172
AD
2813 pm.close(itemPopup); // this calls onClose
2814 }, this.popupDelay);
81bea17a 2815 }
81bea17a
AD
2816 },
2817
1354d172 2818 onItemUnhover: function(/*MenuItem*/ item){
a089699c 2819 // summary:
1354d172 2820 // Callback fires when mouse exits a MenuItem
a089699c
AD
2821 // tags:
2822 // protected
1354d172
AD
2823
2824 if(this.isActive){
2825 this._stopPopupTimer();
2826 }
2827 if(this._hoveredChild == item){ this._hoveredChild = null; }
a089699c
AD
2828 },
2829
1354d172 2830 _stopPopupTimer: function(){
a089699c 2831 // summary:
1354d172
AD
2832 // Cancels the popup timer because the user has stop hovering
2833 // on the MenuItem, etc.
a089699c 2834 // tags:
1354d172
AD
2835 // private
2836 if(this.hover_timer){
2837 clearTimeout(this.hover_timer);
2838 this.hover_timer = null;
81bea17a 2839 }
1354d172 2840 },
81bea17a 2841
1354d172
AD
2842 _stopPendingCloseTimer: function(/*dijit._Widget*/ popup){
2843 // summary:
2844 // Cancels the pending-close timer because the close has been preempted
2845 // tags:
2846 // private
2847 if(popup._pendingClose_timer){
2848 clearTimeout(popup._pendingClose_timer);
2849 popup._pendingClose_timer = null;
81bea17a 2850 }
a089699c
AD
2851 },
2852
1354d172 2853 _stopFocusTimer: function(){
a089699c 2854 // summary:
1354d172 2855 // Cancels the pending-focus timer because the menu was closed before focus occured
a089699c 2856 // tags:
1354d172
AD
2857 // private
2858 if(this._focus_timer){
2859 clearTimeout(this._focus_timer);
2860 this._focus_timer = null;
2861 }
a089699c
AD
2862 },
2863
1354d172 2864 _getTopMenu: function(){
a089699c 2865 // summary:
1354d172
AD
2866 // Returns the top menu in this chain of Menus
2867 // tags:
2868 // private
2869 for(var top=this; top.parentMenu; top=top.parentMenu);
2870 return top;
a089699c
AD
2871 },
2872
1354d172 2873 onItemClick: function(/*dijit._Widget*/ item, /*Event*/ evt){
a089699c 2874 // summary:
1354d172
AD
2875 // Handle clicks on an item.
2876 // tags:
2877 // private
a089699c 2878
1354d172
AD
2879 // this can't be done in _onFocus since the _onFocus events occurs asynchronously
2880 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
2881 this._markActive();
2882 }
a089699c 2883
1354d172 2884 this.focusChild(item);
a089699c 2885
1354d172 2886 if(item.disabled){ return false; }
a089699c 2887
1354d172
AD
2888 if(item.popup){
2889 this._openPopup();
2890 }else{
2891 // before calling user defined handler, close hierarchy of menus
2892 // and restore focus to place it was when menu was opened
2893 this.onExecute();
a089699c 2894
1354d172
AD
2895 // user defined handler for click
2896 item.onClick(evt);
2897 }
a089699c
AD
2898 },
2899
1354d172 2900 _openPopup: function(){
a089699c 2901 // summary:
1354d172 2902 // Open the popup to the side of/underneath the current menu item
a089699c
AD
2903 // tags:
2904 // protected
2905
1354d172
AD
2906 this._stopPopupTimer();
2907 var from_item = this.focusedChild;
2908 if(!from_item){ return; } // the focused child lost focus since the timer was started
2909 var popup = from_item.popup;
2910 if(popup.isShowingNow){ return; }
2911 if(this.currentPopup){
2912 this._stopPendingCloseTimer(this.currentPopup);
2913 pm.close(this.currentPopup);
a089699c 2914 }
1354d172
AD
2915 popup.parentMenu = this;
2916 popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
2917 var self = this;
2918 pm.open({
2919 parent: this,
2920 popup: popup,
2921 around: from_item.domNode,
2922 orient: this._orient || ["after", "before"],
2923 onCancel: function(){ // called when the child menu is canceled
2924 // set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
2925 // which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
2926 self.focusChild(from_item); // put focus back on my node
2927 self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
2928 from_item._setSelected(true); // oops, _cleanUp() deselected the item
2929 self.focusedChild = from_item; // and unset focusedChild
2930 },
2931 onExecute: lang.hitch(this, "_cleanUp")
2932 });
a089699c 2933
1354d172
AD
2934 this.currentPopup = popup;
2935 // detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
2936 popup.connect(popup.domNode, "onmouseenter", lang.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
a089699c 2937
1354d172
AD
2938 if(popup.focus){
2939 // If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar),
2940 // if the cursor happens to collide with the popup, it will generate an onmouseover event
2941 // even though the mouse wasn't moved. Use a setTimeout() to call popup.focus so that
2942 // our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
2943 popup._focus_timer = setTimeout(lang.hitch(popup, function(){
2944 this._focus_timer = null;
2945 this.focus();
2946 }), 0);
a089699c
AD
2947 }
2948 },
2949
1354d172 2950 _markActive: function(){
a089699c 2951 // summary:
1354d172
AD
2952 // Mark this menu's state as active.
2953 // Called when this Menu gets focus from:
2954 // 1) clicking it (mouse or via space/arrow key)
2955 // 2) being opened by a parent menu.
2956 // This is not called just from mouse hover.
2957 // Focusing a menu via TAB does NOT automatically set isActive
2958 // since TAB is a navigation operation and not a selection one.
2959 // For Windows apps, pressing the ALT key focuses the menubar
2960 // menus (similar to TAB navigation) but the menu is not active
2961 // (ie no dropdown) until an item is clicked.
2962 this.isActive = true;
2963 domClass.replace(this.domNode, "dijitMenuActive", "dijitMenuPassive");
a089699c
AD
2964 },
2965
1354d172 2966 onOpen: function(/*Event*/ /*===== e =====*/){
a089699c 2967 // summary:
1354d172
AD
2968 // Callback when this menu is opened.
2969 // This is called by the popup manager as notification that the menu
2970 // was opened.
a089699c 2971 // tags:
1354d172
AD
2972 // private
2973
2974 this.isShowingNow = true;
2975 this._markActive();
a089699c
AD
2976 },
2977
1354d172
AD
2978 _markInactive: function(){
2979 // summary:
2980 // Mark this menu's state as inactive.
2981 this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
2982 domClass.replace(this.domNode, "dijitMenuPassive", "dijitMenuActive");
2983 },
a089699c 2984
1354d172 2985 onClose: function(){
a089699c 2986 // summary:
1354d172
AD
2987 // Callback when this menu is closed.
2988 // This is called by the popup manager as notification that the menu
2989 // was closed.
a089699c 2990 // tags:
1354d172
AD
2991 // private
2992
2993 this._stopFocusTimer();
2994 this._markInactive();
2995 this.isShowingNow = false;
2996 this.parentMenu = null;
a089699c
AD
2997 },
2998
1354d172 2999 _closeChild: function(){
a089699c 3000 // summary:
1354d172 3001 // Called when submenu is clicked or focus is lost. Close hierarchy of menus.
a089699c 3002 // tags:
1354d172
AD
3003 // private
3004 this._stopPopupTimer();
a089699c 3005
1354d172
AD
3006 if(this.currentPopup){
3007 // If focus is on a descendant MenuItem then move focus to me,
3008 // because IE doesn't like it when you display:none a node with focus,
3009 // and also so keyboard users don't lose control.
3010 // Likely, immediately after a user defined onClick handler will move focus somewhere
3011 // else, like a Dialog.
3012 if(array.indexOf(this._focusManager.activeStack, this.id) >= 0){
3013 domAttr.set(this.focusedChild.focusNode, "tabIndex", this.tabIndex);
3014 this.focusedChild.focusNode.focus();
3015 }
3016 // Close all popups that are open and descendants of this menu
3017 pm.close(this.currentPopup);
3018 this.currentPopup = null;
3019 }
a089699c 3020
1354d172
AD
3021 if(this.focusedChild){ // unhighlight the focused item
3022 this.focusedChild._setSelected(false);
3023 this.focusedChild._onUnhover();
3024 this.focusedChild = null;
3025 }
3026 },
a089699c 3027
1354d172
AD
3028 _onItemFocus: function(/*MenuItem*/ item){
3029 // summary:
3030 // Called when child of this Menu gets focus from:
3031 // 1) clicking it
3032 // 2) tabbing into it
3033 // 3) being opened by a parent menu.
3034 // This is not called just from mouse hover.
3035 if(this._hoveredChild && this._hoveredChild != item){
3036 this._hoveredChild._onUnhover(); // any previous mouse movement is trumped by focus selection
a089699c 3037 }
1354d172 3038 },
a089699c 3039
1354d172
AD
3040 _onBlur: function(){
3041 // summary:
3042 // Called when focus is moved away from this Menu and it's submenus.
3043 // tags:
3044 // protected
3045 this._cleanUp();
3046 this.inherited(arguments);
a089699c
AD
3047 },
3048
1354d172 3049 _cleanUp: function(){
a089699c 3050 // summary:
1354d172 3051 // Called when the user is done with this menu. Closes hierarchy of menus.
a089699c
AD
3052 // tags:
3053 // private
3054
1354d172
AD
3055 this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
3056 if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
3057 this._markInactive();
3058 }
3059 }
3060});
a089699c 3061
1354d172 3062});
a089699c 3063
1354d172
AD
3064},
3065'dijit/focus':function(){
3066define("dijit/focus", [
3067 "dojo/aspect",
3068 "dojo/_base/declare", // declare
3069 "dojo/dom", // domAttr.get dom.isDescendant
3070 "dojo/dom-attr", // domAttr.get dom.isDescendant
3071 "dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
3072 "dojo/Evented",
3073 "dojo/_base/lang", // lang.hitch
3074 "dojo/on",
3075 "dojo/ready",
3076 "dojo/_base/sniff", // has("ie")
3077 "dojo/Stateful",
3078 "dojo/_base/unload", // unload.addOnWindowUnload
3079 "dojo/_base/window", // win.body
3080 "dojo/window", // winUtils.get
3081 "./a11y", // a11y.isTabNavigable
3082 "./registry", // registry.byId
3083 "." // to set dijit.focus
3084], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils,
3085 a11y, registry, dijit){
3086
3087 // module:
3088 // dijit/focus
3089 // summary:
3090 // Returns a singleton that tracks the currently focused node, and which widgets are currently "active".
a089699c 3091
1354d172
AD
3092/*=====
3093 dijit.focus = {
a089699c 3094 // summary:
1354d172
AD
3095 // Tracks the currently focused node, and which widgets are currently "active".
3096 // Access via require(["dijit/focus"], function(focus){ ... }).
a089699c 3097 //
1354d172
AD
3098 // A widget is considered active if it or a descendant widget has focus,
3099 // or if a non-focusable node of this widget or a descendant was recently clicked.
3100 //
3101 // Call focus.watch("curNode", callback) to track the current focused DOMNode,
3102 // or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
3103 //
3104 // Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
3105 // when widgets become active/inactive
3106 //
3107 // Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
a089699c 3108
1354d172
AD
3109 // curNode: DomNode
3110 // Currently focused item on screen
3111 curNode: null,
a089699c 3112
1354d172
AD
3113 // activeStack: dijit._Widget[]
3114 // List of currently active widgets (focused widget and it's ancestors)
3115 activeStack: [],
a089699c 3116
1354d172
AD
3117 registerIframe: function(iframe){
3118 // summary:
3119 // Registers listeners on the specified iframe so that any click
3120 // or focus event on that iframe (or anything in it) is reported
3121 // as a focus/click event on the <iframe> itself.
3122 // description:
3123 // Currently only used by editor.
3124 // returns:
3125 // Handle with remove() method to deregister.
3126 },
3127
3128 registerWin: function(targetWindow, effectiveNode){
3129 // summary:
3130 // Registers listeners on the specified window (either the main
3131 // window or an iframe's window) to detect when the user has clicked somewhere
3132 // or focused somewhere.
3133 // description:
3134 // Users should call registerIframe() instead of this method.
3135 // targetWindow: Window?
3136 // If specified this is the window associated with the iframe,
3137 // i.e. iframe.contentWindow.
3138 // effectiveNode: DOMNode?
3139 // If specified, report any focus events inside targetWindow as
3140 // an event on effectiveNode, rather than on evt.target.
3141 // returns:
3142 // Handle with remove() method to deregister.
81bea17a 3143 }
1354d172
AD
3144 };
3145=====*/
81bea17a 3146
1354d172
AD
3147 var FocusManager = declare([Stateful, Evented], {
3148 // curNode: DomNode
3149 // Currently focused item on screen
3150 curNode: null,
a089699c 3151
1354d172
AD
3152 // activeStack: dijit._Widget[]
3153 // List of currently active widgets (focused widget and it's ancestors)
3154 activeStack: [],
a089699c 3155
1354d172
AD
3156 constructor: function(){
3157 // Don't leave curNode/prevNode pointing to bogus elements
3158 var check = lang.hitch(this, function(node){
3159 if(dom.isDescendant(this.curNode, node)){
3160 this.set("curNode", null);
3161 }
3162 if(dom.isDescendant(this.prevNode, node)){
3163 this.set("prevNode", null);
3164 }
3165 });
3166 aspect.before(domConstruct, "empty", check);
3167 aspect.before(domConstruct, "destroy", check);
3168 },
a089699c 3169
1354d172
AD
3170 registerIframe: function(/*DomNode*/ iframe){
3171 // summary:
3172 // Registers listeners on the specified iframe so that any click
3173 // or focus event on that iframe (or anything in it) is reported
3174 // as a focus/click event on the <iframe> itself.
3175 // description:
3176 // Currently only used by editor.
3177 // returns:
3178 // Handle with remove() method to deregister.
3179 return this.registerWin(iframe.contentWindow, iframe);
3180 },
a089699c 3181
1354d172
AD
3182 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
3183 // summary:
3184 // Registers listeners on the specified window (either the main
3185 // window or an iframe's window) to detect when the user has clicked somewhere
3186 // or focused somewhere.
3187 // description:
3188 // Users should call registerIframe() instead of this method.
3189 // targetWindow:
3190 // If specified this is the window associated with the iframe,
3191 // i.e. iframe.contentWindow.
3192 // effectiveNode:
3193 // If specified, report any focus events inside targetWindow as
3194 // an event on effectiveNode, rather than on evt.target.
3195 // returns:
3196 // Handle with remove() method to deregister.
a089699c 3197
1354d172 3198 // TODO: make this function private in 2.0; Editor/users should call registerIframe(),
a089699c 3199
1354d172
AD
3200 var _this = this;
3201 var mousedownListener = function(evt){
3202 _this._justMouseDowned = true;
3203 setTimeout(function(){ _this._justMouseDowned = false; }, 0);
3204
3205 // workaround weird IE bug where the click is on an orphaned node
3206 // (first time clicking a Select/DropDownButton inside a TooltipDialog)
3207 if(has("ie") && evt && evt.srcElement && evt.srcElement.parentNode == null){
3208 return;
3209 }
3210
3211 _this._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
3212 };
3213
3214 // Listen for blur and focus events on targetWindow's document.
3215 // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble
3216 // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers
3217 // fire.
3218 // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because
3219 // (at least for FF) the focus event doesn't fire on <html> or <body>.
3220 var doc = has("ie") ? targetWindow.document.documentElement : targetWindow.document;
3221 if(doc){
3222 if(has("ie")){
3223 targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
3224 var activateListener = function(evt){
3225 // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
3226 // ignore those events
3227 var tag = evt.srcElement.tagName.toLowerCase();
3228 if(tag == "#document" || tag == "body"){ return; }
3229
3230 // Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
3231 // probably just ignore such an event as it will be handled by onmousedown handler above, but
3232 // leaving the code for now.
3233 if(a11y.isTabNavigable(evt.srcElement)){
3234 _this._onFocusNode(effectiveNode || evt.srcElement);
3235 }else{
3236 _this._onTouchNode(effectiveNode || evt.srcElement);
3237 }
3238 };
3239 doc.attachEvent('onactivate', activateListener);
3240 var deactivateListener = function(evt){
3241 _this._onBlurNode(effectiveNode || evt.srcElement);
3242 };
3243 doc.attachEvent('ondeactivate', deactivateListener);
3244
3245 return {
3246 remove: function(){
3247 targetWindow.document.detachEvent('onmousedown', mousedownListener);
3248 doc.detachEvent('onactivate', activateListener);
3249 doc.detachEvent('ondeactivate', deactivateListener);
3250 doc = null; // prevent memory leak (apparent circular reference via closure)
3251 }
3252 };
3253 }else{
3254 doc.body.addEventListener('mousedown', mousedownListener, true);
3255 doc.body.addEventListener('touchstart', mousedownListener, true);
3256 var focusListener = function(evt){
3257 _this._onFocusNode(effectiveNode || evt.target);
3258 };
3259 doc.addEventListener('focus', focusListener, true);
3260 var blurListener = function(evt){
3261 _this._onBlurNode(effectiveNode || evt.target);
3262 };
3263 doc.addEventListener('blur', blurListener, true);
3264
3265 return {
3266 remove: function(){
3267 doc.body.removeEventListener('mousedown', mousedownListener, true);
3268 doc.body.removeEventListener('touchstart', mousedownListener, true);
3269 doc.removeEventListener('focus', focusListener, true);
3270 doc.removeEventListener('blur', blurListener, true);
3271 doc = null; // prevent memory leak (apparent circular reference via closure)
3272 }
3273 };
3274 }
3275 }
3276 },
3277
3278 _onBlurNode: function(/*DomNode*/ /*===== node =====*/){
3279 // summary:
3280 // Called when focus leaves a node.
3281 // Usually ignored, _unless_ it *isn't* followed by touching another node,
3282 // which indicates that we tabbed off the last field on the page,
3283 // in which case every widget is marked inactive
3284 this.set("prevNode", this.curNode);
3285 this.set("curNode", null);
3286
3287 if(this._justMouseDowned){
3288 // the mouse down caused a new widget to be marked as active; this blur event
3289 // is coming late, so ignore it.
a089699c
AD
3290 return;
3291 }
a089699c 3292
1354d172
AD
3293 // if the blur event isn't followed by a focus event then mark all widgets as inactive.
3294 if(this._clearActiveWidgetsTimer){
3295 clearTimeout(this._clearActiveWidgetsTimer);
3296 }
3297 this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){
3298 delete this._clearActiveWidgetsTimer;
3299 this._setStack([]);
3300 this.prevNode = null;
3301 }), 100);
3302 },
a089699c 3303
1354d172
AD
3304 _onTouchNode: function(/*DomNode*/ node, /*String*/ by){
3305 // summary:
3306 // Callback when node is focused or mouse-downed
3307 // node:
3308 // The node that was touched.
3309 // by:
3310 // "mouse" if the focus/touch was caused by a mouse down event
a089699c 3311
1354d172
AD
3312 // ignore the recent blurNode event
3313 if(this._clearActiveWidgetsTimer){
3314 clearTimeout(this._clearActiveWidgetsTimer);
3315 delete this._clearActiveWidgetsTimer;
3316 }
3317
3318 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
3319 var newStack=[];
3320 try{
3321 while(node){
3322 var popupParent = domAttr.get(node, "dijitPopupParent");
3323 if(popupParent){
3324 node=registry.byId(popupParent).domNode;
3325 }else if(node.tagName && node.tagName.toLowerCase() == "body"){
3326 // is this the root of the document or just the root of an iframe?
3327 if(node === win.body()){
3328 // node is the root of the main document
3329 break;
3330 }
3331 // otherwise, find the iframe this node refers to (can't access it via parentNode,
3332 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
3333 node=winUtils.get(node.ownerDocument).frameElement;
3334 }else{
3335 // if this node is the root node of a widget, then add widget id to stack,
3336 // except ignore clicks on disabled widgets (actually focusing a disabled widget still works,
3337 // to support MenuItem)
3338 var id = node.getAttribute && node.getAttribute("widgetId"),
3339 widget = id && registry.byId(id);
3340 if(widget && !(by == "mouse" && widget.get("disabled"))){
3341 newStack.unshift(id);
3342 }
3343 node=node.parentNode;
3344 }
3345 }
3346 }catch(e){ /* squelch */ }
3347
3348 this._setStack(newStack, by);
3349 },
3350
3351 _onFocusNode: function(/*DomNode*/ node){
3352 // summary:
3353 // Callback when node is focused
3354
3355 if(!node){
3356 return;
3357 }
3358
3359 if(node.nodeType == 9){
3360 // Ignore focus events on the document itself. This is here so that
3361 // (for example) clicking the up/down arrows of a spinner
3362 // (which don't get focus) won't cause that widget to blur. (FF issue)
a089699c
AD
3363 return;
3364 }
1354d172
AD
3365
3366 this._onTouchNode(node);
3367
3368 if(node == this.curNode){ return; }
3369 this.set("curNode", node);
3370 },
3371
3372 _setStack: function(/*String[]*/ newStack, /*String*/ by){
3373 // summary:
3374 // The stack of active widgets has changed. Send out appropriate events and records new stack.
3375 // newStack:
3376 // array of widget id's, starting from the top (outermost) widget
3377 // by:
3378 // "mouse" if the focus/touch was caused by a mouse down event
3379
3380 var oldStack = this.activeStack;
3381 this.set("activeStack", newStack);
3382
3383 // compare old stack to new stack to see how many elements they have in common
3384 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
3385 if(oldStack[nCommon] != newStack[nCommon]){
3386 break;
3387 }
3388 }
3389
3390 var widget;
3391 // for all elements that have gone out of focus, set focused=false
3392 for(var i=oldStack.length-1; i>=nCommon; i--){
3393 widget = registry.byId(oldStack[i]);
3394 if(widget){
3395 widget._hasBeenBlurred = true; // TODO: used by form widgets, should be moved there
3396 widget.set("focused", false);
3397 if(widget._focusManager == this){
3398 widget._onBlur(by);
3399 }
3400 this.emit("widget-blur", widget, by);
3401 }
3402 }
3403
3404 // for all element that have come into focus, set focused=true
3405 for(i=nCommon; i<newStack.length; i++){
3406 widget = registry.byId(newStack[i]);
3407 if(widget){
3408 widget.set("focused", true);
3409 if(widget._focusManager == this){
3410 widget._onFocus(by);
3411 }
3412 this.emit("widget-focus", widget, by);
3413 }
3414 }
3415 },
3416
3417 focus: function(node){
3418 // summary:
3419 // Focus the specified node, suppressing errors if they occur
3420 if(node){
3421 try{ node.focus(); }catch(e){/*quiet*/}
3422 }
a089699c 3423 }
1354d172 3424 });
a089699c 3425
1354d172 3426 var singleton = new FocusManager();
a089699c 3427
1354d172
AD
3428 // register top window and all the iframes it contains
3429 ready(function(){
3430 var handle = singleton.registerWin(win.doc.parentWindow || win.doc.defaultView);
3431 if(has("ie")){
3432 unload.addOnWindowUnload(function(){
3433 handle.remove();
3434 handle = null;
3435 })
3436 }
3437 });
a089699c 3438
1354d172
AD
3439 // Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
3440 // as a function to set focus.
3441 dijit.focus = function(node){
3442 singleton.focus(node); // indirection here allows dijit/_base/focus.js to override behavior
3443 };
3444 for(var attr in singleton){
3445 if(!/^_/.test(attr)){
3446 dijit.focus[attr] = typeof singleton[attr] == "function" ? lang.hitch(singleton, attr) : singleton[attr];
a089699c 3447 }
a089699c 3448 }
1354d172
AD
3449 singleton.watch(function(attr, oldVal, newVal){
3450 dijit.focus[attr] = newVal;
3451 });
3452
3453 return singleton;
a089699c
AD
3454});
3455
1354d172
AD
3456},
3457'dojo/i18n':function(){
3458define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json"],
3459 function(dojo, require, has, array, config, lang, xhr, json) {
3460 // module:
3461 // dojo/i18n
3462 // summary:
3463 // This module implements the !dojo/i18n plugin and the v1.6- i18n API
3464 // description:
3465 // We choose to include our own plugin to leverage functionality already contained in dojo
3466 // and thereby reduce the size of the plugin compared to various loader implementations. Also, this
3467 // allows foreign AMD loaders to be used without their plugins.
a089699c 3468
a089699c 3469
1354d172
AD
3470 has.add("dojo-preload-i18n-Api",
3471 // if true, define the preload localizations machinery
3472 1
3473 );
a089699c 3474
1354d172
AD
3475 true || has.add("dojo-v1x-i18n-Api",
3476 // if true, define the v1.x i18n functions
3477 1
3478 );
a089699c 3479
1354d172
AD
3480 var
3481 thisModule= dojo.i18n=
3482 // the dojo.i18n module
3483 {},
3484
3485 nlsRe=
3486 // regexp for reconstructing the master bundle name from parts of the regexp match
3487 // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
3488 // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
3489 // nlsRe.exec("foo/bar/baz/nls/foo") gives:
3490 // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
3491 // so, if match[5] is blank, it means this is the top bundle definition.
3492 // courtesy of http://requirejs.org
3493 /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
3494
3495 getAvailableLocales= function(
3496 root,
3497 locale,
3498 bundlePath,
3499 bundleName
3500 ){
3501 // return a vector of module ids containing all available locales with respect to the target locale
3502 // For example, assuming:
3503 // * the root bundle indicates specific bundles for "fr" and "fr-ca",
3504 // * bundlePath is "myPackage/nls"
3505 // * bundleName is "myBundle"
3506 // Then a locale argument of "fr-ca" would return
3507 // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
3508 // Notice that bundles are returned least-specific to most-specific, starting with the root.
3509 //
3510 // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
3511 // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
3512 //
a089699c 3513
1354d172
AD
3514 for(var result= [bundlePath + bundleName], localeParts= locale.split("-"), current= "", i= 0; i<localeParts.length; i++){
3515 current+= (current ? "-" : "") + localeParts[i];
3516 if(!root || root[current]){
3517 result.push(bundlePath + current + "/" + bundleName);
3518 }
3519 }
3520 return result;
3521 },
a089699c 3522
1354d172 3523 cache= {},
a089699c 3524
1354d172
AD
3525 getL10nName= dojo.getL10nName = function(moduleName, bundleName, locale){
3526 locale = locale ? locale.toLowerCase() : dojo.locale;
3527 moduleName = "dojo/i18n!" + moduleName.replace(/\./g, "/");
3528 bundleName = bundleName.replace(/\./g, "/");
3529 return (/root/i.test(locale)) ?
3530 (moduleName + "/nls/" + bundleName) :
3531 (moduleName + "/nls/" + locale + "/" + bundleName);
3532 },
a089699c 3533
1354d172
AD
3534 doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){
3535 // get the root bundle which instructs which other bundles are required to construct the localized bundle
3536 require([bundlePathAndName], function(root){
3537 var current= lang.clone(root.root),
3538 availableLocales= getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName);
3539 require(availableLocales, function(){
3540 for (var i= 1; i<availableLocales.length; i++){
3541 current= lang.mixin(lang.clone(current), arguments[i]);
3542 }
3543 // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
3544 var target= bundlePathAndName + "/" + locale;
3545 cache[target]= current;
3546 load();
3547 });
3548 });
3549 },
3550
3551 normalize = function(id, toAbsMid){
3552 // id may be relative
3553 // preload has form *preload*<path>/nls/<module>*<flattened locales> and
3554 // therefore never looks like a relative
3555 return /^\./.test(id) ? toAbsMid(id) : id;
3556 },
3557
3558 getLocalesToLoad = function(targetLocale){
3559 var list = config.extraLocale || [];
3560 list = lang.isArray(list) ? list : [list];
3561 list.push(targetLocale);
3562 return list;
3563 },
3564
3565 load = function(id, require, load){
3566 //
3567 // id is in one of the following formats
3568 //
3569 // 1. <path>/nls/<bundle>
3570 // => load the bundle, localized to config.locale; load all bundles localized to
3571 // config.extraLocale (if any); return the loaded bundle localized to config.locale.
3572 //
3573 // 2. <path>/nls/<locale>/<bundle>
3574 // => load then return the bundle localized to <locale>
3575 //
3576 // 3. *preload*<path>/nls/<module>*<JSON array of available locales>
3577 // => for config.locale and all config.extraLocale, load all bundles found
3578 // in the best-matching bundle rollup. A value of 1 is returned, which
3579 // is meaningless other than to say the plugin is executing the requested
3580 // preloads
3581 //
3582 // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
3583 // normalize. In case 3, it <path> is assumed to be absolue; this is arranged by the builder.
3584 //
3585 // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
3586 // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
3587 //
3588 // <path>/nls/<bundle>/<locale>
3589 //
3590 // will hold the value. Similarly, then plugin will publish this value to the loader by
3591 //
3592 // define("<path>/nls/<bundle>/<locale>", <bundle-value>);
3593 //
3594 // Given this algorithm, other machinery can provide fast load paths be preplacing
3595 // values in the plugin's cache, which is public. When a load is demanded the
3596 // cache is inspected before starting any loading. Explicitly placing values in the plugin
3597 // cache is an advanced/experimental feature that should not be needed; use at your own risk.
3598 //
3599 // For the normal AMD algorithm, the root bundle is loaded first, which instructs the
3600 // plugin what additional localized bundles are required for a particular locale. These
3601 // additional locales are loaded and a mix of the root and each progressively-specific
3602 // locale is returned. For example:
3603 //
3604 // 1. The client demands "dojo/i18n!some/path/nls/someBundle
3605 //
3606 // 2. The loader demands load(some/path/nls/someBundle)
3607 //
3608 // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
3609 //
3610 // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
3611 // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
3612 // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
3613 //
3614 // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
3615 // ab-cd-ef as...
3616 //
3617 // mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
3618 // require("some/path/nls/ab/someBundle")),
3619 // require("some/path/nls/ab-cd-ef/someBundle"));
3620 //
3621 // This value is inserted into the cache and published to the loader at the
3622 // key/module-id some/path/nls/someBundle/ab-cd-ef.
3623 //
3624 // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
3625 // (further preload requests will be serviced) until all ongoing preloading has completed.
3626 //
3627 // The preload signature instructs the plugin that a special rollup module is available that contains
3628 // one or more flattened, localized bundles. The JSON array of available locales indicates which locales
3629 // are available. Here is an example:
3630 //
3631 // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
3632 //
3633 // This indicates the following rollup modules are available:
3634 //
3635 // some/path/nls/someModule_ROOT
3636 // some/path/nls/someModule_ab
3637 // some/path/nls/someModule_ab-cd-ef
3638 //
3639 // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
3640 // For example, assume someModule contained the bundles some/bundle/path/someBundle and
3641 // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as folllows:
3642 //
3643 // define({
3644 // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
3645 // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
3646 // });
3647 //
3648 // E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
3649 //
3650 // require(["some/path/nls/someModule_ab"], function(rollup){
3651 // for(var p in rollup){
3652 // var id = p + "/ab",
3653 // cache[id] = rollup[p];
3654 // define(id, rollup[p]);
3655 // }
3656 // });
3657 //
3658 // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
3659 // load accordingly.
3660 //
3661 // The builder will write such rollups for every layer if a non-empty localeList profile property is
3662 // provided. Further, the builder will include the following cache entry in the cache associated with
3663 // any layer.
3664 //
3665 // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
3666 //
3667 // The *now special cache module instructs the loader to apply the provided function to context-require
3668 // with respect to the particular layer being defined. This causes the plugin to hold all normal service
3669 // requests until all preloading is complete.
3670 //
3671 // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
3672 // where the target locale has a single segment and a layer depends on a single bundle:
3673 //
3674 // Without Preloads:
3675 //
3676 // 1. Layer loads root bundle.
3677 // 2. bundle is demanded; plugin loads single localized bundle.
3678 //
3679 // With Preloads:
3680 //
3681 // 1. Layer causes preloading of target bundle.
3682 // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
3683 //
3684 // In each case a single transaction is required to load the target bundle. In cases where multiple bundles
3685 // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
3686 // the normal path requires an additional transaction for each additional bundle/locale-segment. However all
3687 // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
3688 // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
3689 //
3690 if(has("dojo-preload-i18n-Api")){
3691 var split = id.split("*"),
3692 preloadDemand = split[1]=="preload";
3693 if(preloadDemand){
3694 if(!cache[id]){
3695 // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
3696 // who knows what over-aggressive human optimizers may attempt
3697 cache[id] = 1;
3698 preloadL10n(split[2], json.parse(split[3]), 1);
3699 }
3700 // don't stall the loader!
3701 load(1);
3702 }
3703 if(preloadDemand || waitForPreloads(id, require, load)){
3704 return;
3705 }
3706 }
a089699c 3707
1354d172
AD
3708 var match= nlsRe.exec(id),
3709 bundlePath= match[1] + "/",
3710 bundleName= match[5] || match[4],
3711 bundlePathAndName= bundlePath + bundleName,
3712 localeSpecified = (match[5] && match[4]),
3713 targetLocale= localeSpecified || dojo.locale,
3714 loadTarget= bundlePathAndName + "/" + targetLocale,
3715 loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale),
3716 remaining = loadList.length,
3717 finish = function(){
3718 if(!--remaining){
3719 load(lang.delegate(cache[loadTarget]));
3720 }
3721 };
3722 array.forEach(loadList, function(locale){
3723 var target = bundlePathAndName + "/" + locale;
3724 if(has("dojo-preload-i18n-Api")){
3725 checkForLegacyModules(target);
3726 }
3727 if(!cache[target]){
3728 doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish);
3729 }else{
3730 finish();
3731 }
3732 });
81bea17a 3733 };
81bea17a 3734
1354d172
AD
3735 if(has("dojo-unit-tests")){
3736 var unitTests = thisModule.unitTests = [];
3737 }
81bea17a 3738
1354d172
AD
3739 if(has("dojo-preload-i18n-Api") || 1){
3740 var normalizeLocale = thisModule.normalizeLocale= function(locale){
3741 var result = locale ? locale.toLowerCase() : dojo.locale;
3742 return result == "root" ? "ROOT" : result;
3743 },
81bea17a 3744
1354d172
AD
3745 isXd = function(mid){
3746 return (1 && 1) ?
3747 require.isXdUrl(require.toUrl(mid + ".js")) :
3748 true;
3749 },
81bea17a 3750
1354d172 3751 preloading = 0,
81bea17a 3752
1354d172 3753 preloadWaitQueue = [],
81bea17a 3754
1354d172
AD
3755 preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean*/ guaranteedAmdFormat){
3756 // summary:
3757 // Load available flattened resource bundles associated with a particular module for dojo.locale and all dojo.config.extraLocale (if any)
3758 //
3759 // descirption:
3760 // Only called by built layer files. The entire locale hierarchy is loaded. For example,
3761 // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
3762 // in that the v1.6- would lonly load ab-cd...which was *always* flattened.
3763 //
3764 // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
3765 // and the extra possible extra transaction.
3766 //
3767
3768 function forEachLocale(locale, func){
3769 // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
3770 var parts = locale.split("-");
3771 while(parts.length){
3772 if(func(parts.join("-"))){
3773 return true;
3774 }
3775 parts.pop();
3776 }
3777 return func("ROOT");
3778 }
81bea17a 3779
1354d172
AD
3780 function preload(locale){
3781 locale = normalizeLocale(locale);
3782 forEachLocale(locale, function(loc){
3783 if(array.indexOf(localesGenerated, loc)>=0){
3784 var mid = bundlePrefix.replace(/\./g, "/")+"_"+loc;
3785 preloading++;
3786 (isXd(mid) || guaranteedAmdFormat ? require : syncRequire)([mid], function(rollup){
3787 for(var p in rollup){
3788 cache[p + "/" + locale] = rollup[p];
3789 }
3790 --preloading;
3791 while(!preloading && preloadWaitQueue.length){
3792 load.apply(null, preloadWaitQueue.shift());
3793 }
3794 });
3795 return true;
3796 }
3797 return false;
3798 });
3799 }
81bea17a 3800
1354d172
AD
3801 preload();
3802 array.forEach(dojo.config.extraLocale, preload);
3803 },
81bea17a 3804
1354d172
AD
3805 waitForPreloads = function(id, require, load){
3806 if(preloading){
3807 preloadWaitQueue.push([id, require, load]);
3808 }
3809 return preloading;
3810 };
3811 }
81bea17a 3812
1354d172
AD
3813 if(1){
3814 // this code path assumes the dojo loader and won't work with a standard AMD loader
3815 var evalBundle=
3816 // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
3817 new Function(
3818 "__bundle", // the bundle to evalutate
3819 "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
3820 "__mid", // the mid that __bundle is intended to define
3821
3822 // returns one of:
3823 // 1 => the bundle was an AMD bundle
3824 // a legacy bundle object that is the value of __mid
3825 // instance of Error => could not figure out how to evaluate bundle
3826
3827 // used to detect when __bundle calls define
3828 "var define = function(){define.called = 1;},"
3829 + " require = function(){define.called = 1;};"
3830
3831 + "try{"
3832 + "define.called = 0;"
3833 + "eval(__bundle);"
3834 + "if(define.called==1)"
3835 // bundle called define; therefore signal it's an AMD bundle
3836 + "return 1;"
3837
3838 + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
3839 // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
3840 + "return __checkForLegacyModules;"
3841
3842 + "}catch(e){}"
3843 // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
3844 // either way, re-eval *after* surrounding with parentheses
3845
3846 + "try{"
3847 + "return eval('('+__bundle+')');"
3848 + "}catch(e){"
3849 + "return e;"
3850 + "}"
3851 ),
3852
3853 syncRequire= function(deps, callback){
3854 var results= [];
3855 array.forEach(deps, function(mid){
3856 var url= require.toUrl(mid + ".js");
3857
3858 function load(text){
3859 var result = evalBundle(text, checkForLegacyModules, mid);
3860 if(result===1){
3861 // the bundle was an AMD module; re-inject it through the normal AMD path
3862 // we gotta do this since it could be an anonymous module and simply evaluating
3863 // the text here won't provide the loader with the context to know what
3864 // module is being defined()'d. With browser caching, this should be free; further
3865 // this entire code path can be circumvented by using the AMD format to begin with
3866 require([mid], function(bundle){
3867 results.push(cache[url]= bundle);
3868 });
3869 }else{
3870 if(result instanceof Error){
3871 console.error("failed to evaluate i18n bundle; url=" + url, result);
3872 result = {};
3873 }
3874 // nls/<locale>/<bundle-name> indicates not the root.
3875 results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1}));
3876 }
3877 }
81bea17a 3878
1354d172
AD
3879 if(cache[url]){
3880 results.push(cache[url]);
3881 }else{
3882 var bundle= require.syncLoadNls(mid);
3883 // don't need to check for legacy since syncLoadNls returns a module if the module
3884 // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
3885 // from getLocalization --> load, then load will have called checkForLegacyModules() before
3886 // calling syncRequire; if syncRequire is called from preloadLocalizations, then we
3887 // don't care about checkForLegacyModules() because that will be done when a particular
3888 // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
3889 // because cached modules are always v1.7+ built modules.
3890 if(bundle){
3891 results.push(bundle);
3892 }else{
3893 if(!xhr){
3894 try{
3895 require.getText(url, true, load);
3896 }catch(e){
3897 results.push(cache[url]= {});
3898 }
3899 }else{
3900 xhr.get({
3901 url:url,
3902 sync:true,
3903 load:load,
3904 error:function(){
3905 results.push(cache[url]= {});
3906 }
3907 });
3908 }
3909 }
3910 }
3911 });
3912 callback && callback.apply(null, results);
3913 },
81bea17a 3914
1354d172
AD
3915 checkForLegacyModules = function(target){
3916 // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
3917 for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){}
3918 if(object){
3919 result = object[names[i]];
3920 if(!result){
3921 // fallback for incorrect bundle build of 1.6
3922 result = object[names[i].replace(/-/g,"_")];
3923 }
3924 if(result){
3925 cache[target] = result;
3926 }
3927 }
3928 return result;
3929 };
81bea17a 3930
1354d172
AD
3931 thisModule.getLocalization= function(moduleName, bundleName, locale){
3932 var result,
3933 l10nName= getL10nName(moduleName, bundleName, locale).substring(10);
3934 load(l10nName, (!isXd(l10nName) ? syncRequire : require), function(result_){ result= result_; });
3935 return result;
3936 };
81bea17a 3937
1354d172
AD
3938 if(has("dojo-unit-tests")){
3939 unitTests.push(function(doh){
3940 doh.register("tests.i18n.unit", function(t){
3941 var check;
81bea17a 3942
1354d172
AD
3943 check = evalBundle("{prop:1}");
3944 t.is({prop:1}, check); t.is(undefined, check[1]);
81bea17a 3945
1354d172
AD
3946 check = evalBundle("({prop:1})");
3947 t.is({prop:1}, check); t.is(undefined, check[1]);
81bea17a 3948
1354d172
AD
3949 check = evalBundle("{'prop-x':1}");
3950 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
81bea17a 3951
1354d172
AD
3952 check = evalBundle("({'prop-x':1})");
3953 t.is({'prop-x':1}, check); t.is(undefined, check[1]);
81bea17a 3954
1354d172
AD
3955 check = evalBundle("define({'prop-x':1})");
3956 t.is(1, check);
81bea17a 3957
1354d172
AD
3958 check = evalBundle("this is total nonsense and should throw an error");
3959 t.is(check instanceof Error, true);
3960 });
3961 });
81bea17a 3962 }
1354d172 3963 }
81bea17a 3964
1354d172
AD
3965 return lang.mixin(thisModule, {
3966 dynamic:true,
3967 normalize:normalize,
3968 load:load,
3969 cache:cache
3970 });
3971});
81bea17a 3972
1354d172
AD
3973},
3974'dijit/hccss':function(){
3975define("dijit/hccss", [
3976 "require", // require.toUrl
3977 "dojo/_base/config", // config.blankGif
3978 "dojo/dom-class", // domClass.add domConstruct.create domStyle.getComputedStyle
3979 "dojo/dom-construct", // domClass.add domConstruct.create domStyle.getComputedStyle
3980 "dojo/dom-style", // domClass.add domConstruct.create domStyle.getComputedStyle
3981 "dojo/ready", // ready
3982 "dojo/_base/sniff", // has("ie") has("mozilla")
3983 "dojo/_base/window" // win.body
3984], function(require, config, domClass, domConstruct, domStyle, ready, has, win){
3985
3986 // module:
3987 // dijit/hccss
3988 // summary:
3989 // Test if computer is in high contrast mode, and sets dijit_a11y flag on <body> if it is.
81bea17a 3990
1354d172
AD
3991 if(has("ie") || has("mozilla")){ // NOTE: checking in Safari messes things up
3992 // priority is 90 to run ahead of parser priority of 100
3993 ready(90, function(){
3994 // summary:
3995 // Detects if we are in high-contrast mode or not
3996
3997 // create div for testing if high contrast mode is on or images are turned off
3998 var div = domConstruct.create("div",{
3999 id: "a11yTestNode",
4000 style:{
4001 cssText:'border: 1px solid;'
4002 + 'border-color:red green;'
4003 + 'position: absolute;'
4004 + 'height: 5px;'
4005 + 'top: -999px;'
4006 + 'background-image: url("' + (config.blankGif || require.toUrl("dojo/resources/blank.gif")) + '");'
4007 }
4008 }, win.body());
4009
4010 // test it
4011 var cs = domStyle.getComputedStyle(div);
4012 if(cs){
4013 var bkImg = cs.backgroundImage;
4014 var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
4015 if(needsA11y){
4016 domClass.add(win.body(), "dijit_a11y");
4017 }
4018 if(has("ie")){
4019 div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
4020 }else{
4021 win.body().removeChild(div);
4022 }
81bea17a 4023 }
1354d172
AD
4024 });
4025 }
4026});
81bea17a 4027
1354d172
AD
4028},
4029'dijit/tree/ForestStoreModel':function(){
4030define("dijit/tree/ForestStoreModel", [
4031 "dojo/_base/array", // array.indexOf array.some
4032 "dojo/_base/declare", // declare
4033 "dojo/_base/lang", // lang.hitch
4034 "dojo/_base/window", // win.global
4035 "./TreeStoreModel"
4036], function(array, declare, lang, win, TreeStoreModel){
81bea17a 4037
1354d172
AD
4038/*=====
4039var TreeStoreModel = dijit.tree.TreeStoreModel;
4040=====*/
81bea17a 4041
1354d172
AD
4042// module:
4043// dijit/tree/ForestStoreModel
4044// summary:
4045// Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4046// a.k.a. a store that has multiple "top level" items.
81bea17a 4047
1354d172
AD
4048return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
4049 // summary:
4050 // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
4051 // a.k.a. a store that has multiple "top level" items.
4052 //
4053 // description
4054 // Use this class to wrap a dojo.data store, making all the items matching the specified query
4055 // appear as children of a fabricated "root item". If no query is specified then all the
4056 // items returned by fetch() on the underlying store become children of the root item.
4057 // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
4058 //
4059 // When using this class the developer must override a number of methods according to their app and
4060 // data, including:
4061 // - onNewRootItem
4062 // - onAddToRoot
4063 // - onLeaveRoot
4064 // - onNewItem
4065 // - onSetItem
4066
4067 // Parameters to constructor
4068
4069 // rootId: String
4070 // ID of fabricated root item
4071 rootId: "$root$",
4072
4073 // rootLabel: String
4074 // Label of fabricated root item
4075 rootLabel: "ROOT",
4076
4077 // query: String
4078 // Specifies the set of children of the root item.
4079 // example:
4080 // | {type:'continent'}
4081 query: null,
4082
4083 // End of parameters to constructor
4084
4085 constructor: function(params){
81bea17a 4086 // summary:
1354d172 4087 // Sets up variables, etc.
81bea17a 4088 // tags:
1354d172 4089 // private
81bea17a 4090
1354d172
AD
4091 // Make dummy root item
4092 this.root = {
4093 store: this,
4094 root: true,
4095 id: params.rootId,
4096 label: params.rootLabel,
4097 children: params.rootChildren // optional param
4098 };
81bea17a
AD
4099 },
4100
1354d172
AD
4101 // =======================================================================
4102 // Methods for traversing hierarchy
4103
4104 mayHaveChildren: function(/*dojo.data.Item*/ item){
81bea17a 4105 // summary:
1354d172
AD
4106 // Tells if an item has or may have children. Implementing logic here
4107 // avoids showing +/- expando icon for nodes that we know don't have children.
4108 // (For efficiency reasons we may not want to check if an element actually
4109 // has children until user clicks the expando node)
81bea17a
AD
4110 // tags:
4111 // extension
1354d172
AD
4112 return item === this.root || this.inherited(arguments);
4113 },
81bea17a 4114
1354d172
AD
4115 getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
4116 // summary:
4117 // Calls onComplete() with array of child items of given parent item, all loaded.
4118 if(parentItem === this.root){
4119 if(this.root.children){
4120 // already loaded, just return
4121 callback(this.root.children);
4122 }else{
4123 this.store.fetch({
4124 query: this.query,
4125 onComplete: lang.hitch(this, function(items){
4126 this.root.children = items;
4127 callback(items);
4128 }),
4129 onError: onError
4130 });
4131 }
4132 }else{
4133 this.inherited(arguments);
4134 }
4135 },
81bea17a 4136
1354d172
AD
4137 // =======================================================================
4138 // Inspecting items
81bea17a 4139
1354d172
AD
4140 isItem: function(/* anything */ something){
4141 return (something === this.root) ? true : this.inherited(arguments);
4142 },
4143
4144 fetchItemByIdentity: function(/* object */ keywordArgs){
4145 if(keywordArgs.identity == this.root.id){
4146 var scope = keywordArgs.scope?keywordArgs.scope:win.global;
4147 if(keywordArgs.onItem){
4148 keywordArgs.onItem.call(scope, this.root);
4149 }
4150 }else{
4151 this.inherited(arguments);
4152 }
4153 },
81bea17a 4154
1354d172
AD
4155 getIdentity: function(/* item */ item){
4156 return (item === this.root) ? this.root.id : this.inherited(arguments);
4157 },
81bea17a 4158
1354d172
AD
4159 getLabel: function(/* item */ item){
4160 return (item === this.root) ? this.root.label : this.inherited(arguments);
4161 },
81bea17a 4162
1354d172
AD
4163 // =======================================================================
4164 // Write interface
81bea17a 4165
1354d172
AD
4166 newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
4167 // summary:
4168 // Creates a new item. See dojo.data.api.Write for details on args.
4169 // Used in drag & drop when item from external source dropped onto tree.
4170 if(parent === this.root){
4171 this.onNewRootItem(args);
4172 return this.store.newItem(args);
4173 }else{
4174 return this.inherited(arguments);
81bea17a 4175 }
1354d172 4176 },
81bea17a 4177
1354d172
AD
4178 onNewRootItem: function(/* dojo.dnd.Item */ /*===== args =====*/){
4179 // summary:
4180 // User can override this method to modify a new element that's being
4181 // added to the root of the tree, for example to add a flag like root=true
4182 },
81bea17a 4183
1354d172
AD
4184 pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
4185 // summary:
4186 // Move or copy an item from one parent item to another.
4187 // Used in drag & drop
4188 if(oldParentItem === this.root){
4189 if(!bCopy){
4190 // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
4191 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4192 // that this element is no longer a child of the root node
4193 this.onLeaveRoot(childItem);
4194 }
4195 }
4196 this.inherited(arguments, [childItem,
4197 oldParentItem === this.root ? null : oldParentItem,
4198 newParentItem === this.root ? null : newParentItem,
4199 bCopy,
4200 insertIndex
4201 ]);
4202 if(newParentItem === this.root){
4203 // It's onAddToRoot()'s responsibility to modify the item so it matches
4204 // this.query... thus triggering an onChildrenChange() event to notify the Tree
4205 // that this element is now a child of the root node
4206 this.onAddToRoot(childItem);
4207 }
4208 },
81bea17a 4209
1354d172
AD
4210 // =======================================================================
4211 // Handling for top level children
a089699c 4212
1354d172
AD
4213 onAddToRoot: function(/* item */ item){
4214 // summary:
4215 // Called when item added to root of tree; user must override this method
4216 // to modify the item so that it matches the query for top level items
4217 // example:
4218 // | store.setValue(item, "root", true);
4219 // tags:
4220 // extension
4221 console.log(this, ": item ", item, " added to root");
4222 },
a089699c 4223
1354d172
AD
4224 onLeaveRoot: function(/* item */ item){
4225 // summary:
4226 // Called when item removed from root of tree; user must override this method
4227 // to modify the item so it doesn't match the query for top level items
4228 // example:
4229 // | store.unsetAttribute(item, "root");
4230 // tags:
4231 // extension
4232 console.log(this, ": item ", item, " removed from root");
4233 },
a089699c 4234
1354d172
AD
4235 // =======================================================================
4236 // Events from data store
a089699c 4237
1354d172
AD
4238 _requeryTop: function(){
4239 // reruns the query for the children of the root node,
4240 // sending out an onSet notification if those children have changed
4241 var oldChildren = this.root.children || [];
4242 this.store.fetch({
4243 query: this.query,
4244 onComplete: lang.hitch(this, function(newChildren){
4245 this.root.children = newChildren;
81bea17a 4246
1354d172
AD
4247 // If the list of children or the order of children has changed...
4248 if(oldChildren.length != newChildren.length ||
4249 array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
4250 this.onChildrenChange(this.root, newChildren);
4251 }
4252 })
4253 });
4254 },
a089699c 4255
1354d172 4256 onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
a089699c 4257 // summary:
1354d172
AD
4258 // Handler for when new items appear in the store. Developers should override this
4259 // method to be more efficient based on their app/data.
a089699c 4260 // description:
1354d172
AD
4261 // Note that the default implementation requeries the top level items every time
4262 // a new item is created, since any new item could be a top level item (even in
4263 // addition to being a child of another item, since items can have multiple parents).
4264 //
4265 // If developers can detect which items are possible top level items (based on the item and the
4266 // parentInfo parameters), they should override this method to only call _requeryTop() for top
4267 // level items. Often all top level items have parentInfo==null, but
4268 // that will depend on which store you use and what your data is like.
4269 // tags:
4270 // extension
4271 this._requeryTop();
a089699c 4272
1354d172
AD
4273 this.inherited(arguments);
4274 },
a089699c 4275
1354d172
AD
4276 onDeleteItem: function(/*Object*/ item){
4277 // summary:
4278 // Handler for delete notifications from underlying store
a089699c 4279
1354d172
AD
4280 // check if this was a child of root, and if so send notification that root's children
4281 // have changed
4282 if(array.indexOf(this.root.children, item) != -1){
4283 this._requeryTop();
a089699c 4284 }
a089699c 4285
1354d172
AD
4286 this.inherited(arguments);
4287 },
4288
4289 onSetItem: function(/* item */ item,
4290 /* attribute-name-string */ attribute,
4291 /* object | array */ oldValue,
4292 /* object | array */ newValue){
81bea17a 4293 // summary:
1354d172
AD
4294 // Updates the tree view according to changes to an item in the data store.
4295 // Developers should override this method to be more efficient based on their app/data.
a089699c 4296 // description:
1354d172
AD
4297 // Handles updates to an item's children by calling onChildrenChange(), and
4298 // other updates to an item by calling onChange().
4299 //
4300 // Also, any change to any item re-executes the query for the tree's top-level items,
4301 // since this modified item may have started/stopped matching the query for top level items.
4302 //
4303 // If possible, developers should override this function to only call _requeryTop() when
4304 // the change to the item has caused it to stop/start being a top level item in the tree.
4305 // tags:
4306 // extension
a089699c 4307
1354d172
AD
4308 this._requeryTop();
4309 this.inherited(arguments);
4310 }
a089699c 4311
1354d172 4312});
a089699c 4313
1354d172 4314});
a089699c 4315
1354d172
AD
4316},
4317'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",
4318'dijit/form/_ComboBoxMenuMixin':function(){
4319define("dijit/form/_ComboBoxMenuMixin", [
4320 "dojo/_base/array", // array.forEach
4321 "dojo/_base/declare", // declare
4322 "dojo/dom-attr", // domAttr.set
4323 "dojo/i18n", // i18n.getLocalization
4324 "dojo/_base/window", // win.doc.createTextNode
4325 "dojo/i18n!./nls/ComboBox"
4326], function(array, declare, domAttr, i18n, win){
4327
4328// module:
4329// dijit/form/_ComboBoxMenuMixin
4330// summary:
4331// Focus-less menu for internal use in `dijit.form.ComboBox`
a089699c 4332
1354d172
AD
4333return declare( "dijit.form._ComboBoxMenuMixin", null, {
4334 // summary:
4335 // Focus-less menu for internal use in `dijit.form.ComboBox`
4336 // tags:
4337 // private
a089699c 4338
1354d172
AD
4339 // _messages: Object
4340 // Holds "next" and "previous" text for paging buttons on drop down
4341 _messages: null,
a089699c 4342
1354d172
AD
4343 postMixInProperties: function(){
4344 this.inherited(arguments);
4345 this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
4346 },
a089699c 4347
1354d172
AD
4348 buildRendering: function(){
4349 this.inherited(arguments);
a089699c 4350
1354d172
AD
4351 // fill in template with i18n messages
4352 this.previousButton.innerHTML = this._messages["previousMessage"];
4353 this.nextButton.innerHTML = this._messages["nextMessage"];
4354 },
a089699c 4355
1354d172
AD
4356 _setValueAttr: function(/*Object*/ value){
4357 this.value = value;
4358 this.onChange(value);
4359 },
a089699c 4360
1354d172
AD
4361 onClick: function(/*DomNode*/ node){
4362 if(node == this.previousButton){
4363 this._setSelectedAttr(null);
4364 this.onPage(-1);
4365 }else if(node == this.nextButton){
4366 this._setSelectedAttr(null);
4367 this.onPage(1);
4368 }else{
4369 this.onChange(node);
4370 }
4371 },
a089699c 4372
1354d172
AD
4373 // stubs
4374 onChange: function(/*Number*/ /*===== direction =====*/){
4375 // summary:
4376 // Notifies ComboBox/FilteringSelect that user selected an option.
4377 // tags:
4378 // callback
4379 },
a089699c 4380
1354d172
AD
4381 onPage: function(/*Number*/ /*===== direction =====*/){
4382 // summary:
4383 // Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
4384 // tags:
4385 // callback
4386 },
a089699c 4387
1354d172
AD
4388 onClose: function(){
4389 // summary:
4390 // Callback from dijit.popup code to this widget, notifying it that it closed
4391 // tags:
4392 // private
4393 this._setSelectedAttr(null);
4394 },
a089699c 4395
1354d172
AD
4396 _createOption: function(/*Object*/ item, labelFunc){
4397 // summary:
4398 // Creates an option to appear on the popup menu subclassed by
4399 // `dijit.form.FilteringSelect`.
81bea17a 4400
1354d172
AD
4401 var menuitem = this._createMenuItem();
4402 var labelObject = labelFunc(item);
4403 if(labelObject.html){
4404 menuitem.innerHTML = labelObject.label;
4405 }else{
4406 menuitem.appendChild(
4407 win.doc.createTextNode(labelObject.label)
4408 );
4409 }
4410 // #3250: in blank options, assign a normal height
4411 if(menuitem.innerHTML == ""){
4412 menuitem.innerHTML = "&#160;"; // &nbsp;
4413 }
a089699c 4414
1354d172
AD
4415 // update menuitem.dir if BidiSupport was required
4416 this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
a089699c 4417
1354d172
AD
4418 menuitem.item=item;
4419 return menuitem;
4420 },
a089699c 4421
1354d172
AD
4422 createOptions: function(results, options, labelFunc){
4423 // summary:
4424 // Fills in the items in the drop down list
4425 // results:
4426 // Array of items
4427 // options:
4428 // The options to the query function of the store
4429 //
4430 // labelFunc:
4431 // Function to produce a label in the drop down list from a dojo.data item
4432
4433 // display "Previous . . ." button
4434 this.previousButton.style.display = (options.start == 0) ? "none" : "";
4435 domAttr.set(this.previousButton, "id", this.id + "_prev");
4436 // create options using _createOption function defined by parent
4437 // ComboBox (or FilteringSelect) class
4438 // #2309:
4439 // iterate over cache nondestructively
4440 array.forEach(results, function(item, i){
4441 var menuitem = this._createOption(item, labelFunc);
4442 domAttr.set(menuitem, "id", this.id + i);
4443 this.nextButton.parentNode.insertBefore(menuitem, this.nextButton);
4444 }, this);
4445 // display "Next . . ." button
4446 var displayMore = false;
4447 // Try to determine if we should show 'more'...
4448 if(results.total && !results.total.then && results.total != -1){
4449 if((options.start + options.count) < results.total){
4450 displayMore = true;
4451 }else if((options.start + options.count) > results.total && options.count == results.length){
4452 // Weird return from a data store, where a start + count > maxOptions
4453 // implies maxOptions isn't really valid and we have to go into faking it.
4454 // And more or less assume more if count == results.length
4455 displayMore = true;
a089699c 4456 }
1354d172
AD
4457 }else if(options.count == results.length){
4458 //Don't know the size, so we do the best we can based off count alone.
4459 //So, if we have an exact match to count, assume more.
4460 displayMore = true;
4461 }
a089699c 4462
1354d172
AD
4463 this.nextButton.style.display = displayMore ? "" : "none";
4464 domAttr.set(this.nextButton,"id", this.id + "_next");
4465 return this.containerNode.childNodes;
4466 },
a089699c 4467
1354d172
AD
4468 clearResultList: function(){
4469 // summary:
4470 // Clears the entries in the drop down list, but of course keeps the previous and next buttons.
4471 var container = this.containerNode;
4472 while(container.childNodes.length > 2){
4473 container.removeChild(container.childNodes[container.childNodes.length-2]);
4474 }
4475 this._setSelectedAttr(null);
4476 },
a089699c 4477
1354d172
AD
4478 highlightFirstOption: function(){
4479 // summary:
4480 // Highlight the first real item in the list (not Previous Choices).
4481 this.selectFirstNode();
4482 },
a089699c 4483
1354d172
AD
4484 highlightLastOption: function(){
4485 // summary:
4486 // Highlight the last real item in the list (not More Choices).
4487 this.selectLastNode();
4488 },
a089699c 4489
1354d172
AD
4490 selectFirstNode: function(){
4491 this.inherited(arguments);
4492 if(this.getHighlightedOption() == this.previousButton){
4493 this.selectNextNode();
4494 }
4495 },
a089699c 4496
1354d172
AD
4497 selectLastNode: function(){
4498 this.inherited(arguments);
4499 if(this.getHighlightedOption() == this.nextButton){
4500 this.selectPreviousNode();
4501 }
4502 },
a089699c 4503
1354d172
AD
4504 getHighlightedOption: function(){
4505 return this._getSelectedAttr();
4506 }
4507});
a089699c 4508
1354d172 4509});
a089699c 4510
1354d172
AD
4511},
4512'dojo/parser':function(){
4513define(
4514 "dojo/parser", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/html", "./_base/window", "./_base/url",
4515 "./_base/json", "./aspect", "./date/stamp", "./query", "./on", "./ready"],
4516 function(dojo, dlang, darray, dhtml, dwindow, _Url, djson, aspect, dates, query, don){
4517
4518// module:
4519// dojo/parser
4520// summary:
4521// The Dom/Widget parsing package
a089699c 4522
1354d172 4523new Date("X"); // workaround for #11279, new Date("") == NaN
a089699c 4524
1354d172
AD
4525var features = {
4526 // Feature detection for when node.attributes only lists the attributes specified in the markup
4527 // rather than old IE/quirks behavior where it lists every default value too
4528 "dom-attributes-explicit": document.createElement("div").attributes.length < 40
4529};
4530function has(feature){
4531 return features[feature];
4532}
a089699c 4533
a089699c 4534
1354d172
AD
4535dojo.parser = new function(){
4536 // summary:
4537 // The Dom/Widget parsing package
a089699c 4538
1354d172
AD
4539 var _nameMap = {
4540 // Map from widget name (ex: "dijit.form.Button") to structure mapping
4541 // lowercase version of attribute names to the version in the widget ex:
4542 // {
4543 // label: "label",
4544 // onclick: "onClick"
4545 // }
4546 };
4547 function getNameMap(proto){
4548 // summary:
4549 // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
4550 var map = {};
4551 for(var name in proto){
4552 if(name.charAt(0)=="_"){ continue; } // skip internal properties
4553 map[name.toLowerCase()] = name;
a089699c 4554 }
1354d172 4555 return map;
a089699c 4556 }
1354d172
AD
4557 // Widgets like BorderContainer add properties to _Widget via dojo.extend().
4558 // If BorderContainer is loaded after _Widget's parameter list has been cached,
4559 // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget).
4560 aspect.after(dlang, "extend", function(){
4561 _nameMap = {};
4562 }, true);
a089699c 4563
1354d172
AD
4564 // Map from widget name (ex: "dijit.form.Button") to constructor
4565 var _ctorMap = {};
a089699c 4566
1354d172
AD
4567 this._functionFromScript = function(script, attrData){
4568 // summary:
4569 // Convert a <script type="dojo/method" args="a, b, c"> ... </script>
4570 // into a function
4571 // script: DOMNode
4572 // The <script> DOMNode
4573 // attrData: String
4574 // For HTML5 compliance, searches for attrData + "args" (typically
4575 // "data-dojo-args") instead of "args"
4576 var preamble = "";
4577 var suffix = "";
4578 var argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args"));
4579 if(argsStr){
4580 darray.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
4581 preamble += "var "+part+" = arguments["+idx+"]; ";
4582 });
a089699c 4583 }
1354d172
AD
4584 var withStr = script.getAttribute("with");
4585 if(withStr && withStr.length){
4586 darray.forEach(withStr.split(/\s*,\s*/), function(part){
4587 preamble += "with("+part+"){";
4588 suffix += "}";
4589 });
a089699c 4590 }
1354d172
AD
4591 return new Function(preamble+script.innerHTML+suffix);
4592 };
a089699c 4593
1354d172
AD
4594 this.instantiate = /*====== dojo.parser.instantiate= ======*/function(nodes, mixin, args){
4595 // summary:
4596 // Takes array of nodes, and turns them into class instances and
4597 // potentially calls a startup method to allow them to connect with
4598 // any children.
4599 // nodes: Array
4600 // Array of nodes or objects like
4601 // | {
4602 // | type: "dijit.form.Button",
4603 // | node: DOMNode,
4604 // | scripts: [ ... ], // array of <script type="dojo/..."> children of node
4605 // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc.
4606 // | }
4607 // mixin: Object?
4608 // An object that will be mixed in with each node in the array.
4609 // Values in the mixin will override values in the node, if they
4610 // exist.
4611 // args: Object?
4612 // An object used to hold kwArgs for instantiation.
4613 // See parse.args argument for details.
a089699c 4614
1354d172
AD
4615 var thelist = [],
4616 mixin = mixin||{};
4617 args = args||{};
a089699c 4618
1354d172
AD
4619 // Precompute names of special attributes we are looking for
4620 // TODO: for 2.0 default to data-dojo- regardless of scopeName (or maybe scopeName won't exist in 2.0)
4621 var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType"
4622 attrData = "data-" + (args.scope || dojo._scopeName) + "-",// typically "data-dojo-"
4623 dataDojoType = attrData + "type", // typically "data-dojo-type"
4624 dataDojoProps = attrData + "props", // typically "data-dojo-props"
4625 dataDojoAttachPoint = attrData + "attach-point",
4626 dataDojoAttachEvent = attrData + "attach-event",
4627 dataDojoId = attrData + "id";
4628
4629 // And make hash to quickly check if a given attribute is special, and to map the name to something friendly
4630 var specialAttrs = {};
4631 darray.forEach([dataDojoProps, dataDojoType, dojoType, dataDojoId, "jsId", dataDojoAttachPoint,
4632 dataDojoAttachEvent, "dojoAttachPoint", "dojoAttachEvent", "class", "style"], function(name){
4633 specialAttrs[name.toLowerCase()] = name.replace(args.scope, "dojo");
4634 });
a089699c 4635
1354d172
AD
4636 darray.forEach(nodes, function(obj){
4637 if(!obj){ return; }
81bea17a 4638
1354d172
AD
4639 var node = obj.node || obj,
4640 type = dojoType in mixin ? mixin[dojoType] : obj.node ? obj.type : (node.getAttribute(dataDojoType) || node.getAttribute(dojoType)),
4641 ctor = _ctorMap[type] || (_ctorMap[type] = dlang.getObject(type)),
4642 proto = ctor && ctor.prototype;
4643 if(!ctor){
4644 throw new Error("Could not load class '" + type);
a089699c 4645 }
a089699c 4646
1354d172
AD
4647 // Setup hash to hold parameter settings for this widget. Start with the parameter
4648 // settings inherited from ancestors ("dir" and "lang").
4649 // Inherited setting may later be overridden by explicit settings on node itself.
4650 var params = {};
a089699c 4651
1354d172
AD
4652 if(args.defaults){
4653 // settings for the document itself (or whatever subtree is being parsed)
4654 dlang.mixin(params, args.defaults);
a089699c 4655 }
1354d172
AD
4656 if(obj.inherited){
4657 // settings from dir=rtl or lang=... on a node above this node
4658 dlang.mixin(params, obj.inherited);
a089699c 4659 }
a089699c 4660
1354d172
AD
4661 // Get list of attributes explicitly listed in the markup
4662 var attributes;
4663 if(has("dom-attributes-explicit")){
4664 // Standard path to get list of user specified attributes
4665 attributes = node.attributes;
4666 }else{
4667 // Special path for IE, avoid (sometimes >100) bogus entries in node.attributes
4668 var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false),
4669 attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*/, "").replace(/>.*$/, "");
a089699c 4670
1354d172
AD
4671 attributes = darray.map(attrs.split(/\s+/), function(name){
4672 var lcName = name.toLowerCase();
4673 return {
4674 name: name,
4675 // getAttribute() doesn't work for button.value, returns innerHTML of button.
4676 // but getAttributeNode().value doesn't work for the form.encType or li.value
4677 value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ?
4678 node.getAttribute(lcName) : node.getAttributeNode(lcName).value,
4679 specified: true
4680 };
4681 });
a089699c
AD
4682 }
4683
1354d172
AD
4684 // Read in attributes and process them, including data-dojo-props, data-dojo-type,
4685 // dojoAttachPoint, etc., as well as normal foo=bar attributes.
4686 var i=0, item;
4687 while(item = attributes[i++]){
4688 if(!item || !item.specified){
4689 continue;
a089699c 4690 }
a089699c 4691
1354d172
AD
4692 var name = item.name,
4693 lcName = name.toLowerCase(),
4694 value = item.value;
a089699c 4695
1354d172
AD
4696 if(lcName in specialAttrs){
4697 switch(specialAttrs[lcName]){
a089699c 4698
1354d172
AD
4699 // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
4700 case "data-dojo-props":
4701 var extra = value;
4702 break;
81bea17a 4703
1354d172
AD
4704 // data-dojo-id or jsId. TODO: drop jsId in 2.0
4705 case "data-dojo-id":
4706 case "jsId":
4707 var jsname = value;
4708 break;
a089699c 4709
1354d172
AD
4710 // For the benefit of _Templated
4711 case "data-dojo-attach-point":
4712 case "dojoAttachPoint":
4713 params.dojoAttachPoint = value;
4714 break;
4715 case "data-dojo-attach-event":
4716 case "dojoAttachEvent":
4717 params.dojoAttachEvent = value;
4718 break;
a089699c 4719
1354d172
AD
4720 // Special parameter handling needed for IE
4721 case "class":
4722 params["class"] = node.className;
4723 break;
4724 case "style":
4725 params["style"] = node.style && node.style.cssText;
4726 break;
4727 }
4728 }else{
4729 // Normal attribute, ex: value="123"
a089699c 4730
1354d172
AD
4731 // Find attribute in widget corresponding to specified name.
4732 // May involve case conversion, ex: onclick --> onClick
4733 if(!(name in proto)){
4734 var map = (_nameMap[type] || (_nameMap[type] = getNameMap(proto)));
4735 name = map[lcName] || name;
4736 }
a089699c 4737
1354d172
AD
4738 // Set params[name] to value, doing type conversion
4739 if(name in proto){
4740 switch(typeof proto[name]){
4741 case "string":
4742 params[name] = value;
4743 break;
4744 case "number":
4745 params[name] = value.length ? Number(value) : NaN;
4746 break;
4747 case "boolean":
4748 // for checked/disabled value might be "" or "checked". interpret as true.
4749 params[name] = value.toLowerCase() != "false";
4750 break;
4751 case "function":
4752 if(value === "" || value.search(/[^\w\.]+/i) != -1){
4753 // The user has specified some text for a function like "return x+5"
4754 params[name] = new Function(value);
4755 }else{
4756 // The user has specified the name of a function like "myOnClick"
4757 // or a single word function "return"
4758 params[name] = dlang.getObject(value, false) || new Function(value);
4759 }
4760 break;
4761 default:
4762 var pVal = proto[name];
4763 params[name] =
4764 (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array
4765 (pVal instanceof Date) ?
4766 (value == "" ? new Date("") : // the NaN of dates
4767 value == "now" ? new Date() : // current date
4768 dates.fromISOString(value)) :
4769 (pVal instanceof dojo._Url) ? (dojo.baseUrl + value) :
4770 djson.fromJson(value);
4771 }
4772 }else{
4773 params[name] = value;
4774 }
4775 }
4776 }
a089699c 4777
1354d172
AD
4778 // Mix things found in data-dojo-props into the params, overriding any direct settings
4779 if(extra){
4780 try{
4781 extra = djson.fromJson.call(args.propsThis, "{" + extra + "}");
4782 dlang.mixin(params, extra);
4783 }catch(e){
4784 // give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
4785 throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
4786 }
4787 }
a089699c 4788
1354d172
AD
4789 // Any parameters specified in "mixin" override everything else.
4790 dlang.mixin(params, mixin);
a089699c 4791
1354d172
AD
4792 var scripts = obj.node ? obj.scripts : (ctor && (ctor._noScript || proto._noScript) ? [] :
4793 query("> script[type^='dojo/']", node));
a089699c 4794
1354d172
AD
4795 // Process <script type="dojo/*"> script tags
4796 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
4797 // the widget on instantiation.
4798 // <script type="dojo/method"> tags (with no event) are executed after instantiation
4799 // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation
4800 // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
4801 // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
4802 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
4803 var connects = [], // functions to connect after instantiation
4804 calls = [], // functions to call after instantiation
4805 watch = [], //functions to watch after instantiation
4806 on = []; //functions to on after instantiation
4807
4808 if(scripts){
4809 for(i=0; i<scripts.length; i++){
4810 var script = scripts[i];
4811 node.removeChild(script);
4812 // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
4813 var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
4814 prop = script.getAttribute(attrData + "prop"),
4815 type = script.getAttribute("type"),
4816 nf = this._functionFromScript(script, attrData);
4817 if(event){
4818 if(type == "dojo/connect"){
4819 connects.push({event: event, func: nf});
4820 }else if(type == "dojo/on"){
4821 on.push({event: event, func: nf});
4822 }else{
4823 params[event] = nf;
4824 }
4825 }else if(type == "dojo/watch"){
4826 watch.push({prop: prop, func: nf});
4827 }else{
4828 calls.push(nf);
4829 }
a089699c 4830 }
a089699c 4831 }
a089699c 4832
1354d172
AD
4833 // create the instance
4834 var markupFactory = ctor.markupFactory || proto.markupFactory;
4835 var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node);
4836 thelist.push(instance);
a089699c 4837
1354d172
AD
4838 // map it to the JS namespace if that makes sense
4839 if(jsname){
4840 dlang.setObject(jsname, instance);
4841 }
a089699c 4842
1354d172
AD
4843 // process connections and startup functions
4844 for(i=0; i<connects.length; i++){
4845 aspect.after(instance, connects[i].event, dojo.hitch(instance, connects[i].func), true);
4846 }
4847 for(i=0; i<calls.length; i++){
4848 calls[i].call(instance);
4849 }
4850 for(i=0; i<watch.length; i++){
4851 instance.watch(watch[i].prop, watch[i].func);
4852 }
4853 for(i=0; i<on.length; i++){
4854 don(instance, on[i].event, on[i].func);
4855 }
4856 }, this);
a089699c 4857
1354d172
AD
4858 // Call startup on each top level instance if it makes sense (as for
4859 // widgets). Parent widgets will recursively call startup on their
4860 // (non-top level) children
4861 if(!mixin._started){
4862 darray.forEach(thelist, function(instance){
4863 if( !args.noStart && instance &&
4864 dlang.isFunction(instance.startup) &&
4865 !instance._started
4866 ){
4867 instance.startup();
4868 }
4869 });
4870 }
4871 return thelist;
4872 };
a089699c 4873
1354d172 4874 this.parse = /*====== dojo.parser.parse= ======*/ function(rootNode, args){
a089699c 4875 // summary:
1354d172
AD
4876 // Scan the DOM for class instances, and instantiate them.
4877 //
4878 // description:
4879 // Search specified node (or root node) recursively for class instances,
4880 // and instantiate them. Searches for either data-dojo-type="Class" or
4881 // dojoType="Class" where "Class" is a a fully qualified class name,
4882 // like `dijit.form.Button`
4883 //
4884 // Using `data-dojo-type`:
4885 // Attributes using can be mixed into the parameters used to instantiate the
4886 // Class by using a `data-dojo-props` attribute on the node being converted.
4887 // `data-dojo-props` should be a string attribute to be converted from JSON.
4888 //
4889 // Using `dojoType`:
4890 // Attributes are read from the original domNode and converted to appropriate
4891 // types by looking up the Class prototype values. This is the default behavior
4892 // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
4893 // go away in Dojo 2.0.
4894 //
4895 // rootNode: DomNode?
4896 // A default starting root node from which to start the parsing. Can be
4897 // omitted, defaulting to the entire document. If omitted, the `args`
4898 // object can be passed in this place. If the `args` object has a
4899 // `rootNode` member, that is used.
4900 //
4901 // args: Object
4902 // a kwArgs object passed along to instantiate()
4903 //
4904 // * noStart: Boolean?
4905 // when set will prevent the parser from calling .startup()
4906 // when locating the nodes.
4907 // * rootNode: DomNode?
4908 // identical to the function's `rootNode` argument, though
4909 // allowed to be passed in via this `args object.
4910 // * template: Boolean
4911 // If true, ignores ContentPane's stopParser flag and parses contents inside of
4912 // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
4913 // nested inside the ContentPane to work.
4914 // * inherited: Object
4915 // Hash possibly containing dir and lang settings to be applied to
4916 // parsed widgets, unless there's another setting on a sub-node that overrides
4917 // * scope: String
4918 // Root for attribute names to search for. If scopeName is dojo,
4919 // will search for data-dojo-type (or dojoType). For backwards compatibility
4920 // reasons defaults to dojo._scopeName (which is "dojo" except when
4921 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
4922 // * propsThis: Object
4923 // If specified, "this" referenced from data-dojo-props will refer to propsThis.
4924 // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
4925 //
4926 // example:
4927 // Parse all widgets on a page:
4928 // | dojo.parser.parse();
4929 //
4930 // example:
4931 // Parse all classes within the node with id="foo"
4932 // | dojo.parser.parse(dojo.byId('foo'));
4933 //
4934 // example:
4935 // Parse all classes in a page, but do not call .startup() on any
4936 // child
4937 // | dojo.parser.parse({ noStart: true })
4938 //
4939 // example:
4940 // Parse all classes in a node, but do not call .startup()
4941 // | dojo.parser.parse(someNode, { noStart:true });
4942 // | // or
4943 // | dojo.parser.parse({ noStart:true, rootNode: someNode });
a089699c 4944
1354d172
AD
4945 // determine the root node based on the passed arguments.
4946 var root;
4947 if(!args && rootNode && rootNode.rootNode){
4948 args = rootNode;
4949 root = args.rootNode;
4950 }else{
4951 root = rootNode;
4952 }
4953 root = root ? dhtml.byId(root) : dwindow.body();
4954 args = args || {};
a089699c 4955
1354d172
AD
4956 var dojoType = (args.scope || dojo._scopeName) + "Type", // typically "dojoType"
4957 attrData = "data-" + (args.scope || dojo._scopeName) + "-", // typically "data-dojo-"
4958 dataDojoType = attrData + "type", // typically "data-dojo-type"
4959 dataDojoTextDir = attrData + "textdir"; // typically "data-dojo-textdir"
a089699c 4960
1354d172
AD
4961 // List of all nodes on page w/dojoType specified
4962 var list = [];
a089699c 4963
1354d172
AD
4964 // Info on DOMNode currently being processed
4965 var node = root.firstChild;
4966
4967 // Info on parent of DOMNode currently being processed
4968 // - inherited: dir, lang, and textDir setting of parent, or inherited by parent
4969 // - parent: pointer to identical structure for my parent (or null if no parent)
4970 // - scripts: if specified, collects <script type="dojo/..."> type nodes from children
4971 var inherited = args && args.inherited;
4972 if(!inherited){
4973 function findAncestorAttr(node, attr){
4974 return (node.getAttribute && node.getAttribute(attr)) ||
4975 (node !== dwindow.doc && node !== dwindow.doc.documentElement && node.parentNode ? findAncestorAttr(node.parentNode, attr) : null);
4976 }
4977 inherited = {
4978 dir: findAncestorAttr(root, "dir"),
4979 lang: findAncestorAttr(root, "lang"),
4980 textDir: findAncestorAttr(root, dataDojoTextDir)
4981 };
4982 for(var key in inherited){
4983 if(!inherited[key]){ delete inherited[key]; }
a089699c 4984 }
1354d172
AD
4985 }
4986 var parent = {
4987 inherited: inherited
4988 };
a089699c 4989
1354d172
AD
4990 // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
4991 var scripts;
a089699c 4992
1354d172
AD
4993 // when true, only look for <script type="dojo/..."> tags, and don't recurse to children
4994 var scriptsOnly;
a089699c 4995
1354d172 4996 function getEffective(parent){
a089699c 4997 // summary:
1354d172
AD
4998 // Get effective dir, lang, textDir settings for specified obj
4999 // (matching "parent" object structure above), and do caching.
5000 // Take care not to return null entries.
5001 if(!parent.inherited){
5002 parent.inherited = {};
5003 var node = parent.node,
5004 grandparent = getEffective(parent.parent);
5005 var inherited = {
5006 dir: node.getAttribute("dir") || grandparent.dir,
5007 lang: node.getAttribute("lang") || grandparent.lang,
5008 textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir
5009 };
5010 for(var key in inherited){
5011 if(inherited[key]){
5012 parent.inherited[key] = inherited[key];
5013 }
5014 }
5015 }
5016 return parent.inherited;
5017 }
a089699c 5018
1354d172
AD
5019 // DFS on DOM tree, collecting nodes with data-dojo-type specified.
5020 while(true){
5021 if(!node){
5022 // Finished this level, continue to parent's next sibling
5023 if(!parent || !parent.node){
5024 break;
5025 }
5026 node = parent.node.nextSibling;
5027 scripts = parent.scripts;
5028 scriptsOnly = false;
5029 parent = parent.parent;
5030 continue;
5031 }
a089699c 5032
1354d172
AD
5033 if(node.nodeType != 1){
5034 // Text or comment node, skip to next sibling
5035 node = node.nextSibling;
5036 continue;
5037 }
a089699c 5038
1354d172
AD
5039 if(scripts && node.nodeName.toLowerCase() == "script"){
5040 // Save <script type="dojo/..."> for parent, then continue to next sibling
5041 type = node.getAttribute("type");
5042 if(type && /^dojo\/\w/i.test(type)){
5043 scripts.push(node);
5044 }
5045 node = node.nextSibling;
5046 continue;
5047 }
5048 if(scriptsOnly){
5049 node = node.nextSibling;
5050 continue;
a089699c 5051 }
a089699c 5052
1354d172
AD
5053 // Check for data-dojo-type attribute, fallback to backward compatible dojoType
5054 var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
a089699c 5055
1354d172
AD
5056 // Short circuit for leaf nodes containing nothing [but text]
5057 var firstChild = node.firstChild;
5058 if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){
5059 node = node.nextSibling;
5060 continue;
5061 }
a089699c 5062
1354d172
AD
5063 // Setup data structure to save info on current node for when we return from processing descendant nodes
5064 var current = {
5065 node: node,
5066 scripts: scripts,
5067 parent: parent
5068 };
a089699c 5069
1354d172
AD
5070 // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate
5071 var ctor = type && (_ctorMap[type] || (_ctorMap[type] = dlang.getObject(type))), // note: won't find classes declared via dojo.Declaration
5072 childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children
5073 if(type){
5074 list.push({
5075 "type": type,
5076 node: node,
5077 scripts: childScripts,
5078 inherited: getEffective(current) // dir & lang settings for current node, explicit or inherited
5079 });
5080 }
a089699c 5081
1354d172
AD
5082 // Recurse, collecting <script type="dojo/..."> children, and also looking for
5083 // descendant nodes with dojoType specified (unless the widget has the stopParser flag).
5084 // When finished with children, go to my next sibling.
5085 node = firstChild;
5086 scripts = childScripts;
5087 scriptsOnly = ctor && ctor.prototype.stopParser && !(args && args.template);
5088 parent = current;
a089699c 5089
1354d172 5090 }
a089699c 5091
1354d172
AD
5092 // go build the object instances
5093 var mixin = args && args.template ? {template: true} : null;
5094 return this.instantiate(list, mixin, args); // Array
5095 };
5096}();
a089699c 5097
a089699c 5098
1354d172
AD
5099//Register the parser callback. It should be the first callback
5100//after the a11y test.
5101if(dojo.config.parseOnLoad){
5102 dojo.ready(100, dojo.parser, "parse");
5103}
a089699c 5104
1354d172
AD
5105return dojo.parser;
5106});
a089699c 5107
1354d172
AD
5108},
5109'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\"\n/></span>\n",
5110'dojo/dnd/Manager':function(){
5111define("dojo/dnd/Manager", ["../main", "../Evented", "./common", "./autoscroll", "./Avatar"], function(dojo, Evented) {
5112 // module:
5113 // dojo/dnd/Manager
5114 // summary:
5115 // TODOC
a089699c 5116
a089699c 5117
1354d172
AD
5118var Manager = dojo.declare("dojo.dnd.Manager", [Evented], {
5119 // summary:
5120 // the manager of DnD operations (usually a singleton)
5121 constructor: function(){
5122 this.avatar = null;
5123 this.source = null;
5124 this.nodes = [];
5125 this.copy = true;
5126 this.target = null;
5127 this.canDropFlag = false;
5128 this.events = [];
5129 },
81bea17a 5130
1354d172
AD
5131 // avatar's offset from the mouse
5132 OFFSET_X: 16,
5133 OFFSET_Y: 16,
81bea17a 5134
1354d172
AD
5135 // methods
5136 overSource: function(source){
5137 // summary:
5138 // called when a source detected a mouse-over condition
5139 // source: Object
5140 // the reporter
5141 if(this.avatar){
5142 this.target = (source && source.targetState != "Disabled") ? source : null;
5143 this.canDropFlag = Boolean(this.target);
5144 this.avatar.update();
5145 }
5146 dojo.publish("/dnd/source/over", [source]);
5147 },
5148 outSource: function(source){
5149 // summary:
5150 // called when a source detected a mouse-out condition
5151 // source: Object
5152 // the reporter
5153 if(this.avatar){
5154 if(this.target == source){
5155 this.target = null;
5156 this.canDropFlag = false;
5157 this.avatar.update();
5158 dojo.publish("/dnd/source/over", [null]);
a089699c 5159 }
1354d172
AD
5160 }else{
5161 dojo.publish("/dnd/source/over", [null]);
5162 }
5163 },
5164 startDrag: function(source, nodes, copy){
5165 // summary:
5166 // called to initiate the DnD operation
5167 // source: Object
5168 // the source which provides items
5169 // nodes: Array
5170 // the list of transferred items
5171 // copy: Boolean
5172 // copy items, if true, move items otherwise
5173 this.source = source;
5174 this.nodes = nodes;
5175 this.copy = Boolean(copy); // normalizing to true boolean
5176 this.avatar = this.makeAvatar();
5177 dojo.body().appendChild(this.avatar.node);
5178 dojo.publish("/dnd/start", [source, nodes, this.copy]);
5179 this.events = [
5180 dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"),
5181 dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"),
5182 dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"),
5183 dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"),
5184 // cancel text selection and text dragging
5185 dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent),
5186 dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent)
5187 ];
5188 var c = "dojoDnd" + (copy ? "Copy" : "Move");
5189 dojo.addClass(dojo.body(), c);
5190 },
5191 canDrop: function(flag){
5192 // summary:
5193 // called to notify if the current target can accept items
5194 var canDropFlag = Boolean(this.target && flag);
5195 if(this.canDropFlag != canDropFlag){
5196 this.canDropFlag = canDropFlag;
5197 this.avatar.update();
5198 }
5199 },
5200 stopDrag: function(){
5201 // summary:
5202 // stop the DnD in progress
5203 dojo.removeClass(dojo.body(), ["dojoDndCopy", "dojoDndMove"]);
5204 dojo.forEach(this.events, dojo.disconnect);
5205 this.events = [];
5206 this.avatar.destroy();
5207 this.avatar = null;
5208 this.source = this.target = null;
5209 this.nodes = [];
5210 },
5211 makeAvatar: function(){
5212 // summary:
5213 // makes the avatar; it is separate to be overwritten dynamically, if needed
5214 return new dojo.dnd.Avatar(this);
5215 },
5216 updateAvatar: function(){
5217 // summary:
5218 // updates the avatar; it is separate to be overwritten dynamically, if needed
5219 this.avatar.update();
5220 },
a089699c 5221
1354d172
AD
5222 // mouse event processors
5223 onMouseMove: function(e){
5224 // summary:
5225 // event processor for onmousemove
5226 // e: Event
5227 // mouse event
5228 var a = this.avatar;
5229 if(a){
5230 dojo.dnd.autoScrollNodes(e);
5231 //dojo.dnd.autoScroll(e);
5232 var s = a.node.style;
5233 s.left = (e.pageX + this.OFFSET_X) + "px";
5234 s.top = (e.pageY + this.OFFSET_Y) + "px";
5235 var copy = Boolean(this.source.copyState(dojo.isCopyKey(e)));
5236 if(this.copy != copy){
5237 this._setCopyStatus(copy);
5238 }
5239 }
5240 },
5241 onMouseUp: function(e){
5242 // summary:
5243 // event processor for onmouseup
5244 // e: Event
5245 // mouse event
5246 if(this.avatar){
5247 if(this.target && this.canDropFlag){
5248 var copy = Boolean(this.source.copyState(dojo.isCopyKey(e))),
5249 params = [this.source, this.nodes, copy, this.target, e];
5250 dojo.publish("/dnd/drop/before", params);
5251 dojo.publish("/dnd/drop", params);
5252 }else{
5253 dojo.publish("/dnd/cancel");
5254 }
5255 this.stopDrag();
5256 }
5257 },
a089699c 5258
1354d172
AD
5259 // keyboard event processors
5260 onKeyDown: function(e){
5261 // summary:
5262 // event processor for onkeydown:
5263 // watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
5264 // e: Event
5265 // keyboard event
5266 if(this.avatar){
5267 switch(e.keyCode){
5268 case dojo.keys.CTRL:
5269 var copy = Boolean(this.source.copyState(true));
5270 if(this.copy != copy){
5271 this._setCopyStatus(copy);
5272 }
5273 break;
5274 case dojo.keys.ESCAPE:
5275 dojo.publish("/dnd/cancel");
5276 this.stopDrag();
5277 break;
5278 }
5279 }
5280 },
5281 onKeyUp: function(e){
5282 // summary:
5283 // event processor for onkeyup, watching for CTRL for copy/move status
5284 // e: Event
5285 // keyboard event
5286 if(this.avatar && e.keyCode == dojo.keys.CTRL){
5287 var copy = Boolean(this.source.copyState(false));
5288 if(this.copy != copy){
5289 this._setCopyStatus(copy);
5290 }
5291 }
5292 },
a089699c 5293
1354d172
AD
5294 // utilities
5295 _setCopyStatus: function(copy){
5296 // summary:
5297 // changes the copy status
5298 // copy: Boolean
5299 // the copy status
5300 this.copy = copy;
5301 this.source._markDndStatus(this.copy);
5302 this.updateAvatar();
5303 dojo.replaceClass(dojo.body(),
5304 "dojoDnd" + (this.copy ? "Copy" : "Move"),
5305 "dojoDnd" + (this.copy ? "Move" : "Copy"));
5306 }
5307});
a089699c 5308
1354d172
AD
5309// dojo.dnd._manager:
5310// The manager singleton variable. Can be overwritten if needed.
5311dojo.dnd._manager = null;
a089699c 5312
1354d172 5313Manager.manager = dojo.dnd.manager = function(){
a089699c 5314 // summary:
1354d172
AD
5315 // Returns the current DnD manager. Creates one if it is not created yet.
5316 if(!dojo.dnd._manager){
5317 dojo.dnd._manager = new dojo.dnd.Manager();
5318 }
5319 return dojo.dnd._manager; // Object
5320};
a089699c 5321
1354d172
AD
5322return Manager;
5323});
81bea17a 5324
1354d172
AD
5325},
5326'dijit/form/ToggleButton':function(){
5327define("dijit/form/ToggleButton", [
5328 "dojo/_base/declare", // declare
5329 "dojo/_base/kernel", // kernel.deprecated
5330 "./Button",
5331 "./_ToggleButtonMixin"
5332], function(declare, kernel, Button, _ToggleButtonMixin){
81bea17a 5333
1354d172
AD
5334/*=====
5335 var Button = dijit.form.Button;
5336 var _ToggleButtonMixin = dijit.form._ToggleButtonMixin;
5337=====*/
a089699c 5338
1354d172
AD
5339 // module:
5340 // dijit/form/ToggleButton
5341 // summary:
5342 // A templated button widget that can be in two states (checked or not).
a089699c 5343
a089699c 5344
1354d172 5345 return declare("dijit.form.ToggleButton", [Button, _ToggleButtonMixin], {
a089699c 5346 // summary:
1354d172
AD
5347 // A templated button widget that can be in two states (checked or not).
5348 // Can be base class for things like tabs or checkbox or radio buttons
a089699c 5349
1354d172 5350 baseClass: "dijitToggleButton",
a089699c 5351
1354d172
AD
5352 setChecked: function(/*Boolean*/ checked){
5353 // summary:
5354 // Deprecated. Use set('checked', true/false) instead.
5355 kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
5356 this.set('checked', checked);
a089699c 5357 }
1354d172
AD
5358 });
5359});
a089699c 5360
1354d172
AD
5361},
5362'dojo/date/stamp':function(){
5363define("dojo/date/stamp", ["../_base/kernel", "../_base/lang", "../_base/array"], function(dojo, lang, array) {
5364 // module:
5365 // dojo/date/stamp
5366 // summary:
5367 // TODOC
a089699c 5368
1354d172 5369lang.getObject("date.stamp", true, dojo);
a089699c 5370
1354d172 5371// Methods to convert dates to or from a wire (string) format using well-known conventions
a089699c 5372
1354d172
AD
5373dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
5374 // summary:
5375 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
5376 //
5377 // description:
5378 // Accepts a string formatted according to a profile of ISO8601 as defined by
5379 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
5380 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
5381 // The following combinations are valid:
5382 //
5383 // * dates only
5384 // | * yyyy
5385 // | * yyyy-MM
5386 // | * yyyy-MM-dd
5387 // * times only, with an optional time zone appended
5388 // | * THH:mm
5389 // | * THH:mm:ss
5390 // | * THH:mm:ss.SSS
5391 // * and "datetimes" which could be any combination of the above
5392 //
5393 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
5394 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
5395 // input may return null. Arguments which are out of bounds will be handled
5396 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
5397 // Only years between 100 and 9999 are supported.
5398 //
5399 // formattedString:
5400 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
5401 //
5402 // defaultTime:
5403 // Used for defaults for fields omitted in the formattedString.
5404 // Uses 1970-01-01T00:00:00.0Z by default.
a089699c 5405
1354d172
AD
5406 if(!dojo.date.stamp._isoRegExp){
5407 dojo.date.stamp._isoRegExp =
5408//TODO: could be more restrictive and check for 00-59, etc.
5409 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
5410 }
5411
5412 var match = dojo.date.stamp._isoRegExp.exec(formattedString),
5413 result = null;
5414
5415 if(match){
5416 match.shift();
5417 if(match[1]){match[1]--;} // Javascript Date months are 0-based
5418 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
5419
5420 if(defaultTime){
5421 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
5422 defaultTime = new Date(defaultTime);
5423 array.forEach(array.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
5424 return defaultTime["get" + prop]();
5425 }), function(value, index){
5426 match[index] = match[index] || value;
5427 });
a089699c 5428 }
1354d172
AD
5429 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
5430 if(match[0] < 100){
5431 result.setFullYear(match[0] || 1970);
a089699c
AD
5432 }
5433
1354d172
AD
5434 var offset = 0,
5435 zoneSign = match[7] && match[7].charAt(0);
5436 if(zoneSign != 'Z'){
5437 offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
5438 if(zoneSign != '-'){ offset *= -1; }
a089699c 5439 }
1354d172
AD
5440 if(zoneSign){
5441 offset -= result.getTimezoneOffset();
5442 }
5443 if(offset){
5444 result.setTime(result.getTime() + offset * 60000);
a089699c 5445 }
1354d172 5446 }
a089699c 5447
1354d172
AD
5448 return result; // Date or null
5449};
5450
5451/*=====
5452 dojo.date.stamp.__Options = function(){
5453 // selector: String
5454 // "date" or "time" for partial formatting of the Date object.
5455 // Both date and time will be formatted by default.
5456 // zulu: Boolean
5457 // if true, UTC/GMT is used for a timezone
5458 // milliseconds: Boolean
5459 // if true, output milliseconds
5460 this.selector = selector;
5461 this.zulu = zulu;
5462 this.milliseconds = milliseconds;
5463 }
5464=====*/
a089699c 5465
1354d172
AD
5466dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
5467 // summary:
5468 // Format a Date object as a string according a subset of the ISO-8601 standard
5469 //
5470 // description:
5471 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
5472 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
5473 // Does not check bounds. Only years between 100 and 9999 are supported.
5474 //
5475 // dateObject:
5476 // A Date object
a089699c 5477
1354d172
AD
5478 var _ = function(n){ return (n < 10) ? "0" + n : n; };
5479 options = options || {};
5480 var formattedDate = [],
5481 getter = options.zulu ? "getUTC" : "get",
5482 date = "";
5483 if(options.selector != "time"){
5484 var year = dateObject[getter+"FullYear"]();
5485 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
5486 }
5487 formattedDate.push(date);
5488 if(options.selector != "date"){
5489 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
5490 var millis = dateObject[getter+"Milliseconds"]();
5491 if(options.milliseconds){
5492 time += "."+ (millis < 100 ? "0" : "") + _(millis);
5493 }
5494 if(options.zulu){
5495 time += "Z";
5496 }else if(options.selector != "time"){
5497 var timezoneOffset = dateObject.getTimezoneOffset();
5498 var absOffset = Math.abs(timezoneOffset);
5499 time += (timezoneOffset > 0 ? "-" : "+") +
5500 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
a089699c 5501 }
1354d172
AD
5502 formattedDate.push(time);
5503 }
5504 return formattedDate.join('T'); // String
5505};
a089699c 5506
1354d172
AD
5507return dojo.date.stamp;
5508});
a089699c 5509
1354d172
AD
5510},
5511'dojo/Stateful':function(){
5512define("dojo/Stateful", ["./_base/kernel", "./_base/declare", "./_base/lang", "./_base/array"], function(dojo, declare, lang, array) {
5513 // module:
5514 // dojo/Stateful
5515 // summary:
5516 // TODOC
a089699c 5517
1354d172
AD
5518return dojo.declare("dojo.Stateful", null, {
5519 // summary:
5520 // Base class for objects that provide named properties with optional getter/setter
5521 // control and the ability to watch for property changes
5522 // example:
5523 // | var obj = new dojo.Stateful();
5524 // | obj.watch("foo", function(){
5525 // | console.log("foo changed to " + this.get("foo"));
5526 // | });
5527 // | obj.set("foo","bar");
5528 postscript: function(mixin){
5529 if(mixin){
5530 lang.mixin(this, mixin);
5531 }
a089699c
AD
5532 },
5533
1354d172 5534 get: function(/*String*/name){
a089699c 5535 // summary:
1354d172
AD
5536 // Get a property on a Stateful instance.
5537 // name:
5538 // The property to get.
5539 // returns:
5540 // The property value on this Stateful instance.
a089699c 5541 // description:
1354d172
AD
5542 // Get a named property on a Stateful object. The property may
5543 // potentially be retrieved via a getter method in subclasses. In the base class
5544 // this just retrieves the object's property.
5545 // For example:
5546 // | stateful = new dojo.Stateful({foo: 3});
5547 // | stateful.get("foo") // returns 3
5548 // | stateful.foo // returns 3
a089699c 5549
1354d172
AD
5550 return this[name]; //Any
5551 },
5552 set: function(/*String*/name, /*Object*/value){
5553 // summary:
5554 // Set a property on a Stateful instance
5555 // name:
5556 // The property to set.
5557 // value:
5558 // The value to set in the property.
5559 // returns:
5560 // The function returns this dojo.Stateful instance.
5561 // description:
5562 // Sets named properties on a stateful object and notifies any watchers of
5563 // the property. A programmatic setter may be defined in subclasses.
5564 // For example:
5565 // | stateful = new dojo.Stateful();
5566 // | stateful.watch(function(name, oldValue, value){
5567 // | // this will be called on the set below
5568 // | }
5569 // | stateful.set(foo, 5);
5570 //
5571 // set() may also be called with a hash of name/value pairs, ex:
5572 // | myObj.set({
5573 // | foo: "Howdy",
5574 // | bar: 3
5575 // | })
5576 // This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
5577 if(typeof name === "object"){
5578 for(var x in name){
5579 this.set(x, name[x]);
5580 }
5581 return this;
a089699c 5582 }
1354d172
AD
5583 var oldValue = this[name];
5584 this[name] = value;
5585 if(this._watchCallbacks){
5586 this._watchCallbacks(name, oldValue, value);
5587 }
5588 return this; //dojo.Stateful
5589 },
5590 watch: function(/*String?*/name, /*Function*/callback){
5591 // summary:
5592 // Watches a property for changes
5593 // name:
5594 // Indicates the property to watch. This is optional (the callback may be the
5595 // only parameter), and if omitted, all the properties will be watched
5596 // returns:
5597 // An object handle for the watch. The unwatch method of this object
5598 // can be used to discontinue watching this property:
5599 // | var watchHandle = obj.watch("foo", callback);
5600 // | watchHandle.unwatch(); // callback won't be called now
5601 // callback:
5602 // The function to execute when the property changes. This will be called after
5603 // the property has been changed. The callback will be called with the |this|
5604 // set to the instance, the first argument as the name of the property, the
5605 // second argument as the old value and the third argument as the new value.
a089699c 5606
1354d172
AD
5607 var callbacks = this._watchCallbacks;
5608 if(!callbacks){
5609 var self = this;
5610 callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
5611 var notify = function(propertyCallbacks){
5612 if(propertyCallbacks){
5613 propertyCallbacks = propertyCallbacks.slice();
5614 for(var i = 0, l = propertyCallbacks.length; i < l; i++){
5615 try{
5616 propertyCallbacks[i].call(self, name, oldValue, value);
5617 }catch(e){
5618 console.error(e);
5619 }
5620 }
5621 }
5622 };
5623 notify(callbacks['_' + name]);
5624 if(!ignoreCatchall){
5625 notify(callbacks["*"]); // the catch-all
5626 }
5627 }; // we use a function instead of an object so it will be ignored by JSON conversion
5628 }
5629 if(!callback && typeof name === "function"){
5630 callback = name;
5631 name = "*";
5632 }else{
5633 // prepend with dash to prevent name conflicts with function (like "name" property)
5634 name = '_' + name;
5635 }
5636 var propertyCallbacks = callbacks[name];
5637 if(typeof propertyCallbacks !== "object"){
5638 propertyCallbacks = callbacks[name] = [];
5639 }
5640 propertyCallbacks.push(callback);
5641 return {
5642 unwatch: function(){
5643 propertyCallbacks.splice(array.indexOf(propertyCallbacks, callback), 1);
5644 }
5645 }; //Object
a089699c 5646 }
a089699c 5647
1354d172 5648});
a089699c 5649
1354d172 5650});
a089699c 5651
1354d172
AD
5652},
5653'dijit/layout/AccordionContainer':function(){
5654require({cache:{
5655'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"}});
5656define("dijit/layout/AccordionContainer", [
5657 "require",
5658 "dojo/_base/array", // array.forEach array.map
5659 "dojo/_base/declare", // declare
5660 "dojo/_base/event", // event.stop
5661 "dojo/_base/fx", // fx.Animation
5662 "dojo/dom", // dom.setSelectable
5663 "dojo/dom-attr", // domAttr.attr
5664 "dojo/dom-class", // domClass.remove
5665 "dojo/dom-construct", // domConstruct.place
5666 "dojo/dom-geometry",
5667 "dojo/_base/kernel",
5668 "dojo/keys", // keys
5669 "dojo/_base/lang", // lang.getObject lang.hitch
5670 "dojo/_base/sniff", // has("ie")
5671 "dojo/topic", // publish
5672 "../focus", // focus.focus()
5673 "../_base/manager", // manager.defaultDuration
5674 "dojo/ready",
5675 "../_Widget",
5676 "../_Container",
5677 "../_TemplatedMixin",
5678 "../_CssStateMixin",
5679 "./StackContainer",
5680 "./ContentPane",
5681 "dojo/text!./templates/AccordionButton.html"
5682], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
5683 kernel, keys, lang, has, topic, focus, manager, ready,
5684 _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
a089699c 5685
1354d172
AD
5686/*=====
5687 var _Widget = dijit._Widget;
5688 var _Container = dijit._Container;
5689 var _TemplatedMixin = dijit._TemplatedMixin;
5690 var _CssStateMixin = dijit._CssStateMixin;
5691 var StackContainer = dijit.layout.StackContainer;
5692 var ContentPane = dijit.layout.ContentPane;
5693=====*/
a089699c 5694
1354d172
AD
5695 // module:
5696 // dijit/layout/AccordionContainer
5697 // summary:
5698 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
5699 // and switching between panes is visualized by sliding the other panes up/down.
a089699c
AD
5700
5701
1354d172 5702 // Design notes:
a089699c 5703 //
1354d172
AD
5704 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
5705 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
a089699c 5706 //
1354d172
AD
5707 // The resulting markup will look like:
5708 //
5709 // <div class=dijitAccordionContainer>
5710 // <div class=dijitAccordionInnerContainer> (one pane)
5711 // <div class=dijitAccordionTitle> (title bar) ... </div>
5712 // <div class=dijtAccordionChildWrapper> (content pane) </div>
5713 // </div>
5714 // </div>
5715 //
5716 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
5717 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
5718 // which on claro has a 1px border plus a 2px bottom margin.
5719 //
5720 // During animation there are two dijtAccordionChildWrapper's shown, so we need
5721 // to compensate for that.
a089699c 5722
a089699c 5723
1354d172
AD
5724 var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
5725 // summary:
5726 // The title bar to click to open up an accordion pane.
5727 // Internal widget used by AccordionContainer.
5728 // tags:
5729 // private
a089699c 5730
1354d172 5731 templateString: template,
a089699c 5732
1354d172
AD
5733 // label: String
5734 // Title of the pane
5735 label: "",
5736 _setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
a089699c 5737
1354d172
AD
5738 // title: String
5739 // Tooltip that appears on hover
5740 title: "",
5741 _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
a089699c 5742
1354d172
AD
5743 // iconClassAttr: String
5744 // CSS class for icon to left of label
5745 iconClassAttr: "",
5746 _setIconClassAttr: { node: "iconNode", type: "class" },
a089699c 5747
1354d172 5748 baseClass: "dijitAccordionTitle",
a089699c 5749
1354d172
AD
5750 getParent: function(){
5751 // summary:
5752 // Returns the AccordionContainer parent.
5753 // tags:
5754 // private
5755 return this.parent;
5756 },
a089699c 5757
1354d172
AD
5758 buildRendering: function(){
5759 this.inherited(arguments);
5760 var titleTextNodeId = this.id.replace(' ','_');
5761 domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
5762 this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
5763 dom.setSelectable(this.domNode, false);
5764 },
a089699c 5765
1354d172
AD
5766 getTitleHeight: function(){
5767 // summary:
5768 // Returns the height of the title dom node.
5769 return domGeometry.getMarginSize(this.domNode).h; // Integer
5770 },
a089699c 5771
1354d172
AD
5772 // TODO: maybe the parent should set these methods directly rather than forcing the code
5773 // into the button widget?
5774 _onTitleClick: function(){
5775 // summary:
5776 // Callback when someone clicks my title.
5777 var parent = this.getParent();
5778 parent.selectChild(this.contentWidget, true);
5779 focus.focus(this.focusNode);
5780 },
a089699c 5781
1354d172
AD
5782 _onTitleKeyPress: function(/*Event*/ evt){
5783 return this.getParent()._onKeyPress(evt, this.contentWidget);
5784 },
a089699c 5785
1354d172
AD
5786 _setSelectedAttr: function(/*Boolean*/ isSelected){
5787 this._set("selected", isSelected);
5788 this.focusNode.setAttribute("aria-expanded", isSelected);
5789 this.focusNode.setAttribute("aria-selected", isSelected);
5790 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
a089699c 5791 }
1354d172 5792 });
a089699c 5793
1354d172 5794 var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
a089699c 5795 // summary:
1354d172
AD
5796 // Internal widget placed as direct child of AccordionContainer.containerNode.
5797 // When other widgets are added as children to an AccordionContainer they are wrapped in
5798 // this widget.
a089699c 5799
1354d172
AD
5800/*=====
5801 // buttonWidget: Function || String
5802 // Class to use to instantiate title
5803 // (Wish we didn't have a separate widget for just the title but maintaining it
5804 // for backwards compatibility, is it worth it?)
5805 buttonWidget: null,
5806=====*/
a089699c 5807
1354d172
AD
5808/*=====
5809 // contentWidget: dijit._Widget
5810 // Pointer to the real child widget
5811 contentWidget: null,
5812=====*/
a089699c 5813
1354d172 5814 baseClass: "dijitAccordionInnerContainer",
a089699c 5815
1354d172
AD
5816 // tell nested layout widget that we will take care of sizing
5817 isLayoutContainer: true,
a089699c 5818
1354d172
AD
5819 buildRendering: function(){
5820 // Builds a template like:
5821 // <div class=dijitAccordionInnerContainer>
5822 // Button
5823 // <div class=dijitAccordionChildWrapper>
5824 // ContentPane
5825 // </div>
5826 // </div>
a089699c 5827
1354d172
AD
5828 // Create wrapper div, placed where the child is now
5829 this.domNode = domConstruct.place("<div class='" + this.baseClass +
5830 "' role='presentation'>", this.contentWidget.domNode, "after");
a089699c 5831
1354d172
AD
5832 // wrapper div's first child is the button widget (ie, the title bar)
5833 var child = this.contentWidget,
5834 cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
5835 this.button = child._buttonWidget = (new cls({
5836 contentWidget: child,
5837 label: child.title,
5838 title: child.tooltip,
5839 dir: child.dir,
5840 lang: child.lang,
5841 textDir: child.textDir,
5842 iconClass: child.iconClass,
5843 id: child.id + "_button",
5844 parent: this.parent
5845 })).placeAt(this.domNode);
a089699c 5846
1354d172
AD
5847 // and then the actual content widget (changing it from prior-sibling to last-child),
5848 // wrapped by a <div class=dijitAccordionChildWrapper>
5849 this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
5850 domConstruct.place(this.contentWidget.domNode, this.containerNode);
5851 },
a089699c 5852
1354d172
AD
5853 postCreate: function(){
5854 this.inherited(arguments);
a089699c 5855
1354d172
AD
5856 // Map changes in content widget's title etc. to changes in the button
5857 var button = this.button;
5858 this._contentWidgetWatches = [
5859 this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
5860 button.set("label", newValue);
5861 })),
5862 this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
5863 button.set("title", newValue);
5864 })),
5865 this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
5866 button.set("iconClass", newValue);
5867 }))
5868 ];
5869 },
a089699c 5870
1354d172
AD
5871 _setSelectedAttr: function(/*Boolean*/ isSelected){
5872 this._set("selected", isSelected);
5873 this.button.set("selected", isSelected);
5874 if(isSelected){
5875 var cw = this.contentWidget;
5876 if(cw.onSelected){ cw.onSelected(); }
5877 }
5878 },
a089699c 5879
1354d172
AD
5880 startup: function(){
5881 // Called by _Container.addChild()
5882 this.contentWidget.startup();
5883 },
a089699c 5884
1354d172
AD
5885 destroy: function(){
5886 this.button.destroyRecursive();
a089699c 5887
1354d172 5888 array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
a089699c 5889
1354d172
AD
5890 delete this.contentWidget._buttonWidget;
5891 delete this.contentWidget._wrapperWidget;
a089699c 5892
1354d172
AD
5893 this.inherited(arguments);
5894 },
a089699c 5895
1354d172
AD
5896 destroyDescendants: function(/*Boolean*/ preserveDom){
5897 // since getChildren isn't working for me, have to code this manually
5898 this.contentWidget.destroyRecursive(preserveDom);
a089699c 5899 }
1354d172 5900 });
a089699c 5901
1354d172 5902 var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
a089699c 5903 // summary:
1354d172
AD
5904 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
5905 // and switching between panes is visualized by sliding the other panes up/down.
5906 // example:
5907 // | <div data-dojo-type="dijit.layout.AccordionContainer">
5908 // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 1">
5909 // | </div>
5910 // | <div data-dojo-type="dijit.layout.ContentPane" title="pane 2">
5911 // | <p>This is some text</p>
5912 // | </div>
5913 // | </div>
a089699c 5914
1354d172
AD
5915 // duration: Integer
5916 // Amount of time (in ms) it takes to slide panes
5917 duration: manager.defaultDuration,
a089699c 5918
1354d172
AD
5919 // buttonWidget: [const] String
5920 // The name of the widget used to display the title of each pane
5921 buttonWidget: AccordionButton,
a089699c 5922
1354d172
AD
5923/*=====
5924 // _verticalSpace: Number
5925 // Pixels of space available for the open pane
5926 // (my content box size minus the cumulative size of all the title bars)
5927 _verticalSpace: 0,
5928=====*/
5929 baseClass: "dijitAccordionContainer",
a089699c 5930
1354d172
AD
5931 buildRendering: function(){
5932 this.inherited(arguments);
5933 this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
5934 this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
5935 },
a089699c 5936
1354d172
AD
5937 startup: function(){
5938 if(this._started){ return; }
5939 this.inherited(arguments);
5940 if(this.selectedChildWidget){
5941 var style = this.selectedChildWidget.containerNode.style;
5942 style.display = "";
5943 style.overflow = "auto";
5944 this.selectedChildWidget._wrapperWidget.set("selected", true);
a089699c 5945 }
1354d172 5946 },
a089699c 5947
1354d172
AD
5948 layout: function(){
5949 // Implement _LayoutWidget.layout() virtual method.
5950 // Set the height of the open pane based on what room remains.
a089699c 5951
1354d172 5952 var openPane = this.selectedChildWidget;
a089699c 5953
1354d172 5954 if(!openPane){ return;}
81bea17a 5955
1354d172
AD
5956 // space taken up by title, plus wrapper div (with border/margin) for open pane
5957 var wrapperDomNode = openPane._wrapperWidget.domNode,
5958 wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
5959 wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
5960 wrapperContainerNode = openPane._wrapperWidget.containerNode,
5961 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
5962 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
5963 mySize = this._contentBox;
81bea17a 5964
1354d172
AD
5965 // get cumulative height of all the unselected title bars
5966 var totalCollapsedHeight = 0;
5967 array.forEach(this.getChildren(), function(child){
5968 if(child != openPane){
5969 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
5970 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
5971 // margin below the bottom pane (even though we don't want one).
5972 totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
5973 }
5974 });
5975 this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
5976 - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
5977 - openPane._buttonWidget.getTitleHeight();
81bea17a 5978
1354d172
AD
5979 // Memo size to make displayed child
5980 this._containerContentBox = {
5981 h: this._verticalSpace,
5982 w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
5983 - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
5984 };
81bea17a 5985
1354d172
AD
5986 if(openPane){
5987 openPane.resize(this._containerContentBox);
5988 }
5989 },
81bea17a 5990
1354d172
AD
5991 _setupChild: function(child){
5992 // Overrides _LayoutWidget._setupChild().
5993 // Put wrapper widget around the child widget, showing title
81bea17a 5994
1354d172
AD
5995 child._wrapperWidget = AccordionInnerContainer({
5996 contentWidget: child,
5997 buttonWidget: this.buttonWidget,
5998 id: child.id + "_wrapper",
5999 dir: child.dir,
6000 lang: child.lang,
6001 textDir: child.textDir,
6002 parent: this
6003 });
81bea17a 6004
1354d172
AD
6005 this.inherited(arguments);
6006 },
81bea17a 6007
1354d172
AD
6008 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
6009 if(this._started){
6010 // Adding a child to a started Accordion is complicated because children have
6011 // wrapper widgets. Default code path (calling this.inherited()) would add
6012 // the new child inside another child's wrapper.
a089699c 6013
1354d172
AD
6014 // First add in child as a direct child of this AccordionContainer
6015 var refNode = this.containerNode;
6016 if(insertIndex && typeof insertIndex == "number"){
6017 var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
6018 if(children && children.length >= insertIndex){
6019 refNode = children[insertIndex-1].domNode;
6020 insertIndex = "after";
6021 }
6022 }
6023 domConstruct.place(child.domNode, refNode, insertIndex);
a089699c 6024
1354d172
AD
6025 if(!child._started){
6026 child.startup();
6027 }
a089699c 6028
1354d172
AD
6029 // Then stick the wrapper widget around the child widget
6030 this._setupChild(child);
a089699c 6031
1354d172
AD
6032 // Code below copied from StackContainer
6033 topic.publish(this.id+"-addChild", child, insertIndex); // publish
6034 this.layout();
6035 if(!this.selectedChildWidget){
6036 this.selectChild(child);
6037 }
6038 }else{
6039 // We haven't been started yet so just add in the child widget directly,
6040 // and the wrapper will be created on startup()
6041 this.inherited(arguments);
6042 }
6043 },
a089699c 6044
1354d172
AD
6045 removeChild: function(child){
6046 // Overrides _LayoutWidget.removeChild().
81bea17a 6047
1354d172
AD
6048 // Destroy wrapper widget first, before StackContainer.getChildren() call.
6049 // Replace wrapper widget with true child widget (ContentPane etc.).
6050 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
6051 if(child._wrapperWidget){
6052 domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
6053 child._wrapperWidget.destroy();
6054 delete child._wrapperWidget;
6055 }
a089699c 6056
1354d172 6057 domClass.remove(child.domNode, "dijitHidden");
a089699c 6058
1354d172
AD
6059 this.inherited(arguments);
6060 },
a089699c 6061
1354d172
AD
6062 getChildren: function(){
6063 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
6064 return array.map(this.inherited(arguments), function(child){
6065 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
6066 }, this);
6067 },
a089699c 6068
1354d172
AD
6069 destroy: function(){
6070 if(this._animation){
6071 this._animation.stop();
6072 }
6073 array.forEach(this.getChildren(), function(child){
6074 // If AccordionContainer has been started, then each child has a wrapper widget which
6075 // also needs to be destroyed.
6076 if(child._wrapperWidget){
6077 child._wrapperWidget.destroy();
6078 }else{
6079 child.destroyRecursive();
6080 }
6081 });
6082 this.inherited(arguments);
6083 },
a089699c 6084
1354d172
AD
6085 _showChild: function(child){
6086 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6087 child._wrapperWidget.containerNode.style.display="block";
6088 return this.inherited(arguments);
6089 },
a089699c 6090
1354d172
AD
6091 _hideChild: function(child){
6092 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
6093 child._wrapperWidget.containerNode.style.display="none";
6094 this.inherited(arguments);
6095 },
a089699c 6096
1354d172
AD
6097 _transition: function(/*dijit._Widget?*/ newWidget, /*dijit._Widget?*/ oldWidget, /*Boolean*/ animate){
6098 // Overrides StackContainer._transition() to provide sliding of title bars etc.
a089699c 6099
1354d172
AD
6100 if(has("ie") < 8){
6101 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
6102 animate = false;
6103 }
a089699c 6104
1354d172
AD
6105 if(this._animation){
6106 // there's an in-progress animation. speedily end it so we can do the newly requested one
6107 this._animation.stop(true);
6108 delete this._animation;
6109 }
a089699c 6110
1354d172 6111 var self = this;
a089699c 6112
1354d172
AD
6113 if(newWidget){
6114 newWidget._wrapperWidget.set("selected", true);
a089699c 6115
1354d172 6116 var d = this._showChild(newWidget); // prepare widget to be slid in
a089699c 6117
1354d172
AD
6118 // Size the new widget, in case this is the first time it's being shown,
6119 // or I have been resized since the last time it was shown.
6120 // Note that page must be visible for resizing to work.
6121 if(this.doLayout && newWidget.resize){
6122 newWidget.resize(this._containerContentBox);
6123 }
6124 }
a089699c 6125
1354d172
AD
6126 if(oldWidget){
6127 oldWidget._wrapperWidget.set("selected", false);
6128 if(!animate){
6129 this._hideChild(oldWidget);
6130 }
6131 }
a089699c 6132
1354d172
AD
6133 if(animate){
6134 var newContents = newWidget._wrapperWidget.containerNode,
6135 oldContents = oldWidget._wrapperWidget.containerNode;
a089699c 6136
1354d172
AD
6137 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
6138 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
6139 // Have to compensate for that by immediately shrinking the pane being closed.
6140 var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
6141 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
6142 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
6143 animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
a089699c 6144
1354d172 6145 oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
81bea17a 6146
1354d172
AD
6147 this._animation = new fx.Animation({
6148 node: newContents,
6149 duration: this.duration,
6150 curve: [1, this._verticalSpace - animationHeightOverhead - 1],
6151 onAnimate: function(value){
6152 value = Math.floor(value); // avoid fractional values
6153 newContents.style.height = value + "px";
6154 oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
6155 },
6156 onEnd: function(){
6157 delete self._animation;
6158 newContents.style.height = "auto";
6159 oldWidget._wrapperWidget.containerNode.style.display = "none";
6160 oldContents.style.height = "auto";
6161 self._hideChild(oldWidget);
6162 }
6163 });
6164 this._animation.onStop = this._animation.onEnd;
6165 this._animation.play();
6166 }
81bea17a 6167
1354d172
AD
6168 return d; // If child has an href, promise that fires when the widget has finished loading
6169 },
81bea17a 6170
1354d172
AD
6171 // note: we are treating the container as controller here
6172 _onKeyPress: function(/*Event*/ e, /*dijit._Widget*/ fromTitle){
6173 // summary:
6174 // Handle keypress events
6175 // description:
6176 // This is called from a handler on AccordionContainer.domNode
6177 // (setup in StackContainer), and is also called directly from
6178 // the click handler for accordion labels
6179 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
6180 return;
a089699c 6181 }
1354d172
AD
6182 var c = e.charOrCode;
6183 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
6184 (e.ctrlKey && c == keys.PAGE_UP)){
6185 this._adjacent(false)._buttonWidget._onTitleClick();
6186 event.stop(e);
6187 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
6188 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
6189 this._adjacent(true)._buttonWidget._onTitleClick();
6190 event.stop(e);
6191 }
6192 }
6193 });
a089699c 6194
1354d172
AD
6195 // Back compat w/1.6, remove for 2.0
6196 if(!kernel.isAsync){
6197 ready(0, function(){
6198 var requires = ["dijit/layout/AccordionPane"];
6199 require(requires); // use indirection so modules not rolled into a build
6200 });
6201 }
a089699c 6202
1354d172
AD
6203 // For monkey patching
6204 AccordionContainer._InnerContainer = AccordionInnerContainer;
6205 AccordionContainer._Button = AccordionButton;
6206
6207 return AccordionContainer;
6208});
6209
6210},
6211'dijit/form/_AutoCompleterMixin':function(){
6212define("dijit/form/_AutoCompleterMixin", [
6213 "dojo/_base/connect", // keys keys.SHIFT
6214 "dojo/data/util/filter", // patternToRegExp
6215 "dojo/_base/declare", // declare
6216 "dojo/_base/Deferred", // Deferred.when
6217 "dojo/dom-attr", // domAttr.get
6218 "dojo/_base/event", // event.stop
6219 "dojo/keys",
6220 "dojo/_base/lang", // lang.clone lang.hitch
6221 "dojo/query", // query
6222 "dojo/regexp", // regexp.escapeString
6223 "dojo/_base/sniff", // has("ie")
6224 "dojo/string", // string.substitute
6225 "dojo/_base/window", // win.doc.selection.createRange
6226 "./DataList",
6227 "../registry", // registry.byId
6228 "./_TextBoxMixin" // defines _TextBoxMixin.selectInputText
6229], function(connect, filter, declare, Deferred, domAttr, event, keys, lang, query, regexp, has, string, win,
6230 DataList, registry, _TextBoxMixin){
6231
6232 // module:
6233 // dijit/form/_AutoCompleterMixin
6234 // summary:
6235 // A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
6236
6237
6238 return declare("dijit.form._AutoCompleterMixin", null, {
6239 // summary:
6240 // A mixin that implements the base functionality for `dijit.form.ComboBox`/`dijit.form.FilteringSelect`
6241 // description:
6242 // All widgets that mix in dijit.form._AutoCompleterMixin must extend `dijit.form._FormValueWidget`.
6243 // tags:
6244 // protected
6245
6246 // item: Object
6247 // This is the item returned by the dojo.data.store implementation that
6248 // provides the data for this ComboBox, it's the currently selected item.
6249 item: null,
6250
6251 // pageSize: Integer
6252 // Argument to data provider.
6253 // Specifies number of search results per page (before hitting "next" button)
6254 pageSize: Infinity,
6255
6256 // store: [const] dojo.store.api.Store
6257 // Reference to data provider object used by this ComboBox
6258 store: null,
6259
6260 // fetchProperties: Object
6261 // Mixin to the store's fetch.
6262 // For example, to set the sort order of the ComboBox menu, pass:
6263 // | { sort: [{attribute:"name",descending: true}] }
6264 // To override the default queryOptions so that deep=false, do:
6265 // | { queryOptions: {ignoreCase: true, deep: false} }
6266 fetchProperties:{},
6267
6268 // query: Object
6269 // A query that can be passed to 'store' to initially filter the items,
6270 // before doing further filtering based on `searchAttr` and the key.
6271 // Any reference to the `searchAttr` is ignored.
6272 query: {},
6273
6274 // autoComplete: Boolean
6275 // If user types in a partial string, and then tab out of the `<input>` box,
6276 // automatically copy the first entry displayed in the drop down list to
6277 // the `<input>` field
6278 autoComplete: true,
6279
6280 // highlightMatch: String
6281 // One of: "first", "all" or "none".
6282 //
6283 // If the ComboBox/FilteringSelect opens with the search results and the searched
6284 // string can be found, it will be highlighted. If set to "all"
6285 // then will probably want to change `queryExpr` parameter to '*${0}*'
6286 //
6287 // Highlighting is only performed when `labelType` is "text", so as to not
6288 // interfere with any HTML markup an HTML label might contain.
6289 highlightMatch: "first",
6290
6291 // searchDelay: Integer
6292 // Delay in milliseconds between when user types something and we start
6293 // searching based on that value
6294 searchDelay: 100,
6295
6296 // searchAttr: String
6297 // Search for items in the data store where this attribute (in the item)
6298 // matches what the user typed
6299 searchAttr: "name",
6300
6301 // labelAttr: String?
6302 // The entries in the drop down list come from this attribute in the
6303 // dojo.data items.
6304 // If not specified, the searchAttr attribute is used instead.
6305 labelAttr: "",
6306
6307 // labelType: String
6308 // Specifies how to interpret the labelAttr in the data store items.
6309 // Can be "html" or "text".
6310 labelType: "text",
6311
6312 // queryExpr: String
6313 // This specifies what query ComboBox/FilteringSelect sends to the data store,
6314 // based on what the user has typed. Changing this expression will modify
6315 // whether the drop down shows only exact matches, a "starting with" match,
6316 // etc. Use it in conjunction with highlightMatch.
6317 // dojo.data query expression pattern.
6318 // `${0}` will be substituted for the user text.
6319 // `*` is used for wildcards.
6320 // `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
6321 queryExpr: "${0}*",
6322
6323 // ignoreCase: Boolean
6324 // Set true if the ComboBox/FilteringSelect should ignore case when matching possible items
6325 ignoreCase: true,
6326
6327 // Flags to _HasDropDown to limit height of drop down to make it fit in viewport
6328 maxHeight: -1,
6329
6330 // For backwards compatibility let onClick events propagate, even clicks on the down arrow button
6331 _stopClickEvents: false,
6332
6333 _getCaretPos: function(/*DomNode*/ element){
6334 // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
6335 var pos = 0;
6336 if(typeof(element.selectionStart) == "number"){
6337 // FIXME: this is totally borked on Moz < 1.3. Any recourse?
6338 pos = element.selectionStart;
6339 }else if(has("ie")){
6340 // in the case of a mouse click in a popup being handled,
6341 // then the win.doc.selection is not the textarea, but the popup
6342 // var r = win.doc.selection.createRange();
6343 // hack to get IE 6 to play nice. What a POS browser.
6344 var tr = win.doc.selection.createRange().duplicate();
6345 var ntr = element.createTextRange();
6346 tr.move("character",0);
6347 ntr.move("character",0);
6348 try{
6349 // If control doesn't have focus, you get an exception.
6350 // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
6351 // There appears to be no workaround for this - googled for quite a while.
6352 ntr.setEndPoint("EndToEnd", tr);
6353 pos = String(ntr.text).replace(/\r/g,"").length;
6354 }catch(e){
6355 // If focus has shifted, 0 is fine for caret pos.
6356 }
a089699c 6357 }
1354d172 6358 return pos;
a089699c 6359 },
1354d172
AD
6360
6361 _setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
6362 location = parseInt(location);
6363 _TextBoxMixin.selectInputText(element, location, location);
a089699c 6364 },
1354d172
AD
6365
6366 _setDisabledAttr: function(/*Boolean*/ value){
6367 // Additional code to set disabled state of ComboBox node.
6368 // Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
6369 this.inherited(arguments);
6370 this.domNode.setAttribute("aria-disabled", value);
a089699c 6371 },
1354d172
AD
6372
6373 _abortQuery: function(){
6374 // stop in-progress query
6375 if(this.searchTimer){
6376 clearTimeout(this.searchTimer);
6377 this.searchTimer = null;
a089699c 6378 }
1354d172
AD
6379 if(this._fetchHandle){
6380 if(this._fetchHandle.cancel){
6381 this._cancelingQuery = true;
6382 this._fetchHandle.cancel();
6383 this._cancelingQuery = false;
a089699c 6384 }
1354d172 6385 this._fetchHandle = null;
a089699c 6386 }
a089699c 6387 },
1354d172
AD
6388
6389 _onInput: function(/*Event*/ evt){
6390 // summary:
6391 // Handles paste events
6392 this.inherited(arguments);
6393 if(evt.charOrCode == 229){ // IME or cut/paste event
6394 this._onKey(evt);
6395 }
a089699c 6396 },
a089699c 6397
1354d172
AD
6398 _onKey: function(/*Event*/ evt){
6399 // summary:
6400 // Handles keyboard events
a089699c 6401
1354d172
AD
6402 if(this.disabled || this.readOnly){ return; }
6403 var key = evt.charOrCode;
a089699c 6404
1354d172
AD
6405 // except for cutting/pasting case - ctrl + x/v
6406 if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
6407 return; // throw out weird key combinations and spurious events
a089699c 6408 }
a089699c 6409
1354d172
AD
6410 var doSearch = false;
6411 var pw = this.dropDown;
6412 var highlighted = null;
6413 this._prev_key_backspace = false;
6414 this._abortQuery();
a089699c 6415
1354d172
AD
6416 // _HasDropDown will do some of the work:
6417 // 1. when drop down is not yet shown:
6418 // - if user presses the down arrow key, call loadDropDown()
6419 // 2. when drop down is already displayed:
6420 // - on ESC key, call closeDropDown()
6421 // - otherwise, call dropDown.handleKey() to process the keystroke
6422 this.inherited(arguments);
a089699c 6423
1354d172
AD
6424 if(this._opened){
6425 highlighted = pw.getHighlightedOption();
6426 }
6427 switch(key){
6428 case keys.PAGE_DOWN:
6429 case keys.DOWN_ARROW:
6430 case keys.PAGE_UP:
6431 case keys.UP_ARROW:
6432 // Keystroke caused ComboBox_menu to move to a different item.
6433 // Copy new item to <input> box.
6434 if(this._opened){
6435 this._announceOption(highlighted);
6436 }
6437 event.stop(evt);
6438 break;
6439
6440 case keys.ENTER:
6441 // prevent submitting form if user presses enter. Also
6442 // prevent accepting the value if either Next or Previous
6443 // are selected
6444 if(highlighted){
6445 // only stop event on prev/next
6446 if(highlighted == pw.nextButton){
6447 this._nextSearch(1);
6448 event.stop(evt);
6449 break;
6450 }else if(highlighted == pw.previousButton){
6451 this._nextSearch(-1);
6452 event.stop(evt);
6453 break;
a089699c 6454 }
1354d172
AD
6455 }else{
6456 // Update 'value' (ex: KY) according to currently displayed text
6457 this._setBlurValue(); // set value if needed
6458 this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
a089699c 6459 }
1354d172
AD
6460 // default case:
6461 // if enter pressed while drop down is open, or for FilteringSelect,
6462 // if we are in the middle of a query to convert a directly typed in value to an item,
6463 // prevent submit
6464 if(this._opened || this._fetchHandle){
6465 event.stop(evt);
6466 }
6467 // fall through
a089699c 6468
1354d172
AD
6469 case keys.TAB:
6470 var newvalue = this.get('displayedValue');
6471 // if the user had More Choices selected fall into the
6472 // _onBlur handler
6473 if(pw && (
6474 newvalue == pw._messages["previousMessage"] ||
6475 newvalue == pw._messages["nextMessage"])
6476 ){
6477 break;
6478 }
6479 if(highlighted){
6480 this._selectOption(highlighted);
6481 }
6482 // fall through
a089699c 6483
1354d172
AD
6484 case keys.ESCAPE:
6485 if(this._opened){
6486 this._lastQuery = null; // in case results come back later
6487 this.closeDropDown();
6488 }
6489 break;
a089699c 6490
1354d172
AD
6491 case ' ':
6492 if(highlighted){
6493 // user is effectively clicking a choice in the drop down menu
6494 event.stop(evt);
6495 this._selectOption(highlighted);
6496 this.closeDropDown();
6497 }else{
6498 // user typed a space into the input box, treat as normal character
6499 doSearch = true;
6500 }
6501 break;
a089699c 6502
1354d172
AD
6503 case keys.DELETE:
6504 case keys.BACKSPACE:
6505 this._prev_key_backspace = true;
6506 doSearch = true;
6507 break;
a089699c 6508
1354d172
AD
6509 default:
6510 // Non char keys (F1-F12 etc..) shouldn't open list.
6511 // Ascii characters and IME input (Chinese, Japanese etc.) should.
6512 //IME input produces keycode == 229.
6513 doSearch = typeof key == 'string' || key == 229;
6514 }
6515 if(doSearch){
6516 // need to wait a tad before start search so that the event
6517 // bubbles through DOM and we have value visible
6518 this.item = undefined; // undefined means item needs to be set
6519 this.searchTimer = setTimeout(lang.hitch(this, "_startSearchFromInput"),1);
6520 }
6521 },
a089699c 6522
1354d172
AD
6523 _autoCompleteText: function(/*String*/ text){
6524 // summary:
6525 // Fill in the textbox with the first item from the drop down
6526 // list, and highlight the characters that were
6527 // auto-completed. For example, if user typed "CA" and the
6528 // drop down list appeared, the textbox would be changed to
6529 // "California" and "ifornia" would be highlighted.
a089699c 6530
1354d172 6531 var fn = this.focusNode;
a089699c 6532
1354d172
AD
6533 // IE7: clear selection so next highlight works all the time
6534 _TextBoxMixin.selectInputText(fn, fn.value.length);
6535 // does text autoComplete the value in the textbox?
6536 var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
6537 if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
6538 var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length;
6539 // only try to extend if we added the last character at the end of the input
6540 if((cpos+1) > fn.value.length){
6541 // only add to input node as we would overwrite Capitalisation of chars
6542 // actually, that is ok
6543 fn.value = text;//.substr(cpos);
6544 // visually highlight the autocompleted characters
6545 _TextBoxMixin.selectInputText(fn, cpos);
6546 }
6547 }else{
6548 // text does not autoComplete; replace the whole value and highlight
6549 fn.value = text;
6550 _TextBoxMixin.selectInputText(fn);
6551 }
6552 },
a089699c 6553
1354d172
AD
6554 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
6555 // summary:
6556 // Callback when a search completes.
6557 // description:
6558 // 1. generates drop-down list and calls _showResultList() to display it
6559 // 2. if this result list is from user pressing "more choices"/"previous choices"
6560 // then tell screen reader to announce new option
6561 this._fetchHandle = null;
6562 if( this.disabled ||
6563 this.readOnly ||
6564 (query[this.searchAttr] !== this._lastQuery) // TODO: better way to avoid getting unwanted notify
6565 ){
6566 return;
6567 }
6568 var wasSelected = this.dropDown.getHighlightedOption();
6569 this.dropDown.clearResultList();
6570 if(!results.length && options.start == 0){ // if no results and not just the previous choices button
6571 this.closeDropDown();
6572 return;
a089699c 6573 }
a089699c 6574
1354d172
AD
6575 // Fill in the textbox with the first item from the drop down list,
6576 // and highlight the characters that were auto-completed. For
6577 // example, if user typed "CA" and the drop down list appeared, the
6578 // textbox would be changed to "California" and "ifornia" would be
6579 // highlighted.
a089699c 6580
1354d172
AD
6581 var nodes = this.dropDown.createOptions(
6582 results,
6583 options,
6584 lang.hitch(this, "_getMenuLabelFromItem")
6585 );
a089699c 6586
1354d172
AD
6587 // show our list (only if we have content, else nothing)
6588 this._showResultList();
a089699c 6589
1354d172
AD
6590 // #4091:
6591 // tell the screen reader that the paging callback finished by
6592 // shouting the next choice
6593 if(options.direction){
6594 if(1 == options.direction){
6595 this.dropDown.highlightFirstOption();
6596 }else if(-1 == options.direction){
6597 this.dropDown.highlightLastOption();
6598 }
6599 if(wasSelected){
6600 this._announceOption(this.dropDown.getHighlightedOption());
6601 }
6602 }else if(this.autoComplete && !this._prev_key_backspace
6603 // when the user clicks the arrow button to show the full list,
6604 // startSearch looks for "*".
6605 // it does not make sense to autocomplete
6606 // if they are just previewing the options available.
6607 && !/^[*]+$/.test(query[this.searchAttr].toString())){
6608 this._announceOption(nodes[1]); // 1st real item
6609 }
6610 },
a089699c 6611
1354d172
AD
6612 _showResultList: function(){
6613 // summary:
6614 // Display the drop down if not already displayed, or if it is displayed, then
6615 // reposition it if necessary (reposition may be necessary if drop down's height changed).
6616 this.closeDropDown(true);
6617 this.openDropDown();
6618 this.domNode.setAttribute("aria-expanded", "true");
6619 },
a089699c 6620
1354d172
AD
6621 loadDropDown: function(/*Function*/ /*===== callback =====*/){
6622 // Overrides _HasDropDown.loadDropDown().
6623 // This is called when user has pressed button icon or pressed the down arrow key
6624 // to open the drop down.
81bea17a 6625
1354d172
AD
6626 this._startSearchAll();
6627 },
a089699c 6628
1354d172
AD
6629 isLoaded: function(){
6630 // signal to _HasDropDown that it needs to call loadDropDown() to load the
6631 // drop down asynchronously before displaying it
6632 return false;
6633 },
a089699c 6634
1354d172
AD
6635 closeDropDown: function(){
6636 // Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
6637 // This method is the callback when the user types ESC or clicking
6638 // the button icon while the drop down is open. It's also called by other code.
6639 this._abortQuery();
6640 if(this._opened){
6641 this.inherited(arguments);
6642 this.domNode.setAttribute("aria-expanded", "false");
6643 this.focusNode.removeAttribute("aria-activedescendant");
6644 }
6645 },
a089699c 6646
1354d172
AD
6647 _setBlurValue: function(){
6648 // if the user clicks away from the textbox OR tabs away, set the
6649 // value to the textbox value
6650 // #4617:
6651 // if value is now more choices or previous choices, revert
6652 // the value
6653 var newvalue = this.get('displayedValue');
6654 var pw = this.dropDown;
6655 if(pw && (
6656 newvalue == pw._messages["previousMessage"] ||
6657 newvalue == pw._messages["nextMessage"]
6658 )
6659 ){
6660 this._setValueAttr(this._lastValueReported, true);
6661 }else if(typeof this.item == "undefined"){
6662 // Update 'value' (ex: KY) according to currently displayed text
6663 this.item = null;
6664 this.set('displayedValue', newvalue);
6665 }else{
6666 if(this.value != this._lastValueReported){
6667 this._handleOnChange(this.value, true);
6668 }
6669 this._refreshState();
6670 }
6671 },
a089699c 6672
1354d172
AD
6673 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
6674 // summary:
6675 // Set the displayed valued in the input box, and the hidden value
6676 // that gets submitted, based on a dojo.data store item.
6677 // description:
6678 // Users shouldn't call this function; they should be calling
6679 // set('item', value)
6680 // tags:
6681 // private
6682 var value = '';
6683 if(item){
6684 if(!displayedValue){
6685 displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
6686 this.store.getValue(item, this.searchAttr) : item[this.searchAttr];
6687 }
6688 value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue;
6689 }
6690 this.set('value', value, priorityChange, displayedValue, item);
6691 },
a089699c 6692
1354d172
AD
6693 _announceOption: function(/*Node*/ node){
6694 // summary:
6695 // a11y code that puts the highlighted option in the textbox.
6696 // This way screen readers will know what is happening in the
6697 // menu.
a089699c 6698
1354d172
AD
6699 if(!node){
6700 return;
6701 }
6702 // pull the text value from the item attached to the DOM node
6703 var newValue;
6704 if(node == this.dropDown.nextButton ||
6705 node == this.dropDown.previousButton){
6706 newValue = node.innerHTML;
6707 this.item = undefined;
6708 this.value = '';
6709 }else{
6710 newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
6711 this.store.getValue(node.item, this.searchAttr) : node.item[this.searchAttr]).toString();
6712 this.set('item', node.item, false, newValue);
6713 }
6714 // get the text that the user manually entered (cut off autocompleted text)
6715 this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
6716 // set up ARIA activedescendant
6717 this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
6718 // autocomplete the rest of the option to announce change
6719 this._autoCompleteText(newValue);
6720 },
a089699c 6721
1354d172
AD
6722 _selectOption: function(/*DomNode*/ target){
6723 // summary:
6724 // Menu callback function, called when an item in the menu is selected.
6725 this.closeDropDown();
6726 if(target){
6727 this._announceOption(target);
6728 }
6729 this._setCaretPos(this.focusNode, this.focusNode.value.length);
6730 this._handleOnChange(this.value, true);
6731 },
a089699c 6732
1354d172
AD
6733 _startSearchAll: function(){
6734 this._startSearch('');
6735 },
81bea17a 6736
1354d172
AD
6737 _startSearchFromInput: function(){
6738 this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
6739 },
a089699c 6740
1354d172
AD
6741 _getQueryString: function(/*String*/ text){
6742 return string.substitute(this.queryExpr, [text]);
6743 },
a089699c 6744
1354d172
AD
6745 _startSearch: function(/*String*/ key){
6746 // summary:
6747 // Starts a search for elements matching key (key=="" means to return all items),
6748 // and calls _openResultList() when the search completes, to display the results.
6749 if(!this.dropDown){
6750 var popupId = this.id + "_popup",
6751 dropDownConstructor = lang.isString(this.dropDownClass) ?
6752 lang.getObject(this.dropDownClass, false) : this.dropDownClass;
6753 this.dropDown = new dropDownConstructor({
6754 onChange: lang.hitch(this, this._selectOption),
6755 id: popupId,
6756 dir: this.dir,
6757 textDir: this.textDir
6758 });
6759 this.focusNode.removeAttribute("aria-activedescendant");
6760 this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox
a089699c 6761 }
1354d172 6762 this._lastInput = key; // Store exactly what was entered by the user.
a089699c 6763
1354d172
AD
6764 // Setup parameters to be passed to store.query().
6765 // Create a new query to prevent accidentally querying for a hidden
6766 // value from FilteringSelect's keyField
6767 var query = lang.clone(this.query); // #5970
6768 var options = {
6769 start: 0,
6770 count: this.pageSize,
6771 queryOptions: { // remove for 2.0
6772 ignoreCase: this.ignoreCase,
6773 deep: true
6774 }
6775 };
6776 lang.mixin(options, this.fetchProperties);
a089699c 6777
1354d172
AD
6778 // Generate query
6779 var qs = this._getQueryString(key), q;
6780 if(this.store._oldAPI){
6781 // remove this branch for 2.0
6782 q = qs;
6783 }else{
6784 // Query on searchAttr is a regex for benefit of dojo.store.Memory,
6785 // but with a toString() method to help dojo.store.JsonRest.
6786 // Search string like "Co*" converted to regex like /^Co.*$/i.
6787 q = filter.patternToRegExp(qs, this.ignoreCase);
6788 q.toString = function(){ return qs; };
6789 }
6790 this._lastQuery = query[this.searchAttr] = q;
6791
6792 // Function to run the query, wait for the results, and then call _openResultList()
6793 var _this = this,
6794 startQuery = function(){
6795 var resPromise = _this._fetchHandle = _this.store.query(query, options);
6796 Deferred.when(resPromise, function(res){
6797 _this._fetchHandle = null;
6798 res.total = resPromise.total;
6799 _this._openResultList(res, query, options);
6800 }, function(err){
6801 _this._fetchHandle = null;
6802 if(!_this._cancelingQuery){ // don't treat canceled query as an error
6803 console.error(_this.declaredClass + ' ' + err.toString());
6804 _this.closeDropDown();
6805 }
6806 });
6807 };
a089699c 6808
1354d172
AD
6809 // #5970: set _lastQuery, *then* start the timeout
6810 // otherwise, if the user types and the last query returns before the timeout,
6811 // _lastQuery won't be set and their input gets rewritten
a089699c 6812
1354d172
AD
6813 this.searchTimer = setTimeout(lang.hitch(this, function(query, _this){
6814 this.searchTimer = null;
a089699c 6815
1354d172 6816 startQuery();
a089699c 6817
1354d172
AD
6818 // Setup method to handle clicking next/previous buttons to page through results
6819 this._nextSearch = this.dropDown.onPage = function(direction){
6820 options.start += options.count * direction;
6821 // tell callback the direction of the paging so the screen
6822 // reader knows which menu option to shout
6823 options.direction = direction;
6824 startQuery();
6825 _this.focus();
6826 };
6827 }, query, this), this.searchDelay);
6828 },
81bea17a 6829
1354d172
AD
6830 _getValueField: function(){
6831 // summary:
6832 // Helper for postMixInProperties() to set this.value based on data inlined into the markup.
6833 // Returns the attribute name in the item (in dijit.form._ComboBoxDataStore) to use as the value.
6834 return this.searchAttr;
6835 },
a089699c 6836
1354d172 6837 //////////// INITIALIZATION METHODS ///////////////////////////////////////
a089699c 6838
1354d172
AD
6839 constructor: function(){
6840 this.query={};
6841 this.fetchProperties={};
6842 },
a089699c 6843
1354d172
AD
6844 postMixInProperties: function(){
6845 if(!this.store){
6846 var srcNodeRef = this.srcNodeRef;
6847 var list = this.list;
6848 if(list){
6849 this.store = registry.byId(list);
6850 }else{
6851 // if user didn't specify store, then assume there are option tags
6852 this.store = new DataList({}, srcNodeRef);
6853 }
a089699c 6854
1354d172
AD
6855 // if there is no value set and there is an option list, set
6856 // the value to the first value to be consistent with native Select
6857 // Firefox and Safari set value
6858 // IE6 and Opera set selectedIndex, which is automatically set
6859 // by the selected attribute of an option tag
6860 // IE6 does not set value, Opera sets value = selectedIndex
6861 if(!("value" in this.params)){
6862 var item = (this.item = this.store.fetchSelectedItem());
6863 if(item){
6864 var valueField = this._getValueField();
6865 // remove getValue() for 2.0 (old dojo.data API)
6866 this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField];
6867 }
6868 }
6869 }
a089699c 6870
1354d172
AD
6871 this.inherited(arguments);
6872 },
a089699c 6873
1354d172
AD
6874 postCreate: function(){
6875 // summary:
6876 // Subclasses must call this method from their postCreate() methods
6877 // tags:
6878 // protected
a089699c 6879
1354d172
AD
6880 // find any associated label element and add to ComboBox node.
6881 var label=query('label[for="'+this.id+'"]');
6882 if(label.length){
6883 label[0].id = (this.id+"_label");
6884 this.domNode.setAttribute("aria-labelledby", label[0].id);
a089699c 6885
a089699c 6886 }
1354d172
AD
6887 this.inherited(arguments);
6888 },
81bea17a 6889
1354d172
AD
6890 _getMenuLabelFromItem: function(/*Item*/ item){
6891 var label = this.labelFunc(item, this.store),
6892 labelType = this.labelType;
6893 // If labelType is not "text" we don't want to screw any markup ot whatever.
6894 if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
6895 label = this.doHighlight(label, this._escapeHtml(this._lastInput));
6896 labelType = "html";
a089699c 6897 }
1354d172
AD
6898 return {html: labelType == "html", label: label};
6899 },
a089699c 6900
1354d172
AD
6901 doHighlight: function(/*String*/ label, /*String*/ find){
6902 // summary:
6903 // Highlights the string entered by the user in the menu. By default this
6904 // highlights the first occurrence found. Override this method
6905 // to implement your custom highlighting.
6906 // tags:
6907 // protected
a089699c 6908
1354d172
AD
6909 var
6910 // Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
6911 modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
6912 i = this.queryExpr.indexOf("${0}");
6913 find = regexp.escapeString(find); // escape regexp special chars
6914 return this._escapeHtml(label).replace(
6915 // prepend ^ when this.queryExpr == "${0}*" and append $ when this.queryExpr == "*${0}"
6916 new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
6917 '<span class="dijitComboBoxHighlightMatch">$1</span>'
6918 ); // returns String, (almost) valid HTML (entities encoded)
6919 },
a089699c 6920
1354d172
AD
6921 _escapeHtml: function(/*String*/ str){
6922 // TODO Should become dojo.html.entities(), when exists use instead
6923 // summary:
6924 // Adds escape sequences for special characters in XML: &<>"'
6925 str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
6926 .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); //balance"
6927 return str; // string
6928 },
6929
6930 reset: function(){
6931 // Overrides the _FormWidget.reset().
6932 // Additionally reset the .item (to clean up).
6933 this.item = null;
6934 this.inherited(arguments);
6935 },
6936
6937 labelFunc: function(/*item*/ item, /*dojo.store.api.Store*/ store){
6938 // summary:
6939 // Computes the label to display based on the dojo.data store item.
6940 // returns:
6941 // The label that the ComboBox should display
6942 // tags:
6943 // private
6944
6945 // Use toString() because XMLStore returns an XMLItem whereas this
6946 // method is expected to return a String (#9354).
6947 // Remove getValue() for 2.0 (old dojo.data API)
6948 return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) :
6949 item[this.labelAttr || this.searchAttr]).toString(); // String
6950 },
a089699c 6951
1354d172
AD
6952 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
6953 // summary:
6954 // Hook so set('value', value) works.
6955 // description:
6956 // Sets the value of the select.
6957 this._set("item", item||null); // value not looked up in store
6958 if(!value){ value = ''; } // null translates to blank
6959 this.inherited(arguments);
6960 },
6961 _setTextDirAttr: function(/*String*/ textDir){
6962 // summary:
6963 // Setter for textDir, needed for the dropDown's textDir update.
6964 // description:
6965 // Users shouldn't call this function; they should be calling
6966 // set('textDir', value)
6967 // tags:
6968 // private
6969 this.inherited(arguments);
6970 // update the drop down also (_ComboBoxMenuMixin)
6971 if(this.dropDown){
6972 this.dropDown._set("textDir", textDir);
a089699c 6973 }
1354d172
AD
6974 }
6975 });
6976});
a089699c 6977
1354d172
AD
6978},
6979'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",
6980'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\">\n\t<div role=\"presentation\" class=\"dijitTabInnerDiv\" data-dojo-attach-point=\"innerDiv,focusNode\">\n\t\t<div role=\"presentation\" class=\"dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"tabContent\">\n\t\t\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t\t\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n\t\t</div>\n\t</div>\n</div>",
6981'dijit/form/MappedTextBox':function(){
6982define("dijit/form/MappedTextBox", [
6983 "dojo/_base/declare", // declare
6984 "dojo/dom-construct", // domConstruct.place
6985 "./ValidationTextBox"
6986], function(declare, domConstruct, ValidationTextBox){
a089699c 6987
1354d172
AD
6988/*=====
6989 var ValidationTextBox = dijit.form.ValidationTextBox;
6990=====*/
a089699c 6991
1354d172
AD
6992 // module:
6993 // dijit/form/MappedTextBox
6994 // summary:
6995 // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
6996 // a visible formatted display value, and a serializable
6997 // value in a hidden input field which is actually sent to the server.
a089699c 6998
1354d172
AD
6999 return declare("dijit.form.MappedTextBox", ValidationTextBox, {
7000 // summary:
7001 // A dijit.form.ValidationTextBox subclass which provides a base class for widgets that have
7002 // a visible formatted display value, and a serializable
7003 // value in a hidden input field which is actually sent to the server.
7004 // description:
7005 // The visible display may
7006 // be locale-dependent and interactive. The value sent to the server is stored in a hidden
7007 // input field which uses the `name` attribute declared by the original widget. That value sent
7008 // to the server is defined by the dijit.form.MappedTextBox.serialize method and is typically
7009 // locale-neutral.
7010 // tags:
7011 // protected
a089699c 7012
1354d172
AD
7013 postMixInProperties: function(){
7014 this.inherited(arguments);
a089699c 7015
1354d172
AD
7016 // we want the name attribute to go to the hidden <input>, not the displayed <input>,
7017 // so override _FormWidget.postMixInProperties() setting of nameAttrSetting
7018 this.nameAttrSetting = "";
7019 },
a089699c 7020
1354d172
AD
7021 // Override default behavior to assign name to focusNode
7022 _setNameAttr: null,
a089699c 7023
1354d172
AD
7024 serialize: function(val /*=====, options =====*/){
7025 // summary:
7026 // Overridable function used to convert the get('value') result to a canonical
7027 // (non-localized) string. For example, will print dates in ISO format, and
7028 // numbers the same way as they are represented in javascript.
7029 // val: anything
7030 // options: Object?
7031 // tags:
7032 // protected extension
7033 return val.toString ? val.toString() : ""; // String
7034 },
a089699c 7035
1354d172
AD
7036 toString: function(){
7037 // summary:
7038 // Returns widget as a printable string using the widget's value
7039 // tags:
7040 // protected
7041 var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
7042 return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
7043 },
81bea17a 7044
1354d172
AD
7045 validate: function(){
7046 // Overrides `dijit.form.TextBox.validate`
7047 this.valueNode.value = this.toString();
7048 return this.inherited(arguments);
7049 },
a089699c 7050
1354d172
AD
7051 buildRendering: function(){
7052 // Overrides `dijit._TemplatedMixin.buildRendering`
a089699c 7053
1354d172 7054 this.inherited(arguments);
a089699c 7055
1354d172
AD
7056 // Create a hidden <input> node with the serialized value used for submit
7057 // (as opposed to the displayed value).
7058 // Passing in name as markup rather than calling domConstruct.create() with an attrs argument
7059 // to make query(input[name=...]) work on IE. (see #8660)
7060 this.valueNode = domConstruct.place("<input type='hidden'" + (this.name ? " name='" + this.name.replace(/'/g, "&quot;") + "'" : "") + "/>", this.textbox, "after");
7061 },
a089699c 7062
1354d172
AD
7063 reset: function(){
7064 // Overrides `dijit.form.ValidationTextBox.reset` to
7065 // reset the hidden textbox value to ''
7066 this.valueNode.value = '';
7067 this.inherited(arguments);
7068 }
7069 });
7070});
a089699c 7071
1354d172
AD
7072},
7073'dijit/form/ComboBoxMixin':function(){
7074require({cache:{
7075'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"}});
7076define("dijit/form/ComboBoxMixin", [
7077 "dojo/_base/declare", // declare
7078 "dojo/_base/Deferred",
7079 "dojo/_base/kernel", // kernel.deprecated
7080 "dojo/_base/lang", // lang.mixin
7081 "dojo/store/util/QueryResults", // dojo.store.util.QueryResults
7082 "./_AutoCompleterMixin",
7083 "./_ComboBoxMenu",
7084 "../_HasDropDown",
7085 "dojo/text!./templates/DropDownBox.html"
7086], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
a089699c 7087
81bea17a 7088/*=====
1354d172
AD
7089 var _AutoCompleterMixin = dijit.form._AutoCompleterMixin;
7090 var _ComboBoxMenu = dijit.form._ComboBoxMenu;
7091 var _HasDropDown = dijit._HasDropDown;
a089699c
AD
7092=====*/
7093
1354d172
AD
7094 // module:
7095 // dijit/form/ComboBoxMixin
7096 // summary:
7097 // Provides main functionality of ComboBox widget
a089699c 7098
1354d172
AD
7099 return declare("dijit.form.ComboBoxMixin", [_HasDropDown, _AutoCompleterMixin], {
7100 // summary:
7101 // Provides main functionality of ComboBox widget
a089699c 7102
1354d172
AD
7103 // dropDownClass: [protected extension] Function String
7104 // Dropdown widget class used to select a date/time.
7105 // Subclasses should specify this.
7106 dropDownClass: _ComboBoxMenu,
a089699c 7107
1354d172
AD
7108 // hasDownArrow: Boolean
7109 // Set this textbox to have a down arrow button, to display the drop down list.
7110 // Defaults to true.
7111 hasDownArrow: true,
a089699c 7112
1354d172 7113 templateString: template,
a089699c 7114
1354d172 7115 baseClass: "dijitTextBox dijitComboBox",
a089699c 7116
1354d172
AD
7117 /*=====
7118 // store: [const] dojo.store.api.Store || dojo.data.api.Read
7119 // Reference to data provider object used by this ComboBox.
7120 //
7121 // Should be dojo.store.api.Store, but dojo.data.api.Read supported
7122 // for backwards compatibility.
7123 store: null,
7124 =====*/
a089699c 7125
1354d172
AD
7126 // Set classes like dijitDownArrowButtonHover depending on
7127 // mouse action over button node
7128 cssStateNodes: {
7129 "_buttonNode": "dijitDownArrowButton"
7130 },
a089699c 7131
1354d172
AD
7132 _setHasDownArrowAttr: function(/*Boolean*/ val){
7133 this._set("hasDownArrow", val);
7134 this._buttonNode.style.display = val ? "" : "none";
7135 },
a089699c 7136
1354d172
AD
7137 _showResultList: function(){
7138 // hide the tooltip
7139 this.displayMessage("");
7140 this.inherited(arguments);
7141 },
a089699c 7142
1354d172
AD
7143 _setStoreAttr: function(store){
7144 // For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
7145 if(!store.get){
7146 lang.mixin(store, {
7147 _oldAPI: true,
7148 get: function(id){
7149 // summary:
7150 // Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
7151 // Like dojo.store.DataStore.get() except returns native item.
7152 var deferred = new Deferred();
7153 this.fetchItemByIdentity({
7154 identity: id,
7155 onItem: function(object){
7156 deferred.resolve(object);
7157 },
7158 onError: function(error){
7159 deferred.reject(error);
7160 }
7161 });
7162 return deferred.promise;
7163 },
7164 query: function(query, options){
7165 // summary:
7166 // Queries the store for objects. Like dojo.store.DataStore.query()
7167 // except returned Deferred contains array of native items.
7168 var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
7169 var fetchHandle = this.fetch(lang.mixin({
7170 query: query,
7171 onBegin: function(count){
7172 deferred.total = count;
7173 },
7174 onComplete: function(results){
7175 deferred.resolve(results);
7176 },
7177 onError: function(error){
7178 deferred.reject(error);
7179 }
7180 }, options));
7181 return QueryResults(deferred);
7182 }
7183 });
a089699c 7184 }
1354d172
AD
7185 this._set("store", store);
7186 },
a089699c 7187
1354d172
AD
7188 postMixInProperties: function(){
7189 // Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
7190 // Unfortunately, without special code, it ends up executing second.
7191 if(this.params.store){
7192 this._setStoreAttr(this.params.store);
7193 }
a089699c 7194
1354d172
AD
7195 this.inherited(arguments);
7196
7197 // User may try to access this.store.getValue() etc. in a custom labelFunc() function.
7198 // It's not available with the new data store for handling inline <option> tags, so add it.
7199 if(!this.params.store){
7200 var clazz = this.declaredClass;
7201 lang.mixin(this.store, {
7202 getValue: function(item, attr){
7203 kernel.deprecated(clazz + ".store.getValue(item, attr) is deprecated for builtin store. Use item.attr directly", "", "2.0");
7204 return item[attr];
7205 },
7206 getLabel: function(item){
7207 kernel.deprecated(clazz + ".store.getLabel(item) is deprecated for builtin store. Use item.label directly", "", "2.0");
7208 return item.name;
7209 },
7210 fetch: function(args){
7211 kernel.deprecated(clazz + ".store.fetch() is deprecated for builtin store.", "Use store.query()", "2.0");
7212 var shim = ["dojo/data/ObjectStore"]; // indirection so it doesn't get rolled into a build
7213 require(shim, lang.hitch(this, function(ObjectStore){
7214 new ObjectStore({objectStore: this}).fetch(args);
7215 }));
7216 }
7217 });
7218 }
a089699c 7219 }
1354d172
AD
7220 });
7221});
a089699c 7222
1354d172
AD
7223},
7224'dijit/form/_TextBoxMixin':function(){
7225define("dijit/form/_TextBoxMixin", [
7226 "dojo/_base/array", // array.forEach
7227 "dojo/_base/declare", // declare
7228 "dojo/dom", // dom.byId
7229 "dojo/_base/event", // event.stop
7230 "dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
7231 "dojo/_base/lang", // lang.mixin
7232 ".." // for exporting dijit._setSelectionRange, dijit.selectInputText
7233], function(array, declare, dom, event, keys, lang, dijit){
7234
7235// module:
7236// dijit/form/_TextBoxMixin
7237// summary:
7238// A mixin for textbox form input widgets
a089699c 7239
1354d172
AD
7240var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
7241 // summary:
7242 // A mixin for textbox form input widgets
a089699c 7243
1354d172
AD
7244 // trim: Boolean
7245 // Removes leading and trailing whitespace if true. Default is false.
7246 trim: false,
a089699c 7247
1354d172
AD
7248 // uppercase: Boolean
7249 // Converts all characters to uppercase if true. Default is false.
7250 uppercase: false,
a089699c 7251
1354d172
AD
7252 // lowercase: Boolean
7253 // Converts all characters to lowercase if true. Default is false.
7254 lowercase: false,
a089699c 7255
1354d172
AD
7256 // propercase: Boolean
7257 // Converts the first character of each word to uppercase if true.
7258 propercase: false,
a089699c 7259
1354d172
AD
7260 // maxLength: String
7261 // HTML INPUT tag maxLength declaration.
7262 maxLength: "",
7263
7264 // selectOnClick: [const] Boolean
7265 // If true, all text will be selected when focused with mouse
7266 selectOnClick: false,
a089699c 7267
1354d172
AD
7268 // placeHolder: String
7269 // Defines a hint to help users fill out the input field (as defined in HTML 5).
7270 // This should only contain plain text (no html markup).
7271 placeHolder: "",
a089699c 7272
1354d172
AD
7273 _getValueAttr: function(){
7274 // summary:
7275 // Hook so get('value') works as we like.
7276 // description:
7277 // For `dijit.form.TextBox` this basically returns the value of the <input>.
7278 //
7279 // For `dijit.form.MappedTextBox` subclasses, which have both
7280 // a "displayed value" and a separate "submit value",
7281 // This treats the "displayed value" as the master value, computing the
7282 // submit value from it via this.parse().
7283 return this.parse(this.get('displayedValue'), this.constraints);
a089699c
AD
7284 },
7285
1354d172 7286 _setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
a089699c 7287 // summary:
1354d172
AD
7288 // Hook so set('value', ...) works.
7289 //
a089699c 7290 // description:
1354d172
AD
7291 // Sets the value of the widget to "value" which can be of
7292 // any type as determined by the widget.
a089699c 7293 //
1354d172
AD
7294 // value:
7295 // The visual element value is also set to a corresponding,
7296 // but not necessarily the same, value.
7297 //
7298 // formattedValue:
7299 // If specified, used to set the visual element value,
7300 // otherwise a computed visual value is used.
7301 //
7302 // priorityChange:
7303 // If true, an onChange event is fired immediately instead of
7304 // waiting for the next blur event.
7305
7306 var filteredValue;
7307 if(value !== undefined){
7308 // TODO: this is calling filter() on both the display value and the actual value.
7309 // I added a comment to the filter() definition about this, but it should be changed.
7310 filteredValue = this.filter(value);
7311 if(typeof formattedValue != "string"){
7312 if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
7313 formattedValue = this.filter(this.format(filteredValue, this.constraints));
7314 }else{ formattedValue = ''; }
7315 }
7316 }
7317 if(formattedValue != null && formattedValue != undefined && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
7318 this.textbox.value = formattedValue;
7319 this._set("displayedValue", this.get("displayedValue"));
a089699c
AD
7320 }
7321
1354d172
AD
7322 if(this.textDir == "auto"){
7323 this.applyTextDir(this.focusNode, formattedValue);
a089699c 7324 }
1354d172
AD
7325
7326 this.inherited(arguments, [filteredValue, priorityChange]);
a089699c
AD
7327 },
7328
1354d172
AD
7329 // displayedValue: String
7330 // For subclasses like ComboBox where the displayed value
7331 // (ex: Kentucky) and the serialized value (ex: KY) are different,
7332 // this represents the displayed value.
7333 //
7334 // Setting 'displayedValue' through set('displayedValue', ...)
7335 // updates 'value', and vice-versa. Otherwise 'value' is updated
7336 // from 'displayedValue' periodically, like onBlur etc.
7337 //
7338 // TODO: move declaration to MappedTextBox?
7339 // Problem is that ComboBox references displayedValue,
7340 // for benefit of FilteringSelect.
7341 displayedValue: "",
7342
7343 _getDisplayedValueAttr: function(){
a089699c 7344 // summary:
1354d172
AD
7345 // Hook so get('displayedValue') works.
7346 // description:
7347 // Returns the displayed value (what the user sees on the screen),
7348 // after filtering (ie, trimming spaces etc.).
7349 //
7350 // For some subclasses of TextBox (like ComboBox), the displayed value
7351 // is different from the serialized value that's actually
7352 // sent to the server (see dijit.form.ValidationTextBox.serialize)
a089699c 7353
1354d172
AD
7354 // TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
7355 // this method
7356 // TODO: this isn't really the displayed value when the user is typing
7357 return this.filter(this.textbox.value);
7358 },
81bea17a 7359
1354d172
AD
7360 _setDisplayedValueAttr: function(/*String*/ value){
7361 // summary:
7362 // Hook so set('displayedValue', ...) works.
7363 // description:
7364 // Sets the value of the visual element to the string "value".
7365 // The widget value is also set to a corresponding,
7366 // but not necessarily the same, value.
7367
7368 if(value === null || value === undefined){ value = '' }
7369 else if(typeof value != "string"){ value = String(value) }
7370
7371 this.textbox.value = value;
7372
7373 // sets the serialized value to something corresponding to specified displayedValue
7374 // (if possible), and also updates the textbox.value, for example converting "123"
7375 // to "123.00"
7376 this._setValueAttr(this.get('value'), undefined);
7377
7378 this._set("displayedValue", this.get('displayedValue'));
7379
7380 // textDir support
7381 if(this.textDir == "auto"){
7382 this.applyTextDir(this.focusNode, value);
81bea17a 7383 }
a089699c
AD
7384 },
7385
1354d172 7386 format: function(value /*=====, constraints =====*/){
a089699c 7387 // summary:
1354d172 7388 // Replaceable function to convert a value to a properly formatted string.
a089699c 7389 // value: String
1354d172
AD
7390 // constraints: Object
7391 // tags:
7392 // protected extension
7393 return ((value == null || value == undefined) ? "" : (value.toString ? value.toString() : value));
a089699c
AD
7394 },
7395
1354d172 7396 parse: function(value /*=====, constraints =====*/){
a089699c 7397 // summary:
1354d172
AD
7398 // Replaceable function to convert a formatted string to a value
7399 // value: String
7400 // constraints: Object
a089699c 7401 // tags:
1354d172 7402 // protected extension
a089699c 7403
1354d172
AD
7404 return value; // String
7405 },
a089699c 7406
1354d172
AD
7407 _refreshState: function(){
7408 // summary:
7409 // After the user types some characters, etc., this method is
7410 // called to check the field for validity etc. The base method
7411 // in `dijit.form.TextBox` does nothing, but subclasses override.
7412 // tags:
7413 // protected
7414 },
a089699c 7415
1354d172
AD
7416 /*=====
7417 onInput: function(event){
7418 // summary:
7419 // Connect to this function to receive notifications of various user data-input events.
7420 // Return false to cancel the event and prevent it from being processed.
7421 // event:
7422 // keydown | keypress | cut | paste | input
7423 // tags:
7424 // callback
a089699c 7425 },
1354d172
AD
7426 =====*/
7427 onInput: function(){},
a089699c 7428
1354d172
AD
7429 __skipInputEvent: false,
7430 _onInput: function(){
a089699c 7431 // summary:
1354d172
AD
7432 // Called AFTER the input event has happened
7433 // set text direction according to textDir that was defined in creation
7434 if(this.textDir == "auto"){
7435 this.applyTextDir(this.focusNode, this.focusNode.value);
7436 }
a089699c 7437
1354d172 7438 this._refreshState();
a089699c 7439
1354d172
AD
7440 // In case someone is watch()'ing for changes to displayedValue
7441 this._set("displayedValue", this.get("displayedValue"));
7442 },
7443
7444 postCreate: function(){
7445 // setting the value here is needed since value="" in the template causes "undefined"
7446 // and setting in the DOM (instead of the JS object) helps with form reset actions
7447 this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
7448
7449 this.inherited(arguments);
7450
7451 // normalize input events to reduce spurious event processing
7452 // onkeydown: do not forward modifier keys
7453 // set charOrCode to numeric keycode
7454 // onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
7455 // onpaste & oncut: set charOrCode to 229 (IME)
7456 // oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
7457 var handleEvent = function(e){
7458 var charCode = e.charOrCode || e.keyCode || 229;
7459 if(e.type == "keydown"){
7460 switch(charCode){ // ignore "state" keys
7461 case keys.SHIFT:
7462 case keys.ALT:
7463 case keys.CTRL:
7464 case keys.META:
7465 case keys.CAPS_LOCK:
7466 return;
7467 default:
7468 if(charCode >= 65 && charCode <= 90){ return; } // keydown for A-Z can be processed with keypress
7469 }
7470 }
7471 if(e.type == "keypress" && typeof charCode != "string"){ return; }
7472 if(e.type == "input"){
7473 if(this.__skipInputEvent){ // duplicate event
7474 this.__skipInputEvent = false;
7475 return;
7476 }
7477 }else{
7478 this.__skipInputEvent = true;
7479 }
7480 // create fake event to set charOrCode and to know if preventDefault() was called
7481 var faux = lang.mixin({}, e, {
7482 charOrCode: charCode,
7483 wasConsumed: false,
7484 preventDefault: function(){
7485 faux.wasConsumed = true;
7486 e.preventDefault();
7487 },
7488 stopPropagation: function(){ e.stopPropagation(); }
7489 });
7490 // give web page author a chance to consume the event
7491 if(this.onInput(faux) === false){
7492 event.stop(faux); // return false means stop
7493 }
7494 if(faux.wasConsumed){ return; } // if preventDefault was called
7495 setTimeout(lang.hitch(this, "_onInput", faux), 0); // widget notification after key has posted
7496 };
7497 array.forEach([ "onkeydown", "onkeypress", "onpaste", "oncut", "oninput", "oncompositionend" ], function(event){
7498 this.connect(this.textbox, event, handleEvent);
7499 }, this);
7500 },
7501
7502 _blankValue: '', // if the textbox is blank, what value should be reported
7503 filter: function(val){
a089699c 7504 // summary:
1354d172
AD
7505 // Auto-corrections (such as trimming) that are applied to textbox
7506 // value on blur or form submit.
7507 // description:
7508 // For MappedTextBox subclasses, this is called twice
7509 // - once with the display value
7510 // - once the value as set/returned by set('value', ...)
7511 // and get('value'), ex: a Number for NumberTextBox.
7512 //
7513 // In the latter case it does corrections like converting null to NaN. In
7514 // the former case the NumberTextBox.filter() method calls this.inherited()
7515 // to execute standard trimming code in TextBox.filter().
7516 //
7517 // TODO: break this into two methods in 2.0
7518 //
7519 // tags:
7520 // protected extension
7521 if(val === null){ return this._blankValue; }
7522 if(typeof val != "string"){ return val; }
7523 if(this.trim){
7524 val = lang.trim(val);
7525 }
7526 if(this.uppercase){
7527 val = val.toUpperCase();
7528 }
7529 if(this.lowercase){
7530 val = val.toLowerCase();
7531 }
7532 if(this.propercase){
7533 val = val.replace(/[^\s]+/g, function(word){
7534 return word.substring(0,1).toUpperCase() + word.substring(1);
7535 });
7536 }
7537 return val;
7538 },
a089699c 7539
1354d172
AD
7540 _setBlurValue: function(){
7541 this._setValueAttr(this.get('value'), true);
7542 },
a089699c 7543
1354d172
AD
7544 _onBlur: function(e){
7545 if(this.disabled){ return; }
7546 this._setBlurValue();
7547 this.inherited(arguments);
a089699c 7548
1354d172
AD
7549 if(this._selectOnClickHandle){
7550 this.disconnect(this._selectOnClickHandle);
a089699c 7551 }
1354d172 7552 },
a089699c 7553
1354d172
AD
7554 _isTextSelected: function(){
7555 return this.textbox.selectionStart == this.textbox.selectionEnd;
7556 },
7557
7558 _onFocus: function(/*String*/ by){
7559 if(this.disabled || this.readOnly){ return; }
7560
7561 // Select all text on focus via click if nothing already selected.
7562 // Since mouse-up will clear the selection need to defer selection until after mouse-up.
7563 // Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
7564 if(this.selectOnClick && by == "mouse"){
7565 this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
7566 // Only select all text on first click; otherwise users would have no way to clear
7567 // the selection.
7568 this.disconnect(this._selectOnClickHandle);
7569
7570 // Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
7571 // and if not, then select all the text
7572 if(this._isTextSelected()){
7573 _TextBoxMixin.selectInputText(this.textbox);
7574 }
7575 });
7576 }
7577 // call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
7578 // (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
7579 this.inherited(arguments);
7580
7581 this._refreshState();
7582 },
a089699c 7583
1354d172
AD
7584 reset: function(){
7585 // Overrides dijit._FormWidget.reset().
7586 // Additionally resets the displayed textbox value to ''
7587 this.textbox.value = '';
7588 this.inherited(arguments);
7589 },
7590 _setTextDirAttr: function(/*String*/ textDir){
7591 // summary:
7592 // Setter for textDir.
7593 // description:
7594 // Users shouldn't call this function; they should be calling
7595 // set('textDir', value)
7596 // tags:
7597 // private
7598
7599 // only if new textDir is different from the old one
7600 // and on widgets creation.
7601 if(!this._created
7602 || this.textDir != textDir){
7603 this._set("textDir", textDir);
7604 // so the change of the textDir will take place immediately.
7605 this.applyTextDir(this.focusNode, this.focusNode.value);
7606 }
7607 }
7608});
a089699c
AD
7609
7610
1354d172
AD
7611_TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
7612 if(element.setSelectionRange){
7613 element.setSelectionRange(start, stop);
7614 }
7615};
a089699c 7616
1354d172
AD
7617_TextBoxMixin.selectInputText = dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
7618 // summary:
7619 // Select text in the input element argument, from start (default 0), to stop (default end).
a089699c 7620
1354d172
AD
7621 // TODO: use functions in _editor/selection.js?
7622 element = dom.byId(element);
7623 if(isNaN(start)){ start = 0; }
7624 if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
7625 try{
7626 element.focus();
7627 _TextBoxMixin._setSelectionRange(element, start, stop);
7628 }catch(e){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
7629};
a089699c 7630
1354d172
AD
7631return _TextBoxMixin;
7632});
a089699c 7633
1354d172
AD
7634},
7635'dijit/form/SimpleTextarea':function(){
7636define("dijit/form/SimpleTextarea", [
7637 "dojo/_base/declare", // declare
7638 "dojo/dom-class", // domClass.add
7639 "dojo/_base/sniff", // has("ie") has("opera")
7640 "dojo/_base/window", // win.doc.selection win.doc.selection.createRange
7641 "./TextBox"
7642], function(declare, domClass, has, win, TextBox){
a089699c 7643
1354d172
AD
7644/*=====
7645 var TextBox = dijit.form.TextBox;
7646=====*/
a089699c 7647
1354d172
AD
7648// module:
7649// dijit/form/SimpleTextarea
7650// summary:
7651// A simple textarea that degrades, and responds to
7652// minimal LayoutContainer usage, and works with dijit.form.Form.
7653// Doesn't automatically size according to input, like Textarea.
a089699c 7654
1354d172 7655return declare("dijit.form.SimpleTextarea", TextBox, {
a089699c 7656 // summary:
1354d172
AD
7657 // A simple textarea that degrades, and responds to
7658 // minimal LayoutContainer usage, and works with dijit.form.Form.
7659 // Doesn't automatically size according to input, like Textarea.
a089699c
AD
7660 //
7661 // example:
1354d172 7662 // | <textarea data-dojo-type="dijit.form.SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
a089699c
AD
7663 //
7664 // example:
1354d172 7665 // | new dijit.form.SimpleTextarea({ rows:20, cols:30 }, "foo");
a089699c 7666
1354d172 7667 baseClass: "dijitTextBox dijitTextArea",
a089699c 7668
1354d172
AD
7669 // rows: Number
7670 // The number of rows of text.
7671 rows: "3",
a089699c 7672
1354d172
AD
7673 // rows: Number
7674 // The number of characters per line.
7675 cols: "20",
a089699c 7676
1354d172 7677 templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
a089699c 7678
1354d172
AD
7679 postMixInProperties: function(){
7680 // Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
7681 // TODO: parser will handle this in 2.0
7682 if(!this.value && this.srcNodeRef){
7683 this.value = this.srcNodeRef.value;
7684 }
7685 this.inherited(arguments);
7686 },
a089699c 7687
a089699c 7688 buildRendering: function(){
a089699c 7689 this.inherited(arguments);
1354d172
AD
7690 if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
7691 domClass.add(this.textbox, "dijitTextAreaCols");
7692 }
7693 },
a089699c 7694
1354d172
AD
7695 filter: function(/*String*/ value){
7696 // Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
7697 // as \r\n instead of just \n
7698 if(value){
7699 value = value.replace(/\r/g,"");
7700 }
7701 return this.inherited(arguments);
7702 },
7703
7704 _onInput: function(/*Event?*/ e){
7705 // Override TextBox._onInput() to enforce maxLength restriction
7706 if(this.maxLength){
7707 var maxLength = parseInt(this.maxLength);
7708 var value = this.textbox.value.replace(/\r/g,'');
7709 var overflow = value.length - maxLength;
7710 if(overflow > 0){
7711 var textarea = this.textbox;
7712 if(textarea.selectionStart){
7713 var pos = textarea.selectionStart;
7714 var cr = 0;
7715 if(has("opera")){
7716 cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
7717 }
7718 this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
7719 textarea.setSelectionRange(pos-overflow, pos-overflow);
7720 }else if(win.doc.selection){ //IE
7721 textarea.focus();
7722 var range = win.doc.selection.createRange();
7723 // delete overflow characters
7724 range.moveStart("character", -overflow);
7725 range.text = '';
7726 // show cursor
7727 range.select();
7728 }
7729 }
7730 }
7731 this.inherited(arguments);
a089699c
AD
7732 }
7733});
7734
1354d172
AD
7735});
7736
7737},
7738'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n",
7739'dijit/_base/window':function(){
7740define("dijit/_base/window", [
7741 "dojo/window", // windowUtils.get
7742 ".." // export symbol to dijit
7743], function(windowUtils, dijit){
7744 // module:
7745 // dijit/_base/window
a089699c 7746 // summary:
1354d172 7747 // Back compatibility module, new code should use windowUtils directly instead of using this module.
a089699c 7748
1354d172
AD
7749 dijit.getDocumentWindow = function(doc){
7750 return windowUtils.get(doc);
7751 };
7752});
81bea17a 7753
1354d172
AD
7754},
7755'dijit/form/RadioButton':function(){
7756define("dijit/form/RadioButton", [
7757 "dojo/_base/declare", // declare
7758 "./CheckBox",
7759 "./_RadioButtonMixin"
7760], function(declare, CheckBox, _RadioButtonMixin){
a089699c 7761
1354d172
AD
7762/*=====
7763 var CheckBox = dijit.form.CheckBox;
7764 var _RadioButtonMixin = dijit.form._RadioButtonMixin;
7765=====*/
81bea17a 7766
1354d172
AD
7767 // module:
7768 // dijit/form/RadioButton
7769 // summary:
7770 // Radio button widget
81bea17a 7771
1354d172 7772 return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
81bea17a 7773 // summary:
1354d172 7774 // Same as an HTML radio, but with fancy styling.
81bea17a 7775
1354d172
AD
7776 baseClass: "dijitRadio"
7777 });
81bea17a 7778});
a089699c 7779
1354d172
AD
7780},
7781'dijit/main':function(){
7782define("dijit/main", [
7783 "dojo/_base/kernel"
7784], function(dojo){
7785 // module:
7786 // dijit
a089699c 7787 // summary:
1354d172 7788 // The dijit package main module
a089699c 7789
1354d172
AD
7790 return dojo.dijit;
7791});
a089699c 7792
1354d172
AD
7793},
7794'dijit/_OnDijitClickMixin':function(){
7795define("dijit/_OnDijitClickMixin", [
7796 "dojo/on",
7797 "dojo/_base/array", // array.forEach
7798 "dojo/keys", // keys.ENTER keys.SPACE
7799 "dojo/_base/declare", // declare
7800 "dojo/_base/sniff", // has("ie")
7801 "dojo/_base/unload", // unload.addOnWindowUnload
7802 "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
7803], function(on, array, keys, declare, has, unload, win){
7804
7805 // module:
7806 // dijit/_OnDijitClickMixin
a089699c 7807 // summary:
1354d172
AD
7808 // Mixin so you can pass "ondijitclick" to this.connect() method,
7809 // as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key)
7810
7811
7812 // Keep track of where the last keydown event was, to help avoid generating
7813 // spurious ondijitclick events when:
7814 // 1. focus is on a <button> or <a>
7815 // 2. user presses then releases the ENTER key
7816 // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
7817 // 4. onkeyup event fires, causing the ondijitclick handler to fire
7818 var lastKeyDownNode = null;
7819 if(has("ie")){
7820 (function(){
7821 var keydownCallback = function(evt){
7822 lastKeyDownNode = evt.srcElement;
7823 };
7824 win.doc.attachEvent('onkeydown', keydownCallback);
7825 unload.addOnWindowUnload(function(){
7826 win.doc.detachEvent('onkeydown', keydownCallback);
7827 });
7828 })();
7829 }else{
7830 win.doc.addEventListener('keydown', function(evt){
7831 lastKeyDownNode = evt.target;
7832 }, true);
a089699c 7833 }
a089699c 7834
1354d172
AD
7835 // Custom a11yclick (a.k.a. ondijitclick) event
7836 var a11yclick = function(node, listener){
7837 if(/input|button/i.test(node.nodeName)){
7838 // pass through, the browser already generates click event on SPACE/ENTER key
7839 return on(node, "click", listener);
7840 }else{
7841 // Don't fire the click event unless both the keydown and keyup occur on this node.
7842 // Avoids problems where focus shifted to this node or away from the node on keydown,
7843 // either causing this node to process a stray keyup event, or causing another node
7844 // to get a stray keyup event.
7845
7846 function clickKey(/*Event*/ e){
7847 return (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
7848 !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
7849 }
7850 var handles = [
7851 on(node, "keypress", function(e){
7852 //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
7853 if(clickKey(e)){
7854 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
7855 lastKeyDownNode = e.target;
7856
7857 // Prevent viewport scrolling on space key in IE<9.
7858 // (Reproducible on test_Button.html on any of the first dijit.form.Button examples)
7859 // Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will
7860 // suppress the onkeypress event, breaking _HasDropDown
7861 e.preventDefault();
7862 }
7863 }),
a089699c 7864
1354d172
AD
7865 on(node, "keyup", function(e){
7866 //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
7867 if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
7868 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
7869 lastKeyDownNode = null;
7870 listener.call(this, e);
7871 }
7872 }),
a089699c 7873
1354d172
AD
7874 on(node, "click", function(e){
7875 // and connect for mouse clicks too (or touch-clicks on mobile)
7876 listener.call(this, e);
7877 })
7878 ];
81bea17a 7879
1354d172
AD
7880 return {
7881 remove: function(){
7882 array.forEach(handles, function(h){ h.remove(); });
7883 }
7884 };
7885 }
7886 };
81bea17a 7887
1354d172
AD
7888 return declare("dijit._OnDijitClickMixin", null, {
7889 connect: function(
7890 /*Object|null*/ obj,
7891 /*String|Function*/ event,
7892 /*String|Function*/ method){
7893 // summary:
7894 // Connects specified obj/event to specified method of this object
7895 // and registers for disconnect() on widget destroy.
7896 // description:
7897 // Provide widget-specific analog to connect.connect, except with the
7898 // implicit use of this widget as the target object.
7899 // This version of connect also provides a special "ondijitclick"
7900 // event which triggers on a click or space or enter keyup.
7901 // Events connected with `this.connect` are disconnected upon
7902 // destruction.
7903 // returns:
7904 // A handle that can be passed to `disconnect` in order to disconnect before
7905 // the widget is destroyed.
7906 // example:
7907 // | var btn = new dijit.form.Button();
7908 // | // when foo.bar() is called, call the listener we're going to
7909 // | // provide in the scope of btn
7910 // | btn.connect(foo, "bar", function(){
7911 // | console.debug(this.toString());
7912 // | });
7913 // tags:
7914 // protected
a089699c 7915
1354d172
AD
7916 return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
7917 }
7918 });
7919});
a089699c 7920
1354d172
AD
7921},
7922'dijit/InlineEditBox':function(){
7923require({cache:{
7924'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
7925define("dijit/InlineEditBox", [
7926 "dojo/_base/array", // array.forEach
7927 "dojo/_base/declare", // declare
7928 "dojo/dom-attr", // domAttr.set domAttr.get
7929 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
7930 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
7931 "dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
7932 "dojo/_base/event", // event.stop
7933 "dojo/i18n", // i18n.getLocalization
7934 "dojo/_base/kernel", // kernel.deprecated
7935 "dojo/keys", // keys.ENTER keys.ESCAPE
7936 "dojo/_base/lang", // lang.getObject
7937 "dojo/_base/sniff", // has("ie")
7938 "./focus",
7939 "./_Widget",
7940 "./_TemplatedMixin",
7941 "./_WidgetsInTemplateMixin",
7942 "./_Container",
7943 "./form/Button",
7944 "./form/_TextBoxMixin",
7945 "./form/TextBox",
7946 "dojo/text!./templates/InlineEditBox.html",
7947 "dojo/i18n!./nls/common"
7948], function(array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has,
7949 fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
a089699c 7950
1354d172
AD
7951/*=====
7952 var _Widget = dijit._Widget;
7953 var _TemplatedMixin = dijit._TemplatedMixin;
7954 var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
7955 var _Container = dijit._Container;
7956 var Button = dijit.form.Button;
7957 var TextBox = dijit.form.TextBox;
7958=====*/
7959
7960// module:
7961// dijit/InlineEditBox
7962// summary:
7963// An element with in-line edit capabilities
7964
7965var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
a089699c 7966 // summary:
1354d172
AD
7967 // Internal widget used by InlineEditBox, displayed when in editing mode
7968 // to display the editor and maybe save/cancel buttons. Calling code should
7969 // connect to save/cancel methods to detect when editing is finished
7970 //
7971 // Has mainly the same parameters as InlineEditBox, plus these values:
7972 //
7973 // style: Object
7974 // Set of CSS attributes of display node, to replicate in editor
7975 //
7976 // value: String
7977 // Value as an HTML string or plain text string, depending on renderAsHTML flag
a089699c 7978
1354d172 7979 templateString: template,
a089699c 7980
1354d172
AD
7981 postMixInProperties: function(){
7982 this.inherited(arguments);
7983 this.messages = i18n.getLocalization("dijit", "common", this.lang);
7984 array.forEach(["buttonSave", "buttonCancel"], function(prop){
7985 if(!this[prop]){ this[prop] = this.messages[prop]; }
7986 }, this);
7987 },
a089699c 7988
1354d172
AD
7989 buildRendering: function(){
7990 this.inherited(arguments);
a089699c 7991
1354d172
AD
7992 // Create edit widget in place in the template
7993 var cls = typeof this.editor == "string" ? lang.getObject(this.editor) : this.editor;
7994
7995 // Copy the style from the source
7996 // Don't copy ALL properties though, just the necessary/applicable ones.
7997 // wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
7998 // is a relative value like 200%, rather than an absolute value like 24px, and
7999 // the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
8000 var srcStyle = this.sourceStyle,
8001 editStyle = "line-height:" + srcStyle.lineHeight + ";",
8002 destStyle = domStyle.getComputedStyle(this.domNode);
8003 array.forEach(["Weight","Family","Size","Style"], function(prop){
8004 var textStyle = srcStyle["font"+prop],
8005 wrapperStyle = destStyle["font"+prop];
8006 if(wrapperStyle != textStyle){
8007 editStyle += "font-"+prop+":"+srcStyle["font"+prop]+";";
a089699c 8008 }
1354d172
AD
8009 }, this);
8010 array.forEach(["marginTop","marginBottom","marginLeft", "marginRight"], function(prop){
8011 this.domNode.style[prop] = srcStyle[prop];
8012 }, this);
8013 var width = this.inlineEditBox.width;
8014 if(width == "100%"){
8015 // block mode
8016 editStyle += "width:100%;";
8017 this.domNode.style.display = "block";
8018 }else{
8019 // inline-block mode
8020 editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
a089699c 8021 }
1354d172
AD
8022 var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
8023 style: editStyle,
8024 dir: this.dir,
8025 lang: this.lang,
8026 textDir: this.textDir
8027 });
8028 editorParams[ "displayedValue" in cls.prototype ? "displayedValue" : "value"] = this.value;
8029 this.editWidget = new cls(editorParams, this.editorPlaceholder);
8030
8031 if(this.inlineEditBox.autoSave){
8032 // Remove the save/cancel buttons since saving is done by simply tabbing away or
8033 // selecting a value from the drop down list
8034 domConstruct.destroy(this.buttonContainer);
a089699c 8035 }
1354d172 8036 },
a089699c 8037
1354d172
AD
8038 postCreate: function(){
8039 this.inherited(arguments);
a089699c 8040
1354d172
AD
8041 var ew = this.editWidget;
8042
8043 if(this.inlineEditBox.autoSave){
8044 // Selecting a value from a drop down list causes an onChange event and then we save
8045 this.connect(ew, "onChange", "_onChange");
a089699c 8046
1354d172
AD
8047 // ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
8048 // prevent Dialog from closing when the user just wants to revert the value in the edit widget),
8049 // so this is the only way we can see the key press event.
8050 this.connect(ew, "onKeyPress", "_onKeyPress");
8051 }else{
8052 // If possible, enable/disable save button based on whether the user has changed the value
8053 if("intermediateChanges" in ew){
8054 ew.set("intermediateChanges", true);
8055 this.connect(ew, "onChange", "_onIntermediateChange");
8056 this.saveButton.set("disabled", true);
8057 }
8058 }
8059 },
a089699c 8060
1354d172
AD
8061 _onIntermediateChange: function(/*===== val =====*/){
8062 // summary:
8063 // Called for editor widgets that support the intermediateChanges=true flag as a way
8064 // to detect when to enable/disabled the save button
8065 this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
8066 },
a089699c 8067
1354d172
AD
8068 destroy: function(){
8069 this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
8070 this.inherited(arguments);
8071 },
a089699c 8072
1354d172 8073 getValue: function(){
a089699c 8074 // summary:
1354d172
AD
8075 // Return the [display] value of the edit widget
8076 var ew = this.editWidget;
8077 return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
8078 },
81bea17a 8079
1354d172
AD
8080 _onKeyPress: function(e){
8081 // summary:
8082 // Handler for keypress in the edit box in autoSave mode.
8083 // description:
8084 // For autoSave widgets, if Esc/Enter, call cancel/save.
8085 // tags:
8086 // private
81bea17a 8087
1354d172
AD
8088 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8089 if(e.altKey || e.ctrlKey){ return; }
8090 // If Enter/Esc pressed, treat as save/cancel.
8091 if(e.charOrCode == keys.ESCAPE){
8092 event.stop(e);
8093 this.cancel(true); // sets editing=false which short-circuits _onBlur processing
8094 }else if(e.charOrCode == keys.ENTER && e.target.tagName == "INPUT"){
8095 event.stop(e);
8096 this._onChange(); // fire _onBlur and then save
8097 }
81bea17a 8098
1354d172
AD
8099 // _onBlur will handle TAB automatically by allowing
8100 // the TAB to change focus before we mess with the DOM: #6227
8101 // Expounding by request:
8102 // The current focus is on the edit widget input field.
8103 // save() will hide and destroy this widget.
8104 // We want the focus to jump from the currently hidden
8105 // displayNode, but since it's hidden, it's impossible to
8106 // unhide it, focus it, and then have the browser focus
8107 // away from it to the next focusable element since each
8108 // of these events is asynchronous and the focus-to-next-element
8109 // is already queued.
8110 // So we allow the browser time to unqueue the move-focus event
8111 // before we do all the hide/show stuff.
a089699c
AD
8112 }
8113 },
1354d172
AD
8114
8115 _onBlur: function(){
a089699c 8116 // summary:
1354d172
AD
8117 // Called when focus moves outside the editor
8118 // tags:
8119 // private
8120
8121 this.inherited(arguments);
8122 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
8123 if(this.getValue() == this._resetValue){
8124 this.cancel(false);
8125 }else if(this.enableSave()){
8126 this.save(false);
8127 }
a089699c 8128 }
a089699c 8129 },
1354d172
AD
8130
8131 _onChange: function(){
a089699c 8132 // summary:
1354d172
AD
8133 // Called when the underlying widget fires an onChange event,
8134 // such as when the user selects a value from the drop down list of a ComboBox,
8135 // which means that the user has finished entering the value and we should save.
8136 // tags:
8137 // private
8138
8139 if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
8140 fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
a089699c 8141 }
a089699c 8142 },
a089699c 8143
1354d172
AD
8144 enableSave: function(){
8145 // summary:
8146 // User overridable function returning a Boolean to indicate
8147 // if the Save button should be enabled or not - usually due to invalid conditions
8148 // tags:
8149 // extension
8150 return (
8151 this.editWidget.isValid
8152 ? this.editWidget.isValid()
8153 : true
8154 );
8155 },
a089699c 8156
1354d172
AD
8157 focus: function(){
8158 // summary:
8159 // Focus the edit widget.
8160 // tags:
8161 // protected
a089699c 8162
1354d172
AD
8163 this.editWidget.focus();
8164 setTimeout(lang.hitch(this, function(){
8165 if(this.editWidget.focusNode && this.editWidget.focusNode.tagName == "INPUT"){
8166 _TextBoxMixin.selectInputText(this.editWidget.focusNode);
8167 }
8168 }), 0);
8169 }
8170});
a089699c
AD
8171
8172
1354d172
AD
8173var InlineEditBox = declare("dijit.InlineEditBox", _Widget, {
8174 // summary:
8175 // An element with in-line edit capabilities
8176 //
8177 // description:
8178 // Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
8179 // when you click it, an editor shows up in place of the original
8180 // text. Optionally, Save and Cancel button are displayed below the edit widget.
8181 // When Save is clicked, the text is pulled from the edit
8182 // widget and redisplayed and the edit widget is again hidden.
8183 // By default a plain Textarea widget is used as the editor (or for
8184 // inline values a TextBox), but you can specify an editor such as
8185 // dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
8186 // An edit widget must support the following API to be used:
8187 // - displayedValue or value as initialization parameter,
8188 // and available through set('displayedValue') / set('value')
8189 // - void focus()
8190 // - DOM-node focusNode = node containing editable text
a089699c 8191
1354d172
AD
8192 // editing: [readonly] Boolean
8193 // Is the node currently in edit mode?
8194 editing: false,
a089699c 8195
1354d172
AD
8196 // autoSave: Boolean
8197 // Changing the value automatically saves it; don't have to push save button
8198 // (and save button isn't even displayed)
8199 autoSave: true,
a089699c 8200
1354d172
AD
8201 // buttonSave: String
8202 // Save button label
8203 buttonSave: "",
a089699c 8204
1354d172
AD
8205 // buttonCancel: String
8206 // Cancel button label
8207 buttonCancel: "",
8208
8209 // renderAsHtml: Boolean
8210 // Set this to true if the specified Editor's value should be interpreted as HTML
8211 // rather than plain text (ex: `dijit.Editor`)
8212 renderAsHtml: false,
8213
8214 // editor: String|Function
8215 // Class name (or reference to the Class) for Editor widget
8216 editor: TextBox,
8217
8218 // editorWrapper: String|Function
8219 // Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
8220 // buttons.
8221 editorWrapper: InlineEditor,
8222
8223 // editorParams: Object
8224 // Set of parameters for editor, like {required: true}
8225 editorParams: {},
8226
8227 // disabled: Boolean
8228 // If true, clicking the InlineEditBox to edit it will have no effect.
8229 disabled: false,
8230
8231 onChange: function(/*===== value =====*/){
a089699c 8232 // summary:
1354d172
AD
8233 // Set this handler to be notified of changes to value.
8234 // tags:
8235 // callback
a089699c
AD
8236 },
8237
1354d172
AD
8238 onCancel: function(){
8239 // summary:
8240 // Set this handler to be notified when editing is cancelled.
8241 // tags:
8242 // callback
a089699c
AD
8243 },
8244
1354d172
AD
8245 // width: String
8246 // Width of editor. By default it's width=100% (ie, block mode).
8247 width: "100%",
8248
8249 // value: String
8250 // The display value of the widget in read-only mode
8251 value: "",
8252
8253 // noValueIndicator: [const] String
8254 // The text that gets displayed when there is no value (so that the user has a place to click to edit)
8255 noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
8256 "<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
8257 "<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // // &#160; == &nbsp;
8258
8259 constructor: function(){
a089699c 8260 // summary:
1354d172
AD
8261 // Sets up private arrays etc.
8262 // tags:
8263 // private
8264 this.editorParams = {};
a089699c 8265 },
1354d172
AD
8266
8267 postMixInProperties: function(){
8268 this.inherited(arguments);
8269
8270 // save pointer to original source node, since Widget nulls-out srcNodeRef
8271 this.displayNode = this.srcNodeRef;
8272
8273 // connect handlers to the display node
8274 var events = {
8275 ondijitclick: "_onClick",
8276 onmouseover: "_onMouseOver",
8277 onmouseout: "_onMouseOut",
8278 onfocus: "_onMouseOver",
8279 onblur: "_onMouseOut"
8280 };
8281 for(var name in events){
8282 this.connect(this.displayNode, name, events[name]);
a089699c 8283 }
1354d172
AD
8284 this.displayNode.setAttribute("role", "button");
8285 if(!this.displayNode.getAttribute("tabIndex")){
8286 this.displayNode.setAttribute("tabIndex", 0);
a089699c 8287 }
1354d172
AD
8288
8289 if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
8290 this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
8291 (this.displayNode.innerText||this.displayNode.textContent||""));
a089699c 8292 }
1354d172
AD
8293 if(!this.value){
8294 this.displayNode.innerHTML = this.noValueIndicator;
a089699c 8295 }
1354d172
AD
8296
8297 domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
a089699c 8298 },
1354d172
AD
8299
8300 setDisabled: function(/*Boolean*/ disabled){
a089699c 8301 // summary:
1354d172
AD
8302 // Deprecated. Use set('disabled', ...) instead.
8303 // tags:
8304 // deprecated
8305 kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
8306 this.set('disabled', disabled);
a089699c 8307 },
1354d172
AD
8308
8309 _setDisabledAttr: function(/*Boolean*/ disabled){
a089699c 8310 // summary:
1354d172
AD
8311 // Hook to make set("disabled", ...) work.
8312 // Set disabled state of widget.
8313 this.domNode.setAttribute("aria-disabled", disabled);
8314 if(disabled){
8315 this.displayNode.removeAttribute("tabIndex");
8316 }else{
8317 this.displayNode.setAttribute("tabIndex", 0);
8318 }
8319 domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
8320 this._set("disabled", disabled);
a089699c 8321 },
1354d172
AD
8322
8323 _onMouseOver: function(){
a089699c 8324 // summary:
1354d172
AD
8325 // Handler for onmouseover and onfocus event.
8326 // tags:
8327 // private
8328 if(!this.disabled){
8329 domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
8330 }
a089699c 8331 },
1354d172
AD
8332
8333 _onMouseOut: function(){
a089699c 8334 // summary:
1354d172
AD
8335 // Handler for onmouseout and onblur event.
8336 // tags:
8337 // private
8338 domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
a089699c 8339 },
1354d172
AD
8340
8341 _onClick: function(/*Event*/ e){
a089699c 8342 // summary:
1354d172
AD
8343 // Handler for onclick event.
8344 // tags:
8345 // private
8346 if(this.disabled){ return; }
8347 if(e){ event.stop(e); }
8348 this._onMouseOut();
8349
8350 // Since FF gets upset if you move a node while in an event handler for that node...
8351 setTimeout(lang.hitch(this, "edit"), 0);
a089699c 8352 },
1354d172
AD
8353
8354 edit: function(){
a089699c 8355 // summary:
1354d172
AD
8356 // Display the editor widget in place of the original (read only) markup.
8357 // tags:
8358 // private
a089699c 8359
1354d172
AD
8360 if(this.disabled || this.editing){ return; }
8361 this._set('editing', true);
a089699c 8362
1354d172
AD
8363 // save some display node values that can be restored later
8364 this._savedPosition = domStyle.get(this.displayNode, "position") || "static";
8365 this._savedOpacity = domStyle.get(this.displayNode, "opacity") || "1";
8366 this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
a089699c 8367
1354d172
AD
8368 if(this.wrapperWidget){
8369 var ew = this.wrapperWidget.editWidget;
8370 ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
8371 }else{
8372 // Placeholder for edit widget
8373 // Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
8374 // when Calendar dropdown appears, which happens automatically on focus.
8375 var placeholder = domConstruct.create("span", null, this.domNode, "before");
a089699c 8376
1354d172
AD
8377 // Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
8378 var ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
8379 this.wrapperWidget = new ewc({
8380 value: this.value,
8381 buttonSave: this.buttonSave,
8382 buttonCancel: this.buttonCancel,
8383 dir: this.dir,
8384 lang: this.lang,
8385 tabIndex: this._savedTabIndex,
8386 editor: this.editor,
8387 inlineEditBox: this,
8388 sourceStyle: domStyle.getComputedStyle(this.displayNode),
8389 save: lang.hitch(this, "save"),
8390 cancel: lang.hitch(this, "cancel"),
8391 textDir: this.textDir
8392 }, placeholder);
8393 if(!this._started){
8394 this.startup();
8395 }
8396 }
8397 var ww = this.wrapperWidget;
a089699c 8398
1354d172
AD
8399 // to avoid screen jitter, we first create the editor with position:absolute, visibility:hidden,
8400 // and then when it's finished rendering, we switch from display mode to editor
8401 // position:absolute releases screen space allocated to the display node
8402 // opacity:0 is the same as visibility:hidden but is still focusable
8403 // visiblity:hidden removes focus outline
a089699c 8404
1354d172
AD
8405 domStyle.set(this.displayNode, { position: "absolute", opacity: "0" }); // makes display node invisible, display style used for focus-ability
8406 domStyle.set(ww.domNode, { position: this._savedPosition, visibility: "visible", opacity: "1" });
8407 domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
a089699c 8408
1354d172
AD
8409 // Replace the display widget with edit widget, leaving them both displayed for a brief time so that
8410 // focus can be shifted without incident. (browser may needs some time to render the editor.)
8411 setTimeout(lang.hitch(ww, function(){
8412 this.focus(); // both nodes are showing, so we can switch focus safely
8413 this._resetValue = this.getValue();
8414 }), 0);
a089699c
AD
8415 },
8416
1354d172 8417 _onBlur: function(){
a089699c 8418 // summary:
1354d172
AD
8419 // Called when focus moves outside the InlineEditBox.
8420 // Performs garbage collection.
8421 // tags:
8422 // private
8423
8424 this.inherited(arguments);
8425 if(!this.editing){
8426 /* causes IE focus problems, see TooltipDialog_a11y.html...
8427 setTimeout(lang.hitch(this, function(){
8428 if(this.wrapperWidget){
8429 this.wrapperWidget.destroy();
8430 delete this.wrapperWidget;
8431 }
8432 }), 0);
8433 */
8434 }
a089699c 8435 },
1354d172
AD
8436
8437 destroy: function(){
8438 if(this.wrapperWidget && !this.wrapperWidget._destroyed){
8439 this.wrapperWidget.destroy();
8440 delete this.wrapperWidget;
8441 }
8442 this.inherited(arguments);
8443 },
8444
8445 _showText: function(/*Boolean*/ focus){
a089699c 8446 // summary:
1354d172
AD
8447 // Revert to display mode, and optionally focus on display node
8448 // tags:
8449 // private
8450
8451 var ww = this.wrapperWidget;
8452 domStyle.set(ww.domNode, { position: "absolute", visibility: "hidden", opacity: "0" }); // hide the editor from mouse/keyboard events
8453 domStyle.set(this.displayNode, { position: this._savedPosition, opacity: this._savedOpacity }); // make the original text visible
8454 domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
8455 if(focus){
8456 fm.focus(this.displayNode);
a089699c
AD
8457 }
8458 },
1354d172
AD
8459
8460 save: function(/*Boolean*/ focus){
a089699c 8461 // summary:
1354d172
AD
8462 // Save the contents of the editor and revert to display mode.
8463 // focus: Boolean
8464 // Focus on the display mode text
8465 // tags:
8466 // private
a089699c 8467
1354d172
AD
8468 if(this.disabled || !this.editing){ return; }
8469 this._set('editing', false);
a089699c 8470
1354d172
AD
8471 var ww = this.wrapperWidget;
8472 var value = ww.getValue();
8473 this.set('value', value); // display changed, formatted value
8474
8475 this._showText(focus); // set focus as needed
a089699c
AD
8476 },
8477
1354d172 8478 setValue: function(/*String*/ val){
a089699c 8479 // summary:
1354d172
AD
8480 // Deprecated. Use set('value', ...) instead.
8481 // tags:
8482 // deprecated
8483 kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
8484 return this.set("value", val);
8485 },
a089699c 8486
1354d172
AD
8487 _setValueAttr: function(/*String*/ val){
8488 // summary:
8489 // Hook to make set("value", ...) work.
8490 // Inserts specified HTML value into this node, or an "input needed" character if node is blank.
a089699c 8491
1354d172
AD
8492 val = lang.trim(val);
8493 var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
8494 this.displayNode.innerHTML = renderVal || this.noValueIndicator;
8495 this._set("value", val);
a089699c 8496
1354d172
AD
8497 if(this._started){
8498 // tell the world that we have changed
8499 setTimeout(lang.hitch(this, "onChange", val), 0); // setTimeout prevents browser freeze for long-running event handlers
8500 }
8501 // contextual (auto) text direction depends on the text value
8502 if(this.textDir == "auto"){
8503 this.applyTextDir(this.displayNode, this.displayNode.innerText);
8504 }
a089699c
AD
8505 },
8506
1354d172 8507 getValue: function(){
a089699c 8508 // summary:
1354d172
AD
8509 // Deprecated. Use get('value') instead.
8510 // tags:
8511 // deprecated
8512 kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
8513 return this.get("value");
8514 },
8515
8516 cancel: function(/*Boolean*/ focus){
8517 // summary:
8518 // Revert to display mode, discarding any changes made in the editor
8519 // tags:
8520 // private
8521
8522 if(this.disabled || !this.editing){ return; }
8523 this._set('editing', false);
8524
8525 // tell the world that we have no changes
8526 setTimeout(lang.hitch(this, "onCancel"), 0); // setTimeout prevents browser freeze for long-running event handlers
8527
8528 this._showText(focus);
8529 },
8530 _setTextDirAttr: function(/*String*/ textDir){
8531 // summary:
8532 // Setter for textDir.
8533 // description:
8534 // Users shouldn't call this function; they should be calling
8535 // set('textDir', value)
8536 // tags:
8537 // private
8538 if(!this._created || this.textDir != textDir){
8539 this._set("textDir", textDir);
8540 this.applyTextDir(this.displayNode, this.displayNode.innerText);
8541 this.displayNode.align = this.dir == "rtl" ? "right" : "left"; //fix the text alignment
8542 }
8543 }
a089699c
AD
8544});
8545
1354d172 8546InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
a089699c 8547
1354d172
AD
8548return InlineEditBox;
8549});
8550},
8551'dojo/selector/acme':function(){
8552define("dojo/selector/acme", ["../_base/kernel", "../has", "../dom", "../_base/sniff", "../_base/array", "../_base/lang", "../_base/window"], function(dojo, has, dom){
8553 // module:
8554 // dojo/selector/acme
8555 // summary:
8556 // This module defines the Acme selector engine
a089699c 8557
1354d172
AD
8558/*
8559 acme architectural overview:
8560
8561 acme is a relatively full-featured CSS3 query library. It is
8562 designed to take any valid CSS3 selector and return the nodes matching
8563 the selector. To do this quickly, it processes queries in several
8564 steps, applying caching where profitable.
8565
8566 The steps (roughly in reverse order of the way they appear in the code):
8567 1.) check to see if we already have a "query dispatcher"
8568 - if so, use that with the given parameterization. Skip to step 4.
8569 2.) attempt to determine which branch to dispatch the query to:
8570 - JS (optimized DOM iteration)
8571 - native (FF3.1+, Safari 3.1+, IE 8+)
8572 3.) tokenize and convert to executable "query dispatcher"
8573 - this is where the lion's share of the complexity in the
8574 system lies. In the DOM version, the query dispatcher is
8575 assembled as a chain of "yes/no" test functions pertaining to
8576 a section of a simple query statement (".blah:nth-child(odd)"
8577 but not "div div", which is 2 simple statements). Individual
8578 statement dispatchers are cached (to prevent re-definition)
8579 as are entire dispatch chains (to make re-execution of the
8580 same query fast)
8581 4.) the resulting query dispatcher is called in the passed scope
8582 (by default the top-level document)
8583 - for DOM queries, this results in a recursive, top-down
8584 evaluation of nodes based on each simple query section
8585 - for native implementations, this may mean working around spec
8586 bugs. So be it.
8587 5.) matched nodes are pruned to ensure they are unique (if necessary)
8588*/
a089699c 8589
a089699c 8590
1354d172
AD
8591 ////////////////////////////////////////////////////////////////////////
8592 // Toolkit aliases
8593 ////////////////////////////////////////////////////////////////////////
a089699c 8594
1354d172
AD
8595 // if you are extracting acme for use in your own system, you will
8596 // need to provide these methods and properties. No other porting should be
8597 // necessary, save for configuring the system to use a class other than
8598 // dojo.NodeList as the return instance instantiator
8599 var trim = dojo.trim;
8600 var each = dojo.forEach;
8601 // d.isIE; // float
8602 // d.isSafari; // float
8603 // d.isOpera; // float
8604 // d.isWebKit; // float
8605 // d.doc ; // document element
a089699c 8606
1354d172
AD
8607 var getDoc = function(){ return dojo.doc; };
8608 // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
8609 var cssCaseBug = ((dojo.isWebKit||dojo.isMozilla) && ((getDoc().compatMode) == "BackCompat"));
a089699c 8610
1354d172
AD
8611 ////////////////////////////////////////////////////////////////////////
8612 // Global utilities
8613 ////////////////////////////////////////////////////////////////////////
8614
8615
8616 var specials = ">~+";
8617
8618 // global thunk to determine whether we should treat the current query as
8619 // case sensitive or not. This switch is flipped by the query evaluator
8620 // based on the document passed as the context to search.
8621 var caseSensitive = false;
8622
8623 // how high?
8624 var yesman = function(){ return true; };
8625
8626 ////////////////////////////////////////////////////////////////////////
8627 // Tokenizer
8628 ////////////////////////////////////////////////////////////////////////
8629
8630 var getQueryParts = function(query){
8631 // summary:
8632 // state machine for query tokenization
8633 // description:
8634 // instead of using a brittle and slow regex-based CSS parser,
8635 // acme implements an AST-style query representation. This
8636 // representation is only generated once per query. For example,
8637 // the same query run multiple times or under different root nodes
8638 // does not re-parse the selector expression but instead uses the
8639 // cached data structure. The state machine implemented here
8640 // terminates on the last " " (space) character and returns an
8641 // ordered array of query component structures (or "parts"). Each
8642 // part represents an operator or a simple CSS filtering
8643 // expression. The structure for parts is documented in the code
8644 // below.
8645
8646
8647 // NOTE:
8648 // this code is designed to run fast and compress well. Sacrifices
8649 // to readability and maintainability have been made. Your best
8650 // bet when hacking the tokenizer is to put The Donnas on *really*
8651 // loud (may we recommend their "Spend The Night" release?) and
8652 // just assume you're gonna make mistakes. Keep the unit tests
8653 // open and run them frequently. Knowing is half the battle ;-)
8654 if(specials.indexOf(query.slice(-1)) >= 0){
8655 // if we end with a ">", "+", or "~", that means we're implicitly
8656 // searching all children, so make it explicit
8657 query += " * "
8658 }else{
8659 // if you have not provided a terminator, one will be provided for
8660 // you...
8661 query += " ";
8662 }
8663
8664 var ts = function(/*Integer*/ s, /*Integer*/ e){
8665 // trim and slice.
8666
8667 // take an index to start a string slice from and an end position
8668 // and return a trimmed copy of that sub-string
8669 return trim(query.slice(s, e));
8670 };
8671
8672 // the overall data graph of the full query, as represented by queryPart objects
8673 var queryParts = [];
8674
8675
8676 // state keeping vars
8677 var inBrackets = -1, inParens = -1, inMatchFor = -1,
8678 inPseudo = -1, inClass = -1, inId = -1, inTag = -1,
8679 lc = "", cc = "", pStart;
8680
8681 // iteration vars
8682 var x = 0, // index in the query
8683 ql = query.length,
8684 currentPart = null, // data structure representing the entire clause
8685 _cp = null; // the current pseudo or attr matcher
8686
8687 // several temporary variables are assigned to this structure during a
8688 // potential sub-expression match:
8689 // attr:
8690 // a string representing the current full attribute match in a
8691 // bracket expression
8692 // type:
8693 // if there's an operator in a bracket expression, this is
8694 // used to keep track of it
8695 // value:
8696 // the internals of parenthetical expression for a pseudo. for
8697 // :nth-child(2n+1), value might be "2n+1"
8698
8699 var endTag = function(){
8700 // called when the tokenizer hits the end of a particular tag name.
8701 // Re-sets state variables for tag matching and sets up the matcher
8702 // to handle the next type of token (tag or operator).
8703 if(inTag >= 0){
8704 var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
8705 currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
8706 inTag = -1;
a089699c 8707 }
1354d172
AD
8708 };
8709
8710 var endId = function(){
8711 // called when the tokenizer might be at the end of an ID portion of a match
8712 if(inId >= 0){
8713 currentPart.id = ts(inId, x).replace(/\\/g, "");
8714 inId = -1;
a089699c 8715 }
1354d172
AD
8716 };
8717
8718 var endClass = function(){
8719 // called when the tokenizer might be at the end of a class name
8720 // match. CSS allows for multiple classes, so we augment the
8721 // current item with another class in its list
8722 if(inClass >= 0){
8723 currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
8724 inClass = -1;
a089699c 8725 }
1354d172 8726 };
a089699c 8727
1354d172
AD
8728 var endAll = function(){
8729 // at the end of a simple fragment, so wall off the matches
8730 endId();
8731 endTag();
8732 endClass();
8733 };
a089699c 8734
1354d172
AD
8735 var endPart = function(){
8736 endAll();
8737 if(inPseudo >= 0){
8738 currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
8739 }
8740 // hint to the selector engine to tell it whether or not it
8741 // needs to do any iteration. Many simple selectors don't, and
8742 // we can avoid significant construction-time work by advising
8743 // the system to skip them
8744 currentPart.loops = (
8745 currentPart.pseudos.length ||
8746 currentPart.attrs.length ||
8747 currentPart.classes.length );
8748
8749 currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
8750
8751
8752 // otag/tag are hints to suggest to the system whether or not
8753 // it's an operator or a tag. We save a copy of otag since the
8754 // tag name is cast to upper-case in regular HTML matches. The
8755 // system has a global switch to figure out if the current
8756 // expression needs to be case sensitive or not and it will use
8757 // otag or tag accordingly
8758 currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
8759
8760 if(currentPart.tag){
8761 // if we're in a case-insensitive HTML doc, we likely want
8762 // the toUpperCase when matching on element.tagName. If we
8763 // do it here, we can skip the string op per node
8764 // comparison
8765 currentPart.tag = currentPart.tag.toUpperCase();
8766 }
8767
8768 // add the part to the list
8769 if(queryParts.length && (queryParts[queryParts.length-1].oper)){
8770 // operators are always infix, so we remove them from the
8771 // list and attach them to the next match. The evaluator is
8772 // responsible for sorting out how to handle them.
8773 currentPart.infixOper = queryParts.pop();
8774 currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
8775 /*
8776 console.debug( "swapping out the infix",
8777 currentPart.infixOper,
8778 "and attaching it to",
8779 currentPart);
8780 */
8781 }
8782 queryParts.push(currentPart);
8783
8784 currentPart = null;
8785 };
a089699c 8786
1354d172
AD
8787 // iterate over the query, character by character, building up a
8788 // list of query part objects
8789 for(; lc=cc, cc=query.charAt(x), x < ql; x++){
8790 // cc: the current character in the match
8791 // lc: the last character (if any)
8792
8793 // someone is trying to escape something, so don't try to match any
8794 // fragments. We assume we're inside a literal.
8795 if(lc == "\\"){ continue; }
8796 if(!currentPart){ // a part was just ended or none has yet been created
8797 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
8798 pStart = x;
8799 // rules describe full CSS sub-expressions, like:
8800 // #someId
8801 // .className:first-child
8802 // but not:
8803 // thinger > div.howdy[type=thinger]
8804 // the indidual components of the previous query would be
8805 // split into 3 parts that would be represented a structure
8806 // like:
8807 // [
8808 // {
8809 // query: "thinger",
8810 // tag: "thinger",
8811 // },
8812 // {
8813 // query: "div.howdy[type=thinger]",
8814 // classes: ["howdy"],
8815 // infixOper: {
8816 // query: ">",
8817 // oper: ">",
8818 // }
8819 // },
8820 // ]
8821 currentPart = {
8822 query: null, // the full text of the part's rule
8823 pseudos: [], // CSS supports multiple pseud-class matches in a single rule
8824 attrs: [], // CSS supports multi-attribute match, so we need an array
8825 classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
8826 tag: null, // only one tag...
8827 oper: null, // ...or operator per component. Note that these wind up being exclusive.
8828 id: null, // the id component of a rule
8829 getTag: function(){
8830 return (caseSensitive) ? this.otag : this.tag;
8831 }
8832 };
a089699c 8833
1354d172
AD
8834 // if we don't have a part, we assume we're going to start at
8835 // the beginning of a match, which should be a tag name. This
8836 // might fault a little later on, but we detect that and this
8837 // iteration will still be fine.
8838 inTag = x;
8839 }
8840
8841 if(inBrackets >= 0){
8842 // look for a the close first
8843 if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
8844 if(!_cp.attr){
8845 // no attribute match was previously begun, so we
8846 // assume this is an attribute existence match in the
8847 // form of [someAttributeName]
8848 _cp.attr = ts(inBrackets+1, x);
8849 }else{
8850 // we had an attribute already, so we know that we're
8851 // matching some sort of value, as in [attrName=howdy]
8852 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
8853 }
8854 var cmf = _cp.matchFor;
8855 if(cmf){
8856 // try to strip quotes from the matchFor value. We want
8857 // [attrName=howdy] to match the same
8858 // as [attrName = 'howdy' ]
8859 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
8860 _cp.matchFor = cmf.slice(1, -1);
8861 }
8862 }
8863 // end the attribute by adding it to the list of attributes.
8864 currentPart.attrs.push(_cp);
8865 _cp = null; // necessary?
8866 inBrackets = inMatchFor = -1;
8867 }else if(cc == "="){
8868 // if the last char was an operator prefix, make sure we
8869 // record it along with the "=" operator.
8870 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
8871 _cp.type = addToCc+cc;
8872 _cp.attr = ts(inBrackets+1, x-addToCc.length);
8873 inMatchFor = x+1;
8874 }
8875 // now look for other clause parts
8876 }else if(inParens >= 0){
8877 // if we're in a parenthetical expression, we need to figure
8878 // out if it's attached to a pseudo-selector rule like
8879 // :nth-child(1)
8880 if(cc == ")"){
8881 if(inPseudo >= 0){
8882 _cp.value = ts(inParens+1, x);
8883 }
8884 inPseudo = inParens = -1;
8885 }
8886 }else if(cc == "#"){
8887 // start of an ID match
8888 endAll();
8889 inId = x+1;
8890 }else if(cc == "."){
8891 // start of a class match
8892 endAll();
8893 inClass = x;
8894 }else if(cc == ":"){
8895 // start of a pseudo-selector match
8896 endAll();
8897 inPseudo = x;
8898 }else if(cc == "["){
8899 // start of an attribute match.
8900 endAll();
8901 inBrackets = x;
8902 // provide a new structure for the attribute match to fill-in
8903 _cp = {
8904 /*=====
8905 attr: null, type: null, matchFor: null
8906 =====*/
8907 };
8908 }else if(cc == "("){
8909 // we really only care if we've entered a parenthetical
8910 // expression if we're already inside a pseudo-selector match
8911 if(inPseudo >= 0){
8912 // provide a new structure for the pseudo match to fill-in
8913 _cp = {
8914 name: ts(inPseudo+1, x),
8915 value: null
8916 };
8917 currentPart.pseudos.push(_cp);
8918 }
8919 inParens = x;
8920 }else if(
8921 (cc == " ") &&
8922 // if it's a space char and the last char is too, consume the
8923 // current one without doing more work
8924 (lc != cc)
8925 ){
8926 endPart();
8927 }
8928 }
8929 return queryParts;
8930 };
a089699c 8931
a089699c 8932
1354d172
AD
8933 ////////////////////////////////////////////////////////////////////////
8934 // DOM query infrastructure
8935 ////////////////////////////////////////////////////////////////////////
a089699c 8936
1354d172
AD
8937 var agree = function(first, second){
8938 // the basic building block of the yes/no chaining system. agree(f1,
8939 // f2) generates a new function which returns the boolean results of
8940 // both of the passed functions to a single logical-anded result. If
8941 // either are not passed, the other is used exclusively.
8942 if(!first){ return second; }
8943 if(!second){ return first; }
81bea17a 8944
1354d172
AD
8945 return function(){
8946 return first.apply(window, arguments) && second.apply(window, arguments);
8947 }
8948 };
a089699c 8949
1354d172
AD
8950 var getArr = function(i, arr){
8951 // helps us avoid array alloc when we don't need it
8952 var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
8953 if(i){ r.push(i); }
8954 return r;
8955 };
a089699c 8956
1354d172 8957 var _isElement = function(n){ return (1 == n.nodeType); };
a089699c 8958
1354d172
AD
8959 // FIXME: need to coalesce _getAttr with defaultGetter
8960 var blank = "";
8961 var _getAttr = function(elem, attr){
8962 if(!elem){ return blank; }
8963 if(attr == "class"){
8964 return elem.className || blank;
8965 }
8966 if(attr == "for"){
8967 return elem.htmlFor || blank;
8968 }
8969 if(attr == "style"){
8970 return elem.style.cssText || blank;
8971 }
8972 return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
8973 };
8974
8975 var attrs = {
8976 "*=": function(attr, value){
8977 return function(elem){
8978 // E[foo*="bar"]
8979 // an E element whose "foo" attribute value contains
8980 // the substring "bar"
8981 return (_getAttr(elem, attr).indexOf(value)>=0);
8982 }
8983 },
8984 "^=": function(attr, value){
8985 // E[foo^="bar"]
8986 // an E element whose "foo" attribute value begins exactly
8987 // with the string "bar"
8988 return function(elem){
8989 return (_getAttr(elem, attr).indexOf(value)==0);
8990 }
8991 },
8992 "$=": function(attr, value){
8993 // E[foo$="bar"]
8994 // an E element whose "foo" attribute value ends exactly
8995 // with the string "bar"
8996 return function(elem){
8997 var ea = " "+_getAttr(elem, attr);
8998 return (ea.lastIndexOf(value)==(ea.length-value.length));
8999 }
9000 },
9001 "~=": function(attr, value){
9002 // E[foo~="bar"]
9003 // an E element whose "foo" attribute value is a list of
9004 // space-separated values, one of which is exactly equal
9005 // to "bar"
9006
9007 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
9008 var tval = " "+value+" ";
9009 return function(elem){
9010 var ea = " "+_getAttr(elem, attr)+" ";
9011 return (ea.indexOf(tval)>=0);
9012 }
9013 },
9014 "|=": function(attr, value){
9015 // E[hreflang|="en"]
9016 // an E element whose "hreflang" attribute has a
9017 // hyphen-separated list of values beginning (from the
9018 // left) with "en"
9019 var valueDash = value+"-";
9020 return function(elem){
9021 var ea = _getAttr(elem, attr);
9022 return (
9023 (ea == value) ||
9024 (ea.indexOf(valueDash)==0)
9025 );
9026 }
a089699c 9027 },
1354d172
AD
9028 "=": function(attr, value){
9029 return function(elem){
9030 return (_getAttr(elem, attr) == value);
9031 }
9032 }
9033 };
a089699c 9034
1354d172
AD
9035 // avoid testing for node type if we can. Defining this in the negative
9036 // here to avoid negation in the fast path.
9037 var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
9038 var _ns = !_noNES ? "nextElementSibling" : "nextSibling";
9039 var _ps = !_noNES ? "previousElementSibling" : "previousSibling";
9040 var _simpleNodeTest = (_noNES ? _isElement : yesman);
a089699c 9041
1354d172
AD
9042 var _lookLeft = function(node){
9043 // look left
9044 while(node = node[_ps]){
9045 if(_simpleNodeTest(node)){ return false; }
9046 }
9047 return true;
9048 };
9049
9050 var _lookRight = function(node){
9051 // look right
9052 while(node = node[_ns]){
9053 if(_simpleNodeTest(node)){ return false; }
9054 }
9055 return true;
9056 };
9057
9058 var getNodeIndex = function(node){
9059 var root = node.parentNode;
9060 var i = 0,
9061 tret = root.children || root.childNodes,
9062 ci = (node["_i"]||-1),
9063 cl = (root["_l"]||-1);
9064
9065 if(!tret){ return -1; }
9066 var l = tret.length;
9067
9068 // we calculate the parent length as a cheap way to invalidate the
9069 // cache. It's not 100% accurate, but it's much more honest than what
9070 // other libraries do
9071 if( cl == l && ci >= 0 && cl >= 0 ){
9072 // if it's legit, tag and release
9073 return ci;
9074 }
9075
9076 // else re-key things
9077 root["_l"] = l;
9078 ci = -1;
9079 for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
9080 if(_simpleNodeTest(te)){
9081 te["_i"] = ++i;
9082 if(node === te){
9083 // NOTE:
9084 // shortcutting the return at this step in indexing works
9085 // very well for benchmarking but we avoid it here since
9086 // it leads to potential O(n^2) behavior in sequential
9087 // getNodexIndex operations on a previously un-indexed
9088 // parent. We may revisit this at a later time, but for
9089 // now we just want to get the right answer more often
9090 // than not.
9091 ci = i;
a089699c 9092 }
1354d172
AD
9093 }
9094 }
9095 return ci;
9096 };
a089699c 9097
1354d172
AD
9098 var isEven = function(elem){
9099 return !((getNodeIndex(elem)) % 2);
9100 };
9101
9102 var isOdd = function(elem){
9103 return ((getNodeIndex(elem)) % 2);
9104 };
9105
9106 var pseudos = {
9107 "checked": function(name, condition){
9108 return function(elem){
9109 return !!("checked" in elem ? elem.checked : elem.selected);
9110 }
9111 },
9112 "first-child": function(){ return _lookLeft; },
9113 "last-child": function(){ return _lookRight; },
9114 "only-child": function(name, condition){
9115 return function(node){
9116 return _lookLeft(node) && _lookRight(node);
9117 };
9118 },
9119 "empty": function(name, condition){
9120 return function(elem){
9121 // DomQuery and jQuery get this wrong, oddly enough.
9122 // The CSS 3 selectors spec is pretty explicit about it, too.
9123 var cn = elem.childNodes;
9124 var cnl = elem.childNodes.length;
9125 // if(!cnl){ return true; }
9126 for(var x=cnl-1; x >= 0; x--){
9127 var nt = cn[x].nodeType;
9128 if((nt === 1)||(nt == 3)){ return false; }
a089699c 9129 }
1354d172
AD
9130 return true;
9131 }
9132 },
9133 "contains": function(name, condition){
9134 var cz = condition.charAt(0);
9135 if( cz == '"' || cz == "'" ){ //remove quote
9136 condition = condition.slice(1, -1);
9137 }
9138 return function(elem){
9139 return (elem.innerHTML.indexOf(condition) >= 0);
9140 }
9141 },
9142 "not": function(name, condition){
9143 var p = getQueryParts(condition)[0];
9144 var ignores = { el: 1 };
9145 if(p.tag != "*"){
9146 ignores.tag = 1;
9147 }
9148 if(!p.classes.length){
9149 ignores.classes = 1;
9150 }
9151 var ntf = getSimpleFilterFunc(p, ignores);
9152 return function(elem){
9153 return (!ntf(elem));
9154 }
9155 },
9156 "nth-child": function(name, condition){
9157 var pi = parseInt;
9158 // avoid re-defining function objects if we can
9159 if(condition == "odd"){
9160 return isOdd;
9161 }else if(condition == "even"){
9162 return isEven;
9163 }
9164 // FIXME: can we shorten this?
9165 if(condition.indexOf("n") != -1){
9166 var tparts = condition.split("n", 2);
9167 var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
9168 var idx = tparts[1] ? pi(tparts[1]) : 0;
9169 var lb = 0, ub = -1;
9170 if(pred > 0){
9171 if(idx < 0){
9172 idx = (idx % pred) && (pred + (idx % pred));
9173 }else if(idx>0){
9174 if(idx >= pred){
9175 lb = idx - idx % pred;
9176 }
9177 idx = idx % pred;
9178 }
9179 }else if(pred<0){
9180 pred *= -1;
9181 // idx has to be greater than 0 when pred is negative;
9182 // shall we throw an error here?
9183 if(idx > 0){
9184 ub = idx;
9185 idx = idx % pred;
9186 }
a089699c 9187 }
1354d172
AD
9188 if(pred > 0){
9189 return function(elem){
9190 var i = getNodeIndex(elem);
9191 return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
9192 }
a089699c 9193 }else{
1354d172 9194 condition = idx;
a089699c
AD
9195 }
9196 }
1354d172
AD
9197 var ncount = pi(condition);
9198 return function(elem){
9199 return (getNodeIndex(elem) == ncount);
9200 }
9201 }
9202 };
a089699c 9203
1354d172
AD
9204 var defaultGetter = (dojo.isIE && (dojo.isIE < 9 || dojo.isQuirks)) ? function(cond){
9205 var clc = cond.toLowerCase();
9206 if(clc == "class"){ cond = "className"; }
9207 return function(elem){
9208 return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
9209 }
9210 } : function(cond){
9211 return function(elem){
9212 return (elem && elem.getAttribute && elem.hasAttribute(cond));
9213 }
9214 };
a089699c 9215
1354d172
AD
9216 var getSimpleFilterFunc = function(query, ignores){
9217 // generates a node tester function based on the passed query part. The
9218 // query part is one of the structures generated by the query parser
9219 // when it creates the query AST. The "ignores" object specifies which
9220 // (if any) tests to skip, allowing the system to avoid duplicating
9221 // work where it may have already been taken into account by other
9222 // factors such as how the nodes to test were fetched in the first
9223 // place
9224 if(!query){ return yesman; }
9225 ignores = ignores||{};
a089699c 9226
1354d172 9227 var ff = null;
a089699c 9228
1354d172
AD
9229 if(!("el" in ignores)){
9230 ff = agree(ff, _isElement);
9231 }
9232
9233 if(!("tag" in ignores)){
9234 if(query.tag != "*"){
9235 ff = agree(ff, function(elem){
9236 return (elem && (elem.tagName == query.getTag()));
9237 });
9238 }
9239 }
9240
9241 if(!("classes" in ignores)){
9242 each(query.classes, function(cname, idx, arr){
9243 // get the class name
9244 /*
9245 var isWildcard = cname.charAt(cname.length-1) == "*";
9246 if(isWildcard){
9247 cname = cname.substr(0, cname.length-1);
a089699c 9248 }
1354d172
AD
9249 // I dislike the regex thing, even if memoized in a cache, but it's VERY short
9250 var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
9251 */
9252 var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)");
9253 ff = agree(ff, function(elem){
9254 return re.test(elem.className);
9255 });
9256 ff.count = idx;
9257 });
9258 }
a089699c 9259
1354d172
AD
9260 if(!("pseudos" in ignores)){
9261 each(query.pseudos, function(pseudo){
9262 var pn = pseudo.name;
9263 if(pseudos[pn]){
9264 ff = agree(ff, pseudos[pn](pn, pseudo.value));
a089699c 9265 }
1354d172
AD
9266 });
9267 }
9268
9269 if(!("attrs" in ignores)){
9270 each(query.attrs, function(attr){
9271 var matcher;
9272 var a = attr.attr;
9273 // type, attr, matchFor
9274 if(attr.type && attrs[attr.type]){
9275 matcher = attrs[attr.type](a, attr.matchFor);
9276 }else if(a.length){
9277 matcher = defaultGetter(a);
a089699c 9278 }
1354d172
AD
9279 if(matcher){
9280 ff = agree(ff, matcher);
9281 }
9282 });
9283 }
a089699c 9284
1354d172
AD
9285 if(!("id" in ignores)){
9286 if(query.id){
9287 ff = agree(ff, function(elem){
9288 return (!!elem && (elem.id == query.id));
9289 });
9290 }
9291 }
a089699c 9292
1354d172
AD
9293 if(!ff){
9294 if(!("default" in ignores)){
9295 ff = yesman;
9296 }
9297 }
9298 return ff;
9299 };
9300
9301 var _nextSibling = function(filterFunc){
9302 return function(node, ret, bag){
9303 while(node = node[_ns]){
9304 if(_noNES && (!_isElement(node))){ continue; }
9305 if(
9306 (!bag || _isUnique(node, bag)) &&
9307 filterFunc(node)
9308 ){
9309 ret.push(node);
a089699c 9310 }
1354d172
AD
9311 break;
9312 }
9313 return ret;
9314 }
9315 };
a089699c 9316
1354d172
AD
9317 var _nextSiblings = function(filterFunc){
9318 return function(root, ret, bag){
9319 var te = root[_ns];
9320 while(te){
9321 if(_simpleNodeTest(te)){
9322 if(bag && !_isUnique(te, bag)){
9323 break;
9324 }
9325 if(filterFunc(te)){
9326 ret.push(te);
9327 }
9328 }
9329 te = te[_ns];
9330 }
9331 return ret;
9332 }
9333 };
81bea17a 9334
1354d172
AD
9335 // get an array of child *elements*, skipping text and comment nodes
9336 var _childElements = function(filterFunc){
9337 filterFunc = filterFunc||yesman;
9338 return function(root, ret, bag){
9339 // get an array of child elements, skipping text and comment nodes
9340 var te, x = 0, tret = root.children || root.childNodes;
9341 while(te = tret[x++]){
9342 if(
9343 _simpleNodeTest(te) &&
9344 (!bag || _isUnique(te, bag)) &&
9345 (filterFunc(te, x))
9346 ){
9347 ret.push(te);
9348 }
9349 }
9350 return ret;
9351 };
9352 };
a089699c 9353
1354d172
AD
9354 /*
9355 // thanks, Dean!
9356 var itemIsAfterRoot = d.isIE ? function(item, root){
9357 return (item.sourceIndex > root.sourceIndex);
9358 } : function(item, root){
9359 return (item.compareDocumentPosition(root) == 2);
9360 };
9361 */
a089699c 9362
1354d172
AD
9363 // test to see if node is below root
9364 var _isDescendant = function(node, root){
9365 var pn = node.parentNode;
9366 while(pn){
9367 if(pn == root){
9368 break;
9369 }
9370 pn = pn.parentNode;
9371 }
9372 return !!pn;
9373 };
a089699c 9374
1354d172
AD
9375 var _getElementsFuncCache = {};
9376
9377 var getElementsFunc = function(query){
9378 var retFunc = _getElementsFuncCache[query.query];
9379 // if we've got a cached dispatcher, just use that
9380 if(retFunc){ return retFunc; }
9381 // else, generate a new on
9382
9383 // NOTE:
9384 // this function returns a function that searches for nodes and
9385 // filters them. The search may be specialized by infix operators
9386 // (">", "~", or "+") else it will default to searching all
9387 // descendants (the " " selector). Once a group of children is
9388 // found, a test function is applied to weed out the ones we
9389 // don't want. Many common cases can be fast-pathed. We spend a
9390 // lot of cycles to create a dispatcher that doesn't do more work
9391 // than necessary at any point since, unlike this function, the
9392 // dispatchers will be called every time. The logic of generating
9393 // efficient dispatchers looks like this in pseudo code:
9394 //
9395 // # if it's a purely descendant query (no ">", "+", or "~" modifiers)
9396 // if infixOperator == " ":
9397 // if only(id):
9398 // return def(root):
9399 // return d.byId(id, root);
9400 //
9401 // elif id:
9402 // return def(root):
9403 // return filter(d.byId(id, root));
9404 //
9405 // elif cssClass && getElementsByClassName:
9406 // return def(root):
9407 // return filter(root.getElementsByClassName(cssClass));
9408 //
9409 // elif only(tag):
9410 // return def(root):
9411 // return root.getElementsByTagName(tagName);
9412 //
9413 // else:
9414 // # search by tag name, then filter
9415 // return def(root):
9416 // return filter(root.getElementsByTagName(tagName||"*"));
9417 //
9418 // elif infixOperator == ">":
9419 // # search direct children
9420 // return def(root):
9421 // return filter(root.children);
9422 //
9423 // elif infixOperator == "+":
9424 // # search next sibling
9425 // return def(root):
9426 // return filter(root.nextElementSibling);
9427 //
9428 // elif infixOperator == "~":
9429 // # search rightward siblings
9430 // return def(root):
9431 // return filter(nextSiblings(root));
9432
9433 var io = query.infixOper;
9434 var oper = (io ? io.oper : "");
9435 // the default filter func which tests for all conditions in the query
9436 // part. This is potentially inefficient, so some optimized paths may
9437 // re-define it to test fewer things.
9438 var filterFunc = getSimpleFilterFunc(query, { el: 1 });
9439 var qt = query.tag;
9440 var wildcardTag = ("*" == qt);
9441 var ecs = getDoc()["getElementsByClassName"];
9442
9443 if(!oper){
9444 // if there's no infix operator, then it's a descendant query. ID
9445 // and "elements by class name" variants can be accelerated so we
9446 // call them out explicitly:
9447 if(query.id){
9448 // testing shows that the overhead of yesman() is acceptable
9449 // and can save us some bytes vs. re-defining the function
9450 // everywhere.
9451 filterFunc = (!query.loops && wildcardTag) ?
9452 yesman :
9453 getSimpleFilterFunc(query, { el: 1, id: 1 });
9454
9455 retFunc = function(root, arr){
9456 var te = dom.byId(query.id, (root.ownerDocument||root));
9457 if(!te || !filterFunc(te)){ return; }
9458 if(9 == root.nodeType){ // if root's a doc, we just return directly
9459 return getArr(te, arr);
9460 }else{ // otherwise check ancestry
9461 if(_isDescendant(te, root)){
9462 return getArr(te, arr);
a089699c
AD
9463 }
9464 }
1354d172
AD
9465 }
9466 }else if(
9467 ecs &&
9468 // isAlien check. Workaround for Prototype.js being totally evil/dumb.
9469 /\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
9470 query.classes.length &&
9471 !cssCaseBug
9472 ){
9473 // it's a class-based query and we've got a fast way to run it.
9474
9475 // ignore class and ID filters since we will have handled both
9476 filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
9477 var classesString = query.classes.join(" ");
9478 retFunc = function(root, arr, bag){
9479 var ret = getArr(0, arr), te, x=0;
9480 var tret = root.getElementsByClassName(classesString);
9481 while((te = tret[x++])){
9482 if(filterFunc(te, root) && _isUnique(te, bag)){
9483 ret.push(te);
a089699c 9484 }
a089699c 9485 }
1354d172
AD
9486 return ret;
9487 };
a089699c 9488
1354d172
AD
9489 }else if(!wildcardTag && !query.loops){
9490 // it's tag only. Fast-path it.
9491 retFunc = function(root, arr, bag){
9492 var ret = getArr(0, arr), te, x=0;
9493 var tret = root.getElementsByTagName(query.getTag());
9494 while((te = tret[x++])){
9495 if(_isUnique(te, bag)){
9496 ret.push(te);
a089699c 9497 }
a089699c 9498 }
1354d172
AD
9499 return ret;
9500 };
9501 }else{
9502 // the common case:
9503 // a descendant selector without a fast path. By now it's got
9504 // to have a tag selector, even if it's just "*" so we query
9505 // by that and filter
9506 filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
9507 retFunc = function(root, arr, bag){
9508 var ret = getArr(0, arr), te, x=0;
9509 // we use getTag() to avoid case sensitivity issues
9510 var tret = root.getElementsByTagName(query.getTag());
9511 while((te = tret[x++])){
9512 if(filterFunc(te, root) && _isUnique(te, bag)){
9513 ret.push(te);
a089699c
AD
9514 }
9515 }
1354d172
AD
9516 return ret;
9517 };
9518 }
9519 }else{
9520 // the query is scoped in some way. Instead of querying by tag we
9521 // use some other collection to find candidate nodes
9522 var skipFilters = { el: 1 };
9523 if(wildcardTag){
9524 skipFilters.tag = 1;
9525 }
9526 filterFunc = getSimpleFilterFunc(query, skipFilters);
9527 if("+" == oper){
9528 retFunc = _nextSibling(filterFunc);
9529 }else if("~" == oper){
9530 retFunc = _nextSiblings(filterFunc);
9531 }else if(">" == oper){
9532 retFunc = _childElements(filterFunc);
9533 }
9534 }
9535 // cache it and return
9536 return _getElementsFuncCache[query.query] = retFunc;
9537 };
a089699c 9538
1354d172
AD
9539 var filterDown = function(root, queryParts){
9540 // NOTE:
9541 // this is the guts of the DOM query system. It takes a list of
9542 // parsed query parts and a root and finds children which match
9543 // the selector represented by the parts
9544 var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
9545
9546 for(var i = 0; i < qpl; i++){
9547 ret = [];
9548 qp = queryParts[i];
9549 x = candidates.length - 1;
9550 if(x > 0){
9551 // if we have more than one root at this level, provide a new
9552 // hash to use for checking group membership but tell the
9553 // system not to post-filter us since we will already have been
9554 // gauranteed to be unique
9555 bag = {};
9556 ret.nozip = true;
9557 }
9558 var gef = getElementsFunc(qp);
9559 for(var j = 0; (te = candidates[j]); j++){
9560 // for every root, get the elements that match the descendant
9561 // selector, adding them to the "ret" array and filtering them
9562 // via membership in this level's bag. If there are more query
9563 // parts, then this level's return will be used as the next
9564 // level's candidates
9565 gef(te, ret, bag);
9566 }
9567 if(!ret.length){ break; }
9568 candidates = ret;
9569 }
9570 return ret;
9571 };
a089699c 9572
1354d172
AD
9573 ////////////////////////////////////////////////////////////////////////
9574 // the query runner
9575 ////////////////////////////////////////////////////////////////////////
a089699c 9576
1354d172
AD
9577 // these are the primary caches for full-query results. The query
9578 // dispatcher functions are generated then stored here for hash lookup in
9579 // the future
9580 var _queryFuncCacheDOM = {},
9581 _queryFuncCacheQSA = {};
a089699c 9582
1354d172
AD
9583 // this is the second level of spliting, from full-length queries (e.g.,
9584 // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
9585 // ".bar"])
9586 var getStepQueryFunc = function(query){
9587 var qparts = getQueryParts(trim(query));
81bea17a 9588
1354d172
AD
9589 // if it's trivial, avoid iteration and zipping costs
9590 if(qparts.length == 1){
9591 // we optimize this case here to prevent dispatch further down the
9592 // chain, potentially slowing things down. We could more elegantly
9593 // handle this in filterDown(), but it's slower for simple things
9594 // that need to be fast (e.g., "#someId").
9595 var tef = getElementsFunc(qparts[0]);
9596 return function(root){
9597 var r = tef(root, []);
9598 if(r){ r.nozip = true; }
9599 return r;
9600 }
9601 }
a089699c 9602
1354d172
AD
9603 // otherwise, break it up and return a runner that iterates over the parts recursively
9604 return function(root){
9605 return filterDown(root, qparts);
9606 }
9607 };
81bea17a 9608
1354d172
AD
9609 // NOTES:
9610 // * we can't trust QSA for anything but document-rooted queries, so
9611 // caching is split into DOM query evaluators and QSA query evaluators
9612 // * caching query results is dirty and leak-prone (or, at a minimum,
9613 // prone to unbounded growth). Other toolkits may go this route, but
9614 // they totally destroy their own ability to manage their memory
9615 // footprint. If we implement it, it should only ever be with a fixed
9616 // total element reference # limit and an LRU-style algorithm since JS
9617 // has no weakref support. Caching compiled query evaluators is also
9618 // potentially problematic, but even on large documents the size of the
9619 // query evaluators is often < 100 function objects per evaluator (and
9620 // LRU can be applied if it's ever shown to be an issue).
9621 // * since IE's QSA support is currently only for HTML documents and even
9622 // then only in IE 8's "standards mode", we have to detect our dispatch
9623 // route at query time and keep 2 separate caches. Ugg.
9624
9625 // we need to determine if we think we can run a given query via
9626 // querySelectorAll or if we'll need to fall back on DOM queries to get
9627 // there. We need a lot of information about the environment and the query
9628 // to make the determiniation (e.g. does it support QSA, does the query in
9629 // question work in the native QSA impl, etc.).
9630 var nua = navigator.userAgent;
9631 // some versions of Safari provided QSA, but it was buggy and crash-prone.
9632 // We need te detect the right "internal" webkit version to make this work.
9633 var wk = "WebKit/";
9634 var is525 = (
9635 dojo.isWebKit &&
9636 (nua.indexOf(wk) > 0) &&
9637 (parseFloat(nua.split(wk)[1]) > 528)
9638 );
81bea17a 9639
1354d172
AD
9640 // IE QSA queries may incorrectly include comment nodes, so we throw the
9641 // zipping function into "remove" comments mode instead of the normal "skip
9642 // it" which every other QSA-clued browser enjoys
9643 var noZip = dojo.isIE ? "commentStrip" : "nozip";
a089699c 9644
1354d172
AD
9645 var qsa = "querySelectorAll";
9646 var qsaAvail = (
9647 !!getDoc()[qsa] &&
9648 // see #5832
9649 (!dojo.isSafari || (dojo.isSafari > 3.1) || is525 )
9650 );
81bea17a 9651
1354d172
AD
9652 //Don't bother with n+3 type of matches, IE complains if we modify those.
9653 var infixSpaceRe = /n\+\d|([^ ])?([>~+])([^ =])?/g;
9654 var infixSpaceFunc = function(match, pre, ch, post){
9655 return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match;
9656 };
81bea17a 9657
1354d172
AD
9658 var getQueryFunc = function(query, forceDOM){
9659 //Normalize query. The CSS3 selectors spec allows for omitting spaces around
9660 //infix operators, >, ~ and +
9661 //Do the work here since detection for spaces is used as a simple "not use QSA"
9662 //test below.
9663 query = query.replace(infixSpaceRe, infixSpaceFunc);
81bea17a 9664
1354d172
AD
9665 if(qsaAvail){
9666 // if we've got a cached variant and we think we can do it, run it!
9667 var qsaCached = _queryFuncCacheQSA[query];
9668 if(qsaCached && !forceDOM){ return qsaCached; }
9669 }
a089699c 9670
1354d172
AD
9671 // else if we've got a DOM cached variant, assume that we already know
9672 // all we need to and use it
9673 var domCached = _queryFuncCacheDOM[query];
9674 if(domCached){ return domCached; }
81bea17a 9675
1354d172
AD
9676 // TODO:
9677 // today we're caching DOM and QSA branches separately so we
9678 // recalc useQSA every time. If we had a way to tag root+query
9679 // efficiently, we'd be in good shape to do a global cache.
9680
9681 var qcz = query.charAt(0);
9682 var nospace = (-1 == query.indexOf(" "));
9683
9684 // byId searches are wicked fast compared to QSA, even when filtering
9685 // is required
9686 if( (query.indexOf("#") >= 0) && (nospace) ){
9687 forceDOM = true;
9688 }
9689
9690 var useQSA = (
9691 qsaAvail && (!forceDOM) &&
9692 // as per CSS 3, we can't currently start w/ combinator:
9693 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
9694 (specials.indexOf(qcz) == -1) &&
9695 // IE's QSA impl sucks on pseudos
9696 (!dojo.isIE || (query.indexOf(":") == -1)) &&
9697
9698 (!(cssCaseBug && (query.indexOf(".") >= 0))) &&
9699
9700 // FIXME:
9701 // need to tighten up browser rules on ":contains" and "|=" to
9702 // figure out which aren't good
9703 // Latest webkit (around 531.21.8) does not seem to do well with :checked on option
9704 // elements, even though according to spec, selected options should
9705 // match :checked. So go nonQSA for it:
9706 // http://bugs.dojotoolkit.org/ticket/5179
9707 (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) &&
9708 (query.indexOf("|=") == -1) // some browsers don't grok it
9709 );
a089699c 9710
1354d172
AD
9711 // TODO:
9712 // if we've got a descendant query (e.g., "> .thinger" instead of
9713 // just ".thinger") in a QSA-able doc, but are passed a child as a
9714 // root, it should be possible to give the item a synthetic ID and
9715 // trivially rewrite the query to the form "#synid > .thinger" to
9716 // use the QSA branch
81bea17a 9717
81bea17a 9718
1354d172
AD
9719 if(useQSA){
9720 var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ?
9721 (query + " *") : query;
9722 return _queryFuncCacheQSA[query] = function(root){
9723 try{
9724 // the QSA system contains an egregious spec bug which
9725 // limits us, effectively, to only running QSA queries over
9726 // entire documents. See:
9727 // http://ejohn.org/blog/thoughts-on-queryselectorall/
9728 // despite this, we can also handle QSA runs on simple
9729 // selectors, but we don't want detection to be expensive
9730 // so we're just checking for the presence of a space char
9731 // right now. Not elegant, but it's cheaper than running
9732 // the query parser when we might not need to
9733 if(!((9 == root.nodeType) || nospace)){ throw ""; }
9734 var r = root[qsa](tq);
9735 // skip expensive duplication checks and just wrap in a NodeList
9736 r[noZip] = true;
9737 return r;
9738 }catch(e){
9739 // else run the DOM branch on this query, ensuring that we
9740 // default that way in the future
9741 return getQueryFunc(query, true)(root);
9742 }
9743 }
9744 }else{
9745 // DOM branch
9746 var parts = query.split(/\s*,\s*/);
9747 return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
9748 // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
9749 getStepQueryFunc(query) :
9750 // if it *is* a complex query, break it up into its
9751 // constituent parts and return a dispatcher that will
9752 // merge the parts when run
9753 function(root){
9754 var pindex = 0, // avoid array alloc for every invocation
9755 ret = [],
9756 tp;
9757 while((tp = parts[pindex++])){
9758 ret = ret.concat(getStepQueryFunc(tp)(root));
9759 }
9760 return ret;
9761 }
9762 );
a089699c 9763 }
1354d172 9764 };
81bea17a 9765
1354d172 9766 var _zipIdx = 0;
a089699c 9767
1354d172
AD
9768 // NOTE:
9769 // this function is Moo inspired, but our own impl to deal correctly
9770 // with XML in IE
9771 var _nodeUID = dojo.isIE ? function(node){
9772 if(caseSensitive){
9773 // XML docs don't have uniqueID on their nodes
9774 return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
a089699c 9775
1354d172
AD
9776 }else{
9777 return node.uniqueID;
a089699c 9778 }
1354d172
AD
9779 } :
9780 function(node){
9781 return (node._uid || (node._uid = ++_zipIdx));
9782 };
a089699c 9783
1354d172
AD
9784 // determine if a node in is unique in a "bag". In this case we don't want
9785 // to flatten a list of unique items, but rather just tell if the item in
9786 // question is already in the bag. Normally we'd just use hash lookup to do
9787 // this for us but IE's DOM is busted so we can't really count on that. On
9788 // the upside, it gives us a built in unique ID function.
9789 var _isUnique = function(node, bag){
9790 if(!bag){ return 1; }
9791 var id = _nodeUID(node);
9792 if(!bag[id]){ return bag[id] = 1; }
9793 return 0;
9794 };
a089699c 9795
1354d172
AD
9796 // attempt to efficiently determine if an item in a list is a dupe,
9797 // returning a list of "uniques", hopefully in doucment order
9798 var _zipIdxName = "_zipIdx";
9799 var _zip = function(arr){
9800 if(arr && arr.nozip){
9801 return arr;
9802 }
9803 var ret = [];
9804 if(!arr || !arr.length){ return ret; }
9805 if(arr[0]){
9806 ret.push(arr[0]);
9807 }
9808 if(arr.length < 2){ return ret; }
9809
9810 _zipIdx++;
9811
9812 // we have to fork here for IE and XML docs because we can't set
9813 // expandos on their nodes (apparently). *sigh*
9814 if(dojo.isIE && caseSensitive){
9815 var szidx = _zipIdx+"";
9816 arr[0].setAttribute(_zipIdxName, szidx);
9817 for(var x = 1, te; te = arr[x]; x++){
9818 if(arr[x].getAttribute(_zipIdxName) != szidx){
9819 ret.push(te);
9820 }
9821 te.setAttribute(_zipIdxName, szidx);
9822 }
9823 }else if(dojo.isIE && arr.commentStrip){
9824 try{
9825 for(var x = 1, te; te = arr[x]; x++){
9826 if(_isElement(te)){
9827 ret.push(te);
9828 }
9829 }
9830 }catch(e){ /* squelch */ }
9831 }else{
9832 if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; }
9833 for(var x = 1, te; te = arr[x]; x++){
9834 if(arr[x][_zipIdxName] != _zipIdx){
9835 ret.push(te);
9836 }
9837 te[_zipIdxName] = _zipIdx;
9838 }
9839 }
9840 return ret;
9841 };
a089699c 9842
1354d172
AD
9843 // the main executor
9844 var query = function(/*String*/ query, /*String|DOMNode?*/ root){
9845 // summary:
9846 // Returns nodes which match the given CSS3 selector, searching the
9847 // entire document by default but optionally taking a node to scope
9848 // the search by. Returns an array.
9849 // description:
9850 // dojo.query() is the swiss army knife of DOM node manipulation in
9851 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
9852 // "$" function, dojo.query provides robust, high-performance
9853 // CSS-based node selector support with the option of scoping searches
9854 // to a particular sub-tree of a document.
a089699c 9855 //
1354d172
AD
9856 // Supported Selectors:
9857 // --------------------
a089699c 9858 //
1354d172 9859 // acme supports a rich set of CSS3 selectors, including:
a089699c 9860 //
1354d172
AD
9861 // * class selectors (e.g., `.foo`)
9862 // * node type selectors like `span`
9863 // * ` ` descendant selectors
9864 // * `>` child element selectors
9865 // * `#foo` style ID selectors
9866 // * `*` universal selector
9867 // * `~`, the preceded-by sibling selector
9868 // * `+`, the immediately preceded-by sibling selector
9869 // * attribute queries:
9870 // | * `[foo]` attribute presence selector
9871 // | * `[foo='bar']` attribute value exact match
9872 // | * `[foo~='bar']` attribute value list item match
9873 // | * `[foo^='bar']` attribute start match
9874 // | * `[foo$='bar']` attribute end match
9875 // | * `[foo*='bar']` attribute substring match
9876 // * `:first-child`, `:last-child`, and `:only-child` positional selectors
9877 // * `:empty` content emtpy selector
9878 // * `:checked` pseudo selector
9879 // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
9880 // * `:nth-child(even)`, `:nth-child(odd)` positional selectors
9881 // * `:not(...)` negation pseudo selectors
9882 //
9883 // Any legal combination of these selectors will work with
9884 // `dojo.query()`, including compound selectors ("," delimited).
9885 // Very complex and useful searches can be constructed with this
9886 // palette of selectors and when combined with functions for
9887 // manipulation presented by dojo.NodeList, many types of DOM
9888 // manipulation operations become very straightforward.
9889 //
9890 // Unsupported Selectors:
9891 // ----------------------
9892 //
9893 // While dojo.query handles many CSS3 selectors, some fall outside of
9894 // what's reasonable for a programmatic node querying engine to
9895 // handle. Currently unsupported selectors include:
9896 //
9897 // * namespace-differentiated selectors of any form
9898 // * all `::` pseduo-element selectors
9899 // * certain pseduo-selectors which don't get a lot of day-to-day use:
9900 // | * `:root`, `:lang()`, `:target`, `:focus`
9901 // * all visual and state selectors:
9902 // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`,
9903 // `:enabled`, `:disabled`
9904 // * `:*-of-type` pseudo selectors
9905 //
9906 // dojo.query and XML Documents:
9907 // -----------------------------
9908 //
9909 // `dojo.query` (as of dojo 1.2) supports searching XML documents
9910 // in a case-sensitive manner. If an HTML document is served with
9911 // a doctype that forces case-sensitivity (e.g., XHTML 1.1
9912 // Strict), dojo.query() will detect this and "do the right
9913 // thing". Case sensitivity is dependent upon the document being
9914 // searched and not the query used. It is therefore possible to
9915 // use case-sensitive queries on strict sub-documents (iframes,
9916 // etc.) or XML documents while still assuming case-insensitivity
9917 // for a host/root document.
9918 //
9919 // Non-selector Queries:
9920 // ---------------------
9921 //
9922 // If something other than a String is passed for the query,
9923 // `dojo.query` will return a new `dojo.NodeList` instance
9924 // constructed from that parameter alone and all further
9925 // processing will stop. This means that if you have a reference
9926 // to a node or NodeList, you can quickly construct a new NodeList
9927 // from the original by calling `dojo.query(node)` or
9928 // `dojo.query(list)`.
9929 //
9930 // query:
9931 // The CSS3 expression to match against. For details on the syntax of
9932 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
9933 // root:
9934 // A DOMNode (or node id) to scope the search from. Optional.
9935 // returns: Array
9936 // example:
9937 // search the entire document for elements with the class "foo":
9938 // | dojo.query(".foo");
9939 // these elements will match:
9940 // | <span class="foo"></span>
9941 // | <span class="foo bar"></span>
9942 // | <p class="thud foo"></p>
9943 // example:
9944 // search the entire document for elements with the classes "foo" *and* "bar":
9945 // | dojo.query(".foo.bar");
9946 // these elements will match:
9947 // | <span class="foo bar"></span>
9948 // while these will not:
9949 // | <span class="foo"></span>
9950 // | <p class="thud foo"></p>
9951 // example:
9952 // find `<span>` elements which are descendants of paragraphs and
9953 // which have a "highlighted" class:
9954 // | dojo.query("p span.highlighted");
9955 // the innermost span in this fragment matches:
9956 // | <p class="foo">
9957 // | <span>...
9958 // | <span class="highlighted foo bar">...</span>
9959 // | </span>
9960 // | </p>
9961 // example:
9962 // set an "odd" class on all odd table rows inside of the table
9963 // `#tabular_data`, using the `>` (direct child) selector to avoid
9964 // affecting any nested tables:
9965 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
9966 // example:
9967 // remove all elements with the class "error" from the document
9968 // and store them in a list:
9969 // | var errors = dojo.query(".error").orphan();
9970 // example:
9971 // add an onclick handler to every submit button in the document
9972 // which causes the form to be sent via Ajax instead:
9973 // | dojo.query("input[type='submit']").onclick(function(e){
9974 // | dojo.stopEvent(e); // prevent sending the form
9975 // | var btn = e.target;
9976 // | dojo.xhrPost({
9977 // | form: btn.form,
9978 // | load: function(data){
9979 // | // replace the form with the response
9980 // | var div = dojo.doc.createElement("div");
9981 // | dojo.place(div, btn.form, "after");
9982 // | div.innerHTML = data;
9983 // | dojo.style(btn.form, "display", "none");
9984 // | }
9985 // | });
9986 // | });
81bea17a 9987
1354d172
AD
9988 root = root||getDoc();
9989 var od = root.ownerDocument||root.documentElement;
81bea17a 9990
1354d172 9991 // throw the big case sensitivity switch
81bea17a 9992
1354d172
AD
9993 // NOTE:
9994 // Opera in XHTML mode doesn't detect case-sensitivity correctly
9995 // and it's not clear that there's any way to test for it
9996 caseSensitive = (root.contentType && root.contentType=="application/xml") ||
9997 (dojo.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) ||
9998 (!!od) &&
9999 (dojo.isIE ? od.xml : (root.xmlVersion || od.xmlVersion));
81bea17a 10000
1354d172
AD
10001 // NOTE:
10002 // adding "true" as the 2nd argument to getQueryFunc is useful for
10003 // testing the DOM branch without worrying about the
10004 // behavior/performance of the QSA branch.
10005 var r = getQueryFunc(query)(root);
81bea17a 10006
1354d172
AD
10007 // FIXME:
10008 // need to investigate this branch WRT #8074 and #8075
10009 if(r && r.nozip){
10010 return r;
10011 }
10012 return _zip(r); // dojo.NodeList
10013 };
10014 query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
10015 // summary:
10016 // function for filtering a NodeList based on a selector, optimized for simple selectors
10017 var tmpNodeList = [],
10018 parts = getQueryParts(filter),
10019 filterFunc =
10020 (parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
10021 getSimpleFilterFunc(parts[0]) :
10022 function(node){
10023 return dojo.query(filter, root).indexOf(node) != -1;
10024 };
10025 for(var x = 0, te; te = nodeList[x]; x++){
10026 if(filterFunc(te)){ tmpNodeList.push(te); }
10027 }
10028 return tmpNodeList;
10029 };
10030 return query;
10031});//end defineQuery
81bea17a 10032
1354d172
AD
10033},
10034'dojo/dnd/autoscroll':function(){
10035define("dojo/dnd/autoscroll", ["../main", "../window"], function(dojo) {
10036 // module:
10037 // dojo/dnd/autoscroll
10038 // summary:
10039 // TODOC
81bea17a 10040
1354d172 10041dojo.getObject("dnd", true, dojo);
81bea17a 10042
1354d172 10043dojo.dnd.getViewport = dojo.window.getBox;
81bea17a 10044
1354d172
AD
10045dojo.dnd.V_TRIGGER_AUTOSCROLL = 32;
10046dojo.dnd.H_TRIGGER_AUTOSCROLL = 32;
81bea17a 10047
1354d172
AD
10048dojo.dnd.V_AUTOSCROLL_VALUE = 16;
10049dojo.dnd.H_AUTOSCROLL_VALUE = 16;
81bea17a 10050
1354d172
AD
10051dojo.dnd.autoScroll = function(e){
10052 // summary:
10053 // a handler for onmousemove event, which scrolls the window, if
10054 // necesary
10055 // e: Event
10056 // onmousemove event
81bea17a 10057
1354d172
AD
10058 // FIXME: needs more docs!
10059 var v = dojo.window.getBox(), dx = 0, dy = 0;
10060 if(e.clientX < dojo.dnd.H_TRIGGER_AUTOSCROLL){
10061 dx = -dojo.dnd.H_AUTOSCROLL_VALUE;
10062 }else if(e.clientX > v.w - dojo.dnd.H_TRIGGER_AUTOSCROLL){
10063 dx = dojo.dnd.H_AUTOSCROLL_VALUE;
81bea17a 10064 }
1354d172
AD
10065 if(e.clientY < dojo.dnd.V_TRIGGER_AUTOSCROLL){
10066 dy = -dojo.dnd.V_AUTOSCROLL_VALUE;
10067 }else if(e.clientY > v.h - dojo.dnd.V_TRIGGER_AUTOSCROLL){
10068 dy = dojo.dnd.V_AUTOSCROLL_VALUE;
10069 }
10070 window.scrollBy(dx, dy);
10071};
81bea17a 10072
1354d172
AD
10073dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1};
10074dojo.dnd._validOverflow = {"auto": 1, "scroll": 1};
81bea17a 10075
1354d172 10076dojo.dnd.autoScrollNodes = function(e){
81bea17a 10077 // summary:
1354d172
AD
10078 // a handler for onmousemove event, which scrolls the first avaialble
10079 // Dom element, it falls back to dojo.dnd.autoScroll()
10080 // e: Event
10081 // onmousemove event
81bea17a 10082
1354d172 10083 // FIXME: needs more docs!
81bea17a 10084
1354d172 10085 var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
81bea17a 10086
1354d172
AD
10087 for(var n = e.target; n;){
10088 if(n.nodeType == 1 && (n.tagName.toLowerCase() in dojo.dnd._validNodes)){
10089 var s = dojo.getComputedStyle(n),
10090 overflow = (s.overflow.toLowerCase() in dojo.dnd._validOverflow),
10091 overflowX = (s.overflowX.toLowerCase() in dojo.dnd._validOverflow),
10092 overflowY = (s.overflowY.toLowerCase() in dojo.dnd._validOverflow);
10093 if(overflow || overflowX || overflowY){
10094 b = dojo._getContentBox(n, s);
10095 t = dojo.position(n, true);
10096 }
10097 // overflow-x
10098 if(overflow || overflowX){
10099 w = Math.min(dojo.dnd.H_TRIGGER_AUTOSCROLL, b.w / 2);
10100 rx = e.pageX - t.x;
10101 if(dojo.isWebKit || dojo.isOpera){
10102 // FIXME: this code should not be here, it should be taken into account
10103 // either by the event fixing code, or the dojo.position()
10104 // FIXME: this code doesn't work on Opera 9.5 Beta
10105 rx += dojo.body().scrollLeft;
10106 }
10107 dx = 0;
10108 if(rx > 0 && rx < b.w){
10109 if(rx < w){
10110 dx = -w;
10111 }else if(rx > b.w - w){
10112 dx = w;
10113 }
10114 oldLeft = n.scrollLeft;
10115 n.scrollLeft = n.scrollLeft + dx;
10116 }
10117 }
10118 // overflow-y
10119 if(overflow || overflowY){
10120 //console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
10121 h = Math.min(dojo.dnd.V_TRIGGER_AUTOSCROLL, b.h / 2);
10122 ry = e.pageY - t.y;
10123 if(dojo.isWebKit || dojo.isOpera){
10124 // FIXME: this code should not be here, it should be taken into account
10125 // either by the event fixing code, or the dojo.position()
10126 // FIXME: this code doesn't work on Opera 9.5 Beta
10127 ry += dojo.body().scrollTop;
10128 }
10129 dy = 0;
10130 if(ry > 0 && ry < b.h){
10131 if(ry < h){
10132 dy = -h;
10133 }else if(ry > b.h - h){
10134 dy = h;
10135 }
10136 oldTop = n.scrollTop;
10137 n.scrollTop = n.scrollTop + dy;
10138 }
10139 }
10140 if(dx || dy){ return; }
10141 }
10142 try{
10143 n = n.parentNode;
10144 }catch(x){
10145 n = null;
10146 }
10147 }
10148 dojo.dnd.autoScroll(e);
10149};
81bea17a 10150
1354d172
AD
10151 return dojo.dnd;
10152});
81bea17a 10153
1354d172
AD
10154},
10155'dojo/data/ItemFileWriteStore':function(){
10156define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/window",
10157 "./ItemFileReadStore", "../date/stamp"
10158], function(lang, declare, arrayUtil, jsonUtil, window, ItemFileReadStore, dateStamp) {
10159 // module:
10160 // dojo/data/ItemFileWriteStore
10161 // summary:
10162 // TODOC
81bea17a 10163
1354d172
AD
10164/*===== var ItemFileReadStore = dojo.data.ItemFileReadStore; =====*/
10165return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore, {
10166 constructor: function(/* object */ keywordParameters){
10167 // keywordParameters: {typeMap: object)
10168 // The structure of the typeMap object is as follows:
10169 // {
10170 // type0: function || object,
10171 // type1: function || object,
10172 // ...
10173 // typeN: function || object
10174 // }
10175 // Where if it is a function, it is assumed to be an object constructor that takes the
10176 // value of _value as the initialization parameters. It is serialized assuming object.toString()
10177 // serialization. If it is an object, then it is assumed
10178 // to be an object of general form:
10179 // {
10180 // type: function, //constructor.
10181 // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
10182 // serialize: function(object) //The function that converts the object back into the proper file format form.
10183 // }
81bea17a 10184
1354d172
AD
10185 // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
10186 this._features['dojo.data.api.Write'] = true;
10187 this._features['dojo.data.api.Notification'] = true;
81bea17a 10188
1354d172
AD
10189 // For keeping track of changes so that we can implement isDirty and revert
10190 this._pending = {
10191 _newItems:{},
10192 _modifiedItems:{},
10193 _deletedItems:{}
10194 };
81bea17a 10195
1354d172
AD
10196 if(!this._datatypeMap['Date'].serialize){
10197 this._datatypeMap['Date'].serialize = function(obj){
10198 return dateStamp.toISOString(obj, {zulu:true});
10199 };
81bea17a 10200 }
1354d172
AD
10201 //Disable only if explicitly set to false.
10202 if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
10203 this.referenceIntegrity = false;
81bea17a 10204 }
81bea17a 10205
1354d172
AD
10206 // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
10207 this._saveInProgress = false;
10208 },
81bea17a 10209
1354d172 10210 referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
81bea17a 10211
1354d172
AD
10212 _assert: function(/* boolean */ condition){
10213 if(!condition){
10214 throw new Error("assertion failed in ItemFileWriteStore");
81bea17a 10215 }
1354d172 10216 },
81bea17a 10217
1354d172
AD
10218 _getIdentifierAttribute: function(){
10219 // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
10220 return this.getFeatures()['dojo.data.api.Identity'];
81bea17a
AD
10221 },
10222
81bea17a 10223
1354d172 10224/* dojo.data.api.Write */
a089699c 10225
1354d172
AD
10226 newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
10227 // summary: See dojo.data.api.Write.newItem()
a089699c 10228
1354d172 10229 this._assert(!this._saveInProgress);
a089699c 10230
1354d172
AD
10231 if(!this._loadFinished){
10232 // We need to do this here so that we'll be able to find out what
10233 // identifierAttribute was specified in the data file.
10234 this._forceLoad();
81bea17a 10235 }
a089699c 10236
1354d172
AD
10237 if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
10238 throw new Error("newItem() was passed something other than an object");
81bea17a 10239 }
1354d172
AD
10240 var newIdentity = null;
10241 var identifierAttribute = this._getIdentifierAttribute();
10242 if(identifierAttribute === Number){
10243 newIdentity = this._arrayOfAllItems.length;
81bea17a 10244 }else{
1354d172
AD
10245 newIdentity = keywordArgs[identifierAttribute];
10246 if(typeof newIdentity === "undefined"){
10247 throw new Error("newItem() was not passed an identity for the new item");
10248 }
10249 if(lang.isArray(newIdentity)){
10250 throw new Error("newItem() was not passed an single-valued identity");
10251 }
81bea17a 10252 }
a089699c 10253
1354d172
AD
10254 // make sure this identity is not already in use by another item, if identifiers were
10255 // defined in the file. Otherwise it would be the item count,
10256 // which should always be unique in this case.
10257 if(this._itemsByIdentity){
10258 this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
81bea17a 10259 }
1354d172
AD
10260 this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
10261 this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
a089699c 10262
1354d172
AD
10263 var newItem = {};
10264 newItem[this._storeRefPropName] = this;
10265 newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
10266 if(this._itemsByIdentity){
10267 this._itemsByIdentity[newIdentity] = newItem;
10268 //We have to set the identifier now, otherwise we can't look it
10269 //up at calls to setValueorValues in parentInfo handling.
10270 newItem[identifierAttribute] = [newIdentity];
81bea17a 10271 }
1354d172 10272 this._arrayOfAllItems.push(newItem);
a089699c 10273
1354d172
AD
10274 //We need to construct some data for the onNew call too...
10275 var pInfo = null;
a089699c 10276
1354d172
AD
10277 // Now we need to check to see where we want to assign this thingm if any.
10278 if(parentInfo && parentInfo.parent && parentInfo.attribute){
10279 pInfo = {
10280 item: parentInfo.parent,
10281 attribute: parentInfo.attribute,
10282 oldValue: undefined
10283 };
10284
10285 //See if it is multi-valued or not and handle appropriately
10286 //Generally, all attributes are multi-valued for this store
10287 //So, we only need to append if there are already values present.
10288 var values = this.getValues(parentInfo.parent, parentInfo.attribute);
10289 if(values && values.length > 0){
10290 var tempValues = values.slice(0, values.length);
10291 if(values.length === 1){
10292 pInfo.oldValue = values[0];
10293 }else{
10294 pInfo.oldValue = values.slice(0, values.length);
10295 }
10296 tempValues.push(newItem);
10297 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
10298 pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
10299 }else{
10300 this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
10301 pInfo.newValue = newItem;
81bea17a 10302 }
81bea17a 10303 }else{
1354d172
AD
10304 //Toplevel item, add to both top list as well as all list.
10305 newItem[this._rootItemPropName]=true;
10306 this._arrayOfTopLevelItems.push(newItem);
81bea17a 10307 }
a089699c 10308
1354d172 10309 this._pending._newItems[newIdentity] = newItem;
a089699c 10310
1354d172
AD
10311 //Clone over the properties to the new item
10312 for(var key in keywordArgs){
10313 if(key === this._storeRefPropName || key === this._itemNumPropName){
10314 // Bummer, the user is trying to do something like
10315 // newItem({_S:"foo"}). Unfortunately, our superclass,
10316 // ItemFileReadStore, is already using _S in each of our items
10317 // to hold private info. To avoid a naming collision, we
10318 // need to move all our private info to some other property
10319 // of all the items/objects. So, we need to iterate over all
10320 // the items and do something like:
10321 // item.__S = item._S;
10322 // item._S = undefined;
10323 // But first we have to make sure the new "__S" variable is
10324 // not in use, which means we have to iterate over all the
10325 // items checking for that.
10326 throw new Error("encountered bug in ItemFileWriteStore.newItem");
10327 }
10328 var value = keywordArgs[key];
10329 if(!lang.isArray(value)){
10330 value = [value];
10331 }
10332 newItem[key] = value;
10333 if(this.referenceIntegrity){
10334 for(var i = 0; i < value.length; i++){
10335 var val = value[i];
10336 if(this.isItem(val)){
10337 this._addReferenceToMap(val, newItem, key);
10338 }
10339 }
10340 }
a089699c 10341 }
1354d172
AD
10342 this.onNew(newItem, pInfo); // dojo.data.api.Notification call
10343 return newItem; // item
10344 },
81bea17a 10345
1354d172
AD
10346 _removeArrayElement: function(/* Array */ array, /* anything */ element){
10347 var index = arrayUtil.indexOf(array, element);
10348 if(index != -1){
10349 array.splice(index, 1);
10350 return true;
10351 }
10352 return false;
10353 },
a089699c 10354
1354d172
AD
10355 deleteItem: function(/* item */ item){
10356 // summary: See dojo.data.api.Write.deleteItem()
10357 this._assert(!this._saveInProgress);
10358 this._assertIsItem(item);
a089699c 10359
1354d172
AD
10360 // Remove this item from the _arrayOfAllItems, but leave a null value in place
10361 // of the item, so as not to change the length of the array, so that in newItem()
10362 // we can still safely do: newIdentity = this._arrayOfAllItems.length;
10363 var indexInArrayOfAllItems = item[this._itemNumPropName];
10364 var identity = this.getIdentity(item);
81bea17a 10365
1354d172
AD
10366 //If we have reference integrity on, we need to do reference cleanup for the deleted item
10367 if(this.referenceIntegrity){
10368 //First scan all the attributes of this items for references and clean them up in the map
10369 //As this item is going away, no need to track its references anymore.
a089699c 10370
1354d172
AD
10371 //Get the attributes list before we generate the backup so it
10372 //doesn't pollute the attributes list.
10373 var attributes = this.getAttributes(item);
a089699c 10374
1354d172
AD
10375 //Backup the map, we'll have to restore it potentially, in a revert.
10376 if(item[this._reverseRefMap]){
10377 item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
10378 }
a089699c 10379
1354d172
AD
10380 //TODO: This causes a reversion problem. This list won't be restored on revert since it is
10381 //attached to the 'value'. item, not ours. Need to back tese up somehow too.
10382 //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
10383 //later. Or just record them and call _addReferenceToMap on them in revert.
10384 arrayUtil.forEach(attributes, function(attribute){
10385 arrayUtil.forEach(this.getValues(item, attribute), function(value){
10386 if(this.isItem(value)){
10387 //We have to back up all the references we had to others so they can be restored on a revert.
10388 if(!item["backupRefs_" + this._reverseRefMap]){
10389 item["backupRefs_" + this._reverseRefMap] = [];
10390 }
10391 item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
10392 this._removeReferenceFromMap(value, item, attribute);
10393 }
10394 }, this);
10395 }, this);
a089699c 10396
1354d172
AD
10397 //Next, see if we have references to this item, if we do, we have to clean them up too.
10398 var references = item[this._reverseRefMap];
10399 if(references){
10400 //Look through all the items noted as references to clean them up.
10401 for(var itemId in references){
10402 var containingItem = null;
10403 if(this._itemsByIdentity){
10404 containingItem = this._itemsByIdentity[itemId];
10405 }else{
10406 containingItem = this._arrayOfAllItems[itemId];
10407 }
10408 //We have a reference to a containing item, now we have to process the
10409 //attributes and clear all references to the item being deleted.
10410 if(containingItem){
10411 for(var attribute in references[itemId]){
10412 var oldValues = this.getValues(containingItem, attribute) || [];
10413 var newValues = arrayUtil.filter(oldValues, function(possibleItem){
10414 return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
10415 }, this);
10416 //Remove the note of the reference to the item and set the values on the modified attribute.
10417 this._removeReferenceFromMap(item, containingItem, attribute);
10418 if(newValues.length < oldValues.length){
10419 this._setValueOrValues(containingItem, attribute, newValues, true);
10420 }
10421 }
10422 }
a089699c 10423 }
a089699c
AD
10424 }
10425 }
10426
1354d172 10427 this._arrayOfAllItems[indexInArrayOfAllItems] = null;
a089699c 10428
1354d172
AD
10429 item[this._storeRefPropName] = null;
10430 if(this._itemsByIdentity){
10431 delete this._itemsByIdentity[identity];
10432 }
10433 this._pending._deletedItems[identity] = item;
a089699c 10434
1354d172
AD
10435 //Remove from the toplevel items, if necessary...
10436 if(item[this._rootItemPropName]){
10437 this._removeArrayElement(this._arrayOfTopLevelItems, item);
10438 }
10439 this.onDelete(item); // dojo.data.api.Notification call
10440 return true;
10441 },
a089699c 10442
1354d172
AD
10443 setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
10444 // summary: See dojo.data.api.Write.set()
10445 return this._setValueOrValues(item, attribute, value, true); // boolean
10446 },
a089699c 10447
1354d172
AD
10448 setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
10449 // summary: See dojo.data.api.Write.setValues()
10450 return this._setValueOrValues(item, attribute, values, true); // boolean
10451 },
81bea17a 10452
1354d172
AD
10453 unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
10454 // summary: See dojo.data.api.Write.unsetAttribute()
10455 return this._setValueOrValues(item, attribute, [], true);
10456 },
81bea17a 10457
1354d172
AD
10458 _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
10459 this._assert(!this._saveInProgress);
a089699c 10460
1354d172
AD
10461 // Check for valid arguments
10462 this._assertIsItem(item);
10463 this._assert(lang.isString(attribute));
10464 this._assert(typeof newValueOrValues !== "undefined");
10465
10466 // Make sure the user isn't trying to change the item's identity
10467 var identifierAttribute = this._getIdentifierAttribute();
10468 if(attribute == identifierAttribute){
10469 throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
10470 }
10471
10472 // To implement the Notification API, we need to make a note of what
10473 // the old attribute value was, so that we can pass that info when
10474 // we call the onSet method.
10475 var oldValueOrValues = this._getValueOrValues(item, attribute);
10476
10477 var identity = this.getIdentity(item);
10478 if(!this._pending._modifiedItems[identity]){
10479 // Before we actually change the item, we make a copy of it to
10480 // record the original state, so that we'll be able to revert if
10481 // the revert method gets called. If the item has already been
10482 // modified then there's no need to do this now, since we already
10483 // have a record of the original state.
10484 var copyOfItemState = {};
10485 for(var key in item){
10486 if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
10487 copyOfItemState[key] = item[key];
10488 }else if(key === this._reverseRefMap){
10489 copyOfItemState[key] = lang.clone(item[key]);
10490 }else{
10491 copyOfItemState[key] = item[key].slice(0, item[key].length);
a089699c 10492 }
1354d172
AD
10493 }
10494 // Now mark the item as dirty, and save the copy of the original state
10495 this._pending._modifiedItems[identity] = copyOfItemState;
10496 }
a089699c 10497
1354d172
AD
10498 // Okay, now we can actually change this attribute on the item
10499 var success = false;
a089699c 10500
1354d172 10501 if(lang.isArray(newValueOrValues) && newValueOrValues.length === 0){
a089699c 10502
1354d172
AD
10503 // If we were passed an empty array as the value, that counts
10504 // as "unsetting" the attribute, so we need to remove this
10505 // attribute from the item.
10506 success = delete item[attribute];
10507 newValueOrValues = undefined; // used in the onSet Notification call below
10508
10509 if(this.referenceIntegrity && oldValueOrValues){
10510 var oldValues = oldValueOrValues;
10511 if(!lang.isArray(oldValues)){
10512 oldValues = [oldValues];
a089699c 10513 }
1354d172
AD
10514 for(var i = 0; i < oldValues.length; i++){
10515 var value = oldValues[i];
10516 if(this.isItem(value)){
10517 this._removeReferenceFromMap(value, item, attribute);
a089699c
AD
10518 }
10519 }
1354d172
AD
10520 }
10521 }else{
10522 var newValueArray;
10523 if(lang.isArray(newValueOrValues)){
10524 // Unfortunately, it's not safe to just do this:
10525 // newValueArray = newValueOrValues;
10526 // Instead, we need to copy the array, which slice() does very nicely.
10527 // This is so that our internal data structure won't
10528 // get corrupted if the user mucks with the values array *after*
10529 // calling setValues().
10530 newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
10531 }else{
10532 newValueArray = [newValueOrValues];
10533 }
a089699c 10534
1354d172
AD
10535 //We need to handle reference integrity if this is on.
10536 //In the case of set, we need to see if references were added or removed
10537 //and update the reference tracking map accordingly.
10538 if(this.referenceIntegrity){
10539 if(oldValueOrValues){
10540 var oldValues = oldValueOrValues;
10541 if(!lang.isArray(oldValues)){
10542 oldValues = [oldValues];
10543 }
10544 //Use an associative map to determine what was added/removed from the list.
10545 //Should be O(n) performant. First look at all the old values and make a list of them
10546 //Then for any item not in the old list, we add it. If it was already present, we remove it.
10547 //Then we pass over the map and any references left it it need to be removed (IE, no match in
10548 //the new values list).
10549 var map = {};
10550 arrayUtil.forEach(oldValues, function(possibleItem){
10551 if(this.isItem(possibleItem)){
10552 var id = this.getIdentity(possibleItem);
10553 map[id.toString()] = true;
a089699c 10554 }
1354d172
AD
10555 }, this);
10556 arrayUtil.forEach(newValueArray, function(possibleItem){
10557 if(this.isItem(possibleItem)){
10558 var id = this.getIdentity(possibleItem);
10559 if(map[id.toString()]){
10560 delete map[id.toString()];
10561 }else{
10562 this._addReferenceToMap(possibleItem, item, attribute);
10563 }
10564 }
10565 }, this);
10566 for(var rId in map){
10567 var removedItem;
10568 if(this._itemsByIdentity){
10569 removedItem = this._itemsByIdentity[rId];
10570 }else{
10571 removedItem = this._arrayOfAllItems[rId];
10572 }
10573 this._removeReferenceFromMap(removedItem, item, attribute);
a089699c 10574 }
1354d172
AD
10575 }else{
10576 //Everything is new (no old values) so we have to just
10577 //insert all the references, if any.
10578 for(var i = 0; i < newValueArray.length; i++){
10579 var value = newValueArray[i];
10580 if(this.isItem(value)){
10581 this._addReferenceToMap(value, item, attribute);
10582 }
a089699c
AD
10583 }
10584 }
1354d172
AD
10585 }
10586 item[attribute] = newValueArray;
10587 success = true;
10588 }
a089699c 10589
1354d172
AD
10590 // Now we make the dojo.data.api.Notification call
10591 if(callOnSet){
10592 this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
10593 }
10594 return success; // boolean
10595 },
a089699c 10596
1354d172
AD
10597 _addReferenceToMap: function(/* item */ refItem, /* item */ parentItem, /* string */ attribute){
10598 // summary:
10599 // Method to add an reference map entry for an item and attribute.
10600 // description:
10601 // Method to add an reference map entry for an item and attribute. //
10602 // refItem:
10603 // The item that is referenced.
10604 // parentItem:
10605 // The item that holds the new reference to refItem.
10606 // attribute:
10607 // The attribute on parentItem that contains the new reference.
10608
10609 var parentId = this.getIdentity(parentItem);
10610 var references = refItem[this._reverseRefMap];
10611
10612 if(!references){
10613 references = refItem[this._reverseRefMap] = {};
10614 }
10615 var itemRef = references[parentId];
10616 if(!itemRef){
10617 itemRef = references[parentId] = {};
10618 }
10619 itemRef[attribute] = true;
10620 },
10621
10622 _removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /* string */ attribute){
10623 // summary:
10624 // Method to remove an reference map entry for an item and attribute.
10625 // description:
10626 // Method to remove an reference map entry for an item and attribute. This will
10627 // also perform cleanup on the map such that if there are no more references at all to
10628 // the item, its reference object and entry are removed.
10629 //
10630 // refItem:
10631 // The item that is referenced.
10632 // parentItem:
10633 // The item holding a reference to refItem.
10634 // attribute:
10635 // The attribute on parentItem that contains the reference.
10636 var identity = this.getIdentity(parentItem);
10637 var references = refItem[this._reverseRefMap];
10638 var itemId;
10639 if(references){
10640 for(itemId in references){
10641 if(itemId == identity){
10642 delete references[itemId][attribute];
10643 if(this._isEmpty(references[itemId])){
10644 delete references[itemId];
10645 }
a089699c
AD
10646 }
10647 }
1354d172
AD
10648 if(this._isEmpty(references)){
10649 delete refItem[this._reverseRefMap];
10650 }
10651 }
10652 },
a089699c 10653
1354d172
AD
10654 _dumpReferenceMap: function(){
10655 // summary:
10656 // Function to dump the reverse reference map of all items in the store for debug purposes.
10657 // description:
10658 // Function to dump the reverse reference map of all items in the store for debug purposes.
10659 var i;
10660 for(i = 0; i < this._arrayOfAllItems.length; i++){
10661 var item = this._arrayOfAllItems[i];
10662 if(item && item[this._reverseRefMap]){
10663 console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + jsonUtil.toJson(item[this._reverseRefMap]));
10664 }
81bea17a 10665 }
1354d172
AD
10666 },
10667
10668 _getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
10669 var valueOrValues = undefined;
10670 if(this.hasAttribute(item, attribute)){
10671 var valueArray = this.getValues(item, attribute);
10672 if(valueArray.length == 1){
10673 valueOrValues = valueArray[0];
10674 }else{
10675 valueOrValues = valueArray;
10676 }
10677 }
10678 return valueOrValues;
10679 },
10680
10681 _flatten: function(/* anything */ value){
10682 if(this.isItem(value)){
10683 // Given an item, return an serializable object that provides a
10684 // reference to the item.
10685 // For example, given kermit:
10686 // var kermit = store.newItem({id:2, name:"Kermit"});
10687 // we want to return
10688 // {_reference:2}
10689 return {_reference: this.getIdentity(value)};
81bea17a 10690 }else{
1354d172
AD
10691 if(typeof value === "object"){
10692 for(var type in this._datatypeMap){
10693 var typeMap = this._datatypeMap[type];
10694 if(lang.isObject(typeMap) && !lang.isFunction(typeMap)){
10695 if(value instanceof typeMap.type){
10696 if(!typeMap.serialize){
10697 throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
10698 }
10699 return {_type: type, _value: typeMap.serialize(value)};
10700 }
10701 } else if(value instanceof typeMap){
10702 //SImple mapping, therefore, return as a toString serialization.
10703 return {_type: type, _value: value.toString()};
10704 }
10705 }
10706 }
10707 return value;
a089699c 10708 }
1354d172 10709 },
a089699c 10710
1354d172
AD
10711 _getNewFileContentString: function(){
10712 // summary:
10713 // Generate a string that can be saved to a file.
10714 // The result should look similar to:
10715 // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
10716 var serializableStructure = {};
a089699c 10717
1354d172
AD
10718 var identifierAttribute = this._getIdentifierAttribute();
10719 if(identifierAttribute !== Number){
10720 serializableStructure.identifier = identifierAttribute;
10721 }
10722 if(this._labelAttr){
10723 serializableStructure.label = this._labelAttr;
10724 }
10725 serializableStructure.items = [];
10726 for(var i = 0; i < this._arrayOfAllItems.length; ++i){
10727 var item = this._arrayOfAllItems[i];
10728 if(item !== null){
10729 var serializableItem = {};
10730 for(var key in item){
10731 if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
10732 var valueArray = this.getValues(item, key);
10733 if(valueArray.length == 1){
10734 serializableItem[key] = this._flatten(valueArray[0]);
10735 }else{
10736 var serializableArray = [];
10737 for(var j = 0; j < valueArray.length; ++j){
10738 serializableArray.push(this._flatten(valueArray[j]));
10739 serializableItem[key] = serializableArray;
10740 }
10741 }
10742 }
10743 }
10744 serializableStructure.items.push(serializableItem);
10745 }
10746 }
10747 var prettyPrint = true;
10748 return jsonUtil.toJson(serializableStructure, prettyPrint);
10749 },
a089699c 10750
1354d172
AD
10751 _isEmpty: function(something){
10752 // summary:
10753 // Function to determine if an array or object has no properties or values.
10754 // something:
10755 // The array or object to examine.
10756 var empty = true;
10757 if(lang.isObject(something)){
10758 var i;
10759 for(i in something){
10760 empty = false;
10761 break;
10762 }
10763 }else if(lang.isArray(something)){
10764 if(something.length > 0){
10765 empty = false;
10766 }
10767 }
10768 return empty; //boolean
10769 },
a089699c 10770
1354d172
AD
10771 save: function(/* object */ keywordArgs){
10772 // summary: See dojo.data.api.Write.save()
10773 this._assert(!this._saveInProgress);
a089699c 10774
1354d172
AD
10775 // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
10776 this._saveInProgress = true;
a089699c 10777
1354d172
AD
10778 var self = this;
10779 var saveCompleteCallback = function(){
10780 self._pending = {
10781 _newItems:{},
10782 _modifiedItems:{},
10783 _deletedItems:{}
10784 };
a089699c 10785
1354d172
AD
10786 self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
10787 if(keywordArgs && keywordArgs.onComplete){
10788 var scope = keywordArgs.scope || window.global;
10789 keywordArgs.onComplete.call(scope);
10790 }
10791 };
10792 var saveFailedCallback = function(err){
10793 self._saveInProgress = false;
10794 if(keywordArgs && keywordArgs.onError){
10795 var scope = keywordArgs.scope || window.global;
10796 keywordArgs.onError.call(scope, err);
10797 }
10798 };
a089699c 10799
1354d172
AD
10800 if(this._saveEverything){
10801 var newFileContentString = this._getNewFileContentString();
10802 this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
10803 }
10804 if(this._saveCustom){
10805 this._saveCustom(saveCompleteCallback, saveFailedCallback);
10806 }
10807 if(!this._saveEverything && !this._saveCustom){
10808 // Looks like there is no user-defined save-handler function.
10809 // That's fine, it just means the datastore is acting as a "mock-write"
10810 // store -- changes get saved in memory but don't get saved to disk.
10811 saveCompleteCallback();
10812 }
10813 },
a089699c 10814
1354d172
AD
10815 revert: function(){
10816 // summary: See dojo.data.api.Write.revert()
10817 this._assert(!this._saveInProgress);
a089699c 10818
1354d172
AD
10819 var identity;
10820 for(identity in this._pending._modifiedItems){
10821 // find the original item and the modified item that replaced it
10822 var copyOfItemState = this._pending._modifiedItems[identity];
10823 var modifiedItem = null;
10824 if(this._itemsByIdentity){
10825 modifiedItem = this._itemsByIdentity[identity];
10826 }else{
10827 modifiedItem = this._arrayOfAllItems[identity];
10828 }
a089699c 10829
1354d172
AD
10830 // Restore the original item into a full-fledged item again, we want to try to
10831 // keep the same object instance as if we don't it, causes bugs like #9022.
10832 copyOfItemState[this._storeRefPropName] = this;
10833 for(var key in modifiedItem){
10834 delete modifiedItem[key];
10835 }
10836 lang.mixin(modifiedItem, copyOfItemState);
10837 }
10838 var deletedItem;
10839 for(identity in this._pending._deletedItems){
10840 deletedItem = this._pending._deletedItems[identity];
10841 deletedItem[this._storeRefPropName] = this;
10842 var index = deletedItem[this._itemNumPropName];
a089699c 10843
1354d172
AD
10844 //Restore the reverse refererence map, if any.
10845 if(deletedItem["backup_" + this._reverseRefMap]){
10846 deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
10847 delete deletedItem["backup_" + this._reverseRefMap];
10848 }
10849 this._arrayOfAllItems[index] = deletedItem;
10850 if(this._itemsByIdentity){
10851 this._itemsByIdentity[identity] = deletedItem;
10852 }
10853 if(deletedItem[this._rootItemPropName]){
10854 this._arrayOfTopLevelItems.push(deletedItem);
10855 }
10856 }
10857 //We have to pass through it again and restore the reference maps after all the
10858 //undeletes have occurred.
10859 for(identity in this._pending._deletedItems){
10860 deletedItem = this._pending._deletedItems[identity];
10861 if(deletedItem["backupRefs_" + this._reverseRefMap]){
10862 arrayUtil.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
10863 var refItem;
10864 if(this._itemsByIdentity){
10865 refItem = this._itemsByIdentity[reference.id];
10866 }else{
10867 refItem = this._arrayOfAllItems[reference.id];
10868 }
10869 this._addReferenceToMap(refItem, deletedItem, reference.attr);
10870 }, this);
10871 delete deletedItem["backupRefs_" + this._reverseRefMap];
10872 }
10873 }
a089699c 10874
1354d172
AD
10875 for(identity in this._pending._newItems){
10876 var newItem = this._pending._newItems[identity];
10877 newItem[this._storeRefPropName] = null;
10878 // null out the new item, but don't change the array index so
10879 // so we can keep using _arrayOfAllItems.length.
10880 this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
10881 if(newItem[this._rootItemPropName]){
10882 this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
10883 }
10884 if(this._itemsByIdentity){
10885 delete this._itemsByIdentity[identity];
10886 }
10887 }
a089699c 10888
1354d172
AD
10889 this._pending = {
10890 _newItems:{},
10891 _modifiedItems:{},
10892 _deletedItems:{}
10893 };
10894 return true; // boolean
10895 },
a089699c 10896
1354d172
AD
10897 isDirty: function(/* item? */ item){
10898 // summary: See dojo.data.api.Write.isDirty()
10899 if(item){
10900 // return true if the item is dirty
10901 var identity = this.getIdentity(item);
10902 return new Boolean(this._pending._newItems[identity] ||
10903 this._pending._modifiedItems[identity] ||
10904 this._pending._deletedItems[identity]).valueOf(); // boolean
10905 }else{
10906 // return true if the store is dirty -- which means return true
10907 // if there are any new items, dirty items, or modified items
10908 return !this._isEmpty(this._pending._newItems) ||
10909 !this._isEmpty(this._pending._modifiedItems) ||
10910 !this._isEmpty(this._pending._deletedItems); // boolean
10911 }
10912 },
a089699c 10913
1354d172 10914/* dojo.data.api.Notification */
a089699c 10915
1354d172
AD
10916 onSet: function(/* item */ item,
10917 /*attribute-name-string*/ attribute,
10918 /*object|array*/ oldValue,
10919 /*object|array*/ newValue){
10920 // summary: See dojo.data.api.Notification.onSet()
a089699c 10921
1354d172
AD
10922 // No need to do anything. This method is here just so that the
10923 // client code can connect observers to it.
10924 },
81bea17a 10925
1354d172
AD
10926 onNew: function(/* item */ newItem, /*object?*/ parentInfo){
10927 // summary: See dojo.data.api.Notification.onNew()
81bea17a 10928
1354d172
AD
10929 // No need to do anything. This method is here just so that the
10930 // client code can connect observers to it.
81bea17a
AD
10931 },
10932
1354d172
AD
10933 onDelete: function(/* item */ deletedItem){
10934 // summary: See dojo.data.api.Notification.onDelete()
10935
10936 // No need to do anything. This method is here just so that the
10937 // client code can connect observers to it.
a089699c
AD
10938 },
10939
1354d172
AD
10940 close: function(/* object? */ request){
10941 // summary:
10942 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
10943 // description:
10944 // Over-ride of base close function of ItemFileReadStore to add in check for store state.
10945 // If the store is still dirty (unsaved changes), then an error will be thrown instead of
10946 // clearing the internal state for reload from the url.
81bea17a 10947
1354d172
AD
10948 //Clear if not dirty ... or throw an error
10949 if(this.clearOnClose){
10950 if(!this.isDirty()){
10951 this.inherited(arguments);
10952 }else{
10953 //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
10954 throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
10955 }
10956 }
10957 }
10958});
a089699c 10959
1354d172 10960});
a089699c 10961
1354d172
AD
10962},
10963'dijit/form/_RadioButtonMixin':function(){
10964define("dijit/form/_RadioButtonMixin", [
10965 "dojo/_base/array", // array.forEach
10966 "dojo/_base/declare", // declare
10967 "dojo/dom-attr", // domAttr.set
10968 "dojo/_base/event", // event.stop
10969 "dojo/_base/lang", // lang.hitch
10970 "dojo/query", // query
10971 "dojo/_base/window", // win.doc
10972 "../registry" // registry.getEnclosingWidget
10973], function(array, declare, domAttr, event, lang, query, win, registry){
10974
10975 // module:
10976 // dijit/form/_RadioButtonMixin
10977 // summary:
10978 // Mixin to provide widget functionality for an HTML radio button
a089699c 10979
1354d172 10980 return declare("dijit.form._RadioButtonMixin", null, {
a089699c 10981 // summary:
1354d172 10982 // Mixin to provide widget functionality for an HTML radio button
a089699c 10983
1354d172
AD
10984 // type: [private] String
10985 // type attribute on <input> node.
10986 // Users should not change this value.
10987 type: "radio",
a089699c 10988
1354d172
AD
10989 _getRelatedWidgets: function(){
10990 // Private function needed to help iterate over all radio buttons in a group.
10991 var ary = [];
10992 query("input[type=radio]", this.focusNode.form || win.doc).forEach( // can't use name= since query doesn't support [] in the name
10993 lang.hitch(this, function(inputNode){
10994 if(inputNode.name == this.name && inputNode.form == this.focusNode.form){
10995 var widget = registry.getEnclosingWidget(inputNode);
10996 if(widget){
10997 ary.push(widget);
10998 }
10999 }
11000 })
11001 );
11002 return ary;
11003 },
a089699c 11004
1354d172
AD
11005 _setCheckedAttr: function(/*Boolean*/ value){
11006 // If I am being checked then have to deselect currently checked radio button
11007 this.inherited(arguments);
11008 if(!this._created){ return; }
11009 if(value){
11010 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
11011 if(widget != this && widget.checked){
11012 widget.set('checked', false);
11013 }
11014 }));
11015 }
11016 },
a089699c 11017
1354d172
AD
11018 _onClick: function(/*Event*/ e){
11019 if(this.checked || this.disabled){ // nothing to do
11020 event.stop(e);
11021 return false;
11022 }
11023 if(this.readOnly){ // ignored by some browsers so we have to resync the DOM elements with widget values
11024 event.stop(e);
11025 array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
11026 domAttr.set(this.focusNode || this.domNode, 'checked', widget.checked);
11027 }));
11028 return false;
11029 }
11030 return this.inherited(arguments);
11031 }
11032 });
11033});
a089699c 11034
1354d172
AD
11035},
11036'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\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\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></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",
11037'dojo/dnd/TimedMoveable':function(){
11038define("dojo/dnd/TimedMoveable", ["../main", "./Moveable"], function(dojo) {
11039 // module:
11040 // dojo/dnd/TimedMoveable
11041 // summary:
11042 // TODOC
a089699c 11043
1354d172
AD
11044 /*=====
11045 dojo.declare("dojo.dnd.__TimedMoveableArgs", [dojo.dnd.__MoveableArgs], {
11046 // timeout: Number
11047 // delay move by this number of ms,
11048 // accumulating position changes during the timeout
11049 timeout: 0
11050 });
11051 =====*/
a089699c 11052
1354d172
AD
11053 // precalculate long expressions
11054 var oldOnMove = dojo.dnd.Moveable.prototype.onMove;
a089699c 11055
1354d172 11056 dojo.declare("dojo.dnd.TimedMoveable", dojo.dnd.Moveable, {
a089699c 11057 // summary:
1354d172
AD
11058 // A specialized version of Moveable to support an FPS throttling.
11059 // This class puts an upper restriction on FPS, which may reduce
11060 // the CPU load. The additional parameter "timeout" regulates
11061 // the delay before actually moving the moveable object.
a089699c 11062
1354d172
AD
11063 // object attributes (for markup)
11064 timeout: 40, // in ms, 40ms corresponds to 25 fps
a089699c 11065
1354d172
AD
11066 constructor: function(node, params){
11067 // summary:
11068 // an object that makes a node moveable with a timer
11069 // node: Node||String
11070 // a node (or node's id) to be moved
11071 // params: dojo.dnd.__TimedMoveableArgs
11072 // object with additional parameters.
a089699c 11073
1354d172
AD
11074 // sanitize parameters
11075 if(!params){ params = {}; }
11076 if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
11077 this.timeout = params.timeout;
11078 }
11079 },
a089699c 11080
1354d172
AD
11081 onMoveStop: function(/* dojo.dnd.Mover */ mover){
11082 if(mover._timer){
11083 // stop timer
11084 clearTimeout(mover._timer);
11085 // reflect the last received position
11086 oldOnMove.call(this, mover, mover._leftTop)
11087 }
11088 dojo.dnd.Moveable.prototype.onMoveStop.apply(this, arguments);
11089 },
11090 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
11091 mover._leftTop = leftTop;
11092 if(!mover._timer){
11093 var _t = this; // to avoid using dojo.hitch()
11094 mover._timer = setTimeout(function(){
11095 // we don't have any pending requests
11096 mover._timer = null;
11097 // reflect the last received position
11098 oldOnMove.call(_t, mover, mover._leftTop);
11099 }, this.timeout);
11100 }
11101 }
11102 });
a089699c 11103
1354d172
AD
11104 return dojo.dnd.TimedMoveable;
11105
11106});
a089699c 11107
1354d172
AD
11108},
11109'dojo/NodeList-fx':function(){
11110define("dojo/NodeList-fx", ["dojo/_base/NodeList", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
11111 function(NodeList, lang, connectLib, baseFx, coreFx) {
11112 // module:
11113 // dojo/NodeList-fx
11114 // summary:
11115 // TODOC
a089699c 11116
1354d172
AD
11117/*=====
11118dojo["NodeList-fx"] = {
11119 // summary: Adds dojo.fx animation support to dojo.query() by extending the NodeList class
11120 // with additional FX functions. NodeList is the array-like object used to hold query results.
11121};
a089699c 11122
1354d172
AD
11123// doc alias helpers:
11124NodeList = dojo.NodeList;
11125=====*/
a089699c 11126
1354d172
AD
11127lang.extend(NodeList, {
11128 _anim: function(obj, method, args){
11129 args = args||{};
11130 var a = coreFx.combine(
11131 this.map(function(item){
11132 var tmpArgs = { node: item };
11133 lang.mixin(tmpArgs, args);
11134 return obj[method](tmpArgs);
11135 })
11136 );
11137 return args.auto ? a.play() && this : a; // dojo.Animation|dojo.NodeList
a089699c
AD
11138 },
11139
1354d172 11140 wipeIn: function(args){
a089699c 11141 // summary:
1354d172
AD
11142 // wipe in all elements of this NodeList via `dojo.fx.wipeIn`
11143 //
11144 // args: Object?
11145 // Additional dojo.Animation arguments to mix into this set with the addition of
11146 // an `auto` parameter.
11147 //
11148 // returns: dojo.Animation|dojo.NodeList
11149 // A special args member `auto` can be passed to automatically play the animation.
11150 // If args.auto is present, the original dojo.NodeList will be returned for further
11151 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11152 //
11153 // example:
11154 // Fade in all tables with class "blah":
11155 // | dojo.query("table.blah").wipeIn().play();
11156 //
11157 // example:
11158 // Utilizing `auto` to get the NodeList back:
11159 // | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
11160 //
11161 return this._anim(coreFx, "wipeIn", args); // dojo.Animation|dojo.NodeList
a089699c
AD
11162 },
11163
1354d172 11164 wipeOut: function(args){
a089699c 11165 // summary:
1354d172 11166 // wipe out all elements of this NodeList via `dojo.fx.wipeOut`
a089699c 11167 //
1354d172
AD
11168 // args: Object?
11169 // Additional dojo.Animation arguments to mix into this set with the addition of
11170 // an `auto` parameter.
11171 //
11172 // returns: dojo.Animation|dojo.NodeList
11173 // A special args member `auto` can be passed to automatically play the animation.
11174 // If args.auto is present, the original dojo.NodeList will be returned for further
11175 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11176 //
11177 // example:
11178 // Wipe out all tables with class "blah":
11179 // | dojo.query("table.blah").wipeOut().play();
11180 return this._anim(coreFx, "wipeOut", args); // dojo.Animation|dojo.NodeList
a089699c
AD
11181 },
11182
1354d172 11183 slideTo: function(args){
a089699c 11184 // summary:
1354d172
AD
11185 // slide all elements of the node list to the specified place via `dojo.fx.slideTo`
11186 //
11187 // args: Object?
11188 // Additional dojo.Animation arguments to mix into this set with the addition of
11189 // an `auto` parameter.
11190 //
11191 // returns: dojo.Animation|dojo.NodeList
11192 // A special args member `auto` can be passed to automatically play the animation.
11193 // If args.auto is present, the original dojo.NodeList will be returned for further
11194 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11195 //
11196 // example:
11197 // | Move all tables with class "blah" to 300/300:
11198 // | dojo.query("table.blah").slideTo({
11199 // | left: 40,
11200 // | top: 50
11201 // | }).play();
11202 return this._anim(coreFx, "slideTo", args); // dojo.Animation|dojo.NodeList
11203 },
a089699c 11204
a089699c 11205
1354d172
AD
11206 fadeIn: function(args){
11207 // summary:
11208 // fade in all elements of this NodeList via `dojo.fadeIn`
11209 //
11210 // args: Object?
11211 // Additional dojo.Animation arguments to mix into this set with the addition of
11212 // an `auto` parameter.
11213 //
11214 // returns: dojo.Animation|dojo.NodeList
11215 // A special args member `auto` can be passed to automatically play the animation.
11216 // If args.auto is present, the original dojo.NodeList will be returned for further
11217 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11218 //
11219 // example:
11220 // Fade in all tables with class "blah":
11221 // | dojo.query("table.blah").fadeIn().play();
11222 return this._anim(baseFx, "fadeIn", args); // dojo.Animation|dojo.NodeList
a089699c
AD
11223 },
11224
1354d172 11225 fadeOut: function(args){
a089699c 11226 // summary:
1354d172
AD
11227 // fade out all elements of this NodeList via `dojo.fadeOut`
11228 //
11229 // args: Object?
11230 // Additional dojo.Animation arguments to mix into this set with the addition of
11231 // an `auto` parameter.
11232 //
11233 // returns: dojo.Animation|dojo.NodeList
11234 // A special args member `auto` can be passed to automatically play the animation.
11235 // If args.auto is present, the original dojo.NodeList will be returned for further
11236 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11237 //
11238 // example:
11239 // Fade out all elements with class "zork":
11240 // | dojo.query(".zork").fadeOut().play();
11241 // example:
11242 // Fade them on a delay and do something at the end:
11243 // | var fo = dojo.query(".zork").fadeOut();
11244 // | dojo.connect(fo, "onEnd", function(){ /*...*/ });
11245 // | fo.play();
11246 // example:
11247 // Using `auto`:
11248 // | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
11249 //
11250 return this._anim(baseFx, "fadeOut", args); // dojo.Animation|dojo.NodeList
11251 },
a089699c 11252
1354d172
AD
11253 animateProperty: function(args){
11254 // summary:
11255 // Animate all elements of this NodeList across the properties specified.
11256 // syntax identical to `dojo.animateProperty`
11257 //
11258 // args: Object?
11259 // Additional dojo.Animation arguments to mix into this set with the addition of
11260 // an `auto` parameter.
11261 //
11262 // returns: dojo.Animation|dojo.NodeList
11263 // A special args member `auto` can be passed to automatically play the animation.
11264 // If args.auto is present, the original dojo.NodeList will be returned for further
11265 // chaining. Otherwise the dojo.Animation instance is returned and must be .play()'ed
11266 //
11267 // example:
11268 // | dojo.query(".zork").animateProperty({
11269 // | duration: 500,
11270 // | properties: {
11271 // | color: { start: "black", end: "white" },
11272 // | left: { end: 300 }
11273 // | }
11274 // | }).play();
11275 //
11276 // example:
11277 // | dojo.query(".grue").animateProperty({
11278 // | auto:true,
11279 // | properties: {
11280 // | height:240
11281 // | }
11282 // | }).onclick(handler);
11283 return this._anim(baseFx, "animateProperty", args); // dojo.Animation|dojo.NodeList
11284 },
a089699c 11285
1354d172
AD
11286 anim: function( /*Object*/ properties,
11287 /*Integer?*/ duration,
11288 /*Function?*/ easing,
11289 /*Function?*/ onEnd,
11290 /*Integer?*/ delay){
11291 // summary:
11292 // Animate one or more CSS properties for all nodes in this list.
11293 // The returned animation object will already be playing when it
11294 // is returned. See the docs for `dojo.anim` for full details.
11295 // properties: Object
11296 // the properties to animate. does NOT support the `auto` parameter like other
11297 // NodeList-fx methods.
11298 // duration: Integer?
11299 // Optional. The time to run the animations for
11300 // easing: Function?
11301 // Optional. The easing function to use.
11302 // onEnd: Function?
11303 // A function to be called when the animation ends
11304 // delay:
11305 // how long to delay playing the returned animation
11306 // example:
11307 // Another way to fade out:
11308 // | dojo.query(".thinger").anim({ opacity: 0 });
11309 // example:
11310 // animate all elements with the "thigner" class to a width of 500
11311 // pixels over half a second
11312 // | dojo.query(".thinger").anim({ width: 500 }, 700);
11313 var canim = coreFx.combine(
11314 this.map(function(item){
11315 return baseFx.animateProperty({
11316 node: item,
11317 properties: properties,
11318 duration: duration||350,
11319 easing: easing
11320 });
11321 })
11322 );
11323 if(onEnd){
11324 connectLib.connect(canim, "onEnd", onEnd);
a089699c 11325 }
1354d172
AD
11326 return canim.play(delay||0); // dojo.Animation
11327 }
11328});
a089699c 11329
1354d172
AD
11330return NodeList;
11331});
a089699c 11332
1354d172
AD
11333},
11334'dijit/form/_ListMouseMixin':function(){
11335define("dijit/form/_ListMouseMixin", [
11336 "dojo/_base/declare", // declare
11337 "dojo/_base/event", // event.stop
11338 "dojo/touch",
11339 "./_ListBase"
11340], function(declare, event, touch, _ListBase){
a089699c 11341
1354d172
AD
11342/*=====
11343var _ListBase = dijit.form._ListBase;
11344=====*/
a089699c 11345
1354d172
AD
11346// module:
11347// dijit/form/_ListMouseMixin
11348// summary:
11349// a mixin to handle mouse or touch events for a focus-less menu
11350
11351return declare( "dijit.form._ListMouseMixin", _ListBase, {
11352 // summary:
11353 // a Mixin to handle mouse or touch events for a focus-less menu
11354 // Abstract methods that must be defined externally:
11355 // onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
11356 // tags:
11357 // private
11358
11359 postCreate: function(){
11360 this.inherited(arguments);
11361 this.connect(this.domNode, touch.press, "_onMouseDown");
11362 this.connect(this.domNode, touch.release, "_onMouseUp");
11363 this.connect(this.domNode, "onmouseover", "_onMouseOver");
11364 this.connect(this.domNode, "onmouseout", "_onMouseOut");
a089699c
AD
11365 },
11366
1354d172
AD
11367 _onMouseDown: function(/*Event*/ evt){
11368 event.stop(evt);
11369 if(this._hoveredNode){
11370 this.onUnhover(this._hoveredNode);
11371 this._hoveredNode = null;
a089699c 11372 }
1354d172
AD
11373 this._isDragging = true;
11374 this._setSelectedAttr(this._getTarget(evt));
a089699c
AD
11375 },
11376
1354d172
AD
11377 _onMouseUp: function(/*Event*/ evt){
11378 event.stop(evt);
11379 this._isDragging = false;
11380 var selectedNode = this._getSelectedAttr();
11381 var target = this._getTarget(evt);
11382 var hoveredNode = this._hoveredNode;
11383 if(selectedNode && target == selectedNode){
11384 this.onClick(selectedNode);
11385 }else if(hoveredNode && target == hoveredNode){ // drag to select
11386 this._setSelectedAttr(hoveredNode);
11387 this.onClick(hoveredNode);
a089699c
AD
11388 }
11389 },
11390
1354d172
AD
11391 _onMouseOut: function(/*Event*/ /*===== evt ====*/){
11392 if(this._hoveredNode){
11393 this.onUnhover(this._hoveredNode);
11394 if(this._getSelectedAttr() == this._hoveredNode){
11395 this.onSelect(this._hoveredNode);
11396 }
11397 this._hoveredNode = null;
a089699c 11398 }
1354d172
AD
11399 if(this._isDragging){
11400 this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
11401 }
11402 },
a089699c 11403
1354d172
AD
11404 _onMouseOver: function(/*Event*/ evt){
11405 if(this._cancelDrag){
11406 var time = (new Date()).getTime();
11407 if(time > this._cancelDrag){
11408 this._isDragging = false;
a089699c 11409 }
1354d172
AD
11410 this._cancelDrag = null;
11411 }
11412 var node = this._getTarget(evt);
11413 if(!node){ return; }
11414 if(this._hoveredNode != node){
11415 if(this._hoveredNode){
11416 this._onMouseOut({ target: this._hoveredNode });
11417 }
11418 if(node && node.parentNode == this.containerNode){
11419 if(this._isDragging){
11420 this._setSelectedAttr(node);
11421 }else{
11422 this._hoveredNode = node;
11423 this.onHover(node);
a089699c 11424 }
1354d172 11425 }
a089699c 11426 }
1354d172
AD
11427 }
11428});
a089699c 11429
1354d172 11430});
a089699c 11431
1354d172
AD
11432},
11433'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
11434'dojo/cookie':function(){
11435define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo, regexp) {
11436 // module:
11437 // dojo/cookie
11438 // summary:
11439 // TODOC
a089699c 11440
a089699c 11441
1354d172
AD
11442/*=====
11443dojo.__cookieProps = function(){
11444 // expires: Date|String|Number?
11445 // If a number, the number of days from today at which the cookie
11446 // will expire. If a date, the date past which the cookie will expire.
11447 // If expires is in the past, the cookie will be deleted.
11448 // If expires is omitted or is 0, the cookie will expire when the browser closes.
11449 // path: String?
11450 // The path to use for the cookie.
11451 // domain: String?
11452 // The domain to use for the cookie.
11453 // secure: Boolean?
11454 // Whether to only send the cookie on secure connections
11455 this.expires = expires;
11456 this.path = path;
11457 this.domain = domain;
11458 this.secure = secure;
11459}
11460=====*/
a089699c 11461
a089699c 11462
1354d172
AD
11463dojo.cookie = function(/*String*/name, /*String?*/value, /*dojo.__cookieProps?*/props){
11464 // summary:
11465 // Get or set a cookie.
11466 // description:
11467 // If one argument is passed, returns the value of the cookie
11468 // For two or more arguments, acts as a setter.
11469 // name:
11470 // Name of the cookie
11471 // value:
11472 // Value for the cookie
11473 // props:
11474 // Properties for the cookie
11475 // example:
11476 // set a cookie with the JSON-serialized contents of an object which
11477 // will expire 5 days from now:
11478 // | dojo.cookie("configObj", dojo.toJson(config), { expires: 5 });
11479 //
11480 // example:
11481 // de-serialize a cookie back into a JavaScript object:
11482 // | var config = dojo.fromJson(dojo.cookie("configObj"));
11483 //
11484 // example:
11485 // delete a cookie:
11486 // | dojo.cookie("configObj", null, {expires: -1});
11487 var c = document.cookie, ret;
11488 if(arguments.length == 1){
11489 var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)"));
11490 ret = matches ? decodeURIComponent(matches[1]) : undefined;
11491 }else{
11492 props = props || {};
11493// FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
11494 var exp = props.expires;
11495 if(typeof exp == "number"){
11496 var d = new Date();
11497 d.setTime(d.getTime() + exp*24*60*60*1000);
11498 exp = props.expires = d;
a089699c 11499 }
1354d172 11500 if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
a089699c 11501
1354d172
AD
11502 value = encodeURIComponent(value);
11503 var updatedCookie = name + "=" + value, propName;
11504 for(propName in props){
11505 updatedCookie += "; " + propName;
11506 var propValue = props[propName];
11507 if(propValue !== true){ updatedCookie += "=" + propValue; }
a089699c 11508 }
1354d172
AD
11509 document.cookie = updatedCookie;
11510 }
11511 return ret; // String|undefined
11512};
a089699c 11513
1354d172
AD
11514dojo.cookie.isSupported = function(){
11515 // summary:
11516 // Use to determine if the current browser supports cookies or not.
11517 //
11518 // Returns true if user allows cookies.
11519 // Returns false if user doesn't allow cookies.
a089699c 11520
1354d172
AD
11521 if(!("cookieEnabled" in navigator)){
11522 this("__djCookieTest__", "CookiesAllowed");
11523 navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
11524 if(navigator.cookieEnabled){
11525 this("__djCookieTest__", "", {expires: -1});
11526 }
11527 }
11528 return navigator.cookieEnabled;
11529};
a089699c 11530
1354d172
AD
11531return dojo.cookie;
11532});
a089699c 11533
1354d172
AD
11534},
11535'dojo/cache':function(){
11536define("dojo/cache", ["./_base/kernel", "./text"], function(dojo, text){
11537 // module:
11538 // dojo/cache
11539 // summary:
11540 // The module defines dojo.cache by loading dojo/text.
a089699c 11541
1354d172
AD
11542 //dojo.cache is defined in dojo/text
11543 return dojo.cache;
a089699c
AD
11544});
11545
1354d172
AD
11546},
11547'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n",
11548'dijit/ProgressBar':function(){
11549require({cache:{
11550'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"}});
11551define("dijit/ProgressBar", [
11552 "require", // require.toUrl
11553 "dojo/_base/declare", // declare
11554 "dojo/dom-class", // domClass.toggle
11555 "dojo/_base/lang", // lang.mixin
11556 "dojo/number", // number.format
11557 "./_Widget",
11558 "./_TemplatedMixin",
11559 "dojo/text!./templates/ProgressBar.html"
11560], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
a089699c 11561
1354d172
AD
11562/*=====
11563 var _Widget = dijit._Widget;
11564 var _TemplatedMixin = dijit._TemplatedMixin;
11565=====*/
a089699c 11566
1354d172
AD
11567// module:
11568// dijit/ProgressBar
11569// summary:
11570// A progress indication widget, showing the amount completed
11571// (often the percentage completed) of a task.
a089699c
AD
11572
11573
1354d172
AD
11574return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
11575 // summary:
11576 // A progress indication widget, showing the amount completed
11577 // (often the percentage completed) of a task.
11578 //
11579 // example:
11580 // | <div data-dojo-type="ProgressBar"
11581 // | places="0"
11582 // | value="..." maximum="...">
11583 // | </div>
a089699c 11584
1354d172
AD
11585 // progress: [const] String (Percentage or Number)
11586 // Number or percentage indicating amount of task completed.
11587 // Deprecated. Use "value" instead.
11588 progress: "0",
a089699c 11589
1354d172
AD
11590 // value: String (Percentage or Number)
11591 // Number or percentage indicating amount of task completed.
11592 // With "%": percentage value, 0% <= progress <= 100%, or
11593 // without "%": absolute value, 0 <= progress <= maximum.
11594 // Infinity means that the progress bar is indeterminate.
11595 value: "",
a089699c 11596
1354d172
AD
11597 // maximum: [const] Float
11598 // Max sample number
11599 maximum: 100,
a089699c 11600
1354d172
AD
11601 // places: [const] Number
11602 // Number of places to show in values; 0 by default
11603 places: 0,
a089699c 11604
1354d172
AD
11605 // indeterminate: [const] Boolean
11606 // If false: show progress value (number or percentage).
11607 // If true: show that a process is underway but that the amount completed is unknown.
11608 // Deprecated. Use "value" instead.
11609 indeterminate: false,
a089699c 11610
1354d172
AD
11611 // label: String?
11612 // Label on progress bar. Defaults to percentage for determinate progress bar and
11613 // blank for indeterminate progress bar.
11614 label:"",
a089699c 11615
1354d172
AD
11616 // name: String
11617 // this is the field name (for a form) if set. This needs to be set if you want to use
11618 // this widget in a dijit.form.Form widget (such as dijit.Dialog)
11619 name: '',
a089699c 11620
1354d172 11621 templateString: template,
a089699c 11622
1354d172
AD
11623 // _indeterminateHighContrastImagePath: [private] URL
11624 // URL to image to use for indeterminate progress bar when display is in high contrast mode
11625 _indeterminateHighContrastImagePath:
11626 require.toUrl("./themes/a11y/indeterminate_progress.gif"),
a089699c 11627
1354d172
AD
11628 postMixInProperties: function(){
11629 this.inherited(arguments);
11630 if(!("value" in this.params)){
11631 this.value = this.indeterminate ? Infinity : this.progress;
11632 }
11633 },
81bea17a 11634
1354d172
AD
11635 buildRendering: function(){
11636 this.inherited(arguments);
11637 this.indeterminateHighContrastImage.setAttribute("src",
11638 this._indeterminateHighContrastImagePath.toString());
11639 this.update();
11640 },
a089699c 11641
1354d172
AD
11642 update: function(/*Object?*/attributes){
11643 // summary:
11644 // Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
11645 // set("value", ...) rather than calling this method directly.
11646 // attributes:
11647 // May provide progress and/or maximum properties on this parameter;
11648 // see attribute specs for details.
11649 // example:
11650 // | myProgressBar.update({'indeterminate': true});
11651 // | myProgressBar.update({'progress': 80});
11652 // | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
11653 // tags:
11654 // private
a089699c 11655
1354d172 11656 // TODO: deprecate this method and use set() instead
81bea17a 11657
1354d172
AD
11658 lang.mixin(this, attributes || {});
11659 var tip = this.internalProgress, ap = this.domNode;
11660 var percent = 1;
11661 if(this.indeterminate){
11662 ap.removeAttribute("aria-valuenow");
11663 ap.removeAttribute("aria-valuemin");
11664 ap.removeAttribute("aria-valuemax");
11665 }else{
11666 if(String(this.progress).indexOf("%") != -1){
11667 percent = Math.min(parseFloat(this.progress)/100, 1);
11668 this.progress = percent * this.maximum;
11669 }else{
11670 this.progress = Math.min(this.progress, this.maximum);
11671 percent = this.maximum ? this.progress / this.maximum : 0;
11672 }
a089699c 11673
1354d172
AD
11674 ap.setAttribute("aria-describedby", this.labelNode.id);
11675 ap.setAttribute("aria-valuenow", this.progress);
11676 ap.setAttribute("aria-valuemin", 0);
11677 ap.setAttribute("aria-valuemax", this.maximum);
11678 }
11679 this.labelNode.innerHTML = this.report(percent);
a089699c 11680
1354d172
AD
11681 domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
11682 tip.style.width = (percent * 100) + "%";
11683 this.onChange();
11684 },
a089699c 11685
1354d172
AD
11686 _setValueAttr: function(v){
11687 this._set("value", v);
11688 if(v == Infinity){
11689 this.update({indeterminate:true});
11690 }else{
11691 this.update({indeterminate:false, progress:v});
11692 }
11693 },
a089699c 11694
1354d172
AD
11695 _setLabelAttr: function(label){
11696 this._set("label", label);
11697 this.update();
11698 },
a089699c 11699
1354d172
AD
11700 _setIndeterminateAttr: function(indeterminate){
11701 // Deprecated, use set("value", ...) instead
11702 this.indeterminate = indeterminate;
11703 this.update();
11704 },
a089699c 11705
1354d172
AD
11706 report: function(/*float*/percent){
11707 // summary:
11708 // Generates message to show inside progress bar (normally indicating amount of task completed).
11709 // May be overridden.
11710 // tags:
11711 // extension
a089699c 11712
1354d172
AD
11713 return this.label ? this.label :
11714 (this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
11715 },
a089699c 11716
1354d172
AD
11717 onChange: function(){
11718 // summary:
11719 // Callback fired when progress updates.
11720 // tags:
11721 // extension
11722 }
11723});
a089699c 11724
1354d172 11725});
a089699c 11726
1354d172
AD
11727},
11728'dijit/_base/popup':function(){
11729define("dijit/_base/popup", [
11730 "dojo/dom-class", // domClass.contains
11731 "../popup",
11732 "../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
11733], function(domClass, popup){
11734
11735// module:
11736// dijit/_base/popup
11737// summary:
11738// Old module for popups, new code should use dijit/popup directly
11739
11740
11741// Hack support for old API passing in node instead of a widget (to various methods)
11742var origCreateWrapper = popup._createWrapper;
11743popup._createWrapper = function(widget){
11744 if(!widget.declaredClass){
11745 // make fake widget to pass to new API
11746 widget = {
11747 _popupWrapper: (widget.parentNode && domClass.contains(widget.parentNode, "dijitPopup")) ?
11748 widget.parentNode : null,
11749 domNode: widget,
11750 destroy: function(){}
11751 };
11752 }
11753 return origCreateWrapper.call(this, widget);
11754};
a089699c 11755
1354d172
AD
11756// Support old format of orient parameter
11757var origOpen = popup.open;
11758popup.open = function(/*dijit.popup.__OpenArgs*/ args){
11759 // Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
11760 // Don't do conversion for:
11761 // - null parameter (that means to use the default positioning)
11762 // - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
11763 // - new format, ex: ["below", "above"]
11764 // - return value from deprecated dijit.getPopupAroundAlignment() method,
11765 // ex: ["below", "above"]
11766 if(args.orient && typeof args.orient != "string" && !("length" in args.orient)){
11767 var ary = [];
11768 for(var key in args.orient){
11769 ary.push({aroundCorner: key, corner: args.orient[key]});
11770 }
11771 args.orient = ary;
11772 }
a089699c 11773
1354d172
AD
11774 return origOpen.call(this, args);
11775};
a089699c 11776
1354d172
AD
11777return popup;
11778});
a089699c 11779
1354d172
AD
11780},
11781'dijit/ColorPalette':function(){
11782require({cache:{
11783'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"}});
11784define("dijit/ColorPalette", [
11785 "require", // require.toUrl
11786 "dojo/text!./templates/ColorPalette.html",
11787 "./_Widget",
11788 "./_TemplatedMixin",
11789 "./_PaletteMixin",
11790 "dojo/i18n", // i18n.getLocalization
11791 "dojo/_base/Color", // dojo.Color dojo.Color.named
11792 "dojo/_base/declare", // declare
11793 "dojo/dom-class", // domClass.contains
11794 "dojo/dom-construct", // domConstruct.place
11795 "dojo/_base/window", // win.body
11796 "dojo/string", // string.substitute
11797 "dojo/i18n!dojo/nls/colors", // translations
11798 "dojo/colors" // extend dojo.Color w/names of other colors
11799], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, i18n, Color,
11800 declare, domClass, domConstruct, win, string){
a089699c 11801
1354d172
AD
11802/*=====
11803 var _Widget = dijit._Widget;
11804 var _TemplatedMixin = dijit._TemplatedMixin;
11805 var _PaletteMixin = dijit._PaletteMixin;
11806=====*/
a089699c 11807
1354d172
AD
11808// module:
11809// dijit/ColorPalette
11810// summary:
11811// A keyboard accessible color-picking widget
a089699c 11812
1354d172
AD
11813var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
11814 // summary:
11815 // A keyboard accessible color-picking widget
11816 // description:
11817 // Grid showing various colors, so the user can pick a certain color.
11818 // Can be used standalone, or as a popup.
11819 //
11820 // example:
11821 // | <div data-dojo-type="dijit.ColorPalette"></div>
11822 //
11823 // example:
11824 // | var picker = new dijit.ColorPalette({ },srcNode);
11825 // | picker.startup();
a089699c
AD
11826
11827
1354d172
AD
11828 // palette: [const] String
11829 // Size of grid, either "7x10" or "3x4".
11830 palette: "7x10",
a089699c 11831
1354d172
AD
11832 // _palettes: [protected] Map
11833 // This represents the value of the colors.
11834 // The first level is a hashmap of the different palettes available.
11835 // The next two dimensions represent the columns and rows of colors.
11836 _palettes: {
11837 "7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
11838 ["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
11839 ["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
11840 ["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
11841 ["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
11842 ["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
11843 ["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
a089699c 11844
1354d172
AD
11845 "3x4": [["white", "lime", "green", "blue"],
11846 ["silver", "yellow", "fuchsia", "navy"],
11847 ["gray", "red", "purple", "black"]]
11848 },
a089699c 11849
1354d172
AD
11850 // templateString: String
11851 // The template of this widget.
11852 templateString: template,
81bea17a 11853
1354d172 11854 baseClass: "dijitColorPalette",
a089699c 11855
1354d172
AD
11856 _dyeFactory: function(value, row, col){
11857 // Overrides _PaletteMixin._dyeFactory().
11858 return new this._dyeClass(value, row, col);
11859 },
a089699c 11860
1354d172
AD
11861 buildRendering: function(){
11862 // Instantiate the template, which makes a skeleton into which we'll insert a bunch of
11863 // <img> nodes
11864 this.inherited(arguments);
a089699c 11865
1354d172
AD
11866 // Creates customized constructor for dye class (color of a single cell) for
11867 // specified palette and high-contrast vs. normal mode. Used in _getDye().
11868 this._dyeClass = declare(ColorPalette._Color, {
11869 hc: domClass.contains(win.body(), "dijit_a11y"),
11870 palette: this.palette
11871 });
a089699c 11872
1354d172
AD
11873 // Creates <img> nodes in each cell of the template.
11874 this._preparePalette(
11875 this._palettes[this.palette],
11876 i18n.getLocalization("dojo", "colors", this.lang));
11877 }
11878});
a089699c 11879
1354d172
AD
11880ColorPalette._Color = declare("dijit._Color", Color, {
11881 // summary:
11882 // Object associated with each cell in a ColorPalette palette.
11883 // Implements dijit.Dye.
a089699c 11884
1354d172
AD
11885 // Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
11886 // node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
11887 // for showing the color.
11888 template:
11889 "<span class='dijitInline dijitPaletteImg'>" +
11890 "<img src='${blankGif}' alt='${alt}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
11891 "</span>",
a089699c 11892
1354d172
AD
11893 // Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
11894 // but scrolled and clipped to show the correct color only
11895 hcTemplate:
11896 "<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
11897 "<img src='${image}' alt='${alt}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
11898 "</span>",
a089699c 11899
1354d172
AD
11900 // _imagePaths: [protected] Map
11901 // This is stores the path to the palette images used for high-contrast mode display
11902 _imagePaths: {
11903 "7x10": require.toUrl("./themes/a11y/colors7x10.png"),
11904 "3x4": require.toUrl("./themes/a11y/colors3x4.png")
11905 },
a089699c 11906
1354d172
AD
11907 constructor: function(/*String*/alias, /*Number*/ row, /*Number*/ col){
11908 this._alias = alias;
11909 this._row = row;
11910 this._col = col;
11911 this.setColor(Color.named[alias]);
11912 },
a089699c 11913
1354d172
AD
11914 getValue: function(){
11915 // summary:
11916 // Note that although dijit._Color is initialized with a value like "white" getValue() always
11917 // returns a hex value
11918 return this.toHex();
11919 },
a089699c 11920
1354d172
AD
11921 fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
11922 var html = string.substitute(this.hc ? this.hcTemplate : this.template, {
11923 // substitution variables for normal mode
11924 color: this.toHex(),
11925 blankGif: blankGif,
11926 alt: this._alias,
a089699c 11927
1354d172
AD
11928 // variables used for high contrast mode
11929 image: this._imagePaths[this.palette].toString(),
11930 left: this._col * -20 - 5,
11931 top: this._row * -20 - 5,
11932 size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
11933 });
a089699c 11934
1354d172
AD
11935 domConstruct.place(html, cell);
11936 }
11937});
a089699c 11938
a089699c 11939
1354d172
AD
11940return ColorPalette;
11941});
a089699c 11942
1354d172
AD
11943},
11944'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
11945'dojo/_base/url':function(){
11946define("dojo/_base/url", ["./kernel"], function(dojo) {
11947 // module:
11948 // dojo/url
11949 // summary:
11950 // This module contains dojo._Url
11951
11952 var
11953 ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
11954 ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
11955 _Url = function(){
11956 var n = null,
11957 _a = arguments,
11958 uri = [_a[0]];
11959 // resolve uri components relative to each other
11960 for(var i = 1; i<_a.length; i++){
11961 if(!_a[i]){ continue; }
11962
11963 // Safari doesn't support this.constructor so we have to be explicit
11964 // FIXME: Tracked (and fixed) in Webkit bug 3537.
11965 // http://bugs.webkit.org/show_bug.cgi?id=3537
11966 var relobj = new _Url(_a[i]+""),
11967 uriobj = new _Url(uri[0]+"");
11968
11969 if(
11970 relobj.path == "" &&
11971 !relobj.scheme &&
11972 !relobj.authority &&
11973 !relobj.query
11974 ){
11975 if(relobj.fragment != n){
11976 uriobj.fragment = relobj.fragment;
11977 }
11978 relobj = uriobj;
11979 }else if(!relobj.scheme){
11980 relobj.scheme = uriobj.scheme;
11981
11982 if(!relobj.authority){
11983 relobj.authority = uriobj.authority;
11984
11985 if(relobj.path.charAt(0) != "/"){
11986 var path = uriobj.path.substring(0,
11987 uriobj.path.lastIndexOf("/") + 1) + relobj.path;
11988
11989 var segs = path.split("/");
11990 for(var j = 0; j < segs.length; j++){
11991 if(segs[j] == "."){
11992 // flatten "./" references
11993 if(j == segs.length - 1){
11994 segs[j] = "";
11995 }else{
11996 segs.splice(j, 1);
11997 j--;
11998 }
11999 }else if(j > 0 && !(j == 1 && segs[0] == "") &&
12000 segs[j] == ".." && segs[j-1] != ".."){
12001 // flatten "../" references
12002 if(j == (segs.length - 1)){
12003 segs.splice(j, 1);
12004 segs[j - 1] = "";
12005 }else{
12006 segs.splice(j - 1, 2);
12007 j -= 2;
12008 }
12009 }
12010 }
12011 relobj.path = segs.join("/");
12012 }
12013 }
12014 }
a089699c 12015
1354d172
AD
12016 uri = [];
12017 if(relobj.scheme){
12018 uri.push(relobj.scheme, ":");
12019 }
12020 if(relobj.authority){
12021 uri.push("//", relobj.authority);
12022 }
12023 uri.push(relobj.path);
12024 if(relobj.query){
12025 uri.push("?", relobj.query);
12026 }
12027 if(relobj.fragment){
12028 uri.push("#", relobj.fragment);
12029 }
a089699c 12030 }
a089699c 12031
1354d172 12032 this.uri = uri.join("");
a089699c 12033
1354d172
AD
12034 // break the uri into its main components
12035 var r = this.uri.match(ore);
a089699c 12036
1354d172
AD
12037 this.scheme = r[2] || (r[1] ? "" : n);
12038 this.authority = r[4] || (r[3] ? "" : n);
12039 this.path = r[5]; // can never be undefined
12040 this.query = r[7] || (r[6] ? "" : n);
12041 this.fragment = r[9] || (r[8] ? "" : n);
a089699c 12042
1354d172
AD
12043 if(this.authority != n){
12044 // server based naming authority
12045 r = this.authority.match(ire);
12046
12047 this.user = r[3] || n;
12048 this.password = r[4] || n;
12049 this.host = r[6] || r[7]; // ipv6 || ipv4
12050 this.port = r[9] || n;
a089699c 12051 }
1354d172
AD
12052 };
12053 _Url.prototype.toString = function(){ return this.uri; };
a089699c 12054
1354d172
AD
12055 return dojo._Url = _Url;
12056});
a089699c 12057
1354d172
AD
12058},
12059'dojo/text':function(){
12060define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo, require, has, xhr){
12061 // module:
12062 // dojo/text
12063 // summary:
12064 // This module implements the !dojo/text plugin and the dojo.cache API.
12065 // description:
12066 // We choose to include our own plugin to leverage functionality already contained in dojo
12067 // and thereby reduce the size of the plugin compared to various foreign loader implementations.
12068 // Also, this allows foreign AMD loaders to be used without their plugins.
12069 //
12070 // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
12071 // loader. This feature is outside the scope of the CommonJS plugins specification.
a089699c 12072
1354d172
AD
12073 var getText;
12074 if(1){
12075 getText= function(url, sync, load){
12076 xhr("GET", {url:url, sync:!!sync, load:load});
12077 };
12078 }else{
12079 // TODOC: only works for dojo AMD loader
12080 if(require.getText){
12081 getText= require.getText;
12082 }else{
12083 console.error("dojo/text plugin failed to load because loader does not support getText");
12084 }
12085 }
a089699c 12086
1354d172
AD
12087 var
12088 theCache= {},
12089
12090 strip= function(text){
12091 //Strips <?xml ...?> declarations so that external SVG and XML
12092 //documents can be added to a document without worry. Also, if the string
12093 //is an HTML document, only the part inside the body tag is returned.
12094 if(text){
12095 text= text.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
12096 var matches= text.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
12097 if(matches){
12098 text= matches[1];
a089699c 12099 }
a089699c 12100 }else{
1354d172 12101 text = "";
a089699c 12102 }
1354d172
AD
12103 return text;
12104 },
a089699c 12105
1354d172 12106 notFound = {},
a089699c 12107
1354d172 12108 pending = {},
a089699c 12109
1354d172
AD
12110 result= {
12111 dynamic:
12112 // the dojo/text caches it's own resources because of dojo.cache
12113 true,
12114
12115 normalize:function(id, toAbsMid){
12116 // id is something like (path may be relative):
12117 //
12118 // "path/to/text.html"
12119 // "path/to/text.html!strip"
12120 var parts= id.split("!"),
12121 url= parts[0];
12122 return (/^\./.test(url) ? toAbsMid(url) : url) + (parts[1] ? "!" + parts[1] : "");
12123 },
12124
12125 load:function(id, require, load){
12126 // id is something like (path is always absolute):
12127 //
12128 // "path/to/text.html"
12129 // "path/to/text.html!strip"
12130 var
12131 parts= id.split("!"),
12132 stripFlag= parts.length>1,
12133 absMid= parts[0],
12134 url = require.toUrl(parts[0]),
12135 text = notFound,
12136 finish = function(text){
12137 load(stripFlag ? strip(text) : text);
12138 };
12139 if(absMid in theCache){
12140 text = theCache[absMid];
12141 }else if(url in require.cache){
12142 text = require.cache[url];
12143 }else if(url in theCache){
12144 text = theCache[url];
12145 }
12146 if(text===notFound){
12147 if(pending[url]){
12148 pending[url].push(finish);
12149 }else{
12150 var pendingList = pending[url] = [finish];
12151 getText(url, !require.async, function(text){
12152 theCache[absMid]= theCache[url]= text;
12153 for(var i = 0; i<pendingList.length;){
12154 pendingList[i++](text);
12155 }
12156 delete pending[url];
12157 });
12158 }
a089699c 12159 }else{
1354d172 12160 finish(text);
a089699c 12161 }
1354d172
AD
12162 }
12163 };
12164
12165 dojo.cache= function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){
12166 // * (string string [value]) => (module, url, value)
12167 // * (object [value]) => (module, value), url defaults to ""
12168 //
12169 // * if module is an object, then it must be convertable to a string
12170 // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
12171 // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
12172 var key;
12173 if(typeof module=="string"){
12174 if(/\//.test(module)){
12175 // module is a version 1.7+ resolved path
12176 key = module;
12177 value = url;
a089699c 12178 }else{
1354d172
AD
12179 // module is a version 1.6- argument to dojo.moduleUrl
12180 key = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : ""));
a089699c 12181 }
1354d172
AD
12182 }else{
12183 key = module + "";
12184 value = url;
12185 }
12186 var
12187 val = (value != undefined && typeof value != "string") ? value.value : value,
12188 sanitize = value && value.sanitize;
a089699c 12189
1354d172
AD
12190 if(typeof val == "string"){
12191 //We have a string, set cache value
12192 theCache[key] = val;
12193 return sanitize ? strip(val) : val;
12194 }else if(val === null){
12195 //Remove cached value
12196 delete theCache[key];
12197 return null;
12198 }else{
12199 //Allow cache values to be empty strings. If key property does
12200 //not exist, fetch it.
12201 if(!(key in theCache)){
12202 getText(key, true, function(text){
12203 theCache[key]= text;
a089699c
AD
12204 });
12205 }
1354d172
AD
12206 return sanitize ? strip(theCache[key]) : theCache[key];
12207 }
12208 };
a089699c 12209
1354d172 12210 return result;
a089699c 12211
1354d172
AD
12212/*=====
12213dojo.cache = function(module, url, value){
12214 // summary:
12215 // A getter and setter for storing the string content associated with the
12216 // module and url arguments.
12217 // description:
12218 // If module is a string that contains slashes, then it is interpretted as a fully
12219 // resolved path (typically a result returned by require.toUrl), and url should not be
12220 // provided. This is the preferred signature. If module is a string that does not
12221 // contain slashes, then url must also be provided and module and url are used to
12222 // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
12223 // If value is specified, the cache value for the moduleUrl will be set to
12224 // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it
12225 // in its internal cache and return that cached value for the URL. To clear
12226 // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the
12227 // the URL contents, only modules on the same domain of the page can use this capability.
12228 // The build system can inline the cache values though, to allow for xdomain hosting.
12229 // module: String||Object
12230 // If a String with slashes, a fully resolved path; if a String without slashes, the
12231 // module name to use for the base part of the URL, similar to module argument
12232 // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that
12233 // generates a valid path for the cache item. For example, a dojo._Url object.
12234 // url: String
12235 // The rest of the path to append to the path derived from the module argument. If
12236 // module is an object, then this second argument should be the "value" argument instead.
12237 // value: String||Object?
12238 // If a String, the value to use in the cache for the module/url combination.
12239 // If an Object, it can have two properties: value and sanitize. The value property
12240 // should be the value to use in the cache, and sanitize can be set to true or false,
12241 // to indicate if XML declarations should be removed from the value and if the HTML
12242 // inside a body tag in the value should be extracted as the real value. The value argument
12243 // or the value property on the value argument are usually only used by the build system
12244 // as it inlines cache content.
12245 // example:
12246 // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style
12247 // of call is used to avoid an issue with the build system erroneously trying to intern
12248 // this example. To get the build system to intern your dojo.cache calls, use the
12249 // "dojo.cache" style of call):
12250 // | //If template.html contains "<h1>Hello</h1>" that will be
12251 // | //the value for the text variable.
12252 // | var text = dojo["cache"]("my.module", "template.html");
12253 // example:
12254 // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input
12255 // (the dojo["cache"] style of call is used to avoid an issue with the build system
12256 // erroneously trying to intern this example. To get the build system to intern your
12257 // dojo.cache calls, use the "dojo.cache" style of call):
12258 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12259 // | //text variable will contain just "<h1>Hello</h1>".
12260 // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true});
12261 // example:
12262 // Same example as previous, but demostrates how an object can be passed in as
12263 // the first argument, then the value argument can then be the second argument.
12264 // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the
12265 // | //text variable will contain just "<h1>Hello</h1>".
12266 // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true});
12267 return val; //String
12268};
12269=====*/
12270});
a089699c 12271
81bea17a 12272
1354d172
AD
12273},
12274'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\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",
12275'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
12276'dojo/uacss':function(){
12277define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./_base/sniff", "./_base/window"],
12278 function(geometry, lang, ready, has, baseWindow){
12279 // module:
12280 // dojo/uacss
12281 // summary:
12282 // Applies pre-set CSS classes to the top-level HTML node, based on:
12283 // - browser (ex: dj_ie)
12284 // - browser version (ex: dj_ie6)
12285 // - box model (ex: dj_contentBox)
12286 // - text direction (ex: dijitRtl)
12287 //
12288 // In addition, browser, browser version, and box model are
12289 // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl.
a089699c 12290
1354d172
AD
12291 var
12292 html = baseWindow.doc.documentElement,
12293 ie = has("ie"),
12294 opera = has("opera"),
12295 maj = Math.floor,
12296 ff = has("ff"),
12297 boxModel = geometry.boxModel.replace(/-/,''),
81bea17a 12298
1354d172
AD
12299 classes = {
12300 "dj_ie": ie,
12301 "dj_ie6": maj(ie) == 6,
12302 "dj_ie7": maj(ie) == 7,
12303 "dj_ie8": maj(ie) == 8,
12304 "dj_ie9": maj(ie) == 9,
12305 "dj_quirks": has("quirks"),
12306 "dj_iequirks": ie && has("quirks"),
a089699c 12307
1354d172
AD
12308 // NOTE: Opera not supported by dijit
12309 "dj_opera": opera,
a089699c 12310
1354d172 12311 "dj_khtml": has("khtml"),
a089699c 12312
1354d172
AD
12313 "dj_webkit": has("webkit"),
12314 "dj_safari": has("safari"),
12315 "dj_chrome": has("chrome"),
a089699c 12316
1354d172
AD
12317 "dj_gecko": has("mozilla"),
12318 "dj_ff3": maj(ff) == 3
12319 }; // no dojo unsupported browsers
a089699c 12320
1354d172 12321 classes["dj_" + boxModel] = true;
a089699c 12322
1354d172
AD
12323 // apply browser, browser version, and box model class names
12324 var classStr = "";
12325 for(var clz in classes){
12326 if(classes[clz]){
12327 classStr += clz + " ";
12328 }
12329 }
12330 html.className = lang.trim(html.className + " " + classStr);
81bea17a 12331
1354d172
AD
12332 // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension.
12333 // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl).
12334 // priority is 90 to run ahead of parser priority of 100
12335 ready(90, function(){
12336 if(!geometry.isBodyLtr()){
12337 var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ");
12338 html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "));
12339 }
12340 });
12341 return has;
12342});
81bea17a 12343
1354d172
AD
12344},
12345'dijit/Tooltip':function(){
12346require({cache:{
12347'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"}});
12348define("dijit/Tooltip", [
12349 "dojo/_base/array", // array.forEach array.indexOf array.map
12350 "dojo/_base/declare", // declare
12351 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
12352 "dojo/dom", // dom.byId
12353 "dojo/dom-class", // domClass.add
12354 "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
12355 "dojo/dom-style", // domStyle.set, domStyle.get
12356 "dojo/_base/lang", // lang.hitch lang.isArrayLike
12357 "dojo/_base/sniff", // has("ie")
12358 "dojo/_base/window", // win.body
12359 "./_base/manager", // manager.defaultDuration
12360 "./place",
12361 "./_Widget",
12362 "./_TemplatedMixin",
12363 "./BackgroundIframe",
12364 "dojo/text!./templates/Tooltip.html",
12365 "." // sets dijit.showTooltip etc. for back-compat
12366], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, has, win,
12367 manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
a089699c 12368
1354d172
AD
12369/*=====
12370 var _Widget = dijit._Widget;
12371 var BackgroundIframe = dijit.BackgroundIframe;
12372 var _TemplatedMixin = dijit._TemplatedMixin;
12373=====*/
a089699c 12374
1354d172
AD
12375 // module:
12376 // dijit/Tooltip
12377 // summary:
12378 // Defines dijit.Tooltip widget (to display a tooltip), showTooltip()/hideTooltip(), and _MasterTooltip
a089699c 12379
81bea17a 12380
1354d172
AD
12381 var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
12382 // summary:
12383 // Internal widget that holds the actual tooltip markup,
12384 // which occurs once per page.
12385 // Called by Tooltip widgets which are just containers to hold
12386 // the markup
12387 // tags:
12388 // protected
a089699c 12389
1354d172
AD
12390 // duration: Integer
12391 // Milliseconds to fade in/fade out
12392 duration: manager.defaultDuration,
a089699c 12393
1354d172 12394 templateString: template,
a089699c 12395
1354d172
AD
12396 postCreate: function(){
12397 win.body().appendChild(this.domNode);
a089699c 12398
1354d172
AD
12399 this.bgIframe = new BackgroundIframe(this.domNode);
12400
12401 // Setup fade-in and fade-out functions.
12402 this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
12403 this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
a089699c
AD
12404 },
12405
1354d172 12406 show: function(innerHTML, aroundNode, position, rtl, textDir){
a089699c 12407 // summary:
1354d172
AD
12408 // Display tooltip w/specified contents to right of specified node
12409 // (To left if there's no space on the right, or if rtl == true)
12410 // innerHTML: String
12411 // Contents of the tooltip
12412 // aroundNode: DomNode || dijit.__Rectangle
12413 // Specifies that tooltip should be next to this node / area
12414 // position: String[]?
12415 // List of positions to try to position tooltip (ex: ["right", "above"])
12416 // rtl: Boolean?
12417 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
12418 // means "rtl"; specifies GUI direction, not text direction.
12419 // textDir: String?
12420 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
12421
12422
12423 if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
12424 return;
81bea17a 12425 }
81bea17a 12426
1354d172
AD
12427 // reset width; it may have been set by orient() on a previous tooltip show()
12428 this.domNode.width = "auto";
81bea17a 12429
1354d172
AD
12430 if(this.fadeOut.status() == "playing"){
12431 // previous tooltip is being hidden; wait until the hide completes then show new one
12432 this._onDeck=arguments;
12433 return;
12434 }
12435 this.containerNode.innerHTML=innerHTML;
12436
12437 this.set("textDir", textDir);
12438 this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
81bea17a 12439
1354d172
AD
12440 var pos = place.around(this.domNode, aroundNode,
12441 position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
81bea17a 12442
1354d172
AD
12443 // Position the tooltip connector for middle alignment.
12444 // This could not have been done in orient() since the tooltip wasn't positioned at that time.
12445 var aroundNodeCoords = pos.aroundNodePos;
12446 if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
12447 this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
12448 this.connectorNode.style.left = "";
12449 }else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
12450 this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
12451 }
81bea17a 12452
1354d172
AD
12453 // show it
12454 domStyle.set(this.domNode, "opacity", 0);
12455 this.fadeIn.play();
12456 this.isShowingNow = true;
12457 this.aroundNode = aroundNode;
12458 },
81bea17a 12459
1354d172
AD
12460 orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
12461 // summary:
12462 // Private function to set CSS for tooltip node based on which position it's in.
12463 // This is called by the dijit popup code. It will also reduce the tooltip's
12464 // width to whatever width is available
12465 // tags:
12466 // protected
12467 this.connectorNode.style.top = ""; //reset to default
81bea17a 12468
1354d172
AD
12469 //Adjust the spaceAvailable width, without changing the spaceAvailable object
12470 var tooltipSpaceAvaliableWidth = spaceAvailable.w - this.connectorNode.offsetWidth;
81bea17a 12471
1354d172
AD
12472 node.className = "dijitTooltip " +
12473 {
12474 "MR-ML": "dijitTooltipRight",
12475 "ML-MR": "dijitTooltipLeft",
12476 "TM-BM": "dijitTooltipAbove",
12477 "BM-TM": "dijitTooltipBelow",
12478 "BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
12479 "TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
12480 "BR-TR": "dijitTooltipBelow dijitTooltipABRight",
12481 "TR-BR": "dijitTooltipAbove dijitTooltipABRight",
12482 "BR-BL": "dijitTooltipRight",
12483 "BL-BR": "dijitTooltipLeft"
12484 }[aroundCorner + "-" + tooltipCorner];
81bea17a 12485
1354d172
AD
12486 // reduce tooltip's width to the amount of width available, so that it doesn't overflow screen
12487 this.domNode.style.width = "auto";
12488 var size = domGeometry.getContentBox(this.domNode);
81bea17a 12489
1354d172
AD
12490 var width = Math.min((Math.max(tooltipSpaceAvaliableWidth,1)), size.w);
12491 var widthWasReduced = width < size.w;
81bea17a 12492
1354d172 12493 this.domNode.style.width = width+"px";
81bea17a 12494
1354d172
AD
12495 //Adjust width for tooltips that have a really long word or a nowrap setting
12496 if(widthWasReduced){
12497 this.containerNode.style.overflow = "auto"; //temp change to overflow to detect if our tooltip needs to be wider to support the content
12498 var scrollWidth = this.containerNode.scrollWidth;
12499 this.containerNode.style.overflow = "visible"; //change it back
12500 if(scrollWidth > width){
12501 scrollWidth = scrollWidth + domStyle.get(this.domNode,"paddingLeft") + domStyle.get(this.domNode,"paddingRight");
12502 this.domNode.style.width = scrollWidth + "px";
12503 }
12504 }
81bea17a 12505
1354d172
AD
12506 // Reposition the tooltip connector.
12507 if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
12508 var mb = domGeometry.getMarginBox(node);
12509 var tooltipConnectorHeight = this.connectorNode.offsetHeight;
12510 if(mb.h > spaceAvailable.h){
12511 // The tooltip starts at the top of the page and will extend past the aroundNode
12512 var aroundNodePlacement = spaceAvailable.h - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
12513 this.connectorNode.style.top = aroundNodePlacement + "px";
12514 this.connectorNode.style.bottom = "";
12515 }else{
12516 // Align center of connector with center of aroundNode, except don't let bottom
12517 // of connector extend below bottom of tooltip content, or top of connector
12518 // extend past top of tooltip content
12519 this.connectorNode.style.bottom = Math.min(
12520 Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
12521 mb.h - tooltipConnectorHeight) + "px";
12522 this.connectorNode.style.top = "";
12523 }
12524 }else{
12525 // reset the tooltip back to the defaults
12526 this.connectorNode.style.top = "";
12527 this.connectorNode.style.bottom = "";
12528 }
81bea17a 12529
1354d172
AD
12530 return Math.max(0, size.w - tooltipSpaceAvaliableWidth);
12531 },
81bea17a 12532
1354d172
AD
12533 _onShow: function(){
12534 // summary:
12535 // Called at end of fade-in operation
12536 // tags:
12537 // protected
12538 if(has("ie")){
12539 // the arrow won't show up on a node w/an opacity filter
12540 this.domNode.style.filter="";
12541 }
12542 },
81bea17a 12543
1354d172
AD
12544 hide: function(aroundNode){
12545 // summary:
12546 // Hide the tooltip
81bea17a 12547
1354d172
AD
12548 if(this._onDeck && this._onDeck[1] == aroundNode){
12549 // this hide request is for a show() that hasn't even started yet;
12550 // just cancel the pending show()
12551 this._onDeck=null;
12552 }else if(this.aroundNode === aroundNode){
12553 // this hide request is for the currently displayed tooltip
12554 this.fadeIn.stop();
12555 this.isShowingNow = false;
12556 this.aroundNode = null;
12557 this.fadeOut.play();
81bea17a 12558 }else{
1354d172 12559 // just ignore the call, it's for a tooltip that has already been erased
a089699c 12560 }
1354d172 12561 },
a089699c 12562
1354d172
AD
12563 _onHide: function(){
12564 // summary:
12565 // Called at end of fade-out operation
12566 // tags:
12567 // protected
a089699c 12568
1354d172
AD
12569 this.domNode.style.cssText=""; // to position offscreen again
12570 this.containerNode.innerHTML="";
12571 if(this._onDeck){
12572 // a show request has been queued up; do it now
12573 this.show.apply(this, this._onDeck);
12574 this._onDeck=null;
12575 }
12576 },
12577
12578 _setAutoTextDir: function(/*Object*/node){
12579 // summary:
12580 // Resolve "auto" text direction for children nodes
12581 // tags:
12582 // private
a089699c 12583
1354d172
AD
12584 this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
12585 array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
12586 },
12587
12588 _setTextDirAttr: function(/*String*/ textDir){
12589 // summary:
12590 // Setter for textDir.
12591 // description:
12592 // Users shouldn't call this function; they should be calling
12593 // set('textDir', value)
12594 // tags:
12595 // private
12596
12597 this._set("textDir", typeof textDir != 'undefined'? textDir : "");
12598 if (textDir == "auto"){
12599 this._setAutoTextDir(this.containerNode);
12600 }else{
12601 this.containerNode.dir = this.textDir;
12602 }
12603 }
12604 });
a089699c 12605
1354d172
AD
12606 dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
12607 // summary:
12608 // Static method to display tooltip w/specified contents in specified position.
12609 // See description of dijit.Tooltip.defaultPosition for details on position parameter.
12610 // If position is not specified then dijit.Tooltip.defaultPosition is used.
12611 // innerHTML: String
12612 // Contents of the tooltip
12613 // aroundNode: dijit.__Rectangle
12614 // Specifies that tooltip should be next to this node / area
12615 // position: String[]?
12616 // List of positions to try to position tooltip (ex: ["right", "above"])
12617 // rtl: Boolean?
12618 // Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
12619 // means "rtl"; specifies GUI direction, not text direction.
12620 // textDir: String?
12621 // Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
12622
12623 // after/before don't work, but they used to, so for back-compat convert them to after-centered, before-centered
12624 if(position){
12625 position = array.map(position, function(val){
12626 return {after: "after-centered", before: "before-centered"}[val] || val;
12627 });
12628 }
a089699c 12629
1354d172
AD
12630 if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
12631 return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
12632 };
a089699c 12633
1354d172 12634 dijit.hideTooltip = function(aroundNode){
a089699c 12635 // summary:
1354d172
AD
12636 // Static method to hide the tooltip displayed via showTooltip()
12637 return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
12638 };
a089699c 12639
1354d172
AD
12640 var Tooltip = declare("dijit.Tooltip", _Widget, {
12641 // summary:
12642 // Pops up a tooltip (a help message) when you hover over a node.
a089699c 12643
1354d172
AD
12644 // label: String
12645 // Text to display in the tooltip.
12646 // Specified as innerHTML when creating the widget from markup.
12647 label: "",
a089699c 12648
1354d172
AD
12649 // showDelay: Integer
12650 // Number of milliseconds to wait after hovering over/focusing on the object, before
12651 // the tooltip is displayed.
12652 showDelay: 400,
a089699c 12653
1354d172
AD
12654 // connectId: String|String[]
12655 // Id of domNode(s) to attach the tooltip to.
12656 // When user hovers over specified dom node, the tooltip will appear.
12657 connectId: [],
a089699c 12658
1354d172
AD
12659 // position: String[]
12660 // See description of `dijit.Tooltip.defaultPosition` for details on position parameter.
12661 position: [],
a089699c 12662
1354d172
AD
12663 _setConnectIdAttr: function(/*String|String[]*/ newId){
12664 // summary:
12665 // Connect to specified node(s)
a089699c 12666
1354d172
AD
12667 // Remove connections to old nodes (if there are any)
12668 array.forEach(this._connections || [], function(nested){
12669 array.forEach(nested, lang.hitch(this, "disconnect"));
12670 }, this);
a089699c 12671
1354d172
AD
12672 // Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
12673 this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
12674 function(id){ return dom.byId(id); });
12675
12676 // Make connections
12677 this._connections = array.map(this._connectIds, function(id){
12678 var node = dom.byId(id);
12679 return [
12680 this.connect(node, "onmouseenter", "_onHover"),
12681 this.connect(node, "onmouseleave", "_onUnHover"),
12682 this.connect(node, "onfocus", "_onHover"),
12683 this.connect(node, "onblur", "_onUnHover")
12684 ];
12685 }, this);
a089699c 12686
1354d172
AD
12687 this._set("connectId", newId);
12688 },
a089699c 12689
1354d172 12690 addTarget: function(/*DOMNODE || String*/ node){
a089699c 12691 // summary:
1354d172 12692 // Attach tooltip to specified node if it's not already connected
a089699c 12693
1354d172 12694 // TODO: remove in 2.0 and just use set("connectId", ...) interface
a089699c 12695
1354d172
AD
12696 var id = node.id || node;
12697 if(array.indexOf(this._connectIds, id) == -1){
12698 this.set("connectId", this._connectIds.concat(id));
12699 }
a089699c
AD
12700 },
12701
1354d172 12702 removeTarget: function(/*DomNode || String*/ node){
a089699c 12703 // summary:
1354d172 12704 // Detach tooltip from specified node
a089699c 12705
1354d172 12706 // TODO: remove in 2.0 and just use set("connectId", ...) interface
a089699c 12707
1354d172
AD
12708 var id = node.id || node, // map from DOMNode back to plain id string
12709 idx = array.indexOf(this._connectIds, id);
12710 if(idx >= 0){
12711 // remove id (modifies original this._connectIds but that's OK in this case)
12712 this._connectIds.splice(idx, 1);
12713 this.set("connectId", this._connectIds);
81bea17a 12714 }
a089699c
AD
12715 },
12716
81bea17a
AD
12717 buildRendering: function(){
12718 this.inherited(arguments);
1354d172 12719 domClass.add(this.domNode,"dijitTooltipData");
a089699c
AD
12720 },
12721
1354d172 12722 startup: function(){
a089699c 12723 this.inherited(arguments);
81bea17a 12724
1354d172
AD
12725 // If this tooltip was created in a template, or for some other reason the specified connectId[s]
12726 // didn't exist during the widget's initialization, then connect now.
12727 var ids = this.connectId;
12728 array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
a089699c
AD
12729 },
12730
1354d172
AD
12731 _onHover: function(/*Event*/ e){
12732 // summary:
12733 // Despite the name of this method, it actually handles both hover and focus
12734 // events on the target node, setting a timer to show the tooltip.
12735 // tags:
12736 // private
12737 if(!this._showTimer){
12738 var target = e.target;
12739 this._showTimer = setTimeout(lang.hitch(this, function(){this.open(target)}), this.showDelay);
a089699c 12740 }
a089699c
AD
12741 },
12742
1354d172 12743 _onUnHover: function(/*Event*/ /*===== e =====*/){
a089699c 12744 // summary:
1354d172
AD
12745 // Despite the name of this method, it actually handles both mouseleave and blur
12746 // events on the target node, hiding the tooltip.
12747 // tags:
12748 // private
81bea17a 12749
1354d172
AD
12750 // keep a tooltip open if the associated element still has focus (even though the
12751 // mouse moved away)
12752 if(this._focus){ return; }
81bea17a 12753
1354d172
AD
12754 if(this._showTimer){
12755 clearTimeout(this._showTimer);
12756 delete this._showTimer;
a089699c 12757 }
1354d172 12758 this.close();
a089699c
AD
12759 },
12760
1354d172
AD
12761 open: function(/*DomNode*/ target){
12762 // summary:
12763 // Display the tooltip; usually not called directly.
12764 // tags:
12765 // private
81bea17a 12766
1354d172
AD
12767 if(this._showTimer){
12768 clearTimeout(this._showTimer);
12769 delete this._showTimer;
12770 }
12771 Tooltip.show(this.label || this.domNode.innerHTML, target, this.position, !this.isLeftToRight(), this.textDir);
81bea17a 12772
1354d172
AD
12773 this._connectNode = target;
12774 this.onShow(target, this.position);
a089699c
AD
12775 },
12776
1354d172 12777 close: function(){
a089699c 12778 // summary:
1354d172 12779 // Hide the tooltip or cancel timer for show of tooltip
a089699c 12780 // tags:
1354d172 12781 // private
a089699c 12782
1354d172
AD
12783 if(this._connectNode){
12784 // if tooltip is currently shown
12785 Tooltip.hide(this._connectNode);
12786 delete this._connectNode;
12787 this.onHide();
12788 }
12789 if(this._showTimer){
12790 // if tooltip is scheduled to be shown (after a brief delay)
12791 clearTimeout(this._showTimer);
12792 delete this._showTimer;
12793 }
a089699c
AD
12794 },
12795
1354d172 12796 onShow: function(/*===== target, position =====*/){
a089699c 12797 // summary:
1354d172 12798 // Called when the tooltip is shown
a089699c 12799 // tags:
1354d172 12800 // callback
a089699c
AD
12801 },
12802
1354d172 12803 onHide: function(){
a089699c 12804 // summary:
1354d172 12805 // Called when the tooltip is hidden
a089699c 12806 // tags:
1354d172 12807 // callback
a089699c
AD
12808 },
12809
1354d172
AD
12810 uninitialize: function(){
12811 this.close();
12812 this.inherited(arguments);
12813 }
12814 });
a089699c 12815
1354d172
AD
12816 Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
12817 Tooltip.show = dijit.showTooltip; // export function through module return value
12818 Tooltip.hide = dijit.hideTooltip; // export function through module return value
a089699c 12819
1354d172
AD
12820 // dijit.Tooltip.defaultPosition: String[]
12821 // This variable controls the position of tooltips, if the position is not specified to
12822 // the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
12823 // possible for `dijit/place::around()`. The recommended values are:
12824 //
12825 // * before-centered: centers tooltip to the left of the anchor node/widget, or to the right
12826 // in the case of RTL scripts like Hebrew and Arabic
12827 // * after-centered: centers tooltip to the right of the anchor node/widget, or to the left
12828 // in the case of RTL scripts like Hebrew and Arabic
12829 // * above-centered: tooltip is centered above anchor node
12830 // * below-centered: tooltip is centered above anchor node
12831 //
12832 // The list is positions is tried, in order, until a position is found where the tooltip fits
12833 // within the viewport.
12834 //
12835 // Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
12836 // the screen so that there's no room above the target node. Nodes with drop downs, like
12837 // DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
12838 // that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
12839 // is only room below (or above) the target node, but not both.
12840 Tooltip.defaultPosition = ["after-centered", "before-centered"];
a089699c 12841
a089699c 12842
1354d172
AD
12843 return Tooltip;
12844});
a089699c 12845
1354d172
AD
12846},
12847'dojo/string':function(){
12848define("dojo/string", ["./_base/kernel", "./_base/lang"], function(dojo, lang) {
12849 // module:
12850 // dojo/string
12851 // summary:
12852 // TODOC
81bea17a 12853
1354d172 12854lang.getObject("string", true, dojo);
81bea17a 12855
1354d172
AD
12856/*=====
12857dojo.string = {
12858 // summary: String utilities for Dojo
12859};
12860=====*/
a089699c 12861
1354d172
AD
12862dojo.string.rep = function(/*String*/str, /*Integer*/num){
12863 // summary:
12864 // Efficiently replicate a string `n` times.
12865 // str:
12866 // the string to replicate
12867 // num:
12868 // number of times to replicate the string
a089699c 12869
1354d172 12870 if(num <= 0 || !str){ return ""; }
81bea17a 12871
1354d172
AD
12872 var buf = [];
12873 for(;;){
12874 if(num & 1){
12875 buf.push(str);
12876 }
12877 if(!(num >>= 1)){ break; }
12878 str += str;
12879 }
12880 return buf.join(""); // String
12881};
a089699c 12882
1354d172
AD
12883dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){
12884 // summary:
12885 // Pad a string to guarantee that it is at least `size` length by
12886 // filling with the character `ch` at either the start or end of the
12887 // string. Pads at the start, by default.
12888 // text:
12889 // the string to pad
12890 // size:
12891 // length to provide padding
12892 // ch:
12893 // character to pad, defaults to '0'
12894 // end:
12895 // adds padding at the end if true, otherwise pads at start
12896 // example:
12897 // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++".
12898 // | dojo.string.pad("Dojo", 10, "+", true);
a089699c 12899
1354d172
AD
12900 if(!ch){
12901 ch = '0';
12902 }
12903 var out = String(text),
12904 pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length));
12905 return end ? out + pad : pad + out; // String
12906};
12907
12908dojo.string.substitute = function( /*String*/ template,
12909 /*Object|Array*/map,
12910 /*Function?*/ transform,
12911 /*Object?*/ thisObject){
12912 // summary:
12913 // Performs parameterized substitutions on a string. Throws an
12914 // exception if any parameter is unmatched.
12915 // template:
12916 // a string with expressions in the form `${key}` to be replaced or
12917 // `${key:format}` which specifies a format function. keys are case-sensitive.
12918 // map:
12919 // hash to search for substitutions
12920 // transform:
12921 // a function to process all parameters before substitution takes
12922 // place, e.g. mylib.encodeXML
12923 // thisObject:
12924 // where to look for optional format function; default to the global
12925 // namespace
12926 // example:
12927 // Substitutes two expressions in a string from an Array or Object
12928 // | // returns "File 'foo.html' is not found in directory '/temp'."
12929 // | // by providing substitution data in an Array
12930 // | dojo.string.substitute(
12931 // | "File '${0}' is not found in directory '${1}'.",
12932 // | ["foo.html","/temp"]
12933 // | );
12934 // |
12935 // | // also returns "File 'foo.html' is not found in directory '/temp'."
12936 // | // but provides substitution data in an Object structure. Dotted
12937 // | // notation may be used to traverse the structure.
12938 // | dojo.string.substitute(
12939 // | "File '${name}' is not found in directory '${info.dir}'.",
12940 // | { name: "foo.html", info: { dir: "/temp" } }
12941 // | );
12942 // example:
12943 // Use a transform function to modify the values:
12944 // | // returns "file 'foo.html' is not found in directory '/temp'."
12945 // | dojo.string.substitute(
12946 // | "${0} is not found in ${1}.",
12947 // | ["foo.html","/temp"],
12948 // | function(str){
12949 // | // try to figure out the type
12950 // | var prefix = (str.charAt(0) == "/") ? "directory": "file";
12951 // | return prefix + " '" + str + "'";
12952 // | }
12953 // | );
12954 // example:
12955 // Use a formatter
12956 // | // returns "thinger -- howdy"
12957 // | dojo.string.substitute(
12958 // | "${0:postfix}", ["thinger"], null, {
12959 // | postfix: function(value, key){
12960 // | return value + " -- howdy";
12961 // | }
12962 // | }
12963 // | );
12964
12965 thisObject = thisObject || dojo.global;
12966 transform = transform ?
12967 lang.hitch(thisObject, transform) : function(v){ return v; };
12968
12969 return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
12970 function(match, key, format){
12971 var value = lang.getObject(key, false, map);
12972 if(format){
12973 value = lang.getObject(format, false, thisObject).call(thisObject, value, key);
12974 }
12975 return transform(value, key).toString();
12976 }); // String
12977};
12978
12979/*=====
12980dojo.string.trim = function(str){
12981 // summary:
12982 // Trims whitespace from both sides of the string
12983 // str: String
12984 // String to be trimmed
12985 // returns: String
12986 // Returns the trimmed string
12987 // description:
12988 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
12989 // The short yet performant version of this function is dojo.trim(),
12990 // which is part of Dojo base. Uses String.prototype.trim instead, if available.
12991 return ""; // String
12992}
12993=====*/
12994
12995dojo.string.trim = String.prototype.trim ?
12996 lang.trim : // aliasing to the native function
12997 function(str){
12998 str = str.replace(/^\s+/, '');
12999 for(var i = str.length - 1; i >= 0; i--){
13000 if(/\S/.test(str.charAt(i))){
13001 str = str.substring(0, i + 1);
13002 break;
a089699c
AD
13003 }
13004 }
1354d172
AD
13005 return str;
13006 };
a089699c 13007
1354d172
AD
13008return dojo.string;
13009});
a089699c 13010
1354d172
AD
13011},
13012'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>",
13013'dijit/dijit':function(){
13014define("dijit/dijit", [
13015 ".",
13016 "./_base",
13017 "dojo/parser",
13018 "./_Widget",
13019 "./_TemplatedMixin",
13020 "./_Container",
13021 "./layout/_LayoutWidget",
13022 "./form/_FormWidget",
13023 "./form/_FormValueWidget"
13024], function(dijit){
13025
13026 // module:
13027 // dijit/dijit
13028 // summary:
13029 // A roll-up for common dijit methods
13030 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
13031 // And some other stuff that we tend to pull in all the time anyway
a089699c 13032
1354d172
AD
13033 return dijit;
13034});
a089699c 13035
1354d172
AD
13036},
13037'dijit/form/DropDownButton':function(){
13038require({cache:{
13039'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
13040define("dijit/form/DropDownButton", [
13041 "dojo/_base/declare", // declare
13042 "dojo/_base/lang", // hitch
13043 "dojo/query", // query
13044 "../registry", // registry.byNode
13045 "../popup", // dijit.popup2.hide
13046 "./Button",
13047 "../_Container",
13048 "../_HasDropDown",
13049 "dojo/text!./templates/DropDownButton.html"
13050], function(declare, lang, query, registry, popup, Button, _Container, _HasDropDown, template){
a089699c 13051
1354d172
AD
13052/*=====
13053 Button = dijit.form.Button;
13054 _Container = dijit._Container;
13055 _HasDropDown = dijit._HasDropDown;
13056=====*/
a089699c 13057
1354d172
AD
13058// module:
13059// dijit/form/DropDownButton
13060// summary:
13061// A button with a drop down
a089699c
AD
13062
13063
1354d172 13064return declare("dijit.form.DropDownButton", [Button, _Container, _HasDropDown], {
a089699c 13065 // summary:
1354d172
AD
13066 // A button with a drop down
13067 //
a089699c 13068 // example:
1354d172
AD
13069 // | <button data-dojo-type="dijit.form.DropDownButton">
13070 // | Hello world
13071 // | <div data-dojo-type="dijit.Menu">...</div>
13072 // | </button>
a089699c
AD
13073 //
13074 // example:
1354d172
AD
13075 // | var button1 = new dijit.form.DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
13076 // | win.body().appendChild(button1);
a089699c 13077 //
a089699c 13078
1354d172 13079 baseClass : "dijitDropDownButton",
a089699c 13080
1354d172 13081 templateString: template,
a089699c 13082
1354d172
AD
13083 _fillContent: function(){
13084 // Overrides Button._fillContent().
13085 //
13086 // My inner HTML contains both the button contents and a drop down widget, like
13087 // <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
13088 // The first node is assumed to be the button content. The widget is the popup.
a089699c 13089
1354d172
AD
13090 if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
13091 //FIXME: figure out how to filter out the widget and use all remaining nodes as button
13092 // content, not just nodes[0]
13093 var nodes = query("*", this.srcNodeRef);
13094 this.inherited(arguments, [nodes[0]]);
a089699c 13095
1354d172
AD
13096 // save pointer to srcNode so we can grab the drop down widget after it's instantiated
13097 this.dropDownContainer = this.srcNodeRef;
a089699c
AD
13098 }
13099 },
13100
13101 startup: function(){
13102 if(this._started){ return; }
13103
13104 // the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
13105 // make it invisible, and store a reference to pass to the popup code.
81bea17a 13106 if(!this.dropDown && this.dropDownContainer){
1354d172
AD
13107 var dropDownNode = query("[widgetId]", this.dropDownContainer)[0];
13108 this.dropDown = registry.byNode(dropDownNode);
a089699c
AD
13109 delete this.dropDownContainer;
13110 }
81bea17a 13111 if(this.dropDown){
1354d172 13112 popup.hide(this.dropDown);
81bea17a 13113 }
a089699c
AD
13114
13115 this.inherited(arguments);
13116 },
13117
13118 isLoaded: function(){
13119 // Returns whether or not we are loaded - if our dropdown has an href,
13120 // then we want to check that.
13121 var dropDown = this.dropDown;
81bea17a 13122 return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
a089699c
AD
13123 },
13124
1354d172
AD
13125 loadDropDown: function(/*Function*/ callback){
13126 // Default implementation assumes that drop down already exists,
13127 // but hasn't loaded it's data (ex: ContentPane w/href).
13128 // App must override if the drop down is lazy-created.
a089699c 13129 var dropDown = this.dropDown;
1354d172
AD
13130 var handler = dropDown.on("load", lang.hitch(this, function(){
13131 handler.remove();
13132 callback();
13133 }));
13134 dropDown.refresh(); // tell it to load
a089699c
AD
13135 },
13136
13137 isFocusable: function(){
13138 // Overridden so that focus is handled by the _HasDropDown mixin, not by
13139 // the _FormWidget mixin.
13140 return this.inherited(arguments) && !this._mouseDown;
13141 }
13142});
13143
a089699c
AD
13144});
13145
1354d172
AD
13146},
13147'dijit/form/_FormValueMixin':function(){
13148define("dijit/form/_FormValueMixin", [
13149 "dojo/_base/declare", // declare
13150 "dojo/dom-attr", // domAttr.set
13151 "dojo/keys", // keys.ESCAPE
13152 "dojo/_base/sniff", // has("ie"), has("quirks")
13153 "./_FormWidgetMixin"
13154], function(declare, domAttr, keys, has, _FormWidgetMixin){
a089699c 13155
1354d172
AD
13156/*=====
13157 var _FormWidgetMixin = dijit.form._FormWidgetMixin;
13158=====*/
a089699c 13159
1354d172
AD
13160 // module:
13161 // dijit/form/_FormValueMixin
13162 // summary:
13163 // Mixin for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
a089699c 13164
1354d172 13165 return declare("dijit.form._FormValueMixin", _FormWidgetMixin, {
a089699c 13166 // summary:
1354d172 13167 // Mixin for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
a089699c 13168 // description:
1354d172
AD
13169 // Each _FormValueMixin represents a single input value, and has a (possibly hidden) <input> element,
13170 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
13171 // works as expected.
a089699c
AD
13172
13173 // readOnly: Boolean
13174 // Should this widget respond to user input?
13175 // In markup, this is specified as "readOnly".
13176 // Similar to disabled except readOnly form values are submitted.
13177 readOnly: false,
a089699c
AD
13178
13179 _setReadOnlyAttr: function(/*Boolean*/ value){
1354d172
AD
13180 domAttr.set(this.focusNode, 'readOnly', value);
13181 this.focusNode.setAttribute("aria-readonly", value);
81bea17a 13182 this._set("readOnly", value);
a089699c
AD
13183 },
13184
1354d172
AD
13185 postCreate: function(){
13186 this.inherited(arguments);
13187
13188 if(has("ie")){ // IE won't stop the event with keypress
13189 this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown);
a089699c 13190 }
1354d172
AD
13191 // Update our reset value if it hasn't yet been set (because this.set()
13192 // is only called when there *is* a value)
13193 if(this._resetValue === undefined){
13194 this._lastValueReported = this._resetValue = this.value;
a089699c
AD
13195 }
13196 },
1354d172
AD
13197
13198 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
a089699c 13199 // summary:
1354d172 13200 // Hook so set('value', value) works.
a089699c 13201 // description:
1354d172
AD
13202 // Sets the value of the widget.
13203 // If the value has changed, then fire onChange event, unless priorityChange
13204 // is specified as null (or false?)
13205 this._handleOnChange(newValue, priorityChange);
a089699c
AD
13206 },
13207
1354d172
AD
13208 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13209 // summary:
13210 // Called when the value of the widget has changed. Saves the new value in this.value,
13211 // and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
13212 this._set("value", newValue);
a089699c
AD
13213 this.inherited(arguments);
13214 },
13215
1354d172
AD
13216 undo: function(){
13217 // summary:
13218 // Restore the value to the last value passed to onChange
13219 this._setValueAttr(this._lastValueReported, false);
a089699c
AD
13220 },
13221
13222 reset: function(){
1354d172
AD
13223 // summary:
13224 // Reset the widget's value to what it was at initialization time
a089699c 13225 this._hasBeenBlurred = false;
1354d172
AD
13226 this._setValueAttr(this._resetValue, true);
13227 },
13228
13229 _onKeyDown: function(e){
13230 if(e.keyCode == keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
13231 var te;
13232 if(has("ie") < 9 || (has("ie") && has("quirks"))){
13233 e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
13234 te = document.createEventObject();
13235 te.keyCode = keys.ESCAPE;
13236 te.shiftKey = e.shiftKey;
13237 e.srcElement.fireEvent('onkeypress', te);
13238 }
13239 }
13240 }
13241 });
13242});
a089699c 13243
1354d172
AD
13244},
13245'dijit/form/_FormWidgetMixin':function(){
13246define("dijit/form/_FormWidgetMixin", [
13247 "dojo/_base/array", // array.forEach
13248 "dojo/_base/declare", // declare
13249 "dojo/dom-attr", // domAttr.set
13250 "dojo/dom-style", // domStyle.get
13251 "dojo/_base/lang", // lang.hitch lang.isArray
13252 "dojo/mouse", // mouse.isLeft
13253 "dojo/_base/sniff", // has("webkit")
13254 "dojo/_base/window", // win.body
13255 "dojo/window", // winUtils.scrollIntoView
13256 "../a11y" // a11y.hasDefaultTabStop
13257], function(array, declare, domAttr, domStyle, lang, mouse, has, win, winUtils, a11y){
13258
13259// module:
13260// dijit/form/_FormWidgetMixin
13261// summary:
13262// Mixin for widgets corresponding to native HTML elements such as <checkbox> or <button>,
13263// which can be children of a <form> node or a `dijit.form.Form` widget.
a089699c 13264
1354d172
AD
13265return declare("dijit.form._FormWidgetMixin", null, {
13266 // summary:
13267 // Mixin for widgets corresponding to native HTML elements such as <checkbox> or <button>,
13268 // which can be children of a <form> node or a `dijit.form.Form` widget.
13269 //
13270 // description:
13271 // Represents a single HTML element.
13272 // All these widgets should have these attributes just like native HTML input elements.
13273 // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
13274 //
13275 // They also share some common methods.
a089699c 13276
1354d172
AD
13277 // name: [const] String
13278 // Name used when submitting form; same as "name" attribute or plain HTML elements
13279 name: "",
a089699c 13280
1354d172
AD
13281 // alt: String
13282 // Corresponds to the native HTML <input> element's attribute.
13283 alt: "",
a089699c 13284
1354d172
AD
13285 // value: String
13286 // Corresponds to the native HTML <input> element's attribute.
13287 value: "",
a089699c 13288
1354d172
AD
13289 // type: [const] String
13290 // Corresponds to the native HTML <input> element's attribute.
13291 type: "text",
a089699c 13292
1354d172
AD
13293 // tabIndex: Integer
13294 // Order fields are traversed when user hits the tab key
13295 tabIndex: "0",
13296 _setTabIndexAttr: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
a089699c 13297
1354d172
AD
13298 // disabled: Boolean
13299 // Should this widget respond to user input?
13300 // In markup, this is specified as "disabled='disabled'", or just "disabled".
13301 disabled: false,
a089699c 13302
1354d172
AD
13303 // intermediateChanges: Boolean
13304 // Fires onChange for each value change or only on demand
13305 intermediateChanges: false,
a089699c 13306
1354d172
AD
13307 // scrollOnFocus: Boolean
13308 // On focus, should this widget scroll into view?
13309 scrollOnFocus: true,
a089699c 13310
1354d172
AD
13311 // Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
13312 // works with screen reader
13313 _setIdAttr: "focusNode",
a089699c 13314
1354d172
AD
13315 _setDisabledAttr: function(/*Boolean*/ value){
13316 this._set("disabled", value);
13317 domAttr.set(this.focusNode, 'disabled', value);
13318 if(this.valueNode){
13319 domAttr.set(this.valueNode, 'disabled', value);
13320 }
13321 this.focusNode.setAttribute("aria-disabled", value ? "true" : "false");
a089699c 13322
1354d172
AD
13323 if(value){
13324 // reset these, because after the domNode is disabled, we can no longer receive
13325 // mouse related events, see #4200
13326 this._set("hovering", false);
13327 this._set("active", false);
a089699c 13328
1354d172
AD
13329 // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes)
13330 var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex :
13331 ("_setTabIndexAttr" in this) ? this._setTabIndexAttr : "focusNode";
13332 array.forEach(lang.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
13333 var node = this[attachPointName];
13334 // complex code because tabIndex=-1 on a <div> doesn't work on FF
13335 if(has("webkit") || a11y.hasDefaultTabStop(node)){ // see #11064 about webkit bug
13336 node.setAttribute('tabIndex', "-1");
13337 }else{
13338 node.removeAttribute('tabIndex');
13339 }
13340 }, this);
13341 }else{
13342 if(this.tabIndex != ""){
13343 this.set('tabIndex', this.tabIndex);
13344 }
13345 }
13346 },
81bea17a 13347
1354d172
AD
13348 _onFocus: function(/*String*/ by){
13349 // If user clicks on the widget, even if the mouse is released outside of it,
13350 // this widget's focusNode should get focus (to mimic native browser hehavior).
13351 // Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
13352 if(by == "mouse" && this.isFocusable()){
13353 // IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
13354 var focusConnector = this.connect(this.focusNode, "onfocus", function(){
13355 this.disconnect(mouseUpConnector);
13356 this.disconnect(focusConnector);
13357 });
13358 // Set a global event to handle mouseup, so it fires properly
13359 // even if the cursor leaves this.domNode before the mouse up event.
13360 var mouseUpConnector = this.connect(win.body(), "onmouseup", function(){
13361 this.disconnect(mouseUpConnector);
13362 this.disconnect(focusConnector);
13363 // if here, then the mousedown did not focus the focusNode as the default action
13364 if(this.focused){
13365 this.focus();
13366 }
13367 });
13368 }
13369 if(this.scrollOnFocus){
13370 this.defer(function(){ winUtils.scrollIntoView(this.domNode); }); // without defer, the input caret position can change on mouse click
13371 }
13372 this.inherited(arguments);
13373 },
a089699c 13374
1354d172
AD
13375 isFocusable: function(){
13376 // summary:
13377 // Tells if this widget is focusable or not. Used internally by dijit.
13378 // tags:
13379 // protected
13380 return !this.disabled && this.focusNode && (domStyle.get(this.domNode, "display") != "none");
13381 },
a089699c 13382
1354d172
AD
13383 focus: function(){
13384 // summary:
13385 // Put focus on this widget
13386 if(!this.disabled && this.focusNode.focus){
13387 try{ this.focusNode.focus(); }catch(e){}/*squelch errors from hidden nodes*/
13388 }
13389 },
81bea17a 13390
1354d172
AD
13391 compare: function(/*anything*/ val1, /*anything*/ val2){
13392 // summary:
13393 // Compare 2 values (as returned by get('value') for this widget).
13394 // tags:
13395 // protected
13396 if(typeof val1 == "number" && typeof val2 == "number"){
13397 return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
13398 }else if(val1 > val2){
13399 return 1;
13400 }else if(val1 < val2){
13401 return -1;
13402 }else{
13403 return 0;
13404 }
13405 },
a089699c 13406
1354d172
AD
13407 onChange: function(/*===== newValue =====*/){
13408 // summary:
13409 // Callback when this widget's value is changed.
13410 // tags:
13411 // callback
13412 },
a089699c 13413
1354d172
AD
13414 // _onChangeActive: [private] Boolean
13415 // Indicates that changes to the value should call onChange() callback.
13416 // This is false during widget initialization, to avoid calling onChange()
13417 // when the initial value is set.
13418 _onChangeActive: false,
13419
13420 _handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
13421 // summary:
13422 // Called when the value of the widget is set. Calls onChange() if appropriate
13423 // newValue:
13424 // the new value
13425 // priorityChange:
13426 // For a slider, for example, dragging the slider is priorityChange==false,
13427 // but on mouse up, it's priorityChange==true. If intermediateChanges==false,
13428 // onChange is only called form priorityChange=true events.
13429 // tags:
13430 // private
13431 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
13432 // this block executes not for a change, but during initialization,
13433 // and is used to store away the original value (or for ToggleButton, the original checked state)
13434 this._resetValue = this._lastValueReported = newValue;
a089699c 13435 }
1354d172
AD
13436 this._pendingOnChange = this._pendingOnChange
13437 || (typeof newValue != typeof this._lastValueReported)
13438 || (this.compare(newValue, this._lastValueReported) != 0);
13439 if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
13440 this._lastValueReported = newValue;
13441 this._pendingOnChange = false;
13442 if(this._onChangeActive){
13443 if(this._onChangeHandle){
13444 this._onChangeHandle.remove();
13445 }
13446 // defer allows hidden value processing to run and
13447 // also the onChange handler can safely adjust focus, etc
13448 this._onChangeHandle = this.defer(
13449 function(){
13450 this._onChangeHandle = null;
13451 this.onChange(newValue);
13452 }); // try to collapse multiple onChange's fired faster than can be processed
13453 }
13454 }
13455 },
a089699c 13456
1354d172
AD
13457 create: function(){
13458 // Overrides _Widget.create()
13459 this.inherited(arguments);
13460 this._onChangeActive = true;
13461 },
a089699c 13462
1354d172
AD
13463 destroy: function(){
13464 if(this._onChangeHandle){ // destroy called before last onChange has fired
13465 this._onChangeHandle.remove();
13466 this.onChange(this._lastValueReported);
13467 }
13468 this.inherited(arguments);
a089699c 13469 }
1354d172 13470});
a089699c 13471
1354d172 13472});
a089699c 13473
1354d172
AD
13474},
13475'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
13476'dijit/layout/_ContentPaneResizeMixin':function(){
13477define("dijit/layout/_ContentPaneResizeMixin", [
13478 "dojo/_base/array", // array.filter array.forEach
13479 "dojo/_base/declare", // declare
13480 "dojo/dom-attr", // domAttr.has
13481 "dojo/dom-class", // domClass.contains domClass.toggle
13482 "dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
13483 "dojo/_base/lang", // lang.mixin
13484 "dojo/query", // query
13485 "dojo/_base/sniff", // has("ie")
13486 "dojo/_base/window", // win.global
13487 "../registry", // registry.byId
13488 "./utils", // marginBox2contextBox
13489 "../_Contained"
13490], function(array, declare, domAttr, domClass, domGeometry, lang, query, has, win,
13491 registry, layoutUtils, _Contained){
a089699c 13492
1354d172
AD
13493/*=====
13494var _Contained = dijit._Contained;
13495=====*/
a089699c 13496
1354d172
AD
13497// module:
13498// dijit/layout/_ContentPaneResizeMixin
13499// summary:
13500// Resize() functionality of ContentPane. If there's a single layout widget
13501// child then it will call resize() with the same dimensions as the ContentPane.
13502// Otherwise just calls resize on each child.
a089699c 13503
a089699c 13504
1354d172
AD
13505return declare("dijit.layout._ContentPaneResizeMixin", null, {
13506 // summary:
13507 // Resize() functionality of ContentPane. If there's a single layout widget
13508 // child then it will call resize() with the same dimensions as the ContentPane.
13509 // Otherwise just calls resize on each child.
13510 //
13511 // Also implements basic startup() functionality, where starting the parent
13512 // will start the children
81bea17a 13513
1354d172
AD
13514 // doLayout: Boolean
13515 // - false - don't adjust size of children
13516 // - true - if there is a single visible child widget, set it's size to
13517 // however big the ContentPane is
13518 doLayout: true,
a089699c 13519
1354d172
AD
13520 // isLayoutContainer: [protected] Boolean
13521 // Indicates that this widget will call resize() on it's child widgets
13522 // when they become visible.
13523 isLayoutContainer: true,
a089699c 13524
1354d172
AD
13525 startup: function(){
13526 // summary:
13527 // See `dijit.layout._LayoutWidget.startup` for description.
13528 // Although ContentPane doesn't extend _LayoutWidget, it does implement
13529 // the same API.
a089699c 13530
1354d172 13531 if(this._started){ return; }
a089699c 13532
1354d172
AD
13533 var parent = this.getParent();
13534 this._childOfLayoutWidget = parent && parent.isLayoutContainer;
a089699c 13535
1354d172
AD
13536 // I need to call resize() on my child/children (when I become visible), unless
13537 // I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
13538 this._needLayout = !this._childOfLayoutWidget;
a089699c 13539
1354d172 13540 this.inherited(arguments);
81bea17a 13541
1354d172
AD
13542 if(this._isShown()){
13543 this._onShow();
13544 }
a089699c 13545
1354d172
AD
13546 if(!this._childOfLayoutWidget){
13547 // If my parent isn't a layout container, since my style *may be* width=height=100%
13548 // or something similar (either set directly or via a CSS class),
13549 // monitor when my size changes so that I can re-layout.
13550 // For browsers where I can't directly monitor when my size changes,
13551 // monitor when the viewport changes size, which *may* indicate a size change for me.
13552 this.connect(has("ie") ? this.domNode : win.global, 'onresize', function(){
13553 // Using function(){} closure to ensure no arguments to resize.
13554 this._needLayout = !this._childOfLayoutWidget;
13555 this.resize();
13556 });
a089699c 13557 }
1354d172 13558 },
a089699c 13559
1354d172
AD
13560 _checkIfSingleChild: function(){
13561 // summary:
13562 // Test if we have exactly one visible widget as a child,
13563 // and if so assume that we are a container for that widget,
13564 // and should propagate startup() and resize() calls to it.
13565 // Skips over things like data stores since they aren't visible.
a089699c 13566
1354d172
AD
13567 var childNodes = query("> *", this.containerNode).filter(function(node){
13568 return node.tagName !== "SCRIPT"; // or a regexp for hidden elements like script|area|map|etc..
13569 }),
13570 childWidgetNodes = childNodes.filter(function(node){
13571 return domAttr.has(node, "data-dojo-type") || domAttr.has(node, "dojoType") || domAttr.has(node, "widgetId");
13572 }),
13573 candidateWidgets = array.filter(childWidgetNodes.map(registry.byNode), function(widget){
13574 return widget && widget.domNode && widget.resize;
13575 });
a089699c 13576
1354d172
AD
13577 if(
13578 // all child nodes are widgets
13579 childNodes.length == childWidgetNodes.length &&
a089699c 13580
1354d172
AD
13581 // all but one are invisible (like dojo.data)
13582 candidateWidgets.length == 1
13583 ){
13584 this._singleChild = candidateWidgets[0];
13585 }else{
13586 delete this._singleChild;
a089699c 13587 }
1354d172
AD
13588
13589 // So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
13590 domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
13591 },
13592
13593 resize: function(changeSize, resultSize){
13594 // summary:
13595 // See `dijit.layout._LayoutWidget.resize` for description.
13596 // Although ContentPane doesn't extend _LayoutWidget, it does implement
13597 // the same API.
13598
13599 // For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
13600 // never called, so resize() is our trigger to do the initial href download (see [20099]).
13601 // However, don't load href for closed TitlePanes.
13602 if(!this._wasShown && this.open !== false){
13603 this._onShow();
a089699c 13604 }
1354d172
AD
13605
13606 this._resizeCalled = true;
13607
13608 this._scheduleLayout(changeSize, resultSize);
13609 },
13610
13611 _scheduleLayout: function(changeSize, resultSize){
13612 // summary:
13613 // Resize myself, and call resize() on each of my child layout widgets, either now
13614 // (if I'm currently visible) or when I become visible
13615 if(this._isShown()){
13616 this._layout(changeSize, resultSize);
13617 }else{
13618 this._needLayout = true;
13619 this._changeSize = changeSize;
13620 this._resultSize = resultSize;
a089699c 13621 }
1354d172
AD
13622 },
13623
13624 _layout: function(changeSize, resultSize){
13625 // summary:
13626 // Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
13627 // Also, since I am a Container widget, each of my children expects me to
13628 // call resize() or layout() on them.
13629 //
13630 // Should be called on initialization and also whenever we get new content
13631 // (from an href, or from set('content', ...))... but deferred until
13632 // the ContentPane is visible
13633
13634 // Set margin box size, unless it wasn't specified, in which case use current size.
13635 if(changeSize){
13636 domGeometry.setMarginBox(this.domNode, changeSize);
a089699c 13637 }
1354d172
AD
13638
13639 // Compute content box size of containerNode in case we [later] need to size our single child.
13640 var cn = this.containerNode;
13641 if(cn === this.domNode){
13642 // If changeSize or resultSize was passed to this method and this.containerNode ==
13643 // this.domNode then we can compute the content-box size without querying the node,
13644 // which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
13645 var mb = resultSize || {};
13646 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
13647 if(!("h" in mb) || !("w" in mb)){
13648 mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
a089699c 13649 }
1354d172
AD
13650 this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
13651 }else{
13652 this._contentBox = domGeometry.getContentBox(cn);
a089699c 13653 }
a089699c 13654
1354d172 13655 this._layoutChildren();
a089699c 13656
1354d172
AD
13657 delete this._needLayout;
13658 },
a089699c 13659
1354d172
AD
13660 _layoutChildren: function(){
13661 // Call _checkIfSingleChild() again in case app has manually mucked w/the content
13662 // of the ContentPane (rather than changing it through the set("content", ...) API.
13663 if(this.doLayout){
13664 this._checkIfSingleChild();
a089699c 13665 }
a089699c 13666
1354d172
AD
13667 if(this._singleChild && this._singleChild.resize){
13668 var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
a089699c 13669
1354d172
AD
13670 // note: if widget has padding this._contentBox will have l and t set,
13671 // but don't pass them to resize() or it will doubly-offset the child
13672 this._singleChild.resize({w: cb.w, h: cb.h});
13673 }else{
13674 // All my child widgets are independently sized (rather than matching my size),
13675 // but I still need to call resize() on each child to make it layout.
13676 array.forEach(this.getChildren(), function(widget){
13677 if(widget.resize){
13678 widget.resize();
13679 }
13680 });
13681 }
13682 },
a089699c 13683
1354d172 13684 _isShown: function(){
a089699c 13685 // summary:
1354d172
AD
13686 // Returns true if the content is currently shown.
13687 // description:
13688 // If I am a child of a layout widget then it actually returns true if I've ever been visible,
13689 // not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
13690 // tree every call, and at least solves the performance problem on page load by deferring loading
13691 // hidden ContentPanes until they are first shown
a089699c 13692
1354d172
AD
13693 if(this._childOfLayoutWidget){
13694 // If we are TitlePane, etc - we return that only *IF* we've been resized
13695 if(this._resizeCalled && "open" in this){
13696 return this.open;
13697 }
13698 return this._resizeCalled;
13699 }else if("open" in this){
13700 return this.open; // for TitlePane, etc.
13701 }else{
13702 var node = this.domNode, parent = this.domNode.parentNode;
13703 return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
13704 parent && parent.style && (parent.style.display != 'none');
13705 }
13706 },
a089699c 13707
1354d172
AD
13708 _onShow: function(){
13709 // summary:
13710 // Called when the ContentPane is made visible
13711 // description:
13712 // For a plain ContentPane, this is called on initialization, from startup().
13713 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
13714 // called whenever the pane is made visible.
13715 //
13716 // Does layout/resize of child widget(s)
a089699c 13717
1354d172
AD
13718 if(this._needLayout){
13719 // If a layout has been scheduled for when we become visible, do it now
13720 this._layout(this._changeSize, this._resultSize);
13721 }
a089699c 13722
1354d172 13723 this.inherited(arguments);
a089699c 13724
1354d172
AD
13725 // Need to keep track of whether ContentPane has been shown (which is different than
13726 // whether or not it's currently visible).
13727 this._wasShown = true;
13728 }
13729});
a089699c 13730
1354d172 13731});
a089699c 13732
1354d172
AD
13733},
13734'dijit/WidgetSet':function(){
13735define("dijit/WidgetSet", [
13736 "dojo/_base/array", // array.forEach array.map
13737 "dojo/_base/declare", // declare
13738 "dojo/_base/window", // win.global
13739 "./registry" // to add functions to dijit.registry
13740], function(array, declare, win, registry){
13741
13742 // module:
13743 // dijit/WidgetSet
13744 // summary:
13745 // Legacy registry code. New modules should just use registry.
13746 // Will be removed in 2.0.
a089699c 13747
1354d172
AD
13748 var WidgetSet = declare("dijit.WidgetSet", null, {
13749 // summary:
13750 // A set of widgets indexed by id. A default instance of this class is
13751 // available as `dijit.registry`
13752 //
13753 // example:
13754 // Create a small list of widgets:
13755 // | var ws = new dijit.WidgetSet();
13756 // | ws.add(dijit.byId("one"));
13757 // | ws.add(dijit.byId("two"));
13758 // | // destroy both:
13759 // | ws.forEach(function(w){ w.destroy(); });
13760 //
13761 // example:
13762 // Using dijit.registry:
13763 // | dijit.registry.forEach(function(w){ /* do something */ });
a089699c 13764
1354d172
AD
13765 constructor: function(){
13766 this._hash = {};
13767 this.length = 0;
a089699c
AD
13768 },
13769
1354d172
AD
13770 add: function(/*dijit._Widget*/ widget){
13771 // summary:
13772 // Add a widget to this list. If a duplicate ID is detected, a error is thrown.
13773 //
13774 // widget: dijit._Widget
13775 // Any dijit._Widget subclass.
13776 if(this._hash[widget.id]){
13777 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
a089699c 13778 }
1354d172
AD
13779 this._hash[widget.id] = widget;
13780 this.length++;
a089699c
AD
13781 },
13782
1354d172 13783 remove: function(/*String*/ id){
a089699c 13784 // summary:
1354d172
AD
13785 // Remove a widget from this WidgetSet. Does not destroy the widget; simply
13786 // removes the reference.
13787 if(this._hash[id]){
13788 delete this._hash[id];
13789 this.length--;
13790 }
a089699c
AD
13791 },
13792
1354d172 13793 forEach: function(/*Function*/ func, /* Object? */thisObj){
a089699c 13794 // summary:
1354d172 13795 // Call specified function for each widget in this set.
a089699c 13796 //
1354d172
AD
13797 // func:
13798 // A callback function to run for each item. Is passed the widget, the index
13799 // in the iteration, and the full hash, similar to `array.forEach`.
a089699c 13800 //
1354d172
AD
13801 // thisObj:
13802 // An optional scope parameter
a089699c 13803 //
1354d172
AD
13804 // example:
13805 // Using the default `dijit.registry` instance:
13806 // | dijit.registry.forEach(function(widget){
13807 // | console.log(widget.declaredClass);
13808 // | });
a089699c 13809 //
1354d172
AD
13810 // returns:
13811 // Returns self, in order to allow for further chaining.
a089699c 13812
1354d172
AD
13813 thisObj = thisObj || win.global;
13814 var i = 0, id;
13815 for(id in this._hash){
13816 func.call(thisObj, this._hash[id], i++, this._hash);
a089699c 13817 }
1354d172 13818 return this; // dijit.WidgetSet
a089699c
AD
13819 },
13820
1354d172 13821 filter: function(/*Function*/ filter, /* Object? */thisObj){
a089699c 13822 // summary:
1354d172
AD
13823 // Filter down this WidgetSet to a smaller new WidgetSet
13824 // Works the same as `array.filter` and `NodeList.filter`
13825 //
13826 // filter:
13827 // Callback function to test truthiness. Is passed the widget
13828 // reference and the pseudo-index in the object.
13829 //
13830 // thisObj: Object?
13831 // Option scope to use for the filter function.
13832 //
13833 // example:
13834 // Arbitrary: select the odd widgets in this list
13835 // | dijit.registry.filter(function(w, i){
13836 // | return i % 2 == 0;
13837 // | }).forEach(function(w){ /* odd ones */ });
13838
13839 thisObj = thisObj || win.global;
13840 var res = new WidgetSet(), i = 0, id;
13841 for(id in this._hash){
13842 var w = this._hash[id];
13843 if(filter.call(thisObj, w, i++, this._hash)){
13844 res.add(w);
13845 }
13846 }
13847 return res; // dijit.WidgetSet
a089699c
AD
13848 },
13849
1354d172 13850 byId: function(/*String*/ id){
a089699c 13851 // summary:
1354d172
AD
13852 // Find a widget in this list by it's id.
13853 // example:
13854 // Test if an id is in a particular WidgetSet
13855 // | var ws = new dijit.WidgetSet();
13856 // | ws.add(dijit.byId("bar"));
13857 // | var t = ws.byId("bar") // returns a widget
13858 // | var x = ws.byId("foo"); // returns undefined
a089699c 13859
1354d172 13860 return this._hash[id]; // dijit._Widget
a089699c
AD
13861 },
13862
1354d172 13863 byClass: function(/*String*/ cls){
a089699c 13864 // summary:
1354d172
AD
13865 // Reduce this widgetset to a new WidgetSet of a particular `declaredClass`
13866 //
13867 // cls: String
13868 // The Class to scan for. Full dot-notated string.
13869 //
13870 // example:
13871 // Find all `dijit.TitlePane`s in a page:
13872 // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
13873
13874 var res = new WidgetSet(), id, widget;
13875 for(id in this._hash){
13876 widget = this._hash[id];
13877 if(widget.declaredClass == cls){
13878 res.add(widget);
13879 }
13880 }
13881 return res; // dijit.WidgetSet
a089699c
AD
13882 },
13883
1354d172 13884 toArray: function(){
a089699c 13885 // summary:
1354d172
AD
13886 // Convert this WidgetSet into a true Array
13887 //
13888 // example:
13889 // Work with the widget .domNodes in a real Array
13890 // | array.map(dijit.registry.toArray(), function(w){ return w.domNode; });
81bea17a 13891
1354d172
AD
13892 var ar = [];
13893 for(var id in this._hash){
13894 ar.push(this._hash[id]);
13895 }
13896 return ar; // dijit._Widget[]
a089699c
AD
13897 },
13898
1354d172 13899 map: function(/* Function */func, /* Object? */thisObj){
a089699c 13900 // summary:
1354d172
AD
13901 // Create a new Array from this WidgetSet, following the same rules as `array.map`
13902 // example:
13903 // | var nodes = dijit.registry.map(function(w){ return w.domNode; });
13904 //
13905 // returns:
13906 // A new array of the returned values.
13907 return array.map(this.toArray(), func, thisObj); // Array
a089699c
AD
13908 },
13909
1354d172 13910 every: function(func, thisObj){
a089699c 13911 // summary:
1354d172
AD
13912 // A synthetic clone of `array.every` acting explicitly on this WidgetSet
13913 //
13914 // func: Function
13915 // A callback function run for every widget in this list. Exits loop
13916 // when the first false return is encountered.
13917 //
13918 // thisObj: Object?
13919 // Optional scope parameter to use for the callback
13920
13921 thisObj = thisObj || win.global;
13922 var x = 0, i;
13923 for(i in this._hash){
13924 if(!func.call(thisObj, this._hash[i], x++, this._hash)){
13925 return false; // Boolean
13926 }
13927 }
13928 return true; // Boolean
a089699c
AD
13929 },
13930
1354d172 13931 some: function(func, thisObj){
a089699c 13932 // summary:
1354d172
AD
13933 // A synthetic clone of `array.some` acting explicitly on this WidgetSet
13934 //
13935 // func: Function
13936 // A callback function run for every widget in this list. Exits loop
13937 // when the first true return is encountered.
13938 //
13939 // thisObj: Object?
13940 // Optional scope parameter to use for the callback
a089699c 13941
1354d172
AD
13942 thisObj = thisObj || win.global;
13943 var x = 0, i;
13944 for(i in this._hash){
13945 if(func.call(thisObj, this._hash[i], x++, this._hash)){
13946 return true; // Boolean
a089699c
AD
13947 }
13948 }
1354d172
AD
13949 return false; // Boolean
13950 }
81bea17a 13951
1354d172 13952 });
a089699c 13953
1354d172
AD
13954 // Add in 1.x compatibility methods to dijit.registry.
13955 // These functions won't show up in the API doc but since they are deprecated anyway,
13956 // that's probably for the best.
13957 array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
13958 registry[func] = WidgetSet.prototype[func];
13959 });
81bea17a 13960
81bea17a 13961
1354d172
AD
13962 return WidgetSet;
13963});
81bea17a 13964
1354d172
AD
13965},
13966'dojo/dnd/Moveable':function(){
13967define("dojo/dnd/Moveable", ["../main", "../Evented", "../touch", "./Mover"], function(dojo, Evented, touch) {
13968 // module:
13969 // dojo/dnd/Moveable
13970 // summary:
13971 // TODOC
a089699c 13972
a089699c 13973
1354d172
AD
13974/*=====
13975dojo.declare("dojo.dnd.__MoveableArgs", [], {
13976 // handle: Node||String
13977 // A node (or node's id), which is used as a mouse handle.
13978 // If omitted, the node itself is used as a handle.
13979 handle: null,
a089699c 13980
1354d172
AD
13981 // delay: Number
13982 // delay move by this number of pixels
13983 delay: 0,
a089699c 13984
1354d172
AD
13985 // skip: Boolean
13986 // skip move of form elements
13987 skip: false,
a089699c 13988
1354d172
AD
13989 // mover: Object
13990 // a constructor of custom Mover
13991 mover: dojo.dnd.Mover
13992});
13993=====*/
a089699c 13994
1354d172
AD
13995dojo.declare("dojo.dnd.Moveable", [Evented], {
13996 // object attributes (for markup)
13997 handle: "",
13998 delay: 0,
13999 skip: false,
81bea17a 14000
1354d172
AD
14001 constructor: function(node, params){
14002 // summary:
14003 // an object, which makes a node moveable
14004 // node: Node
14005 // a node (or node's id) to be moved
14006 // params: dojo.dnd.__MoveableArgs?
14007 // optional parameters
14008 this.node = dojo.byId(node);
14009 if(!params){ params = {}; }
14010 this.handle = params.handle ? dojo.byId(params.handle) : null;
14011 if(!this.handle){ this.handle = this.node; }
14012 this.delay = params.delay > 0 ? params.delay : 0;
14013 this.skip = params.skip;
14014 this.mover = params.mover ? params.mover : dojo.dnd.Mover;
14015 this.events = [
14016 dojo.connect(this.handle, touch.press, this, "onMouseDown"),
14017 // cancel text selection and text dragging
14018 dojo.connect(this.handle, "ondragstart", this, "onSelectStart"),
14019 dojo.connect(this.handle, "onselectstart", this, "onSelectStart")
14020 ];
14021 },
a089699c 14022
1354d172
AD
14023 // markup methods
14024 markupFactory: function(params, node, ctor){
14025 return new ctor(node, params);
14026 },
a089699c 14027
1354d172
AD
14028 // methods
14029 destroy: function(){
14030 // summary:
14031 // stops watching for possible move, deletes all references, so the object can be garbage-collected
14032 dojo.forEach(this.events, dojo.disconnect);
14033 this.events = this.node = this.handle = null;
14034 },
a089699c 14035
1354d172
AD
14036 // mouse event processors
14037 onMouseDown: function(e){
14038 // summary:
14039 // event processor for onmousedown/ontouchstart, creates a Mover for the node
14040 // e: Event
14041 // mouse/touch event
14042 if(this.skip && dojo.dnd.isFormElement(e)){ return; }
14043 if(this.delay){
14044 this.events.push(
14045 dojo.connect(this.handle, touch.move, this, "onMouseMove"),
14046 dojo.connect(this.handle, touch.release, this, "onMouseUp")
14047 );
14048 this._lastX = e.pageX;
14049 this._lastY = e.pageY;
14050 }else{
14051 this.onDragDetected(e);
a089699c 14052 }
1354d172
AD
14053 dojo.stopEvent(e);
14054 },
14055 onMouseMove: function(e){
14056 // summary:
14057 // event processor for onmousemove/ontouchmove, used only for delayed drags
14058 // e: Event
14059 // mouse/touch event
14060 if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
14061 this.onMouseUp(e);
14062 this.onDragDetected(e);
14063 }
14064 dojo.stopEvent(e);
14065 },
14066 onMouseUp: function(e){
14067 // summary:
14068 // event processor for onmouseup, used only for delayed drags
14069 // e: Event
14070 // mouse event
14071 for(var i = 0; i < 2; ++i){
14072 dojo.disconnect(this.events.pop());
14073 }
14074 dojo.stopEvent(e);
14075 },
14076 onSelectStart: function(e){
14077 // summary:
14078 // event processor for onselectevent and ondragevent
14079 // e: Event
14080 // mouse event
14081 if(!this.skip || !dojo.dnd.isFormElement(e)){
14082 dojo.stopEvent(e);
14083 }
14084 },
a089699c 14085
1354d172
AD
14086 // local events
14087 onDragDetected: function(/* Event */ e){
14088 // summary:
14089 // called when the drag is detected;
14090 // responsible for creation of the mover
14091 new this.mover(this.node, e, this);
14092 },
14093 onMoveStart: function(/* dojo.dnd.Mover */ mover){
14094 // summary:
14095 // called before every move operation
14096 dojo.publish("/dnd/move/start", [mover]);
14097 dojo.addClass(dojo.body(), "dojoMove");
14098 dojo.addClass(this.node, "dojoMoveItem");
14099 },
14100 onMoveStop: function(/* dojo.dnd.Mover */ mover){
14101 // summary:
14102 // called after every move operation
14103 dojo.publish("/dnd/move/stop", [mover]);
14104 dojo.removeClass(dojo.body(), "dojoMove");
14105 dojo.removeClass(this.node, "dojoMoveItem");
14106 },
14107 onFirstMove: function(/* dojo.dnd.Mover */ mover, /* Event */ e){
14108 // summary:
14109 // called during the very first move notification;
14110 // can be used to initialize coordinates, can be overwritten.
a089699c 14111
1354d172
AD
14112 // default implementation does nothing
14113 },
14114 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop, /* Event */ e){
14115 // summary:
14116 // called during every move notification;
14117 // should actually move the node; can be overwritten.
14118 this.onMoving(mover, leftTop);
14119 var s = mover.node.style;
14120 s.left = leftTop.l + "px";
14121 s.top = leftTop.t + "px";
14122 this.onMoved(mover, leftTop);
14123 },
14124 onMoving: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
14125 // summary:
14126 // called before every incremental move; can be overwritten.
a089699c 14127
1354d172
AD
14128 // default implementation does nothing
14129 },
14130 onMoved: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
a089699c 14131 // summary:
1354d172 14132 // called after every incremental move; can be overwritten.
a089699c 14133
1354d172
AD
14134 // default implementation does nothing
14135 }
14136});
a089699c 14137
1354d172
AD
14138return dojo.dnd.Moveable;
14139});
a089699c 14140
1354d172
AD
14141},
14142'dojo/store/util/SimpleQueryEngine':function(){
14143define("dojo/store/util/SimpleQueryEngine", ["../../_base/array"], function(arrayUtil) {
14144 // module:
14145 // dojo/store/util/SimpleQueryEngine
14146 // summary:
14147 // The module defines a simple filtering query engine for object stores.
14148
14149return function(query, options){
14150 // summary:
14151 // Simple query engine that matches using filter functions, named filter
14152 // functions or objects by name-value on a query object hash
14153 //
14154 // description:
14155 // The SimpleQueryEngine provides a way of getting a QueryResults through
14156 // the use of a simple object hash as a filter. The hash will be used to
14157 // match properties on data objects with the corresponding value given. In
14158 // other words, only exact matches will be returned.
14159 //
14160 // This function can be used as a template for more complex query engines;
14161 // for example, an engine can be created that accepts an object hash that
14162 // contains filtering functions, or a string that gets evaluated, etc.
14163 //
14164 // When creating a new dojo.store, simply set the store's queryEngine
14165 // field as a reference to this function.
14166 //
14167 // query: Object
14168 // An object hash with fields that may match fields of items in the store.
14169 // Values in the hash will be compared by normal == operator, but regular expressions
14170 // or any object that provides a test() method are also supported and can be
14171 // used to match strings by more complex expressions
14172 // (and then the regex's or object's test() method will be used to match values).
14173 //
14174 // options: dojo.store.util.SimpleQueryEngine.__queryOptions?
14175 // An object that contains optional information such as sort, start, and count.
14176 //
14177 // returns: Function
14178 // A function that caches the passed query under the field "matches". See any
14179 // of the "query" methods on dojo.stores.
14180 //
14181 // example:
14182 // Define a store with a reference to this engine, and set up a query method.
14183 //
14184 // | var myStore = function(options){
14185 // | // ...more properties here
14186 // | this.queryEngine = dojo.store.util.SimpleQueryEngine;
14187 // | // define our query method
14188 // | this.query = function(query, options){
14189 // | return dojo.store.util.QueryResults(this.queryEngine(query, options)(this.data));
14190 // | };
14191 // | };
14192
14193 // create our matching query function
14194 switch(typeof query){
14195 default:
14196 throw new Error("Can not query with a " + typeof query);
14197 case "object": case "undefined":
14198 var queryObject = query;
14199 query = function(object){
14200 for(var key in queryObject){
14201 var required = queryObject[key];
14202 if(required && required.test){
14203 if(!required.test(object[key])){
14204 return false;
14205 }
14206 }else if(required != object[key]){
14207 return false;
14208 }
14209 }
14210 return true;
14211 };
14212 break;
14213 case "string":
14214 // named query
14215 if(!this[query]){
14216 throw new Error("No filter function " + query + " was found in store");
14217 }
14218 query = this[query];
14219 // fall through
14220 case "function":
14221 // fall through
14222 }
14223 function execute(array){
14224 // execute the whole query, first we filter
14225 var results = arrayUtil.filter(array, query);
14226 // next we sort
14227 if(options && options.sort){
14228 results.sort(function(a, b){
14229 for(var sort, i=0; sort = options.sort[i]; i++){
14230 var aValue = a[sort.attribute];
14231 var bValue = b[sort.attribute];
14232 if (aValue != bValue) {
14233 return !!sort.descending == aValue > bValue ? -1 : 1;
14234 }
14235 }
14236 return 0;
14237 });
14238 }
14239 // now we paginate
14240 if(options && (options.start || options.count)){
14241 var total = results.length;
14242 results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
14243 results.total = total;
14244 }
14245 return results;
14246 }
14247 execute.matches = query;
14248 return execute;
14249};
14250});
a089699c 14251
1354d172
AD
14252},
14253'dijit/typematic':function(){
14254define("dijit/typematic", [
14255 "dojo/_base/array", // array.forEach
14256 "dojo/_base/connect", // connect.connect
14257 "dojo/_base/event", // event.stop
14258 "dojo/_base/kernel", // kernel.deprecated
14259 "dojo/_base/lang", // lang.mixin, lang.hitch
14260 "dojo/on",
14261 "dojo/_base/sniff", // has("ie")
14262 "." // setting dijit.typematic global
14263], function(array, connect, event, kernel, lang, on, has, dijit){
14264
14265// module:
14266// dijit/typematic
14267// summary:
14268// These functions are used to repetitively call a user specified callback
14269// method when a specific key or mouse click over a specific DOM node is
14270// held down for a specific amount of time.
14271// Only 1 such event is allowed to occur on the browser page at 1 time.
a089699c 14272
1354d172
AD
14273var typematic = (dijit.typematic = {
14274 // summary:
14275 // These functions are used to repetitively call a user specified callback
14276 // method when a specific key or mouse click over a specific DOM node is
14277 // held down for a specific amount of time.
14278 // Only 1 such event is allowed to occur on the browser page at 1 time.
a089699c 14279
1354d172
AD
14280 _fireEventAndReload: function(){
14281 this._timer = null;
14282 this._callback(++this._count, this._node, this._evt);
a089699c 14283
1354d172
AD
14284 // Schedule next event, timer is at most minDelay (default 10ms) to avoid
14285 // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
14286 this._currentTimeout = Math.max(
14287 this._currentTimeout < 0 ? this._initialDelay :
14288 (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)),
14289 this._minDelay);
14290 this._timer = setTimeout(lang.hitch(this, "_fireEventAndReload"), this._currentTimeout);
14291 },
a089699c 14292
1354d172
AD
14293 trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
14294 // summary:
14295 // Start a timed, repeating callback sequence.
14296 // If already started, the function call is ignored.
14297 // This method is not normally called by the user but can be
14298 // when the normal listener code is insufficient.
14299 // evt:
14300 // key or mouse event object to pass to the user callback
14301 // _this:
14302 // pointer to the user's widget space.
14303 // node:
14304 // the DOM node object to pass the the callback function
14305 // callback:
14306 // function to call until the sequence is stopped called with 3 parameters:
14307 // count:
14308 // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
14309 // node:
14310 // the DOM node object passed in
14311 // evt:
14312 // key or mouse event object
14313 // obj:
14314 // user space object used to uniquely identify each typematic sequence
14315 // subsequentDelay (optional):
14316 // if > 1, the number of milliseconds until the 3->n events occur
14317 // or else the fractional time multiplier for the next event's delay, default=0.9
14318 // initialDelay (optional):
14319 // the number of milliseconds until the 2nd event occurs, default=500ms
14320 // minDelay (optional):
14321 // the maximum delay in milliseconds for event to fire, default=10ms
14322 if(obj != this._obj){
14323 this.stop();
14324 this._initialDelay = initialDelay || 500;
14325 this._subsequentDelay = subsequentDelay || 0.90;
14326 this._minDelay = minDelay || 10;
14327 this._obj = obj;
14328 this._evt = evt;
14329 this._node = node;
14330 this._currentTimeout = -1;
14331 this._count = -1;
14332 this._callback = lang.hitch(_this, callback);
14333 this._fireEventAndReload();
14334 this._evt = lang.mixin({faux: true}, evt);
14335 }
14336 },
a089699c 14337
1354d172
AD
14338 stop: function(){
14339 // summary:
14340 // Stop an ongoing timed, repeating callback sequence.
14341 if(this._timer){
14342 clearTimeout(this._timer);
14343 this._timer = null;
14344 }
14345 if(this._obj){
14346 this._callback(-1, this._node, this._evt);
14347 this._obj = null;
14348 }
14349 },
a089699c 14350
1354d172
AD
14351 addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
14352 // summary:
14353 // Start listening for a specific typematic key.
14354 // See also the trigger method for other parameters.
14355 // keyObject:
14356 // an object defining the key to listen for:
14357 // charOrCode:
14358 // the printable character (string) or keyCode (number) to listen for.
14359 // keyCode:
14360 // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0).
14361 // charCode:
14362 // (deprecated - use charOrCode) the charCode (number) to listen for.
14363 // ctrlKey:
14364 // desired ctrl key state to initiate the callback sequence:
14365 // - pressed (true)
14366 // - released (false)
14367 // - either (unspecified)
14368 // altKey:
14369 // same as ctrlKey but for the alt key
14370 // shiftKey:
14371 // same as ctrlKey but for the shift key
14372 // returns:
14373 // a connection handle
14374 if(keyObject.keyCode){
14375 keyObject.charOrCode = keyObject.keyCode;
14376 kernel.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
14377 }else if(keyObject.charCode){
14378 keyObject.charOrCode = String.fromCharCode(keyObject.charCode);
14379 kernel.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
14380 }
14381 var handles = [
14382 on(node, connect._keypress, lang.hitch(this, function(evt){
14383 if(evt.charOrCode == keyObject.charOrCode &&
14384 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
14385 (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) &&
14386 (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey
14387 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){
14388 event.stop(evt);
14389 typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
14390 }else if(typematic._obj == keyObject){
14391 typematic.stop();
81bea17a 14392 }
1354d172
AD
14393 })),
14394 on(node, "keyup", lang.hitch(this, function(){
14395 if(typematic._obj == keyObject){
14396 typematic.stop();
81bea17a 14397 }
1354d172
AD
14398 }))
14399 ];
14400 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
14401 },
a089699c 14402
1354d172
AD
14403 addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
14404 // summary:
14405 // Start listening for a typematic mouse click.
14406 // See the trigger method for other parameters.
14407 // returns:
14408 // a connection handle
14409 var handles = [
14410 on(node, "mousedown", lang.hitch(this, function(evt){
14411 event.stop(evt);
14412 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
14413 })),
14414 on(node, "mouseup", lang.hitch(this, function(evt){
14415 if(this._obj){
14416 event.stop(evt);
14417 }
14418 typematic.stop();
14419 })),
14420 on(node, "mouseout", lang.hitch(this, function(evt){
14421 event.stop(evt);
14422 typematic.stop();
14423 })),
14424 on(node, "mousemove", lang.hitch(this, function(evt){
14425 evt.preventDefault();
14426 })),
14427 on(node, "dblclick", lang.hitch(this, function(evt){
14428 event.stop(evt);
14429 if(has("ie")){
14430 typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
14431 setTimeout(lang.hitch(this, typematic.stop), 50);
14432 }
14433 }))
14434 ];
14435 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
14436 },
a089699c 14437
1354d172
AD
14438 addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){
14439 // summary:
14440 // Start listening for a specific typematic key and mouseclick.
14441 // This is a thin wrapper to addKeyListener and addMouseListener.
14442 // See the addMouseListener and addKeyListener methods for other parameters.
14443 // mouseNode:
14444 // the DOM node object to listen on for mouse events.
14445 // keyNode:
14446 // the DOM node object to listen on for key events.
14447 // returns:
14448 // a connection handle
14449 var handles = [
14450 this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay),
14451 this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)
14452 ];
14453 return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
14454 }
14455});
81bea17a 14456
1354d172 14457return typematic;
a089699c 14458
1354d172 14459});
a089699c 14460
1354d172
AD
14461},
14462'dijit/MenuItem':function(){
14463require({cache:{
14464'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-event=\"onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick\">\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"}});
14465define("dijit/MenuItem", [
14466 "dojo/_base/declare", // declare
14467 "dojo/dom", // dom.setSelectable
14468 "dojo/dom-attr", // domAttr.set
14469 "dojo/dom-class", // domClass.toggle
14470 "dojo/_base/event", // event.stop
14471 "dojo/_base/kernel", // kernel.deprecated
14472 "dojo/_base/sniff", // has("ie")
14473 "./_Widget",
14474 "./_TemplatedMixin",
14475 "./_Contained",
14476 "./_CssStateMixin",
14477 "dojo/text!./templates/MenuItem.html"
14478], function(declare, dom, domAttr, domClass, event, kernel, has,
14479 _Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
a089699c 14480
1354d172
AD
14481/*=====
14482 var _Widget = dijit._Widget;
14483 var _TemplatedMixin = dijit._TemplatedMixin;
14484 var _Contained = dijit._Contained;
14485 var _CssStateMixin = dijit._CssStateMixin;
14486=====*/
a089699c 14487
1354d172
AD
14488 // module:
14489 // dijit/MenuItem
a089699c 14490 // summary:
1354d172 14491 // A line item in a Menu Widget
a089699c 14492
a089699c 14493
1354d172
AD
14494 return declare("dijit.MenuItem",
14495 [_Widget, _TemplatedMixin, _Contained, _CssStateMixin],
14496 {
a089699c 14497 // summary:
1354d172 14498 // A line item in a Menu Widget
a089699c 14499
1354d172
AD
14500 // Make 3 columns
14501 // icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
14502 templateString: template,
a089699c 14503
1354d172 14504 baseClass: "dijitMenuItem",
a089699c 14505
1354d172
AD
14506 // label: String
14507 // Menu text
14508 label: '',
14509 _setLabelAttr: { node: "containerNode", type: "innerHTML" },
a089699c 14510
1354d172
AD
14511 // iconClass: String
14512 // Class to apply to DOMNode to make it display an icon.
14513 iconClass: "dijitNoIcon",
14514 _setIconClassAttr: { node: "iconNode", type: "class" },
a089699c 14515
1354d172
AD
14516 // accelKey: String
14517 // Text for the accelerator (shortcut) key combination.
14518 // Note that although Menu can display accelerator keys there
14519 // is no infrastructure to actually catch and execute these
14520 // accelerators.
14521 accelKey: "",
a089699c 14522
1354d172
AD
14523 // disabled: Boolean
14524 // If true, the menu item is disabled.
14525 // If false, the menu item is enabled.
14526 disabled: false,
81bea17a 14527
1354d172
AD
14528 _fillContent: function(/*DomNode*/ source){
14529 // If button label is specified as srcNodeRef.innerHTML rather than
14530 // this.params.label, handle it here.
14531 if(source && !("label" in this.params)){
14532 this.set('label', source.innerHTML);
14533 }
a089699c
AD
14534 },
14535
1354d172
AD
14536 buildRendering: function(){
14537 this.inherited(arguments);
14538 var label = this.id+"_text";
14539 domAttr.set(this.containerNode, "id", label);
14540 if(this.accelKeyNode){
14541 domAttr.set(this.accelKeyNode, "id", this.id + "_accel");
14542 label += " " + this.id + "_accel";
81bea17a 14543 }
1354d172
AD
14544 this.domNode.setAttribute("aria-labelledby", label);
14545 dom.setSelectable(this.domNode, false);
a089699c
AD
14546 },
14547
1354d172 14548 _onHover: function(){
a089699c 14549 // summary:
1354d172
AD
14550 // Handler when mouse is moved onto menu item
14551 // tags:
14552 // protected
14553 this.getParent().onItemHover(this);
a089699c
AD
14554 },
14555
1354d172
AD
14556 _onUnhover: function(){
14557 // summary:
14558 // Handler when mouse is moved off of menu item,
14559 // possibly to a child menu, or maybe to a sibling
14560 // menuitem or somewhere else entirely.
14561 // tags:
14562 // protected
a089699c 14563
1354d172
AD
14564 // if we are unhovering the currently selected item
14565 // then unselect it
14566 this.getParent().onItemUnhover(this);
a089699c 14567
1354d172
AD
14568 // When menu is hidden (collapsed) due to clicking a MenuItem and having it execute,
14569 // FF and IE don't generate an onmouseout event for the MenuItem.
14570 // So, help out _CssStateMixin in this case.
14571 this._set("hovering", false);
a089699c
AD
14572 },
14573
1354d172 14574 _onClick: function(evt){
a089699c 14575 // summary:
1354d172 14576 // Internal handler for click events on MenuItem.
a089699c
AD
14577 // tags:
14578 // private
1354d172
AD
14579 this.getParent().onItemClick(this, evt);
14580 event.stop(evt);
a089699c
AD
14581 },
14582
1354d172 14583 onClick: function(/*Event*/){
a089699c 14584 // summary:
1354d172 14585 // User defined function to handle clicks
a089699c 14586 // tags:
1354d172
AD
14587 // callback
14588 },
14589
14590 focus: function(){
14591 // summary:
14592 // Focus on this MenuItem
14593 try{
14594 if(has("ie") == 8){
14595 // needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
14596 this.containerNode.focus();
14597 }
14598 this.focusNode.focus();
14599 }catch(e){
14600 // this throws on IE (at least) in some scenarios
14601 }
a089699c
AD
14602 },
14603
1354d172 14604 _onFocus: function(){
a089699c 14605 // summary:
1354d172
AD
14606 // This is called by the focus manager when focus
14607 // goes to this MenuItem or a child menu.
a089699c 14608 // tags:
1354d172
AD
14609 // protected
14610 this._setSelected(true);
14611 this.getParent()._onItemFocus(this);
a089699c 14612
1354d172 14613 this.inherited(arguments);
a089699c
AD
14614 },
14615
1354d172 14616 _setSelected: function(selected){
a089699c 14617 // summary:
1354d172 14618 // Indicate that this node is the currently selected one
a089699c
AD
14619 // tags:
14620 // private
14621
1354d172
AD
14622 /***
14623 * TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
14624 * Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
14625 * That's not supposed to happen, but the problem is:
14626 * In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
14627 * points to the parent Menu, bypassing the parent MenuItem... thus the
14628 * MenuItem is not in the chain of active widgets and gets a premature call to
14629 * _onBlur()
14630 */
14631
14632 domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
a089699c
AD
14633 },
14634
1354d172 14635 setLabel: function(/*String*/ content){
a089699c 14636 // summary:
1354d172 14637 // Deprecated. Use set('label', ...) instead.
a089699c 14638 // tags:
1354d172
AD
14639 // deprecated
14640 kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
14641 this.set("label", content);
a089699c
AD
14642 },
14643
1354d172 14644 setDisabled: function(/*Boolean*/ disabled){
a089699c 14645 // summary:
1354d172 14646 // Deprecated. Use set('disabled', bool) instead.
a089699c 14647 // tags:
1354d172
AD
14648 // deprecated
14649 kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
14650 this.set('disabled', disabled);
14651 },
14652 _setDisabledAttr: function(/*Boolean*/ value){
14653 // summary:
14654 // Hook for attr('disabled', ...) to work.
14655 // Enable or disable this menu item.
a089699c 14656
1354d172
AD
14657 this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
14658 this._set("disabled", value);
14659 },
14660 _setAccelKeyAttr: function(/*String*/ value){
14661 // summary:
14662 // Hook for attr('accelKey', ...) to work.
14663 // Set accelKey on this menu item.
a089699c 14664
1354d172
AD
14665 this.accelKeyNode.style.display=value?"":"none";
14666 this.accelKeyNode.innerHTML=value;
14667 //have to use colSpan to make it work in IE
14668 domAttr.set(this.containerNode,'colSpan',value?"1":"2");
14669
14670 this._set("accelKey", value);
14671 }
14672 });
14673});
14674
14675},
14676'dijit/layout/TabController':function(){
14677require({cache:{
14678'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode\" data-dojo-attach-event='onclick:onClick'>\n <div role=\"presentation\" class='dijitTabInnerDiv' data-dojo-attach-point='innerDiv'>\n <div role=\"presentation\" class='dijitTabContent' data-dojo-attach-point='tabContent'>\n \t<div role=\"presentation\" data-dojo-attach-point='focusNode'>\n\t\t <img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode' />\n\t\t <span data-dojo-attach-point='containerNode' class='tabLabel'></span>\n\t\t <span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t \t\tdata-dojo-attach-event='onclick: onClickCloseButton' role=\"presentation\">\n\t\t <span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t ></span>\n\t\t\t</div>\n </div>\n </div>\n</div>\n"}});
14679define("dijit/layout/TabController", [
14680 "dojo/_base/declare", // declare
14681 "dojo/dom", // dom.setSelectable
14682 "dojo/dom-attr", // domAttr.attr
14683 "dojo/dom-class", // domClass.toggle
14684 "dojo/i18n", // i18n.getLocalization
14685 "dojo/_base/lang", // lang.hitch lang.trim
14686 "./StackController",
14687 "../Menu",
14688 "../MenuItem",
14689 "dojo/text!./templates/_TabButton.html",
14690 "dojo/i18n!../nls/common"
14691], function(declare, dom, domAttr, domClass, i18n, lang, StackController, Menu, MenuItem, template){
14692
14693/*=====
14694 var StackController = dijit.layout.StackController;
14695 var Menu = dijit.Menu;
14696 var MenuItem = dijit.MenuItem;
14697=====*/
14698
14699 // module:
14700 // dijit/layout/TabController
14701 // summary:
14702 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
14703 // Used internally by `dijit.layout.TabContainer`.
14704
14705 var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, {
14706 // summary:
14707 // A tab (the thing you click to select a pane).
14708 // description:
14709 // Contains the title of the pane, and optionally a close-button to destroy the pane.
14710 // This is an internal widget and should not be instantiated directly.
14711 // tags:
14712 // private
14713
14714 // baseClass: String
14715 // The CSS class applied to the domNode.
14716 baseClass: "dijitTab",
14717
14718 // Apply dijitTabCloseButtonHover when close button is hovered
14719 cssStateNodes: {
14720 closeNode: "dijitTabCloseButton"
a089699c
AD
14721 },
14722
1354d172 14723 templateString: template,
a089699c 14724
1354d172
AD
14725 // Override _FormWidget.scrollOnFocus.
14726 // Don't scroll the whole tab container into view when the button is focused.
14727 scrollOnFocus: false,
a089699c 14728
1354d172
AD
14729 buildRendering: function(){
14730 this.inherited(arguments);
14731
14732 dom.setSelectable(this.containerNode, false);
a089699c
AD
14733 },
14734
1354d172
AD
14735 startup: function(){
14736 this.inherited(arguments);
14737 var n = this.domNode;
a089699c 14738
1354d172
AD
14739 // Required to give IE6 a kick, as it initially hides the
14740 // tabs until they are focused on.
14741 setTimeout(function(){
14742 n.className = n.className;
14743 }, 1);
a089699c
AD
14744 },
14745
1354d172 14746 _setCloseButtonAttr: function(/*Boolean*/ disp){
a089699c 14747 // summary:
1354d172
AD
14748 // Hide/show close button
14749 this._set("closeButton", disp);
14750 domClass.toggle(this.innerDiv, "dijitClosable", disp);
14751 this.closeNode.style.display = disp ? "" : "none";
14752 if(disp){
14753 var _nlsResources = i18n.getLocalization("dijit", "common");
14754 if(this.closeNode){
14755 domAttr.set(this.closeNode,"title", _nlsResources.itemClose);
14756 }
14757 // add context menu onto title button
14758 this._closeMenu = new Menu({
14759 id: this.id+"_Menu",
14760 dir: this.dir,
14761 lang: this.lang,
14762 textDir: this.textDir,
14763 targetNodeIds: [this.domNode]
14764 });
a089699c 14765
1354d172
AD
14766 this._closeMenu.addChild(new MenuItem({
14767 label: _nlsResources.itemClose,
14768 dir: this.dir,
14769 lang: this.lang,
14770 textDir: this.textDir,
14771 onClick: lang.hitch(this, "onClickCloseButton")
14772 }));
14773 }else{
14774 if(this._closeMenu){
14775 this._closeMenu.destroyRecursive();
14776 delete this._closeMenu;
14777 }
14778 }
14779 },
14780 _setLabelAttr: function(/*String*/ content){
a089699c 14781 // summary:
1354d172
AD
14782 // Hook for set('label', ...) to work.
14783 // description:
14784 // takes an HTML string.
14785 // Inherited ToggleButton implementation will Set the label (text) of the button;
14786 // Need to set the alt attribute of icon on tab buttons if no label displayed
14787 this.inherited(arguments);
14788 if(!this.showLabel && !this.params.title){
14789 this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
14790 }
a089699c
AD
14791 },
14792
1354d172
AD
14793 destroy: function(){
14794 if(this._closeMenu){
14795 this._closeMenu.destroyRecursive();
14796 delete this._closeMenu;
14797 }
a089699c
AD
14798 this.inherited(arguments);
14799 }
1354d172 14800 });
a089699c 14801
1354d172
AD
14802 var TabController = declare("dijit.layout.TabController", StackController, {
14803 // summary:
14804 // Set of tabs (the things with titles and a close button, that you click to show a tab panel).
14805 // Used internally by `dijit.layout.TabContainer`.
14806 // description:
14807 // Lets the user select the currently shown pane in a TabContainer or StackContainer.
14808 // TabController also monitors the TabContainer, and whenever a pane is
14809 // added or deleted updates itself accordingly.
14810 // tags:
14811 // private
a089699c 14812
1354d172 14813 baseClass: "dijitTabController",
a089699c 14814
1354d172 14815 templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
a089699c 14816
1354d172
AD
14817 // tabPosition: String
14818 // Defines where tabs go relative to the content.
14819 // "top", "bottom", "left-h", "right-h"
14820 tabPosition: "top",
a089699c 14821
1354d172
AD
14822 // buttonWidget: Constructor
14823 // The tab widget to create to correspond to each page
14824 buttonWidget: TabButton,
a089699c 14825
1354d172
AD
14826 _rectifyRtlTabList: function(){
14827 // summary:
14828 // For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
14829
14830 if(0 >= this.tabPosition.indexOf('-h')){ return; }
14831 if(!this.pane2button){ return; }
14832
14833 var maxWidth = 0;
14834 for(var pane in this.pane2button){
14835 var ow = this.pane2button[pane].innerDiv.scrollWidth;
14836 maxWidth = Math.max(maxWidth, ow);
14837 }
14838 //unify the length of all the tabs
14839 for(pane in this.pane2button){
14840 this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
14841 }
14842 }
14843 });
14844
14845 TabController.TabButton = TabButton; // for monkey patching
a089699c 14846
1354d172
AD
14847 return TabController;
14848});
a089699c 14849
1354d172
AD
14850},
14851'dijit/layout/_LayoutWidget':function(){
14852define("dijit/layout/_LayoutWidget", [
14853 "dojo/_base/lang", // lang.mixin
14854 "../_Widget",
14855 "../_Container",
14856 "../_Contained",
14857 "dojo/_base/declare", // declare
14858 "dojo/dom-class", // domClass.add domClass.remove
14859 "dojo/dom-geometry", // domGeometry.marginBox
14860 "dojo/dom-style", // domStyle.getComputedStyle
14861 "dojo/_base/sniff", // has("ie")
14862 "dojo/_base/window" // win.global
14863], function(lang, _Widget, _Container, _Contained,
14864 declare, domClass, domGeometry, domStyle, has, win){
a089699c 14865
a089699c 14866/*=====
1354d172
AD
14867 var _Widget = dijit._Widget;
14868 var _Container = dijit._Container;
14869 var _Contained = dijit._Contained;
a089699c
AD
14870=====*/
14871
1354d172
AD
14872 // module:
14873 // dijit/layout/_LayoutWidget
14874 // summary:
14875 // _LayoutWidget Base class for a _Container widget which is responsible for laying out its children.
14876 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
14877
14878
14879 return declare("dijit.layout._LayoutWidget", [_Widget, _Container, _Contained], {
a089699c 14880 // summary:
1354d172
AD
14881 // Base class for a _Container widget which is responsible for laying out its children.
14882 // Widgets which mixin this code must define layout() to manage placement and sizing of the children.
a089699c 14883
1354d172
AD
14884 // baseClass: [protected extension] String
14885 // This class name is applied to the widget's domNode
14886 // and also may be used to generate names for sub nodes,
14887 // for example dijitTabContainer-content.
14888 baseClass: "dijitLayoutContainer",
a089699c 14889
1354d172
AD
14890 // isLayoutContainer: [protected] Boolean
14891 // Indicates that this widget is going to call resize() on its
14892 // children widgets, setting their size, when they become visible.
14893 isLayoutContainer: true,
a089699c 14894
1354d172
AD
14895 buildRendering: function(){
14896 this.inherited(arguments);
14897 domClass.add(this.domNode, "dijitContainer");
14898 },
a089699c 14899
1354d172
AD
14900 startup: function(){
14901 // summary:
14902 // Called after all the widgets have been instantiated and their
14903 // dom nodes have been inserted somewhere under win.doc.body.
14904 //
14905 // Widgets should override this method to do any initialization
14906 // dependent on other widgets existing, and then call
14907 // this superclass method to finish things off.
14908 //
14909 // startup() in subclasses shouldn't do anything
14910 // size related because the size of the widget hasn't been set yet.
a089699c 14911
1354d172 14912 if(this._started){ return; }
a089699c 14913
1354d172
AD
14914 // Need to call inherited first - so that child widgets get started
14915 // up correctly
14916 this.inherited(arguments);
81bea17a 14917
1354d172
AD
14918 // If I am a not being controlled by a parent layout widget...
14919 var parent = this.getParent && this.getParent();
14920 if(!(parent && parent.isLayoutContainer)){
14921 // Do recursive sizing and layout of all my descendants
14922 // (passing in no argument to resize means that it has to glean the size itself)
14923 this.resize();
a089699c 14924
1354d172
AD
14925 // Since my parent isn't a layout container, and my style *may be* width=height=100%
14926 // or something similar (either set directly or via a CSS class),
14927 // monitor when viewport size changes so that I can re-layout.
14928 this.connect(win.global, 'onresize', function(){
14929 // Using function(){} closure to ensure no arguments passed to resize().
14930 this.resize();
14931 });
14932 }
14933 },
a089699c 14934
1354d172 14935 resize: function(changeSize, resultSize){
a089699c 14936 // summary:
1354d172
AD
14937 // Call this to resize a widget, or after its size has changed.
14938 // description:
14939 // Change size mode:
14940 // When changeSize is specified, changes the marginBox of this widget
14941 // and forces it to relayout its contents accordingly.
14942 // changeSize may specify height, width, or both.
14943 //
14944 // If resultSize is specified it indicates the size the widget will
14945 // become after changeSize has been applied.
14946 //
14947 // Notification mode:
14948 // When changeSize is null, indicates that the caller has already changed
14949 // the size of the widget, or perhaps it changed because the browser
14950 // window was resized. Tells widget to relayout its contents accordingly.
14951 //
14952 // If resultSize is also specified it indicates the size the widget has
14953 // become.
14954 //
14955 // In either mode, this method also:
14956 // 1. Sets this._borderBox and this._contentBox to the new size of
14957 // the widget. Queries the current domNode size if necessary.
14958 // 2. Calls layout() to resize contents (and maybe adjust child widgets).
14959 //
14960 // changeSize: Object?
14961 // Sets the widget to this margin-box size and position.
14962 // May include any/all of the following properties:
14963 // | {w: int, h: int, l: int, t: int}
14964 //
14965 // resultSize: Object?
14966 // The margin-box size of this widget after applying changeSize (if
14967 // changeSize is specified). If caller knows this size and
14968 // passes it in, we don't need to query the browser to get the size.
14969 // | {w: int, h: int}
a089699c 14970
1354d172 14971 var node = this.domNode;
a089699c 14972
1354d172
AD
14973 // set margin box size, unless it wasn't specified, in which case use current size
14974 if(changeSize){
14975 domGeometry.setMarginBox(node, changeSize);
14976 }
a089699c 14977
1354d172
AD
14978 // If either height or width wasn't specified by the user, then query node for it.
14979 // But note that setting the margin box and then immediately querying dimensions may return
14980 // inaccurate results, so try not to depend on it.
14981 var mb = resultSize || {};
14982 lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
14983 if( !("h" in mb) || !("w" in mb) ){
14984 mb = lang.mixin(domGeometry.getMarginBox(node), mb); // just use domGeometry.marginBox() to fill in missing values
14985 }
14986
14987 // Compute and save the size of my border box and content box
14988 // (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
14989 var cs = domStyle.getComputedStyle(node);
14990 var me = domGeometry.getMarginExtents(node, cs);
14991 var be = domGeometry.getBorderExtents(node, cs);
14992 var bb = (this._borderBox = {
14993 w: mb.w - (me.w + be.w),
14994 h: mb.h - (me.h + be.h)
14995 });
14996 var pe = domGeometry.getPadExtents(node, cs);
14997 this._contentBox = {
14998 l: domStyle.toPixelValue(node, cs.paddingLeft),
14999 t: domStyle.toPixelValue(node, cs.paddingTop),
15000 w: bb.w - pe.w,
15001 h: bb.h - pe.h
15002 };
15003
15004 // Callback for widget to adjust size of its children
15005 this.layout();
a089699c
AD
15006 },
15007
1354d172 15008 layout: function(){
a089699c 15009 // summary:
1354d172
AD
15010 // Widgets override this method to size and position their contents/children.
15011 // When this is called this._contentBox is guaranteed to be set (see resize()).
15012 //
15013 // This is called after startup(), and also when the widget's size has been
15014 // changed.
a089699c 15015 // tags:
1354d172 15016 // protected extension
a089699c
AD
15017 },
15018
1354d172 15019 _setupChild: function(/*dijit._Widget*/child){
a089699c 15020 // summary:
1354d172
AD
15021 // Common setup for initial children and children which are added after startup
15022 // tags:
15023 // protected extension
15024
15025 var cls = this.baseClass + "-child "
15026 + (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
15027 domClass.add(child.domNode, cls);
a089699c
AD
15028 },
15029
1354d172
AD
15030 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
15031 // Overrides _Container.addChild() to call _setupChild()
15032 this.inherited(arguments);
15033 if(this._started){
15034 this._setupChild(child);
15035 }
a089699c
AD
15036 },
15037
1354d172
AD
15038 removeChild: function(/*dijit._Widget*/ child){
15039 // Overrides _Container.removeChild() to remove class added by _setupChild()
15040 var cls = this.baseClass + "-child"
15041 + (child.baseClass ?
15042 " " + this.baseClass + "-" + child.baseClass : "");
15043 domClass.remove(child.domNode, cls);
15044
15045 this.inherited(arguments);
15046 }
15047 });
15048});
15049
15050},
15051'dijit/popup':function(){
15052define("dijit/popup", [
15053 "dojo/_base/array", // array.forEach array.some
15054 "dojo/aspect",
15055 "dojo/_base/connect", // connect._keypress
15056 "dojo/_base/declare", // declare
15057 "dojo/dom", // dom.isDescendant
15058 "dojo/dom-attr", // domAttr.set
15059 "dojo/dom-construct", // domConstruct.create domConstruct.destroy
15060 "dojo/dom-geometry", // domGeometry.isBodyLtr
15061 "dojo/dom-style", // domStyle.set
15062 "dojo/_base/event", // event.stop
15063 "dojo/keys",
15064 "dojo/_base/lang", // lang.hitch
15065 "dojo/on",
15066 "dojo/_base/sniff", // has("ie") has("mozilla")
15067 "dojo/_base/window", // win.body
15068 "./place",
15069 "./BackgroundIframe",
15070 "." // dijit (defining dijit.popup to match API doc)
15071], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has, win,
15072 place, BackgroundIframe, dijit){
15073
15074 // module:
15075 // dijit/popup
15076 // summary:
15077 // Used to show drop downs (ex: the select list of a ComboBox)
15078 // or popups (ex: right-click context menus)
15079
15080
15081 /*=====
15082 dijit.popup.__OpenArgs = function(){
15083 // popup: Widget
15084 // widget to display
15085 // parent: Widget
15086 // the button etc. that is displaying this popup
15087 // around: DomNode
15088 // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.)
15089 // x: Integer
15090 // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15091 // y: Integer
15092 // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.)
15093 // orient: Object|String
15094 // When the around parameter is specified, orient should be a list of positions to try, ex:
15095 // | [ "below", "above" ]
15096 // For backwards compatibility it can also be an (ordered) hash of tuples of the form
15097 // (around-node-corner, popup-node-corner), ex:
15098 // | { "BL": "TL", "TL": "BL" }
15099 // where BL means "bottom left" and "TL" means "top left", etc.
15100 //
15101 // dijit.popup.open() tries to position the popup according to each specified position, in order,
15102 // until the popup appears fully within the viewport.
15103 //
15104 // The default value is ["below", "above"]
15105 //
15106 // When an (x,y) position is specified rather than an around node, orient is either
15107 // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse,
15108 // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't
15109 // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner,
15110 // and the top-right corner.
15111 // onCancel: Function
15112 // callback when user has canceled the popup by
15113 // 1. hitting ESC or
15114 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
15115 // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called
15116 // onClose: Function
15117 // callback whenever this popup is closed
15118 // onExecute: Function
15119 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
15120 // padding: dijit.__Position
15121 // adding a buffer around the opening position. This is only useful when around is not set.
15122 this.popup = popup;
15123 this.parent = parent;
15124 this.around = around;
15125 this.x = x;
15126 this.y = y;
15127 this.orient = orient;
15128 this.onCancel = onCancel;
15129 this.onClose = onClose;
15130 this.onExecute = onExecute;
15131 this.padding = padding;
15132 }
15133 =====*/
15134
15135 /*=====
15136 dijit.popup = {
15137 // summary:
15138 // Used to show drop downs (ex: the select list of a ComboBox)
15139 // or popups (ex: right-click context menus).
15140 //
15141 // Access via require(["dijit/popup"], function(popup){ ... }).
15142
15143 moveOffScreen: function(widget){
a089699c 15144 // summary:
1354d172
AD
15145 // Moves the popup widget off-screen.
15146 // Do not use this method to hide popups when not in use, because
15147 // that will create an accessibility issue: the offscreen popup is
15148 // still in the tabbing order.
15149 // widget: dijit._WidgetBase
15150 // The widget
a089699c
AD
15151 },
15152
1354d172 15153 hide: function(widget){
a089699c 15154 // summary:
1354d172
AD
15155 // Hide this popup widget (until it is ready to be shown).
15156 // Initialization for widgets that will be used as popups
15157 //
15158 // Also puts widget inside a wrapper DIV (if not already in one)
15159 //
15160 // If popup widget needs to layout it should
15161 // do so when it is made visible, and popup._onShow() is called.
15162 // widget: dijit._WidgetBase
15163 // The widget
a089699c
AD
15164 },
15165
1354d172 15166 open: function(args){
a089699c 15167 // summary:
1354d172
AD
15168 // Popup the widget at the specified position
15169 // example:
15170 // opening at the mouse position
15171 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
15172 // example:
15173 // opening the widget as a dropdown
15174 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
15175 //
15176 // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
15177 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
15178 // args: dijit.popup.__OpenArgs
15179 // Parameters
15180 return {}; // Object specifying which position was chosen
a089699c
AD
15181 },
15182
1354d172 15183 close: function(popup){
a089699c 15184 // summary:
1354d172
AD
15185 // Close specified popup and any popups that it parented.
15186 // If no popup is specified, closes all popups.
15187 // widget: dijit._WidgetBase?
15188 // The widget, optional
15189 }
15190 };
15191 =====*/
81bea17a 15192
1354d172
AD
15193 var PopupManager = declare(null, {
15194 // _stack: dijit._Widget[]
15195 // Stack of currently popped up widgets.
15196 // (someone opened _stack[0], and then it opened _stack[1], etc.)
15197 _stack: [],
81bea17a 15198
1354d172
AD
15199 // _beginZIndex: Number
15200 // Z-index of the first popup. (If first popup opens other
15201 // popups they get a higher z-index.)
15202 _beginZIndex: 1000,
a089699c 15203
1354d172
AD
15204 _idGen: 1,
15205
15206 _createWrapper: function(/*Widget*/ widget){
a089699c 15207 // summary:
1354d172
AD
15208 // Initialization for widgets that will be used as popups.
15209 // Puts widget inside a wrapper DIV (if not already in one),
15210 // and returns pointer to that wrapper DIV.
15211
15212 var wrapper = widget._popupWrapper,
15213 node = widget.domNode;
15214
15215 if(!wrapper){
15216 // Create wrapper <div> for when this widget [in the future] will be used as a popup.
15217 // This is done early because of IE bugs where creating/moving DOM nodes causes focus
15218 // to go wonky, see tests/robot/Toolbar.html to reproduce
15219 wrapper = domConstruct.create("div",{
15220 "class":"dijitPopup",
15221 style:{ display: "none"},
15222 role: "presentation"
15223 }, win.body());
15224 wrapper.appendChild(node);
15225
15226 var s = node.style;
15227 s.display = "";
15228 s.visibility = "";
15229 s.position = "";
15230 s.top = "0px";
15231
15232 widget._popupWrapper = wrapper;
15233 aspect.after(widget, "destroy", function(){
15234 domConstruct.destroy(wrapper);
15235 delete widget._popupWrapper;
15236 });
a089699c 15237 }
1354d172
AD
15238
15239 return wrapper;
a089699c
AD
15240 },
15241
1354d172
AD
15242 moveOffScreen: function(/*Widget*/ widget){
15243 // summary:
15244 // Moves the popup widget off-screen.
15245 // Do not use this method to hide popups when not in use, because
15246 // that will create an accessibility issue: the offscreen popup is
15247 // still in the tabbing order.
15248
15249 // Create wrapper if not already there
15250 var wrapper = this._createWrapper(widget);
15251
15252 domStyle.set(wrapper, {
15253 visibility: "hidden",
15254 top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
15255 display: ""
15256 });
a089699c
AD
15257 },
15258
1354d172
AD
15259 hide: function(/*Widget*/ widget){
15260 // summary:
15261 // Hide this popup widget (until it is ready to be shown).
15262 // Initialization for widgets that will be used as popups
15263 //
15264 // Also puts widget inside a wrapper DIV (if not already in one)
15265 //
15266 // If popup widget needs to layout it should
15267 // do so when it is made visible, and popup._onShow() is called.
a089699c 15268
1354d172
AD
15269 // Create wrapper if not already there
15270 var wrapper = this._createWrapper(widget);
15271
15272 domStyle.set(wrapper, "display", "none");
a089699c
AD
15273 },
15274
1354d172
AD
15275 getTopPopup: function(){
15276 // summary:
15277 // Compute the closest ancestor popup that's *not* a child of another popup.
15278 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
15279 var stack = this._stack;
15280 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
15281 /* do nothing, just trying to get right value for pi */
a089699c 15282 }
1354d172 15283 return stack[pi];
a089699c
AD
15284 },
15285
1354d172
AD
15286 open: function(/*dijit.popup.__OpenArgs*/ args){
15287 // summary:
15288 // Popup the widget at the specified position
15289 //
15290 // example:
15291 // opening at the mouse position
15292 // | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
15293 //
15294 // example:
15295 // opening the widget as a dropdown
15296 // | popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}});
15297 //
15298 // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback
15299 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
15300
15301 var stack = this._stack,
15302 widget = args.popup,
15303 orient = args.orient || ["below", "below-alt", "above", "above-alt"],
15304 ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(),
15305 around = args.around,
15306 id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
15307
15308 // If we are opening a new popup that isn't a child of a currently opened popup, then
15309 // close currently opened popup(s). This should happen automatically when the old popups
15310 // gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
15311 while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
15312 this.close(stack[stack.length-1].widget);
15313 }
15314
15315 // Get pointer to popup wrapper, and create wrapper if it doesn't exist
15316 var wrapper = this._createWrapper(widget);
15317
15318
15319 domAttr.set(wrapper, {
15320 id: id,
15321 style: {
15322 zIndex: this._beginZIndex + stack.length
15323 },
15324 "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup",
15325 dijitPopupParent: args.parent ? args.parent.id : ""
15326 });
15327
15328 if(has("ie") || has("mozilla")){
15329 if(!widget.bgIframe){
15330 // setting widget.bgIframe triggers cleanup in _Widget.destroy()
15331 widget.bgIframe = new BackgroundIframe(wrapper);
15332 }
15333 }
15334
15335 // position the wrapper node and make it visible
15336 var best = around ?
15337 place.around(wrapper, around, orient, ltr, widget.orient ? lang.hitch(widget, "orient") : null) :
15338 place.at(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
15339
15340 wrapper.style.display = "";
15341 wrapper.style.visibility = "visible";
15342 widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown
15343
15344 var handlers = [];
15345
15346 // provide default escape and tab key handling
15347 // (this will work for any widget, not just menu)
15348 handlers.push(on(wrapper, connect._keypress, lang.hitch(this, function(evt){
15349 if(evt.charOrCode == keys.ESCAPE && args.onCancel){
15350 event.stop(evt);
15351 args.onCancel();
15352 }else if(evt.charOrCode === keys.TAB){
15353 event.stop(evt);
15354 var topPopup = this.getTopPopup();
15355 if(topPopup && topPopup.onCancel){
15356 topPopup.onCancel();
a089699c
AD
15357 }
15358 }
1354d172 15359 })));
a089699c 15360
1354d172
AD
15361 // watch for cancel/execute events on the popup and notify the caller
15362 // (for a menu, "execute" means clicking an item)
15363 if(widget.onCancel && args.onCancel){
15364 handlers.push(widget.on("cancel", args.onCancel));
15365 }
a089699c 15366
1354d172
AD
15367 handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
15368 var topPopup = this.getTopPopup();
15369 if(topPopup && topPopup.onExecute){
15370 topPopup.onExecute();
15371 }
15372 })));
15373
15374 stack.push({
15375 widget: widget,
15376 parent: args.parent,
15377 onExecute: args.onExecute,
15378 onCancel: args.onCancel,
15379 onClose: args.onClose,
15380 handlers: handlers
15381 });
a089699c 15382
1354d172
AD
15383 if(widget.onOpen){
15384 // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here)
15385 widget.onOpen(best);
15386 }
a089699c 15387
1354d172 15388 return best;
81bea17a
AD
15389 },
15390
1354d172
AD
15391 close: function(/*Widget?*/ popup){
15392 // summary:
15393 // Close specified popup and any popups that it parented.
15394 // If no popup is specified, closes all popups.
15395
15396 var stack = this._stack;
15397
15398 // Basically work backwards from the top of the stack closing popups
15399 // until we hit the specified popup, but IIRC there was some issue where closing
15400 // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C]
15401 // closing C might close B indirectly and then the while() condition will run where stack==[A]...
15402 // so the while condition is constructed defensively.
15403 while((popup && array.some(stack, function(elem){return elem.widget == popup;})) ||
15404 (!popup && stack.length)){
15405 var top = stack.pop(),
15406 widget = top.widget,
15407 onClose = top.onClose;
15408
15409 if(widget.onClose){
15410 // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
15411 widget.onClose();
15412 }
a089699c 15413
1354d172
AD
15414 var h;
15415 while(h = top.handlers.pop()){ h.remove(); }
81bea17a 15416
1354d172
AD
15417 // Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
15418 if(widget && widget.domNode){
15419 this.hide(widget);
15420 }
15421
15422 if(onClose){
15423 onClose();
15424 }
15425 }
a089699c 15426 }
1354d172 15427 });
a089699c 15428
1354d172
AD
15429 return (dijit.popup = new PopupManager());
15430});
15431
15432},
15433'dijit/_base/manager':function(){
15434define("dijit/_base/manager", [
15435 "dojo/_base/array",
15436 "dojo/_base/config", // defaultDuration
15437 "../registry",
15438 ".." // for setting exports to dijit namespace
15439], function(array, config, registry, dijit){
15440
15441 // module:
15442 // dijit/_base/manager
15443 // summary:
15444 // Shim to methods on registry, plus a few other declarations.
15445 // New code should access dijit/registry directly when possible.
15446
15447 /*=====
15448 dijit.byId = function(id){
a089699c 15449 // summary:
1354d172
AD
15450 // Returns a widget by it's id, or if passed a widget, no-op (like dom.byId())
15451 // id: String|dijit._Widget
15452 return registry.byId(id); // dijit._Widget
15453 };
a089699c 15454
1354d172
AD
15455 dijit.getUniqueId = function(widgetType){
15456 // summary:
15457 // Generates a unique id for a given widgetType
15458 // widgetType: String
15459 return registry.getUniqueId(widgetType); // String
15460 };
a089699c 15461
1354d172
AD
15462 dijit.findWidgets = function(root){
15463 // summary:
15464 // Search subtree under root returning widgets found.
15465 // Doesn't search for nested widgets (ie, widgets inside other widgets).
15466 // root: DOMNode
15467 return registry.findWidgets(root);
15468 };
a089699c 15469
1354d172
AD
15470 dijit._destroyAll = function(){
15471 // summary:
15472 // Code to destroy all widgets and do other cleanup on page unload
a089699c 15473
1354d172
AD
15474 return registry._destroyAll();
15475 };
a089699c 15476
1354d172
AD
15477 dijit.byNode = function(node){
15478 // summary:
15479 // Returns the widget corresponding to the given DOMNode
15480 // node: DOMNode
15481 return registry.byNode(node); // dijit._Widget
15482 };
a089699c 15483
1354d172
AD
15484 dijit.getEnclosingWidget = function(node){
15485 // summary:
15486 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
15487 // the node is not contained within the DOM tree of any widget
15488 // node: DOMNode
15489 return registry.getEnclosingWidget(node);
15490 };
15491 =====*/
15492 array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){
15493 dijit[name] = registry[name];
15494 });
a089699c 15495
1354d172
AD
15496 /*=====
15497 dojo.mixin(dijit, {
15498 // defaultDuration: Integer
15499 // The default fx.animation speed (in ms) to use for all Dijit
15500 // transitional fx.animations, unless otherwise specified
15501 // on a per-instance basis. Defaults to 200, overrided by
15502 // `djConfig.defaultDuration`
15503 defaultDuration: 200
15504 });
15505 =====*/
15506 dijit.defaultDuration = config["defaultDuration"] || 200;
a089699c 15507
1354d172
AD
15508 return dijit;
15509});
a089699c 15510
1354d172
AD
15511},
15512'dijit/layout/StackController':function(){
15513define("dijit/layout/StackController", [
15514 "dojo/_base/array", // array.forEach array.indexOf array.map
15515 "dojo/_base/declare", // declare
15516 "dojo/_base/event", // event.stop
15517 "dojo/keys", // keys
15518 "dojo/_base/lang", // lang.getObject
15519 "dojo/_base/sniff", // has("ie")
15520 "../focus", // focus.focus()
15521 "../registry", // registry.byId
15522 "../_Widget",
15523 "../_TemplatedMixin",
15524 "../_Container",
15525 "../form/ToggleButton",
15526 "dojo/i18n!../nls/common"
15527], function(array, declare, event, keys, lang, has,
15528 focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
a089699c
AD
15529
15530/*=====
1354d172
AD
15531 var _Widget = dijit._Widget;
15532 var _TemplatedMixin = dijit._TemplatedMixin;
15533 var _Container = dijit._Container;
15534 var ToggleButton = dijit.form.ToggleButton;
a089699c
AD
15535=====*/
15536
1354d172
AD
15537 // module:
15538 // dijit/layout/StackController
15539 // summary:
15540 // Set of buttons to select a page in a `dijit.layout.StackContainer`
a089699c 15541
1354d172
AD
15542 var StackButton = declare("dijit.layout._StackButton", ToggleButton, {
15543 // summary:
15544 // Internal widget used by StackContainer.
15545 // description:
15546 // The button-like or tab-like object you click to select or delete a page
15547 // tags:
15548 // private
a089699c 15549
1354d172
AD
15550 // Override _FormWidget.tabIndex.
15551 // StackContainer buttons are not in the tab order by default.
15552 // Probably we should be calling this.startupKeyNavChildren() instead.
15553 tabIndex: "-1",
a089699c 15554
1354d172
AD
15555 // closeButton: Boolean
15556 // When true, display close button for this tab
15557 closeButton: false,
15558
15559 _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
15560 this.inherited(arguments);
15561 this.focusNode.removeAttribute("aria-pressed");
a089699c
AD
15562 },
15563
1354d172
AD
15564 buildRendering: function(/*Event*/ evt){
15565 this.inherited(arguments);
15566 (this.focusNode || this.domNode).setAttribute("role", "tab");
a089699c
AD
15567 },
15568
1354d172 15569 onClick: function(/*Event*/ /*===== evt =====*/){
a089699c 15570 // summary:
1354d172
AD
15571 // This is for TabContainer where the tabs are <span> rather than button,
15572 // so need to set focus explicitly (on some browsers)
15573 // Note that you shouldn't override this method, but you can connect to it.
15574 focus.focus(this.focusNode);
15575
15576 // ... now let StackController catch the event and tell me what to do
a089699c
AD
15577 },
15578
1354d172 15579 onClickCloseButton: function(/*Event*/ evt){
a089699c 15580 // summary:
1354d172
AD
15581 // StackContainer connects to this function; if your widget contains a close button
15582 // then clicking it should call this function.
15583 // Note that you shouldn't override this method, but you can connect to it.
15584 evt.stopPropagation();
15585 }
15586 });
a089699c
AD
15587
15588
1354d172
AD
15589 var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], {
15590 // summary:
15591 // Set of buttons to select a page in a `dijit.layout.StackContainer`
15592 // description:
15593 // Monitors the specified StackContainer, and whenever a page is
15594 // added, deleted, or selected, updates itself accordingly.
a089699c 15595
1354d172 15596 baseClass: "dijitStackController",
a089699c 15597
1354d172 15598 templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
a089699c 15599
1354d172
AD
15600 // containerId: [const] String
15601 // The id of the page container that I point to
15602 containerId: "",
a089699c 15603
1354d172
AD
15604 // buttonWidget: [const] Constructor
15605 // The button widget to create to correspond to each page
15606 buttonWidget: StackButton,
a089699c 15607
1354d172
AD
15608 constructor: function(){
15609 this.pane2button = {}; // mapping from pane id to buttons
15610 this.pane2connects = {}; // mapping from pane id to this.connect() handles
15611 this.pane2watches = {}; // mapping from pane id to watch() handles
15612 },
a089699c 15613
1354d172
AD
15614 postCreate: function(){
15615 this.inherited(arguments);
a089699c 15616
1354d172
AD
15617 // Listen to notifications from StackContainer
15618 this.subscribe(this.containerId+"-startup", "onStartup");
15619 this.subscribe(this.containerId+"-addChild", "onAddChild");
15620 this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
15621 this.subscribe(this.containerId+"-selectChild", "onSelectChild");
15622 this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
15623 },
a089699c 15624
1354d172
AD
15625 onStartup: function(/*Object*/ info){
15626 // summary:
15627 // Called after StackContainer has finished initializing
15628 // tags:
15629 // private
15630 array.forEach(info.children, this.onAddChild, this);
15631 if(info.selected){
15632 // Show button corresponding to selected pane (unless selected
15633 // is null because there are no panes)
15634 this.onSelectChild(info.selected);
15635 }
15636 },
a089699c 15637
1354d172
AD
15638 destroy: function(){
15639 for(var pane in this.pane2button){
15640 this.onRemoveChild(registry.byId(pane));
15641 }
15642 this.inherited(arguments);
15643 },
a089699c 15644
1354d172
AD
15645 onAddChild: function(/*dijit._Widget*/ page, /*Integer?*/ insertIndex){
15646 // summary:
15647 // Called whenever a page is added to the container.
15648 // Create button corresponding to the page.
15649 // tags:
15650 // private
a089699c 15651
1354d172
AD
15652 // create an instance of the button widget
15653 // (remove typeof buttonWidget == string support in 2.0)
15654 var cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
15655 var button = new cls({
15656 id: this.id + "_" + page.id,
15657 label: page.title,
15658 dir: page.dir,
15659 lang: page.lang,
15660 textDir: page.textDir,
15661 showLabel: page.showTitle,
15662 iconClass: page.iconClass,
15663 closeButton: page.closable,
15664 title: page.tooltip
15665 });
15666 button.focusNode.setAttribute("aria-selected", "false");
a089699c 15667
a089699c 15668
1354d172
AD
15669 // map from page attribute to corresponding tab button attribute
15670 var pageAttrList = ["title", "showTitle", "iconClass", "closable", "tooltip"],
15671 buttonAttrList = ["label", "showLabel", "iconClass", "closeButton", "title"];
a089699c 15672
1354d172
AD
15673 // watch() so events like page title changes are reflected in tab button
15674 this.pane2watches[page.id] = array.map(pageAttrList, function(pageAttr, idx){
15675 return page.watch(pageAttr, function(name, oldVal, newVal){
15676 button.set(buttonAttrList[idx], newVal);
15677 });
15678 });
a089699c 15679
1354d172
AD
15680 // connections so that clicking a tab button selects the corresponding page
15681 this.pane2connects[page.id] = [
15682 this.connect(button, 'onClick', lang.hitch(this,"onButtonClick", page)),
15683 this.connect(button, 'onClickCloseButton', lang.hitch(this,"onCloseButtonClick", page))
15684 ];
a089699c 15685
1354d172
AD
15686 this.addChild(button, insertIndex);
15687 this.pane2button[page.id] = button;
15688 page.controlButton = button; // this value might be overwritten if two tabs point to same container
15689 if(!this._currentChild){ // put the first child into the tab order
15690 button.focusNode.setAttribute("tabIndex", "0");
15691 button.focusNode.setAttribute("aria-selected", "true");
15692 this._currentChild = page;
15693 }
15694 // make sure all tabs have the same length
15695 if(!this.isLeftToRight() && has("ie") && this._rectifyRtlTabList){
15696 this._rectifyRtlTabList();
15697 }
15698 },
a089699c 15699
1354d172
AD
15700 onRemoveChild: function(/*dijit._Widget*/ page){
15701 // summary:
15702 // Called whenever a page is removed from the container.
15703 // Remove the button corresponding to the page.
15704 // tags:
15705 // private
a089699c 15706
1354d172 15707 if(this._currentChild === page){ this._currentChild = null; }
a089699c 15708
1354d172
AD
15709 // disconnect/unwatch connections/watches related to page being removed
15710 array.forEach(this.pane2connects[page.id], lang.hitch(this, "disconnect"));
15711 delete this.pane2connects[page.id];
15712 array.forEach(this.pane2watches[page.id], function(w){ w.unwatch(); });
15713 delete this.pane2watches[page.id];
a089699c 15714
1354d172
AD
15715 var button = this.pane2button[page.id];
15716 if(button){
15717 this.removeChild(button);
15718 delete this.pane2button[page.id];
15719 button.destroy();
15720 }
15721 delete page.controlButton;
15722 },
a089699c 15723
1354d172
AD
15724 onSelectChild: function(/*dijit._Widget*/ page){
15725 // summary:
15726 // Called when a page has been selected in the StackContainer, either by me or by another StackController
15727 // tags:
15728 // private
a089699c 15729
1354d172 15730 if(!page){ return; }
a089699c 15731
1354d172
AD
15732 if(this._currentChild){
15733 var oldButton=this.pane2button[this._currentChild.id];
15734 oldButton.set('checked', false);
15735 oldButton.focusNode.setAttribute("aria-selected", "false");
15736 oldButton.focusNode.setAttribute("tabIndex", "-1");
15737 }
a089699c 15738
1354d172
AD
15739 var newButton=this.pane2button[page.id];
15740 newButton.set('checked', true);
15741 newButton.focusNode.setAttribute("aria-selected", "true");
15742 this._currentChild = page;
15743 newButton.focusNode.setAttribute("tabIndex", "0");
15744 var container = registry.byId(this.containerId);
15745 container.containerNode.setAttribute("aria-labelledby", newButton.id);
15746 },
a089699c 15747
1354d172
AD
15748 onButtonClick: function(/*dijit._Widget*/ page){
15749 // summary:
15750 // Called whenever one of my child buttons is pressed in an attempt to select a page
15751 // tags:
15752 // private
81bea17a 15753
1354d172
AD
15754 if(this._currentChild.id === page.id) {
15755 //In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
15756 var button=this.pane2button[page.id];
15757 button.set('checked', true);
15758 }
15759 var container = registry.byId(this.containerId);
15760 container.selectChild(page);
a089699c
AD
15761 },
15762
1354d172
AD
15763 onCloseButtonClick: function(/*dijit._Widget*/ page){
15764 // summary:
15765 // Called whenever one of my child buttons [X] is pressed in an attempt to close a page
15766 // tags:
15767 // private
81bea17a 15768
1354d172
AD
15769 var container = registry.byId(this.containerId);
15770 container.closeChild(page);
15771 if(this._currentChild){
15772 var b = this.pane2button[this._currentChild.id];
15773 if(b){
15774 focus.focus(b.focusNode || b.domNode);
a089699c
AD
15775 }
15776 }
a089699c
AD
15777 },
15778
1354d172
AD
15779 // TODO: this is a bit redundant with forward, back api in StackContainer
15780 adjacent: function(/*Boolean*/ forward){
15781 // summary:
15782 // Helper for onkeypress to find next/previous button
15783 // tags:
15784 // private
a089699c 15785
1354d172
AD
15786 if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
15787 // find currently focused button in children array
15788 var children = this.getChildren();
15789 var current = array.indexOf(children, this.pane2button[this._currentChild.id]);
15790 // pick next button to focus on
15791 var offset = forward ? 1 : children.length - 1;
15792 return children[ (current + offset) % children.length ]; // dijit._Widget
a089699c
AD
15793 },
15794
1354d172 15795 onkeypress: function(/*Event*/ e){
a089699c 15796 // summary:
1354d172
AD
15797 // Handle keystrokes on the page list, for advancing to next/previous button
15798 // and closing the current page if the page is closable.
15799 // tags:
15800 // private
15801
15802 if(this.disabled || e.altKey ){ return; }
15803 var forward = null;
15804 if(e.ctrlKey || !e._djpage){
15805 switch(e.charOrCode){
15806 case keys.LEFT_ARROW:
15807 case keys.UP_ARROW:
15808 if(!e._djpage){ forward = false; }
15809 break;
15810 case keys.PAGE_UP:
15811 if(e.ctrlKey){ forward = false; }
15812 break;
15813 case keys.RIGHT_ARROW:
15814 case keys.DOWN_ARROW:
15815 if(!e._djpage){ forward = true; }
15816 break;
15817 case keys.PAGE_DOWN:
15818 if(e.ctrlKey){ forward = true; }
15819 break;
15820 case keys.HOME:
15821 case keys.END:
15822 var children = this.getChildren();
15823 if(children && children.length){
15824 children[e.charOrCode == keys.HOME ? 0 : children.length-1].onClick();
15825 }
15826 event.stop(e);
15827 break;
15828 case keys.DELETE:
15829 if(this._currentChild.closable){
15830 this.onCloseButtonClick(this._currentChild);
15831 }
15832 event.stop(e);
15833 break;
15834 default:
15835 if(e.ctrlKey){
15836 if(e.charOrCode === keys.TAB){
15837 this.adjacent(!e.shiftKey).onClick();
15838 event.stop(e);
15839 }else if(e.charOrCode == "w"){
15840 if(this._currentChild.closable){
15841 this.onCloseButtonClick(this._currentChild);
15842 }
15843 event.stop(e); // avoid browser tab closing.
15844 }
15845 }
15846 }
15847 // handle next/previous page navigation (left/right arrow, etc.)
15848 if(forward !== null){
15849 this.adjacent(forward).onClick();
15850 event.stop(e);
15851 }
a089699c 15852 }
a089699c
AD
15853 },
15854
1354d172 15855 onContainerKeyPress: function(/*Object*/ info){
a089699c 15856 // summary:
1354d172
AD
15857 // Called when there was a keypress on the container
15858 // tags:
15859 // private
15860 info.e._djpage = info.page;
15861 this.onkeypress(info.e);
15862 }
15863 });
81bea17a 15864
1354d172 15865 StackController.StackButton = StackButton; // for monkey patching
81bea17a 15866
1354d172
AD
15867 return StackController;
15868});
81bea17a 15869
1354d172
AD
15870},
15871'dojo/dnd/Mover':function(){
15872define("dojo/dnd/Mover", ["../main", "../Evented", "../touch", "./common", "./autoscroll"], function(dojo, Evented, touch) {
15873 // module:
15874 // dojo/dnd/Mover
15875 // summary:
15876 // TODOC
81bea17a 15877
a089699c 15878
1354d172
AD
15879dojo.declare("dojo.dnd.Mover", [Evented], {
15880 constructor: function(node, e, host){
15881 // summary:
15882 // an object which makes a node follow the mouse, or touch-drag on touch devices.
15883 // Used as a default mover, and as a base class for custom movers.
15884 // node: Node
15885 // a node (or node's id) to be moved
15886 // e: Event
15887 // a mouse event, which started the move;
15888 // only pageX and pageY properties are used
15889 // host: Object?
15890 // object which implements the functionality of the move,
15891 // and defines proper events (onMoveStart and onMoveStop)
15892 this.node = dojo.byId(node);
15893 this.marginBox = {l: e.pageX, t: e.pageY};
15894 this.mouseButton = e.button;
15895 var h = (this.host = host), d = node.ownerDocument;
15896 this.events = [
15897 // At the start of a drag, onFirstMove is called, and then the following two
15898 // connects are disconnected
15899 dojo.connect(d, touch.move, this, "onFirstMove"),
a089699c 15900
1354d172
AD
15901 // These are called continually during the drag
15902 dojo.connect(d, touch.move, this, "onMouseMove"),
a089699c 15903
1354d172
AD
15904 // And these are called at the end of the drag
15905 dojo.connect(d, touch.release, this, "onMouseUp"),
a089699c 15906
1354d172
AD
15907 // cancel text selection and text dragging
15908 dojo.connect(d, "ondragstart", dojo.stopEvent),
15909 dojo.connect(d.body, "onselectstart", dojo.stopEvent)
15910 ];
15911 // notify that the move has started
15912 if(h && h.onMoveStart){
15913 h.onMoveStart(this);
15914 }
15915 },
15916 // mouse event processors
15917 onMouseMove: function(e){
15918 // summary:
15919 // event processor for onmousemove/ontouchmove
15920 // e: Event
15921 // mouse/touch event
15922 dojo.dnd.autoScroll(e);
15923 var m = this.marginBox;
15924 this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
15925 dojo.stopEvent(e);
15926 },
15927 onMouseUp: function(e){
15928 if(dojo.isWebKit && dojo.isMac && this.mouseButton == 2 ?
15929 e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
15930 this.destroy();
15931 }
15932 dojo.stopEvent(e);
15933 },
15934 // utilities
15935 onFirstMove: function(e){
15936 // summary:
15937 // makes the node absolute; it is meant to be called only once.
15938 // relative and absolutely positioned nodes are assumed to use pixel units
15939 var s = this.node.style, l, t, h = this.host;
15940 switch(s.position){
15941 case "relative":
15942 case "absolute":
15943 // assume that left and top values are in pixels already
15944 l = Math.round(parseFloat(s.left)) || 0;
15945 t = Math.round(parseFloat(s.top)) || 0;
15946 break;
15947 default:
15948 s.position = "absolute"; // enforcing the absolute mode
15949 var m = dojo.marginBox(this.node);
15950 // event.pageX/pageY (which we used to generate the initial
15951 // margin box) includes padding and margin set on the body.
15952 // However, setting the node's position to absolute and then
15953 // doing dojo.marginBox on it *doesn't* take that additional
15954 // space into account - so we need to subtract the combined
15955 // padding and margin. We use getComputedStyle and
15956 // _getMarginBox/_getContentBox to avoid the extra lookup of
15957 // the computed style.
15958 var b = dojo.doc.body;
15959 var bs = dojo.getComputedStyle(b);
15960 var bm = dojo._getMarginBox(b, bs);
15961 var bc = dojo._getContentBox(b, bs);
15962 l = m.l - (bc.l - bm.l);
15963 t = m.t - (bc.t - bm.t);
15964 break;
15965 }
15966 this.marginBox.l = l - this.marginBox.l;
15967 this.marginBox.t = t - this.marginBox.t;
15968 if(h && h.onFirstMove){
15969 h.onFirstMove(this, e);
15970 }
a089699c 15971
1354d172
AD
15972 // Disconnect onmousemove and ontouchmove events that call this function
15973 dojo.disconnect(this.events.shift());
15974 },
15975 destroy: function(){
15976 // summary:
15977 // stops the move, deletes all references, so the object can be garbage-collected
15978 dojo.forEach(this.events, dojo.disconnect);
15979 // undo global settings
15980 var h = this.host;
15981 if(h && h.onMoveStop){
15982 h.onMoveStop(this);
15983 }
15984 // destroy objects
15985 this.events = this.node = this.host = null;
15986 }
15987});
a089699c 15988
1354d172
AD
15989return dojo.dnd.Mover;
15990});
a089699c 15991
1354d172
AD
15992},
15993'dijit/layout/TabContainer':function(){
15994define("dijit/layout/TabContainer", [
15995 "dojo/_base/lang", // lang.getObject
15996 "dojo/_base/declare", // declare
15997 "./_TabContainerBase",
15998 "./TabController",
15999 "./ScrollingTabController"
16000], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){
a089699c 16001
1354d172
AD
16002/*=====
16003 var _TabContainerBase = dijit.layout._TabContainerBase;
16004 var TabController = dijit.layout.TabController;
16005 var ScrollingTabController = dijit.layout.ScrollingTabController;
16006=====*/
a089699c 16007
1354d172
AD
16008 // module:
16009 // dijit/layout/TabContainer
16010 // summary:
16011 // A Container with tabs to select each child (only one of which is displayed at a time).
a089699c 16012
a089699c 16013
1354d172
AD
16014 return declare("dijit.layout.TabContainer", _TabContainerBase, {
16015 // summary:
16016 // A Container with tabs to select each child (only one of which is displayed at a time).
16017 // description:
16018 // A TabContainer is a container that has multiple panes, but shows only
16019 // one pane at a time. There are a set of tabs corresponding to each pane,
16020 // where each tab has the name (aka title) of the pane, and optionally a close button.
a089699c 16021
1354d172
AD
16022 // useMenu: [const] Boolean
16023 // True if a menu should be used to select tabs when they are too
16024 // wide to fit the TabContainer, false otherwise.
16025 useMenu: true,
a089699c 16026
1354d172
AD
16027 // useSlider: [const] Boolean
16028 // True if a slider should be used to select tabs when they are too
16029 // wide to fit the TabContainer, false otherwise.
16030 useSlider: true,
a089699c 16031
1354d172
AD
16032 // controllerWidget: String
16033 // An optional parameter to override the widget used to display the tab labels
16034 controllerWidget: "",
81bea17a 16035
1354d172
AD
16036 _makeController: function(/*DomNode*/ srcNode){
16037 // summary:
16038 // Instantiate tablist controller widget and return reference to it.
16039 // Callback from _TabContainerBase.postCreate().
16040 // tags:
16041 // protected extension
81bea17a 16042
1354d172
AD
16043 var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
16044 TabController = lang.getObject(this.controllerWidget);
a089699c 16045
1354d172
AD
16046 return new TabController({
16047 id: this.id + "_tablist",
16048 dir: this.dir,
16049 lang: this.lang,
16050 textDir: this.textDir,
16051 tabPosition: this.tabPosition,
16052 doLayout: this.doLayout,
16053 containerId: this.id,
16054 "class": cls,
16055 nested: this.nested,
16056 useMenu: this.useMenu,
16057 useSlider: this.useSlider,
16058 tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
16059 }, srcNode);
a089699c
AD
16060 },
16061
1354d172
AD
16062 postMixInProperties: function(){
16063 this.inherited(arguments);
a089699c 16064
1354d172
AD
16065 // Scrolling controller only works for horizontal non-nested tabs
16066 if(!this.controllerWidget){
16067 this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
16068 "dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
a089699c 16069 }
1354d172
AD
16070 }
16071 });
16072});
a089699c 16073
1354d172
AD
16074},
16075'dijit/BackgroundIframe':function(){
16076define("dijit/BackgroundIframe", [
16077 "require", // require.toUrl
16078 ".", // to export dijit.BackgroundIframe
16079 "dojo/_base/config",
16080 "dojo/dom-construct", // domConstruct.create
16081 "dojo/dom-style", // domStyle.set
16082 "dojo/_base/lang", // lang.extend lang.hitch
16083 "dojo/on",
16084 "dojo/_base/sniff", // has("ie"), has("mozilla"), has("quirks")
16085 "dojo/_base/window" // win.doc.createElement
16086], function(require, dijit, config, domConstruct, domStyle, lang, on, has, win){
16087
16088 // module:
16089 // dijit/BackgroundIFrame
16090 // summary:
16091 // new dijit.BackgroundIframe(node)
16092 // Makes a background iframe as a child of node, that fills
16093 // area (and position) of node
a089699c 16094
1354d172
AD
16095 // TODO: remove _frames, it isn't being used much, since popups never release their
16096 // iframes (see [22236])
16097 var _frames = new function(){
16098 // summary:
16099 // cache of iframes
a089699c 16100
1354d172 16101 var queue = [];
a089699c 16102
1354d172
AD
16103 this.pop = function(){
16104 var iframe;
16105 if(queue.length){
16106 iframe = queue.pop();
16107 iframe.style.display="";
a089699c 16108 }else{
1354d172
AD
16109 if(has("ie") < 9){
16110 var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
16111 var html="<iframe src='" + burl + "' role='presentation'"
16112 + " style='position: absolute; left: 0px; top: 0px;"
16113 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
16114 iframe = win.doc.createElement(html);
16115 }else{
16116 iframe = domConstruct.create("iframe");
16117 iframe.src = 'javascript:""';
16118 iframe.className = "dijitBackgroundIframe";
16119 iframe.setAttribute("role", "presentation");
16120 domStyle.set(iframe, "opacity", 0.1);
16121 }
16122 iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
a089699c 16123 }
1354d172
AD
16124 return iframe;
16125 };
a089699c 16126
1354d172
AD
16127 this.push = function(iframe){
16128 iframe.style.display="none";
16129 queue.push(iframe);
16130 }
16131 }();
a089699c 16132
a089699c 16133
1354d172
AD
16134 dijit.BackgroundIframe = function(/*DomNode*/ node){
16135 // summary:
16136 // For IE/FF z-index schenanigans. id attribute is required.
16137 //
16138 // description:
16139 // new dijit.BackgroundIframe(node)
16140 // Makes a background iframe as a child of node, that fills
16141 // area (and position) of node
16142
16143 if(!node.id){ throw new Error("no id"); }
16144 if(has("ie") || has("mozilla")){
16145 var iframe = (this.iframe = _frames.pop());
16146 node.appendChild(iframe);
16147 if(has("ie")<7 || has("quirks")){
16148 this.resize(node);
16149 this._conn = on(node, 'resize', lang.hitch(this, function(){
16150 this.resize(node);
16151 }));
16152 }else{
16153 domStyle.set(iframe, {
16154 width: '100%',
16155 height: '100%'
a089699c 16156 });
a089699c 16157 }
1354d172
AD
16158 }
16159 };
a089699c 16160
1354d172
AD
16161 lang.extend(dijit.BackgroundIframe, {
16162 resize: function(node){
81bea17a 16163 // summary:
1354d172
AD
16164 // Resize the iframe so it's the same size as node.
16165 // Needed on IE6 and IE/quirks because height:100% doesn't work right.
16166 if(this.iframe){
16167 domStyle.set(this.iframe, {
16168 width: node.offsetWidth + 'px',
16169 height: node.offsetHeight + 'px'
16170 });
a089699c 16171 }
a089699c 16172 },
1354d172 16173 destroy: function(){
a089699c 16174 // summary:
1354d172
AD
16175 // destroy the iframe
16176 if(this._conn){
16177 this._conn.remove();
16178 this._conn = null;
16179 }
16180 if(this.iframe){
16181 _frames.push(this.iframe);
16182 delete this.iframe;
16183 }
16184 }
16185 });
a089699c 16186
1354d172
AD
16187 return dijit.BackgroundIframe;
16188});
a089699c 16189
1354d172
AD
16190},
16191'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
16192'dojo/dnd/Avatar':function(){
16193define("dojo/dnd/Avatar", ["../main", "./common"], function(dojo) {
16194 // module:
16195 // dojo/dnd/Avatar
16196 // summary:
16197 // TODOC
a089699c 16198
a089699c 16199
1354d172
AD
16200dojo.declare("dojo.dnd.Avatar", null, {
16201 // summary:
16202 // Object that represents transferred DnD items visually
16203 // manager: Object
16204 // a DnD manager object
a089699c 16205
1354d172
AD
16206 constructor: function(manager){
16207 this.manager = manager;
16208 this.construct();
16209 },
a089699c 16210
1354d172
AD
16211 // methods
16212 construct: function(){
16213 // summary:
16214 // constructor function;
16215 // it is separate so it can be (dynamically) overwritten in case of need
16216 this.isA11y = dojo.hasClass(dojo.body(),"dijit_a11y");
16217 var a = dojo.create("table", {
16218 "class": "dojoDndAvatar",
16219 style: {
16220 position: "absolute",
16221 zIndex: "1999",
16222 margin: "0px"
16223 }
16224 }),
16225 source = this.manager.source, node,
16226 b = dojo.create("tbody", null, a),
16227 tr = dojo.create("tr", null, b),
16228 td = dojo.create("td", null, tr),
16229 icon = this.isA11y ? dojo.create("span", {
16230 id : "a11yIcon",
16231 innerHTML : this.manager.copy ? '+' : "<"
16232 }, td) : null,
16233 span = dojo.create("span", {
16234 innerHTML: source.generateText ? this._generateText() : ""
16235 }, td),
16236 k = Math.min(5, this.manager.nodes.length), i = 0;
16237 // we have to set the opacity on IE only after the node is live
16238 dojo.attr(tr, {
16239 "class": "dojoDndAvatarHeader",
16240 style: {opacity: 0.9}
16241 });
16242 for(; i < k; ++i){
16243 if(source.creator){
16244 // create an avatar representation of the node
16245 node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
16246 }else{
16247 // or just clone the node and hope it works
16248 node = this.manager.nodes[i].cloneNode(true);
16249 if(node.tagName.toLowerCase() == "tr"){
16250 // insert extra table nodes
16251 var table = dojo.create("table"),
16252 tbody = dojo.create("tbody", null, table);
16253 tbody.appendChild(node);
16254 node = table;
16255 }
16256 }
16257 node.id = "";
16258 tr = dojo.create("tr", null, b);
16259 td = dojo.create("td", null, tr);
16260 td.appendChild(node);
16261 dojo.attr(tr, {
16262 "class": "dojoDndAvatarItem",
16263 style: {opacity: (9 - i) / 10}
16264 });
a089699c 16265 }
1354d172
AD
16266 this.node = a;
16267 },
16268 destroy: function(){
a089699c 16269 // summary:
1354d172
AD
16270 // destructor for the avatar; called to remove all references so it can be garbage-collected
16271 dojo.destroy(this.node);
16272 this.node = false;
16273 },
16274 update: function(){
16275 // summary:
16276 // updates the avatar to reflect the current DnD state
16277 dojo[(this.manager.canDropFlag ? "add" : "remove") + "Class"](this.node, "dojoDndAvatarCanDrop");
16278 if (this.isA11y){
16279 var icon = dojo.byId("a11yIcon");
16280 var text = '+'; // assume canDrop && copy
16281 if (this.manager.canDropFlag && !this.manager.copy) {
16282 text = '< '; // canDrop && move
16283 }else if (!this.manager.canDropFlag && !this.manager.copy) {
16284 text = "o"; //!canDrop && move
16285 }else if(!this.manager.canDropFlag){
16286 text = 'x'; // !canDrop && copy
16287 }
16288 icon.innerHTML=text;
16289 }
16290 // replace text
16291 dojo.query(("tr.dojoDndAvatarHeader td span" +(this.isA11y ? " span" : "")), this.node).forEach(
16292 function(node){
16293 node.innerHTML = this._generateText();
16294 }, this);
16295 },
16296 _generateText: function(){
16297 // summary: generates a proper text to reflect copying or moving of items
16298 return this.manager.nodes.length.toString();
16299 }
16300});
a089699c 16301
1354d172
AD
16302return dojo.dnd.Avatar;
16303});
a089699c 16304
1354d172
AD
16305},
16306'dijit/form/Button':function(){
16307require({cache:{
16308'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
16309define("dijit/form/Button", [
16310 "require",
16311 "dojo/_base/declare", // declare
16312 "dojo/dom-class", // domClass.toggle
16313 "dojo/_base/kernel", // kernel.deprecated
16314 "dojo/_base/lang", // lang.trim
16315 "dojo/ready",
16316 "./_FormWidget",
16317 "./_ButtonMixin",
16318 "dojo/text!./templates/Button.html"
16319], function(require, declare, domClass, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
a089699c 16320
1354d172
AD
16321/*=====
16322 var _FormWidget = dijit.form._FormWidget;
16323 var _ButtonMixin = dijit.form._ButtonMixin;
16324=====*/
81bea17a 16325
1354d172
AD
16326// module:
16327// dijit/form/Button
16328// summary:
16329// Button widget
81bea17a 16330
1354d172
AD
16331// Back compat w/1.6, remove for 2.0
16332if(!kernel.isAsync){
16333 ready(0, function(){
16334 var requires = ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
16335 require(requires); // use indirection so modules not rolled into a build
16336 });
16337}
a089699c 16338
1354d172
AD
16339return declare("dijit.form.Button", [_FormWidget, _ButtonMixin], {
16340 // summary:
16341 // Basically the same thing as a normal HTML button, but with special styling.
16342 // description:
16343 // Buttons can display a label, an icon, or both.
16344 // A label should always be specified (through innerHTML) or the label
16345 // attribute. It can be hidden via showLabel=false.
16346 // example:
16347 // | <button data-dojo-type="dijit.form.Button" onClick="...">Hello world</button>
16348 //
16349 // example:
16350 // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
16351 // | dojo.body().appendChild(button1.domNode);
a089699c 16352
1354d172
AD
16353 // showLabel: Boolean
16354 // Set this to true to hide the label text and display only the icon.
16355 // (If showLabel=false then iconClass must be specified.)
16356 // Especially useful for toolbars.
16357 // If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
16358 //
16359 // The exception case is for computers in high-contrast mode, where the label
16360 // will still be displayed, since the icon doesn't appear.
16361 showLabel: true,
a089699c 16362
1354d172
AD
16363 // iconClass: String
16364 // Class to apply to DOMNode in button to make it display an icon
16365 iconClass: "dijitNoIcon",
16366 _setIconClassAttr: { node: "iconNode", type: "class" },
a089699c 16367
1354d172 16368 baseClass: "dijitButton",
a089699c 16369
1354d172 16370 templateString: template,
a089699c 16371
1354d172
AD
16372 // Map widget attributes to DOMNode attributes.
16373 _setValueAttr: "valueNode",
a089699c 16374
1354d172
AD
16375 _onClick: function(/*Event*/ e){
16376 // summary:
16377 // Internal function to handle click actions
16378 var ok = this.inherited(arguments);
16379 if(ok){
16380 if(this.valueNode){
16381 this.valueNode.click();
16382 e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
16383 // leave ok = true so that subclasses can do what they need to do
a089699c 16384 }
1354d172
AD
16385 }
16386 return ok;
16387 },
a089699c 16388
1354d172
AD
16389 _fillContent: function(/*DomNode*/ source){
16390 // Overrides _Templated._fillContent().
16391 // If button label is specified as srcNodeRef.innerHTML rather than
16392 // this.params.label, handle it here.
16393 // TODO: remove the method in 2.0, parser will do it all for me
16394 if(source && (!this.params || !("label" in this.params))){
16395 var sourceLabel = lang.trim(source.innerHTML);
16396 if(sourceLabel){
16397 this.label = sourceLabel; // _applyAttributes will be called after buildRendering completes to update the DOM
a089699c 16398 }
1354d172
AD
16399 }
16400 },
a089699c 16401
1354d172
AD
16402 _setShowLabelAttr: function(val){
16403 if(this.containerNode){
16404 domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
16405 }
16406 this._set("showLabel", val);
16407 },
a089699c 16408
1354d172
AD
16409 setLabel: function(/*String*/ content){
16410 // summary:
16411 // Deprecated. Use set('label', ...) instead.
16412 kernel.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
16413 this.set("label", content);
16414 },
a089699c 16415
1354d172
AD
16416 _setLabelAttr: function(/*String*/ content){
16417 // summary:
16418 // Hook for set('label', ...) to work.
16419 // description:
16420 // Set the label (text) of the button; takes an HTML string.
16421 // If the label is hidden (showLabel=false) then and no title has
16422 // been specified, then label is also set as title attribute of icon.
16423 this.inherited(arguments);
16424 if(!this.showLabel && !("title" in this.params)){
16425 this.titleNode.title = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
16426 }
16427 }
16428});
a089699c 16429
a089699c 16430
1354d172 16431});
a089699c 16432
a089699c 16433
1354d172
AD
16434},
16435'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",
16436'dojo/dnd/move':function(){
16437define("dojo/dnd/move", ["../main", "./Mover", "./Moveable"], function(dojo) {
16438 // module:
16439 // dojo/dnd/move
16440 // summary:
16441 // TODOC
a089699c 16442
a089699c 16443
1354d172
AD
16444/*=====
16445dojo.declare("dojo.dnd.move.__constrainedMoveableArgs", [dojo.dnd.__MoveableArgs], {
16446 // constraints: Function
16447 // Calculates a constraint box.
16448 // It is called in a context of the moveable object.
16449 constraints: function(){},
a089699c 16450
1354d172
AD
16451 // within: Boolean
16452 // restrict move within boundaries.
16453 within: false
16454});
16455=====*/
a089699c 16456
1354d172
AD
16457dojo.declare("dojo.dnd.move.constrainedMoveable", dojo.dnd.Moveable, {
16458 // object attributes (for markup)
16459 constraints: function(){},
16460 within: false,
a089699c 16461
1354d172
AD
16462 constructor: function(node, params){
16463 // summary:
16464 // an object that makes a node moveable
16465 // node: Node
16466 // a node (or node's id) to be moved
16467 // params: dojo.dnd.move.__constrainedMoveableArgs?
16468 // an optional object with additional parameters;
16469 // the rest is passed to the base class
16470 if(!params){ params = {}; }
16471 this.constraints = params.constraints;
16472 this.within = params.within;
16473 },
16474 onFirstMove: function(/* dojo.dnd.Mover */ mover){
16475 // summary:
16476 // called during the very first move notification;
16477 // can be used to initialize coordinates, can be overwritten.
16478 var c = this.constraintBox = this.constraints.call(this, mover);
16479 c.r = c.l + c.w;
16480 c.b = c.t + c.h;
16481 if(this.within){
16482 var mb = dojo._getMarginSize(mover.node);
16483 c.r -= mb.w;
16484 c.b -= mb.h;
a089699c 16485 }
1354d172
AD
16486 },
16487 onMove: function(/* dojo.dnd.Mover */ mover, /* Object */ leftTop){
16488 // summary:
16489 // called during every move notification;
16490 // should actually move the node; can be overwritten.
16491 var c = this.constraintBox, s = mover.node.style;
16492 this.onMoving(mover, leftTop);
16493 leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
16494 leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
16495 s.left = leftTop.l + "px";
16496 s.top = leftTop.t + "px";
16497 this.onMoved(mover, leftTop);
a089699c 16498 }
1354d172 16499});
a089699c 16500
1354d172
AD
16501/*=====
16502dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
16503 // box: Object
16504 // a constraint box
16505 box: {}
16506});
16507=====*/
a089699c 16508
1354d172
AD
16509dojo.declare("dojo.dnd.move.boxConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
16510 // box:
16511 // object attributes (for markup)
16512 box: {},
16513
16514 constructor: function(node, params){
16515 // summary:
16516 // an object, which makes a node moveable
16517 // node: Node
16518 // a node (or node's id) to be moved
16519 // params: dojo.dnd.move.__boxConstrainedMoveableArgs?
16520 // an optional object with parameters
16521 var box = params && params.box;
16522 this.constraints = function(){ return box; };
a089699c 16523 }
1354d172 16524});
a089699c 16525
1354d172
AD
16526/*=====
16527dojo.declare("dojo.dnd.move.__parentConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], {
16528 // area: String
16529 // A parent's area to restrict the move.
16530 // Can be "margin", "border", "padding", or "content".
16531 area: ""
16532});
16533=====*/
a089699c 16534
1354d172
AD
16535dojo.declare("dojo.dnd.move.parentConstrainedMoveable", dojo.dnd.move.constrainedMoveable, {
16536 // area:
16537 // object attributes (for markup)
16538 area: "content",
a089699c 16539
1354d172
AD
16540 constructor: function(node, params){
16541 // summary:
16542 // an object, which makes a node moveable
16543 // node: Node
16544 // a node (or node's id) to be moved
16545 // params: dojo.dnd.move.__parentConstrainedMoveableArgs?
16546 // an optional object with parameters
16547 var area = params && params.area;
16548 this.constraints = function(){
16549 var n = this.node.parentNode,
16550 s = dojo.getComputedStyle(n),
16551 mb = dojo._getMarginBox(n, s);
16552 if(area == "margin"){
16553 return mb; // Object
16554 }
16555 var t = dojo._getMarginExtents(n, s);
16556 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
16557 if(area == "border"){
16558 return mb; // Object
16559 }
16560 t = dojo._getBorderExtents(n, s);
16561 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
16562 if(area == "padding"){
16563 return mb; // Object
16564 }
16565 t = dojo._getPadExtents(n, s);
16566 mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
16567 return mb; // Object
16568 };
16569 }
16570});
a089699c 16571
1354d172 16572// patching functions one level up for compatibility
a089699c 16573
1354d172
AD
16574dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover;
16575dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover;
16576dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover;
a089699c 16577
1354d172
AD
16578return dojo.dnd.move;
16579});
a089699c 16580
1354d172
AD
16581},
16582'dijit/_WidgetBase':function(){
16583define("dijit/_WidgetBase", [
16584 "require", // require.toUrl
16585 "dojo/_base/array", // array.forEach array.map
16586 "dojo/aspect",
16587 "dojo/_base/config", // config.blankGif
16588 "dojo/_base/connect", // connect.connect
16589 "dojo/_base/declare", // declare
16590 "dojo/dom", // dom.byId
16591 "dojo/dom-attr", // domAttr.set domAttr.remove
16592 "dojo/dom-class", // domClass.add domClass.replace
16593 "dojo/dom-construct", // domConstruct.create domConstruct.destroy domConstruct.place
16594 "dojo/dom-geometry", // isBodyLtr
16595 "dojo/dom-style", // domStyle.set, domStyle.get
16596 "dojo/_base/kernel",
16597 "dojo/_base/lang", // mixin(), isArray(), etc.
16598 "dojo/on",
16599 "dojo/ready",
16600 "dojo/Stateful", // Stateful
16601 "dojo/topic",
16602 "dojo/_base/window", // win.doc.createTextNode
16603 "./registry" // registry.getUniqueId(), registry.findWidgets()
16604], function(require, array, aspect, config, connect, declare,
16605 dom, domAttr, domClass, domConstruct, domGeometry, domStyle, kernel,
16606 lang, on, ready, Stateful, topic, win, registry){
a089699c 16607
1354d172
AD
16608/*=====
16609var Stateful = dojo.Stateful;
16610=====*/
a089699c 16611
1354d172
AD
16612// module:
16613// dijit/_WidgetBase
16614// summary:
16615// Future base class for all Dijit widgets.
a089699c 16616
1354d172
AD
16617// For back-compat, remove in 2.0.
16618if(!kernel.isAsync){
16619 ready(0, function(){
16620 var requires = ["dijit/_base/manager"];
16621 require(requires); // use indirection so modules not rolled into a build
16622 });
16623}
a089699c 16624
1354d172
AD
16625// Nested hash listing attributes for each tag, all strings in lowercase.
16626// ex: {"div": {"style": true, "tabindex" true}, "form": { ...
16627var tagAttrs = {};
16628function getAttrs(obj){
16629 var ret = {};
16630 for(var attr in obj){
16631 ret[attr.toLowerCase()] = true;
a089699c 16632 }
1354d172
AD
16633 return ret;
16634}
a089699c 16635
1354d172
AD
16636function nonEmptyAttrToDom(attr){
16637 // summary:
16638 // Returns a setter function that copies the attribute to this.domNode,
16639 // or removes the attribute from this.domNode, depending on whether the
16640 // value is defined or not.
16641 return function(val){
16642 domAttr[val ? "set" : "remove"](this.domNode, attr, val);
16643 this._set(attr, val);
16644 };
a089699c
AD
16645}
16646
1354d172
AD
16647return declare("dijit._WidgetBase", Stateful, {
16648 // summary:
16649 // Future base class for all Dijit widgets.
16650 // description:
16651 // Future base class for all Dijit widgets.
16652 // _Widget extends this class adding support for various features needed by desktop.
16653 //
16654 // Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
16655 // postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
16656 //
16657 // Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
16658 // For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
16659 //
16660 // _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
16661 //
16662 // - DOM node attribute
16663 // | _setFocusAttr: {node: "focusNode", type: "attribute"}
16664 // | _setFocusAttr: "focusNode" (shorthand)
16665 // | _setFocusAttr: "" (shorthand, maps to this.domNode)
16666 // Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
16667 //
16668 // - DOM node innerHTML
16669 // | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
16670 // Maps this.title to this.titleNode.innerHTML
16671 //
16672 // - DOM node innerText
16673 // | _setTitleAttr: { node: "titleNode", type: "innerText" }
16674 // Maps this.title to this.titleNode.innerText
16675 //
16676 // - DOM node CSS class
16677 // | _setMyClassAttr: { node: "domNode", type: "class" }
16678 // Maps this.myClass to this.domNode.className
16679 //
16680 // If the value of _setXXXAttr is an array, then each element in the array matches one of the
16681 // formats of the above list.
16682 //
16683 // If the custom setter is null, no action is performed other than saving the new value
16684 // in the widget (in this).
16685 //
16686 // If no custom setter is defined for an attribute, then it will be copied
16687 // to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
16688 // That's only done though for attributes that match DOMNode attributes (title,
16689 // alt, aria-labelledby, etc.)
a089699c 16690
1354d172
AD
16691 // id: [const] String
16692 // A unique, opaque ID string that can be assigned by users or by the
16693 // system. If the developer passes an ID which is known not to be
16694 // unique, the specified ID is ignored and the system-generated ID is
16695 // used instead.
16696 id: "",
16697 _setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's
a089699c 16698
1354d172
AD
16699 // lang: [const] String
16700 // Rarely used. Overrides the default Dojo locale used to render this widget,
16701 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
16702 // Value must be among the list of locales specified during by the Dojo bootstrap,
16703 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
16704 lang: "",
16705 // set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
16706 _setLangAttr: nonEmptyAttrToDom("lang"),
a089699c 16707
1354d172
AD
16708 // dir: [const] String
16709 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
16710 // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's
16711 // default direction.
16712 dir: "",
16713 // set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
16714 _setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
16715
16716 // textDir: String
16717 // Bi-directional support, the main variable which is responsible for the direction of the text.
16718 // The text direction can be different than the GUI direction by using this parameter in creation
16719 // of a widget.
16720 // Allowed values:
16721 // 1. "ltr"
16722 // 2. "rtl"
16723 // 3. "auto" - contextual the direction of a text defined by first strong letter.
16724 // By default is as the page direction.
16725 textDir: "",
a089699c 16726
1354d172
AD
16727 // class: String
16728 // HTML class attribute
16729 "class": "",
16730 _setClassAttr: { node: "domNode", type: "class" },
a089699c 16731
1354d172
AD
16732 // style: String||Object
16733 // HTML style attributes as cssText string or name/value hash
16734 style: "",
81bea17a 16735
1354d172
AD
16736 // title: String
16737 // HTML title attribute.
16738 //
16739 // For form widgets this specifies a tooltip to display when hovering over
16740 // the widget (just like the native HTML title attribute).
16741 //
16742 // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer,
16743 // etc., it's used to specify the tab label, accordion pane title, etc.
16744 title: "",
a089699c 16745
1354d172
AD
16746 // tooltip: String
16747 // When this widget's title attribute is used to for a tab label, accordion pane title, etc.,
16748 // this specifies the tooltip to appear when the mouse is hovered over that text.
16749 tooltip: "",
a089699c 16750
1354d172
AD
16751 // baseClass: [protected] String
16752 // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate
16753 // widget state.
16754 baseClass: "",
a089699c 16755
1354d172
AD
16756 // srcNodeRef: [readonly] DomNode
16757 // pointer to original DOM node
16758 srcNodeRef: null,
a089699c 16759
1354d172
AD
16760 // domNode: [readonly] DomNode
16761 // This is our visible representation of the widget! Other DOM
16762 // Nodes may by assigned to other properties, usually through the
16763 // template system's data-dojo-attach-point syntax, but the domNode
16764 // property is the canonical "top level" node in widget UI.
16765 domNode: null,
a089699c 16766
1354d172
AD
16767 // containerNode: [readonly] DomNode
16768 // Designates where children of the source DOM node will be placed.
16769 // "Children" in this case refers to both DOM nodes and widgets.
16770 // For example, for myWidget:
16771 //
16772 // | <div data-dojo-type=myWidget>
16773 // | <b> here's a plain DOM node
16774 // | <span data-dojo-type=subWidget>and a widget</span>
16775 // | <i> and another plain DOM node </i>
16776 // | </div>
16777 //
16778 // containerNode would point to:
16779 //
16780 // | <b> here's a plain DOM node
16781 // | <span data-dojo-type=subWidget>and a widget</span>
16782 // | <i> and another plain DOM node </i>
16783 //
16784 // In templated widgets, "containerNode" is set via a
16785 // data-dojo-attach-point assignment.
16786 //
16787 // containerNode must be defined for any widget that accepts innerHTML
16788 // (like ContentPane or BorderContainer or even Button), and conversely
16789 // is null for widgets that don't, like TextBox.
16790 containerNode: null,
81bea17a 16791
1354d172
AD
16792/*=====
16793 // _started: Boolean
16794 // startup() has completed.
16795 _started: false,
16796=====*/
a089699c 16797
1354d172
AD
16798 // attributeMap: [protected] Object
16799 // Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
16800 // for each XXX attribute to be mapped to the DOM.
16801 //
16802 // attributeMap sets up a "binding" between attributes (aka properties)
16803 // of the widget and the widget's DOM.
16804 // Changes to widget attributes listed in attributeMap will be
16805 // reflected into the DOM.
16806 //
16807 // For example, calling set('title', 'hello')
16808 // on a TitlePane will automatically cause the TitlePane's DOM to update
16809 // with the new title.
16810 //
16811 // attributeMap is a hash where the key is an attribute of the widget,
16812 // and the value reflects a binding to a:
16813 //
16814 // - DOM node attribute
16815 // | focus: {node: "focusNode", type: "attribute"}
16816 // Maps this.focus to this.focusNode.focus
16817 //
16818 // - DOM node innerHTML
16819 // | title: { node: "titleNode", type: "innerHTML" }
16820 // Maps this.title to this.titleNode.innerHTML
16821 //
16822 // - DOM node innerText
16823 // | title: { node: "titleNode", type: "innerText" }
16824 // Maps this.title to this.titleNode.innerText
16825 //
16826 // - DOM node CSS class
16827 // | myClass: { node: "domNode", type: "class" }
16828 // Maps this.myClass to this.domNode.className
16829 //
16830 // If the value is an array, then each element in the array matches one of the
16831 // formats of the above list.
16832 //
16833 // There are also some shorthands for backwards compatibility:
16834 // - string --> { node: string, type: "attribute" }, for example:
16835 // | "focusNode" ---> { node: "focusNode", type: "attribute" }
16836 // - "" --> { node: "domNode", type: "attribute" }
16837 attributeMap: {},
a089699c 16838
1354d172
AD
16839 // _blankGif: [protected] String
16840 // Path to a blank 1x1 image.
16841 // Used by <img> nodes in templates that really get their image via CSS background-image.
16842 _blankGif: config.blankGif || require.toUrl("dojo/resources/blank.gif"),
a089699c 16843
1354d172 16844 //////////// INITIALIZATION METHODS ///////////////////////////////////////
a089699c 16845
1354d172
AD
16846 postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
16847 // summary:
16848 // Kicks off widget instantiation. See create() for details.
16849 // tags:
16850 // private
16851 this.create(params, srcNodeRef);
16852 },
a089699c 16853
1354d172
AD
16854 create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){
16855 // summary:
16856 // Kick off the life-cycle of a widget
16857 // params:
16858 // Hash of initialization parameters for widget, including
16859 // scalar values (like title, duration etc.) and functions,
16860 // typically callbacks like onClick.
16861 // srcNodeRef:
16862 // If a srcNodeRef (DOM node) is specified:
16863 // - use srcNodeRef.innerHTML as my contents
16864 // - if this is a behavioral widget then apply behavior
16865 // to that srcNodeRef
16866 // - otherwise, replace srcNodeRef with my generated DOM
16867 // tree
16868 // description:
16869 // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
16870 // etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
16871 // for a discussion of the widget creation lifecycle.
16872 //
16873 // Of course, adventurous developers could override create entirely, but this should
16874 // only be done as a last resort.
16875 // tags:
16876 // private
a089699c 16877
1354d172
AD
16878 // store pointer to original DOM tree
16879 this.srcNodeRef = dom.byId(srcNodeRef);
a089699c 16880
1354d172
AD
16881 // For garbage collection. An array of listener handles returned by this.connect() / this.subscribe()
16882 this._connects = [];
a089699c 16883
1354d172
AD
16884 // For widgets internal to this widget, invisible to calling code
16885 this._supportingWidgets = [];
a089699c 16886
1354d172
AD
16887 // this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
16888 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
a089699c 16889
1354d172
AD
16890 // mix in our passed parameters
16891 if(params){
16892 this.params = params;
16893 lang.mixin(this, params);
16894 }
16895 this.postMixInProperties();
81bea17a 16896
1354d172
AD
16897 // generate an id for the widget if one wasn't specified
16898 // (be sure to do this before buildRendering() because that function might
16899 // expect the id to be there.)
16900 if(!this.id){
16901 this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
16902 }
16903 registry.add(this);
a089699c 16904
1354d172
AD
16905 this.buildRendering();
16906
16907 if(this.domNode){
16908 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
16909 // Also calls custom setters for all attributes with custom setters.
16910 this._applyAttributes();
16911
16912 // If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
16913 // For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
16914 // widget being attached to the DOM since it isn't when a widget is created programmatically like
16915 // new MyWidget({}). See #11635.
16916 var source = this.srcNodeRef;
16917 if(source && source.parentNode && this.domNode !== source){
16918 source.parentNode.replaceChild(this.domNode, source);
a089699c 16919 }
1354d172 16920 }
a089699c 16921
1354d172
AD
16922 if(this.domNode){
16923 // Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
16924 // assuming that dojo._scopeName even exists in 2.0
16925 this.domNode.setAttribute("widgetId", this.id);
a089699c 16926 }
1354d172 16927 this.postCreate();
a089699c 16928
1354d172
AD
16929 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
16930 if(this.srcNodeRef && !this.srcNodeRef.parentNode){
16931 delete this.srcNodeRef;
16932 }
a089699c 16933
1354d172
AD
16934 this._created = true;
16935 },
a089699c 16936
1354d172
AD
16937 _applyAttributes: function(){
16938 // summary:
16939 // Step during widget creation to copy widget attributes to the
16940 // DOM according to attributeMap and _setXXXAttr objects, and also to call
16941 // custom _setXXXAttr() methods.
16942 //
16943 // Skips over blank/false attribute values, unless they were explicitly specified
16944 // as parameters to the widget, since those are the default anyway,
16945 // and setting tabIndex="" is different than not setting tabIndex at all.
16946 //
16947 // For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
16948 // _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
16949 // tags:
16950 // private
a089699c 16951
1354d172
AD
16952 // Get list of attributes where this.set(name, value) will do something beyond
16953 // setting this[name] = value. Specifically, attributes that have:
16954 // - associated _setXXXAttr() method/hash/string/array
16955 // - entries in attributeMap.
16956 var ctor = this.constructor,
16957 list = ctor._setterAttrs;
16958 if(!list){
16959 list = (ctor._setterAttrs = []);
16960 for(var attr in this.attributeMap){
16961 list.push(attr);
16962 }
a089699c 16963
1354d172
AD
16964 var proto = ctor.prototype;
16965 for(var fxName in proto){
16966 if(fxName in this.attributeMap){ continue; }
16967 var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr";
16968 if(setterName in proto){
16969 list.push(fxName);
16970 }
16971 }
16972 }
a089699c 16973
1354d172
AD
16974 // Call this.set() for each attribute that was either specified as parameter to constructor,
16975 // or was found above and has a default non-null value. For correlated attributes like value and displayedValue, the one
16976 // specified as a parameter should take precedence, so apply attributes in this.params last.
16977 // Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
16978 // NaN and thus is not ignored like a default value of "".
16979 array.forEach(list, function(attr){
16980 if(this.params && attr in this.params){
16981 // skip this one, do it below
16982 }else if(this[attr]){
16983 this.set(attr, this[attr]);
16984 }
16985 }, this);
16986 for(var param in this.params){
16987 this.set(param, this[param]);
16988 }
16989 },
a089699c 16990
1354d172
AD
16991 postMixInProperties: function(){
16992 // summary:
16993 // Called after the parameters to the widget have been read-in,
16994 // but before the widget template is instantiated. Especially
16995 // useful to set properties that are referenced in the widget
16996 // template.
16997 // tags:
16998 // protected
16999 },
81bea17a 17000
1354d172 17001 buildRendering: function(){
a089699c 17002 // summary:
1354d172
AD
17003 // Construct the UI for this widget, setting this.domNode.
17004 // Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
17005 // tags:
17006 // protected
a089699c 17007
1354d172
AD
17008 if(!this.domNode){
17009 // Create root node if it wasn't created by _Templated
17010 this.domNode = this.srcNodeRef || domConstruct.create('div');
17011 }
a089699c 17012
1354d172
AD
17013 // baseClass is a single class name or occasionally a space-separated list of names.
17014 // Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
17015 // TODO: make baseClass custom setter
17016 if(this.baseClass){
17017 var classes = this.baseClass.split(" ");
17018 if(!this.isLeftToRight()){
17019 classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; }));
17020 }
17021 domClass.add(this.domNode, classes);
17022 }
17023 },
a089699c 17024
1354d172
AD
17025 postCreate: function(){
17026 // summary:
17027 // Processing after the DOM fragment is created
17028 // description:
17029 // Called after the DOM fragment has been created, but not necessarily
17030 // added to the document. Do not include any operations which rely on
17031 // node dimensions or placement.
17032 // tags:
17033 // protected
17034 },
a089699c 17035
1354d172
AD
17036 startup: function(){
17037 // summary:
17038 // Processing after the DOM fragment is added to the document
17039 // description:
17040 // Called after a widget and its children have been created and added to the page,
17041 // and all related widgets have finished their create() cycle, up through postCreate().
17042 // This is useful for composite widgets that need to control or layout sub-widgets.
17043 // Many layout widgets can use this as a wiring phase.
17044 if(this._started){ return; }
17045 this._started = true;
17046 array.forEach(this.getChildren(), function(obj){
17047 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
17048 obj.startup();
17049 obj._started = true;
17050 }
17051 });
17052 },
a089699c 17053
1354d172 17054 //////////// DESTROY FUNCTIONS ////////////////////////////////
a089699c 17055
1354d172
AD
17056 destroyRecursive: function(/*Boolean?*/ preserveDom){
17057 // summary:
17058 // Destroy this widget and its descendants
17059 // description:
17060 // This is the generic "destructor" function that all widget users
17061 // should call to cleanly discard with a widget. Once a widget is
17062 // destroyed, it is removed from the manager object.
17063 // preserveDom:
17064 // If true, this method will leave the original DOM structure
17065 // alone of descendant Widgets. Note: This will NOT work with
17066 // dijit._Templated widgets.
a089699c 17067
1354d172
AD
17068 this._beingDestroyed = true;
17069 this.destroyDescendants(preserveDom);
17070 this.destroy(preserveDom);
17071 },
a089699c 17072
1354d172
AD
17073 destroy: function(/*Boolean*/ preserveDom){
17074 // summary:
17075 // Destroy this widget, but not its descendants.
17076 // This method will, however, destroy internal widgets such as those used within a template.
17077 // preserveDom: Boolean
17078 // If true, this method will leave the original DOM structure alone.
17079 // Note: This will not yet work with _Templated widgets
a089699c 17080
1354d172
AD
17081 this._beingDestroyed = true;
17082 this.uninitialize();
a089699c 17083
1354d172
AD
17084 // remove this.connect() and this.subscribe() listeners
17085 var c;
17086 while((c = this._connects.pop())){
17087 c.remove();
17088 }
a089699c 17089
1354d172
AD
17090 // destroy widgets created as part of template, etc.
17091 var w;
17092 while((w = this._supportingWidgets.pop())){
17093 if(w.destroyRecursive){
17094 w.destroyRecursive();
17095 }else if(w.destroy){
17096 w.destroy();
17097 }
17098 }
a089699c 17099
1354d172
AD
17100 this.destroyRendering(preserveDom);
17101 registry.remove(this.id);
17102 this._destroyed = true;
17103 },
a089699c 17104
1354d172
AD
17105 destroyRendering: function(/*Boolean?*/ preserveDom){
17106 // summary:
17107 // Destroys the DOM nodes associated with this widget
17108 // preserveDom:
17109 // If true, this method will leave the original DOM structure alone
17110 // during tear-down. Note: this will not work with _Templated
17111 // widgets yet.
17112 // tags:
17113 // protected
a089699c 17114
1354d172
AD
17115 if(this.bgIframe){
17116 this.bgIframe.destroy(preserveDom);
17117 delete this.bgIframe;
17118 }
a089699c 17119
1354d172
AD
17120 if(this.domNode){
17121 if(preserveDom){
17122 domAttr.remove(this.domNode, "widgetId");
17123 }else{
17124 domConstruct.destroy(this.domNode);
a089699c 17125 }
1354d172
AD
17126 delete this.domNode;
17127 }
a089699c 17128
1354d172
AD
17129 if(this.srcNodeRef){
17130 if(!preserveDom){
17131 domConstruct.destroy(this.srcNodeRef);
17132 }
17133 delete this.srcNodeRef;
17134 }
17135 },
a089699c 17136
1354d172
AD
17137 destroyDescendants: function(/*Boolean?*/ preserveDom){
17138 // summary:
17139 // Recursively destroy the children of this widget and their
17140 // descendants.
17141 // preserveDom:
17142 // If true, the preserveDom attribute is passed to all descendant
17143 // widget's .destroy() method. Not for use with _Templated
17144 // widgets.
17145
17146 // get all direct descendants and destroy them recursively
17147 array.forEach(this.getChildren(), function(widget){
17148 if(widget.destroyRecursive){
17149 widget.destroyRecursive(preserveDom);
a089699c 17150 }
1354d172
AD
17151 });
17152 },
a089699c 17153
1354d172
AD
17154 uninitialize: function(){
17155 // summary:
17156 // Stub function. Override to implement custom widget tear-down
17157 // behavior.
17158 // tags:
17159 // protected
17160 return false;
17161 },
a089699c 17162
1354d172 17163 ////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
a089699c 17164
1354d172
AD
17165 _setStyleAttr: function(/*String||Object*/ value){
17166 // summary:
17167 // Sets the style attribute of the widget according to value,
17168 // which is either a hash like {height: "5px", width: "3px"}
17169 // or a plain string
17170 // description:
17171 // Determines which node to set the style on based on style setting
17172 // in attributeMap.
17173 // tags:
17174 // protected
a089699c 17175
1354d172 17176 var mapNode = this.domNode;
a089699c 17177
1354d172
AD
17178 // Note: technically we should revert any style setting made in a previous call
17179 // to his method, but that's difficult to keep track of.
a089699c 17180
1354d172
AD
17181 if(lang.isObject(value)){
17182 domStyle.set(mapNode, value);
17183 }else{
17184 if(mapNode.style.cssText){
17185 mapNode.style.cssText += "; " + value;
17186 }else{
17187 mapNode.style.cssText = value;
a089699c
AD
17188 }
17189 }
a089699c 17190
1354d172
AD
17191 this._set("style", value);
17192 },
a089699c 17193
1354d172
AD
17194 _attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
17195 // summary:
17196 // Reflect a widget attribute (title, tabIndex, duration etc.) to
17197 // the widget DOM, as specified by commands parameter.
17198 // If commands isn't specified then it's looked up from attributeMap.
17199 // Note some attributes like "type"
17200 // cannot be processed this way as they are not mutable.
17201 //
17202 // tags:
17203 // private
a089699c 17204
1354d172 17205 commands = arguments.length >= 3 ? commands : this.attributeMap[attr];
a089699c 17206
1354d172 17207 array.forEach(lang.isArray(commands) ? commands : [commands], function(command){
81bea17a 17208
1354d172
AD
17209 // Get target node and what we are doing to that node
17210 var mapNode = this[command.node || command || "domNode"]; // DOM node
17211 var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute
a089699c 17212
1354d172
AD
17213 switch(type){
17214 case "attribute":
17215 if(lang.isFunction(value)){ // functions execute in the context of the widget
17216 value = lang.hitch(this, value);
17217 }
a089699c 17218
1354d172
AD
17219 // Get the name of the DOM node attribute; usually it's the same
17220 // as the name of the attribute in the widget (attr), but can be overridden.
17221 // Also maps handler names to lowercase, like onSubmit --> onsubmit
17222 var attrName = command.attribute ? command.attribute :
17223 (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr);
a089699c 17224
1354d172
AD
17225 domAttr.set(mapNode, attrName, value);
17226 break;
17227 case "innerText":
17228 mapNode.innerHTML = "";
17229 mapNode.appendChild(win.doc.createTextNode(value));
17230 break;
17231 case "innerHTML":
17232 mapNode.innerHTML = value;
17233 break;
17234 case "class":
17235 domClass.replace(mapNode, value, this[attr]);
17236 break;
17237 }
17238 }, this);
17239 },
a089699c 17240
1354d172
AD
17241 get: function(name){
17242 // summary:
17243 // Get a property from a widget.
17244 // name:
17245 // The property to get.
17246 // description:
17247 // Get a named property from a widget. The property may
17248 // potentially be retrieved via a getter method. If no getter is defined, this
17249 // just retrieves the object's property.
17250 //
17251 // For example, if the widget has properties `foo` and `bar`
17252 // and a method named `_getFooAttr()`, calling:
17253 // `myWidget.get("foo")` would be equivalent to calling
17254 // `widget._getFooAttr()` and `myWidget.get("bar")`
17255 // would be equivalent to the expression
17256 // `widget.bar2`
17257 var names = this._getAttrNames(name);
17258 return this[names.g] ? this[names.g]() : this[name];
17259 },
a089699c 17260
1354d172
AD
17261 set: function(name, value){
17262 // summary:
17263 // Set a property on a widget
17264 // name:
17265 // The property to set.
17266 // value:
17267 // The value to set in the property.
17268 // description:
17269 // Sets named properties on a widget which may potentially be handled by a
17270 // setter in the widget.
17271 //
17272 // For example, if the widget has properties `foo` and `bar`
17273 // and a method named `_setFooAttr()`, calling
17274 // `myWidget.set("foo", "Howdy!")` would be equivalent to calling
17275 // `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
17276 // would be equivalent to the statement `widget.bar = 3;`
17277 //
17278 // set() may also be called with a hash of name/value pairs, ex:
17279 //
17280 // | myWidget.set({
17281 // | foo: "Howdy",
17282 // | bar: 3
17283 // | });
17284 //
17285 // This is equivalent to calling `set(foo, "Howdy")` and `set(bar, 3)`
a089699c 17286
1354d172
AD
17287 if(typeof name === "object"){
17288 for(var x in name){
17289 this.set(x, name[x]);
17290 }
17291 return this;
17292 }
17293 var names = this._getAttrNames(name),
17294 setter = this[names.s];
17295 if(lang.isFunction(setter)){
17296 // use the explicit setter
17297 var result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
17298 }else{
17299 // Mapping from widget attribute to DOMNode attribute/value/etc.
17300 // Map according to:
17301 // 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
17302 // 2. _setFooAttr: {...} type attribute in the widget (if one exists)
17303 // 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
17304 // Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
17305 // attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
17306 // Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
17307 var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode",
17308 tag = this[defaultNode].tagName,
17309 attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])),
17310 map = name in this.attributeMap ? this.attributeMap[name] :
17311 names.s in this ? this[names.s] :
17312 ((names.l in attrsForTag && typeof value != "function") ||
17313 /^aria-|^data-|^role$/.test(name)) ? defaultNode : null;
17314 if(map != null){
17315 this._attrToDom(name, value, map);
17316 }
17317 this._set(name, value);
17318 }
17319 return result || this;
17320 },
a089699c 17321
1354d172
AD
17322 _attrPairNames: {}, // shared between all widgets
17323 _getAttrNames: function(name){
17324 // summary:
17325 // Helper function for get() and set().
17326 // Caches attribute name values so we don't do the string ops every time.
17327 // tags:
17328 // private
a089699c 17329
1354d172
AD
17330 var apn = this._attrPairNames;
17331 if(apn[name]){ return apn[name]; }
17332 var uc = name.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); });
17333 return (apn[name] = {
17334 n: name+"Node",
17335 s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
17336 g: "_get"+uc+"Attr",
17337 l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
17338 });
17339 },
a089699c 17340
1354d172
AD
17341 _set: function(/*String*/ name, /*anything*/ value){
17342 // summary:
17343 // Helper function to set new value for specified attribute, and call handlers
17344 // registered with watch() if the value has changed.
17345 var oldValue = this[name];
17346 this[name] = value;
17347 if(this._watchCallbacks && this._created && value !== oldValue){
17348 this._watchCallbacks(name, oldValue, value);
17349 }
17350 },
a089699c 17351
1354d172
AD
17352 on: function(/*String*/ type, /*Function*/ func){
17353 // summary:
17354 // Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
17355 // description:
17356 // Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
17357 // Note that the function is not run in any particular scope, so if (for example) you want it to run in the
17358 // widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
a089699c 17359
1354d172
AD
17360 return aspect.after(this, this._onMap(type), func, true);
17361 },
a089699c 17362
1354d172
AD
17363 _onMap: function(/*String*/ type){
17364 // summary:
17365 // Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove")
17366 var ctor = this.constructor, map = ctor._onMap;
17367 if(!map){
17368 map = (ctor._onMap = {});
17369 for(var attr in ctor.prototype){
17370 if(/^on/.test(attr)){
17371 map[attr.replace(/^on/, "").toLowerCase()] = attr;
17372 }
17373 }
17374 }
17375 return map[type.toLowerCase()]; // String
17376 },
a089699c 17377
1354d172
AD
17378 toString: function(){
17379 // summary:
17380 // Returns a string that represents the widget
17381 // description:
17382 // When a widget is cast to a string, this method will be used to generate the
17383 // output. Currently, it does not implement any sort of reversible
17384 // serialization.
17385 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
17386 },
a089699c 17387
1354d172
AD
17388 getChildren: function(){
17389 // summary:
17390 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
17391 // Does not return nested widgets, nor widgets that are part of this widget's template.
17392 return this.containerNode ? registry.findWidgets(this.containerNode) : []; // dijit._Widget[]
17393 },
a089699c 17394
1354d172
AD
17395 getParent: function(){
17396 // summary:
17397 // Returns the parent widget of this widget
17398 return registry.getEnclosingWidget(this.domNode.parentNode);
17399 },
a089699c 17400
1354d172
AD
17401 connect: function(
17402 /*Object|null*/ obj,
17403 /*String|Function*/ event,
17404 /*String|Function*/ method){
17405 // summary:
17406 // Connects specified obj/event to specified method of this object
17407 // and registers for disconnect() on widget destroy.
17408 // description:
17409 // Provide widget-specific analog to dojo.connect, except with the
17410 // implicit use of this widget as the target object.
17411 // Events connected with `this.connect` are disconnected upon
17412 // destruction.
17413 // returns:
17414 // A handle that can be passed to `disconnect` in order to disconnect before
17415 // the widget is destroyed.
17416 // example:
17417 // | var btn = new dijit.form.Button();
17418 // | // when foo.bar() is called, call the listener we're going to
17419 // | // provide in the scope of btn
17420 // | btn.connect(foo, "bar", function(){
17421 // | console.debug(this.toString());
17422 // | });
17423 // tags:
17424 // protected
a089699c 17425
1354d172
AD
17426 var handle = connect.connect(obj, event, this, method);
17427 this._connects.push(handle);
17428 return handle; // _Widget.Handle
17429 },
17430
17431 disconnect: function(handle){
a089699c 17432 // summary:
1354d172
AD
17433 // Disconnects handle created by `connect`.
17434 // Also removes handle from this widget's list of connects.
17435 // tags:
17436 // protected
17437 var i = array.indexOf(this._connects, handle);
17438 if(i != -1){
17439 handle.remove();
17440 this._connects.splice(i, 1);
17441 }
17442 },
17443
17444 subscribe: function(t, method){
17445 // summary:
17446 // Subscribes to the specified topic and calls the specified method
17447 // of this object and registers for unsubscribe() on widget destroy.
17448 // description:
17449 // Provide widget-specific analog to dojo.subscribe, except with the
17450 // implicit use of this widget as the target object.
17451 // t: String
17452 // The topic
17453 // method: Function
17454 // The callback
17455 // example:
17456 // | var btn = new dijit.form.Button();
17457 // | // when /my/topic is published, this button changes its label to
17458 // | // be the parameter of the topic.
17459 // | btn.subscribe("/my/topic", function(v){
17460 // | this.set("label", v);
17461 // | });
17462 // tags:
17463 // protected
17464 var handle = topic.subscribe(t, lang.hitch(this, method));
17465 this._connects.push(handle);
17466 return handle; // _Widget.Handle
17467 },
17468
17469 unsubscribe: function(/*Object*/ handle){
17470 // summary:
17471 // Unsubscribes handle created by this.subscribe.
17472 // Also removes handle from this widget's list of subscriptions
17473 // tags:
17474 // protected
17475 this.disconnect(handle);
17476 },
17477
17478 isLeftToRight: function(){
17479 // summary:
17480 // Return this widget's explicit or implicit orientation (true for LTR, false for RTL)
17481 // tags:
17482 // protected
17483 return this.dir ? (this.dir == "ltr") : domGeometry.isBodyLtr(); //Boolean
17484 },
17485
17486 isFocusable: function(){
17487 // summary:
17488 // Return true if this widget can currently be focused
17489 // and false if not
17490 return this.focus && (domStyle.get(this.domNode, "display") != "none");
17491 },
17492
17493 placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){
17494 // summary:
17495 // Place this widget's domNode reference somewhere in the DOM based
17496 // on standard domConstruct.place conventions, or passing a Widget reference that
17497 // contains and addChild member.
a089699c 17498 //
1354d172
AD
17499 // description:
17500 // A convenience function provided in all _Widgets, providing a simple
17501 // shorthand mechanism to put an existing (or newly created) Widget
17502 // somewhere in the dom, and allow chaining.
a089699c 17503 //
1354d172
AD
17504 // reference:
17505 // The String id of a domNode, a domNode reference, or a reference to a Widget possessing
17506 // an addChild method.
a089699c 17507 //
1354d172
AD
17508 // position:
17509 // If passed a string or domNode reference, the position argument
17510 // accepts a string just as domConstruct.place does, one of: "first", "last",
17511 // "before", or "after".
a089699c 17512 //
1354d172
AD
17513 // If passed a _Widget reference, and that widget reference has an ".addChild" method,
17514 // it will be called passing this widget instance into that method, supplying the optional
17515 // position index passed.
a089699c
AD
17516 //
17517 // returns:
1354d172
AD
17518 // dijit._Widget
17519 // Provides a useful return of the newly created dijit._Widget instance so you
17520 // can "chain" this function by instantiating, placing, then saving the return value
17521 // to a variable.
17522 //
17523 // example:
17524 // | // create a Button with no srcNodeRef, and place it in the body:
17525 // | var button = new dijit.form.Button({ label:"click" }).placeAt(win.body());
17526 // | // now, 'button' is still the widget reference to the newly created button
17527 // | button.on("click", function(e){ console.log('click'); }));
17528 //
17529 // example:
17530 // | // create a button out of a node with id="src" and append it to id="wrapper":
17531 // | var button = new dijit.form.Button({},"src").placeAt("wrapper");
17532 //
17533 // example:
17534 // | // place a new button as the first element of some div
17535 // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first");
17536 //
17537 // example:
17538 // | // create a contentpane and add it to a TabContainer
17539 // | var tc = dijit.byId("myTabs");
17540 // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
a089699c 17541
1354d172
AD
17542 if(reference.declaredClass && reference.addChild){
17543 reference.addChild(this, position);
17544 }else{
17545 domConstruct.place(this.domNode, reference, position);
a089699c 17546 }
1354d172 17547 return this;
a089699c
AD
17548 },
17549
1354d172 17550 getTextDir: function(/*String*/ text,/*String*/ originalDir){
a089699c 17551 // summary:
1354d172
AD
17552 // Return direction of the text.
17553 // The function overridden in the _BidiSupport module,
17554 // its main purpose is to calculate the direction of the
17555 // text, if was defined by the programmer through textDir.
17556 // tags:
17557 // protected.
17558 return originalDir;
a089699c
AD
17559 },
17560
1354d172 17561 applyTextDir: function(/*===== element, text =====*/){
a089699c 17562 // summary:
1354d172
AD
17563 // The function overridden in the _BidiSupport module,
17564 // originally used for setting element.dir according to this.textDir.
17565 // In this case does nothing.
17566 // element: DOMNode
17567 // text: String
17568 // tags:
17569 // protected.
a089699c
AD
17570 },
17571
1354d172 17572 defer: function(fcn, delay){
a089699c 17573 // summary:
1354d172
AD
17574 // Wrapper to setTimeout to avoid deferred functions executing
17575 // after the originating widget has been destroyed.
17576 // Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
17577 // fcn: function reference
17578 // delay: Optional number (defaults to 0)
17579 // tags:
17580 // protected.
17581 var timer = setTimeout(lang.hitch(this,
17582 function(){
17583 timer = null;
17584 if(!this._destroyed){
17585 lang.hitch(this, fcn)();
17586 }
17587 }),
17588 delay || 0
17589 );
17590 return {
17591 remove: function(){
17592 if(timer){
17593 clearTimeout(timer);
17594 timer = null;
17595 }
17596 return null; // so this works well: handle = handle.remove();
17597 }
17598 };
17599 }
17600});
a089699c 17601
1354d172 17602});
a089699c 17603
1354d172
AD
17604},
17605'dijit/form/Form':function(){
17606define("dijit/form/Form", [
17607 "dojo/_base/declare", // declare
17608 "dojo/dom-attr", // domAttr.set
17609 "dojo/_base/event", // event.stop
17610 "dojo/_base/kernel", // kernel.deprecated
17611 "dojo/_base/sniff", // has("ie")
17612 "../_Widget",
17613 "../_TemplatedMixin",
17614 "./_FormMixin",
17615 "../layout/_ContentPaneResizeMixin"
17616], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
a089699c 17617
1354d172
AD
17618/*=====
17619 var _Widget = dijit._Widget;
17620 var _TemplatedMixin = dijit._TemplatedMixin;
17621 var _FormMixin = dijit.form._FormMixin;
17622 var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
17623=====*/
a089699c 17624
1354d172
AD
17625 // module:
17626 // dijit/form/Form
17627 // summary:
17628 // Widget corresponding to HTML form tag, for validation and serialization
a089699c 17629
81bea17a 17630
1354d172 17631 return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
a089699c 17632 // summary:
1354d172
AD
17633 // Widget corresponding to HTML form tag, for validation and serialization
17634 //
17635 // example:
17636 // | <form data-dojo-type="dijit.form.Form" id="myForm">
17637 // | Name: <input type="text" name="name" />
17638 // | </form>
17639 // | myObj = {name: "John Doe"};
17640 // | dijit.byId('myForm').set('value', myObj);
17641 // |
17642 // | myObj=dijit.byId('myForm').get('value');
a089699c 17643
1354d172 17644 // HTML <FORM> attributes
a089699c 17645
1354d172
AD
17646 // name: String?
17647 // Name of form for scripting.
17648 name: "",
a089699c 17649
1354d172
AD
17650 // action: String?
17651 // Server-side form handler.
17652 action: "",
a089699c 17653
1354d172
AD
17654 // method: String?
17655 // HTTP method used to submit the form, either "GET" or "POST".
17656 method: "",
a089699c 17657
1354d172
AD
17658 // encType: String?
17659 // Encoding type for the form, ex: application/x-www-form-urlencoded.
17660 encType: "",
a089699c 17661
1354d172
AD
17662 // accept-charset: String?
17663 // List of supported charsets.
17664 "accept-charset": "",
a089699c 17665
1354d172
AD
17666 // accept: String?
17667 // List of MIME types for file upload.
17668 accept: "",
a089699c 17669
1354d172
AD
17670 // target: String?
17671 // Target frame for the document to be opened in.
17672 target: "",
a089699c 17673
1354d172 17674 templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
a089699c 17675
1354d172
AD
17676 postMixInProperties: function(){
17677 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
17678 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8660
17679 this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
17680 this.inherited(arguments);
17681 },
a089699c 17682
1354d172
AD
17683 execute: function(/*Object*/ /*===== formContents =====*/){
17684 // summary:
17685 // Deprecated: use submit()
17686 // tags:
17687 // deprecated
17688 },
a089699c 17689
1354d172
AD
17690 onExecute: function(){
17691 // summary:
17692 // Deprecated: use onSubmit()
17693 // tags:
17694 // deprecated
17695 },
81bea17a 17696
1354d172
AD
17697 _setEncTypeAttr: function(/*String*/ value){
17698 this.encType = value;
17699 domAttr.set(this.domNode, "encType", value);
17700 if(has("ie")){ this.domNode.encoding = value; }
17701 },
a089699c 17702
1354d172
AD
17703 reset: function(/*Event?*/ e){
17704 // summary:
17705 // restores all widget values back to their init values,
17706 // calls onReset() which can cancel the reset by returning false
a089699c 17707
1354d172
AD
17708 // create fake event so we can know if preventDefault() is called
17709 var faux = {
17710 returnValue: true, // the IE way
17711 preventDefault: function(){ // not IE
17712 this.returnValue = false;
17713 },
17714 stopPropagation: function(){},
17715 currentTarget: e ? e.target : this.domNode,
17716 target: e ? e.target : this.domNode
17717 };
17718 // if return value is not exactly false, and haven't called preventDefault(), then reset
17719 if(!(this.onReset(faux) === false) && faux.returnValue){
17720 this.inherited(arguments, []);
17721 }
17722 },
a089699c 17723
1354d172
AD
17724 onReset: function(/*Event?*/ /*===== e =====*/){
17725 // summary:
17726 // Callback when user resets the form. This method is intended
17727 // to be over-ridden. When the `reset` method is called
17728 // programmatically, the return value from `onReset` is used
17729 // to compute whether or not resetting should proceed
17730 // tags:
17731 // callback
17732 return true; // Boolean
17733 },
a089699c 17734
1354d172
AD
17735 _onReset: function(e){
17736 this.reset(e);
17737 event.stop(e);
17738 return false;
17739 },
17740
17741 _onSubmit: function(e){
17742 var fp = this.constructor.prototype;
17743 // TODO: remove this if statement beginning with 2.0
17744 if(this.execute != fp.execute || this.onExecute != fp.onExecute){
17745 kernel.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
17746 this.onExecute();
17747 this.execute(this.getValues());
17748 }
17749 if(this.onSubmit(e) === false){ // only exactly false stops submit
17750 event.stop(e);
17751 }
17752 },
17753
17754 onSubmit: function(/*Event?*/ /*===== e =====*/){
17755 // summary:
17756 // Callback when user submits the form.
17757 // description:
17758 // This method is intended to be over-ridden, but by default it checks and
17759 // returns the validity of form elements. When the `submit`
17760 // method is called programmatically, the return value from
17761 // `onSubmit` is used to compute whether or not submission
17762 // should proceed
17763 // tags:
17764 // extension
17765
17766 return this.isValid(); // Boolean
17767 },
17768
17769 submit: function(){
17770 // summary:
17771 // programmatically submit form if and only if the `onSubmit` returns true
17772 if(!(this.onSubmit() === false)){
17773 this.containerNode.submit();
a089699c 17774 }
a089699c 17775 }
1354d172
AD
17776 });
17777});
a089699c 17778
1354d172
AD
17779},
17780'dijit/layout/_TabContainerBase':function(){
17781require({cache:{
17782'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"}});
17783define("dijit/layout/_TabContainerBase", [
17784 "dojo/text!./templates/TabContainer.html",
17785 "./StackContainer",
17786 "./utils", // marginBox2contextBox, layoutChildren
17787 "../_TemplatedMixin",
17788 "dojo/_base/declare", // declare
17789 "dojo/dom-class", // domClass.add
17790 "dojo/dom-geometry", // domGeometry.contentBox
17791 "dojo/dom-style" // domStyle.style
17792], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){
a089699c 17793
a089699c 17794
1354d172
AD
17795/*=====
17796 var StackContainer = dijit.layout.StackContainer;
17797 var _TemplatedMixin = dijit._TemplatedMixin;
17798=====*/
a089699c 17799
1354d172
AD
17800// module:
17801// dijit/layout/_TabContainerBase
17802// summary:
17803// Abstract base class for TabContainer. Must define _makeController() to instantiate
17804// and return the widget that displays the tab labels
a089699c 17805
a089699c 17806
1354d172
AD
17807return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
17808 // summary:
17809 // Abstract base class for TabContainer. Must define _makeController() to instantiate
17810 // and return the widget that displays the tab labels
17811 // description:
17812 // A TabContainer is a container that has multiple panes, but shows only
17813 // one pane at a time. There are a set of tabs corresponding to each pane,
17814 // where each tab has the name (aka title) of the pane, and optionally a close button.
a089699c 17815
1354d172
AD
17816 // tabPosition: String
17817 // Defines where tabs go relative to tab content.
17818 // "top", "bottom", "left-h", "right-h"
17819 tabPosition: "top",
a089699c 17820
1354d172 17821 baseClass: "dijitTabContainer",
a089699c 17822
1354d172
AD
17823 // tabStrip: [const] Boolean
17824 // Defines whether the tablist gets an extra class for layouting, putting a border/shading
17825 // around the set of tabs. Not supported by claro theme.
17826 tabStrip: false,
a089699c 17827
1354d172
AD
17828 // nested: [const] Boolean
17829 // If true, use styling for a TabContainer nested inside another TabContainer.
17830 // For tundra etc., makes tabs look like links, and hides the outer
17831 // border since the outer TabContainer already has a border.
17832 nested: false,
a089699c 17833
1354d172 17834 templateString: template,
a089699c 17835
1354d172
AD
17836 postMixInProperties: function(){
17837 // set class name according to tab position, ex: dijitTabContainerTop
17838 this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
81bea17a 17839
1354d172 17840 this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden");
a089699c 17841
1354d172
AD
17842 this.inherited(arguments);
17843 },
a089699c 17844
1354d172
AD
17845 buildRendering: function(){
17846 this.inherited(arguments);
a089699c 17847
1354d172
AD
17848 // Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
17849 this.tablist = this._makeController(this.tablistNode);
a089699c 17850
1354d172 17851 if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); }
a089699c 17852
1354d172
AD
17853 if(this.nested){
17854 /* workaround IE's lack of support for "a > b" selectors by
17855 * tagging each node in the template.
17856 */
17857 domClass.add(this.domNode, "dijitTabContainerNested");
17858 domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested");
17859 domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested");
17860 domClass.add(this.containerNode, "dijitTabPaneWrapperNested");
17861 }else{
17862 domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
17863 }
17864 },
a089699c 17865
1354d172
AD
17866 _setupChild: function(/*dijit._Widget*/ tab){
17867 // Overrides StackContainer._setupChild().
17868 domClass.add(tab.domNode, "dijitTabPane");
17869 this.inherited(arguments);
17870 },
a089699c 17871
1354d172
AD
17872 startup: function(){
17873 if(this._started){ return; }
a089699c 17874
1354d172
AD
17875 // wire up the tablist and its tabs
17876 this.tablist.startup();
a089699c 17877
1354d172
AD
17878 this.inherited(arguments);
17879 },
a089699c 17880
1354d172
AD
17881 layout: function(){
17882 // Overrides StackContainer.layout().
17883 // Configure the content pane to take up all the space except for where the tabs are
81bea17a 17884
1354d172 17885 if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
a089699c 17886
1354d172 17887 var sc = this.selectedChildWidget;
a089699c 17888
1354d172
AD
17889 if(this.doLayout){
17890 // position and size the titles and the container node
17891 var titleAlign = this.tabPosition.replace(/-h/, "");
17892 this.tablist.layoutAlign = titleAlign;
17893 var children = [this.tablist, {
17894 domNode: this.tablistSpacer,
17895 layoutAlign: titleAlign
17896 }, {
17897 domNode: this.containerNode,
17898 layoutAlign: "client"
17899 }];
17900 layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
a089699c 17901
1354d172
AD
17902 // Compute size to make each of my children.
17903 // children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
17904 this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
a089699c 17905
1354d172
AD
17906 if(sc && sc.resize){
17907 sc.resize(this._containerContentBox);
17908 }
17909 }else{
17910 // just layout the tab controller, so it can position left/right buttons etc.
17911 if(this.tablist.resize){
17912 //make the tabs zero width so that they don't interfere with width calc, then reset
17913 var s = this.tablist.domNode.style;
17914 s.width="0";
17915 var width = domGeometry.getContentBox(this.domNode).w;
17916 s.width="";
17917 this.tablist.resize({w: width});
a089699c 17918 }
a089699c 17919
1354d172
AD
17920 // and call resize() on the selected pane just to tell it that it's been made visible
17921 if(sc && sc.resize){
17922 sc.resize();
a089699c 17923 }
1354d172
AD
17924 }
17925 },
a089699c 17926
1354d172
AD
17927 destroy: function(){
17928 if(this.tablist){
17929 this.tablist.destroy();
17930 }
17931 this.inherited(arguments);
17932 }
17933});
a089699c 17934
1354d172 17935});
a089699c 17936
1354d172
AD
17937},
17938'dojo/store/Memory':function(){
17939define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine"], function(declare, QueryResults, SimpleQueryEngine) {
17940 // module:
17941 // dojo/store/Memory
17942 // summary:
17943 // The module defines an in-memory object store.
81bea17a 17944
1354d172
AD
17945
17946return declare("dojo.store.Memory", null, {
17947 // summary:
17948 // This is a basic in-memory object store. It implements dojo.store.api.Store.
17949 constructor: function(/*dojo.store.Memory*/ options){
17950 // summary:
17951 // Creates a memory object store.
17952 // options:
17953 // This provides any configuration information that will be mixed into the store.
17954 // This should generally include the data property to provide the starting set of data.
17955 for(var i in options){
17956 this[i] = options[i];
17957 }
17958 this.setData(this.data || []);
17959 },
17960 // data: Array
17961 // The array of all the objects in the memory store
17962 data:null,
17963
17964 // idProperty: String
17965 // Indicates the property to use as the identity property. The values of this
17966 // property should be unique.
17967 idProperty: "id",
17968
17969 // index: Object
17970 // An index of data indices into the data array by id
17971 index:null,
17972
17973 // queryEngine: Function
17974 // Defines the query engine to use for querying the data store
17975 queryEngine: SimpleQueryEngine,
17976 get: function(id){
17977 // summary:
17978 // Retrieves an object by its identity
17979 // id: Number
17980 // The identity to use to lookup the object
17981 // returns: Object
17982 // The object in the store that matches the given id.
17983 return this.data[this.index[id]];
17984 },
17985 getIdentity: function(object){
17986 // summary:
17987 // Returns an object's identity
17988 // object: Object
17989 // The object to get the identity from
17990 // returns: Number
17991 return object[this.idProperty];
17992 },
17993 put: function(object, options){
17994 // summary:
17995 // Stores an object
17996 // object: Object
17997 // The object to store.
17998 // options: dojo.store.api.Store.PutDirectives??
17999 // Additional metadata for storing the data. Includes an "id"
18000 // property if a specific id is to be used.
18001 // returns: Number
18002 var data = this.data,
18003 index = this.index,
18004 idProperty = this.idProperty;
18005 var id = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
18006 if(id in index){
18007 // object exists
18008 if(options && options.overwrite === false){
18009 throw new Error("Object already exists");
18010 }
18011 // replace the entry in data
18012 data[index[id]] = object;
18013 }else{
18014 // add the new object
18015 index[id] = data.push(object) - 1;
18016 }
18017 return id;
18018 },
18019 add: function(object, options){
18020 // summary:
18021 // Creates an object, throws an error if the object already exists
18022 // object: Object
18023 // The object to store.
18024 // options: dojo.store.api.Store.PutDirectives??
18025 // Additional metadata for storing the data. Includes an "id"
18026 // property if a specific id is to be used.
18027 // returns: Number
18028 (options = options || {}).overwrite = false;
18029 // call put with overwrite being false
18030 return this.put(object, options);
18031 },
18032 remove: function(id){
18033 // summary:
18034 // Deletes an object by its identity
18035 // id: Number
18036 // The identity to use to delete the object
18037 // returns: Boolean
18038 // Returns true if an object was removed, falsy (undefined) if no object matched the id
18039 var index = this.index;
18040 var data = this.data;
18041 if(id in index){
18042 data.splice(index[id], 1);
18043 // now we have to reindex
18044 this.setData(data);
18045 return true;
18046 }
18047 },
18048 query: function(query, options){
18049 // summary:
18050 // Queries the store for objects.
18051 // query: Object
18052 // The query to use for retrieving objects from the store.
18053 // options: dojo.store.api.Store.QueryOptions?
18054 // The optional arguments to apply to the resultset.
18055 // returns: dojo.store.api.Store.QueryResults
18056 // The results of the query, extended with iterative methods.
18057 //
18058 // example:
18059 // Given the following store:
18060 //
18061 // | var store = new dojo.store.Memory({
18062 // | data: [
18063 // | {id: 1, name: "one", prime: false },
18064 // | {id: 2, name: "two", even: true, prime: true},
18065 // | {id: 3, name: "three", prime: true},
18066 // | {id: 4, name: "four", even: true, prime: false},
18067 // | {id: 5, name: "five", prime: true}
18068 // | ]
18069 // | });
18070 //
18071 // ...find all items where "prime" is true:
18072 //
18073 // | var results = store.query({ prime: true });
18074 //
18075 // ...or find all items where "even" is true:
18076 //
18077 // | var results = store.query({ even: true });
18078 return QueryResults(this.queryEngine(query, options)(this.data));
18079 },
18080 setData: function(data){
18081 // summary:
18082 // Sets the given data as the source for this store, and indexes it
18083 // data: Object[]
18084 // An array of objects to use as the source of data.
18085 if(data.items){
18086 // just for convenience with the data format IFRS expects
18087 this.idProperty = data.identifier;
18088 data = this.data = data.items;
18089 }else{
18090 this.data = data;
18091 }
18092 this.index = {};
18093 for(var i = 0, l = data.length; i < l; i++){
18094 this.index[data[i][this.idProperty]] = i;
a089699c
AD
18095 }
18096 }
1354d172 18097});
a089699c 18098
1354d172 18099});
a089699c 18100
1354d172
AD
18101},
18102'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",
18103'dijit/_base/sniff':function(){
18104define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
18105 // module:
18106 // dijit/_base/sniff
18107 // summary:
18108 // Back compatibility module, new code should require dojo/uacss directly instead of this module.
18109});
a089699c 18110
1354d172
AD
18111},
18112'dijit/Toolbar':function(){
18113define("dijit/Toolbar", [
18114 "require",
18115 "dojo/_base/declare", // declare
18116 "dojo/_base/kernel",
18117 "dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
18118 "dojo/ready",
18119 "./_Widget",
18120 "./_KeyNavContainer",
18121 "./_TemplatedMixin"
18122], function(require, declare, kernel, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
a089699c 18123
1354d172
AD
18124/*=====
18125 var _Widget = dijit._Widget;
18126 var _KeyNavContainer = dijit._KeyNavContainer;
18127 var _TemplatedMixin = dijit._TemplatedMixin;
18128=====*/
a089699c 18129
1354d172
AD
18130 // module:
18131 // dijit/Toolbar
18132 // summary:
18133 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
a089699c
AD
18134
18135
1354d172
AD
18136 // Back compat w/1.6, remove for 2.0
18137 if(!kernel.isAsync){
18138 ready(0, function(){
18139 var requires = ["dijit/ToolbarSeparator"];
18140 require(requires); // use indirection so modules not rolled into a build
18141 });
18142 }
a089699c 18143
1354d172 18144 return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
a089699c 18145 // summary:
1354d172 18146 // A Toolbar widget, used to hold things like `dijit.Editor` buttons
a089699c 18147
1354d172
AD
18148 templateString:
18149 '<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
18150 '</div>',
a089699c 18151
1354d172 18152 baseClass: "dijitToolbar",
a089699c 18153
1354d172
AD
18154 postCreate: function(){
18155 this.inherited(arguments);
a089699c 18156
1354d172
AD
18157 this.connectKeyNavHandlers(
18158 this.isLeftToRight() ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
18159 this.isLeftToRight() ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
18160 );
18161 }
18162 });
18163});
a089699c 18164
1354d172
AD
18165},
18166'dijit/layout/StackContainer':function(){
18167define("dijit/layout/StackContainer", [
18168 "dojo/_base/array", // array.forEach array.indexOf array.some
18169 "dojo/cookie", // cookie
18170 "dojo/_base/declare", // declare
18171 "dojo/dom-class", // domClass.add domClass.replace
18172 "dojo/_base/kernel", // kernel.isAsync
18173 "dojo/_base/lang", // lang.extend
18174 "dojo/ready",
18175 "dojo/topic", // publish
18176 "../registry", // registry.byId
18177 "../_WidgetBase",
18178 "./_LayoutWidget",
18179 "dojo/i18n!../nls/common"
18180], function(array, cookie, declare, domClass, kernel, lang, ready, topic,
18181 registry, _WidgetBase, _LayoutWidget){
a089699c 18182
1354d172
AD
18183/*=====
18184var _WidgetBase = dijit._WidgetBase;
18185var _LayoutWidget = dijit.layout._LayoutWidget;
18186var StackController = dijit.layout.StackController;
18187=====*/
a089699c 18188
1354d172
AD
18189// module:
18190// dijit/layout/StackContainer
18191// summary:
18192// A container that has multiple children, but shows only one child at a time.
a089699c 18193
1354d172
AD
18194// Back compat w/1.6, remove for 2.0
18195if(!kernel.isAsync){
18196 ready(0, function(){
18197 var requires = ["dijit/layout/StackController"];
18198 require(requires); // use indirection so modules not rolled into a build
18199 });
18200}
a089699c 18201
1354d172
AD
18202// These arguments can be specified for the children of a StackContainer.
18203// Since any widget can be specified as a StackContainer child, mix them
18204// into the base widget class. (This is a hack, but it's effective.)
18205lang.extend(_WidgetBase, {
18206 // selected: Boolean
18207 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
18208 // Specifies that this widget should be the initially displayed pane.
18209 // Note: to change the selected child use `dijit.layout.StackContainer.selectChild`
18210 selected: false,
a089699c 18211
1354d172
AD
18212 // closable: Boolean
18213 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
18214 // True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
18215 closable: false,
a089699c 18216
1354d172
AD
18217 // iconClass: String
18218 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
18219 // CSS Class specifying icon to use in label associated with this pane.
18220 iconClass: "dijitNoIcon",
a089699c 18221
1354d172
AD
18222 // showTitle: Boolean
18223 // Parameter for children of `dijit.layout.StackContainer` or subclasses.
18224 // When true, display title of this widget as tab label etc., rather than just using
18225 // icon specified in iconClass
18226 showTitle: true
18227});
a089699c 18228
1354d172
AD
18229return declare("dijit.layout.StackContainer", _LayoutWidget, {
18230 // summary:
18231 // A container that has multiple children, but shows only
18232 // one child at a time
18233 //
18234 // description:
18235 // A container for widgets (ContentPanes, for example) That displays
18236 // only one Widget at a time.
18237 //
18238 // Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
18239 //
18240 // Can be base class for container, Wizard, Show, etc.
a089699c 18241
1354d172
AD
18242 // doLayout: Boolean
18243 // If true, change the size of my currently displayed child to match my size
18244 doLayout: true,
a089699c 18245
1354d172
AD
18246 // persist: Boolean
18247 // Remembers the selected child across sessions
18248 persist: false,
a089699c 18249
1354d172 18250 baseClass: "dijitStackContainer",
a089699c 18251
1354d172
AD
18252/*=====
18253 // selectedChildWidget: [readonly] dijit._Widget
18254 // References the currently selected child widget, if any.
18255 // Adjust selected child with selectChild() method.
18256 selectedChildWidget: null,
18257=====*/
a089699c 18258
1354d172
AD
18259 buildRendering: function(){
18260 this.inherited(arguments);
18261 domClass.add(this.domNode, "dijitLayoutContainer");
18262 this.containerNode.setAttribute("role", "tabpanel");
18263 },
a089699c 18264
1354d172
AD
18265 postCreate: function(){
18266 this.inherited(arguments);
18267 this.connect(this.domNode, "onkeypress", this._onKeyPress);
18268 },
a089699c 18269
1354d172
AD
18270 startup: function(){
18271 if(this._started){ return; }
a089699c 18272
1354d172 18273 var children = this.getChildren();
81bea17a 18274
1354d172
AD
18275 // Setup each page panel to be initially hidden
18276 array.forEach(children, this._setupChild, this);
a089699c 18277
1354d172
AD
18278 // Figure out which child to initially display, defaulting to first one
18279 if(this.persist){
18280 this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
18281 }else{
18282 array.some(children, function(child){
18283 if(child.selected){
18284 this.selectedChildWidget = child;
18285 }
18286 return child.selected;
18287 }, this);
18288 }
18289 var selected = this.selectedChildWidget;
18290 if(!selected && children[0]){
18291 selected = this.selectedChildWidget = children[0];
18292 selected.selected = true;
a089699c 18293 }
a089699c 18294
1354d172
AD
18295 // Publish information about myself so any StackControllers can initialize.
18296 // This needs to happen before this.inherited(arguments) so that for
18297 // TabContainer, this._contentBox doesn't include the space for the tab labels.
18298 topic.publish(this.id+"-startup", {children: children, selected: selected});
a089699c 18299
1354d172
AD
18300 // Startup each child widget, and do initial layout like setting this._contentBox,
18301 // then calls this.resize() which does the initial sizing on the selected child.
18302 this.inherited(arguments);
18303 },
a089699c 18304
1354d172
AD
18305 resize: function(){
18306 // Resize is called when we are first made visible (it's called from startup()
18307 // if we are initially visible). If this is the first time we've been made
18308 // visible then show our first child.
18309 if(!this._hasBeenShown){
18310 this._hasBeenShown = true;
18311 var selected = this.selectedChildWidget;
18312 if(selected){
18313 this._showChild(selected);
a089699c 18314 }
a089699c 18315 }
1354d172
AD
18316 this.inherited(arguments);
18317 },
a089699c 18318
1354d172
AD
18319 _setupChild: function(/*dijit._Widget*/ child){
18320 // Overrides _LayoutWidget._setupChild()
a089699c 18321
1354d172 18322 this.inherited(arguments);
a089699c 18323
1354d172 18324 domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
a089699c 18325
1354d172
AD
18326 // remove the title attribute so it doesn't show up when i hover
18327 // over a node
18328 child.domNode.title = "";
18329 },
a089699c 18330
1354d172
AD
18331 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
18332 // Overrides _Container.addChild() to do layout and publish events
a089699c 18333
1354d172 18334 this.inherited(arguments);
a089699c 18335
1354d172
AD
18336 if(this._started){
18337 topic.publish(this.id+"-addChild", child, insertIndex); // publish
a089699c 18338
1354d172
AD
18339 // in case the tab titles have overflowed from one line to two lines
18340 // (or, if this if first child, from zero lines to one line)
18341 // TODO: w/ScrollingTabController this is no longer necessary, although
18342 // ScrollTabController.resize() does need to get called to show/hide
18343 // the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
18344 // If this is updated to not layout [except for initial child added / last child removed], update
18345 // "childless startup" test in StackContainer.html to check for no resize event after second addChild()
18346 this.layout();
a089699c 18347
1354d172
AD
18348 // if this is the first child, then select it
18349 if(!this.selectedChildWidget){
18350 this.selectChild(child);
a089699c 18351 }
a089699c 18352 }
1354d172 18353 },
a089699c 18354
1354d172
AD
18355 removeChild: function(/*dijit._Widget*/ page){
18356 // Overrides _Container.removeChild() to do layout and publish events
a089699c 18357
1354d172 18358 this.inherited(arguments);
a089699c 18359
1354d172
AD
18360 if(this._started){
18361 // this will notify any tablists to remove a button; do this first because it may affect sizing
18362 topic.publish(this.id + "-removeChild", page); // publish
18363 }
a089699c 18364
1354d172
AD
18365 // If all our children are being destroyed than don't run the code below (to select another page),
18366 // because we are deleting every page one by one
18367 if(this._descendantsBeingDestroyed){ return; }
a089699c 18368
1354d172
AD
18369 // Select new page to display, also updating TabController to show the respective tab.
18370 // Do this before layout call because it can affect the height of the TabController.
18371 if(this.selectedChildWidget === page){
18372 this.selectedChildWidget = undefined;
18373 if(this._started){
18374 var children = this.getChildren();
18375 if(children.length){
18376 this.selectChild(children[0]);
18377 }
18378 }
18379 }
a089699c 18380
1354d172
AD
18381 if(this._started){
18382 // In case the tab titles now take up one line instead of two lines
18383 // (note though that ScrollingTabController never overflows to multiple lines),
18384 // or the height has changed slightly because of addition/removal of tab which close icon
18385 this.layout();
18386 }
18387 },
a089699c 18388
1354d172 18389 selectChild: function(/*dijit._Widget|String*/ page, /*Boolean*/ animate){
a089699c 18390 // summary:
1354d172
AD
18391 // Show the given widget (which must be one of my children)
18392 // page:
18393 // Reference to child widget or id of child widget
a089699c 18394
1354d172 18395 page = registry.byId(page);
a089699c 18396
1354d172
AD
18397 if(this.selectedChildWidget != page){
18398 // Deselect old page and select new one
18399 var d = this._transition(page, this.selectedChildWidget, animate);
18400 this._set("selectedChildWidget", page);
18401 topic.publish(this.id+"-selectChild", page); // publish
a089699c 18402
1354d172
AD
18403 if(this.persist){
18404 cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
18405 }
a089699c 18406 }
81bea17a 18407
1354d172 18408 return d; // If child has an href, promise that fires when the child's href finishes loading
a089699c
AD
18409 },
18410
1354d172 18411 _transition: function(newWidget, oldWidget /*===== , animate =====*/){
a089699c 18412 // summary:
1354d172
AD
18413 // Hide the old widget and display the new widget.
18414 // Subclasses should override this.
18415 // newWidget: dijit._Widget
18416 // The newly selected widget.
18417 // oldWidget: dijit._Widget
18418 // The previously selected widget.
18419 // animate: Boolean
18420 // Used by AccordionContainer to turn on/off slide effect.
a089699c 18421 // tags:
1354d172
AD
18422 // protected extension
18423 if(oldWidget){
18424 this._hideChild(oldWidget);
18425 }
18426 var d = this._showChild(newWidget);
18427
18428 // Size the new widget, in case this is the first time it's being shown,
18429 // or I have been resized since the last time it was shown.
18430 // Note that page must be visible for resizing to work.
18431 if(newWidget.resize){
18432 if(this.doLayout){
18433 newWidget.resize(this._containerContentBox || this._contentBox);
18434 }else{
18435 // the child should pick it's own size but we still need to call resize()
18436 // (with no arguments) to let the widget lay itself out
18437 newWidget.resize();
18438 }
18439 }
18440
18441 return d; // If child has an href, promise that fires when the child's href finishes loading
a089699c
AD
18442 },
18443
1354d172 18444 _adjacent: function(/*Boolean*/ forward){
a089699c 18445 // summary:
1354d172
AD
18446 // Gets the next/previous child widget in this container from the current selection.
18447 var children = this.getChildren();
18448 var index = array.indexOf(children, this.selectedChildWidget);
18449 index += forward ? 1 : children.length - 1;
18450 return children[ index % children.length ]; // dijit._Widget
a089699c
AD
18451 },
18452
1354d172 18453 forward: function(){
a089699c 18454 // summary:
1354d172
AD
18455 // Advance to next page.
18456 return this.selectChild(this._adjacent(true), true);
a089699c
AD
18457 },
18458
1354d172 18459 back: function(){
a089699c 18460 // summary:
1354d172
AD
18461 // Go back to previous page.
18462 return this.selectChild(this._adjacent(false), true);
a089699c
AD
18463 },
18464
1354d172
AD
18465 _onKeyPress: function(e){
18466 topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
18467 },
a089699c 18468
1354d172
AD
18469 layout: function(){
18470 // Implement _LayoutWidget.layout() virtual method.
18471 var child = this.selectedChildWidget;
18472 if(child && child.resize){
18473 if(this.doLayout){
18474 child.resize(this._containerContentBox || this._contentBox);
18475 }else{
18476 child.resize();
a089699c
AD
18477 }
18478 }
a089699c
AD
18479 },
18480
1354d172 18481 _showChild: function(/*dijit._Widget*/ page){
a089699c 18482 // summary:
1354d172
AD
18483 // Show the specified child by changing it's CSS, and call _onShow()/onShow() so
18484 // it can do any updates it needs regarding loading href's etc.
18485 // returns:
18486 // Promise that fires when page has finished showing, or true if there's no href
18487 var children = this.getChildren();
18488 page.isFirstChild = (page == children[0]);
18489 page.isLastChild = (page == children[children.length-1]);
18490 page._set("selected", true);
a089699c 18491
1354d172 18492 domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
a089699c 18493
1354d172 18494 return (page._onShow && page._onShow()) || true;
a089699c
AD
18495 },
18496
1354d172 18497 _hideChild: function(/*dijit._Widget*/ page){
a089699c 18498 // summary:
1354d172
AD
18499 // Hide the specified child by changing it's CSS, and call _onHide() so
18500 // it's notified.
18501 page._set("selected", false);
18502 domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
a089699c 18503
1354d172 18504 page.onHide && page.onHide();
a089699c
AD
18505 },
18506
1354d172 18507 closeChild: function(/*dijit._Widget*/ page){
a089699c 18508 // summary:
1354d172
AD
18509 // Callback when user clicks the [X] to remove a page.
18510 // If onClose() returns true then remove and destroy the child.
a089699c
AD
18511 // tags:
18512 // private
1354d172
AD
18513 var remove = page.onClose(this, page);
18514 if(remove){
18515 this.removeChild(page);
18516 // makes sure we can clean up executeScripts in ContentPane onUnLoad
18517 page.destroyRecursive();
a089699c
AD
18518 }
18519 },
18520
1354d172
AD
18521 destroyDescendants: function(/*Boolean*/ preserveDom){
18522 this._descendantsBeingDestroyed = true;
18523 this.selectedChildWidget = undefined;
18524 array.forEach(this.getChildren(), function(child){
18525 if(!preserveDom){
18526 this.removeChild(child);
18527 }
18528 child.destroyRecursive(preserveDom);
18529 }, this);
18530 this._descendantsBeingDestroyed = false;
18531 }
18532});
a089699c 18533
1354d172 18534});
a089699c 18535
1354d172
AD
18536},
18537'dojo/regexp':function(){
18538define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang) {
18539 // module:
18540 // dojo/regexp
18541 // summary:
18542 // TODOC
a089699c 18543
1354d172 18544lang.getObject("regexp", true, dojo);
a089699c 18545
1354d172
AD
18546/*=====
18547dojo.regexp = {
18548 // summary: Regular expressions and Builder resources
18549};
18550=====*/
a089699c 18551
1354d172
AD
18552dojo.regexp.escapeString = function(/*String*/str, /*String?*/except){
18553 // summary:
18554 // Adds escape sequences for special characters in regular expressions
18555 // except:
18556 // a String with special characters to be left unescaped
a089699c 18557
1354d172
AD
18558 return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
18559 if(except && except.indexOf(ch) != -1){
18560 return ch;
a089699c 18561 }
1354d172
AD
18562 return "\\" + ch;
18563 }); // String
18564};
a089699c 18565
1354d172
AD
18566dojo.regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
18567 // summary:
18568 // Builds a regular expression that groups subexpressions
18569 // description:
18570 // A utility function used by some of the RE generators. The
18571 // subexpressions are constructed by the function, re, in the second
18572 // parameter. re builds one subexpression for each elem in the array
18573 // a, in the first parameter. Returns a string for a regular
18574 // expression that groups all the subexpressions.
18575 // arr:
18576 // A single value or an array of values.
18577 // re:
18578 // A function. Takes one parameter and converts it to a regular
18579 // expression.
18580 // nonCapture:
18581 // If true, uses non-capturing match, otherwise matches are retained
18582 // by regular expression. Defaults to false
a089699c 18583
1354d172
AD
18584 // case 1: a is a single value.
18585 if(!(arr instanceof Array)){
18586 return re(arr); // String
18587 }
a089699c 18588
1354d172
AD
18589 // case 2: a is an array
18590 var b = [];
18591 for(var i = 0; i < arr.length; i++){
18592 // convert each elem to a RE
18593 b.push(re(arr[i]));
18594 }
a089699c 18595
1354d172
AD
18596 // join the REs as alternatives in a RE group.
18597 return dojo.regexp.group(b.join("|"), nonCapture); // String
18598};
a089699c 18599
1354d172
AD
18600dojo.regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
18601 // summary:
18602 // adds group match to expression
18603 // nonCapture:
18604 // If true, uses non-capturing match, otherwise matches are retained
18605 // by regular expression.
18606 return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
18607};
a089699c 18608
1354d172
AD
18609return dojo.regexp;
18610});
a089699c 18611
1354d172
AD
18612},
18613'dijit/form/_FormMixin':function(){
18614define("dijit/form/_FormMixin", [
18615 "dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
18616 "dojo/_base/declare", // declare
18617 "dojo/_base/kernel", // kernel.deprecated
18618 "dojo/_base/lang", // lang.hitch lang.isArray
18619 "dojo/window" // winUtils.scrollIntoView
18620], function(array, declare, kernel, lang, winUtils){
18621
18622 // module:
18623 // dijit/form/_FormMixin
18624 // summary:
18625 // Mixin for containers of form widgets (i.e. widgets that represent a single value
18626 // and can be children of a <form> node or dijit.form.Form widget)
a089699c 18627
1354d172 18628 return declare("dijit.form._FormMixin", null, {
a089699c 18629 // summary:
1354d172
AD
18630 // Mixin for containers of form widgets (i.e. widgets that represent a single value
18631 // and can be children of a <form> node or dijit.form.Form widget)
18632 // description:
18633 // Can extract all the form widgets
18634 // values and combine them into a single javascript object, or alternately
18635 // take such an object and set the values for all the contained
18636 // form widgets
81bea17a 18637
1354d172
AD
18638 /*=====
18639 // value: Object
18640 // Name/value hash for each child widget with a name and value.
18641 // Child widgets without names are not part of the hash.
18642 //
18643 // If there are multiple child widgets w/the same name, value is an array,
18644 // unless they are radio buttons in which case value is a scalar (since only
18645 // one radio button can be checked at a time).
18646 //
18647 // If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
18648 //
18649 // Example:
18650 // | { name: "John Smith", interests: ["sports", "movies"] }
18651 =====*/
81bea17a 18652
1354d172
AD
18653 // state: [readonly] String
18654 // Will be "Error" if one or more of the child widgets has an invalid value,
18655 // "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
18656 // which indicates that the form is ready to be submitted.
18657 state: "",
81bea17a 18658
1354d172
AD
18659 // TODO:
18660 // * Repeater
18661 // * better handling for arrays. Often form elements have names with [] like
18662 // * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
18663 //
18664 //
a089699c 18665
1354d172
AD
18666 _getDescendantFormWidgets: function(/*dijit._WidgetBase[]?*/ children){
18667 // summary:
18668 // Returns all form widget descendants, searching through non-form child widgets like BorderContainer
18669 var res = [];
18670 array.forEach(children || this.getChildren(), function(child){
18671 if("value" in child){
18672 res.push(child);
18673 }else{
18674 res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
18675 }
18676 }, this);
18677 return res;
18678 },
a089699c 18679
1354d172
AD
18680 reset: function(){
18681 array.forEach(this._getDescendantFormWidgets(), function(widget){
18682 if(widget.reset){
18683 widget.reset();
18684 }
18685 });
18686 },
a089699c 18687
1354d172
AD
18688 validate: function(){
18689 // summary:
18690 // returns if the form is valid - same as isValid - but
18691 // provides a few additional (ui-specific) features.
18692 // 1 - it will highlight any sub-widgets that are not
18693 // valid
18694 // 2 - it will call focus() on the first invalid
18695 // sub-widget
18696 var didFocus = false;
18697 return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
18698 // Need to set this so that "required" widgets get their
18699 // state set.
18700 widget._hasBeenBlurred = true;
18701 var valid = widget.disabled || !widget.validate || widget.validate();
18702 if(!valid && !didFocus){
18703 // Set focus of the first non-valid widget
18704 winUtils.scrollIntoView(widget.containerNode || widget.domNode);
18705 widget.focus();
18706 didFocus = true;
18707 }
18708 return valid;
18709 }), function(item){ return item; });
18710 },
a089699c 18711
1354d172
AD
18712 setValues: function(val){
18713 kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
18714 return this.set('value', val);
18715 },
18716 _setValueAttr: function(/*Object*/ obj){
18717 // summary:
18718 // Fill in form values from according to an Object (in the format returned by get('value'))
a089699c 18719
1354d172
AD
18720 // generate map from name --> [list of widgets with that name]
18721 var map = { };
18722 array.forEach(this._getDescendantFormWidgets(), function(widget){
18723 if(!widget.name){ return; }
18724 var entry = map[widget.name] || (map[widget.name] = [] );
18725 entry.push(widget);
18726 });
a089699c 18727
1354d172
AD
18728 for(var name in map){
18729 if(!map.hasOwnProperty(name)){
18730 continue;
18731 }
18732 var widgets = map[name], // array of widgets w/this name
18733 values = lang.getObject(name, false, obj); // list of values for those widgets
a089699c 18734
1354d172
AD
18735 if(values === undefined){
18736 continue;
18737 }
18738 if(!lang.isArray(values)){
18739 values = [ values ];
18740 }
18741 if(typeof widgets[0].checked == 'boolean'){
18742 // for checkbox/radio, values is a list of which widgets should be checked
18743 array.forEach(widgets, function(w){
18744 w.set('value', array.indexOf(values, w.value) != -1);
18745 });
18746 }else if(widgets[0].multiple){
18747 // it takes an array (e.g. multi-select)
18748 widgets[0].set('value', values);
18749 }else{
18750 // otherwise, values is a list of values to be assigned sequentially to each widget
18751 array.forEach(widgets, function(w, i){
18752 w.set('value', values[i]);
18753 });
18754 }
18755 }
a089699c 18756
1354d172
AD
18757 /***
18758 * TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
a089699c 18759
1354d172
AD
18760 array.forEach(this.containerNode.elements, function(element){
18761 if(element.name == ''){return}; // like "continue"
18762 var namePath = element.name.split(".");
18763 var myObj=obj;
18764 var name=namePath[namePath.length-1];
18765 for(var j=1,len2=namePath.length;j<len2;++j){
18766 var p=namePath[j - 1];
18767 // repeater support block
18768 var nameA=p.split("[");
18769 if(nameA.length > 1){
18770 if(typeof(myObj[nameA[0]]) == "undefined"){
18771 myObj[nameA[0]]=[ ];
18772 } // if
a089699c 18773
1354d172
AD
18774 nameIndex=parseInt(nameA[1]);
18775 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
18776 myObj[nameA[0]][nameIndex] = { };
18777 }
18778 myObj=myObj[nameA[0]][nameIndex];
18779 continue;
18780 } // repeater support ends
a089699c 18781
1354d172
AD
18782 if(typeof(myObj[p]) == "undefined"){
18783 myObj=undefined;
18784 break;
18785 };
18786 myObj=myObj[p];
18787 }
a089699c 18788
1354d172
AD
18789 if(typeof(myObj) == "undefined"){
18790 return; // like "continue"
18791 }
18792 if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
18793 return; // like "continue"
18794 }
a089699c 18795
1354d172 18796 // TODO: widget values (just call set('value', ...) on the widget)
a089699c 18797
1354d172
AD
18798 // TODO: maybe should call dojo.getNodeProp() instead
18799 switch(element.type){
18800 case "checkbox":
18801 element.checked = (name in myObj) &&
18802 array.some(myObj[name], function(val){ return val == element.value; });
18803 break;
18804 case "radio":
18805 element.checked = (name in myObj) && myObj[name] == element.value;
18806 break;
18807 case "select-multiple":
18808 element.selectedIndex=-1;
18809 array.forEach(element.options, function(option){
18810 option.selected = array.some(myObj[name], function(val){ return option.value == val; });
18811 });
18812 break;
18813 case "select-one":
18814 element.selectedIndex="0";
18815 array.forEach(element.options, function(option){
18816 option.selected = option.value == myObj[name];
18817 });
18818 break;
18819 case "hidden":
18820 case "text":
18821 case "textarea":
18822 case "password":
18823 element.value = myObj[name] || "";
18824 break;
18825 }
18826 });
18827 */
a089699c 18828
1354d172
AD
18829 // Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
18830 // which I am monitoring.
18831 },
a089699c 18832
1354d172
AD
18833 getValues: function(){
18834 kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
18835 return this.get('value');
18836 },
18837 _getValueAttr: function(){
18838 // summary:
18839 // Returns Object representing form values. See description of `value` for details.
18840 // description:
a089699c 18841
1354d172
AD
18842 // The value is updated into this.value every time a child has an onChange event,
18843 // so in the common case this function could just return this.value. However,
18844 // that wouldn't work when:
18845 //
18846 // 1. User presses return key to submit a form. That doesn't fire an onchange event,
18847 // and even if it did it would come too late due to the setTimeout(..., 0) in _handleOnChange()
18848 //
18849 // 2. app for some reason calls this.get("value") while the user is typing into a
18850 // form field. Not sure if that case needs to be supported or not.
18851
18852 // get widget values
18853 var obj = { };
18854 array.forEach(this._getDescendantFormWidgets(), function(widget){
18855 var name = widget.name;
18856 if(!name || widget.disabled){ return; }
18857
18858 // Single value widget (checkbox, radio, or plain <input> type widget)
18859 var value = widget.get('value');
18860
18861 // Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
18862 if(typeof widget.checked == 'boolean'){
18863 if(/Radio/.test(widget.declaredClass)){
18864 // radio button
18865 if(value !== false){
18866 lang.setObject(name, value, obj);
18867 }else{
18868 // give radio widgets a default of null
18869 value = lang.getObject(name, false, obj);
18870 if(value === undefined){
18871 lang.setObject(name, null, obj);
18872 }
18873 }
a089699c 18874 }else{
1354d172
AD
18875 // checkbox/toggle button
18876 var ary=lang.getObject(name, false, obj);
18877 if(!ary){
18878 ary=[];
18879 lang.setObject(name, ary, obj);
18880 }
18881 if(value !== false){
18882 ary.push(value);
18883 }
a089699c
AD
18884 }
18885 }else{
1354d172
AD
18886 var prev=lang.getObject(name, false, obj);
18887 if(typeof prev != "undefined"){
18888 if(lang.isArray(prev)){
18889 prev.push(value);
18890 }else{
18891 lang.setObject(name, [prev, value], obj);
18892 }
18893 }else{
18894 // unique name
18895 lang.setObject(name, value, obj);
18896 }
a089699c 18897 }
1354d172 18898 });
a089699c 18899
1354d172
AD
18900 /***
18901 * code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
18902 * but it doesn't understand [] notation, presumably)
18903 var obj = { };
18904 array.forEach(this.containerNode.elements, function(elm){
18905 if(!elm.name) {
18906 return; // like "continue"
18907 }
18908 var namePath = elm.name.split(".");
18909 var myObj=obj;
18910 var name=namePath[namePath.length-1];
18911 for(var j=1,len2=namePath.length;j<len2;++j){
18912 var nameIndex = null;
18913 var p=namePath[j - 1];
18914 var nameA=p.split("[");
18915 if(nameA.length > 1){
18916 if(typeof(myObj[nameA[0]]) == "undefined"){
18917 myObj[nameA[0]]=[ ];
18918 } // if
18919 nameIndex=parseInt(nameA[1]);
18920 if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
18921 myObj[nameA[0]][nameIndex] = { };
18922 }
18923 }else if(typeof(myObj[nameA[0]]) == "undefined"){
18924 myObj[nameA[0]] = { }
18925 } // if
a089699c 18926
1354d172
AD
18927 if(nameA.length == 1){
18928 myObj=myObj[nameA[0]];
18929 }else{
18930 myObj=myObj[nameA[0]][nameIndex];
18931 } // if
18932 } // for
a089699c 18933
1354d172
AD
18934 if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
18935 if(name == name.split("[")[0]){
18936 myObj[name]=elm.value;
18937 }else{
18938 // can not set value when there is no name
18939 }
18940 }else if(elm.type == "checkbox" && elm.checked){
18941 if(typeof(myObj[name]) == 'undefined'){
18942 myObj[name]=[ ];
18943 }
18944 myObj[name].push(elm.value);
18945 }else if(elm.type == "select-multiple"){
18946 if(typeof(myObj[name]) == 'undefined'){
18947 myObj[name]=[ ];
18948 }
18949 for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
18950 if(elm.options[jdx].selected){
18951 myObj[name].push(elm.options[jdx].value);
18952 }
18953 }
18954 } // if
18955 name=undefined;
18956 }); // forEach
18957 ***/
18958 return obj;
18959 },
a089699c 18960
1354d172
AD
18961 isValid: function(){
18962 // summary:
18963 // Returns true if all of the widgets are valid.
18964 // Deprecated, will be removed in 2.0. Use get("state") instead.
a089699c 18965
1354d172
AD
18966 return this.state == "";
18967 },
a089699c 18968
1354d172
AD
18969 onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
18970 // summary:
18971 // Stub function to connect to if you want to do something
18972 // (like disable/enable a submit button) when the valid
18973 // state changes on the form as a whole.
18974 //
18975 // Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
18976 },
a089699c 18977
1354d172
AD
18978 _getState: function(){
18979 // summary:
18980 // Compute what this.state should be based on state of children
18981 var states = array.map(this._descendants, function(w){
18982 return w.get("state") || "";
18983 });
a089699c 18984
1354d172
AD
18985 return array.indexOf(states, "Error") >= 0 ? "Error" :
18986 array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
18987 },
a089699c 18988
1354d172
AD
18989 disconnectChildren: function(){
18990 // summary:
18991 // Remove connections to monitor changes to children's value, error state, and disabled state,
18992 // in order to update Form.value and Form.state.
18993 array.forEach(this._childConnections || [], lang.hitch(this, "disconnect"));
18994 array.forEach(this._childWatches || [], function(w){ w.unwatch(); });
18995 },
a089699c 18996
1354d172
AD
18997 connectChildren: function(/*Boolean*/ inStartup){
18998 // summary:
18999 // Setup connections to monitor changes to children's value, error state, and disabled state,
19000 // in order to update Form.value and Form.state.
19001 //
19002 // You can call this function directly, ex. in the event that you
19003 // programmatically add a widget to the form *after* the form has been
19004 // initialized.
a089699c 19005
1354d172 19006 var _this = this;
a089699c 19007
1354d172
AD
19008 // Remove old connections, if any
19009 this.disconnectChildren();
a089699c 19010
1354d172 19011 this._descendants = this._getDescendantFormWidgets();
a089699c 19012
1354d172
AD
19013 // (Re)set this.value and this.state. Send watch() notifications but not on startup.
19014 var set = inStartup ? function(name, val){ _this[name] = val; } : lang.hitch(this, "_set");
19015 set("value", this.get("value"));
19016 set("state", this._getState());
a089699c 19017
1354d172
AD
19018 // Monitor changes to error state and disabled state in order to update
19019 // Form.state
19020 var conns = (this._childConnections = []),
19021 watches = (this._childWatches = []);
19022 array.forEach(array.filter(this._descendants,
19023 function(item){ return item.validate; }
19024 ),
19025 function(widget){
19026 // We are interested in whenever the widget changes validity state - or
19027 // whenever the disabled attribute on that widget is changed.
19028 array.forEach(["state", "disabled"], function(attr){
19029 watches.push(widget.watch(attr, function(){
19030 _this.set("state", _this._getState());
19031 }));
19032 });
19033 });
a089699c 19034
1354d172
AD
19035 // And monitor calls to child.onChange so we can update this.value
19036 var onChange = function(){
19037 // summary:
19038 // Called when child's value or disabled state changes
a089699c 19039
1354d172
AD
19040 // Use setTimeout() to collapse value changes in multiple children into a single
19041 // update to my value. Multiple updates will occur on:
19042 // 1. Form.set()
19043 // 2. Form.reset()
19044 // 3. user selecting a radio button (which will de-select another radio button,
19045 // causing two onChange events)
19046 if(_this._onChangeDelayTimer){
19047 clearTimeout(_this._onChangeDelayTimer);
19048 }
19049 _this._onChangeDelayTimer = setTimeout(function(){
19050 delete _this._onChangeDelayTimer;
19051 _this._set("value", _this.get("value"));
19052 }, 10);
19053 };
19054 array.forEach(
19055 array.filter(this._descendants, function(item){ return item.onChange; } ),
19056 function(widget){
19057 // When a child widget's value changes,
19058 // the efficient thing to do is to just update that one attribute in this.value,
19059 // but that gets a little complicated when a checkbox is checked/unchecked
19060 // since this.value["checkboxName"] contains an array of all the checkboxes w/the same name.
19061 // Doing simple thing for now.
19062 conns.push(_this.connect(widget, "onChange", onChange));
a089699c 19063
1354d172
AD
19064 // Disabling/enabling a child widget should remove it's value from this.value.
19065 // Again, this code could be more efficient, doing simple thing for now.
19066 watches.push(widget.watch("disabled", onChange));
19067 }
19068 );
19069 },
a089699c 19070
1354d172
AD
19071 startup: function(){
19072 this.inherited(arguments);
a089699c 19073
1354d172
AD
19074 // Initialize value and valid/invalid state tracking. Needs to be done in startup()
19075 // so that children are initialized.
19076 this.connectChildren(true);
a089699c 19077
1354d172
AD
19078 // Make state change call onValidStateChange(), will be removed in 2.0
19079 this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
19080 },
a089699c 19081
1354d172
AD
19082 destroy: function(){
19083 this.disconnectChildren();
19084 this.inherited(arguments);
a089699c
AD
19085 }
19086
1354d172
AD
19087 });
19088});
a089699c 19089
1354d172
AD
19090},
19091'dijit/DropDownMenu':function(){
19092require({cache:{
19093'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\" data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
19094define("dijit/DropDownMenu", [
19095 "dojo/_base/declare", // declare
19096 "dojo/_base/event", // event.stop
19097 "dojo/keys", // keys
19098 "dojo/text!./templates/Menu.html",
19099 "./_OnDijitClickMixin",
19100 "./_MenuBase"
19101], function(declare, event, keys, template, _OnDijitClickMixin, _MenuBase){
a089699c 19102
1354d172
AD
19103/*=====
19104 var _MenuBase = dijit._MenuBase;
19105 var _OnDijitClickMixin = dijit._OnDijitClickMixin;
19106=====*/
a089699c 19107
1354d172
AD
19108 // module:
19109 // dijit/DropDownMenu
19110 // summary:
19111 // dijit.DropDownMenu widget
a089699c 19112
1354d172
AD
19113 return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
19114 // summary:
19115 // A menu, without features for context menu (Meaning, drop down menu)
a089699c 19116
1354d172 19117 templateString: template,
a089699c 19118
1354d172 19119 baseClass: "dijitMenu",
a089699c 19120
1354d172
AD
19121 postCreate: function(){
19122 var l = this.isLeftToRight();
19123 this._openSubMenuKey = l ? keys.RIGHT_ARROW : keys.LEFT_ARROW;
19124 this._closeSubMenuKey = l ? keys.LEFT_ARROW : keys.RIGHT_ARROW;
19125 this.connectKeyNavHandlers([keys.UP_ARROW], [keys.DOWN_ARROW]);
19126 },
a089699c 19127
1354d172
AD
19128 _onKeyPress: function(/*Event*/ evt){
19129 // summary:
19130 // Handle keyboard based menu navigation.
19131 // tags:
19132 // protected
a089699c 19133
1354d172 19134 if(evt.ctrlKey || evt.altKey){ return; }
a089699c 19135
1354d172
AD
19136 switch(evt.charOrCode){
19137 case this._openSubMenuKey:
19138 this._moveToPopup(evt);
19139 event.stop(evt);
19140 break;
19141 case this._closeSubMenuKey:
19142 if(this.parentMenu){
19143 if(this.parentMenu._isMenuBar){
19144 this.parentMenu.focusPrev();
19145 }else{
19146 this.onCancel(false);
19147 }
19148 }else{
19149 event.stop(evt);
19150 }
19151 break;
19152 }
19153 }
19154 });
19155});
a089699c 19156
1354d172
AD
19157},
19158'dojo/data/util/simpleFetch':function(){
19159define("dojo/data/util/simpleFetch", ["dojo/_base/lang", "dojo/_base/window", "./sorter"],
19160 function(lang, winUtil, sorter) {
19161 // module:
19162 // dojo/data/util/simpleFetch
a089699c 19163 // summary:
1354d172 19164 // TODOC
81bea17a 19165
1354d172 19166var simpleFetch = lang.getObject("dojo.data.util.simpleFetch", true);
81bea17a 19167
1354d172
AD
19168simpleFetch.fetch = function(/* Object? */ request){
19169 // summary:
19170 // The simpleFetch mixin is designed to serve as a set of function(s) that can
19171 // be mixed into other datastore implementations to accelerate their development.
19172 // The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
19173 // call by returning an array of all the found items that matched the query. The simpleFetch mixin
19174 // is not designed to work for datastores that respond to a fetch() call by incrementally
19175 // loading items, or sequentially loading partial batches of the result
19176 // set. For datastores that mixin simpleFetch, simpleFetch
19177 // implements a fetch method that automatically handles eight of the fetch()
19178 // arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
19179 // The class mixing in simpleFetch should not implement fetch(),
19180 // but should instead implement a _fetchItems() method. The _fetchItems()
19181 // method takes three arguments, the keywordArgs object that was passed
19182 // to fetch(), a callback function to be called when the result array is
19183 // available, and an error callback to be called if something goes wrong.
19184 // The _fetchItems() method should ignore any keywordArgs parameters for
19185 // start, count, onBegin, onItem, onComplete, onError, sort, and scope.
19186 // The _fetchItems() method needs to correctly handle any other keywordArgs
19187 // parameters, including the query parameter and any optional parameters
19188 // (such as includeChildren). The _fetchItems() method should create an array of
19189 // result items and pass it to the fetchHandler along with the original request object
19190 // -- or, the _fetchItems() method may, if it wants to, create an new request object
19191 // with other specifics about the request that are specific to the datastore and pass
19192 // that as the request object to the handler.
19193 //
19194 // For more information on this specific function, see dojo.data.api.Read.fetch()
19195 request = request || {};
19196 if(!request.store){
19197 request.store = this;
19198 }
19199 var self = this;
81bea17a 19200
1354d172
AD
19201 var _errorHandler = function(errorData, requestObject){
19202 if(requestObject.onError){
19203 var scope = requestObject.scope || winUtil.global;
19204 requestObject.onError.call(scope, errorData, requestObject);
19205 }
19206 };
81bea17a 19207
1354d172
AD
19208 var _fetchHandler = function(items, requestObject){
19209 var oldAbortFunction = requestObject.abort || null;
19210 var aborted = false;
19211
19212 var startIndex = requestObject.start?requestObject.start:0;
19213 var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
19214
19215 requestObject.abort = function(){
19216 aborted = true;
19217 if(oldAbortFunction){
19218 oldAbortFunction.call(requestObject);
a089699c 19219 }
1354d172 19220 };
a089699c 19221
1354d172
AD
19222 var scope = requestObject.scope || winUtil.global;
19223 if(!requestObject.store){
19224 requestObject.store = self;
19225 }
19226 if(requestObject.onBegin){
19227 requestObject.onBegin.call(scope, items.length, requestObject);
19228 }
19229 if(requestObject.sort){
19230 items.sort(sorter.createSortFunction(requestObject.sort, self));
19231 }
19232 if(requestObject.onItem){
19233 for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
19234 var item = items[i];
19235 if(!aborted){
19236 requestObject.onItem.call(scope, item, requestObject);
19237 }
19238 }
19239 }
19240 if(requestObject.onComplete && !aborted){
19241 var subset = null;
19242 if(!requestObject.onItem){
19243 subset = items.slice(startIndex, endIndex);
19244 }
19245 requestObject.onComplete.call(scope, subset, requestObject);
19246 }
19247 };
19248 this._fetchItems(request, _fetchHandler, _errorHandler);
19249 return request; // Object
19250};
a089699c 19251
1354d172
AD
19252return simpleFetch;
19253});
a089699c 19254
1354d172
AD
19255},
19256'dijit/Menu':function(){
19257define("dijit/Menu", [
19258 "require",
19259 "dojo/_base/array", // array.forEach
19260 "dojo/_base/declare", // declare
19261 "dojo/_base/event", // event.stop
19262 "dojo/dom", // dom.byId dom.isDescendant
19263 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
19264 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
19265 "dojo/dom-style", // domStyle.getComputedStyle
19266 "dojo/_base/kernel",
19267 "dojo/keys", // keys.F10
19268 "dojo/_base/lang", // lang.hitch
19269 "dojo/on",
19270 "dojo/_base/sniff", // has("ie"), has("quirks")
19271 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames win.withGlobal
19272 "dojo/window", // winUtils.get
19273 "./popup",
19274 "./DropDownMenu",
19275 "dojo/ready"
19276], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, kernel, keys, lang, on,
19277 has, win, winUtils, pm, DropDownMenu, ready){
a089699c 19278
1354d172
AD
19279/*=====
19280 var DropDownMenu = dijit.DropDownMenu;
19281=====*/
a089699c 19282
1354d172
AD
19283// module:
19284// dijit/Menu
19285// summary:
19286// Includes dijit.Menu widget and base class dijit._MenuBase
a089699c 19287
1354d172
AD
19288// Back compat w/1.6, remove for 2.0
19289if(!kernel.isAsync){
19290 ready(0, function(){
19291 var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
19292 require(requires); // use indirection so modules not rolled into a build
19293 });
19294}
a089699c 19295
1354d172
AD
19296return declare("dijit.Menu", DropDownMenu, {
19297 // summary:
19298 // A context menu you can assign to multiple elements
81bea17a 19299
1354d172
AD
19300 constructor: function(){
19301 this._bindings = [];
19302 },
a089699c 19303
1354d172
AD
19304 // targetNodeIds: [const] String[]
19305 // Array of dom node ids of nodes to attach to.
19306 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
19307 targetNodeIds: [],
a089699c 19308
1354d172
AD
19309 // contextMenuForWindow: [const] Boolean
19310 // If true, right clicking anywhere on the window will cause this context menu to open.
19311 // If false, must specify targetNodeIds.
19312 contextMenuForWindow: false,
a089699c 19313
1354d172
AD
19314 // leftClickToOpen: [const] Boolean
19315 // If true, menu will open on left click instead of right click, similar to a file menu.
19316 leftClickToOpen: false,
a089699c 19317
1354d172
AD
19318 // refocus: Boolean
19319 // When this menu closes, re-focus the element which had focus before it was opened.
19320 refocus: true,
a089699c 19321
1354d172
AD
19322 postCreate: function(){
19323 if(this.contextMenuForWindow){
19324 this.bindDomNode(win.body());
a089699c 19325 }else{
1354d172
AD
19326 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
19327 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
19328 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
19329 array.forEach(this.targetNodeIds, this.bindDomNode, this);
a089699c 19330 }
1354d172 19331 this.inherited(arguments);
a089699c
AD
19332 },
19333
1354d172
AD
19334 // thanks burstlib!
19335 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
a089699c 19336 // summary:
1354d172
AD
19337 // Returns the window reference of the passed iframe
19338 // tags:
19339 // private
19340 return winUtils.get(this._iframeContentDocument(iframe_el)) ||
19341 // Moz. TODO: is this available when defaultView isn't?
19342 this._iframeContentDocument(iframe_el)['__parent__'] ||
19343 (iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window
a089699c
AD
19344 },
19345
1354d172
AD
19346 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
19347 // summary:
19348 // Returns a reference to the document object inside iframe_el
19349 // tags:
19350 // protected
19351 return iframe_el.contentDocument // W3
19352 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
19353 || (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document)
19354 || null; // HTMLDocument
a089699c
AD
19355 },
19356
1354d172 19357 bindDomNode: function(/*String|DomNode*/ node){
a089699c 19358 // summary:
1354d172
AD
19359 // Attach menu to given node
19360 node = dom.byId(node);
a089699c 19361
1354d172 19362 var cn; // Connect node
a089699c 19363
1354d172
AD
19364 // Support context menus on iframes. Rather than binding to the iframe itself we need
19365 // to bind to the <body> node inside the iframe.
19366 if(node.tagName.toLowerCase() == "iframe"){
19367 var iframe = node,
19368 window = this._iframeContentWindow(iframe);
19369 cn = win.withGlobal(window, win.body);
19370 }else{
a089699c 19371
1354d172
AD
19372 // To capture these events at the top level, attach to <html>, not <body>.
19373 // Otherwise right-click context menu just doesn't work.
19374 cn = (node == win.body() ? win.doc.documentElement : node);
a089699c 19375 }
a089699c 19376
a089699c 19377
1354d172
AD
19378 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
19379 var binding = {
19380 node: node,
19381 iframe: iframe
19382 };
a089699c 19383
1354d172
AD
19384 // Save info about binding in _bindings[], and make node itself record index(+1) into
19385 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
19386 // start with a number, which fails on FF/safari.
19387 domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
19388
19389 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
19390 // loading yet, in which case we need to wait for the onload event first, and then connect
19391 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
19392 // we need to monitor keyboard events in addition to the oncontextmenu event.
19393 var doConnects = lang.hitch(this, function(cn){
19394 return [
19395 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
19396 // rather than shift-F10?
19397 on(cn, this.leftClickToOpen ? "click" : "contextmenu", lang.hitch(this, function(evt){
19398 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
19399 event.stop(evt);
19400 this._scheduleOpen(evt.target, iframe, {x: evt.pageX, y: evt.pageY});
19401 })),
19402 on(cn, "keydown", lang.hitch(this, function(evt){
19403 if(evt.shiftKey && evt.keyCode == keys.F10){
19404 event.stop(evt);
19405 this._scheduleOpen(evt.target, iframe); // no coords - open near target node
19406 }
19407 }))
19408 ];
19409 });
19410 binding.connects = cn ? doConnects(cn) : [];
19411
19412 if(iframe){
19413 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
19414 // and every time the contents change.
19415 // Need to do this b/c we are actually binding to the iframe's <body> node.
19416 // Note: can't use connect.connect(), see #9609.
19417
19418 binding.onloadHandler = lang.hitch(this, function(){
19419 // want to remove old connections, but IE throws exceptions when trying to
19420 // access the <body> node because it's already gone, or at least in a state of limbo
19421
19422 var window = this._iframeContentWindow(iframe);
19423 cn = win.withGlobal(window, win.body);
19424 binding.connects = doConnects(cn);
19425 });
19426 if(iframe.addEventListener){
19427 iframe.addEventListener("load", binding.onloadHandler, false);
19428 }else{
19429 iframe.attachEvent("onload", binding.onloadHandler);
a089699c
AD
19430 }
19431 }
a089699c
AD
19432 },
19433
1354d172 19434 unBindDomNode: function(/*String|DomNode*/ nodeName){
a089699c 19435 // summary:
1354d172 19436 // Detach menu from given node
a089699c 19437
1354d172
AD
19438 var node;
19439 try{
19440 node = dom.byId(nodeName);
19441 }catch(e){
19442 // On IE the dom.byId() call will get an exception if the attach point was
19443 // the <body> node of an <iframe> that has since been reloaded (and thus the
19444 // <body> node is in a limbo state of destruction.
19445 return;
19446 }
19447
19448 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
19449 var attrName = "_dijitMenu" + this.id;
19450 if(node && domAttr.has(node, attrName)){
19451 var bid = domAttr.get(node, attrName)-1, b = this._bindings[bid], h;
19452 while(h = b.connects.pop()){
19453 h.remove();
19454 }
19455
19456 // Remove listener for iframe onload events
19457 var iframe = b.iframe;
19458 if(iframe){
19459 if(iframe.removeEventListener){
19460 iframe.removeEventListener("load", b.onloadHandler, false);
19461 }else{
19462 iframe.detachEvent("onload", b.onloadHandler);
19463 }
19464 }
19465
19466 domAttr.remove(node, attrName);
19467 delete this._bindings[bid];
19468 }
a089699c
AD
19469 },
19470
1354d172 19471 _scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
a089699c 19472 // summary:
1354d172
AD
19473 // Set timer to display myself. Using a timer rather than displaying immediately solves
19474 // two problems:
19475 //
19476 // 1. IE: without the delay, focus work in "open" causes the system
19477 // context menu to appear in spite of stopEvent.
19478 //
19479 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
19480 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
19481 // oncontextmenu event.)
19482
19483 if(!this._openTimer){
19484 this._openTimer = setTimeout(lang.hitch(this, function(){
19485 delete this._openTimer;
19486 this._openMyself({
19487 target: target,
19488 iframe: iframe,
19489 coords: coords
19490 });
19491 }), 1);
19492 }
a089699c
AD
19493 },
19494
1354d172 19495 _openMyself: function(args){
81bea17a 19496 // summary:
1354d172
AD
19497 // Internal function for opening myself when the user does a right-click or something similar.
19498 // args:
19499 // This is an Object containing:
19500 // * target:
19501 // The node that is being clicked
19502 // * iframe:
19503 // If an <iframe> is being clicked, iframe points to that iframe
19504 // * coords:
19505 // Put menu at specified x/y position in viewport, or if iframe is
19506 // specified, then relative to iframe.
19507 //
19508 // _openMyself() formerly took the event object, and since various code references
19509 // evt.target (after connecting to _openMyself()), using an Object for parameters
19510 // (so that old code still works).
81bea17a 19511
1354d172
AD
19512 var target = args.target,
19513 iframe = args.iframe,
19514 coords = args.coords;
81bea17a 19515
1354d172
AD
19516 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
19517 // then near the node the menu is assigned to.
19518 if(coords){
19519 if(iframe){
19520 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
19521 var ifc = domGeometry.position(iframe, true),
19522 window = this._iframeContentWindow(iframe),
19523 scroll = win.withGlobal(window, "_docScroll", dojo);
a089699c 19524
1354d172
AD
19525 var cs = domStyle.getComputedStyle(iframe),
19526 tp = domStyle.toPixelValue,
19527 left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
19528 top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
a089699c 19529
1354d172
AD
19530 coords.x += ifc.x + left - scroll.x;
19531 coords.y += ifc.y + top - scroll.y;
19532 }
19533 }else{
19534 coords = domGeometry.position(target, true);
19535 coords.x += 10;
19536 coords.y += 10;
19537 }
a089699c 19538
1354d172
AD
19539 var self=this;
19540 var prevFocusNode = this._focusManager.get("prevNode");
19541 var curFocusNode = this._focusManager.get("curNode");
19542 var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
a089699c 19543
1354d172
AD
19544 function closeAndRestoreFocus(){
19545 // user has clicked on a menu or popup
19546 if(self.refocus && savedFocusNode){
19547 savedFocusNode.focus();
19548 }
19549 pm.close(self);
a089699c 19550 }
1354d172
AD
19551 pm.open({
19552 popup: this,
19553 x: coords.x,
19554 y: coords.y,
19555 onExecute: closeAndRestoreFocus,
19556 onCancel: closeAndRestoreFocus,
19557 orient: this.isLeftToRight() ? 'L' : 'R'
19558 });
19559 this.focus();
19560
19561 this._onBlur = function(){
19562 this.inherited('_onBlur', arguments);
19563 // Usually the parent closes the child widget but if this is a context
19564 // menu then there is no parent
19565 pm.close(this);
19566 // don't try to restore focus; user has clicked another part of the screen
19567 // and set focus there
19568 };
a089699c
AD
19569 },
19570
1354d172
AD
19571 uninitialize: function(){
19572 array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
19573 this.inherited(arguments);
a089699c
AD
19574 }
19575});
19576
1354d172 19577});
a089699c 19578
1354d172
AD
19579},
19580'dijit/form/_CheckBoxMixin':function(){
19581define("dijit/form/_CheckBoxMixin", [
19582 "dojo/_base/declare", // declare
19583 "dojo/dom-attr", // domAttr.set
19584 "dojo/_base/event" // event.stop
19585], function(declare, domAttr, event){
19586
19587 // module:
19588 // dijit/form/_CheckBoxMixin
19589 // summary:
19590 // Mixin to provide widget functionality corresponding to an HTML checkbox
a089699c 19591
1354d172
AD
19592 return declare("dijit.form._CheckBoxMixin", null, {
19593 // summary:
19594 // Mixin to provide widget functionality corresponding to an HTML checkbox
19595 //
19596 // description:
19597 // User interacts with real html inputs.
19598 // On onclick (which occurs by mouse click, space-bar, or
19599 // using the arrow keys to switch the selected radio button),
19600 // we update the state of the checkbox/radio.
19601 //
a089699c 19602
1354d172
AD
19603 // type: [private] String
19604 // type attribute on <input> node.
19605 // Overrides `dijit.form.Button.type`. Users should not change this value.
19606 type: "checkbox",
a089699c 19607
1354d172
AD
19608 // value: String
19609 // As an initialization parameter, equivalent to value field on normal checkbox
19610 // (if checked, the value is passed as the value when form is submitted).
19611 value: "on",
a089699c 19612
1354d172
AD
19613 // readOnly: Boolean
19614 // Should this widget respond to user input?
19615 // In markup, this is specified as "readOnly".
19616 // Similar to disabled except readOnly form values are submitted.
19617 readOnly: false,
19618
19619 // aria-pressed for toggle buttons, and aria-checked for checkboxes
19620 _aria_attr: "aria-checked",
a089699c 19621
1354d172
AD
19622 _setReadOnlyAttr: function(/*Boolean*/ value){
19623 this._set("readOnly", value);
19624 domAttr.set(this.focusNode, 'readOnly', value);
19625 this.focusNode.setAttribute("aria-readonly", value);
19626 },
a089699c 19627
1354d172
AD
19628 // Override dijit.form.Button._setLabelAttr() since we don't even have a containerNode.
19629 // Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox.layout.TabContainer
19630 _setLabelAttr: undefined,
a089699c 19631
1354d172
AD
19632 postMixInProperties: function(){
19633 if(this.value == ""){
19634 this.value = "on";
19635 }
19636 this.inherited(arguments);
19637 },
a089699c 19638
1354d172
AD
19639 reset: function(){
19640 this.inherited(arguments);
19641 // Handle unlikely event that the <input type=checkbox> value attribute has changed
19642 this._set("value", this.params.value || "on");
19643 domAttr.set(this.focusNode, 'value', this.value);
19644 },
a089699c 19645
1354d172
AD
19646 _onClick: function(/*Event*/ e){
19647 // summary:
19648 // Internal function to handle click actions - need to check
19649 // readOnly, since button no longer does that check.
19650 if(this.readOnly){
19651 event.stop(e);
19652 return false;
19653 }
19654 return this.inherited(arguments);
a089699c 19655 }
1354d172
AD
19656 });
19657});
a089699c 19658
1354d172
AD
19659},
19660'dijit/layout/ContentPane':function(){
19661define("dijit/layout/ContentPane", [
19662 "dojo/_base/kernel", // kernel.deprecated
19663 "dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
19664 "../_Widget",
19665 "./_ContentPaneResizeMixin",
19666 "dojo/string", // string.substitute
19667 "dojo/html", // html._ContentSetter html._emptyNode
19668 "dojo/i18n!../nls/loading",
19669 "dojo/_base/array", // array.forEach
19670 "dojo/_base/declare", // declare
19671 "dojo/_base/Deferred", // Deferred
19672 "dojo/dom", // dom.byId
19673 "dojo/dom-attr", // domAttr.attr
19674 "dojo/_base/window", // win.body win.doc.createDocumentFragment
19675 "dojo/_base/xhr", // xhr.get
19676 "dojo/i18n" // i18n.getLocalization
19677], function(kernel, lang, _Widget, _ContentPaneResizeMixin, string, html, nlsLoading,
19678 array, declare, Deferred, dom, domAttr, win, xhr, i18n){
a089699c 19679
1354d172
AD
19680/*=====
19681 var _Widget = dijit._Widget;
19682 var _ContentPaneResizeMixin = dijit.layout._ContentPaneResizeMixin;
19683=====*/
a089699c 19684
1354d172
AD
19685// module:
19686// dijit/layout/ContentPane
19687// summary:
19688// A widget containing an HTML fragment, specified inline
19689// or by uri. Fragment may include widgets.
a089699c
AD
19690
19691
1354d172
AD
19692return declare("dijit.layout.ContentPane", [_Widget, _ContentPaneResizeMixin], {
19693 // summary:
19694 // A widget containing an HTML fragment, specified inline
19695 // or by uri. Fragment may include widgets.
19696 //
19697 // description:
19698 // This widget embeds a document fragment in the page, specified
19699 // either by uri, javascript generated markup or DOM reference.
19700 // Any widgets within this content are instantiated and managed,
19701 // but laid out according to the HTML structure. Unlike IFRAME,
19702 // ContentPane embeds a document fragment as would be found
19703 // inside the BODY tag of a full HTML document. It should not
19704 // contain the HTML, HEAD, or BODY tags.
19705 // For more advanced functionality with scripts and
19706 // stylesheets, see dojox.layout.ContentPane. This widget may be
19707 // used stand alone or as a base class for other widgets.
19708 // ContentPane is useful as a child of other layout containers
19709 // such as BorderContainer or TabContainer, but note that those
19710 // widgets can contain any widget as a child.
19711 //
19712 // example:
19713 // Some quick samples:
19714 // To change the innerHTML: cp.set('content', '<b>new content</b>')
19715 //
19716 // Or you can send it a NodeList: cp.set('content', dojo.query('div [class=selected]', userSelection))
19717 //
19718 // To do an ajax update: cp.set('href', url)
a089699c 19719
1354d172
AD
19720 // href: String
19721 // The href of the content that displays now.
19722 // Set this at construction if you want to load data externally when the
19723 // pane is shown. (Set preload=true to load it immediately.)
19724 // Changing href after creation doesn't have any effect; Use set('href', ...);
19725 href: "",
a089699c 19726
1354d172
AD
19727 // content: String || DomNode || NodeList || dijit._Widget
19728 // The innerHTML of the ContentPane.
19729 // Note that the initialization parameter / argument to set("content", ...)
19730 // can be a String, DomNode, Nodelist, or _Widget.
19731 content: "",
a089699c 19732
1354d172
AD
19733 // extractContent: Boolean
19734 // Extract visible content from inside of <body> .... </body>.
19735 // I.e., strip <html> and <head> (and it's contents) from the href
19736 extractContent: false,
a089699c 19737
1354d172
AD
19738 // parseOnLoad: Boolean
19739 // Parse content and create the widgets, if any.
19740 parseOnLoad: true,
a089699c 19741
1354d172
AD
19742 // parserScope: String
19743 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
19744 // will search for data-dojo-type (or dojoType). For backwards compatibility
19745 // reasons defaults to dojo._scopeName (which is "dojo" except when
19746 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
19747 parserScope: kernel._scopeName,
a089699c 19748
1354d172
AD
19749 // preventCache: Boolean
19750 // Prevent caching of data from href's by appending a timestamp to the href.
19751 preventCache: false,
a089699c 19752
1354d172
AD
19753 // preload: Boolean
19754 // Force load of data on initialization even if pane is hidden.
19755 preload: false,
a089699c 19756
1354d172
AD
19757 // refreshOnShow: Boolean
19758 // Refresh (re-download) content when pane goes from hidden to shown
19759 refreshOnShow: false,
a089699c 19760
1354d172
AD
19761 // loadingMessage: String
19762 // Message that shows while downloading
19763 loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
a089699c 19764
1354d172
AD
19765 // errorMessage: String
19766 // Message that shows if an error occurs
19767 errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
a089699c 19768
1354d172
AD
19769 // isLoaded: [readonly] Boolean
19770 // True if the ContentPane has data in it, either specified
19771 // during initialization (via href or inline content), or set
19772 // via set('content', ...) / set('href', ...)
19773 //
19774 // False if it doesn't have any content, or if ContentPane is
19775 // still in the process of downloading href.
19776 isLoaded: false,
a089699c 19777
1354d172 19778 baseClass: "dijitContentPane",
a089699c 19779
1354d172
AD
19780 /*======
19781 // ioMethod: dojo.xhrGet|dojo.xhrPost
19782 // Function that should grab the content specified via href.
19783 ioMethod: dojo.xhrGet,
19784 ======*/
81bea17a 19785
1354d172
AD
19786 // ioArgs: Object
19787 // Parameters to pass to xhrGet() request, for example:
19788 // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
19789 ioArgs: {},
a089699c 19790
1354d172
AD
19791 // onLoadDeferred: [readonly] dojo.Deferred
19792 // This is the `dojo.Deferred` returned by set('href', ...) and refresh().
19793 // Calling onLoadDeferred.addCallback() or addErrback() registers your
19794 // callback to be called only once, when the prior set('href', ...) call or
19795 // the initial href parameter to the constructor finishes loading.
19796 //
19797 // This is different than an onLoad() handler which gets called any time any href
19798 // or content is loaded.
19799 onLoadDeferred: null,
a089699c 19800
1354d172
AD
19801 // Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
19802 // tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
19803 // entire pane.
19804 _setTitleAttr: null,
a089699c 19805
1354d172
AD
19806 // Flag to parser that I'll parse my contents, so it shouldn't.
19807 stopParser: true,
a089699c 19808
1354d172
AD
19809 // template: [private] Boolean
19810 // Flag from the parser that this ContentPane is inside a template
19811 // so the contents are pre-parsed.
19812 // (TODO: this declaration can be commented out in 2.0)
19813 template: false,
a089699c 19814
1354d172
AD
19815 create: function(params, srcNodeRef){
19816 // Convert a srcNodeRef argument into a content parameter, so that the original contents are
19817 // processed in the same way as contents set via set("content", ...), calling the parser etc.
19818 // Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
19819 if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
19820 var df = win.doc.createDocumentFragment();
19821 srcNodeRef = dom.byId(srcNodeRef);
19822 while(srcNodeRef.firstChild){
19823 df.appendChild(srcNodeRef.firstChild);
19824 }
19825 params = lang.delegate(params, {content: df});
19826 }
19827 this.inherited(arguments, [params, srcNodeRef]);
a089699c
AD
19828 },
19829
19830 postMixInProperties: function(){
19831 this.inherited(arguments);
1354d172
AD
19832 var messages = i18n.getLocalization("dijit", "loading", this.lang);
19833 this.loadingMessage = string.substitute(this.loadingMessage, messages);
19834 this.errorMessage = string.substitute(this.errorMessage, messages);
19835 },
a089699c 19836
1354d172
AD
19837 buildRendering: function(){
19838 this.inherited(arguments);
a089699c 19839
1354d172
AD
19840 // Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
19841 // For subclasses of ContentPane that do have a template, does nothing.
19842 if(!this.containerNode){
19843 this.containerNode = this.domNode;
a089699c
AD
19844 }
19845
1354d172
AD
19846 // remove the title attribute so it doesn't show up when hovering
19847 // over a node (TODO: remove in 2.0, no longer needed after #11490)
19848 this.domNode.title = "";
a089699c 19849
1354d172
AD
19850 if(!domAttr.get(this.domNode,"role")){
19851 this.domNode.setAttribute("role", "group");
19852 }
a089699c
AD
19853 },
19854
1354d172 19855 startup: function(){
a089699c 19856 // summary:
1354d172 19857 // Call startup() on all children including non _Widget ones like dojo.dnd.Source objects
a089699c 19858
1354d172
AD
19859 // This starts all the widgets
19860 this.inherited(arguments);
a089699c 19861
1354d172
AD
19862 // And this catches stuff like dojo.dnd.Source
19863 if(this._contentSetter){
19864 array.forEach(this._contentSetter.parseResults, function(obj){
19865 if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
19866 obj.startup();
19867 obj._started = true;
19868 }
19869 }, this);
a089699c
AD
19870 }
19871 },
19872
1354d172 19873 setHref: function(/*String|Uri*/ href){
a089699c 19874 // summary:
1354d172
AD
19875 // Deprecated. Use set('href', ...) instead.
19876 kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
19877 return this.set("href", href);
a089699c 19878 },
1354d172 19879 _setHrefAttr: function(/*String|Uri*/ href){
a089699c 19880 // summary:
1354d172
AD
19881 // Hook so set("href", ...) works.
19882 // description:
19883 // Reset the (external defined) content of this pane and replace with new url
19884 // Note: It delays the download until widget is shown if preload is false.
19885 // href:
19886 // url to the page you want to get, must be within the same domain as your mainpage
a089699c 19887
1354d172
AD
19888 // Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
19889 this.cancel();
19890
19891 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
19892 this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
19893
19894 this._set("href", href);
19895
19896 // _setHrefAttr() is called during creation and by the user, after creation.
19897 // Assuming preload == false, only in the second case do we actually load the URL;
19898 // otherwise it's done in startup(), and only if this widget is shown.
19899 if(this.preload || (this._created && this._isShown())){
19900 this._load();
19901 }else{
19902 // Set flag to indicate that href needs to be loaded the next time the
19903 // ContentPane is made visible
19904 this._hrefChanged = true;
19905 }
19906
19907 return this.onLoadDeferred; // Deferred
a089699c
AD
19908 },
19909
1354d172 19910 setContent: function(/*String|DomNode|Nodelist*/data){
a089699c 19911 // summary:
1354d172
AD
19912 // Deprecated. Use set('content', ...) instead.
19913 kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
19914 this.set("content", data);
19915 },
19916 _setContentAttr: function(/*String|DomNode|Nodelist*/data){
19917 // summary:
19918 // Hook to make set("content", ...) work.
19919 // Replaces old content with data content, include style classes from old content
19920 // data:
19921 // the new Content may be String, DomNode or NodeList
19922 //
19923 // if data is a NodeList (or an array of nodes) nodes are copied
19924 // so you can import nodes from another document implicitly
a089699c 19925
1354d172
AD
19926 // clear href so we can't run refresh and clear content
19927 // refresh should only work if we downloaded the content
19928 this._set("href", "");
a089699c 19929
1354d172
AD
19930 // Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
19931 this.cancel();
a089699c 19932
1354d172
AD
19933 // Even though user is just setting content directly, still need to define an onLoadDeferred
19934 // because the _onLoadHandler() handler is still getting called from setContent()
19935 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
19936 if(this._created){
19937 // For back-compat reasons, call onLoad() for set('content', ...)
19938 // calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
19939 // or as initialization parameter (ie: new ContentPane({content: ...})
19940 this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
a089699c 19941 }
a089699c 19942
1354d172 19943 this._setContent(data || "");
a089699c 19944
1354d172 19945 this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
a089699c 19946
1354d172 19947 return this.onLoadDeferred; // Deferred
a089699c 19948 },
1354d172 19949 _getContentAttr: function(){
a089699c 19950 // summary:
1354d172
AD
19951 // Hook to make get("content") work
19952 return this.containerNode.innerHTML;
19953 },
a089699c 19954
1354d172
AD
19955 cancel: function(){
19956 // summary:
19957 // Cancels an in-flight download of content
19958 if(this._xhrDfd && (this._xhrDfd.fired == -1)){
19959 this._xhrDfd.cancel();
a089699c 19960 }
1354d172
AD
19961 delete this._xhrDfd; // garbage collect
19962
19963 this.onLoadDeferred = null;
a089699c
AD
19964 },
19965
1354d172
AD
19966 uninitialize: function(){
19967 if(this._beingDestroyed){
19968 this.cancel();
a089699c
AD
19969 }
19970 this.inherited(arguments);
19971 },
19972
1354d172 19973 destroyRecursive: function(/*Boolean*/ preserveDom){
a089699c 19974 // summary:
1354d172 19975 // Destroy the ContentPane and its contents
a089699c 19976
1354d172
AD
19977 // if we have multiple controllers destroying us, bail after the first
19978 if(this._beingDestroyed){
19979 return;
a089699c 19980 }
1354d172 19981 this.inherited(arguments);
a089699c
AD
19982 },
19983
1354d172 19984 _onShow: function(){
a089699c 19985 // summary:
1354d172
AD
19986 // Called when the ContentPane is made visible
19987 // description:
19988 // For a plain ContentPane, this is called on initialization, from startup().
19989 // If the ContentPane is a hidden pane of a TabContainer etc., then it's
19990 // called whenever the pane is made visible.
19991 //
19992 // Does necessary processing, including href download and layout/resize of
19993 // child widget(s)
a089699c 19994
1354d172 19995 this.inherited(arguments);
a089699c 19996
1354d172
AD
19997 if(this.href){
19998 if(!this._xhrDfd && // if there's an href that isn't already being loaded
19999 (!this.isLoaded || this._hrefChanged || this.refreshOnShow)
20000 ){
20001 return this.refresh(); // If child has an href, promise that fires when the load is complete
20002 }
20003 }
a089699c
AD
20004 },
20005
1354d172 20006 refresh: function(){
a089699c 20007 // summary:
1354d172
AD
20008 // [Re]download contents of href and display
20009 // description:
20010 // 1. cancels any currently in-flight requests
20011 // 2. posts "loading..." message
20012 // 3. sends XHR to download new data
a089699c 20013
1354d172
AD
20014 // Cancel possible prior in-flight request
20015 this.cancel();
81bea17a 20016
1354d172
AD
20017 this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
20018 this.onLoadDeferred.addCallback(lang.hitch(this, "onLoad"));
20019 this._load();
20020 return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
a089699c
AD
20021 },
20022
1354d172 20023 _load: function(){
a089699c 20024 // summary:
1354d172 20025 // Load/reload the href specified in this.href
a089699c 20026
1354d172
AD
20027 // display loading message
20028 this._setContent(this.onDownloadStart(), true);
a089699c 20029
1354d172
AD
20030 var self = this;
20031 var getArgs = {
20032 preventCache: (this.preventCache || this.refreshOnShow),
20033 url: this.href,
20034 handleAs: "text"
20035 };
20036 if(lang.isObject(this.ioArgs)){
20037 lang.mixin(getArgs, this.ioArgs);
20038 }
a089699c 20039
1354d172 20040 var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs));
a089699c 20041
1354d172
AD
20042 hand.addCallback(function(html){
20043 try{
20044 self._isDownloaded = true;
20045 self._setContent(html, false);
20046 self.onDownloadEnd();
20047 }catch(err){
20048 self._onError('Content', err); // onContentError
20049 }
20050 delete self._xhrDfd;
20051 return html;
20052 });
a089699c 20053
1354d172
AD
20054 hand.addErrback(function(err){
20055 if(!hand.canceled){
20056 // show error message in the pane
20057 self._onError('Download', err); // onDownloadError
20058 }
20059 delete self._xhrDfd;
20060 return err;
20061 });
a089699c 20062
1354d172
AD
20063 // Remove flag saying that a load is needed
20064 delete this._hrefChanged;
a089699c
AD
20065 },
20066
1354d172
AD
20067 _onLoadHandler: function(data){
20068 // summary:
20069 // This is called whenever new content is being loaded
20070 this._set("isLoaded", true);
20071 try{
20072 this.onLoadDeferred.callback(data);
20073 }catch(e){
20074 console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
a089699c 20075 }
1354d172 20076 },
a089699c 20077
1354d172
AD
20078 _onUnloadHandler: function(){
20079 // summary:
20080 // This is called whenever the content is being unloaded
20081 this._set("isLoaded", false);
20082 try{
20083 this.onUnload();
20084 }catch(e){
20085 console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
81bea17a
AD
20086 }
20087 },
20088
1354d172
AD
20089 destroyDescendants: function(/*Boolean*/ preserveDom){
20090 // summary:
20091 // Destroy all the widgets inside the ContentPane and empty containerNode
81bea17a 20092
1354d172
AD
20093 // Make sure we call onUnload (but only when the ContentPane has real content)
20094 if(this.isLoaded){
20095 this._onUnloadHandler();
20096 }
a089699c 20097
1354d172
AD
20098 // Even if this.isLoaded == false there might still be a "Loading..." message
20099 // to erase, so continue...
a089699c 20100
1354d172
AD
20101 // For historical reasons we need to delete all widgets under this.containerNode,
20102 // even ones that the user has created manually.
20103 var setter = this._contentSetter;
20104 array.forEach(this.getChildren(), function(widget){
20105 if(widget.destroyRecursive){
20106 widget.destroyRecursive(preserveDom);
a089699c 20107 }
1354d172
AD
20108 });
20109 if(setter){
20110 // Most of the widgets in setter.parseResults have already been destroyed, but
20111 // things like Menu that have been moved to <body> haven't yet
20112 array.forEach(setter.parseResults, function(widget){
20113 if(widget.destroyRecursive && widget.domNode && widget.domNode.parentNode == win.body()){
20114 widget.destroyRecursive(preserveDom);
20115 }
20116 });
20117 delete setter.parseResults;
a089699c 20118 }
a089699c 20119
1354d172
AD
20120 // And then clear away all the DOM nodes
20121 if(!preserveDom){
20122 html._emptyNode(this.containerNode);
20123 }
a089699c 20124
1354d172
AD
20125 // Delete any state information we have about current contents
20126 delete this._singleChild;
a089699c
AD
20127 },
20128
1354d172 20129 _setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
a089699c 20130 // summary:
1354d172 20131 // Insert the content into the container node
a089699c 20132
1354d172
AD
20133 // first get rid of child widgets
20134 this.destroyDescendants();
a089699c 20135
1354d172
AD
20136 // html.set will take care of the rest of the details
20137 // we provide an override for the error handling to ensure the widget gets the errors
20138 // configure the setter instance with only the relevant widget instance properties
20139 // NOTE: unless we hook into attr, or provide property setters for each property,
20140 // we need to re-configure the ContentSetter with each use
20141 var setter = this._contentSetter;
20142 if(! (setter && setter instanceof html._ContentSetter)){
20143 setter = this._contentSetter = new html._ContentSetter({
20144 node: this.containerNode,
20145 _onError: lang.hitch(this, this._onError),
20146 onContentError: lang.hitch(this, function(e){
20147 // fires if a domfault occurs when we are appending this.errorMessage
20148 // like for instance if domNode is a UL and we try append a DIV
20149 var errMess = this.onContentError(e);
20150 try{
20151 this.containerNode.innerHTML = errMess;
20152 }catch(e){
20153 console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
20154 }
20155 })/*,
20156 _onError */
20157 });
20158 }
a089699c 20159
1354d172
AD
20160 var setterParams = lang.mixin({
20161 cleanContent: this.cleanContent,
20162 extractContent: this.extractContent,
20163 parseContent: !cont.domNode && this.parseOnLoad,
20164 parserScope: this.parserScope,
20165 startup: false,
20166 dir: this.dir,
20167 lang: this.lang,
20168 textDir: this.textDir
20169 }, this._contentSetterParams || {});
20170
20171 setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
20172
20173 // setter params must be pulled afresh from the ContentPane each time
20174 delete this._contentSetterParams;
20175
20176 if(this.doLayout){
20177 this._checkIfSingleChild();
a089699c 20178 }
a089699c 20179
1354d172
AD
20180 if(!isFakeContent){
20181 if(this._started){
20182 // Startup each top level child widget (and they will start their children, recursively)
20183 delete this._started;
20184 this.startup();
a089699c 20185
1354d172
AD
20186 // Call resize() on each of my child layout widgets,
20187 // or resize() on my single child layout widget...
20188 // either now (if I'm currently visible) or when I become visible
20189 this._scheduleLayout();
a089699c 20190 }
1354d172
AD
20191
20192 this._onLoadHandler(cont);
a089699c
AD
20193 }
20194 },
20195
1354d172
AD
20196 _onError: function(type, err, consoleText){
20197 this.onLoadDeferred.errback(err);
a089699c 20198
1354d172
AD
20199 // shows user the string that is returned by on[type]Error
20200 // override on[type]Error and return your own string to customize
20201 var errText = this['on' + type + 'Error'].call(this, err);
20202 if(consoleText){
20203 console.error(consoleText, err);
20204 }else if(errText){// a empty string won't change current content
20205 this._setContent(errText, true);
a089699c
AD
20206 }
20207 },
20208
1354d172
AD
20209 // EVENT's, should be overide-able
20210 onLoad: function(/*===== data =====*/){
a089699c 20211 // summary:
1354d172 20212 // Event hook, is called after everything is loaded and widgetified
a089699c 20213 // tags:
1354d172 20214 // callback
a089699c
AD
20215 },
20216
1354d172 20217 onUnload: function(){
a089699c 20218 // summary:
1354d172 20219 // Event hook, is called before old content is cleared
a089699c 20220 // tags:
1354d172
AD
20221 // callback
20222 },
a089699c 20223
1354d172
AD
20224 onDownloadStart: function(){
20225 // summary:
20226 // Called before download starts.
20227 // description:
20228 // The string returned by this function will be the html
20229 // that tells the user we are loading something.
20230 // Override with your own function if you want to change text.
20231 // tags:
20232 // extension
20233 return this.loadingMessage;
20234 },
a089699c 20235
1354d172
AD
20236 onContentError: function(/*Error*/ /*===== error =====*/){
20237 // summary:
20238 // Called on DOM faults, require faults etc. in content.
20239 //
20240 // In order to display an error message in the pane, return
20241 // the error message from this method, as an HTML string.
20242 //
20243 // By default (if this method is not overriden), it returns
20244 // nothing, so the error message is just printed to the console.
20245 // tags:
20246 // extension
20247 },
a089699c 20248
1354d172
AD
20249 onDownloadError: function(/*Error*/ /*===== error =====*/){
20250 // summary:
20251 // Called when download error occurs.
20252 //
20253 // In order to display an error message in the pane, return
20254 // the error message from this method, as an HTML string.
20255 //
20256 // Default behavior (if this method is not overriden) is to display
20257 // the error message inside the pane.
20258 // tags:
20259 // extension
20260 return this.errorMessage;
20261 },
a089699c 20262
1354d172
AD
20263 onDownloadEnd: function(){
20264 // summary:
20265 // Called when download is finished.
20266 // tags:
20267 // callback
20268 }
20269});
a089699c 20270
1354d172 20271});
a089699c 20272
1354d172
AD
20273},
20274'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
20275'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",
20276'dijit/layout/utils':function(){
20277define("dijit/layout/utils", [
20278 "dojo/_base/array", // array.filter array.forEach
20279 "dojo/dom-class", // domClass.add domClass.remove
20280 "dojo/dom-geometry", // domGeometry.marginBox
20281 "dojo/dom-style", // domStyle.getComputedStyle
20282 "dojo/_base/lang", // lang.mixin
20283 ".." // for exporting symbols to dijit, remove in 2.0
20284], function(array, domClass, domGeometry, domStyle, lang, dijit){
20285
20286 // module:
20287 // dijit/layout/utils
20288 // summary:
20289 // marginBox2contentBox() and layoutChildren()
a089699c 20290
1354d172
AD
20291 var layout = lang.getObject("layout", true, dijit);
20292 /*===== layout = dijit.layout =====*/
a089699c 20293
1354d172
AD
20294 layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
20295 // summary:
20296 // Given the margin-box size of a node, return its content box size.
20297 // Functions like domGeometry.contentBox() but is more reliable since it doesn't have
20298 // to wait for the browser to compute sizes.
20299 var cs = domStyle.getComputedStyle(node);
20300 var me = domGeometry.getMarginExtents(node, cs);
20301 var pb = domGeometry.getPadBorderExtents(node, cs);
20302 return {
20303 l: domStyle.toPixelValue(node, cs.paddingLeft),
20304 t: domStyle.toPixelValue(node, cs.paddingTop),
20305 w: mb.w - (me.w + pb.w),
20306 h: mb.h - (me.h + pb.h)
20307 };
20308 };
a089699c 20309
1354d172
AD
20310 function capitalize(word){
20311 return word.substring(0,1).toUpperCase() + word.substring(1);
a089699c 20312 }
a089699c 20313
1354d172
AD
20314 function size(widget, dim){
20315 // size the child
20316 var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
a089699c 20317
1354d172
AD
20318 // record child's size
20319 if(newSize){
20320 // if the child returned it's new size then use that
20321 lang.mixin(widget, newSize);
20322 }else{
20323 // otherwise, call getMarginBox(), but favor our own numbers when we have them.
20324 // the browser lies sometimes
20325 lang.mixin(widget, domGeometry.getMarginBox(widget.domNode));
20326 lang.mixin(widget, dim);
a089699c
AD
20327 }
20328 }
a089699c 20329
1354d172
AD
20330 layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
20331 /*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
20332 // summary:
20333 // Layout a bunch of child dom nodes within a parent dom node
20334 // container:
20335 // parent node
20336 // dim:
20337 // {l, t, w, h} object specifying dimensions of container into which to place children
20338 // children:
20339 // an array of Widgets or at least objects containing:
20340 // * domNode: pointer to DOM node to position
20341 // * region or layoutAlign: position to place DOM node
20342 // * resize(): (optional) method to set size of node
20343 // * id: (optional) Id of widgets, referenced from resize object, below.
20344 // changedRegionId:
20345 // If specified, the slider for the region with the specified id has been dragged, and thus
20346 // the region's height or width should be adjusted according to changedRegionSize
20347 // changedRegionSize:
20348 // See changedRegionId.
a089699c 20349
1354d172
AD
20350 // copy dim because we are going to modify it
20351 dim = lang.mixin({}, dim);
a089699c 20352
1354d172 20353 domClass.add(container, "dijitLayoutContainer");
a089699c 20354
1354d172
AD
20355 // Move "client" elements to the end of the array for layout. a11y dictates that the author
20356 // needs to be able to put them in the document in tab-order, but this algorithm requires that
20357 // client be last. TODO: move these lines to LayoutContainer? Unneeded other places I think.
20358 children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
20359 .concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
a089699c 20360
1354d172
AD
20361 // set positions/sizes
20362 array.forEach(children, function(child){
20363 var elm = child.domNode,
20364 pos = (child.region || child.layoutAlign);
20365 if(!pos){
20366 throw new Error("No region setting for " + child.id)
20367 }
a089699c 20368
1354d172
AD
20369 // set elem to upper left corner of unused space; may move it later
20370 var elmStyle = elm.style;
20371 elmStyle.left = dim.l+"px";
20372 elmStyle.top = dim.t+"px";
20373 elmStyle.position = "absolute";
a089699c 20374
1354d172 20375 domClass.add(elm, "dijitAlign" + capitalize(pos));
a089699c 20376
1354d172
AD
20377 // Size adjustments to make to this child widget
20378 var sizeSetting = {};
a089699c 20379
1354d172
AD
20380 // Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
20381 // panes and width adjustment for left/right align panes.
20382 if(changedRegionId && changedRegionId == child.id){
20383 sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
20384 }
a089699c 20385
1354d172
AD
20386 // set size && adjust record of remaining space.
20387 // note that setting the width of a <div> may affect its height.
20388 if(pos == "top" || pos == "bottom"){
20389 sizeSetting.w = dim.w;
20390 size(child, sizeSetting);
20391 dim.h -= child.h;
20392 if(pos == "top"){
20393 dim.t += child.h;
20394 }else{
20395 elmStyle.top = dim.t + dim.h + "px";
20396 }
20397 }else if(pos == "left" || pos == "right"){
20398 sizeSetting.h = dim.h;
20399 size(child, sizeSetting);
20400 dim.w -= child.w;
20401 if(pos == "left"){
20402 dim.l += child.w;
20403 }else{
20404 elmStyle.left = dim.l + dim.w + "px";
20405 }
20406 }else if(pos == "client" || pos == "center"){
20407 size(child, dim);
20408 }
20409 });
20410 };
a089699c 20411
a089699c 20412
1354d172
AD
20413 return {
20414 marginBox2contentBox: layout.marginBox2contentBox,
20415 layoutChildren: layout.layoutChildren
20416 };
20417});
a089699c 20418
1354d172
AD
20419},
20420'dijit/_Contained':function(){
20421define("dijit/_Contained", [
20422 "dojo/_base/declare", // declare
20423 "./registry" // registry.getEnclosingWidget(), registry.byNode()
20424], function(declare, registry){
20425
20426 // module:
20427 // dijit/_Contained
20428 // summary:
20429 // Mixin for widgets that are children of a container widget
81bea17a 20430
1354d172
AD
20431 return declare("dijit._Contained", null, {
20432 // summary:
20433 // Mixin for widgets that are children of a container widget
20434 //
20435 // example:
20436 // | // make a basic custom widget that knows about it's parents
20437 // | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
81bea17a 20438
1354d172
AD
20439 _getSibling: function(/*String*/ which){
20440 // summary:
20441 // Returns next or previous sibling
20442 // which:
20443 // Either "next" or "previous"
20444 // tags:
20445 // private
20446 var node = this.domNode;
20447 do{
20448 node = node[which+"Sibling"];
20449 }while(node && node.nodeType != 1);
20450 return node && registry.byNode(node); // dijit._Widget
20451 },
a089699c 20452
1354d172
AD
20453 getPreviousSibling: function(){
20454 // summary:
20455 // Returns null if this is the first child of the parent,
20456 // otherwise returns the next element sibling to the "left".
a089699c 20457
1354d172
AD
20458 return this._getSibling("previous"); // dijit._Widget
20459 },
a089699c 20460
1354d172
AD
20461 getNextSibling: function(){
20462 // summary:
20463 // Returns null if this is the last child of the parent,
20464 // otherwise returns the next element sibling to the "right".
a089699c 20465
1354d172
AD
20466 return this._getSibling("next"); // dijit._Widget
20467 },
81bea17a 20468
1354d172
AD
20469 getIndexInParent: function(){
20470 // summary:
20471 // Returns the index of this widget within its container parent.
20472 // It returns -1 if the parent does not exist, or if the parent
20473 // is not a dijit._Container
81bea17a 20474
1354d172
AD
20475 var p = this.getParent();
20476 if(!p || !p.getIndexOfChild){
20477 return -1; // int
20478 }
20479 return p.getIndexOfChild(this); // int
20480 }
20481 });
20482});
81bea17a 20483
1354d172
AD
20484},
20485'dijit/_KeyNavContainer':function(){
20486define("dijit/_KeyNavContainer", [
20487 "dojo/_base/kernel", // kernel.deprecated
20488 "./_Container",
20489 "./_FocusMixin",
20490 "dojo/_base/array", // array.forEach
20491 "dojo/keys", // keys.END keys.HOME
20492 "dojo/_base/declare", // declare
20493 "dojo/_base/event", // event.stop
20494 "dojo/dom-attr", // domAttr.set
20495 "dojo/_base/lang" // lang.hitch
20496], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
a089699c 20497
1354d172
AD
20498/*=====
20499 var _FocusMixin = dijit._FocusMixin;
20500 var _Container = dijit._Container;
20501=====*/
a089699c 20502
1354d172
AD
20503 // module:
20504 // dijit/_KeyNavContainer
20505 // summary:
20506 // A _Container with keyboard navigation of its children.
a089699c 20507
1354d172 20508 return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
a089699c 20509
1354d172
AD
20510 // summary:
20511 // A _Container with keyboard navigation of its children.
20512 // description:
20513 // To use this mixin, call connectKeyNavHandlers() in
20514 // postCreate().
20515 // It provides normalized keyboard and focusing code for Container
20516 // widgets.
a089699c 20517
1354d172
AD
20518/*=====
20519 // focusedChild: [protected] Widget
20520 // The currently focused child widget, or null if there isn't one
20521 focusedChild: null,
20522=====*/
a089699c 20523
1354d172
AD
20524 // tabIndex: Integer
20525 // Tab index of the container; same as HTML tabIndex attribute.
20526 // Note then when user tabs into the container, focus is immediately
20527 // moved to the first item in the container.
20528 tabIndex: "0",
a089699c 20529
1354d172
AD
20530 connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
20531 // summary:
20532 // Call in postCreate() to attach the keyboard handlers
20533 // to the container.
20534 // preKeyCodes: keys[]
20535 // Key codes for navigating to the previous child.
20536 // nextKeyCodes: keys[]
20537 // Key codes for navigating to the next child.
20538 // tags:
20539 // protected
a089699c 20540
1354d172 20541 // TODO: call this automatically from my own postCreate()
a089699c 20542
1354d172
AD
20543 var keyCodes = (this._keyNavCodes = {});
20544 var prev = lang.hitch(this, "focusPrev");
20545 var next = lang.hitch(this, "focusNext");
20546 array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
20547 array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
20548 keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
20549 keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
20550 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
20551 this.connect(this.domNode, "onfocus", "_onContainerFocus");
20552 },
a089699c 20553
1354d172
AD
20554 startupKeyNavChildren: function(){
20555 kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
20556 },
a089699c 20557
1354d172
AD
20558 startup: function(){
20559 this.inherited(arguments);
20560 array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
20561 },
a089699c 20562
1354d172 20563 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
a089699c 20564 this.inherited(arguments);
1354d172 20565 this._startupChild(widget);
a089699c
AD
20566 },
20567
1354d172 20568 focus: function(){
a089699c 20569 // summary:
1354d172
AD
20570 // Default focus() implementation: focus the first child.
20571 this.focusFirstChild();
20572 },
a089699c 20573
1354d172
AD
20574 focusFirstChild: function(){
20575 // summary:
20576 // Focus the first focusable child in the container.
20577 // tags:
20578 // protected
20579 this.focusChild(this._getFirstFocusableChild());
a089699c
AD
20580 },
20581
1354d172 20582 focusLastChild: function(){
a089699c 20583 // summary:
1354d172
AD
20584 // Focus the last focusable child in the container.
20585 // tags:
20586 // protected
20587 this.focusChild(this._getLastFocusableChild());
20588 },
a089699c 20589
1354d172
AD
20590 focusNext: function(){
20591 // summary:
20592 // Focus the next widget
20593 // tags:
20594 // protected
20595 this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
20596 },
a089699c 20597
1354d172
AD
20598 focusPrev: function(){
20599 // summary:
20600 // Focus the last focusable node in the previous widget
20601 // (ex: go to the ComboButton icon section rather than button section)
20602 // tags:
20603 // protected
20604 this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
20605 },
a089699c 20606
1354d172
AD
20607 focusChild: function(/*dijit._Widget*/ widget, /*Boolean*/ last){
20608 // summary:
20609 // Focus specified child widget.
20610 // widget:
20611 // Reference to container's child widget
20612 // last:
20613 // If true and if widget has multiple focusable nodes, focus the
20614 // last one instead of the first one
20615 // tags:
20616 // protected
a089699c 20617
1354d172 20618 if(!widget){ return; }
a089699c 20619
1354d172
AD
20620 if(this.focusedChild && widget !== this.focusedChild){
20621 this._onChildBlur(this.focusedChild); // used by _MenuBase
20622 }
20623 widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
20624 widget.focus(last ? "end" : "start");
20625 this._set("focusedChild", widget);
20626 },
a089699c 20627
1354d172
AD
20628 _startupChild: function(/*dijit._Widget*/ widget){
20629 // summary:
20630 // Setup for each child widget
20631 // description:
20632 // Sets tabIndex=-1 on each child, so that the tab key will
20633 // leave the container rather than visiting each child.
20634 // tags:
20635 // private
a089699c 20636
1354d172 20637 widget.set("tabIndex", "-1");
a089699c 20638
1354d172
AD
20639 this.connect(widget, "_onFocus", function(){
20640 // Set valid tabIndex so tabbing away from widget goes to right place, see #10272
20641 widget.set("tabIndex", this.tabIndex);
20642 });
20643 this.connect(widget, "_onBlur", function(){
20644 widget.set("tabIndex", "-1");
20645 });
20646 },
81bea17a 20647
1354d172
AD
20648 _onContainerFocus: function(evt){
20649 // summary:
20650 // Handler for when the container gets focus
20651 // description:
20652 // Initially the container itself has a tabIndex, but when it gets
20653 // focus, switch focus to first child...
20654 // tags:
20655 // private
a089699c 20656
1354d172
AD
20657 // Note that we can't use _onFocus() because switching focus from the
20658 // _onFocus() handler confuses the focus.js code
20659 // (because it causes _onFocusNode() to be called recursively)
20660 // Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
a089699c 20661
1354d172
AD
20662 // Ignore spurious focus events:
20663 // 1. focus on a child widget bubbles on FF
20664 // 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
20665 if(evt.target !== this.domNode || this.focusedChild){ return; }
a089699c 20666
1354d172 20667 this.focusFirstChild();
a089699c 20668
1354d172
AD
20669 // and then set the container's tabIndex to -1,
20670 // (don't remove as that breaks Safari 4)
20671 // so that tab or shift-tab will go to the fields after/before
20672 // the container, rather than the container itself
20673 domAttr.set(this.domNode, "tabIndex", "-1");
20674 },
a089699c 20675
1354d172
AD
20676 _onBlur: function(evt){
20677 // When focus is moved away the container, and its descendant (popup) widgets,
20678 // then restore the container's tabIndex so that user can tab to it again.
20679 // Note that using _onBlur() so that this doesn't happen when focus is shifted
20680 // to one of my child widgets (typically a popup)
20681 if(this.tabIndex){
20682 domAttr.set(this.domNode, "tabIndex", this.tabIndex);
20683 }
20684 this.focusedChild = null;
20685 this.inherited(arguments);
20686 },
81bea17a 20687
1354d172
AD
20688 _onContainerKeypress: function(evt){
20689 // summary:
20690 // When a key is pressed, if it's an arrow key etc. then
20691 // it's handled here.
20692 // tags:
20693 // private
20694 if(evt.ctrlKey || evt.altKey){ return; }
20695 var func = this._keyNavCodes[evt.charOrCode];
20696 if(func){
20697 func();
20698 event.stop(evt);
20699 }
20700 },
a089699c 20701
1354d172
AD
20702 _onChildBlur: function(/*dijit._Widget*/ /*===== widget =====*/){
20703 // summary:
20704 // Called when focus leaves a child widget to go
20705 // to a sibling widget.
20706 // Used by MenuBase.js (TODO: move code there)
20707 // tags:
20708 // protected
20709 },
a089699c 20710
1354d172
AD
20711 _getFirstFocusableChild: function(){
20712 // summary:
20713 // Returns first child that can be focused
20714 return this._getNextFocusableChild(null, 1); // dijit._Widget
20715 },
a089699c 20716
1354d172
AD
20717 _getLastFocusableChild: function(){
20718 // summary:
20719 // Returns last child that can be focused
20720 return this._getNextFocusableChild(null, -1); // dijit._Widget
20721 },
a089699c 20722
1354d172
AD
20723 _getNextFocusableChild: function(child, dir){
20724 // summary:
20725 // Returns the next or previous focusable child, compared
20726 // to "child"
20727 // child: Widget
20728 // The current widget
20729 // dir: Integer
20730 // * 1 = after
20731 // * -1 = before
20732 if(child){
20733 child = this._getSiblingOfChild(child, dir);
20734 }
20735 var children = this.getChildren();
20736 for(var i=0; i < children.length; i++){
20737 if(!child){
20738 child = children[(dir>0) ? 0 : (children.length-1)];
a089699c 20739 }
1354d172
AD
20740 if(child.isFocusable()){
20741 return child; // dijit._Widget
20742 }
20743 child = this._getSiblingOfChild(child, dir);
20744 }
20745 // no focusable child found
20746 return null; // dijit._Widget
a089699c 20747 }
1354d172
AD
20748 });
20749});
a089699c 20750
1354d172
AD
20751},
20752'dijit/form/DataList':function(){
20753define("dijit/form/DataList", [
20754 "dojo/_base/declare", // declare
20755 "dojo/dom", // dom.byId
20756 "dojo/_base/lang", // lang.trim
20757 "dojo/query", // query
20758 "dojo/store/Memory", // dojo.store.Memory
20759 "../registry" // registry.add registry.remove
20760], function(declare, dom, lang, query, MemoryStore, registry){
20761
20762 // module:
20763 // dijit/form/DataList
20764 // summary:
20765 // Inefficient but small data store specialized for inlined data via OPTION tags
a089699c 20766
1354d172
AD
20767 function toItem(/*DOMNode*/ option){
20768 // summary:
20769 // Convert <option> node to hash
20770 return {
20771 id: option.value,
20772 value: option.value,
20773 name: lang.trim(option.innerText || option.textContent || '')
20774 };
20775 }
a089699c 20776
1354d172
AD
20777 return declare("dijit.form.DataList", MemoryStore, {
20778 // summary:
20779 // Inefficient but small data store specialized for inlined data via OPTION tags
20780 //
20781 // description:
20782 // Provides a store for inlined data like:
20783 //
20784 // | <datalist>
20785 // | <option value="AL">Alabama</option>
20786 // | ...
a089699c 20787
1354d172
AD
20788 constructor: function(/*Object?*/ params, /*DomNode|String*/ srcNodeRef){
20789 // store pointer to original DOM tree
20790 this.domNode = dom.byId(srcNodeRef);
a089699c 20791
1354d172
AD
20792 lang.mixin(this, params);
20793 if(this.id){
20794 registry.add(this); // add to registry so it can be easily found by id
20795 }
20796 this.domNode.style.display = "none";
a089699c 20797
1354d172
AD
20798 this.inherited(arguments, [{
20799 data: query("option", this.domNode).map(toItem)
20800 }]);
20801 },
a089699c 20802
1354d172
AD
20803 destroy: function(){
20804 registry.remove(this.id);
20805 },
a089699c 20806
1354d172
AD
20807 fetchSelectedItem: function(){
20808 // summary:
20809 // Get the option marked as selected, like `<option selected>`.
20810 // Not part of dojo.data API.
20811 var option = query("> option[selected]", this.domNode)[0] || query("> option", this.domNode)[0];
20812 return option && toItem(option);
20813 }
20814 });
20815});
a089699c 20816
1354d172
AD
20817},
20818'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<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
20819'dijit/form/CheckBox':function(){
20820require({cache:{
20821'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n"}});
20822define("dijit/form/CheckBox", [
20823 "require",
20824 "dojo/_base/declare", // declare
20825 "dojo/dom-attr", // domAttr.set
20826 "dojo/_base/kernel",
20827 "dojo/query", // query
20828 "dojo/ready",
20829 "./ToggleButton",
20830 "./_CheckBoxMixin",
20831 "dojo/text!./templates/CheckBox.html",
20832 "dojo/NodeList-dom" // NodeList.addClass/removeClass
20833], function(require, declare, domAttr, kernel, query, ready, ToggleButton, _CheckBoxMixin, template){
a089699c 20834
1354d172
AD
20835/*=====
20836 var ToggleButton = dijit.form.ToggleButton;
20837 var _CheckBoxMixin = dijit.form._CheckBoxMixin;
20838=====*/
a089699c 20839
1354d172
AD
20840 // module:
20841 // dijit/form/CheckBox
20842 // summary:
20843 // Checkbox widget
a089699c 20844
1354d172
AD
20845 // Back compat w/1.6, remove for 2.0
20846 if(!kernel.isAsync){
20847 ready(0, function(){
20848 var requires = ["dijit/form/RadioButton"];
20849 require(requires); // use indirection so modules not rolled into a build
20850 });
20851 }
a089699c 20852
1354d172
AD
20853 return declare("dijit.form.CheckBox", [ToggleButton, _CheckBoxMixin], {
20854 // summary:
20855 // Same as an HTML checkbox, but with fancy styling.
20856 //
20857 // description:
20858 // User interacts with real html inputs.
20859 // On onclick (which occurs by mouse click, space-bar, or
20860 // using the arrow keys to switch the selected radio button),
20861 // we update the state of the checkbox/radio.
20862 //
20863 // There are two modes:
20864 // 1. High contrast mode
20865 // 2. Normal mode
20866 //
20867 // In case 1, the regular html inputs are shown and used by the user.
20868 // In case 2, the regular html inputs are invisible but still used by
20869 // the user. They are turned quasi-invisible and overlay the background-image.
a089699c 20870
1354d172 20871 templateString: template,
a089699c 20872
1354d172 20873 baseClass: "dijitCheckBox",
a089699c 20874
1354d172
AD
20875 _setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
20876 // summary:
20877 // Handler for value= attribute to constructor, and also calls to
20878 // set('value', val).
20879 // description:
20880 // During initialization, just saves as attribute to the <input type=checkbox>.
20881 //
20882 // After initialization,
20883 // when passed a boolean, controls whether or not the CheckBox is checked.
20884 // If passed a string, changes the value attribute of the CheckBox (the one
20885 // specified as "value" when the CheckBox was constructed (ex: <input
20886 // data-dojo-type="dijit.CheckBox" value="chicken">)
20887 // widget.set('value', string) will check the checkbox and change the value to the
20888 // specified string
20889 // widget.set('value', boolean) will change the checked state.
20890 if(typeof newValue == "string"){
20891 this._set("value", newValue);
20892 domAttr.set(this.focusNode, 'value', newValue);
20893 newValue = true;
a089699c 20894 }
1354d172
AD
20895 if(this._created){
20896 this.set('checked', newValue, priorityChange);
20897 }
20898 },
20899 _getValueAttr: function(){
20900 // summary:
20901 // Hook so get('value') works.
20902 // description:
20903 // If the CheckBox is checked, returns the value attribute.
20904 // Otherwise returns false.
20905 return (this.checked ? this.value : false);
20906 },
a089699c 20907
1354d172
AD
20908 // Override behavior from Button, since we don't have an iconNode
20909 _setIconClassAttr: null,
a089699c 20910
1354d172
AD
20911 postMixInProperties: function(){
20912 this.inherited(arguments);
a089699c 20913
1354d172
AD
20914 // Need to set initial checked state as part of template, so that form submit works.
20915 // domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
20916 // to <body>, see #8666
20917 this.checkedAttrSetting = this.checked ? "checked" : "";
20918 },
a089699c 20919
1354d172
AD
20920 _fillContent: function(){
20921 // Override Button::_fillContent() since it doesn't make sense for CheckBox,
20922 // since CheckBox doesn't even have a container
20923 },
a089699c 20924
1354d172
AD
20925 _onFocus: function(){
20926 if(this.id){
20927 query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
a089699c 20928 }
1354d172
AD
20929 this.inherited(arguments);
20930 },
a089699c 20931
1354d172
AD
20932 _onBlur: function(){
20933 if(this.id){
20934 query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
a089699c 20935 }
1354d172 20936 this.inherited(arguments);
a089699c 20937 }
1354d172
AD
20938 });
20939});
81bea17a 20940
1354d172
AD
20941},
20942'dijit/tree/_dndSelector':function(){
20943define("dijit/tree/_dndSelector", [
20944 "dojo/_base/array", // array.filter array.forEach array.map
20945 "dojo/_base/connect", // connect.isCopyKey
20946 "dojo/_base/declare", // declare
20947 "dojo/_base/lang", // lang.hitch
20948 "dojo/mouse", // mouse.isLeft
20949 "dojo/on",
20950 "dojo/touch",
20951 "dojo/_base/window", // win.global
20952 "./_dndContainer"
20953], function(array, connect, declare, lang, mouse, on, touch, win, _dndContainer){
20954
20955 // module:
20956 // dijit/tree/_dndSelector
20957 // summary:
20958 // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
20959 // It's based on `dojo.dnd.Selector`.
a089699c 20960
a089699c 20961
1354d172 20962 return declare("dijit.tree._dndSelector", _dndContainer, {
a089699c 20963 // summary:
1354d172
AD
20964 // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly.
20965 // It's based on `dojo.dnd.Selector`.
20966 // tags:
20967 // protected
a089699c 20968
1354d172
AD
20969 /*=====
20970 // selection: Hash<String, DomNode>
20971 // (id, DomNode) map for every TreeNode that's currently selected.
20972 // The DOMNode is the TreeNode.rowNode.
20973 selection: {},
20974 =====*/
a089699c 20975
1354d172
AD
20976 constructor: function(){
20977 // summary:
20978 // Initialization
20979 // tags:
20980 // private
a089699c 20981
1354d172
AD
20982 this.selection={};
20983 this.anchor = null;
a089699c 20984
1354d172 20985 this.tree.domNode.setAttribute("aria-multiselect", !this.singular);
a089699c 20986
1354d172
AD
20987 this.events.push(
20988 on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
20989 on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
20990 on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
20991 );
20992 },
a089699c 20993
1354d172
AD
20994 // singular: Boolean
20995 // Allows selection of only one element, if true.
20996 // Tree hasn't been tested in singular=true mode, unclear if it works.
20997 singular: false,
a089699c 20998
1354d172
AD
20999 // methods
21000 getSelectedTreeNodes: function(){
21001 // summary:
21002 // Returns a list of selected node(s).
21003 // Used by dndSource on the start of a drag.
21004 // tags:
21005 // protected
21006 var nodes=[], sel = this.selection;
21007 for(var i in sel){
21008 nodes.push(sel[i]);
21009 }
21010 return nodes;
21011 },
a089699c 21012
1354d172
AD
21013 selectNone: function(){
21014 // summary:
21015 // Unselects all items
21016 // tags:
21017 // private
a089699c 21018
1354d172
AD
21019 this.setSelection([]);
21020 return this; // self
21021 },
a089699c 21022
1354d172
AD
21023 destroy: function(){
21024 // summary:
21025 // Prepares the object to be garbage-collected
21026 this.inherited(arguments);
21027 this.selection = this.anchor = null;
21028 },
21029 addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){
21030 // summary:
21031 // add node to current selection
21032 // node: Node
21033 // node to add
21034 // isAnchor: Boolean
21035 // Whether the node should become anchor.
a089699c 21036
1354d172
AD
21037 this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
21038 if(isAnchor){ this.anchor = node; }
21039 return node;
21040 },
21041 removeTreeNode: function(/*dijit._TreeNode*/node){
21042 // summary:
21043 // remove node from current selection
21044 // node: Node
21045 // node to remove
21046 this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
21047 return node;
21048 },
21049 isTreeNodeSelected: function(/*dijit._TreeNode*/node){
21050 // summary:
21051 // return true if node is currently selected
21052 // node: Node
21053 // the node to check whether it's in the current selection
a089699c 21054
1354d172
AD
21055 return node.id && !!this.selection[node.id];
21056 },
21057 setSelection: function(/*dijit._treeNode[]*/ newSelection){
21058 // summary:
21059 // set the list of selected nodes to be exactly newSelection. All changes to the
21060 // selection should be passed through this function, which ensures that derived
21061 // attributes are kept up to date. Anchor will be deleted if it has been removed
21062 // from the selection, but no new anchor will be added by this function.
21063 // newSelection: Node[]
21064 // list of tree nodes to make selected
21065 var oldSelection = this.getSelectedTreeNodes();
21066 array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
21067 node.setSelected(false);
21068 if(this.anchor == node){
21069 delete this.anchor;
21070 }
21071 delete this.selection[node.id];
21072 }));
21073 array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
21074 node.setSelected(true);
21075 this.selection[node.id] = node;
21076 }));
21077 this._updateSelectionProperties();
21078 },
21079 _setDifference: function(xs,ys){
21080 // summary:
21081 // Returns a copy of xs which lacks any objects
21082 // occurring in ys. Checks for membership by
21083 // modifying and then reading the object, so it will
21084 // not properly handle sets of numbers or strings.
a089699c 21085
1354d172
AD
21086 array.forEach(ys, function(y){ y.__exclude__ = true; });
21087 var ret = array.filter(xs, function(x){ return !x.__exclude__; });
a089699c 21088
1354d172
AD
21089 // clean up after ourselves.
21090 array.forEach(ys, function(y){ delete y['__exclude__'] });
21091 return ret;
21092 },
21093 _updateSelectionProperties: function(){
21094 // summary:
21095 // Update the following tree properties from the current selection:
21096 // path[s], selectedItem[s], selectedNode[s]
a089699c 21097
1354d172
AD
21098 var selected = this.getSelectedTreeNodes();
21099 var paths = [], nodes = [];
21100 array.forEach(selected, function(node){
21101 nodes.push(node);
21102 paths.push(node.getTreePath());
21103 });
21104 var items = array.map(nodes,function(node){ return node.item; });
21105 this.tree._set("paths", paths);
21106 this.tree._set("path", paths[0] || []);
21107 this.tree._set("selectedNodes", nodes);
21108 this.tree._set("selectedNode", nodes[0] || null);
21109 this.tree._set("selectedItems", items);
21110 this.tree._set("selectedItem", items[0] || null);
21111 },
21112 // mouse events
21113 onMouseDown: function(e){
21114 // summary:
21115 // Event processor for onmousedown/ontouchstart
21116 // e: Event
21117 // onmousedown/ontouchstart event
21118 // tags:
21119 // protected
a089699c 21120
1354d172
AD
21121 // ignore click on expando node
21122 if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
a089699c 21123
1354d172 21124 if(!mouse.isLeft(e)){ return; } // ignore right-click
a089699c 21125
1354d172 21126 e.preventDefault();
a089699c 21127
1354d172
AD
21128 var treeNode = this.current,
21129 copy = connect.isCopyKey(e), id = treeNode.id;
a089699c 21130
1354d172
AD
21131 // if shift key is not pressed, and the node is already in the selection,
21132 // delay deselection until onmouseup so in the case of DND, deselection
21133 // will be canceled by onmousemove.
21134 if(!this.singular && !e.shiftKey && this.selection[id]){
21135 this._doDeselect = true;
21136 return;
21137 }else{
21138 this._doDeselect = false;
21139 }
21140 this.userSelect(treeNode, copy, e.shiftKey);
21141 },
a089699c 21142
1354d172
AD
21143 onMouseUp: function(e){
21144 // summary:
21145 // Event processor for onmouseup/ontouchend
21146 // e: Event
21147 // onmouseup/ontouchend event
21148 // tags:
21149 // protected
a089699c 21150
1354d172
AD
21151 // _doDeselect is the flag to indicate that the user wants to either ctrl+click on
21152 // a already selected item (to deselect the item), or click on a not-yet selected item
21153 // (which should remove all current selection, and add the clicked item). This can not
21154 // be done in onMouseDown, because the user may start a drag after mousedown. By moving
21155 // the deselection logic here, the user can drags an already selected item.
21156 if(!this._doDeselect){ return; }
21157 this._doDeselect = false;
21158 this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
21159 },
21160 onMouseMove: function(/*===== e =====*/){
21161 // summary:
21162 // event processor for onmousemove/ontouchmove
21163 // e: Event
21164 // onmousemove/ontouchmove event
21165 this._doDeselect = false;
21166 },
a089699c 21167
1354d172
AD
21168 _compareNodes: function(n1, n2){
21169 if(n1 === n2){
21170 return 0;
21171 }
a089699c 21172
1354d172
AD
21173 if('sourceIndex' in document.documentElement){ //IE
21174 //TODO: does not yet work if n1 and/or n2 is a text node
21175 return n1.sourceIndex - n2.sourceIndex;
21176 }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
21177 return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
21178 }else if(document.createRange){ //Webkit
21179 var r1 = doc.createRange();
21180 r1.setStartBefore(n1);
a089699c 21181
1354d172
AD
21182 var r2 = doc.createRange();
21183 r2.setStartBefore(n2);
a089699c 21184
1354d172
AD
21185 return r1.compareBoundaryPoints(r1.END_TO_END, r2);
21186 }else{
21187 throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
21188 }
21189 },
a089699c 21190
1354d172
AD
21191 userSelect: function(node, multi, range){
21192 // summary:
21193 // Add or remove the given node from selection, responding
21194 // to a user action such as a click or keypress.
21195 // multi: Boolean
21196 // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
21197 // range: Boolean
21198 // Indicates whether this is meant to be a ranged action (e.g. shift-click)
21199 // tags:
21200 // protected
a089699c 21201
1354d172
AD
21202 if(this.singular){
21203 if(this.anchor == node && multi){
21204 this.selectNone();
21205 }else{
21206 this.setSelection([node]);
21207 this.anchor = node;
21208 }
21209 }else{
21210 if(range && this.anchor){
21211 var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
21212 begin, end, anchor = this.anchor;
a089699c 21213
1354d172
AD
21214 if(cr < 0){ //current is after anchor
21215 begin = anchor;
21216 end = node;
21217 }else{ //current is before anchor
21218 begin = node;
21219 end = anchor;
21220 }
21221 var nodes = [];
21222 //add everything betweeen begin and end inclusively
21223 while(begin != end){
21224 nodes.push(begin);
21225 begin = this.tree._getNextNode(begin);
21226 }
21227 nodes.push(end);
a089699c 21228
1354d172
AD
21229 this.setSelection(nodes);
21230 }else{
21231 if( this.selection[ node.id ] && multi ){
21232 this.removeTreeNode( node );
21233 }else if(multi){
21234 this.addTreeNode(node, true);
21235 }else{
21236 this.setSelection([node]);
21237 this.anchor = node;
21238 }
21239 }
21240 }
21241 },
a089699c 21242
1354d172
AD
21243 getItem: function(/*String*/ key){
21244 // summary:
21245 // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id).
21246 // Called by dojo.dnd.Source.checkAcceptance().
21247 // tags:
21248 // protected
a089699c 21249
1354d172
AD
21250 var widget = this.selection[key];
21251 return {
21252 data: widget,
21253 type: ["treeNode"]
21254 }; // dojo.dnd.Item
21255 },
a089699c 21256
1354d172
AD
21257 forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
21258 // summary:
21259 // Iterates over selected items;
21260 // see `dojo.dnd.Container.forInItems()` for details
21261 o = o || win.global;
21262 for(var id in this.selection){
21263 // console.log("selected item id: " + id);
21264 f.call(o, this.getItem(id), id, this);
21265 }
21266 }
21267 });
21268});
a089699c 21269
1354d172
AD
21270},
21271'dijit/_Container':function(){
21272define("dijit/_Container", [
21273 "dojo/_base/array", // array.forEach array.indexOf
21274 "dojo/_base/declare", // declare
21275 "dojo/dom-construct", // domConstruct.place
21276 "./registry" // registry.byNode()
21277], function(array, declare, domConstruct, registry){
21278
21279 // module:
21280 // dijit/_Container
21281 // summary:
21282 // Mixin for widgets that contain a set of widget children.
a089699c 21283
1354d172
AD
21284 return declare("dijit._Container", null, {
21285 // summary:
21286 // Mixin for widgets that contain a set of widget children.
21287 // description:
21288 // Use this mixin for widgets that needs to know about and
21289 // keep track of their widget children. Suitable for widgets like BorderContainer
21290 // and TabContainer which contain (only) a set of child widgets.
21291 //
21292 // It's not suitable for widgets like ContentPane
21293 // which contains mixed HTML (plain DOM nodes in addition to widgets),
21294 // and where contained widgets are not necessarily directly below
21295 // this.containerNode. In that case calls like addChild(node, position)
21296 // wouldn't make sense.
a089699c 21297
81bea17a 21298 buildRendering: function(){
a089699c 21299 this.inherited(arguments);
1354d172
AD
21300 if(!this.containerNode){
21301 // all widgets with descendants must set containerNode
21302 this.containerNode = this.domNode;
a089699c
AD
21303 }
21304 },
21305
1354d172
AD
21306 addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){
21307 // summary:
21308 // Makes the given widget a child of this widget.
21309 // description:
21310 // Inserts specified child widget's dom node as a child of this widget's
21311 // container node, and possibly does other processing (such as layout).
a089699c 21312
1354d172
AD
21313 var refNode = this.containerNode;
21314 if(insertIndex && typeof insertIndex == "number"){
21315 var children = this.getChildren();
21316 if(children && children.length >= insertIndex){
21317 refNode = children[insertIndex-1].domNode;
21318 insertIndex = "after";
a089699c 21319 }
a089699c 21320 }
1354d172 21321 domConstruct.place(widget.domNode, refNode, insertIndex);
a089699c 21322
1354d172
AD
21323 // If I've been started but the child widget hasn't been started,
21324 // start it now. Make sure to do this after widget has been
21325 // inserted into the DOM tree, so it can see that it's being controlled by me,
21326 // so it doesn't try to size itself.
21327 if(this._started && !widget._started){
21328 widget.startup();
a089699c
AD
21329 }
21330 },
21331
1354d172
AD
21332 removeChild: function(/*Widget|int*/ widget){
21333 // summary:
21334 // Removes the passed widget instance from this widget but does
21335 // not destroy it. You can also pass in an integer indicating
21336 // the index within the container to remove
a089699c 21337
1354d172
AD
21338 if(typeof widget == "number"){
21339 widget = this.getChildren()[widget];
81bea17a
AD
21340 }
21341
1354d172
AD
21342 if(widget){
21343 var node = widget.domNode;
21344 if(node && node.parentNode){
21345 node.parentNode.removeChild(node); // detach but don't destroy
81bea17a 21346 }
1354d172 21347 }
a089699c
AD
21348 },
21349
1354d172
AD
21350 hasChildren: function(){
21351 // summary:
21352 // Returns true if widget has children, i.e. if this.containerNode contains something.
21353 return this.getChildren().length > 0; // Boolean
81bea17a
AD
21354 },
21355
1354d172
AD
21356 _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){
21357 // summary:
21358 // Get the next or previous widget sibling of child
21359 // dir:
21360 // if 1, get the next sibling
21361 // if -1, get the previous sibling
21362 // tags:
21363 // private
21364 var node = child.domNode,
21365 which = (dir>0 ? "nextSibling" : "previousSibling");
21366 do{
21367 node = node[which];
21368 }while(node && (node.nodeType != 1 || !registry.byNode(node)));
21369 return node && registry.byNode(node); // dijit._Widget
81bea17a
AD
21370 },
21371
1354d172
AD
21372 getIndexOfChild: function(/*dijit._Widget*/ child){
21373 // summary:
21374 // Gets the index of the child in this container or -1 if not found
21375 return array.indexOf(this.getChildren(), child); // int
21376 }
21377 });
21378});
a089699c 21379
1354d172
AD
21380},
21381'dojo/data/ItemFileReadStore':function(){
21382define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
21383 "../Evented", "../_base/window", "./util/filter", "./util/simpleFetch", "../date/stamp"
21384], function(kernel, lang, declare, array, xhr, Evented, window, filterUtil, simpleFetch, dateStamp) {
21385 // module:
21386 // dojo/data/ItemFileReadStore
21387 // summary:
21388 // TODOC
81bea17a 21389
81bea17a 21390
1354d172
AD
21391var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
21392 // summary:
21393 // The ItemFileReadStore implements the dojo.data.api.Read API and reads
21394 // data from JSON files that have contents in this format --
21395 // { items: [
21396 // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
21397 // { name:'Fozzie Bear', wears:['hat', 'tie']},
21398 // { name:'Miss Piggy', pets:'Foo-Foo'}
21399 // ]}
21400 // Note that it can also contain an 'identifer' property that specified which attribute on the items
21401 // in the array of items that acts as the unique identifier for that item.
21402 //
21403 constructor: function(/* Object */ keywordParameters){
21404 // summary: constructor
21405 // keywordParameters: {url: String}
21406 // keywordParameters: {data: jsonObject}
21407 // keywordParameters: {typeMap: object)
21408 // The structure of the typeMap object is as follows:
21409 // {
21410 // type0: function || object,
21411 // type1: function || object,
21412 // ...
21413 // typeN: function || object
21414 // }
21415 // Where if it is a function, it is assumed to be an object constructor that takes the
21416 // value of _value as the initialization parameters. If it is an object, then it is assumed
21417 // to be an object of general form:
21418 // {
21419 // type: function, //constructor.
21420 // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
21421 // }
81bea17a 21422
1354d172
AD
21423 this._arrayOfAllItems = [];
21424 this._arrayOfTopLevelItems = [];
21425 this._loadFinished = false;
21426 this._jsonFileUrl = keywordParameters.url;
21427 this._ccUrl = keywordParameters.url;
21428 this.url = keywordParameters.url;
21429 this._jsonData = keywordParameters.data;
21430 this.data = null;
21431 this._datatypeMap = keywordParameters.typeMap || {};
21432 if(!this._datatypeMap['Date']){
21433 //If no default mapping for dates, then set this as default.
21434 //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
21435 //of generically representing dates.
21436 this._datatypeMap['Date'] = {
21437 type: Date,
21438 deserialize: function(value){
21439 return dateStamp.fromISOString(value);
21440 }
21441 };
21442 }
21443 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
21444 this._itemsByIdentity = null;
21445 this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
21446 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
21447 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
21448 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
21449 this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
21450 this._queuedFetches = [];
21451 if(keywordParameters.urlPreventCache !== undefined){
21452 this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
21453 }
21454 if(keywordParameters.hierarchical !== undefined){
21455 this.hierarchical = keywordParameters.hierarchical?true:false;
21456 }
21457 if(keywordParameters.clearOnClose){
21458 this.clearOnClose = true;
21459 }
21460 if("failOk" in keywordParameters){
21461 this.failOk = keywordParameters.failOk?true:false;
21462 }
21463 },
a089699c 21464
1354d172 21465 url: "", // use "" rather than undefined for the benefit of the parser (#3539)
a089699c 21466
1354d172
AD
21467 //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
21468 //when clearOnClose and close is used.
21469 _ccUrl: "",
81bea17a 21470
1354d172 21471 data: null, // define this so that the parser can populate it
a089699c 21472
1354d172 21473 typeMap: null, //Define so parser can populate.
81bea17a 21474
1354d172
AD
21475 //Parameter to allow users to specify if a close call should force a reload or not.
21476 //By default, it retains the old behavior of not clearing if close is called. But
21477 //if set true, the store will be reset to default state. Note that by doing this,
21478 //all item handles will become invalid and a new fetch must be issued.
21479 clearOnClose: false,
81bea17a 21480
1354d172
AD
21481 //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
21482 //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
21483 //Added for tracker: #6072
21484 urlPreventCache: false,
81bea17a 21485
1354d172
AD
21486 //Parameter for specifying that it is OK for the xhrGet call to fail silently.
21487 failOk: false,
81bea17a 21488
1354d172
AD
21489 //Parameter to indicate to process data from the url as hierarchical
21490 //(data items can contain other data items in js form). Default is true
21491 //for backwards compatibility. False means only root items are processed
21492 //as items, all child objects outside of type-mapped objects and those in
21493 //specific reference format, are left straight JS data objects.
21494 hierarchical: true,
a089699c 21495
1354d172
AD
21496 _assertIsItem: function(/* item */ item){
21497 // summary:
21498 // This function tests whether the item passed in is indeed an item in the store.
21499 // item:
21500 // The item to test for being contained by the store.
21501 if(!this.isItem(item)){
21502 throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
a089699c 21503 }
1354d172 21504 },
a089699c 21505
1354d172
AD
21506 _assertIsAttribute: function(/* attribute-name-string */ attribute){
21507 // summary:
21508 // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
21509 // attribute:
21510 // The attribute to test for being contained by the store.
21511 if(typeof attribute !== "string"){
21512 throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
21513 }
21514 },
81bea17a 21515
1354d172
AD
21516 getValue: function( /* item */ item,
21517 /* attribute-name-string */ attribute,
21518 /* value? */ defaultValue){
21519 // summary:
21520 // See dojo.data.api.Read.getValue()
21521 var values = this.getValues(item, attribute);
21522 return (values.length > 0)?values[0]:defaultValue; // mixed
21523 },
a089699c 21524
1354d172
AD
21525 getValues: function(/* item */ item,
21526 /* attribute-name-string */ attribute){
21527 // summary:
21528 // See dojo.data.api.Read.getValues()
81bea17a 21529
1354d172
AD
21530 this._assertIsItem(item);
21531 this._assertIsAttribute(attribute);
21532 // Clone it before returning. refs: #10474
21533 return (item[attribute] || []).slice(0); // Array
21534 },
a089699c 21535
1354d172
AD
21536 getAttributes: function(/* item */ item){
21537 // summary:
21538 // See dojo.data.api.Read.getAttributes()
21539 this._assertIsItem(item);
21540 var attributes = [];
21541 for(var key in item){
21542 // Save off only the real item attributes, not the special id marks for O(1) isItem.
21543 if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
21544 attributes.push(key);
a089699c 21545 }
a089699c 21546 }
1354d172
AD
21547 return attributes; // Array
21548 },
a089699c 21549
1354d172
AD
21550 hasAttribute: function( /* item */ item,
21551 /* attribute-name-string */ attribute){
21552 // summary:
21553 // See dojo.data.api.Read.hasAttribute()
21554 this._assertIsItem(item);
21555 this._assertIsAttribute(attribute);
21556 return (attribute in item);
a089699c
AD
21557 },
21558
1354d172
AD
21559 containsValue: function(/* item */ item,
21560 /* attribute-name-string */ attribute,
21561 /* anything */ value){
21562 // summary:
21563 // See dojo.data.api.Read.containsValue()
21564 var regexp = undefined;
21565 if(typeof value === "string"){
21566 regexp = filterUtil.patternToRegExp(value, false);
a089699c 21567 }
1354d172 21568 return this._containsValue(item, attribute, value, regexp); //boolean.
a089699c
AD
21569 },
21570
1354d172
AD
21571 _containsValue: function( /* item */ item,
21572 /* attribute-name-string */ attribute,
21573 /* anything */ value,
21574 /* RegExp?*/ regexp){
21575 // summary:
21576 // Internal function for looking at the values contained by the item.
21577 // description:
21578 // Internal function for looking at the values contained by the item. This
21579 // function allows for denoting if the comparison should be case sensitive for
21580 // strings or not (for handling filtering cases where string case should not matter)
21581 //
21582 // item:
21583 // The data item to examine for attribute values.
21584 // attribute:
21585 // The attribute to inspect.
21586 // value:
21587 // The value to match.
21588 // regexp:
21589 // Optional regular expression generated off value if value was of string type to handle wildcarding.
21590 // If present and attribute values are string, then it can be used for comparison instead of 'value'
21591 return array.some(this.getValues(item, attribute), function(possibleValue){
21592 if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
21593 if(possibleValue.toString().match(regexp)){
21594 return true; // Boolean
21595 }
21596 }else if(value === possibleValue){
21597 return true; // Boolean
21598 }
21599 });
a089699c
AD
21600 },
21601
1354d172
AD
21602 isItem: function(/* anything */ something){
21603 // summary:
21604 // See dojo.data.api.Read.isItem()
21605 if(something && something[this._storeRefPropName] === this){
21606 if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
21607 return true;
a089699c 21608 }
a089699c 21609 }
1354d172 21610 return false; // Boolean
a089699c
AD
21611 },
21612
1354d172
AD
21613 isItemLoaded: function(/* anything */ something){
21614 // summary:
21615 // See dojo.data.api.Read.isItemLoaded()
21616 return this.isItem(something); //boolean
a089699c
AD
21617 },
21618
1354d172
AD
21619 loadItem: function(/* object */ keywordArgs){
21620 // summary:
21621 // See dojo.data.api.Read.loadItem()
21622 this._assertIsItem(keywordArgs.item);
a089699c
AD
21623 },
21624
1354d172
AD
21625 getFeatures: function(){
21626 // summary:
21627 // See dojo.data.api.Read.getFeatures()
21628 return this._features; //Object
a089699c
AD
21629 },
21630
1354d172
AD
21631 getLabel: function(/* item */ item){
21632 // summary:
21633 // See dojo.data.api.Read.getLabel()
21634 if(this._labelAttr && this.isItem(item)){
21635 return this.getValue(item,this._labelAttr); //String
21636 }
21637 return undefined; //undefined
a089699c
AD
21638 },
21639
1354d172
AD
21640 getLabelAttributes: function(/* item */ item){
21641 // summary:
21642 // See dojo.data.api.Read.getLabelAttributes()
21643 if(this._labelAttr){
21644 return [this._labelAttr]; //array
a089699c 21645 }
1354d172 21646 return null; //null
a089699c
AD
21647 },
21648
1354d172
AD
21649 _fetchItems: function( /* Object */ keywordArgs,
21650 /* Function */ findCallback,
21651 /* Function */ errorCallback){
21652 // summary:
21653 // See dojo.data.util.simpleFetch.fetch()
21654 var self = this,
21655 filter = function(requestArgs, arrayOfItems){
21656 var items = [],
21657 i, key;
21658 if(requestArgs.query){
21659 var value,
21660 ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
a089699c 21661
1354d172
AD
21662 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
21663 //same value for each item examined. Much more efficient.
21664 var regexpList = {};
21665 for(key in requestArgs.query){
21666 value = requestArgs.query[key];
21667 if(typeof value === "string"){
21668 regexpList[key] = filterUtil.patternToRegExp(value, ignoreCase);
21669 }else if(value instanceof RegExp){
21670 regexpList[key] = value;
21671 }
a089699c 21672 }
1354d172
AD
21673 for(i = 0; i < arrayOfItems.length; ++i){
21674 var match = true;
21675 var candidateItem = arrayOfItems[i];
21676 if(candidateItem === null){
21677 match = false;
21678 }else{
21679 for(key in requestArgs.query){
21680 value = requestArgs.query[key];
21681 if(!self._containsValue(candidateItem, key, value, regexpList[key])){
21682 match = false;
21683 }
21684 }
21685 }
21686 if(match){
21687 items.push(candidateItem);
21688 }
21689 }
21690 findCallback(items, requestArgs);
21691 }else{
21692 // We want a copy to pass back in case the parent wishes to sort the array.
21693 // We shouldn't allow resort of the internal list, so that multiple callers
21694 // can get lists and sort without affecting each other. We also need to
21695 // filter out any null values that have been left as a result of deleteItem()
21696 // calls in ItemFileWriteStore.
21697 for(i = 0; i < arrayOfItems.length; ++i){
21698 var item = arrayOfItems[i];
21699 if(item !== null){
21700 items.push(item);
21701 }
21702 }
21703 findCallback(items, requestArgs);
81bea17a 21704 }
a089699c
AD
21705 };
21706
1354d172
AD
21707 if(this._loadFinished){
21708 filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
21709 }else{
21710 //Do a check on the JsonFileUrl and crosscheck it.
21711 //If it doesn't match the cross-check, it needs to be updated
21712 //This allows for either url or _jsonFileUrl to he changed to
21713 //reset the store load location. Done this way for backwards
21714 //compatibility. People use _jsonFileUrl (even though officially
21715 //private.
21716 if(this._jsonFileUrl !== this._ccUrl){
21717 kernel.deprecated("dojo.data.ItemFileReadStore: ",
21718 "To change the url, set the url property of the store," +
21719 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
21720 this._ccUrl = this._jsonFileUrl;
21721 this.url = this._jsonFileUrl;
21722 }else if(this.url !== this._ccUrl){
21723 this._jsonFileUrl = this.url;
21724 this._ccUrl = this.url;
81bea17a 21725 }
a089699c 21726
1354d172
AD
21727 //See if there was any forced reset of data.
21728 if(this.data != null){
21729 this._jsonData = this.data;
21730 this.data = null;
21731 }
a089699c 21732
1354d172
AD
21733 if(this._jsonFileUrl){
21734 //If fetches come in before the loading has finished, but while
21735 //a load is in progress, we have to defer the fetching to be
21736 //invoked in the callback.
21737 if(this._loadInProgress){
21738 this._queuedFetches.push({args: keywordArgs, filter: filter});
21739 }else{
21740 this._loadInProgress = true;
21741 var getArgs = {
21742 url: self._jsonFileUrl,
21743 handleAs: "json-comment-optional",
21744 preventCache: this.urlPreventCache,
21745 failOk: this.failOk
21746 };
21747 var getHandler = xhr.get(getArgs);
21748 getHandler.addCallback(function(data){
21749 try{
21750 self._getItemsFromLoadedData(data);
21751 self._loadFinished = true;
21752 self._loadInProgress = false;
a089699c 21753
1354d172
AD
21754 filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
21755 self._handleQueuedFetches();
21756 }catch(e){
21757 self._loadFinished = true;
21758 self._loadInProgress = false;
21759 errorCallback(e, keywordArgs);
21760 }
21761 });
21762 getHandler.addErrback(function(error){
21763 self._loadInProgress = false;
21764 errorCallback(error, keywordArgs);
21765 });
81bea17a 21766
1354d172
AD
21767 //Wire up the cancel to abort of the request
21768 //This call cancel on the deferred if it hasn't been called
21769 //yet and then will chain to the simple abort of the
21770 //simpleFetch keywordArgs
21771 var oldAbort = null;
21772 if(keywordArgs.abort){
21773 oldAbort = keywordArgs.abort;
21774 }
21775 keywordArgs.abort = function(){
21776 var df = getHandler;
21777 if(df && df.fired === -1){
21778 df.cancel();
21779 df = null;
21780 }
21781 if(oldAbort){
21782 oldAbort.call(keywordArgs);
21783 }
21784 };
21785 }
21786 }else if(this._jsonData){
21787 try{
21788 this._loadFinished = true;
21789 this._getItemsFromLoadedData(this._jsonData);
21790 this._jsonData = null;
21791 filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
21792 }catch(e){
21793 errorCallback(e, keywordArgs);
21794 }
21795 }else{
21796 errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
21797 }
21798 }
21799 },
a089699c 21800
1354d172
AD
21801 _handleQueuedFetches: function(){
21802 // summary:
21803 // Internal function to execute delayed request in the store.
21804 //Execute any deferred fetches now.
21805 if(this._queuedFetches.length > 0){
21806 for(var i = 0; i < this._queuedFetches.length; i++){
21807 var fData = this._queuedFetches[i],
21808 delayedQuery = fData.args,
21809 delayedFilter = fData.filter;
21810 if(delayedFilter){
21811 delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
21812 }else{
21813 this.fetchItemByIdentity(delayedQuery);
21814 }
21815 }
21816 this._queuedFetches = [];
21817 }
21818 },
a089699c 21819
1354d172
AD
21820 _getItemsArray: function(/*object?*/queryOptions){
21821 // summary:
21822 // Internal function to determine which list of items to search over.
21823 // queryOptions: The query options parameter, if any.
21824 if(queryOptions && queryOptions.deep){
21825 return this._arrayOfAllItems;
21826 }
21827 return this._arrayOfTopLevelItems;
21828 },
a089699c 21829
1354d172
AD
21830 close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
21831 // summary:
21832 // See dojo.data.api.Read.close()
21833 if(this.clearOnClose &&
21834 this._loadFinished &&
21835 !this._loadInProgress){
21836 //Reset all internalsback to default state. This will force a reload
21837 //on next fetch. This also checks that the data or url param was set
21838 //so that the store knows it can get data. Without one of those being set,
21839 //the next fetch will trigger an error.
a089699c 21840
1354d172
AD
21841 if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
21842 (this.url == "" || this.url == null)
21843 ) && this.data == null){
21844 console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " +
21845 " information has not been provided." +
21846 " Please set 'url' or 'data' to the appropriate value before" +
21847 " the next fetch");
21848 }
21849 this._arrayOfAllItems = [];
21850 this._arrayOfTopLevelItems = [];
21851 this._loadFinished = false;
21852 this._itemsByIdentity = null;
21853 this._loadInProgress = false;
21854 this._queuedFetches = [];
21855 }
21856 },
a089699c 21857
1354d172
AD
21858 _getItemsFromLoadedData: function(/* Object */ dataObject){
21859 // summary:
21860 // Function to parse the loaded data into item format and build the internal items array.
21861 // description:
21862 // Function to parse the loaded data into item format and build the internal items array.
21863 //
21864 // dataObject:
21865 // The JS data object containing the raw data to convery into item format.
21866 //
21867 // returns: array
21868 // Array of items in store item format.
a089699c 21869
1354d172
AD
21870 // First, we define a couple little utility functions...
21871 var addingArrays = false,
21872 self = this;
a089699c 21873
1354d172
AD
21874 function valueIsAnItem(/* anything */ aValue){
21875 // summary:
21876 // Given any sort of value that could be in the raw json data,
21877 // return true if we should interpret the value as being an
21878 // item itself, rather than a literal value or a reference.
21879 // example:
21880 // | false == valueIsAnItem("Kermit");
21881 // | false == valueIsAnItem(42);
21882 // | false == valueIsAnItem(new Date());
21883 // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
21884 // | false == valueIsAnItem({_reference:'Kermit'});
21885 // | true == valueIsAnItem({name:'Kermit', color:'green'});
21886 // | true == valueIsAnItem({iggy:'pop'});
21887 // | true == valueIsAnItem({foo:42});
21888 return (aValue !== null) &&
21889 (typeof aValue === "object") &&
21890 (!lang.isArray(aValue) || addingArrays) &&
21891 (!lang.isFunction(aValue)) &&
21892 (aValue.constructor == Object || lang.isArray(aValue)) &&
21893 (typeof aValue._reference === "undefined") &&
21894 (typeof aValue._type === "undefined") &&
21895 (typeof aValue._value === "undefined") &&
21896 self.hierarchical;
21897 }
a089699c 21898
1354d172
AD
21899 function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
21900 self._arrayOfAllItems.push(anItem);
21901 for(var attribute in anItem){
21902 var valueForAttribute = anItem[attribute];
21903 if(valueForAttribute){
21904 if(lang.isArray(valueForAttribute)){
21905 var valueArray = valueForAttribute;
21906 for(var k = 0; k < valueArray.length; ++k){
21907 var singleValue = valueArray[k];
21908 if(valueIsAnItem(singleValue)){
21909 addItemAndSubItemsToArrayOfAllItems(singleValue);
21910 }
21911 }
21912 }else{
21913 if(valueIsAnItem(valueForAttribute)){
21914 addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
21915 }
21916 }
21917 }
21918 }
21919 }
a089699c 21920
1354d172 21921 this._labelAttr = dataObject.label;
81bea17a 21922
1354d172
AD
21923 // We need to do some transformations to convert the data structure
21924 // that we read from the file into a format that will be convenient
21925 // to work with in memory.
81bea17a 21926
1354d172
AD
21927 // Step 1: Walk through the object hierarchy and build a list of all items
21928 var i,
21929 item;
21930 this._arrayOfAllItems = [];
21931 this._arrayOfTopLevelItems = dataObject.items;
81bea17a 21932
1354d172
AD
21933 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
21934 item = this._arrayOfTopLevelItems[i];
21935 if(lang.isArray(item)){
21936 addingArrays = true;
a089699c 21937 }
1354d172
AD
21938 addItemAndSubItemsToArrayOfAllItems(item);
21939 item[this._rootItemPropName]=true;
a089699c 21940 }
a089699c 21941
1354d172
AD
21942 // Step 2: Walk through all the attribute values of all the items,
21943 // and replace single values with arrays. For example, we change this:
21944 // { name:'Miss Piggy', pets:'Foo-Foo'}
21945 // into this:
21946 // { name:['Miss Piggy'], pets:['Foo-Foo']}
21947 //
21948 // We also store the attribute names so we can validate our store
21949 // reference and item id special properties for the O(1) isItem
21950 var allAttributeNames = {},
21951 key;
a089699c 21952
1354d172
AD
21953 for(i = 0; i < this._arrayOfAllItems.length; ++i){
21954 item = this._arrayOfAllItems[i];
21955 for(key in item){
21956 if(key !== this._rootItemPropName){
21957 var value = item[key];
21958 if(value !== null){
21959 if(!lang.isArray(value)){
21960 item[key] = [value];
21961 }
21962 }else{
21963 item[key] = [null];
21964 }
21965 }
21966 allAttributeNames[key]=key;
21967 }
a089699c 21968 }
a089699c 21969
1354d172
AD
21970 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
21971 // This should go really fast, it will generally never even run the loop.
21972 while(allAttributeNames[this._storeRefPropName]){
21973 this._storeRefPropName += "_";
a089699c 21974 }
1354d172
AD
21975 while(allAttributeNames[this._itemNumPropName]){
21976 this._itemNumPropName += "_";
21977 }
21978 while(allAttributeNames[this._reverseRefMap]){
21979 this._reverseRefMap += "_";
a089699c
AD
21980 }
21981
1354d172
AD
21982 // Step 4: Some data files specify an optional 'identifier', which is
21983 // the name of an attribute that holds the identity of each item.
21984 // If this data file specified an identifier attribute, then build a
21985 // hash table of items keyed by the identity of the items.
21986 var arrayOfValues;
a089699c 21987
1354d172
AD
21988 var identifier = dataObject.identifier;
21989 if(identifier){
21990 this._itemsByIdentity = {};
21991 this._features['dojo.data.api.Identity'] = identifier;
21992 for(i = 0; i < this._arrayOfAllItems.length; ++i){
21993 item = this._arrayOfAllItems[i];
21994 arrayOfValues = item[identifier];
21995 var identity = arrayOfValues[0];
21996 if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
21997 this._itemsByIdentity[identity] = item;
21998 }else{
21999 if(this._jsonFileUrl){
22000 throw new Error("dojo.data.ItemFileReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
22001 }else if(this._jsonData){
22002 throw new Error("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
22003 }
a089699c 22004 }
a089699c 22005 }
1354d172
AD
22006 }else{
22007 this._features['dojo.data.api.Identity'] = Number;
a089699c
AD
22008 }
22009
1354d172
AD
22010 // Step 5: Walk through all the items, and set each item's properties
22011 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
22012 for(i = 0; i < this._arrayOfAllItems.length; ++i){
22013 item = this._arrayOfAllItems[i];
22014 item[this._storeRefPropName] = this;
22015 item[this._itemNumPropName] = i;
a089699c 22016 }
a089699c 22017
1354d172
AD
22018 // Step 6: We walk through all the attribute values of all the items,
22019 // looking for type/value literals and item-references.
22020 //
22021 // We replace item-references with pointers to items. For example, we change:
22022 // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
22023 // into this:
22024 // { name:['Kermit'], friends:[miss_piggy] }
22025 // (where miss_piggy is the object representing the 'Miss Piggy' item).
22026 //
22027 // We replace type/value pairs with typed-literals. For example, we change:
22028 // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
22029 // into this:
22030 // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
22031 //
22032 // We also generate the associate map for all items for the O(1) isItem function.
22033 for(i = 0; i < this._arrayOfAllItems.length; ++i){
22034 item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
22035 for(key in item){
22036 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
22037 for(var j = 0; j < arrayOfValues.length; ++j){
22038 value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
22039 if(value !== null && typeof value == "object"){
22040 if(("_type" in value) && ("_value" in value)){
22041 var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
22042 var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
22043 if(!mappingObj){
22044 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
22045 }else if(lang.isFunction(mappingObj)){
22046 arrayOfValues[j] = new mappingObj(value._value);
22047 }else if(lang.isFunction(mappingObj.deserialize)){
22048 arrayOfValues[j] = mappingObj.deserialize(value._value);
22049 }else{
22050 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
22051 }
22052 }
22053 if(value._reference){
22054 var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
22055 if(!lang.isObject(referenceDescription)){
22056 // example: 'Miss Piggy'
22057 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
22058 arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
22059 }else{
22060 // example: {name:'Miss Piggy'}
22061 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
22062 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
22063 var candidateItem = this._arrayOfAllItems[k],
22064 found = true;
22065 for(var refKey in referenceDescription){
22066 if(candidateItem[refKey] != referenceDescription[refKey]){
22067 found = false;
22068 }
22069 }
22070 if(found){
22071 arrayOfValues[j] = candidateItem;
22072 }
22073 }
22074 }
22075 if(this.referenceIntegrity){
22076 var refItem = arrayOfValues[j];
22077 if(this.isItem(refItem)){
22078 this._addReferenceToMap(refItem, item, key);
22079 }
22080 }
22081 }else if(this.isItem(value)){
22082 //It's a child item (not one referenced through _reference).
22083 //We need to treat this as a referenced item, so it can be cleaned up
22084 //in a write store easily.
22085 if(this.referenceIntegrity){
22086 this._addReferenceToMap(value, item, key);
22087 }
22088 }
22089 }
22090 }
22091 }
a089699c 22092 }
a089699c
AD
22093 },
22094
1354d172
AD
22095 _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
22096 // summary:
22097 // Method to add an reference map entry for an item and attribute.
22098 // description:
22099 // Method to add an reference map entry for an item and attribute. //
22100 // refItem:
22101 // The item that is referenced.
22102 // parentItem:
22103 // The item that holds the new reference to refItem.
22104 // attribute:
22105 // The attribute on parentItem that contains the new reference.
a089699c 22106
1354d172 22107 //Stub function, does nothing. Real processing is in ItemFileWriteStore.
81bea17a
AD
22108 },
22109
1354d172
AD
22110 getIdentity: function(/* item */ item){
22111 // summary:
22112 // See dojo.data.api.Identity.getIdentity()
22113 var identifier = this._features['dojo.data.api.Identity'];
22114 if(identifier === Number){
22115 return item[this._itemNumPropName]; // Number
22116 }else{
22117 var arrayOfValues = item[identifier];
22118 if(arrayOfValues){
22119 return arrayOfValues[0]; // Object || String
22120 }
22121 }
22122 return null; // null
22123 },
a089699c 22124
1354d172
AD
22125 fetchItemByIdentity: function(/* Object */ keywordArgs){
22126 // summary:
22127 // See dojo.data.api.Identity.fetchItemByIdentity()
a089699c 22128
1354d172
AD
22129 // Hasn't loaded yet, we have to trigger the load.
22130 var item,
22131 scope;
22132 if(!this._loadFinished){
22133 var self = this;
22134 //Do a check on the JsonFileUrl and crosscheck it.
22135 //If it doesn't match the cross-check, it needs to be updated
22136 //This allows for either url or _jsonFileUrl to he changed to
22137 //reset the store load location. Done this way for backwards
22138 //compatibility. People use _jsonFileUrl (even though officially
22139 //private.
22140 if(this._jsonFileUrl !== this._ccUrl){
22141 kernel.deprecated("dojo.data.ItemFileReadStore: ",
22142 "To change the url, set the url property of the store," +
22143 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
22144 this._ccUrl = this._jsonFileUrl;
22145 this.url = this._jsonFileUrl;
22146 }else if(this.url !== this._ccUrl){
22147 this._jsonFileUrl = this.url;
22148 this._ccUrl = this.url;
22149 }
a089699c 22150
1354d172
AD
22151 //See if there was any forced reset of data.
22152 if(this.data != null && this._jsonData == null){
22153 this._jsonData = this.data;
22154 this.data = null;
22155 }
a089699c 22156
1354d172 22157 if(this._jsonFileUrl){
a089699c 22158
1354d172
AD
22159 if(this._loadInProgress){
22160 this._queuedFetches.push({args: keywordArgs});
22161 }else{
22162 this._loadInProgress = true;
22163 var getArgs = {
22164 url: self._jsonFileUrl,
22165 handleAs: "json-comment-optional",
22166 preventCache: this.urlPreventCache,
22167 failOk: this.failOk
22168 };
22169 var getHandler = xhr.get(getArgs);
22170 getHandler.addCallback(function(data){
22171 var scope = keywordArgs.scope?keywordArgs.scope:window.global;
22172 try{
22173 self._getItemsFromLoadedData(data);
22174 self._loadFinished = true;
22175 self._loadInProgress = false;
22176 item = self._getItemByIdentity(keywordArgs.identity);
22177 if(keywordArgs.onItem){
22178 keywordArgs.onItem.call(scope, item);
22179 }
22180 self._handleQueuedFetches();
22181 }catch(error){
22182 self._loadInProgress = false;
22183 if(keywordArgs.onError){
22184 keywordArgs.onError.call(scope, error);
22185 }
22186 }
22187 });
22188 getHandler.addErrback(function(error){
22189 self._loadInProgress = false;
22190 if(keywordArgs.onError){
22191 var scope = keywordArgs.scope?keywordArgs.scope:window.global;
22192 keywordArgs.onError.call(scope, error);
22193 }
22194 });
22195 }
a089699c 22196
1354d172
AD
22197 }else if(this._jsonData){
22198 // Passed in data, no need to xhr.
22199 self._getItemsFromLoadedData(self._jsonData);
22200 self._jsonData = null;
22201 self._loadFinished = true;
22202 item = self._getItemByIdentity(keywordArgs.identity);
22203 if(keywordArgs.onItem){
22204 scope = keywordArgs.scope?keywordArgs.scope:window.global;
22205 keywordArgs.onItem.call(scope, item);
22206 }
22207 }
22208 }else{
22209 // Already loaded. We can just look it up and call back.
22210 item = this._getItemByIdentity(keywordArgs.identity);
22211 if(keywordArgs.onItem){
22212 scope = keywordArgs.scope?keywordArgs.scope:window.global;
22213 keywordArgs.onItem.call(scope, item);
22214 }
22215 }
22216 },
a089699c 22217
1354d172
AD
22218 _getItemByIdentity: function(/* Object */ identity){
22219 // summary:
22220 // Internal function to look an item up by its identity map.
22221 var item = null;
22222 if(this._itemsByIdentity){
22223 // If this map is defined, we need to just try to get it. If it fails
22224 // the item does not exist.
22225 if(Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
22226 item = this._itemsByIdentity[identity];
22227 }
22228 }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
22229 item = this._arrayOfAllItems[identity];
22230 }
22231 if(item === undefined){
22232 item = null;
22233 }
22234 return item; // Object
22235 },
a089699c 22236
1354d172
AD
22237 getIdentityAttributes: function(/* item */ item){
22238 // summary:
22239 // See dojo.data.api.Identity.getIdentityAttributes()
a089699c 22240
1354d172
AD
22241 var identifier = this._features['dojo.data.api.Identity'];
22242 if(identifier === Number){
22243 // If (identifier === Number) it means getIdentity() just returns
22244 // an integer item-number for each item. The dojo.data.api.Identity
22245 // spec says we need to return null if the identity is not composed
22246 // of attributes
22247 return null; // null
a089699c 22248 }else{
1354d172 22249 return [identifier]; // Array
a089699c
AD
22250 }
22251 },
22252
1354d172
AD
22253 _forceLoad: function(){
22254 // summary:
22255 // Internal function to force a load of the store if it hasn't occurred yet. This is required
22256 // for specific functions to work properly.
22257 var self = this;
22258 //Do a check on the JsonFileUrl and crosscheck it.
22259 //If it doesn't match the cross-check, it needs to be updated
22260 //This allows for either url or _jsonFileUrl to he changed to
22261 //reset the store load location. Done this way for backwards
22262 //compatibility. People use _jsonFileUrl (even though officially
22263 //private.
22264 if(this._jsonFileUrl !== this._ccUrl){
22265 kernel.deprecated("dojo.data.ItemFileReadStore: ",
22266 "To change the url, set the url property of the store," +
22267 " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
22268 this._ccUrl = this._jsonFileUrl;
22269 this.url = this._jsonFileUrl;
22270 }else if(this.url !== this._ccUrl){
22271 this._jsonFileUrl = this.url;
22272 this._ccUrl = this.url;
22273 }
a089699c 22274
1354d172
AD
22275 //See if there was any forced reset of data.
22276 if(this.data != null){
22277 this._jsonData = this.data;
22278 this.data = null;
a089699c 22279 }
a089699c 22280
1354d172
AD
22281 if(this._jsonFileUrl){
22282 var getArgs = {
22283 url: this._jsonFileUrl,
22284 handleAs: "json-comment-optional",
22285 preventCache: this.urlPreventCache,
22286 failOk: this.failOk,
22287 sync: true
22288 };
22289 var getHandler = xhr.get(getArgs);
22290 getHandler.addCallback(function(data){
22291 try{
22292 //Check to be sure there wasn't another load going on concurrently
22293 //So we don't clobber data that comes in on it. If there is a load going on
22294 //then do not save this data. It will potentially clobber current data.
22295 //We mainly wanted to sync/wait here.
22296 //TODO: Revisit the loading scheme of this store to improve multi-initial
22297 //request handling.
22298 if(self._loadInProgress !== true && !self._loadFinished){
22299 self._getItemsFromLoadedData(data);
22300 self._loadFinished = true;
22301 }else if(self._loadInProgress){
22302 //Okay, we hit an error state we can't recover from. A forced load occurred
22303 //while an async load was occurring. Since we cannot block at this point, the best
22304 //that can be managed is to throw an error.
22305 throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");
22306 }
22307 }catch(e){
22308 console.log(e);
22309 throw e;
22310 }
22311 });
22312 getHandler.addErrback(function(error){
22313 throw error;
22314 });
22315 }else if(this._jsonData){
22316 self._getItemsFromLoadedData(self._jsonData);
22317 self._jsonData = null;
22318 self._loadFinished = true;
a089699c 22319 }
a089699c
AD
22320 }
22321});
1354d172
AD
22322//Mix in the simple fetch implementation to this class.
22323lang.extend(ItemFileReadStore,simpleFetch);
a089699c 22324
1354d172
AD
22325return ItemFileReadStore;
22326});
a089699c 22327
1354d172
AD
22328},
22329'dojo/html':function(){
22330define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"], function(dojo, lang, darray, declare, dom, domConstruct, parser) {
22331 // module:
22332 // dojo/html
22333 // summary:
22334 // TODOC
a089699c 22335
1354d172 22336 lang.getObject("html", true, dojo);
a089699c 22337
1354d172 22338 // the parser might be needed..
a089699c 22339
1354d172
AD
22340 // idCounter is incremented with each instantiation to allow asignment of a unique id for tracking, logging purposes
22341 var idCounter = 0;
a089699c 22342
1354d172
AD
22343 dojo.html._secureForInnerHtml = function(/*String*/ cont){
22344 // summary:
22345 // removes !DOCTYPE and title elements from the html string.
22346 //
22347 // khtml is picky about dom faults, you can't attach a style or <title> node as child of body
22348 // must go into head, so we need to cut out those tags
22349 // cont:
22350 // An html string for insertion into the dom
22351 //
22352 return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
22353 };
a089699c 22354
1354d172
AD
22355/*====
22356 dojo.html._emptyNode = function(node){
22357 // summary:
22358 // removes all child nodes from the given node
22359 // node: DOMNode
22360 // the parent element
22361 };
22362=====*/
22363 dojo.html._emptyNode = domConstruct.empty;
a089699c 22364
1354d172
AD
22365 dojo.html._setNodeContent = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont){
22366 // summary:
22367 // inserts the given content into the given node
22368 // node:
22369 // the parent element
22370 // content:
22371 // the content to be set on the parent element.
22372 // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
a089699c 22373
1354d172
AD
22374 // always empty
22375 domConstruct.empty(node);
a089699c 22376
1354d172
AD
22377 if(cont) {
22378 if(typeof cont == "string") {
22379 cont = domConstruct.toDom(cont, node.ownerDocument);
22380 }
22381 if(!cont.nodeType && lang.isArrayLike(cont)) {
22382 // handle as enumerable, but it may shrink as we enumerate it
22383 for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0) {
22384 domConstruct.place( cont[i], node, "last");
22385 }
22386 } else {
22387 // pass nodes, documentFragments and unknowns through to dojo.place
22388 domConstruct.place(cont, node, "last");
22389 }
22390 }
a089699c 22391
1354d172
AD
22392 // return DomNode
22393 return node;
22394 };
a089699c 22395
1354d172
AD
22396 // we wrap up the content-setting operation in a object
22397 declare("dojo.html._ContentSetter", null,
22398 {
22399 // node: DomNode|String
22400 // An node which will be the parent element that we set content into
22401 node: "",
a089699c 22402
1354d172
AD
22403 // content: String|DomNode|DomNode[]
22404 // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
22405 content: "",
a089699c 22406
1354d172
AD
22407 // id: String?
22408 // Usually only used internally, and auto-generated with each instance
22409 id: "",
a089699c 22410
1354d172
AD
22411 // cleanContent: Boolean
22412 // Should the content be treated as a full html document,
22413 // and the real content stripped of <html>, <body> wrapper before injection
22414 cleanContent: false,
a089699c 22415
1354d172
AD
22416 // extractContent: Boolean
22417 // Should the content be treated as a full html document, and the real content stripped of <html>, <body> wrapper before injection
22418 extractContent: false,
a089699c 22419
1354d172
AD
22420 // parseContent: Boolean
22421 // Should the node by passed to the parser after the new content is set
22422 parseContent: false,
a089699c 22423
1354d172
AD
22424 // parserScope: String
22425 // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
22426 // will search for data-dojo-type (or dojoType). For backwards compatibility
22427 // reasons defaults to dojo._scopeName (which is "dojo" except when
22428 // multi-version support is used, when it will be something like dojo16, dojo20, etc.)
22429 parserScope: dojo._scopeName,
a089699c 22430
1354d172
AD
22431 // startup: Boolean
22432 // Start the child widgets after parsing them. Only obeyed if parseContent is true.
22433 startup: true,
a089699c 22434
1354d172
AD
22435 // lifecyle methods
22436 constructor: function(/* Object */params, /* String|DomNode */node){
22437 // summary:
22438 // Provides a configurable, extensible object to wrap the setting on content on a node
22439 // call the set() method to actually set the content..
a089699c 22440
1354d172
AD
22441 // the original params are mixed directly into the instance "this"
22442 lang.mixin(this, params || {});
a089699c 22443
1354d172
AD
22444 // give precedence to params.node vs. the node argument
22445 // and ensure its a node, not an id string
22446 node = this.node = dom.byId( this.node || node );
a089699c 22447
1354d172
AD
22448 if(!this.id){
22449 this.id = [
22450 "Setter",
22451 (node) ? node.id || node.tagName : "",
22452 idCounter++
22453 ].join("_");
22454 }
22455 },
22456 set: function(/* String|DomNode|NodeList? */ cont, /* Object? */ params){
22457 // summary:
22458 // front-end to the set-content sequence
22459 // cont:
22460 // An html string, node or enumerable list of nodes for insertion into the dom
22461 // If not provided, the object's content property will be used
22462 if(undefined !== cont){
22463 this.content = cont;
22464 }
22465 // in the re-use scenario, set needs to be able to mixin new configuration
22466 if(params){
22467 this._mixin(params);
22468 }
a089699c 22469
1354d172
AD
22470 this.onBegin();
22471 this.setContent();
22472 this.onEnd();
a089699c 22473
1354d172
AD
22474 return this.node;
22475 },
22476 setContent: function(){
22477 // summary:
22478 // sets the content on the node
a089699c 22479
1354d172
AD
22480 var node = this.node;
22481 if(!node) {
22482 // can't proceed
22483 throw new Error(this.declaredClass + ": setContent given no node");
22484 }
22485 try{
22486 node = dojo.html._setNodeContent(node, this.content);
22487 }catch(e){
22488 // check if a domfault occurs when we are appending this.errorMessage
22489 // like for instance if domNode is a UL and we try append a DIV
a089699c 22490
1354d172
AD
22491 // FIXME: need to allow the user to provide a content error message string
22492 var errMess = this.onContentError(e);
22493 try{
22494 node.innerHTML = errMess;
22495 }catch(e){
22496 console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
22497 }
22498 }
22499 // always put back the node for the next method
22500 this.node = node; // DomNode
22501 },
a089699c 22502
1354d172
AD
22503 empty: function() {
22504 // summary
22505 // cleanly empty out existing content
a089699c 22506
1354d172
AD
22507 // destroy any widgets from a previous run
22508 // NOTE: if you dont want this you'll need to empty
22509 // the parseResults array property yourself to avoid bad things happenning
22510 if(this.parseResults && this.parseResults.length) {
22511 darray.forEach(this.parseResults, function(w) {
22512 if(w.destroy){
22513 w.destroy();
22514 }
22515 });
22516 delete this.parseResults;
22517 }
22518 // this is fast, but if you know its already empty or safe, you could
22519 // override empty to skip this step
22520 dojo.html._emptyNode(this.node);
22521 },
a089699c 22522
1354d172
AD
22523 onBegin: function(){
22524 // summary
22525 // Called after instantiation, but before set();
22526 // It allows modification of any of the object properties
22527 // - including the node and content provided - before the set operation actually takes place
22528 // This default implementation checks for cleanContent and extractContent flags to
22529 // optionally pre-process html string content
22530 var cont = this.content;
a089699c 22531
1354d172
AD
22532 if(lang.isString(cont)){
22533 if(this.cleanContent){
22534 cont = dojo.html._secureForInnerHtml(cont);
22535 }
a089699c 22536
1354d172
AD
22537 if(this.extractContent){
22538 var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
22539 if(match){ cont = match[1]; }
22540 }
22541 }
a089699c 22542
1354d172
AD
22543 // clean out the node and any cruft associated with it - like widgets
22544 this.empty();
a089699c 22545
1354d172
AD
22546 this.content = cont;
22547 return this.node; /* DomNode */
22548 },
81bea17a 22549
1354d172
AD
22550 onEnd: function(){
22551 // summary
22552 // Called after set(), when the new content has been pushed into the node
22553 // It provides an opportunity for post-processing before handing back the node to the caller
22554 // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
22555 if(this.parseContent){
22556 // populates this.parseResults if you need those..
22557 this._parse();
22558 }
22559 return this.node; /* DomNode */
22560 },
81bea17a 22561
1354d172
AD
22562 tearDown: function(){
22563 // summary
22564 // manually reset the Setter instance if its being re-used for example for another set()
22565 // description
22566 // tearDown() is not called automatically.
22567 // In normal use, the Setter instance properties are simply allowed to fall out of scope
22568 // but the tearDown method can be called to explicitly reset this instance.
22569 delete this.parseResults;
22570 delete this.node;
22571 delete this.content;
22572 },
a089699c 22573
1354d172
AD
22574 onContentError: function(err){
22575 return "Error occured setting content: " + err;
22576 },
a089699c 22577
1354d172
AD
22578 _mixin: function(params){
22579 // mix properties/methods into the instance
22580 // TODO: the intention with tearDown is to put the Setter's state
22581 // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
22582 // so we could do something here to move the original properties aside for later restoration
22583 var empty = {}, key;
22584 for(key in params){
22585 if(key in empty){ continue; }
22586 // TODO: here's our opportunity to mask the properties we dont consider configurable/overridable
22587 // .. but history shows we'll almost always guess wrong
22588 this[key] = params[key];
22589 }
22590 },
22591 _parse: function(){
22592 // summary:
22593 // runs the dojo parser over the node contents, storing any results in this.parseResults
22594 // Any errors resulting from parsing are passed to _onError for handling
a089699c 22595
1354d172
AD
22596 var rootNode = this.node;
22597 try{
22598 // store the results (widgets, whatever) for potential retrieval
22599 var inherited = {};
22600 darray.forEach(["dir", "lang", "textDir"], function(name){
22601 if(this[name]){
22602 inherited[name] = this[name];
22603 }
22604 }, this);
22605 this.parseResults = parser.parse({
22606 rootNode: rootNode,
22607 noStart: !this.startup,
22608 inherited: inherited,
22609 scope: this.parserScope
22610 });
22611 }catch(e){
22612 this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
22613 }
22614 },
a089699c 22615
1354d172
AD
22616 _onError: function(type, err, consoleText){
22617 // summary:
22618 // shows user the string that is returned by on[type]Error
22619 // overide/implement on[type]Error and return your own string to customize
22620 var errText = this['on' + type + 'Error'].call(this, err);
22621 if(consoleText){
22622 console.error(consoleText, err);
22623 }else if(errText){ // a empty string won't change current content
22624 dojo.html._setNodeContent(this.node, errText, true);
22625 }
22626 }
22627 }); // end dojo.declare()
a089699c 22628
1354d172
AD
22629 dojo.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
22630 // summary:
22631 // inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
22632 // may be a better choice for simple HTML insertion.
22633 // description:
22634 // Unless you need to use the params capabilities of this method, you should use
22635 // dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
22636 // an HTML string into the DOM, but it only handles inserting an HTML string as DOM
22637 // elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
22638 // or the other capabilities as defined by the params object for this method.
22639 // node:
22640 // the parent element that will receive the content
22641 // cont:
22642 // the content to be set on the parent element.
22643 // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
22644 // params:
22645 // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
22646 // example:
22647 // A safe string/node/nodelist content replacement/injection with hooks for extension
22648 // Example Usage:
22649 // dojo.html.set(node, "some string");
22650 // dojo.html.set(node, contentNode, {options});
22651 // dojo.html.set(node, myNode.childNodes, {options});
22652 if(undefined == cont){
22653 console.warn("dojo.html.set: no cont argument provided, using empty string");
22654 cont = "";
22655 }
22656 if(!params){
22657 // simple and fast
22658 return dojo.html._setNodeContent(node, cont, true);
22659 }else{
22660 // more options but slower
22661 // note the arguments are reversed in order, to match the convention for instantiation via the parser
22662 var op = new dojo.html._ContentSetter(lang.mixin(
22663 params,
22664 { content: cont, node: node }
22665 ));
22666 return op.set();
22667 }
22668 };
a089699c 22669
1354d172
AD
22670 return dojo.html;
22671});
a089699c 22672
1354d172
AD
22673},
22674'dijit/_PaletteMixin':function(){
22675define("dijit/_PaletteMixin", [
22676 "dojo/_base/declare", // declare
22677 "dojo/dom-attr", // domAttr.set
22678 "dojo/dom-class", // domClass.add domClass.remove
22679 "dojo/dom-construct", // domConstruct.create domConstruct.place
22680 "dojo/_base/event", // event.stop
22681 "dojo/keys", // keys
22682 "dojo/_base/lang", // lang.getObject
22683 "./_CssStateMixin",
22684 "./focus",
22685 "./typematic"
22686], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
a089699c 22687
1354d172
AD
22688/*=====
22689 var _CssStateMixin = dijit._CssStateMixin;
22690=====*/
a089699c 22691
1354d172
AD
22692// module:
22693// dijit/_PaletteMixin
22694// summary:
22695// A keyboard accessible palette, for picking a color/emoticon/etc.
a089699c 22696
1354d172
AD
22697return declare("dijit._PaletteMixin", [_CssStateMixin], {
22698 // summary:
22699 // A keyboard accessible palette, for picking a color/emoticon/etc.
22700 // description:
22701 // A mixin for a grid showing various entities, so the user can pick a certain entity.
a089699c 22702
1354d172
AD
22703 // defaultTimeout: Number
22704 // Number of milliseconds before a held key or button becomes typematic
22705 defaultTimeout: 500,
a089699c 22706
1354d172
AD
22707 // timeoutChangeRate: Number
22708 // Fraction of time used to change the typematic timer between events
22709 // 1.0 means that each typematic event fires at defaultTimeout intervals
22710 // < 1.0 means that each typematic event fires at an increasing faster rate
22711 timeoutChangeRate: 0.90,
a089699c 22712
1354d172
AD
22713 // value: String
22714 // Currently selected color/emoticon/etc.
22715 value: "",
a089699c 22716
1354d172
AD
22717 // _selectedCell: [private] Integer
22718 // Index of the currently selected cell. Initially, none selected
22719 _selectedCell: -1,
a089699c 22720
1354d172
AD
22721/*=====
22722 // _currentFocus: [private] DomNode
22723 // The currently focused cell (if the palette itself has focus), or otherwise
22724 // the cell to be focused when the palette itself gets focus.
22725 // Different from value, which represents the selected (i.e. clicked) cell.
22726 _currentFocus: null,
22727=====*/
a089699c 22728
1354d172
AD
22729/*=====
22730 // _xDim: [protected] Integer
22731 // This is the number of cells horizontally across.
22732 _xDim: null,
22733=====*/
a089699c 22734
1354d172
AD
22735/*=====
22736 // _yDim: [protected] Integer
22737 // This is the number of cells vertically down.
22738 _yDim: null,
22739=====*/
a089699c 22740
1354d172
AD
22741 // tabIndex: String
22742 // Widget tab index.
22743 tabIndex: "0",
a089699c 22744
1354d172
AD
22745 // cellClass: [protected] String
22746 // CSS class applied to each cell in the palette
22747 cellClass: "dijitPaletteCell",
a089699c 22748
1354d172
AD
22749 // dyeClass: [protected] String
22750 // Name of javascript class for Object created for each cell of the palette.
22751 // dyeClass should implements dijit.Dye interface
22752 dyeClass: '',
22753
22754 // summary: String
22755 // Localized summary for the palette table
22756 summary: '',
22757 _setSummaryAttr: "paletteTableNode",
a089699c 22758
1354d172 22759 _dyeFactory: function(value /*===== , row, col =====*/){
a089699c 22760 // summary:
1354d172
AD
22761 // Return instance of dijit.Dye for specified cell of palette
22762 // tags:
22763 // extension
22764 var dyeClassObj = lang.getObject(this.dyeClass);
22765 return new dyeClassObj(value);
a089699c
AD
22766 },
22767
1354d172 22768 _preparePalette: function(choices, titles) {
a089699c 22769 // summary:
1354d172
AD
22770 // Subclass must call _preparePalette() from postCreate(), passing in the tooltip
22771 // for each cell
22772 // choices: String[][]
22773 // id's for each cell of the palette, used to create Dye JS object for each cell
22774 // titles: String[]
22775 // Localized tooltip for each cell
a089699c 22776
1354d172
AD
22777 this._cells = [];
22778 var url = this._blankGif;
a089699c 22779
1354d172 22780 this.connect(this.gridNode, "ondijitclick", "_onCellClick");
a089699c 22781
1354d172
AD
22782 for(var row=0; row < choices.length; row++){
22783 var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
22784 for(var col=0; col < choices[row].length; col++){
22785 var value = choices[row][col];
22786 if(value){
22787 var cellObject = this._dyeFactory(value, row, col);
a089699c 22788
1354d172
AD
22789 var cellNode = domConstruct.create("td", {
22790 "class": this.cellClass,
22791 tabIndex: "-1",
22792 title: titles[value],
22793 role: "gridcell"
22794 });
a089699c 22795
1354d172
AD
22796 // prepare cell inner structure
22797 cellObject.fillCell(cellNode, url);
a089699c 22798
1354d172
AD
22799 domConstruct.place(cellNode, rowNode);
22800
22801 cellNode.index = this._cells.length;
22802
22803 // save cell info into _cells
22804 this._cells.push({node:cellNode, dye:cellObject});
22805 }
a089699c 22806 }
a089699c 22807 }
1354d172
AD
22808 this._xDim = choices[0].length;
22809 this._yDim = choices.length;
a089699c 22810
1354d172
AD
22811 // Now set all events
22812 // The palette itself is navigated to with the tab key on the keyboard
22813 // Keyboard navigation within the Palette is with the arrow keys
22814 // Spacebar selects the cell.
22815 // For the up key the index is changed by negative the x dimension.
81bea17a 22816
1354d172
AD
22817 var keyIncrementMap = {
22818 UP_ARROW: -this._xDim,
22819 // The down key the index is increase by the x dimension.
22820 DOWN_ARROW: this._xDim,
22821 // Right and left move the index by 1.
22822 RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
22823 LEFT_ARROW: this.isLeftToRight() ? -1 : 1
22824 };
22825 for(var key in keyIncrementMap){
22826 this._connects.push(
22827 typematic.addKeyListener(
22828 this.domNode,
22829 {charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
22830 this,
22831 function(){
22832 var increment = keyIncrementMap[key];
22833 return function(count){ this._navigateByKey(increment, count); };
22834 }(),
22835 this.timeoutChangeRate,
22836 this.defaultTimeout
22837 )
22838 );
22839 }
a089699c
AD
22840 },
22841
1354d172
AD
22842 postCreate: function(){
22843 this.inherited(arguments);
a089699c 22844
1354d172
AD
22845 // Set initial navigable node.
22846 this._setCurrent(this._cells[0].node);
a089699c
AD
22847 },
22848
1354d172 22849 focus: function(){
a089699c 22850 // summary:
1354d172 22851 // Focus this widget. Puts focus on the most recently focused cell.
a089699c 22852
1354d172
AD
22853 // The cell already has tabIndex set, just need to set CSS and focus it
22854 focus.focus(this._currentFocus);
22855 },
a089699c 22856
1354d172
AD
22857 _onCellClick: function(/*Event*/ evt){
22858 // summary:
22859 // Handler for click, enter key & space key. Selects the cell.
22860 // evt:
22861 // The event.
22862 // tags:
22863 // private
a089699c 22864
1354d172 22865 var target = evt.target;
a089699c 22866
1354d172
AD
22867 // Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
22868 while(target.tagName != "TD"){
22869 if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
22870 return;
a089699c 22871 }
1354d172 22872 target = target.parentNode;
a089699c
AD
22873 }
22874
1354d172
AD
22875 var value = this._getDye(target).getValue();
22876
22877 // First focus the clicked cell, and then send onChange() notification.
22878 // onChange() (via _setValueAttr) must be after the focus call, because
22879 // it may trigger a refocus to somewhere else (like the Editor content area), and that
22880 // second focus should win.
22881 this._setCurrent(target);
22882 focus.focus(target);
22883 this._setValueAttr(value, true);
22884
22885 event.stop(evt);
a089699c
AD
22886 },
22887
1354d172 22888 _setCurrent: function(/*DomNode*/ node){
a089699c 22889 // summary:
1354d172
AD
22890 // Sets which node is the focused cell.
22891 // description:
22892 // At any point in time there's exactly one
22893 // cell with tabIndex != -1. If focus is inside the palette then
22894 // focus is on that cell.
22895 //
22896 // After calling this method, arrow key handlers and mouse click handlers
22897 // should focus the cell in a setTimeout().
22898 // tags:
22899 // protected
22900 if("_currentFocus" in this){
22901 // Remove tabIndex on old cell
22902 domAttr.set(this._currentFocus, "tabIndex", "-1");
a089699c 22903 }
a089699c 22904
1354d172
AD
22905 // Set tabIndex of new cell
22906 this._currentFocus = node;
22907 if(node){
22908 domAttr.set(node, "tabIndex", this.tabIndex);
22909 }
a089699c
AD
22910 },
22911
1354d172 22912 _setValueAttr: function(value, priorityChange){
a089699c 22913 // summary:
1354d172
AD
22914 // This selects a cell. It triggers the onChange event.
22915 // value: String value of the cell to select
22916 // tags:
22917 // protected
22918 // priorityChange:
22919 // Optional parameter used to tell the select whether or not to fire
22920 // onChange event.
a089699c 22921
1354d172
AD
22922 // clear old selected cell
22923 if(this._selectedCell >= 0){
22924 domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
a089699c 22925 }
1354d172 22926 this._selectedCell = -1;
a089699c 22927
1354d172
AD
22928 // search for cell matching specified value
22929 if(value){
22930 for(var i = 0; i < this._cells.length; i++){
22931 if(value == this._cells[i].dye.getValue()){
22932 this._selectedCell = i;
22933 domClass.add(this._cells[i].node, this.cellClass + "Selected");
22934 break;
a089699c 22935 }
1354d172
AD
22936 }
22937 }
a089699c 22938
1354d172
AD
22939 // record new value, or null if no matching cell
22940 this._set("value", this._selectedCell >= 0 ? value : null);
a089699c 22941
1354d172
AD
22942 if(priorityChange || priorityChange === undefined){
22943 this.onChange(value);
a089699c 22944 }
a089699c
AD
22945 },
22946
1354d172 22947 onChange: function(/*===== value =====*/){
a089699c 22948 // summary:
1354d172
AD
22949 // Callback when a cell is selected.
22950 // value: String
22951 // Value corresponding to cell.
a089699c
AD
22952 },
22953
1354d172 22954 _navigateByKey: function(increment, typeCount){
a089699c 22955 // summary:
1354d172
AD
22956 // This is the callback for typematic.
22957 // It changes the focus and the highlighed cell.
22958 // increment:
22959 // How much the key is navigated.
22960 // typeCount:
22961 // How many times typematic has fired.
22962 // tags:
22963 // private
a089699c 22964
1354d172
AD
22965 // typecount == -1 means the key is released.
22966 if(typeCount == -1){ return; }
a089699c 22967
1354d172
AD
22968 var newFocusIndex = this._currentFocus.index + increment;
22969 if(newFocusIndex < this._cells.length && newFocusIndex > -1){
22970 var focusNode = this._cells[newFocusIndex].node;
22971 this._setCurrent(focusNode);
a089699c 22972
1354d172
AD
22973 // Actually focus the node, for the benefit of screen readers.
22974 // Use setTimeout because IE doesn't like changing focus inside of an event handler
22975 setTimeout(lang.hitch(dijit, "focus", focusNode), 0);
22976 }
a089699c
AD
22977 },
22978
1354d172 22979 _getDye: function(/*DomNode*/ cell){
a089699c 22980 // summary:
1354d172 22981 // Get JS object for given cell DOMNode
a089699c 22982
1354d172 22983 return this._cells[cell.index].dye;
a089699c
AD
22984 }
22985});
22986
1354d172
AD
22987/*=====
22988declare("dijit.Dye",
22989 null,
22990 {
22991 // summary:
22992 // Interface for the JS Object associated with a palette cell (i.e. DOMNode)
a089699c 22993
1354d172
AD
22994 constructor: function(alias, row, col){
22995 // summary:
22996 // Initialize according to value or alias like "white"
22997 // alias: String
22998 },
81bea17a 22999
1354d172
AD
23000 getValue: function(){
23001 // summary:
23002 // Return "value" of cell; meaning of "value" varies by subclass.
23003 // description:
23004 // For example color hex value, emoticon ascii value etc, entity hex value.
23005 },
a089699c 23006
1354d172
AD
23007 fillCell: function(cell, blankGif){
23008 // summary:
23009 // Add cell DOMNode inner structure
23010 // cell: DomNode
23011 // The surrounding cell
23012 // blankGif: String
23013 // URL for blank cell image
23014 }
23015 }
23016);
23017=====*/
81bea17a 23018
81bea17a
AD
23019});
23020
1354d172
AD
23021},
23022'dijit/form/ValidationTextBox':function(){
23023require({cache:{
23024'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
23025define("dijit/form/ValidationTextBox", [
23026 "dojo/_base/declare", // declare
23027 "dojo/i18n", // i18n.getLocalization
23028 "./TextBox",
23029 "../Tooltip",
23030 "dojo/text!./templates/ValidationTextBox.html",
23031 "dojo/i18n!./nls/validate"
23032], function(declare, i18n, TextBox, Tooltip, template){
81bea17a 23033
1354d172
AD
23034/*=====
23035 var Tooltip = dijit.Tooltip;
23036 var TextBox = dijit.form.TextBox;
23037=====*/
81bea17a 23038
1354d172
AD
23039 // module:
23040 // dijit/form/ValidationTextBox
23041 // summary:
23042 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
81bea17a 23043
81bea17a 23044
1354d172
AD
23045 /*=====
23046 dijit.form.ValidationTextBox.__Constraints = function(){
23047 // locale: String
23048 // locale used for validation, picks up value from this widget's lang attribute
23049 // _flags_: anything
23050 // various flags passed to regExpGen function
23051 this.locale = "";
23052 this._flags_ = "";
81bea17a 23053 }
1354d172 23054 =====*/
a089699c 23055
1354d172
AD
23056 return declare("dijit.form.ValidationTextBox", TextBox, {
23057 // summary:
23058 // Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
23059 // tags:
23060 // protected
a089699c 23061
1354d172
AD
23062 templateString: template,
23063 baseClass: "dijitTextBox dijitValidationTextBox",
a089699c 23064
1354d172
AD
23065 // required: Boolean
23066 // User is required to enter data into this field.
23067 required: false,
a089699c 23068
1354d172
AD
23069 // promptMessage: String
23070 // If defined, display this hint string immediately on focus to the textbox, if empty.
23071 // Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
23072 // Think of this like a tooltip that tells the user what to do, not an error message
23073 // that tells the user what they've done wrong.
23074 //
23075 // Message disappears when user starts typing.
23076 promptMessage: "",
a089699c 23077
1354d172
AD
23078 // invalidMessage: String
23079 // The message to display if value is invalid.
23080 // The translated string value is read from the message file by default.
23081 // Set to "" to use the promptMessage instead.
23082 invalidMessage: "$_unset_$",
a089699c 23083
1354d172
AD
23084 // missingMessage: String
23085 // The message to display if value is empty and the field is required.
23086 // The translated string value is read from the message file by default.
23087 // Set to "" to use the invalidMessage instead.
23088 missingMessage: "$_unset_$",
a089699c 23089
1354d172
AD
23090 // message: String
23091 // Currently error/prompt message.
23092 // When using the default tooltip implementation, this will only be
23093 // displayed when the field is focused.
23094 message: "",
a089699c 23095
1354d172
AD
23096 // constraints: dijit.form.ValidationTextBox.__Constraints
23097 // user-defined object needed to pass parameters to the validator functions
23098 constraints: {},
a089699c 23099
1354d172
AD
23100 // regExp: [extension protected] String
23101 // regular expression string used to validate the input
23102 // Do not specify both regExp and regExpGen
23103 regExp: ".*",
23104
23105 regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/ /*===== constraints =====*/){
a089699c 23106 // summary:
1354d172
AD
23107 // Overridable function used to generate regExp when dependent on constraints.
23108 // Do not specify both regExp and regExpGen.
a089699c 23109 // tags:
1354d172
AD
23110 // extension protected
23111 return this.regExp; // String
23112 },
a089699c 23113
1354d172
AD
23114 // state: [readonly] String
23115 // Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
23116 state: "",
a089699c 23117
1354d172
AD
23118 // tooltipPosition: String[]
23119 // See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
23120 tooltipPosition: [],
a089699c 23121
1354d172
AD
23122 _setValueAttr: function(){
23123 // summary:
23124 // Hook so set('value', ...) works.
a089699c 23125 this.inherited(arguments);
1354d172
AD
23126 this.validate(this.focused);
23127 },
a089699c 23128
1354d172
AD
23129 validator: function(/*anything*/ value, /*dijit.form.ValidationTextBox.__Constraints*/ constraints){
23130 // summary:
23131 // Overridable function used to validate the text input against the regular expression.
23132 // tags:
23133 // protected
23134 return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
23135 (!this.required || !this._isEmpty(value)) &&
23136 (this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
23137 },
a089699c 23138
1354d172
AD
23139 _isValidSubset: function(){
23140 // summary:
23141 // Returns true if the value is either already valid or could be made valid by appending characters.
23142 // This is used for validation while the user [may be] still typing.
23143 return this.textbox.value.search(this._partialre) == 0;
23144 },
a089699c 23145
1354d172
AD
23146 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
23147 // summary:
23148 // Tests if value is valid.
23149 // Can override with your own routine in a subclass.
23150 // tags:
23151 // protected
23152 return this.validator(this.textbox.value, this.constraints);
23153 },
a089699c 23154
1354d172
AD
23155 _isEmpty: function(value){
23156 // summary:
23157 // Checks for whitespace
23158 return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
23159 },
a089699c 23160
1354d172
AD
23161 getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
23162 // summary:
23163 // Return an error message to show if appropriate
23164 // tags:
23165 // protected
23166 return (this.required && this._isEmpty(this.textbox.value)) ? this.missingMessage : this.invalidMessage; // String
23167 },
a089699c 23168
1354d172
AD
23169 getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
23170 // summary:
23171 // Return a hint message to show when widget is first focused
23172 // tags:
23173 // protected
23174 return this.promptMessage; // String
23175 },
a089699c 23176
1354d172
AD
23177 _maskValidSubsetError: true,
23178 validate: function(/*Boolean*/ isFocused){
23179 // summary:
23180 // Called by oninit, onblur, and onkeypress.
23181 // description:
23182 // Show missing or invalid messages if appropriate, and highlight textbox field.
23183 // tags:
23184 // protected
23185 var message = "";
23186 var isValid = this.disabled || this.isValid(isFocused);
23187 if(isValid){ this._maskValidSubsetError = true; }
23188 var isEmpty = this._isEmpty(this.textbox.value);
23189 var isValidSubset = !isValid && isFocused && this._isValidSubset();
23190 this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && this._maskValidSubsetError) ? "Incomplete" : "Error"));
23191 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
a089699c 23192
1354d172
AD
23193 if(this.state == "Error"){
23194 this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
23195 message = this.getErrorMessage(isFocused);
23196 }else if(this.state == "Incomplete"){
23197 message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
23198 this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
23199 }else if(isEmpty){
23200 message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
23201 }
23202 this.set("message", message);
a089699c 23203
1354d172
AD
23204 return isValid;
23205 },
a089699c 23206
1354d172
AD
23207 displayMessage: function(/*String*/ message){
23208 // summary:
23209 // Overridable method to display validation errors/hints.
23210 // By default uses a tooltip.
23211 // tags:
23212 // extension
23213 if(message && this.focused){
23214 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
23215 }else{
23216 Tooltip.hide(this.domNode);
23217 }
23218 },
a089699c 23219
1354d172
AD
23220 _refreshState: function(){
23221 // Overrides TextBox._refreshState()
23222 this.validate(this.focused);
23223 this.inherited(arguments);
23224 },
a089699c 23225
1354d172 23226 //////////// INITIALIZATION METHODS ///////////////////////////////////////
a089699c 23227
1354d172
AD
23228 constructor: function(){
23229 this.constraints = {};
23230 },
a089699c 23231
1354d172
AD
23232 _setConstraintsAttr: function(/*Object*/ constraints){
23233 if(!constraints.locale && this.lang){
23234 constraints.locale = this.lang;
23235 }
23236 this._set("constraints", constraints);
23237 this._computePartialRE();
23238 },
a089699c 23239
1354d172
AD
23240 _computePartialRE: function(){
23241 var p = this.regExpGen(this.constraints);
23242 this.regExp = p;
23243 var partialre = "";
23244 // parse the regexp and produce a new regexp that matches valid subsets
23245 // if the regexp is .* then there's no use in matching subsets since everything is valid
23246 if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
23247 function(re){
23248 switch(re.charAt(0)){
23249 case '{':
23250 case '+':
23251 case '?':
23252 case '*':
23253 case '^':
23254 case '$':
23255 case '|':
23256 case '(':
23257 partialre += re;
23258 break;
23259 case ")":
23260 partialre += "|$)";
23261 break;
23262 default:
23263 partialre += "(?:"+re+"|$)";
23264 break;
23265 }
23266 }
23267 );}
23268 try{ // this is needed for now since the above regexp parsing needs more test verification
23269 "".search(partialre);
23270 }catch(e){ // should never be here unless the original RE is bad or the parsing is bad
23271 partialre = this.regExp;
23272 console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
23273 } // should never be here unless the original RE is bad or the parsing is bad
23274 this._partialre = "^(?:" + partialre + ")$";
23275 },
a089699c 23276
1354d172
AD
23277 postMixInProperties: function(){
23278 this.inherited(arguments);
23279 this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
23280 if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
23281 if(!this.invalidMessage){ this.invalidMessage = this.promptMessage; }
23282 if(this.missingMessage == "$_unset_$"){ this.missingMessage = this.messages.missingMessage; }
23283 if(!this.missingMessage){ this.missingMessage = this.invalidMessage; }
23284 this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
23285 },
a089699c 23286
1354d172
AD
23287 _setDisabledAttr: function(/*Boolean*/ value){
23288 this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
23289 this._refreshState();
23290 },
a089699c 23291
1354d172
AD
23292 _setRequiredAttr: function(/*Boolean*/ value){
23293 this._set("required", value);
23294 this.focusNode.setAttribute("aria-required", value);
23295 this._refreshState();
23296 },
a089699c 23297
1354d172
AD
23298 _setMessageAttr: function(/*String*/ message){
23299 this._set("message", message);
23300 this.displayMessage(message);
23301 },
a089699c 23302
1354d172
AD
23303 reset:function(){
23304 // Overrides dijit.form.TextBox.reset() by also
23305 // hiding errors about partial matches
23306 this._maskValidSubsetError = true;
23307 this.inherited(arguments);
23308 },
a089699c 23309
1354d172
AD
23310 _onBlur: function(){
23311 // the message still exists but for back-compat, and to erase the tooltip
23312 // (if the message is being displayed as a tooltip), call displayMessage('')
23313 this.displayMessage('');
a089699c 23314
1354d172 23315 this.inherited(arguments);
a089699c 23316 }
1354d172
AD
23317 });
23318});
a089699c 23319
1354d172
AD
23320},
23321'dijit/_base/typematic':function(){
23322define("dijit/_base/typematic", ["../typematic"], function(){
23323 // for back-compat, just loads top level module
23324});
a089699c 23325
1354d172
AD
23326},
23327'dijit/_base':function(){
23328define("dijit/_base", [
23329 ".",
23330 "./a11y", // used to be in dijit/_base/manager
23331 "./WidgetSet", // used to be in dijit/_base/manager
23332 "./_base/focus",
23333 "./_base/manager",
23334 "./_base/place",
23335 "./_base/popup",
23336 "./_base/scroll",
23337 "./_base/sniff",
23338 "./_base/typematic",
23339 "./_base/wai",
23340 "./_base/window"
23341], function(dijit){
23342
23343 // module:
23344 // dijit/_base
23345 // summary:
23346 // Includes all the modules in dijit/_base
a089699c 23347
1354d172
AD
23348 return dijit._base;
23349});
a089699c 23350
1354d172
AD
23351},
23352'dijit/layout/BorderContainer':function(){
23353define("dijit/layout/BorderContainer", [
23354 "dojo/_base/array", // array.filter array.forEach array.map
23355 "dojo/cookie", // cookie
23356 "dojo/_base/declare", // declare
23357 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
23358 "dojo/dom-construct", // domConstruct.destroy domConstruct.place
23359 "dojo/dom-geometry", // domGeometry.marginBox
23360 "dojo/dom-style", // domStyle.style
23361 "dojo/_base/event", // event.stop
23362 "dojo/keys",
23363 "dojo/_base/lang", // lang.getObject lang.hitch
23364 "dojo/on",
23365 "dojo/touch",
23366 "dojo/_base/window", // win.body win.doc win.doc.createElement
23367 "../_WidgetBase",
23368 "../_Widget",
23369 "../_TemplatedMixin",
23370 "./_LayoutWidget",
23371 "./utils" // layoutUtils.layoutChildren
23372], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch, win,
23373 _WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
a089699c
AD
23374
23375/*=====
1354d172
AD
23376 var _WidgetBase = dijit._WidgetBase;
23377 var _Widget = dijit._Widget;
23378 var _TemplatedMixin = dijit._TemplatedMixin;
23379 var _LayoutWidget = dijit.layout._LayoutWidget;
a089699c 23380=====*/
a089699c 23381
1354d172
AD
23382// module:
23383// dijit/layout/BorderContainer
23384// summary:
23385// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
a089699c 23386
1354d172
AD
23387var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ],
23388{
23389 // summary:
23390 // A draggable spacer between two items in a `dijit.layout.BorderContainer`.
23391 // description:
23392 // This is instantiated by `dijit.layout.BorderContainer`. Users should not
23393 // create it directly.
23394 // tags:
23395 // private
a089699c 23396
1354d172
AD
23397/*=====
23398 // container: [const] dijit.layout.BorderContainer
23399 // Pointer to the parent BorderContainer
23400 container: null,
a089699c 23401
1354d172
AD
23402 // child: [const] dijit.layout._LayoutWidget
23403 // Pointer to the pane associated with this splitter
23404 child: null,
a089699c 23405
1354d172
AD
23406 // region: [const] String
23407 // Region of pane associated with this splitter.
23408 // "top", "bottom", "left", "right".
23409 region: null,
23410=====*/
a089699c 23411
1354d172
AD
23412 // live: [const] Boolean
23413 // If true, the child's size changes and the child widget is redrawn as you drag the splitter;
23414 // otherwise, the size doesn't change until you drop the splitter (by mouse-up)
23415 live: true,
a089699c 23416
1354d172 23417 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>',
a089699c 23418
1354d172
AD
23419 constructor: function(){
23420 this._handlers = [];
23421 },
a089699c 23422
1354d172
AD
23423 postMixInProperties: function(){
23424 this.inherited(arguments);
a089699c 23425
1354d172
AD
23426 this.horizontal = /top|bottom/.test(this.region);
23427 this._factor = /top|left/.test(this.region) ? 1 : -1;
23428 this._cookieName = this.container.id + "_" + this.region;
23429 },
a089699c 23430
1354d172
AD
23431 buildRendering: function(){
23432 this.inherited(arguments);
a089699c 23433
1354d172 23434 domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
a089699c 23435
1354d172
AD
23436 if(this.container.persist){
23437 // restore old size
23438 var persistSize = cookie(this._cookieName);
23439 if(persistSize){
23440 this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
23441 }
23442 }
23443 },
a089699c 23444
1354d172
AD
23445 _computeMaxSize: function(){
23446 // summary:
23447 // Return the maximum size that my corresponding pane can be set to
a089699c 23448
1354d172
AD
23449 var dim = this.horizontal ? 'h' : 'w',
23450 childSize = domGeometry.getMarginBox(this.child.domNode)[dim],
23451 center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
23452 spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0
a089699c 23453
1354d172
AD
23454 return Math.min(this.child.maxSize, childSize + spaceAvailable);
23455 },
23456
23457 _startDrag: function(e){
23458 if(!this.cover){
23459 this.cover = win.doc.createElement('div');
23460 domClass.add(this.cover, "dijitSplitterCover");
23461 domConstruct.place(this.cover, this.child.domNode, "after");
a089699c 23462 }
1354d172 23463 domClass.add(this.cover, "dijitSplitterCoverActive");
a089699c 23464
1354d172
AD
23465 // Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
23466 if(this.fake){ domConstruct.destroy(this.fake); }
23467 if(!(this._resize = this.live)){ //TODO: disable live for IE6?
23468 // create fake splitter to display at old position while we drag
23469 (this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
23470 domClass.add(this.domNode, "dijitSplitterShadow");
23471 domConstruct.place(this.fake, this.domNode, "after");
23472 }
23473 domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
23474 if(this.fake){
23475 domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
23476 }
81bea17a 23477
1354d172
AD
23478 //Performance: load data info local vars for onmousevent function closure
23479 var factor = this._factor,
23480 isHorizontal = this.horizontal,
23481 axis = isHorizontal ? "pageY" : "pageX",
23482 pageStart = e[axis],
23483 splitterStyle = this.domNode.style,
23484 dim = isHorizontal ? 'h' : 'w',
23485 childStart = domGeometry.getMarginBox(this.child.domNode)[dim],
23486 max = this._computeMaxSize(),
23487 min = this.child.minSize || 20,
23488 region = this.region,
23489 splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
23490 splitterStart = parseInt(splitterStyle[splitterAttr], 10),
23491 resize = this._resize,
23492 layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id),
23493 de = win.doc;
81bea17a 23494
1354d172
AD
23495 this._handlers = this._handlers.concat([
23496 on(de, touch.move, this._drag = function(e, forceResize){
23497 var delta = e[axis] - pageStart,
23498 childSize = factor * delta + childStart,
23499 boundChildSize = Math.max(Math.min(childSize, max), min);
81bea17a 23500
1354d172
AD
23501 if(resize || forceResize){
23502 layoutFunc(boundChildSize);
23503 }
23504 // TODO: setting style directly (usually) sets content box size, need to set margin box size
23505 splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
23506 }),
23507 on(de, "dragstart", event.stop),
23508 on(win.body(), "selectstart", event.stop),
23509 on(de, touch.release, lang.hitch(this, "_stopDrag"))
23510 ]);
23511 event.stop(e);
23512 },
81bea17a 23513
1354d172
AD
23514 _onMouse: function(e){
23515 // summary:
23516 // Handler for onmouseenter / onmouseleave events
23517 var o = (e.type == "mouseover" || e.type == "mouseenter");
23518 domClass.toggle(this.domNode, "dijitSplitterHover", o);
23519 domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
23520 },
81bea17a 23521
1354d172
AD
23522 _stopDrag: function(e){
23523 try{
23524 if(this.cover){
23525 domClass.remove(this.cover, "dijitSplitterCoverActive");
81bea17a 23526 }
1354d172
AD
23527 if(this.fake){ domConstruct.destroy(this.fake); }
23528 domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter"
23529 + (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
23530 this._drag(e); //TODO: redundant with onmousemove?
23531 this._drag(e, true);
23532 }finally{
23533 this._cleanupHandlers();
23534 delete this._drag;
23535 }
81bea17a 23536
1354d172
AD
23537 if(this.container.persist){
23538 cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
23539 }
23540 },
81bea17a 23541
1354d172
AD
23542 _cleanupHandlers: function(){
23543 var h;
23544 while(h = this._handlers.pop()){ h.remove(); }
23545 },
81bea17a 23546
1354d172
AD
23547 _onKeyPress: function(/*Event*/ e){
23548 // should we apply typematic to this?
23549 this._resize = true;
23550 var horizontal = this.horizontal;
23551 var tick = 1;
23552 switch(e.charOrCode){
23553 case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW:
23554 tick *= -1;
23555// break;
23556 case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW:
23557 break;
23558 default:
23559// this.inherited(arguments);
23560 return;
23561 }
23562 var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
23563 this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
23564 event.stop(e);
23565 },
81bea17a 23566
1354d172
AD
23567 destroy: function(){
23568 this._cleanupHandlers();
23569 delete this.child;
23570 delete this.container;
23571 delete this.cover;
23572 delete this.fake;
23573 this.inherited(arguments);
23574 }
23575});
81bea17a 23576
1354d172
AD
23577var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin],
23578{
23579 // summary:
23580 // Just a spacer div to separate side pane from center pane.
23581 // Basically a trick to lookup the gutter/splitter width from the theme.
23582 // description:
23583 // Instantiated by `dijit.layout.BorderContainer`. Users should not
23584 // create directly.
23585 // tags:
23586 // private
81bea17a 23587
1354d172 23588 templateString: '<div class="dijitGutter" role="presentation"></div>',
81bea17a 23589
1354d172
AD
23590 postMixInProperties: function(){
23591 this.inherited(arguments);
23592 this.horizontal = /top|bottom/.test(this.region);
23593 },
81bea17a 23594
1354d172
AD
23595 buildRendering: function(){
23596 this.inherited(arguments);
23597 domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
23598 }
23599});
81bea17a 23600
1354d172 23601var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, {
81bea17a 23602 // summary:
1354d172
AD
23603 // Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
23604 //
23605 // description:
23606 // A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
23607 // that contains a child widget marked region="center" and optionally children widgets marked
23608 // region equal to "top", "bottom", "leading", "trailing", "left" or "right".
23609 // Children along the edges will be laid out according to width or height dimensions and may
23610 // include optional splitters (splitter="true") to make them resizable by the user. The remaining
23611 // space is designated for the center region.
23612 //
23613 // The outer size must be specified on the BorderContainer node. Width must be specified for the sides
23614 // and height for the top and bottom, respectively. No dimensions should be specified on the center;
23615 // it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
23616 // "left" and "right" except that they will be reversed in right-to-left environments.
81bea17a 23617 //
1354d172
AD
23618 // For complex layouts, multiple children can be specified for a single region. In this case, the
23619 // layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
23620 // and which child is closer to the center (high layoutPriority). layoutPriority can also be used
23621 // instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
81bea17a 23622 // example:
1354d172
AD
23623 // | <div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
23624 // | style="width: 400px; height: 300px;">
23625 // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'top'">header text</div>
23626 // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
23627 // | <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region: 'center'">client area</div>
81bea17a
AD
23628 // | </div>
23629
1354d172
AD
23630 // design: String
23631 // Which design is used for the layout:
23632 // - "headline" (default) where the top and bottom extend
23633 // the full width of the container
23634 // - "sidebar" where the left and right sides extend from top to bottom.
23635 design: "headline",
81bea17a 23636
1354d172
AD
23637 // gutters: [const] Boolean
23638 // Give each pane a border and margin.
23639 // Margin determined by domNode.paddingLeft.
23640 // When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
23641 gutters: true,
81bea17a 23642
1354d172
AD
23643 // liveSplitters: [const] Boolean
23644 // Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
23645 liveSplitters: true,
81bea17a 23646
1354d172
AD
23647 // persist: Boolean
23648 // Save splitter positions in a cookie.
23649 persist: false,
81bea17a 23650
1354d172 23651 baseClass: "dijitBorderContainer",
81bea17a 23652
1354d172
AD
23653 // _splitterClass: Function||String
23654 // Optional hook to override the default Splitter widget used by BorderContainer
23655 _splitterClass: _Splitter,
81bea17a
AD
23656
23657 postMixInProperties: function(){
1354d172
AD
23658 // change class name to indicate that BorderContainer is being used purely for
23659 // layout (like LayoutContainer) rather than for pretty formatting.
23660 if(!this.gutters){
23661 this.baseClass += "NoGutter";
81bea17a 23662 }
1354d172 23663 this.inherited(arguments);
81bea17a
AD
23664 },
23665
1354d172
AD
23666 startup: function(){
23667 if(this._started){ return; }
23668 array.forEach(this.getChildren(), this._setupChild, this);
81bea17a 23669 this.inherited(arguments);
81bea17a
AD
23670 },
23671
1354d172
AD
23672 _setupChild: function(/*dijit._Widget*/ child){
23673 // Override _LayoutWidget._setupChild().
81bea17a 23674
1354d172
AD
23675 var region = child.region;
23676 if(region){
23677 this.inherited(arguments);
81bea17a 23678
1354d172 23679 domClass.add(child.domNode, this.baseClass+"Pane");
81bea17a 23680
1354d172
AD
23681 var ltr = this.isLeftToRight();
23682 if(region == "leading"){ region = ltr ? "left" : "right"; }
23683 if(region == "trailing"){ region = ltr ? "right" : "left"; }
81bea17a 23684
1354d172
AD
23685 // Create draggable splitter for resizing pane,
23686 // or alternately if splitter=false but BorderContainer.gutters=true then
23687 // insert dummy div just for spacing
23688 if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
23689 var _Splitter = child.splitter ? this._splitterClass : _Gutter;
23690 if(lang.isString(_Splitter)){
23691 _Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
23692 }
23693 var splitter = new _Splitter({
23694 id: child.id + "_splitter",
23695 container: this,
23696 child: child,
23697 region: region,
23698 live: this.liveSplitters
23699 });
23700 splitter.isSplitter = true;
23701 child._splitterWidget = splitter;
23702
23703 domConstruct.place(splitter.domNode, child.domNode, "after");
23704
23705 // Splitters aren't added as Contained children, so we need to call startup explicitly
23706 splitter.startup();
23707 }
23708 child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
81bea17a
AD
23709 }
23710 },
23711
1354d172
AD
23712 layout: function(){
23713 // Implement _LayoutWidget.layout() virtual method.
23714 this._layoutChildren();
81bea17a
AD
23715 },
23716
1354d172
AD
23717 addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
23718 // Override _LayoutWidget.addChild().
23719 this.inherited(arguments);
23720 if(this._started){
23721 this.layout(); //OPT
23722 }
81bea17a
AD
23723 },
23724
1354d172
AD
23725 removeChild: function(/*dijit._Widget*/ child){
23726 // Override _LayoutWidget.removeChild().
81bea17a 23727
1354d172
AD
23728 var region = child.region;
23729 var splitter = child._splitterWidget;
23730 if(splitter){
23731 splitter.destroy();
23732 delete child._splitterWidget;
23733 }
23734 this.inherited(arguments);
23735
23736 if(this._started){
23737 this._layoutChildren();
23738 }
23739 // Clean up whatever style changes we made to the child pane.
23740 // Unclear how height and width should be handled.
23741 domClass.remove(child.domNode, this.baseClass+"Pane");
23742 domStyle.set(child.domNode, {
23743 top: "auto",
23744 bottom: "auto",
23745 left: "auto",
23746 right: "auto",
23747 position: "static"
23748 });
23749 domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
81bea17a
AD
23750 },
23751
1354d172
AD
23752 getChildren: function(){
23753 // Override _LayoutWidget.getChildren() to only return real children, not the splitters.
23754 return array.filter(this.inherited(arguments), function(widget){
23755 return !widget.isSplitter;
23756 });
23757 },
23758
23759 // TODO: remove in 2.0
23760 getSplitter: function(/*String*/region){
81bea17a 23761 // summary:
1354d172 23762 // Returns the widget responsible for rendering the splitter associated with region
81bea17a 23763 // tags:
1354d172
AD
23764 // deprecated
23765 return array.filter(this.getChildren(), function(child){
23766 return child.region == region;
23767 })[0]._splitterWidget;
23768 },
81bea17a 23769
1354d172
AD
23770 resize: function(newSize, currentSize){
23771 // Overrides _LayoutWidget.resize().
81bea17a 23772
1354d172
AD
23773 // resetting potential padding to 0px to provide support for 100% width/height + padding
23774 // TODO: this hack doesn't respect the box model and is a temporary fix
23775 if(!this.cs || !this.pe){
23776 var node = this.domNode;
23777 this.cs = domStyle.getComputedStyle(node);
23778 this.pe = domGeometry.getPadExtents(node, this.cs);
23779 this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight);
23780 this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom);
81bea17a 23781
1354d172
AD
23782 domStyle.set(node, "padding", "0px");
23783 }
81bea17a 23784
1354d172
AD
23785 this.inherited(arguments);
23786 },
81bea17a 23787
1354d172 23788 _layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
81bea17a 23789 // summary:
1354d172
AD
23790 // This is the main routine for setting size/position of each child.
23791 // description:
23792 // With no arguments, measures the height of top/bottom panes, the width
23793 // of left/right panes, and then sizes all panes accordingly.
23794 //
23795 // With changedRegion specified (as "left", "top", "bottom", or "right"),
23796 // it changes that region's width/height to changedRegionSize and
23797 // then resizes other regions that were affected.
23798 // changedChildId:
23799 // Id of the child which should be resized because splitter was dragged.
23800 // changedChildSize:
23801 // The new width/height (in pixels) to make specified child
81bea17a 23802
1354d172
AD
23803 if(!this._borderBox || !this._borderBox.h){
23804 // We are currently hidden, or we haven't been sized by our parent yet.
23805 // Abort. Someone will resize us later.
23806 return;
23807 }
81bea17a 23808
1354d172
AD
23809 // Generate list of wrappers of my children in the order that I want layoutChildren()
23810 // to process them (i.e. from the outside to the inside)
23811 var wrappers = array.map(this.getChildren(), function(child, idx){
23812 return {
23813 pane: child,
23814 weight: [
23815 child.region == "center" ? Infinity : 0,
23816 child.layoutPriority,
23817 (this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
23818 idx
23819 ]
23820 };
23821 }, this);
23822 wrappers.sort(function(a, b){
23823 var aw = a.weight, bw = b.weight;
23824 for(var i=0; i<aw.length; i++){
23825 if(aw[i] != bw[i]){
23826 return aw[i] - bw[i];
23827 }
23828 }
23829 return 0;
23830 });
81bea17a 23831
1354d172
AD
23832 // Make new list, combining the externally specified children with splitters and gutters
23833 var childrenAndSplitters = [];
23834 array.forEach(wrappers, function(wrapper){
23835 var pane = wrapper.pane;
23836 childrenAndSplitters.push(pane);
23837 if(pane._splitterWidget){
23838 childrenAndSplitters.push(pane._splitterWidget);
23839 }
23840 });
81bea17a 23841
1354d172
AD
23842 // Compute the box in which to lay out my children
23843 var dim = {
23844 l: this.pe.l,
23845 t: this.pe.t,
23846 w: this._borderBox.w - this.pe.w,
23847 h: this._borderBox.h - this.pe.h
23848 };
81bea17a 23849
1354d172
AD
23850 // Layout the children, possibly changing size due to a splitter drag
23851 layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
23852 changedChildId, changedChildSize);
23853 },
81bea17a 23854
1354d172
AD
23855 destroyRecursive: function(){
23856 // Destroy splitters first, while getChildren() still works
23857 array.forEach(this.getChildren(), function(child){
23858 var splitter = child._splitterWidget;
23859 if(splitter){
23860 splitter.destroy();
23861 }
23862 delete child._splitterWidget;
23863 });
81bea17a 23864
1354d172
AD
23865 // Then destroy the real children, and myself
23866 this.inherited(arguments);
23867 }
23868});
81bea17a 23869
1354d172
AD
23870// This argument can be specified for the children of a BorderContainer.
23871// Since any widget can be specified as a LayoutContainer child, mix it
23872// into the base widget class. (This is a hack, but it's effective.)
23873lang.extend(_WidgetBase, {
23874 // region: [const] String
23875 // Parameter for children of `dijit.layout.BorderContainer`.
23876 // Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
23877 // See the `dijit.layout.BorderContainer` description for details.
23878 region: '',
81bea17a 23879
1354d172
AD
23880 // layoutPriority: [const] Number
23881 // Parameter for children of `dijit.layout.BorderContainer`.
23882 // Children with a higher layoutPriority will be placed closer to the BorderContainer center,
23883 // between children with a lower layoutPriority.
23884 layoutPriority: 0,
81bea17a 23885
1354d172
AD
23886 // splitter: [const] Boolean
23887 // Parameter for child of `dijit.layout.BorderContainer` where region != "center".
23888 // If true, enables user to resize the widget by putting a draggable splitter between
23889 // this widget and the region=center widget.
23890 splitter: false,
81bea17a 23891
1354d172
AD
23892 // minSize: [const] Number
23893 // Parameter for children of `dijit.layout.BorderContainer`.
23894 // Specifies a minimum size (in pixels) for this widget when resized by a splitter.
23895 minSize: 0,
81bea17a 23896
1354d172
AD
23897 // maxSize: [const] Number
23898 // Parameter for children of `dijit.layout.BorderContainer`.
23899 // Specifies a maximum size (in pixels) for this widget when resized by a splitter.
23900 maxSize: Infinity
23901});
a089699c 23902
1354d172
AD
23903// For monkey patching
23904BorderContainer._Splitter = _Splitter;
23905BorderContainer._Gutter = _Gutter;
a089699c 23906
1354d172
AD
23907return BorderContainer;
23908});
a089699c 23909
1354d172
AD
23910},
23911'dojo/window':function(){
23912define("dojo/window", ["./_base/lang", "./_base/sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
23913 function(lang, has, baseWindow, dom, geom, style) {
81bea17a 23914
1354d172
AD
23915// module:
23916// dojo/window
23917// summary:
23918// TODOC
81bea17a 23919
1354d172 23920var window = lang.getObject("dojo.window", true);
a089699c 23921
1354d172
AD
23922/*=====
23923dojo.window = {
23924 // summary:
23925 // TODO
23926};
23927window = dojo.window;
23928=====*/
a089699c 23929
1354d172
AD
23930window.getBox = function(){
23931 // summary:
23932 // Returns the dimensions and scroll position of the viewable area of a browser window
a089699c 23933
1354d172
AD
23934 var
23935 scrollRoot = (baseWindow.doc.compatMode == 'BackCompat') ? baseWindow.body() : baseWindow.doc.documentElement,
23936 // get scroll position
23937 scroll = geom.docScroll(), // scrollRoot.scrollTop/Left should work
23938 w, h;
23939
23940 if(has("touch")){ // if(scrollbars not supported)
23941 var uiWindow = baseWindow.doc.parentWindow || baseWindow.doc.defaultView; // use UI window, not dojo.global window. baseWindow.doc.parentWindow probably not needed since it's not defined for webkit
23942 // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
23943 w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated
23944 h = uiWindow.innerHeight || scrollRoot.clientHeight;
23945 }else{
23946 // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
23947 // uiWindow.innerWidth/Height includes the scrollbar and cannot be used
23948 w = scrollRoot.clientWidth;
23949 h = scrollRoot.clientHeight;
23950 }
23951 return {
23952 l: scroll.x,
23953 t: scroll.y,
23954 w: w,
23955 h: h
23956 };
23957};
a089699c 23958
1354d172 23959window.get = function(doc){
81bea17a 23960 // summary:
1354d172
AD
23961 // Get window object associated with document doc
23962
23963 // In some IE versions (at least 6.0), document.parentWindow does not return a
23964 // reference to the real window object (maybe a copy), so we must fix it as well
23965 // We use IE specific execScript to attach the real window reference to
23966 // document._parentWindow for later use
23967 if(has("ie") && window !== document.parentWindow){
23968 /*
23969 In IE 6, only the variable "window" can be used to connect events (others
23970 may be only copies).
23971 */
23972 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
23973 //to prevent memory leak, unset it after use
23974 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
23975 var win = doc._parentWindow;
23976 doc._parentWindow = null;
23977 return win; // Window
81bea17a 23978 }
1354d172
AD
23979
23980 return doc.parentWindow || doc.defaultView; // Window
23981};
23982
23983window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
23984 // summary:
23985 // Scroll the passed node into view, if it is not already.
23986
23987 // don't rely on node.scrollIntoView working just because the function is there
23988
23989 try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method
23990 node = dom.byId(node);
23991 var doc = node.ownerDocument || baseWindow.doc,
23992 body = doc.body || baseWindow.body(),
23993 html = doc.documentElement || body.parentNode,
23994 isIE = has("ie"), isWK = has("webkit");
23995 // if an untested browser, then use the native method
23996 if((!(has("mozilla") || isIE || isWK || has("opera")) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
23997 node.scrollIntoView(false); // short-circuit to native if possible
23998 return;
23999 }
24000 var backCompat = doc.compatMode == 'BackCompat',
24001 clientAreaRoot = (isIE >= 9 && node.ownerDocument.parentWindow.frameElement)
24002 ? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body)
24003 : (backCompat ? body : html),
24004 scrollRoot = isWK ? body : clientAreaRoot,
24005 rootWidth = clientAreaRoot.clientWidth,
24006 rootHeight = clientAreaRoot.clientHeight,
24007 rtl = !geom.isBodyLtr(),
24008 nodePos = pos || geom.position(node),
24009 el = node.parentNode,
24010 isFixed = function(el){
24011 return ((isIE <= 6 || (isIE && backCompat))? false : (style.get(el, 'position').toLowerCase() == "fixed"));
24012 };
24013 if(isFixed(node)){ return; } // nothing to do
24014
24015 while(el){
24016 if(el == body){ el = scrollRoot; }
24017 var elPos = geom.position(el),
24018 fixedPos = isFixed(el);
24019
24020 if(el == scrollRoot){
24021 elPos.w = rootWidth; elPos.h = rootHeight;
24022 if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x
24023 if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0
24024 if(elPos.y < 0 || !isIE){ elPos.y = 0; }
81bea17a 24025 }else{
1354d172
AD
24026 var pb = geom.getPadBorderExtents(el);
24027 elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
24028 var clientSize = el.clientWidth,
24029 scrollBarSize = elPos.w - clientSize;
24030 if(clientSize > 0 && scrollBarSize > 0){
24031 elPos.w = clientSize;
24032 elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0;
24033 }
24034 clientSize = el.clientHeight;
24035 scrollBarSize = elPos.h - clientSize;
24036 if(clientSize > 0 && scrollBarSize > 0){
24037 elPos.h = clientSize;
24038 }
a089699c 24039 }
1354d172
AD
24040 if(fixedPos){ // bounded by viewport, not parents
24041 if(elPos.y < 0){
24042 elPos.h += elPos.y; elPos.y = 0;
24043 }
24044 if(elPos.x < 0){
24045 elPos.w += elPos.x; elPos.x = 0;
24046 }
24047 if(elPos.y + elPos.h > rootHeight){
24048 elPos.h = rootHeight - elPos.y;
24049 }
24050 if(elPos.x + elPos.w > rootWidth){
24051 elPos.w = rootWidth - elPos.x;
24052 }
81bea17a 24053 }
1354d172
AD
24054 // calculate overflow in all 4 directions
24055 var l = nodePos.x - elPos.x, // beyond left: < 0
24056 t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0
24057 r = l + nodePos.w - elPos.w, // beyond right: > 0
24058 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0
24059 if(r * l > 0){
24060 var s = Math[l < 0? "max" : "min"](l, r);
24061 if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
24062 nodePos.x += el.scrollLeft;
24063 el.scrollLeft += s;
24064 nodePos.x -= el.scrollLeft;
81bea17a 24065 }
1354d172
AD
24066 if(bot * t > 0){
24067 nodePos.y += el.scrollTop;
24068 el.scrollTop += Math[t < 0? "max" : "min"](t, bot);
24069 nodePos.y -= el.scrollTop;
81bea17a 24070 }
1354d172 24071 el = (el != scrollRoot) && !fixedPos && el.parentNode;
81bea17a 24072 }
1354d172
AD
24073 }catch(error){
24074 console.error('scrollIntoView: ' + error);
24075 node.scrollIntoView(false);
24076 }
81bea17a 24077};
a089699c 24078
1354d172
AD
24079return window;
24080});
24081
24082},
24083'dojo/number':function(){
24084define("dojo/number", ["./_base/kernel", "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
24085 function(dojo, lang, i18n, nlsNumber, dstring, dregexp) {
24086
24087 // module:
24088 // dojo/number
81bea17a 24089 // summary:
1354d172 24090 // TODOC
a089699c 24091
1354d172 24092lang.getObject("number", true, dojo);
a089699c 24093
1354d172
AD
24094/*=====
24095dojo.number = {
24096 // summary: localized formatting and parsing routines for Number
81bea17a 24097}
a089699c 24098
1354d172
AD
24099dojo.number.__FormatOptions = function(){
24100 // pattern: String?
24101 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
24102 // with this string. Default value is based on locale. Overriding this property will defeat
24103 // localization. Literal characters in patterns are not supported.
24104 // type: String?
24105 // choose a format type based on the locale from the following:
24106 // decimal, scientific (not yet supported), percent, currency. decimal by default.
24107 // places: Number?
24108 // fixed number of decimal places to show. This overrides any
24109 // information in the provided pattern.
24110 // round: Number?
24111 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
24112 // means do not round.
24113 // locale: String?
24114 // override the locale used to determine formatting rules
24115 // fractional: Boolean?
24116 // If false, show no decimal places, overriding places and pattern settings.
24117 this.pattern = pattern;
24118 this.type = type;
24119 this.places = places;
24120 this.round = round;
24121 this.locale = locale;
24122 this.fractional = fractional;
24123}
24124=====*/
a089699c 24125
1354d172
AD
24126dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
24127 // summary:
24128 // Format a Number as a String, using locale-specific settings
24129 // description:
24130 // Create a string from a Number using a known localized pattern.
24131 // Formatting patterns appropriate to the locale are chosen from the
24132 // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
24133 // delimiters.
24134 // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
24135 // value:
24136 // the number to be formatted
a089699c 24137
1354d172
AD
24138 options = lang.mixin({}, options || {});
24139 var locale = i18n.normalizeLocale(options.locale),
24140 bundle = i18n.getLocalization("dojo.cldr", "number", locale);
24141 options.customs = bundle;
24142 var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
24143 if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
24144 return dojo.number._applyPattern(value, pattern, options); // String
24145};
a089699c 24146
1354d172
AD
24147//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
24148dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
a089699c 24149
1354d172
AD
24150dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
24151 // summary:
24152 // Apply pattern to format value as a string using options. Gives no
24153 // consideration to local customs.
24154 // value:
24155 // the number to be formatted.
24156 // pattern:
24157 // a pattern string as described by
24158 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
24159 // options: dojo.number.__FormatOptions?
24160 // _applyPattern is usually called via `dojo.number.format()` which
24161 // populates an extra property in the options parameter, "customs".
24162 // The customs object specifies group and decimal parameters if set.
a089699c 24163
1354d172
AD
24164 //TODO: support escapes
24165 options = options || {};
24166 var group = options.customs.group,
24167 decimal = options.customs.decimal,
24168 patternList = pattern.split(';'),
24169 positivePattern = patternList[0];
24170 pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
a089699c 24171
1354d172
AD
24172 //TODO: only test against unescaped
24173 if(pattern.indexOf('%') != -1){
24174 value *= 100;
24175 }else if(pattern.indexOf('\u2030') != -1){
24176 value *= 1000; // per mille
24177 }else if(pattern.indexOf('\u00a4') != -1){
24178 group = options.customs.currencyGroup || group;//mixins instead?
24179 decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
24180 pattern = pattern.replace(/\u00a4{1,3}/, function(match){
24181 var prop = ["symbol", "currency", "displayName"][match.length-1];
24182 return options[prop] || options.currency || "";
24183 });
24184 }else if(pattern.indexOf('E') != -1){
24185 throw new Error("exponential notation not supported");
24186 }
a089699c 24187
1354d172
AD
24188 //TODO: support @ sig figs?
24189 var numberPatternRE = dojo.number._numberPatternRE;
24190 var numberPattern = positivePattern.match(numberPatternRE);
24191 if(!numberPattern){
24192 throw new Error("unable to find a number expression in pattern: "+pattern);
24193 }
24194 if(options.fractional === false){ options.places = 0; }
24195 return pattern.replace(numberPatternRE,
24196 dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
24197};
a089699c 24198
1354d172
AD
24199dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
24200 // summary:
24201 // Rounds to the nearest value with the given number of decimal places, away from zero
24202 // description:
24203 // Rounds to the nearest value with the given number of decimal places, away from zero if equal.
24204 // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
24205 // fractional increments also, such as the nearest quarter.
24206 // NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
24207 // value:
24208 // The number to round
24209 // places:
24210 // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
24211 // Must be non-negative.
24212 // increment:
24213 // Rounds next place to nearest value of increment/10. 10 by default.
24214 // example:
24215 // >>> dojo.number.round(-0.5)
24216 // -1
24217 // >>> dojo.number.round(162.295, 2)
24218 // 162.29 // note floating point error. Should be 162.3
24219 // >>> dojo.number.round(10.71, 0, 2.5)
24220 // 10.75
24221 var factor = 10 / (increment || 10);
24222 return (factor * +value).toFixed(places) / factor; // Number
24223};
a089699c 24224
1354d172
AD
24225if((0.9).toFixed() == 0){
24226 // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
24227 // is just after the rounding place and is >=5
24228 var round = dojo.number.round;
24229 dojo.number.round = function(v, p, m){
24230 var d = Math.pow(10, -p || 0), a = Math.abs(v);
24231 if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
24232 d = 0;
24233 }
24234 return round(v, p, m) + (v > 0 ? d : -d);
24235 };
24236}
a089699c 24237
1354d172
AD
24238/*=====
24239dojo.number.__FormatAbsoluteOptions = function(){
24240 // decimal: String?
24241 // the decimal separator
24242 // group: String?
24243 // the group separator
24244 // places: Number?|String?
24245 // number of decimal places. the range "n,m" will format to m places.
24246 // round: Number?
24247 // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
24248 // means don't round.
24249 this.decimal = decimal;
24250 this.group = group;
24251 this.places = places;
24252 this.round = round;
24253}
24254=====*/
a089699c 24255
1354d172
AD
24256dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
24257 // summary:
24258 // Apply numeric pattern to absolute value using options. Gives no
24259 // consideration to local customs.
24260 // value:
24261 // the number to be formatted, ignores sign
24262 // pattern:
24263 // the number portion of a pattern (e.g. `#,##0.00`)
24264 options = options || {};
24265 if(options.places === true){options.places=0;}
24266 if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
a089699c 24267
1354d172
AD
24268 var patternParts = pattern.split("."),
24269 comma = typeof options.places == "string" && options.places.indexOf(","),
24270 maxPlaces = options.places;
24271 if(comma){
24272 maxPlaces = options.places.substring(comma + 1);
24273 }else if(!(maxPlaces >= 0)){
24274 maxPlaces = (patternParts[1] || []).length;
24275 }
24276 if(!(options.round < 0)){
24277 value = dojo.number.round(value, maxPlaces, options.round);
24278 }
a089699c 24279
1354d172
AD
24280 var valueParts = String(Math.abs(value)).split("."),
24281 fractional = valueParts[1] || "";
24282 if(patternParts[1] || options.places){
24283 if(comma){
24284 options.places = options.places.substring(0, comma);
24285 }
24286 // Pad fractional with trailing zeros
24287 var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
24288 if(pad > fractional.length){
24289 valueParts[1] = dstring.pad(fractional, pad, '0', true);
24290 }
a089699c 24291
1354d172
AD
24292 // Truncate fractional
24293 if(maxPlaces < fractional.length){
24294 valueParts[1] = fractional.substr(0, maxPlaces);
24295 }
24296 }else{
24297 if(valueParts[1]){ valueParts.pop(); }
24298 }
a089699c 24299
1354d172
AD
24300 // Pad whole with leading zeros
24301 var patternDigits = patternParts[0].replace(',', '');
24302 pad = patternDigits.indexOf("0");
24303 if(pad != -1){
24304 pad = patternDigits.length - pad;
24305 if(pad > valueParts[0].length){
24306 valueParts[0] = dstring.pad(valueParts[0], pad);
24307 }
a089699c 24308
1354d172
AD
24309 // Truncate whole
24310 if(patternDigits.indexOf("#") == -1){
24311 valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
24312 }
24313 }
a089699c 24314
1354d172
AD
24315 // Add group separators
24316 var index = patternParts[0].lastIndexOf(','),
24317 groupSize, groupSize2;
24318 if(index != -1){
24319 groupSize = patternParts[0].length - index - 1;
24320 var remainder = patternParts[0].substr(0, index);
24321 index = remainder.lastIndexOf(',');
24322 if(index != -1){
24323 groupSize2 = remainder.length - index - 1;
24324 }
24325 }
24326 var pieces = [];
24327 for(var whole = valueParts[0]; whole;){
24328 var off = whole.length - groupSize;
24329 pieces.push((off > 0) ? whole.substr(off) : whole);
24330 whole = (off > 0) ? whole.slice(0, off) : "";
24331 if(groupSize2){
24332 groupSize = groupSize2;
24333 delete groupSize2;
24334 }
24335 }
24336 valueParts[0] = pieces.reverse().join(options.group || ",");
a089699c 24337
1354d172
AD
24338 return valueParts.join(options.decimal || ".");
24339};
a089699c 24340
1354d172
AD
24341/*=====
24342dojo.number.__RegexpOptions = function(){
24343 // pattern: String?
24344 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
24345 // with this string. Default value is based on locale. Overriding this property will defeat
24346 // localization.
24347 // type: String?
24348 // choose a format type based on the locale from the following:
24349 // decimal, scientific (not yet supported), percent, currency. decimal by default.
24350 // locale: String?
24351 // override the locale used to determine formatting rules
24352 // strict: Boolean?
24353 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
24354 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
24355 // places: Number|String?
24356 // number of decimal places to accept: Infinity, a positive number, or
24357 // a range "n,m". Defined by pattern or Infinity if pattern not provided.
24358 this.pattern = pattern;
24359 this.type = type;
24360 this.locale = locale;
24361 this.strict = strict;
24362 this.places = places;
24363}
24364=====*/
24365dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
24366 // summary:
24367 // Builds the regular needed to parse a number
24368 // description:
24369 // Returns regular expression with positive and negative match, group
24370 // and decimal separators
24371 return dojo.number._parseInfo(options).regexp; // String
24372};
a089699c 24373
1354d172
AD
24374dojo.number._parseInfo = function(/*Object?*/options){
24375 options = options || {};
24376 var locale = i18n.normalizeLocale(options.locale),
24377 bundle = i18n.getLocalization("dojo.cldr", "number", locale),
24378 pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
24379//TODO: memoize?
24380 group = bundle.group,
24381 decimal = bundle.decimal,
24382 factor = 1;
a089699c 24383
1354d172
AD
24384 if(pattern.indexOf('%') != -1){
24385 factor /= 100;
24386 }else if(pattern.indexOf('\u2030') != -1){
24387 factor /= 1000; // per mille
24388 }else{
24389 var isCurrency = pattern.indexOf('\u00a4') != -1;
24390 if(isCurrency){
24391 group = bundle.currencyGroup || group;
24392 decimal = bundle.currencyDecimal || decimal;
24393 }
24394 }
a089699c 24395
1354d172
AD
24396 //TODO: handle quoted escapes
24397 var patternList = pattern.split(';');
24398 if(patternList.length == 1){
24399 patternList.push("-" + patternList[0]);
24400 }
a089699c 24401
1354d172
AD
24402 var re = dregexp.buildGroupRE(patternList, function(pattern){
24403 pattern = "(?:"+dregexp.escapeString(pattern, '.')+")";
24404 return pattern.replace(dojo.number._numberPatternRE, function(format){
24405 var flags = {
24406 signed: false,
24407 separator: options.strict ? group : [group,""],
24408 fractional: options.fractional,
24409 decimal: decimal,
24410 exponent: false
24411 },
a089699c 24412
1354d172
AD
24413 parts = format.split('.'),
24414 places = options.places;
a089699c 24415
1354d172
AD
24416 // special condition for percent (factor != 1)
24417 // allow decimal places even if not specified in pattern
24418 if(parts.length == 1 && factor != 1){
24419 parts[1] = "###";
81bea17a 24420 }
1354d172
AD
24421 if(parts.length == 1 || places === 0){
24422 flags.fractional = false;
81bea17a 24423 }else{
1354d172
AD
24424 if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
24425 if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
24426 if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
24427 flags.places = places;
81bea17a 24428 }
1354d172
AD
24429 var groups = parts[0].split(',');
24430 if(groups.length > 1){
24431 flags.groupSize = groups.pop().length;
24432 if(groups.length > 1){
24433 flags.groupSize2 = groups.pop().length;
81bea17a
AD
24434 }
24435 }
1354d172
AD
24436 return "("+dojo.number._realNumberRegexp(flags)+")";
24437 });
24438 }, true);
a089699c 24439
1354d172
AD
24440 if(isCurrency){
24441 // substitute the currency symbol for the placeholder in the pattern
24442 re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
24443 var prop = ["symbol", "currency", "displayName"][target.length-1],
24444 symbol = dregexp.escapeString(options[prop] || options.currency || "");
24445 before = before ? "[\\s\\xa0]" : "";
24446 after = after ? "[\\s\\xa0]" : "";
24447 if(!options.strict){
24448 if(before){before += "*";}
24449 if(after){after += "*";}
24450 return "(?:"+before+symbol+after+")?";
24451 }
24452 return before+symbol+after;
24453 });
24454 }
a089699c 24455
1354d172 24456//TODO: substitute localized sign/percent/permille/etc.?
a089699c 24457
1354d172
AD
24458 // normalize whitespace and return
24459 return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
24460};
a089699c 24461
1354d172
AD
24462/*=====
24463dojo.number.__ParseOptions = function(){
24464 // pattern: String?
24465 // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
24466 // with this string. Default value is based on locale. Overriding this property will defeat
24467 // localization. Literal characters in patterns are not supported.
24468 // type: String?
24469 // choose a format type based on the locale from the following:
24470 // decimal, scientific (not yet supported), percent, currency. decimal by default.
24471 // locale: String?
24472 // override the locale used to determine formatting rules
24473 // strict: Boolean?
24474 // strict parsing, false by default. Strict parsing requires input as produced by the format() method.
24475 // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
24476 // fractional: Boolean?|Array?
24477 // Whether to include the fractional portion, where the number of decimal places are implied by pattern
24478 // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
24479 this.pattern = pattern;
24480 this.type = type;
24481 this.locale = locale;
24482 this.strict = strict;
24483 this.fractional = fractional;
24484}
24485=====*/
24486dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
24487 // summary:
24488 // Convert a properly formatted string to a primitive Number, using
24489 // locale-specific settings.
24490 // description:
24491 // Create a Number from a string using a known localized pattern.
24492 // Formatting patterns are chosen appropriate to the locale
24493 // and follow the syntax described by
24494 // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
24495 // Note that literal characters in patterns are not supported.
24496 // expression:
24497 // A string representation of a Number
24498 var info = dojo.number._parseInfo(options),
24499 results = (new RegExp("^"+info.regexp+"$")).exec(expression);
24500 if(!results){
24501 return NaN; //NaN
24502 }
24503 var absoluteMatch = results[1]; // match for the positive expression
24504 if(!results[1]){
24505 if(!results[2]){
24506 return NaN; //NaN
24507 }
24508 // matched the negative pattern
24509 absoluteMatch =results[2];
24510 info.factor *= -1;
24511 }
a089699c 24512
1354d172
AD
24513 // Transform it to something Javascript can parse as a number. Normalize
24514 // decimal point and strip out group separators or alternate forms of whitespace
24515 absoluteMatch = absoluteMatch.
24516 replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
24517 replace(info.decimal, ".");
24518 // Adjust for negative sign, percent, etc. as necessary
24519 return absoluteMatch * info.factor; //Number
24520};
a089699c 24521
1354d172
AD
24522/*=====
24523dojo.number.__RealNumberRegexpFlags = function(){
24524 // places: Number?
24525 // The integer number of decimal places or a range given as "n,m". If
24526 // not given, the decimal part is optional and the number of places is
24527 // unlimited.
24528 // decimal: String?
24529 // A string for the character used as the decimal point. Default
24530 // is ".".
24531 // fractional: Boolean?|Array?
24532 // Whether decimal places are used. Can be true, false, or [true,
24533 // false]. Default is [true, false] which means optional.
24534 // exponent: Boolean?|Array?
24535 // Express in exponential notation. Can be true, false, or [true,
24536 // false]. Default is [true, false], (i.e. will match if the
24537 // exponential part is present are not).
24538 // eSigned: Boolean?|Array?
24539 // The leading plus-or-minus sign on the exponent. Can be true,
24540 // false, or [true, false]. Default is [true, false], (i.e. will
24541 // match if it is signed or unsigned). flags in regexp.integer can be
24542 // applied.
24543 this.places = places;
24544 this.decimal = decimal;
24545 this.fractional = fractional;
24546 this.exponent = exponent;
24547 this.eSigned = eSigned;
24548}
24549=====*/
a089699c 24550
1354d172
AD
24551dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
24552 // summary:
24553 // Builds a regular expression to match a real number in exponential
24554 // notation
a089699c 24555
1354d172
AD
24556 // assign default values to missing parameters
24557 flags = flags || {};
24558 //TODO: use mixin instead?
24559 if(!("places" in flags)){ flags.places = Infinity; }
24560 if(typeof flags.decimal != "string"){ flags.decimal = "."; }
24561 if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
24562 if(!("exponent" in flags)){ flags.exponent = [true, false]; }
24563 if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
a089699c 24564
1354d172
AD
24565 var integerRE = dojo.number._integerRegexp(flags),
24566 decimalRE = dregexp.buildGroupRE(flags.fractional,
24567 function(q){
24568 var re = "";
24569 if(q && (flags.places!==0)){
24570 re = "\\" + flags.decimal;
24571 if(flags.places == Infinity){
24572 re = "(?:" + re + "\\d+)?";
24573 }else{
24574 re += "\\d{" + flags.places + "}";
24575 }
24576 }
24577 return re;
81bea17a 24578 },
1354d172
AD
24579 true
24580 );
a089699c 24581
1354d172
AD
24582 var exponentRE = dregexp.buildGroupRE(flags.exponent,
24583 function(q){
24584 if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
24585 return "";
a089699c 24586 }
1354d172
AD
24587 );
24588
24589 var realRE = integerRE + decimalRE;
24590 // allow for decimals without integers, e.g. .25
24591 if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
24592 return realRE + exponentRE; // String
24593};
a089699c 24594
1354d172
AD
24595/*=====
24596dojo.number.__IntegerRegexpFlags = function(){
24597 // signed: Boolean?
24598 // The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
24599 // Default is `[true, false]`, (i.e. will match if it is signed
24600 // or unsigned).
24601 // separator: String?
24602 // The character used as the thousands separator. Default is no
24603 // separator. For more than one symbol use an array, e.g. `[",", ""]`,
24604 // makes ',' optional.
24605 // groupSize: Number?
24606 // group size between separators
24607 // groupSize2: Number?
24608 // second grouping, where separators 2..n have a different interval than the first separator (for India)
24609 this.signed = signed;
24610 this.separator = separator;
24611 this.groupSize = groupSize;
24612 this.groupSize2 = groupSize2;
a089699c 24613}
1354d172 24614=====*/
a089699c 24615
1354d172
AD
24616dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
24617 // summary:
24618 // Builds a regular expression that matches an integer
a089699c 24619
1354d172
AD
24620 // assign default values to missing parameters
24621 flags = flags || {};
24622 if(!("signed" in flags)){ flags.signed = [true, false]; }
24623 if(!("separator" in flags)){
24624 flags.separator = "";
24625 }else if(!("groupSize" in flags)){
24626 flags.groupSize = 3;
24627 }
a089699c 24628
1354d172
AD
24629 var signRE = dregexp.buildGroupRE(flags.signed,
24630 function(q){ return q ? "[-+]" : ""; },
24631 true
24632 );
a089699c 24633
1354d172
AD
24634 var numberRE = dregexp.buildGroupRE(flags.separator,
24635 function(sep){
24636 if(!sep){
24637 return "(?:\\d+)";
24638 }
a089699c 24639
1354d172
AD
24640 sep = dregexp.escapeString(sep);
24641 if(sep == " "){ sep = "\\s"; }
24642 else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
a089699c 24643
1354d172
AD
24644 var grp = flags.groupSize, grp2 = flags.groupSize2;
24645 //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
24646 if(grp2){
24647 var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
24648 return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
24649 }
24650 return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
24651 },
24652 true
24653 );
a089699c 24654
1354d172
AD
24655 return signRE + numberRE; // String
24656};
a089699c 24657
1354d172
AD
24658return dojo.number;
24659});
a089699c 24660
1354d172
AD
24661},
24662'dijit/_FocusMixin':function(){
24663define("dijit/_FocusMixin", [
24664 "./focus",
24665 "./_WidgetBase",
24666 "dojo/_base/declare", // declare
24667 "dojo/_base/lang" // lang.extend
24668], function(focus, _WidgetBase, declare, lang){
a089699c 24669
1354d172
AD
24670/*=====
24671 var _WidgetBase = dijit._WidgetBase;
24672=====*/
a089699c 24673
1354d172
AD
24674 // module:
24675 // dijit/_FocusMixin
24676 // summary:
24677 // Mixin to widget to provide _onFocus() and _onBlur() methods that
24678 // fire when a widget or it's descendants get/lose focus
24679
24680 // We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
24681 // to be last in the inheritance chain, so mixin to _WidgetBase.
24682 lang.extend(_WidgetBase, {
24683 // focused: [readonly] Boolean
24684 // This widget or a widget it contains has focus, or is "active" because
24685 // it was recently clicked.
24686 focused: false,
24687
24688 onFocus: function(){
24689 // summary:
24690 // Called when the widget becomes "active" because
24691 // it or a widget inside of it either has focus, or has recently
24692 // been clicked.
24693 // tags:
24694 // callback
24695 },
a089699c 24696
1354d172
AD
24697 onBlur: function(){
24698 // summary:
24699 // Called when the widget stops being "active" because
24700 // focus moved to something outside of it, or the user
24701 // clicked somewhere outside of it, or the widget was
24702 // hidden.
24703 // tags:
24704 // callback
24705 },
a089699c 24706
1354d172
AD
24707 _onFocus: function(){
24708 // summary:
24709 // This is where widgets do processing for when they are active,
24710 // such as changing CSS classes. See onFocus() for more details.
24711 // tags:
24712 // protected
24713 this.onFocus();
24714 },
a089699c 24715
1354d172
AD
24716 _onBlur: function(){
24717 // summary:
24718 // This is where widgets do processing for when they stop being active,
24719 // such as changing CSS classes. See onBlur() for more details.
24720 // tags:
24721 // protected
24722 this.onBlur();
81bea17a 24723 }
1354d172 24724 });
a089699c 24725
1354d172
AD
24726 return declare("dijit._FocusMixin", null, {
24727 // summary:
24728 // Mixin to widget to provide _onFocus() and _onBlur() methods that
24729 // fire when a widget or it's descendants get/lose focus
a089699c 24730
1354d172
AD
24731 // flag that I want _onFocus()/_onBlur() notifications from focus manager
24732 _focusManager: focus
24733 });
a089699c 24734
1354d172 24735});
a089699c 24736
1354d172
AD
24737},
24738'dojo/data/util/filter':function(){
24739define("dojo/data/util/filter", ["dojo/_base/lang"], function(lang) {
24740 // module:
24741 // dojo/data/util/filter
24742 // summary:
24743 // TODOC
a089699c 24744
1354d172 24745var filter = lang.getObject("dojo.data.util.filter", true);
a089699c 24746
1354d172
AD
24747filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
24748 // summary:
24749 // Helper function to convert a simple pattern to a regular expression for matching.
24750 // description:
24751 // Returns a regular expression object that conforms to the defined conversion rules.
24752 // For example:
24753 // ca* -> /^ca.*$/
24754 // *ca* -> /^.*ca.*$/
24755 // *c\*a* -> /^.*c\*a.*$/
24756 // *c\*a?* -> /^.*c\*a..*$/
24757 // and so on.
24758 //
24759 // pattern: string
24760 // A simple matching pattern to convert that follows basic rules:
24761 // * Means match anything, so ca* means match anything starting with ca
24762 // ? Means match single character. So, b?b will match to bob and bab, and so on.
24763 // \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
24764 // To use a \ as a character in the string, it must be escaped. So in the pattern it should be
24765 // represented by \\ to be treated as an ordinary \ character instead of an escape.
24766 //
24767 // ignoreCase:
24768 // An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
24769 // By default, it is assumed case sensitive.
a089699c 24770
1354d172
AD
24771 var rxp = "^";
24772 var c = null;
24773 for(var i = 0; i < pattern.length; i++){
24774 c = pattern.charAt(i);
24775 switch(c){
24776 case '\\':
24777 rxp += c;
24778 i++;
24779 rxp += pattern.charAt(i);
24780 break;
24781 case '*':
24782 rxp += ".*"; break;
24783 case '?':
24784 rxp += "."; break;
24785 case '$':
24786 case '^':
24787 case '/':
24788 case '+':
24789 case '.':
24790 case '|':
24791 case '(':
24792 case ')':
24793 case '{':
24794 case '}':
24795 case '[':
24796 case ']':
24797 rxp += "\\"; //fallthrough
24798 default:
24799 rxp += c;
81bea17a 24800 }
1354d172
AD
24801 }
24802 rxp += "$";
24803 if(ignoreCase){
24804 return new RegExp(rxp,"mi"); //RegExp
24805 }else{
24806 return new RegExp(rxp,"m"); //RegExp
24807 }
a089699c 24808
1354d172 24809};
a089699c 24810
1354d172
AD
24811return filter;
24812});
a089699c 24813
1354d172
AD
24814},
24815'dijit/_WidgetsInTemplateMixin':function(){
24816define("dijit/_WidgetsInTemplateMixin", [
24817 "dojo/_base/array", // array.forEach
24818 "dojo/_base/declare", // declare
24819 "dojo/parser", // parser.parse
24820 "dijit/registry" // registry.findWidgets
24821], function(array, declare, parser, registry){
24822
24823 // module:
24824 // dijit/_WidgetsInTemplateMixin
24825 // summary:
24826 // Mixin to supplement _TemplatedMixin when template contains widgets
a089699c 24827
1354d172 24828 return declare("dijit._WidgetsInTemplateMixin", null, {
81bea17a 24829 // summary:
1354d172 24830 // Mixin to supplement _TemplatedMixin when template contains widgets
a089699c 24831
1354d172
AD
24832 // _earlyTemplatedStartup: Boolean
24833 // A fallback to preserve the 1.0 - 1.3 behavior of children in
24834 // templates having their startup called before the parent widget
24835 // fires postCreate. Defaults to 'false', causing child widgets to
24836 // have their .startup() called immediately before a parent widget
24837 // .startup(), but always after the parent .postCreate(). Set to
24838 // 'true' to re-enable to previous, arguably broken, behavior.
24839 _earlyTemplatedStartup: false,
a089699c 24840
1354d172
AD
24841 // widgetsInTemplate: [protected] Boolean
24842 // Should we parse the template to find widgets that might be
24843 // declared in markup inside it? (Remove for 2.0 and assume true)
24844 widgetsInTemplate: true,
a089699c 24845
1354d172
AD
24846 _beforeFillContent: function(){
24847 if(this.widgetsInTemplate){
24848 // Before copying over content, instantiate widgets in template
24849 var node = this.domNode;
a089699c 24850
1354d172
AD
24851 var cw = (this._startupWidgets = parser.parse(node, {
24852 noStart: !this._earlyTemplatedStartup,
24853 template: true,
24854 inherited: {dir: this.dir, lang: this.lang, textDir: this.textDir},
24855 propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
24856 scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
24857 }));
a089699c 24858
1354d172 24859 this._supportingWidgets = registry.findWidgets(node);
a089699c 24860
1354d172
AD
24861 this._attachTemplateNodes(cw, function(n,p){
24862 return n[p];
24863 });
24864 }
24865 },
a089699c 24866
1354d172
AD
24867 startup: function(){
24868 array.forEach(this._startupWidgets, function(w){
24869 if(w && !w._started && w.startup){
24870 w.startup();
24871 }
24872 });
24873 this.inherited(arguments);
81bea17a 24874 }
1354d172 24875 });
81bea17a 24876});
a089699c 24877
1354d172
AD
24878},
24879'dojo/fx/Toggler':function(){
24880define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
24881 function(lang, declare, baseFx, connectUtil) {
24882 // module:
24883 // dojo/fx/Toggler
24884 // summary:
24885 // TODOC
a089699c 24886
1354d172 24887return declare("dojo.fx.Toggler", null, {
81bea17a 24888 // summary:
1354d172
AD
24889 // A simple `dojo.Animation` toggler API.
24890 //
24891 // description:
24892 // class constructor for an animation toggler. It accepts a packed
24893 // set of arguments about what type of animation to use in each
24894 // direction, duration, etc. All available members are mixed into
24895 // these animations from the constructor (for example, `node`,
24896 // `showDuration`, `hideDuration`).
24897 //
24898 // example:
24899 // | var t = new dojo.fx.Toggler({
24900 // | node: "nodeId",
24901 // | showDuration: 500,
24902 // | // hideDuration will default to "200"
24903 // | showFunc: dojo.fx.wipeIn,
24904 // | // hideFunc will default to "fadeOut"
24905 // | });
24906 // | t.show(100); // delay showing for 100ms
24907 // | // ...time passes...
24908 // | t.hide();
24909
24910 // node: DomNode
24911 // the node to target for the showing and hiding animations
24912 node: null,
24913
24914 // showFunc: Function
24915 // The function that returns the `dojo.Animation` to show the node
24916 showFunc: baseFx.fadeIn,
24917
24918 // hideFunc: Function
24919 // The function that returns the `dojo.Animation` to hide the node
24920 hideFunc: baseFx.fadeOut,
24921
24922 // showDuration:
24923 // Time in milliseconds to run the show Animation
24924 showDuration: 200,
24925
24926 // hideDuration:
24927 // Time in milliseconds to run the hide Animation
24928 hideDuration: 200,
24929
24930 // FIXME: need a policy for where the toggler should "be" the next
24931 // time show/hide are called if we're stopped somewhere in the
24932 // middle.
24933 // FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
24934 // each animation individually.
24935 // FIXME: also would be nice to have events from the animations exposed/bridged
a089699c 24936
81bea17a 24937 /*=====
1354d172
AD
24938 _showArgs: null,
24939 _showAnim: null,
24940
24941 _hideArgs: null,
24942 _hideAnim: null,
24943
24944 _isShowing: false,
24945 _isHiding: false,
81bea17a 24946 =====*/
a089699c 24947
1354d172
AD
24948 constructor: function(args){
24949 var _t = this;
a089699c 24950
1354d172
AD
24951 lang.mixin(_t, args);
24952 _t.node = args.node;
24953 _t._showArgs = lang.mixin({}, args);
24954 _t._showArgs.node = _t.node;
24955 _t._showArgs.duration = _t.showDuration;
24956 _t.showAnim = _t.showFunc(_t._showArgs);
a089699c 24957
1354d172
AD
24958 _t._hideArgs = lang.mixin({}, args);
24959 _t._hideArgs.node = _t.node;
24960 _t._hideArgs.duration = _t.hideDuration;
24961 _t.hideAnim = _t.hideFunc(_t._hideArgs);
a089699c 24962
1354d172
AD
24963 connectUtil.connect(_t.showAnim, "beforeBegin", lang.hitch(_t.hideAnim, "stop", true));
24964 connectUtil.connect(_t.hideAnim, "beforeBegin", lang.hitch(_t.showAnim, "stop", true));
a089699c
AD
24965 },
24966
1354d172
AD
24967 show: function(delay){
24968 // summary: Toggle the node to showing
24969 // delay: Integer?
24970 // Ammount of time to stall playing the show animation
24971 return this.showAnim.play(delay || 0);
a089699c 24972 },
1354d172
AD
24973
24974 hide: function(delay){
24975 // summary: Toggle the node to hidden
24976 // delay: Integer?
24977 // Ammount of time to stall playing the hide animation
24978 return this.hideAnim.play(delay || 0);
81bea17a
AD
24979 }
24980});
a089699c 24981
1354d172 24982});
a089699c 24983
1354d172
AD
24984},
24985'dijit/form/FilteringSelect':function(){
24986define("dijit/form/FilteringSelect", [
24987 "dojo/data/util/filter", // filter.patternToRegExp
24988 "dojo/_base/declare", // declare
24989 "dojo/_base/Deferred", // Deferred.when
24990 "dojo/_base/lang", // lang.mixin
24991 "./MappedTextBox",
24992 "./ComboBoxMixin"
24993], function(filter, declare, Deferred, lang, MappedTextBox, ComboBoxMixin){
a089699c 24994
1354d172
AD
24995/*=====
24996 var MappedTextBox = dijit.form.MappedTextBox;
24997 var ComboBoxMixin = dijit.form.ComboBoxMixin;
24998=====*/
a089699c 24999
1354d172
AD
25000 // module:
25001 // dijit/form/FilteringSelect
81bea17a 25002 // summary:
1354d172 25003 // An enhanced version of the HTML SELECT tag, populated dynamically
81bea17a 25004
a089699c 25005
1354d172
AD
25006 return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
25007 // summary:
25008 // An enhanced version of the HTML SELECT tag, populated dynamically
25009 //
25010 // description:
25011 // An enhanced version of the HTML SELECT tag, populated dynamically. It works
25012 // very nicely with very large data sets because it can load and page data as needed.
25013 // It also resembles ComboBox, but does not allow values outside of the provided ones.
25014 // If OPTION tags are used as the data provider via markup, then the
25015 // OPTION tag's child text node is used as the displayed value when selected
25016 // while the OPTION tag's value attribute is used as the widget value on form submit.
25017 // To set the default value when using OPTION tags, specify the selected
25018 // attribute on 1 of the child OPTION tags.
25019 //
25020 // Similar features:
25021 // - There is a drop down list of possible values.
25022 // - You can only enter a value from the drop down list. (You can't
25023 // enter an arbitrary value.)
25024 // - The value submitted with the form is the hidden value (ex: CA),
25025 // not the displayed value a.k.a. label (ex: California)
25026 //
25027 // Enhancements over plain HTML version:
25028 // - If you type in some text then it will filter down the list of
25029 // possible values in the drop down list.
25030 // - List can be specified either as a static list or via a javascript
25031 // function (that can get the list from a server)
a089699c 25032
1354d172
AD
25033 // required: Boolean
25034 // True (default) if user is required to enter a value into this field.
25035 required: true,
a089699c 25036
1354d172 25037 _lastDisplayedValue: "",
a089699c 25038
1354d172
AD
25039 _isValidSubset: function(){
25040 return this._opened;
25041 },
a089699c 25042
1354d172
AD
25043 isValid: function(){
25044 // Overrides ValidationTextBox.isValid()
25045 return !!this.item || (!this.required && this.get('displayedValue') == ""); // #5974
25046 },
a089699c 25047
1354d172
AD
25048 _refreshState: function(){
25049 if(!this.searchTimer){ // state will be refreshed after results are returned
25050 this.inherited(arguments);
25051 }
25052 },
a089699c 25053
1354d172
AD
25054 _callbackSetLabel: function(
25055 /*Array*/ result,
25056 /*Object*/ query,
25057 /*Object*/ options,
25058 /*Boolean?*/ priorityChange){
81bea17a 25059 // summary:
1354d172 25060 // Callback from dojo.store after lookup of user entered value finishes
a089699c 25061
1354d172
AD
25062 // setValue does a synchronous lookup,
25063 // so it calls _callbackSetLabel directly,
25064 // and so does not pass dataObject
25065 // still need to test against _lastQuery in case it came too late
25066 if((query && query[this.searchAttr] !== this._lastQuery) || (!query && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
25067 return;
25068 }
25069 if(!result.length){
25070 //#3268: don't modify display value on bad input
25071 //#3285: change CSS to indicate error
25072 this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
25073 }else{
25074 this.set('item', result[0], priorityChange);
25075 }
81bea17a 25076 },
a089699c 25077
1354d172
AD
25078 _openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
25079 // Callback when a data store query completes.
25080 // Overrides ComboBox._openResultList()
a089699c 25081
1354d172
AD
25082 // #3285: tap into search callback to see if user's query resembles a match
25083 if(query[this.searchAttr] !== this._lastQuery){
25084 return;
25085 }
25086 this.inherited(arguments);
a089699c 25087
1354d172
AD
25088 if(this.item === undefined){ // item == undefined for keyboard search
25089 // If the search returned no items that means that the user typed
25090 // in something invalid (and they can't make it valid by typing more characters),
25091 // so flag the FilteringSelect as being in an invalid state
25092 this.validate(true);
25093 }
81bea17a 25094 },
a089699c 25095
1354d172 25096 _getValueAttr: function(){
81bea17a 25097 // summary:
1354d172 25098 // Hook for get('value') to work.
a089699c 25099
1354d172
AD
25100 // don't get the textbox value but rather the previously set hidden value.
25101 // Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
25102 return this.valueNode.value;
81bea17a 25103 },
a089699c 25104
1354d172
AD
25105 _getValueField: function(){
25106 // Overrides ComboBox._getValueField()
25107 return "value";
81bea17a 25108 },
a089699c 25109
1354d172 25110 _setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
81bea17a 25111 // summary:
1354d172
AD
25112 // Hook so set('value', value) works.
25113 // description:
25114 // Sets the value of the select.
25115 // Also sets the label to the corresponding value by reverse lookup.
25116 if(!this._onChangeActive){ priorityChange = null; }
a089699c 25117
1354d172
AD
25118 if(item === undefined){
25119 if(value === null || value === ''){
25120 value = '';
25121 if(!lang.isString(displayedValue)){
25122 this._setDisplayedValueAttr(displayedValue||'', priorityChange);
25123 return;
25124 }
25125 }
a089699c 25126
1354d172
AD
25127 var self = this;
25128 this._lastQuery = value;
25129 Deferred.when(this.store.get(value), function(item){
25130 self._callbackSetLabel(item? [item] : [], undefined, undefined, priorityChange);
25131 });
25132 }else{
25133 this.valueNode.value = value;
25134 this.inherited(arguments);
25135 }
81bea17a 25136 },
a089699c 25137
1354d172 25138 _setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
81bea17a 25139 // summary:
1354d172
AD
25140 // Set the displayed valued in the input box, and the hidden value
25141 // that gets submitted, based on a dojo.data store item.
25142 // description:
25143 // Users shouldn't call this function; they should be calling
25144 // set('item', value)
25145 // tags:
25146 // private
25147 this.inherited(arguments);
25148 this._lastDisplayedValue = this.textbox.value;
81bea17a 25149 },
a089699c 25150
1354d172
AD
25151 _getDisplayQueryString: function(/*String*/ text){
25152 return text.replace(/([\\\*\?])/g, "\\$1");
81bea17a
AD
25153 },
25154
1354d172 25155 _setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
81bea17a 25156 // summary:
1354d172
AD
25157 // Hook so set('displayedValue', label) works.
25158 // description:
25159 // Sets textbox to display label. Also performs reverse lookup
25160 // to set the hidden value. label should corresponding to item.searchAttr.
a089699c 25161
1354d172 25162 if(label == null){ label = ''; }
a089699c 25163
1354d172
AD
25164 // This is called at initialization along with every custom setter.
25165 // Usually (or always?) the call can be ignored. If it needs to be
25166 // processed then at least make sure that the XHR request doesn't trigger an onChange()
25167 // event, even if it returns after creation has finished
25168 if(!this._created){
25169 if(!("displayedValue" in this.params)){
25170 return;
25171 }
25172 priorityChange = false;
25173 }
a089699c 25174
1354d172
AD
25175 // Do a reverse lookup to map the specified displayedValue to the hidden value.
25176 // Note that if there's a custom labelFunc() this code
25177 if(this.store){
25178 this.closeDropDown();
25179 var query = lang.clone(this.query); // #6196: populate query with user-specifics
a089699c 25180
1354d172
AD
25181 // Generate query
25182 var qs = this._getDisplayQueryString(label), q;
25183 if(this.store._oldAPI){
25184 // remove this branch for 2.0
25185 q = qs;
25186 }else{
25187 // Query on searchAttr is a regex for benefit of dojo.store.Memory,
25188 // but with a toString() method to help dojo.store.JsonRest.
25189 // Search string like "Co*" converted to regex like /^Co.*$/i.
25190 q = filter.patternToRegExp(qs, this.ignoreCase);
25191 q.toString = function(){ return qs; };
25192 }
25193 this._lastQuery = query[this.searchAttr] = q;
a089699c 25194
1354d172
AD
25195 // If the label is not valid, the callback will never set it,
25196 // so the last valid value will get the warning textbox. Set the
25197 // textbox value now so that the impending warning will make
25198 // sense to the user
25199 this.textbox.value = label;
25200 this._lastDisplayedValue = label;
25201 this._set("displayedValue", label); // for watch("displayedValue") notification
25202 var _this = this;
25203 var options = {
25204 ignoreCase: this.ignoreCase,
25205 deep: true
25206 };
25207 lang.mixin(options, this.fetchProperties);
25208 this._fetchHandle = this.store.query(query, options);
25209 Deferred.when(this._fetchHandle, function(result){
25210 _this._fetchHandle = null;
25211 _this._callbackSetLabel(result || [], query, options, priorityChange);
25212 }, function(err){
25213 _this._fetchHandle = null;
25214 if(!_this._cancelingQuery){ // don't treat canceled query as an error
25215 console.error('dijit.form.FilteringSelect: ' + err.toString());
25216 }
25217 });
25218 }
25219 },
a089699c 25220
1354d172
AD
25221 undo: function(){
25222 this.set('displayedValue', this._lastDisplayedValue);
25223 }
25224 });
25225});
a089699c 25226
1354d172
AD
25227},
25228'dojo/data/util/sorter':function(){
25229define("dojo/data/util/sorter", ["dojo/_base/lang"], function(lang) {
25230 // module:
25231 // dojo/data/util/sorter
25232 // summary:
25233 // TODOC
a089699c 25234
1354d172 25235var sorter = lang.getObject("dojo.data.util.sorter", true);
a089699c 25236
1354d172
AD
25237sorter.basicComparator = function( /*anything*/ a,
25238 /*anything*/ b){
25239 // summary:
25240 // Basic comparision function that compares if an item is greater or less than another item
25241 // description:
25242 // returns 1 if a > b, -1 if a < b, 0 if equal.
25243 // 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
25244 // And compared to each other, null is equivalent to undefined.
a089699c 25245
1354d172
AD
25246 //null is a problematic compare, so if null, we set to undefined.
25247 //Makes the check logic simple, compact, and consistent
25248 //And (null == undefined) === true, so the check later against null
25249 //works for undefined and is less bytes.
25250 var r = -1;
25251 if(a === null){
25252 a = undefined;
25253 }
25254 if(b === null){
25255 b = undefined;
25256 }
25257 if(a == b){
25258 r = 0;
25259 }else if(a > b || a == null){
25260 r = 1;
25261 }
25262 return r; //int {-1,0,1}
25263};
a089699c 25264
1354d172
AD
25265sorter.createSortFunction = function( /* attributes array */sortSpec, /*dojo.data.core.Read*/ store){
25266 // summary:
25267 // Helper function to generate the sorting function based off the list of sort attributes.
25268 // description:
25269 // The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
25270 // it will look in the mapping for comparisons function for the attributes. If one is found, it will
25271 // use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
25272 // Returns the sorting function for this particular list of attributes and sorting directions.
25273 //
25274 // sortSpec: array
25275 // A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
25276 // The objects should be formatted as follows:
25277 // {
25278 // attribute: "attributeName-string" || attribute,
25279 // descending: true|false; // Default is false.
25280 // }
25281 // store: object
25282 // The datastore object to look up item values from.
25283 //
25284 var sortFunctions=[];
a089699c 25285
1354d172
AD
25286 function createSortFunction(attr, dir, comp, s){
25287 //Passing in comp and s (comparator and store), makes this
25288 //function much faster.
25289 return function(itemA, itemB){
25290 var a = s.getValue(itemA, attr);
25291 var b = s.getValue(itemB, attr);
25292 return dir * comp(a,b); //int
25293 };
25294 }
25295 var sortAttribute;
25296 var map = store.comparatorMap;
25297 var bc = sorter.basicComparator;
25298 for(var i = 0; i < sortSpec.length; i++){
25299 sortAttribute = sortSpec[i];
25300 var attr = sortAttribute.attribute;
25301 if(attr){
25302 var dir = (sortAttribute.descending) ? -1 : 1;
25303 var comp = bc;
25304 if(map){
25305 if(typeof attr !== "string" && ("toString" in attr)){
25306 attr = attr.toString();
25307 }
25308 comp = map[attr] || bc;
81bea17a 25309 }
1354d172
AD
25310 sortFunctions.push(createSortFunction(attr,
25311 dir, comp, store));
25312 }
25313 }
25314 return function(rowA, rowB){
25315 var i=0;
25316 while(i < sortFunctions.length){
25317 var ret = sortFunctions[i++](rowA, rowB);
25318 if(ret !== 0){
25319 return ret;//int
25320 }
25321 }
25322 return 0; //int
25323 }; // Function
25324};
a089699c 25325
1354d172
AD
25326return sorter;
25327});
a089699c 25328
1354d172
AD
25329},
25330'dijit/form/_ButtonMixin':function(){
25331define("dijit/form/_ButtonMixin", [
25332 "dojo/_base/declare", // declare
25333 "dojo/dom", // dom.setSelectable
25334 "dojo/_base/event", // event.stop
25335 "../registry" // registry.byNode
25336], function(declare, dom, event, registry){
25337
25338// module:
25339// dijit/form/_ButtonMixin
25340// summary:
25341// A mixin to add a thin standard API wrapper to a normal HTML button
a089699c 25342
1354d172
AD
25343return declare("dijit.form._ButtonMixin", null, {
25344 // summary:
25345 // A mixin to add a thin standard API wrapper to a normal HTML button
25346 // description:
25347 // A label should always be specified (through innerHTML) or the label attribute.
25348 // Attach points:
25349 // focusNode (required): this node receives focus
25350 // valueNode (optional): this node's value gets submitted with FORM elements
25351 // containerNode (optional): this node gets the innerHTML assignment for label
25352 // example:
25353 // | <button data-dojo-type="dijit.form.Button" onClick="...">Hello world</button>
25354 //
25355 // example:
25356 // | var button1 = new dijit.form.Button({label: "hello world", onClick: foo});
25357 // | dojo.body().appendChild(button1.domNode);
a089699c 25358
1354d172
AD
25359 // label: HTML String
25360 // Content to display in button.
25361 label: "",
a089699c 25362
1354d172
AD
25363 // type: [const] String
25364 // Type of button (submit, reset, button, checkbox, radio)
25365 type: "button",
a089699c 25366
1354d172
AD
25367 _onClick: function(/*Event*/ e){
25368 // summary:
25369 // Internal function to handle click actions
25370 if(this.disabled){
25371 event.stop(e);
25372 return false;
25373 }
25374 var preventDefault = this.onClick(e) === false; // user click actions
25375 if(!preventDefault && this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a non-form widget needs to be signalled
25376 for(var node=this.domNode; node.parentNode; node=node.parentNode){
25377 var widget=registry.byNode(node);
25378 if(widget && typeof widget._onSubmit == "function"){
25379 widget._onSubmit(e);
25380 preventDefault = true;
25381 break;
25382 }
25383 }
25384 }
25385 if(preventDefault){
25386 e.preventDefault();
25387 }
25388 return !preventDefault;
25389 },
a089699c 25390
1354d172
AD
25391 postCreate: function(){
25392 this.inherited(arguments);
25393 dom.setSelectable(this.focusNode, false);
25394 },
a089699c 25395
1354d172
AD
25396 onClick: function(/*Event*/ /*===== e =====*/){
25397 // summary:
25398 // Callback for when button is clicked.
25399 // If type="submit", return true to perform submit, or false to cancel it.
25400 // type:
25401 // callback
25402 return true; // Boolean
25403 },
a089699c 25404
1354d172
AD
25405 _setLabelAttr: function(/*String*/ content){
25406 // summary:
25407 // Hook for set('label', ...) to work.
25408 // description:
25409 // Set the label (text) of the button; takes an HTML string.
25410 this._set("label", content);
25411 (this.containerNode||this.focusNode).innerHTML = content;
25412 }
25413});
a089699c 25414
1354d172 25415});
a089699c 25416
1354d172
AD
25417},
25418'dojo/colors':function(){
25419define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil) {
25420 // module:
25421 // dojo/colors
25422 // summary:
25423 // TODOC
a089699c 25424
1354d172 25425 var ColorExt = lang.getObject("dojo.colors", true);
a089699c 25426
1354d172 25427//TODO: this module appears to break naming conventions
a089699c 25428
1354d172
AD
25429/*=====
25430 lang.mixin(dojo, {
25431 colors: {
25432 // summary: Color utilities, extending Base dojo.Color
25433 }
25434 });
25435=====*/
a089699c 25436
1354d172
AD
25437 // this is a standard conversion prescribed by the CSS3 Color Module
25438 var hue2rgb = function(m1, m2, h){
25439 if(h < 0){ ++h; }
25440 if(h > 1){ --h; }
25441 var h6 = 6 * h;
25442 if(h6 < 1){ return m1 + (m2 - m1) * h6; }
25443 if(2 * h < 1){ return m2; }
25444 if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
25445 return m1;
25446 };
25447 // Override base Color.fromRgb with the impl in this module
25448 dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
25449 // summary:
25450 // get rgb(a) array from css-style color declarations
25451 // description:
25452 // this function can handle all 4 CSS3 Color Module formats: rgb,
25453 // rgba, hsl, hsla, including rgb(a) with percentage values.
25454 var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
25455 if(m){
25456 var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
25457 if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
25458 var r = c[0];
25459 if(r.charAt(r.length - 1) == "%"){
25460 // 3 rgb percentage values
25461 a = ArrayUtil.map(c, function(x){
25462 return parseFloat(x) * 2.56;
25463 });
25464 if(l == 4){ a[3] = c[3]; }
25465 return Color.fromArray(a, obj); // dojo.Color
a089699c 25466 }
1354d172 25467 return Color.fromArray(c, obj); // dojo.Color
a089699c 25468 }
1354d172
AD
25469 if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
25470 // normalize hsl values
25471 var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
25472 S = parseFloat(c[1]) / 100,
25473 L = parseFloat(c[2]) / 100,
25474 // calculate rgb according to the algorithm
25475 // recommended by the CSS3 Color Module
25476 m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
25477 m1 = 2 * L - m2;
25478 a = [
25479 hue2rgb(m1, m2, H + 1 / 3) * 256,
25480 hue2rgb(m1, m2, H) * 256,
25481 hue2rgb(m1, m2, H - 1 / 3) * 256,
25482 1
25483 ];
25484 if(l == 4){ a[3] = c[3]; }
25485 return Color.fromArray(a, obj); // dojo.Color
81bea17a 25486 }
a089699c 25487 }
1354d172
AD
25488 return null; // dojo.Color
25489 };
a089699c 25490
1354d172
AD
25491 var confine = function(c, low, high){
25492 // summary:
25493 // sanitize a color component by making sure it is a number,
25494 // and clamping it to valid values
25495 c = Number(c);
25496 return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
25497 };
a089699c 25498
1354d172
AD
25499 Color.prototype.sanitize = function(){
25500 // summary: makes sure that the object has correct attributes
25501 var t = this;
25502 t.r = Math.round(confine(t.r, 0, 255));
25503 t.g = Math.round(confine(t.g, 0, 255));
25504 t.b = Math.round(confine(t.b, 0, 255));
25505 t.a = confine(t.a, 0, 1);
25506 return this; // dojo.Color
25507 };
a089699c 25508
1354d172
AD
25509 ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){
25510 // summary: creates a greyscale color with an optional alpha
25511 return Color.fromArray([g, g, g, a]); // dojo.Color
25512 };
a089699c 25513
1354d172
AD
25514 // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
25515 lang.mixin(Color.named, {
25516 "aliceblue": [240,248,255],
25517 "antiquewhite": [250,235,215],
25518 "aquamarine": [127,255,212],
25519 "azure": [240,255,255],
25520 "beige": [245,245,220],
25521 "bisque": [255,228,196],
25522 "blanchedalmond": [255,235,205],
25523 "blueviolet": [138,43,226],
25524 "brown": [165,42,42],
25525 "burlywood": [222,184,135],
25526 "cadetblue": [95,158,160],
25527 "chartreuse": [127,255,0],
25528 "chocolate": [210,105,30],
25529 "coral": [255,127,80],
25530 "cornflowerblue": [100,149,237],
25531 "cornsilk": [255,248,220],
25532 "crimson": [220,20,60],
25533 "cyan": [0,255,255],
25534 "darkblue": [0,0,139],
25535 "darkcyan": [0,139,139],
25536 "darkgoldenrod": [184,134,11],
25537 "darkgray": [169,169,169],
25538 "darkgreen": [0,100,0],
25539 "darkgrey": [169,169,169],
25540 "darkkhaki": [189,183,107],
25541 "darkmagenta": [139,0,139],
25542 "darkolivegreen": [85,107,47],
25543 "darkorange": [255,140,0],
25544 "darkorchid": [153,50,204],
25545 "darkred": [139,0,0],
25546 "darksalmon": [233,150,122],
25547 "darkseagreen": [143,188,143],
25548 "darkslateblue": [72,61,139],
25549 "darkslategray": [47,79,79],
25550 "darkslategrey": [47,79,79],
25551 "darkturquoise": [0,206,209],
25552 "darkviolet": [148,0,211],
25553 "deeppink": [255,20,147],
25554 "deepskyblue": [0,191,255],
25555 "dimgray": [105,105,105],
25556 "dimgrey": [105,105,105],
25557 "dodgerblue": [30,144,255],
25558 "firebrick": [178,34,34],
25559 "floralwhite": [255,250,240],
25560 "forestgreen": [34,139,34],
25561 "gainsboro": [220,220,220],
25562 "ghostwhite": [248,248,255],
25563 "gold": [255,215,0],
25564 "goldenrod": [218,165,32],
25565 "greenyellow": [173,255,47],
25566 "grey": [128,128,128],
25567 "honeydew": [240,255,240],
25568 "hotpink": [255,105,180],
25569 "indianred": [205,92,92],
25570 "indigo": [75,0,130],
25571 "ivory": [255,255,240],
25572 "khaki": [240,230,140],
25573 "lavender": [230,230,250],
25574 "lavenderblush": [255,240,245],
25575 "lawngreen": [124,252,0],
25576 "lemonchiffon": [255,250,205],
25577 "lightblue": [173,216,230],
25578 "lightcoral": [240,128,128],
25579 "lightcyan": [224,255,255],
25580 "lightgoldenrodyellow": [250,250,210],
25581 "lightgray": [211,211,211],
25582 "lightgreen": [144,238,144],
25583 "lightgrey": [211,211,211],
25584 "lightpink": [255,182,193],
25585 "lightsalmon": [255,160,122],
25586 "lightseagreen": [32,178,170],
25587 "lightskyblue": [135,206,250],
25588 "lightslategray": [119,136,153],
25589 "lightslategrey": [119,136,153],
25590 "lightsteelblue": [176,196,222],
25591 "lightyellow": [255,255,224],
25592 "limegreen": [50,205,50],
25593 "linen": [250,240,230],
25594 "magenta": [255,0,255],
25595 "mediumaquamarine": [102,205,170],
25596 "mediumblue": [0,0,205],
25597 "mediumorchid": [186,85,211],
25598 "mediumpurple": [147,112,219],
25599 "mediumseagreen": [60,179,113],
25600 "mediumslateblue": [123,104,238],
25601 "mediumspringgreen": [0,250,154],
25602 "mediumturquoise": [72,209,204],
25603 "mediumvioletred": [199,21,133],
25604 "midnightblue": [25,25,112],
25605 "mintcream": [245,255,250],
25606 "mistyrose": [255,228,225],
25607 "moccasin": [255,228,181],
25608 "navajowhite": [255,222,173],
25609 "oldlace": [253,245,230],
25610 "olivedrab": [107,142,35],
25611 "orange": [255,165,0],
25612 "orangered": [255,69,0],
25613 "orchid": [218,112,214],
25614 "palegoldenrod": [238,232,170],
25615 "palegreen": [152,251,152],
25616 "paleturquoise": [175,238,238],
25617 "palevioletred": [219,112,147],
25618 "papayawhip": [255,239,213],
25619 "peachpuff": [255,218,185],
25620 "peru": [205,133,63],
25621 "pink": [255,192,203],
25622 "plum": [221,160,221],
25623 "powderblue": [176,224,230],
25624 "rosybrown": [188,143,143],
25625 "royalblue": [65,105,225],
25626 "saddlebrown": [139,69,19],
25627 "salmon": [250,128,114],
25628 "sandybrown": [244,164,96],
25629 "seagreen": [46,139,87],
25630 "seashell": [255,245,238],
25631 "sienna": [160,82,45],
25632 "skyblue": [135,206,235],
25633 "slateblue": [106,90,205],
25634 "slategray": [112,128,144],
25635 "slategrey": [112,128,144],
25636 "snow": [255,250,250],
25637 "springgreen": [0,255,127],
25638 "steelblue": [70,130,180],
25639 "tan": [210,180,140],
25640 "thistle": [216,191,216],
25641 "tomato": [255,99,71],
25642 "turquoise": [64,224,208],
25643 "violet": [238,130,238],
25644 "wheat": [245,222,179],
25645 "whitesmoke": [245,245,245],
25646 "yellowgreen": [154,205,50]
25647 });
a089699c 25648
1354d172
AD
25649 return Color;
25650});
a089699c 25651
1354d172
AD
25652},
25653'dijit/registry':function(){
25654define("dijit/registry", [
25655 "dojo/_base/array", // array.forEach array.map
25656 "dojo/_base/sniff", // has("ie")
25657 "dojo/_base/unload", // unload.addOnWindowUnload
25658 "dojo/_base/window", // win.body
25659 "." // dijit._scopeName
25660], function(array, has, unload, win, dijit){
25661
25662 // module:
25663 // dijit/registry
25664 // summary:
25665 // Registry of existing widget on page, plus some utility methods.
25666 // Must be accessed through AMD api, ex:
25667 // require(["dijit/registry"], function(registry){ registry.byId("foo"); })
a089699c 25668
1354d172 25669 var _widgetTypeCtr = {}, hash = {};
a089699c 25670
1354d172
AD
25671 var registry = {
25672 // summary:
25673 // A set of widgets indexed by id
a089699c 25674
1354d172 25675 length: 0,
a089699c 25676
1354d172
AD
25677 add: function(/*dijit._Widget*/ widget){
25678 // summary:
25679 // Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
25680 //
25681 // widget: dijit._Widget
25682 // Any dijit._Widget subclass.
25683 if(hash[widget.id]){
25684 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
25685 }
25686 hash[widget.id] = widget;
25687 this.length++;
25688 },
a089699c 25689
1354d172
AD
25690 remove: function(/*String*/ id){
25691 // summary:
25692 // Remove a widget from the registry. Does not destroy the widget; simply
25693 // removes the reference.
25694 if(hash[id]){
25695 delete hash[id];
25696 this.length--;
25697 }
25698 },
a089699c 25699
1354d172
AD
25700 byId: function(/*String|Widget*/ id){
25701 // summary:
25702 // Find a widget by it's id.
25703 // If passed a widget then just returns the widget.
25704 return typeof id == "string" ? hash[id] : id; // dijit._Widget
25705 },
a089699c 25706
1354d172
AD
25707 byNode: function(/*DOMNode*/ node){
25708 // summary:
25709 // Returns the widget corresponding to the given DOMNode
25710 return hash[node.getAttribute("widgetId")]; // dijit._Widget
25711 },
a089699c 25712
1354d172
AD
25713 toArray: function(){
25714 // summary:
25715 // Convert registry into a true Array
25716 //
25717 // example:
25718 // Work with the widget .domNodes in a real Array
25719 // | array.map(dijit.registry.toArray(), function(w){ return w.domNode; });
a089699c 25720
1354d172
AD
25721 var ar = [];
25722 for(var id in hash){
25723 ar.push(hash[id]);
25724 }
25725 return ar; // dijit._Widget[]
25726 },
a089699c 25727
1354d172
AD
25728 getUniqueId: function(/*String*/widgetType){
25729 // summary:
25730 // Generates a unique id for a given widgetType
a089699c 25731
1354d172
AD
25732 var id;
25733 do{
25734 id = widgetType + "_" +
25735 (widgetType in _widgetTypeCtr ?
25736 ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0);
25737 }while(hash[id]);
25738 return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String
25739 },
a089699c 25740
1354d172
AD
25741 findWidgets: function(/*DomNode*/ root){
25742 // summary:
25743 // Search subtree under root returning widgets found.
25744 // Doesn't search for nested widgets (ie, widgets inside other widgets).
25745
25746 var outAry = [];
25747
25748 function getChildrenHelper(root){
25749 for(var node = root.firstChild; node; node = node.nextSibling){
25750 if(node.nodeType == 1){
25751 var widgetId = node.getAttribute("widgetId");
25752 if(widgetId){
25753 var widget = hash[widgetId];
25754 if(widget){ // may be null on page w/multiple dojo's loaded
25755 outAry.push(widget);
25756 }
25757 }else{
25758 getChildrenHelper(node);
25759 }
25760 }
25761 }
25762 }
a089699c 25763
1354d172
AD
25764 getChildrenHelper(root);
25765 return outAry;
25766 },
a089699c 25767
1354d172
AD
25768 _destroyAll: function(){
25769 // summary:
25770 // Code to destroy all widgets and do other cleanup on page unload
a089699c 25771
1354d172
AD
25772 // Clean up focus manager lingering references to widgets and nodes
25773 dijit._curFocus = null;
25774 dijit._prevFocus = null;
25775 dijit._activeStack = [];
25776
25777 // Destroy all the widgets, top down
25778 array.forEach(registry.findWidgets(win.body()), function(widget){
25779 // Avoid double destroy of widgets like Menu that are attached to <body>
25780 // even though they are logically children of other widgets.
25781 if(!widget._destroyed){
25782 if(widget.destroyRecursive){
25783 widget.destroyRecursive();
25784 }else if(widget.destroy){
25785 widget.destroy();
25786 }
25787 }
25788 });
25789 },
a089699c 25790
1354d172
AD
25791 getEnclosingWidget: function(/*DOMNode*/ node){
25792 // summary:
25793 // Returns the widget whose DOM tree contains the specified DOMNode, or null if
25794 // the node is not contained within the DOM tree of any widget
25795 while(node){
25796 var id = node.getAttribute && node.getAttribute("widgetId");
25797 if(id){
25798 return hash[id];
25799 }
25800 node = node.parentNode;
25801 }
25802 return null;
25803 },
a089699c 25804
1354d172
AD
25805 // In case someone needs to access hash.
25806 // Actually, this is accessed from WidgetSet back-compatibility code
25807 _hash: hash
25808 };
81bea17a 25809
1354d172
AD
25810 if(has("ie")){
25811 // Only run _destroyAll() for IE because we think it's only necessary in that case,
25812 // and because it causes problems on FF. See bug #3531 for details.
25813 unload.addOnWindowUnload(function(){
25814 registry._destroyAll();
25815 });
25816 }
a089699c 25817
1354d172
AD
25818 /*=====
25819 dijit.registry = {
25820 // summary:
25821 // A list of widgets on a page.
25822 };
25823 =====*/
25824 dijit.registry = registry;
a089699c 25825
1354d172
AD
25826 return registry;
25827});
a089699c 25828
1354d172
AD
25829},
25830'dijit/tree/_dndContainer':function(){
25831define("dijit/tree/_dndContainer", [
25832 "dojo/aspect", // aspect.after
25833 "dojo/_base/declare", // declare
25834 "dojo/dom-class", // domClass.add domClass.remove domClass.replace
25835 "dojo/_base/event", // event.stop
25836 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
25837 "dojo/mouse", // mouse.enter, mouse.leave
25838 "dojo/on"
25839], function(aspect, declare, domClass, event, lang, mouse, on){
25840
25841 // module:
25842 // dijit/tree/_dndContainer
25843 // summary:
25844 // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
25845 // It's modeled after `dojo.dnd.Container`.
a089699c 25846
1354d172 25847 return declare("dijit.tree._dndContainer", null, {
a089699c 25848
1354d172
AD
25849 // summary:
25850 // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly.
25851 // It's modeled after `dojo.dnd.Container`.
25852 // tags:
25853 // protected
a089699c 25854
1354d172
AD
25855 /*=====
25856 // current: DomNode
25857 // The currently hovered TreeNode.rowNode (which is the DOM node
25858 // associated w/a given node in the tree, excluding it's descendants)
25859 current: null,
25860 =====*/
a089699c 25861
1354d172
AD
25862 constructor: function(tree, params){
25863 // summary:
25864 // A constructor of the Container
25865 // tree: Node
25866 // Node or node's id to build the container on
25867 // params: dijit.tree.__SourceArgs
25868 // A dict of parameters, which gets mixed into the object
25869 // tags:
25870 // private
25871 this.tree = tree;
25872 this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
25873 lang.mixin(this, params);
a089699c 25874
1354d172
AD
25875 // class-specific variables
25876 this.current = null; // current TreeNode's DOM node
81bea17a 25877
1354d172
AD
25878 // states
25879 this.containerState = "";
25880 domClass.add(this.node, "dojoDndContainer");
81bea17a 25881
1354d172
AD
25882 // set up events
25883 this.events = [
25884 // container level events
25885 on(this.node, mouse.enter, lang.hitch(this, "onOverEvent")),
25886 on(this.node, mouse.leave, lang.hitch(this, "onOutEvent")),
a089699c 25887
1354d172
AD
25888 // switching between TreeNodes
25889 aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true),
25890 aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true),
a089699c 25891
1354d172
AD
25892 // cancel text selection and text dragging
25893 on(this.node, "dragstart", lang.hitch(event, "stop")),
25894 on(this.node, "selectstart", lang.hitch(event, "stop"))
25895 ];
25896 },
a089699c 25897
1354d172
AD
25898 destroy: function(){
25899 // summary:
25900 // Prepares this object to be garbage-collected
a089699c 25901
1354d172
AD
25902 var h;
25903 while(h = this.events.pop()){ h.remove(); }
a089699c 25904
1354d172
AD
25905 // this.clearItems();
25906 this.node = this.parent = null;
25907 },
a089699c 25908
1354d172
AD
25909 // mouse events
25910 onMouseOver: function(widget /*===== , evt =====*/){
25911 // summary:
25912 // Called when mouse is moved over a TreeNode
25913 // widget: TreeNode
25914 // evt: Event
25915 // tags:
25916 // protected
25917 this.current = widget;
25918 },
a089699c 25919
1354d172
AD
25920 onMouseOut: function(/*===== widget, evt =====*/){
25921 // summary:
25922 // Called when mouse is moved away from a TreeNode
25923 // widget: TreeNode
25924 // evt: Event
25925 // tags:
25926 // protected
25927 this.current = null;
25928 },
a089699c 25929
1354d172
AD
25930 _changeState: function(type, newState){
25931 // summary:
25932 // Changes a named state to new state value
25933 // type: String
25934 // A name of the state to change
25935 // newState: String
25936 // new state
25937 var prefix = "dojoDnd" + type;
25938 var state = type.toLowerCase() + "State";
25939 //domClass.replace(this.node, prefix + newState, prefix + this[state]);
25940 domClass.replace(this.node, prefix + newState, prefix + this[state]);
25941 this[state] = newState;
25942 },
a089699c 25943
1354d172
AD
25944 _addItemClass: function(node, type){
25945 // summary:
25946 // Adds a class with prefix "dojoDndItem"
25947 // node: Node
25948 // A node
25949 // type: String
25950 // A variable suffix for a class name
25951 domClass.add(node, "dojoDndItem" + type);
25952 },
a089699c 25953
1354d172
AD
25954 _removeItemClass: function(node, type){
25955 // summary:
25956 // Removes a class with prefix "dojoDndItem"
25957 // node: Node
25958 // A node
25959 // type: String
25960 // A variable suffix for a class name
25961 domClass.remove(node, "dojoDndItem" + type);
25962 },
81bea17a 25963
1354d172
AD
25964 onOverEvent: function(){
25965 // summary:
25966 // This function is called once, when mouse is over our container
25967 // tags:
25968 // protected
25969 this._changeState("Container", "Over");
25970 },
a089699c 25971
1354d172
AD
25972 onOutEvent: function(){
25973 // summary:
25974 // This function is called once, when mouse is out of our container
25975 // tags:
25976 // protected
25977 this._changeState("Container", "");
25978 }
25979 });
25980});
a089699c 25981
1354d172
AD
25982},
25983'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" style=\"position: absolute; visibility:hidden\" class=\"dijitReset dijitInline\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit.form.Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n",
25984'dijit/_base/wai':function(){
25985define("dijit/_base/wai", [
25986 "dojo/dom-attr", // domAttr.attr
25987 "dojo/_base/lang", // lang.mixin
25988 "..", // export symbols to dijit
25989 "../hccss" // not using this module directly, but loading it sets CSS flag on <html>
25990], function(domAttr, lang, dijit){
25991
25992 // module:
25993 // dijit/_base/wai
25994 // summary:
25995 // Deprecated methods for setting/getting wai roles and states.
25996 // New code should call setAttribute()/getAttribute() directly.
25997 //
25998 // Also loads hccss to apply dijit_a11y class to root node if machine is in high-contrast mode.
a089699c 25999
1354d172
AD
26000 lang.mixin(dijit, {
26001 hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
26002 // summary:
26003 // Determines if an element has a particular role.
26004 // returns:
26005 // True if elem has the specific role attribute and false if not.
26006 // For backwards compatibility if role parameter not provided,
26007 // returns true if has a role
26008 var waiRole = this.getWaiRole(elem);
26009 return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
26010 },
81bea17a 26011
1354d172
AD
26012 getWaiRole: function(/*Element*/ elem){
26013 // summary:
26014 // Gets the role for an element (which should be a wai role).
26015 // returns:
26016 // The role of elem or an empty string if elem
26017 // does not have a role.
26018 return lang.trim((domAttr.get(elem, "role") || "").replace("wairole:",""));
26019 },
81bea17a 26020
1354d172
AD
26021 setWaiRole: function(/*Element*/ elem, /*String*/ role){
26022 // summary:
26023 // Sets the role on an element.
26024 // description:
26025 // Replace existing role attribute with new role.
81bea17a 26026
1354d172
AD
26027 domAttr.set(elem, "role", role);
26028 },
81bea17a 26029
1354d172
AD
26030 removeWaiRole: function(/*Element*/ elem, /*String*/ role){
26031 // summary:
26032 // Removes the specified role from an element.
26033 // Removes role attribute if no specific role provided (for backwards compat.)
26034
26035 var roleValue = domAttr.get(elem, "role");
26036 if(!roleValue){ return; }
26037 if(role){
26038 var t = lang.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
26039 domAttr.set(elem, "role", t);
26040 }else{
26041 elem.removeAttribute("role");
26042 }
26043 },
81bea17a 26044
1354d172
AD
26045 hasWaiState: function(/*Element*/ elem, /*String*/ state){
26046 // summary:
26047 // Determines if an element has a given state.
26048 // description:
26049 // Checks for an attribute called "aria-"+state.
26050 // returns:
26051 // true if elem has a value for the given state and
26052 // false if it does not.
81bea17a 26053
1354d172
AD
26054 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
26055 },
a089699c 26056
1354d172
AD
26057 getWaiState: function(/*Element*/ elem, /*String*/ state){
26058 // summary:
26059 // Gets the value of a state on an element.
26060 // description:
26061 // Checks for an attribute called "aria-"+state.
26062 // returns:
26063 // The value of the requested state on elem
26064 // or an empty string if elem has no value for state.
a089699c 26065
1354d172
AD
26066 return elem.getAttribute("aria-"+state) || "";
26067 },
a089699c 26068
1354d172
AD
26069 setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
26070 // summary:
26071 // Sets a state on an element.
26072 // description:
26073 // Sets an attribute called "aria-"+state.
a089699c 26074
1354d172
AD
26075 elem.setAttribute("aria-"+state, value);
26076 },
81bea17a 26077
1354d172
AD
26078 removeWaiState: function(/*Element*/ elem, /*String*/ state){
26079 // summary:
26080 // Removes a state from an element.
26081 // description:
26082 // Sets an attribute called "aria-"+state.
26083
26084 elem.removeAttribute("aria-"+state);
a089699c 26085 }
1354d172 26086 });
a089699c 26087
1354d172
AD
26088 return dijit;
26089});
81bea17a 26090
1354d172
AD
26091},
26092'dijit/form/_FormSelectWidget':function(){
26093define("dijit/form/_FormSelectWidget", [
26094 "dojo/_base/array", // array.filter array.forEach array.map array.some
26095 "dojo/aspect", // aspect.after
26096 "dojo/data/util/sorter", // util.sorter.createSortFunction
26097 "dojo/_base/declare", // declare
26098 "dojo/dom", // dom.setSelectable
26099 "dojo/dom-class", // domClass.toggle
26100 "dojo/_base/kernel", // _scopeName
26101 "dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
26102 "dojo/query", // query
26103 "./_FormValueWidget"
26104], function(array, aspect, sorter, declare, dom, domClass, kernel, lang, query, _FormValueWidget){
81bea17a 26105
1354d172
AD
26106/*=====
26107 var _FormValueWidget = dijit.form._FormValueWidget;
26108=====*/
81bea17a 26109
1354d172
AD
26110// module:
26111// dijit/form/_FormSelectWidget
26112// summary:
26113// Extends _FormValueWidget in order to provide "select-specific"
26114// values - i.e., those values that are unique to <select> elements.
a089699c 26115
a089699c 26116
1354d172
AD
26117/*=====
26118dijit.form.__SelectOption = function(){
26119 // value: String
26120 // The value of the option. Setting to empty (or missing) will
26121 // place a separator at that location
26122 // label: String
26123 // The label for our option. It can contain html tags.
26124 // selected: Boolean
26125 // Whether or not we are a selected option
26126 // disabled: Boolean
26127 // Whether or not this specific option is disabled
26128 this.value = value;
26129 this.label = label;
26130 this.selected = selected;
26131 this.disabled = disabled;
26132}
26133=====*/
a089699c 26134
1354d172
AD
26135return declare("dijit.form._FormSelectWidget", _FormValueWidget, {
26136 // summary:
26137 // Extends _FormValueWidget in order to provide "select-specific"
26138 // values - i.e., those values that are unique to <select> elements.
26139 // This also provides the mechanism for reading the elements from
26140 // a store, if desired.
a089699c 26141
1354d172
AD
26142 // multiple: [const] Boolean
26143 // Whether or not we are multi-valued
26144 multiple: false,
a089699c 26145
1354d172
AD
26146 // options: dijit.form.__SelectOption[]
26147 // The set of options for our select item. Roughly corresponds to
26148 // the html <option> tag.
26149 options: null,
81bea17a 26150
1354d172
AD
26151 // store: dojo.data.api.Identity
26152 // A store which, at the very least implements dojo.data.api.Identity
26153 // to use for getting our list of options - rather than reading them
26154 // from the <option> html tags.
26155 store: null,
a089699c 26156
1354d172
AD
26157 // query: object
26158 // A query to use when fetching items from our store
26159 query: null,
a089699c 26160
1354d172
AD
26161 // queryOptions: object
26162 // Query options to use when fetching from the store
26163 queryOptions: null,
81bea17a 26164
1354d172
AD
26165 // onFetch: Function
26166 // A callback to do with an onFetch - but before any items are actually
26167 // iterated over (i.e. to filter even further what you want to add)
26168 onFetch: null,
81bea17a 26169
1354d172
AD
26170 // sortByLabel: Boolean
26171 // Flag to sort the options returned from a store by the label of
26172 // the store.
26173 sortByLabel: true,
a089699c 26174
a089699c 26175
1354d172
AD
26176 // loadChildrenOnOpen: Boolean
26177 // By default loadChildren is called when the items are fetched from the
26178 // store. This property allows delaying loadChildren (and the creation
26179 // of the options/menuitems) until the user clicks the button to open the
26180 // dropdown.
26181 loadChildrenOnOpen: false,
a089699c 26182
1354d172
AD
26183 getOptions: function(/*anything*/ valueOrIdx){
26184 // summary:
26185 // Returns a given option (or options).
26186 // valueOrIdx:
26187 // If passed in as a string, that string is used to look up the option
26188 // in the array of options - based on the value property.
26189 // (See dijit.form.__SelectOption).
26190 //
26191 // If passed in a number, then the option with the given index (0-based)
26192 // within this select will be returned.
26193 //
26194 // If passed in a dijit.form.__SelectOption, the same option will be
26195 // returned if and only if it exists within this select.
26196 //
26197 // If passed an array, then an array will be returned with each element
26198 // in the array being looked up.
26199 //
26200 // If not passed a value, then all options will be returned
26201 //
26202 // returns:
26203 // The option corresponding with the given value or index. null
26204 // is returned if any of the following are true:
26205 // - A string value is passed in which doesn't exist
26206 // - An index is passed in which is outside the bounds of the array of options
26207 // - A dijit.form.__SelectOption is passed in which is not a part of the select
a089699c 26208
1354d172
AD
26209 // NOTE: the compare for passing in a dijit.form.__SelectOption checks
26210 // if the value property matches - NOT if the exact option exists
26211 // NOTE: if passing in an array, null elements will be placed in the returned
26212 // array when a value is not found.
26213 var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
a089699c 26214
1354d172
AD
26215 if(lookupValue === undefined){
26216 return opts; // dijit.form.__SelectOption[]
a089699c 26217 }
1354d172
AD
26218 if(lang.isArray(lookupValue)){
26219 return array.map(lookupValue, "return this.getOptions(item);", this); // dijit.form.__SelectOption[]
26220 }
26221 if(lang.isObject(valueOrIdx)){
26222 // We were passed an option - so see if it's in our array (directly),
26223 // and if it's not, try and find it by value.
26224 if(!array.some(this.options, function(o, idx){
26225 if(o === lookupValue ||
26226 (o.value && o.value === lookupValue.value)){
26227 lookupValue = idx;
26228 return true;
26229 }
26230 return false;
26231 })){
26232 lookupValue = -1;
26233 }
26234 }
26235 if(typeof lookupValue == "string"){
26236 for(var i=0; i<l; i++){
26237 if(opts[i].value === lookupValue){
26238 lookupValue = i;
26239 break;
26240 }
26241 }
26242 }
26243 if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
26244 return this.options[lookupValue]; // dijit.form.__SelectOption
26245 }
26246 return null; // null
a089699c
AD
26247 },
26248
1354d172 26249 addOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ option){
a089699c 26250 // summary:
1354d172
AD
26251 // Adds an option or options to the end of the select. If value
26252 // of the option is empty or missing, a separator is created instead.
26253 // Passing in an array of options will yield slightly better performance
26254 // since the children are only loaded once.
26255 if(!lang.isArray(option)){ option = [option]; }
26256 array.forEach(option, function(i){
26257 if(i && lang.isObject(i)){
26258 this.options.push(i);
26259 }
26260 }, this);
26261 this._loadChildren();
a089699c
AD
26262 },
26263
1354d172 26264 removeOption: function(/*String|dijit.form.__SelectOption|Number|Array*/ valueOrIdx){
a089699c 26265 // summary:
1354d172
AD
26266 // Removes the given option or options. You can remove by string
26267 // (in which case the value is removed), number (in which case the
26268 // index in the options array is removed), or select option (in
26269 // which case, the select option with a matching value is removed).
26270 // You can also pass in an array of those values for a slightly
26271 // better performance since the children are only loaded once.
26272 if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
26273 var oldOpts = this.getOptions(valueOrIdx);
26274 array.forEach(oldOpts, function(i){
26275 // We can get null back in our array - if our option was not found. In
26276 // that case, we don't want to blow up...
26277 if(i){
26278 this.options = array.filter(this.options, function(node){
26279 return (node.value !== i.value || node.label !== i.label);
26280 });
26281 this._removeOptionItem(i);
26282 }
26283 }, this);
26284 this._loadChildren();
a089699c 26285 },
81bea17a 26286
1354d172 26287 updateOption: function(/*dijit.form.__SelectOption|dijit.form.__SelectOption[]*/ newOption){
a089699c 26288 // summary:
1354d172
AD
26289 // Updates the values of the given option. The option to update
26290 // is matched based on the value of the entered option. Passing
26291 // in an array of new options will yield better performance since
26292 // the children will only be loaded once.
26293 if(!lang.isArray(newOption)){ newOption = [newOption]; }
26294 array.forEach(newOption, function(i){
26295 var oldOpt = this.getOptions(i), k;
26296 if(oldOpt){
26297 for(k in i){ oldOpt[k] = i[k]; }
26298 }
26299 }, this);
26300 this._loadChildren();
a089699c
AD
26301 },
26302
1354d172
AD
26303 setStore: function(/*dojo.data.api.Identity*/ store,
26304 /*anything?*/ selectedValue,
26305 /*Object?*/ fetchArgs){
a089699c 26306 // summary:
1354d172
AD
26307 // Sets the store you would like to use with this select widget.
26308 // The selected value is the value of the new store to set. This
26309 // function returns the original store, in case you want to reuse
26310 // it or something.
26311 // store: dojo.data.api.Identity
26312 // The store you would like to use - it MUST implement dojo.data.api.Identity,
26313 // and MAY implement dojo.data.api.Notification.
26314 // selectedValue: anything?
26315 // The value that this widget should set itself to *after* the store
26316 // has been loaded
26317 // fetchArgs: Object?
26318 // The arguments that will be passed to the store's fetch() function
26319 var oStore = this.store;
26320 fetchArgs = fetchArgs || {};
26321 if(oStore !== store){
26322 // Our store has changed, so update our notifications
26323 var h;
26324 while(h = this._notifyConnections.pop()){ h.remove(); }
a089699c 26325
1354d172
AD
26326 if(store && store.getFeatures()["dojo.data.api.Notification"]){
26327 this._notifyConnections = [
26328 aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
26329 aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
26330 aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
26331 ];
26332 }
26333 this._set("store", store);
26334 }
81bea17a 26335
1354d172
AD
26336 // Turn off change notifications while we make all these changes
26337 this._onChangeActive = false;
a089699c 26338
1354d172
AD
26339 // Remove existing options (if there are any)
26340 if(this.options && this.options.length){
26341 this.removeOption(this.options);
26342 }
a089699c 26343
1354d172
AD
26344 // Add our new options
26345 if(store){
26346 this._loadingStore = true;
26347 store.fetch(lang.delegate(fetchArgs, {
26348 onComplete: function(items, opts){
26349 if(this.sortByLabel && !fetchArgs.sort && items.length){
26350 items.sort(sorter.createSortFunction([{
26351 attribute: store.getLabelAttributes(items[0])[0]
26352 }], store));
26353 }
a089699c 26354
1354d172
AD
26355 if(fetchArgs.onFetch){
26356 items = fetchArgs.onFetch.call(this, items, opts);
26357 }
26358 // TODO: Add these guys as a batch, instead of separately
26359 array.forEach(items, function(i){
26360 this._addOptionForItem(i);
26361 }, this);
a089699c 26362
1354d172
AD
26363 // Set our value (which might be undefined), and then tweak
26364 // it to send a change event with the real value
26365 this._loadingStore = false;
26366 this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
26367 delete this._pendingValue;
a089699c 26368
1354d172
AD
26369 if(!this.loadChildrenOnOpen){
26370 this._loadChildren();
26371 }else{
26372 this._pseudoLoadChildren(items);
26373 }
26374 this._fetchedWith = opts;
26375 this._lastValueReported = this.multiple ? [] : null;
26376 this._onChangeActive = true;
26377 this.onSetStore();
26378 this._handleOnChange(this.value);
26379 },
26380 scope: this
26381 }));
26382 }else{
26383 delete this._fetchedWith;
26384 }
26385 return oStore; // dojo.data.api.Identity
26386 },
a089699c 26387
1354d172
AD
26388 // TODO: implement set() and watch() for store and query, although not sure how to handle
26389 // setting them individually rather than together (as in setStore() above)
a089699c 26390
1354d172
AD
26391 _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
26392 // summary:
26393 // set the value of the widget.
26394 // If a string is passed, then we set our value from looking it up.
26395 if(this._loadingStore){
26396 // Our store is loading - so save our value, and we'll set it when
26397 // we're done
26398 this._pendingValue = newValue;
26399 return;
26400 }
26401 var opts = this.getOptions() || [];
26402 if(!lang.isArray(newValue)){
26403 newValue = [newValue];
26404 }
26405 array.forEach(newValue, function(i, idx){
26406 if(!lang.isObject(i)){
26407 i = i + "";
26408 }
26409 if(typeof i === "string"){
26410 newValue[idx] = array.filter(opts, function(node){
26411 return node.value === i;
26412 })[0] || {value: "", label: ""};
26413 }
26414 }, this);
a089699c 26415
1354d172
AD
26416 // Make sure some sane default is set
26417 newValue = array.filter(newValue, function(i){ return i && i.value; });
26418 if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
26419 newValue[0] = opts[0];
26420 }
26421 array.forEach(opts, function(i){
26422 i.selected = array.some(newValue, function(v){ return v.value === i.value; });
26423 });
26424 var val = array.map(newValue, function(i){ return i.value; }),
26425 disp = array.map(newValue, function(i){ return i.label; });
a089699c 26426
1354d172
AD
26427 this._set("value", this.multiple ? val : val[0]);
26428 this._setDisplay(this.multiple ? disp : disp[0]);
26429 this._updateSelection();
26430 this._handleOnChange(this.value, priorityChange);
26431 },
a089699c 26432
1354d172
AD
26433 _getDisplayedValueAttr: function(){
26434 // summary:
26435 // returns the displayed value of the widget
26436 var val = this.get("value");
26437 if(!lang.isArray(val)){
26438 val = [val];
26439 }
26440 var ret = array.map(this.getOptions(val), function(v){
26441 if(v && "label" in v){
26442 return v.label;
26443 }else if(v){
26444 return v.value;
26445 }
26446 return null;
26447 }, this);
26448 return this.multiple ? ret : ret[0];
26449 },
a089699c 26450
1354d172
AD
26451 _loadChildren: function(){
26452 // summary:
26453 // Loads the children represented by this widget's options.
26454 // reset the menu to make it populatable on the next click
26455 if(this._loadingStore){ return; }
26456 array.forEach(this._getChildren(), function(child){
26457 child.destroyRecursive();
26458 });
26459 // Add each menu item
26460 array.forEach(this.options, this._addOptionItem, this);
a089699c 26461
1354d172
AD
26462 // Update states
26463 this._updateSelection();
26464 },
a089699c 26465
1354d172
AD
26466 _updateSelection: function(){
26467 // summary:
26468 // Sets the "selected" class on the item for styling purposes
26469 this._set("value", this._getValueFromOpts());
26470 var val = this.value;
26471 if(!lang.isArray(val)){
26472 val = [val];
26473 }
26474 if(val && val[0]){
26475 array.forEach(this._getChildren(), function(child){
26476 var isSelected = array.some(val, function(v){
26477 return child.option && (v === child.option.value);
26478 });
26479 domClass.toggle(child.domNode, this.baseClass + "SelectedOption", isSelected);
26480 child.domNode.setAttribute("aria-selected", isSelected);
26481 }, this);
26482 }
26483 },
a089699c 26484
1354d172
AD
26485 _getValueFromOpts: function(){
26486 // summary:
26487 // Returns the value of the widget by reading the options for
26488 // the selected flag
26489 var opts = this.getOptions() || [];
26490 if(!this.multiple && opts.length){
26491 // Mirror what a select does - choose the first one
26492 var opt = array.filter(opts, function(i){
26493 return i.selected;
26494 })[0];
26495 if(opt && opt.value){
26496 return opt.value
26497 }else{
26498 opts[0].selected = true;
26499 return opts[0].value;
26500 }
26501 }else if(this.multiple){
26502 // Set value to be the sum of all selected
26503 return array.map(array.filter(opts, function(i){
26504 return i.selected;
26505 }), function(i){
26506 return i.value;
26507 }) || [];
26508 }
26509 return "";
26510 },
a089699c 26511
1354d172
AD
26512 // Internal functions to call when we have store notifications come in
26513 _onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
26514 if(!parentInfo || !parentInfo.parent){
26515 // Only add it if we are top-level
26516 this._addOptionForItem(item);
26517 }
26518 },
26519 _onDeleteItem: function(/*item*/ item){
26520 var store = this.store;
26521 this.removeOption(store.getIdentity(item));
26522 },
26523 _onSetItem: function(/*item*/ item){
26524 this.updateOption(this._getOptionObjForItem(item));
26525 },
a089699c 26526
1354d172
AD
26527 _getOptionObjForItem: function(item){
26528 // summary:
26529 // Returns an option object based off the given item. The "value"
26530 // of the option item will be the identity of the item, the "label"
26531 // of the option will be the label of the item. If the item contains
26532 // children, the children value of the item will be set
26533 var store = this.store, label = store.getLabel(item),
26534 value = (label ? store.getIdentity(item) : null);
26535 return {value: value, label: label, item:item}; // dijit.form.__SelectOption
26536 },
a089699c 26537
1354d172
AD
26538 _addOptionForItem: function(/*item*/ item){
26539 // summary:
26540 // Creates (and adds) the option for the given item
26541 var store = this.store;
26542 if(!store.isItemLoaded(item)){
26543 // We are not loaded - so let's load it and add later
26544 store.loadItem({item: item, onItem: function(i){
26545 this._addOptionForItem(i);
26546 },
26547 scope: this});
26548 return;
26549 }
26550 var newOpt = this._getOptionObjForItem(item);
26551 this.addOption(newOpt);
26552 },
a089699c 26553
1354d172
AD
26554 constructor: function(/*Object*/ keywordArgs){
26555 // summary:
26556 // Saves off our value, if we have an initial one set so we
26557 // can use it if we have a store as well (see startup())
26558 this._oValue = (keywordArgs || {}).value || null;
26559 this._notifyConnections = [];
26560 },
81bea17a 26561
1354d172
AD
26562 buildRendering: function(){
26563 this.inherited(arguments);
26564 dom.setSelectable(this.focusNode, false);
26565 },
81bea17a 26566
1354d172 26567 _fillContent: function(){
81bea17a 26568 // summary:
1354d172
AD
26569 // Loads our options and sets up our dropdown correctly. We
26570 // don't want any content, so we don't call any inherit chain
26571 // function.
26572 var opts = this.options;
26573 if(!opts){
26574 opts = this.options = this.srcNodeRef ? query("> *",
26575 this.srcNodeRef).map(function(node){
26576 if(node.getAttribute("type") === "separator"){
26577 return { value: "", label: "", selected: false, disabled: false };
26578 }
26579 return {
26580 value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
26581 label: String(node.innerHTML),
26582 // FIXME: disabled and selected are not valid on complex markup children (which is why we're
26583 // looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
26584 // decide before 1.6
26585 selected: node.getAttribute("selected") || false,
26586 disabled: node.getAttribute("disabled") || false
26587 };
26588 }, this) : [];
26589 }
26590 if(!this.value){
26591 this._set("value", this._getValueFromOpts());
26592 }else if(this.multiple && typeof this.value == "string"){
26593 this._set("value", this.value.split(","));
26594 }
a089699c 26595 },
a089699c 26596
1354d172 26597 postCreate: function(){
a089699c 26598 // summary:
1354d172
AD
26599 // sets up our event handling that we need for functioning
26600 // as a select
26601 this.inherited(arguments);
26602
26603 // Make our event connections for updating state
26604 this.connect(this, "onChange", "_updateSelection");
26605 this.connect(this, "startup", "_loadChildren");
26606
26607 this._setValueAttr(this.value, null);
81bea17a 26608 },
81bea17a 26609
1354d172 26610 startup: function(){
81bea17a 26611 // summary:
1354d172
AD
26612 // Connects in our store, if we have one defined
26613 this.inherited(arguments);
26614 var store = this.store, fetchArgs = {};
26615 array.forEach(["query", "queryOptions", "onFetch"], function(i){
26616 if(this[i]){
26617 fetchArgs[i] = this[i];
26618 }
26619 delete this[i];
26620 }, this);
26621 if(store && store.getFeatures()["dojo.data.api.Identity"]){
26622 // Temporarily set our store to null so that it will get set
26623 // and connected appropriately
26624 this.store = null;
26625 this.setStore(store, this._oValue, fetchArgs);
26626 }
81bea17a 26627 },
81bea17a 26628
1354d172
AD
26629 destroy: function(){
26630 // summary:
26631 // Clean up our connections
26632 var h;
26633 while(h = this._notifyConnections.pop()){ h.remove(); }
26634 this.inherited(arguments);
26635 },
81bea17a 26636
1354d172
AD
26637 _addOptionItem: function(/*dijit.form.__SelectOption*/ /*===== option =====*/){
26638 // summary:
26639 // User-overridable function which, for the given option, adds an
26640 // item to the select. If the option doesn't have a value, then a
26641 // separator is added in that place. Make sure to store the option
26642 // in the created option widget.
26643 },
a089699c 26644
1354d172 26645 _removeOptionItem: function(/*dijit.form.__SelectOption*/ /*===== option =====*/){
81bea17a 26646 // summary:
1354d172
AD
26647 // User-overridable function which, for the given option, removes
26648 // its item from the select.
a089699c
AD
26649 },
26650
1354d172
AD
26651 _setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
26652 // summary:
26653 // Overridable function which will set the display for the
26654 // widget. newDisplay is either a string (in the case of
26655 // single selects) or array of strings (in the case of multi-selects)
26656 },
a089699c 26657
1354d172
AD
26658 _getChildren: function(){
26659 // summary:
26660 // Overridable function to return the children that this widget contains.
26661 return [];
26662 },
a089699c 26663
1354d172
AD
26664 _getSelectedOptionsAttr: function(){
26665 // summary:
26666 // hooks into this.attr to provide a mechanism for getting the
26667 // option items for the current value of the widget.
26668 return this.getOptions(this.get("value"));
26669 },
a089699c 26670
1354d172
AD
26671 _pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
26672 // summary:
26673 // a function that will "fake" loading children, if needed, and
26674 // if we have set to not load children until the widget opens.
26675 // items:
26676 // An array of items that will be loaded, when needed
26677 },
a089699c 26678
1354d172
AD
26679 onSetStore: function(){
26680 // summary:
26681 // a function that can be connected to in order to receive a
26682 // notification that the store has finished loading and all options
26683 // from that store are available
26684 }
26685});
a089699c 26686
1354d172 26687});
a089699c 26688
1354d172
AD
26689},
26690'dijit/form/Select':function(){
26691require({cache:{
26692'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=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n"}});
26693define("dijit/form/Select", [
26694 "dojo/_base/array", // array.forEach
26695 "dojo/_base/declare", // declare
26696 "dojo/dom-attr", // domAttr.set
26697 "dojo/dom-class", // domClass.add domClass.remove domClass.toggle
26698 "dojo/dom-construct", // domConstruct.create
26699 "dojo/dom-geometry", // domGeometry.setMarginBox
26700 "dojo/_base/event", // event.stop
26701 "dojo/i18n", // i18n.getLocalization
26702 "dojo/_base/lang", // lang.hitch
26703 "./_FormSelectWidget",
26704 "../_HasDropDown",
26705 "../Menu",
26706 "../MenuItem",
26707 "../MenuSeparator",
26708 "../Tooltip",
26709 "dojo/text!./templates/Select.html",
26710 "dojo/i18n!./nls/validate"
26711], function(array, declare, domAttr, domClass, domConstruct, domGeometry, event, i18n, lang,
26712 _FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
a089699c 26713
1354d172
AD
26714/*=====
26715 var _FormSelectWidget = dijit.form._FormSelectWidget;
26716 var _HasDropDown = dijit._HasDropDown;
26717 var _FormSelectWidget = dijit._FormSelectWidget;
26718 var Menu = dijit.Menu;
26719 var MenuItem = dijit.MenuItem;
26720 var MenuSeparator = dijit.MenuSeparator;
26721 var Tooltip = dijit.Tooltip;
26722=====*/
a089699c 26723
1354d172
AD
26724// module:
26725// dijit/form/Select
26726// summary:
26727// This is a "styleable" select box - it is basically a DropDownButton which
26728// can take a <select> as its input.
a089699c 26729
a089699c 26730
1354d172
AD
26731var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
26732 // summary:
26733 // An internally-used menu for dropdown that allows us a vertical scrollbar
26734 buildRendering: function(){
26735 // summary:
26736 // Stub in our own changes, so that our domNode is not a table
26737 // otherwise, we won't respond correctly to heights/overflows
81bea17a 26738 this.inherited(arguments);
1354d172
AD
26739 var o = (this.menuTableNode = this.domNode);
26740 var n = (this.domNode = domConstruct.create("div", {style: {overflowX: "hidden", overflowY: "scroll"}}));
26741 if(o.parentNode){
26742 o.parentNode.replaceChild(n, o);
a089699c 26743 }
1354d172
AD
26744 domClass.remove(o, "dijitMenuTable");
26745 n.className = o.className + " dijitSelectMenu";
26746 o.className = "dijitReset dijitMenuTable";
26747 o.setAttribute("role", "listbox");
26748 n.setAttribute("role", "presentation");
26749 n.appendChild(o);
a089699c 26750 },
a089699c 26751
1354d172 26752 postCreate: function(){
81bea17a 26753 // summary:
1354d172 26754 // stop mousemove from selecting text on IE to be consistent with other browsers
a089699c 26755
1354d172 26756 this.inherited(arguments);
81bea17a 26757
1354d172 26758 this.connect(this.domNode, "onmousemove", event.stop);
a089699c
AD
26759 },
26760
1354d172 26761 resize: function(/*Object*/ mb){
a089699c 26762 // summary:
1354d172
AD
26763 // Overridden so that we are able to handle resizing our
26764 // internal widget. Note that this is not a "full" resize
26765 // implementation - it only works correctly if you pass it a
26766 // marginBox.
26767 //
26768 // mb: Object
26769 // The margin box to set this dropdown to.
26770 if(mb){
26771 domGeometry.setMarginBox(this.domNode, mb);
26772 if("w" in mb){
26773 // We've explicitly set the wrapper <div>'s width, so set <table> width to match.
26774 // 100% is safer than a pixel value because there may be a scroll bar with
26775 // browser/OS specific width.
26776 this.menuTableNode.style.width = "100%";
26777 }
26778 }
26779 }
26780});
a089699c 26781
1354d172
AD
26782var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
26783 // summary:
26784 // This is a "styleable" select box - it is basically a DropDownButton which
26785 // can take a <select> as its input.
a089699c 26786
1354d172 26787 baseClass: "dijitSelect",
a089699c 26788
1354d172 26789 templateString: template,
a089699c 26790
1354d172
AD
26791 // required: Boolean
26792 // Can be true or false, default is false.
26793 required: false,
a089699c 26794
1354d172
AD
26795 // state: [readonly] String
26796 // "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
26797 state: "",
a089699c 26798
1354d172
AD
26799 // message: String
26800 // Currently displayed error/prompt message
26801 message: "",
26802
26803 // tooltipPosition: String[]
26804 // See description of dijit.Tooltip.defaultPosition for details on this parameter.
26805 tooltipPosition: [],
26806
26807 // emptyLabel: string
26808 // What to display in an "empty" dropdown
26809 emptyLabel: "&#160;", // &nbsp;
26810
26811 // _isLoaded: Boolean
26812 // Whether or not we have been loaded
26813 _isLoaded: false,
26814
26815 // _childrenLoaded: Boolean
26816 // Whether or not our children have been loaded
26817 _childrenLoaded: false,
26818
26819 _fillContent: function(){
26820 // summary:
26821 // Set the value to be the first, or the selected index
26822 this.inherited(arguments);
26823 // set value from selected option
26824 if(this.options.length && !this.value && this.srcNodeRef){
26825 var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
26826 this.value = this.options[si >= 0 ? si : 0].value;
26827 }
26828 // Create the dropDown widget
26829 this.dropDown = new _SelectMenu({id: this.id + "_menu"});
26830 domClass.add(this.dropDown.domNode, this.baseClass + "Menu");
81bea17a 26831 },
a089699c 26832
1354d172 26833 _getMenuItemForOption: function(/*dijit.form.__SelectOption*/ option){
a089699c 26834 // summary:
1354d172
AD
26835 // For the given option, return the menu item that should be
26836 // used to display it. This can be overridden as needed
26837 if(!option.value && !option.label){
26838 // We are a separator (no label set for it)
26839 return new MenuSeparator();
26840 }else{
26841 // Just a regular menu option
26842 var click = lang.hitch(this, "_setValueAttr", option);
26843 var item = new MenuItem({
26844 option: option,
26845 label: option.label || this.emptyLabel,
26846 onClick: click,
26847 disabled: option.disabled || false
81bea17a 26848 });
1354d172
AD
26849 item.focusNode.setAttribute("role", "listitem");
26850 return item;
26851 }
a089699c
AD
26852 },
26853
1354d172 26854 _addOptionItem: function(/*dijit.form.__SelectOption*/ option){
81bea17a 26855 // summary:
1354d172
AD
26856 // For the given option, add an option to our dropdown.
26857 // If the option doesn't have a value, then a separator is added
26858 // in that place.
26859 if(this.dropDown){
26860 this.dropDown.addChild(this._getMenuItemForOption(option));
81bea17a
AD
26861 }
26862 },
a089699c 26863
1354d172
AD
26864 _getChildren: function(){
26865 if(!this.dropDown){
26866 return [];
26867 }
26868 return this.dropDown.getChildren();
26869 },
81bea17a 26870
1354d172
AD
26871 _loadChildren: function(/*Boolean*/ loadMenuItems){
26872 // summary:
26873 // Resets the menu and the length attribute of the button - and
26874 // ensures that the label is appropriately set.
26875 // loadMenuItems: Boolean
26876 // actually loads the child menu items - we only do this when we are
26877 // populating for showing the dropdown.
81bea17a 26878
1354d172
AD
26879 if(loadMenuItems === true){
26880 // this.inherited destroys this.dropDown's child widgets (MenuItems).
26881 // Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
26882 // issues later in _setSelected). (see #10296)
26883 if(this.dropDown){
26884 delete this.dropDown.focusedChild;
81bea17a 26885 }
1354d172
AD
26886 if(this.options.length){
26887 this.inherited(arguments);
26888 }else{
26889 // Drop down menu is blank but add one blank entry just so something appears on the screen
26890 // to let users know that they are no choices (mimicing native select behavior)
26891 array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
26892 var item = new MenuItem({label: "&#160;"});
26893 this.dropDown.addChild(item);
26894 }
26895 }else{
26896 this._updateSelection();
81bea17a 26897 }
1354d172
AD
26898
26899 this._isLoaded = false;
26900 this._childrenLoaded = true;
26901
26902 if(!this._loadingStore){
26903 // Don't call this if we are loading - since we will handle it later
26904 this._setValueAttr(this.value);
a089699c 26905 }
81bea17a 26906 },
a089699c 26907
1354d172
AD
26908 _setValueAttr: function(value){
26909 this.inherited(arguments);
26910 domAttr.set(this.valueNode, "value", this.get("value"));
26911 this.validate(this.focused); // to update this.state
a089699c 26912 },
81bea17a 26913
1354d172
AD
26914 _setDisabledAttr: function(/*Boolean*/ value){
26915 this.inherited(arguments);
26916 this.validate(this.focused); // to update this.state
26917 },
81bea17a 26918
1354d172
AD
26919 _setRequiredAttr: function(/*Boolean*/ value){
26920 this._set("required", value);
26921 this.focusNode.setAttribute("aria-required", value);
26922 this.validate(this.focused); // to update this.state
26923 },
81bea17a 26924
1354d172 26925 _setDisplay: function(/*String*/ newDisplay){
a089699c 26926 // summary:
1354d172
AD
26927 // sets the display for the given value (or values)
26928 var lbl = newDisplay || this.emptyLabel;
26929 this.containerNode.innerHTML = '<span class="dijitReset dijitInline ' + this.baseClass + 'Label">' + lbl + '</span>';
26930 this.focusNode.setAttribute("aria-valuetext", lbl);
a089699c 26931 },
81bea17a 26932
1354d172 26933 validate: function(/*Boolean*/ isFocused){
a089699c 26934 // summary:
1354d172
AD
26935 // Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
26936 // description:
26937 // Show missing or invalid messages if appropriate, and highlight textbox field.
26938 // Used when a select is initially set to no value and the user is required to
26939 // set the value.
26940
26941 var isValid = this.disabled || this.isValid(isFocused);
26942 this._set("state", isValid ? "" : "Incomplete");
26943 this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
26944 var message = isValid ? "" : this._missingMsg;
26945 if(message && this.focused && this._hasBeenBlurred){
26946 Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
26947 }else{
26948 Tooltip.hide(this.domNode);
26949 }
26950 this._set("message", message);
26951 return isValid;
a089699c 26952 },
81bea17a 26953
1354d172 26954 isValid: function(/*Boolean*/ /*===== isFocused =====*/){
a089699c 26955 // summary:
1354d172
AD
26956 // Whether or not this is a valid value. The only way a Select
26957 // can be invalid is when it's required but nothing is selected.
26958 return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
a089699c 26959 },
81bea17a 26960
1354d172 26961 reset: function(){
a089699c 26962 // summary:
1354d172
AD
26963 // Overridden so that the state will be cleared.
26964 this.inherited(arguments);
26965 Tooltip.hide(this.domNode);
26966 this.validate(this.focused); // to update this.state
a089699c 26967 },
81bea17a 26968
1354d172 26969 postMixInProperties: function(){
a089699c 26970 // summary:
1354d172
AD
26971 // set the missing message
26972 this.inherited(arguments);
26973 this._missingMsg = i18n.getLocalization("dijit.form", "validate",
26974 this.lang).missingMessage;
a089699c 26975 },
81bea17a 26976
1354d172 26977 postCreate: function(){
a089699c 26978 // summary:
1354d172 26979 // stop mousemove from selecting text on IE to be consistent with other browsers
81bea17a 26980
1354d172
AD
26981 this.inherited(arguments);
26982
26983 this.connect(this.domNode, "onmousemove", event.stop);
81bea17a
AD
26984 },
26985
1354d172
AD
26986 _setStyleAttr: function(/*String||Object*/ value){
26987 this.inherited(arguments);
26988 domClass.toggle(this.domNode, this.baseClass + "FixedWidth", !!this.domNode.style.width);
a089699c 26989 },
81bea17a 26990
1354d172
AD
26991 isLoaded: function(){
26992 return this._isLoaded;
81bea17a
AD
26993 },
26994
1354d172 26995 loadDropDown: function(/*Function*/ loadCallback){
81bea17a 26996 // summary:
1354d172
AD
26997 // populates the menu
26998 this._loadChildren(true);
26999 this._isLoaded = true;
27000 loadCallback();
81bea17a
AD
27001 },
27002
1354d172
AD
27003 closeDropDown: function(){
27004 // overriding _HasDropDown.closeDropDown()
27005 this.inherited(arguments);
81bea17a 27006
1354d172
AD
27007 if(this.dropDown && this.dropDown.menuTableNode){
27008 // Erase possible width: 100% setting from _SelectMenu.resize().
27009 // Leaving it would interfere with the next openDropDown() call, which
27010 // queries the natural size of the drop down.
27011 this.dropDown.menuTableNode.style.width = "";
27012 }
27013 },
81bea17a 27014
1354d172
AD
27015 uninitialize: function(preserveDom){
27016 if(this.dropDown && !this.dropDown._destroyed){
27017 this.dropDown.destroyRecursive(preserveDom);
27018 delete this.dropDown;
a089699c 27019 }
1354d172 27020 this.inherited(arguments);
a089699c
AD
27021 },
27022
1354d172
AD
27023 _onFocus: function(){
27024 this.validate(true); // show tooltip if second focus of required tooltip, but no selection
27025 this.inherited(arguments);
a089699c 27026 },
81bea17a 27027
1354d172
AD
27028 _onBlur: function(){
27029 Tooltip.hide(this.domNode);
27030 this.inherited(arguments);
27031 }
27032});
27033
27034Select._Menu = _SelectMenu; // for monkey patching
27035
27036return Select;
27037});
27038
27039},
27040'dojo/store/util/QueryResults':function(){
27041define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
27042], function(array, lang, Deferred) {
27043 // module:
27044 // dojo/store/util/QueryResults
27045 // summary:
27046 // The module defines a query results wrapper
27047
27048var util = lang.getObject("dojo.store.util", true);
27049
27050util.QueryResults = function(results){
27051 // summary:
27052 // A function that wraps the results of a store query with additional
27053 // methods.
27054 //
27055 // description:
27056 // QueryResults is a basic wrapper that allows for array-like iteration
27057 // over any kind of returned data from a query. While the simplest store
27058 // will return a plain array of data, other stores may return deferreds or
27059 // promises; this wrapper makes sure that *all* results can be treated
27060 // the same.
27061 //
27062 // Additional methods include `forEach`, `filter` and `map`.
27063 //
27064 // returns: Object
27065 // An array-like object that can be used for iterating over.
27066 //
27067 // example:
27068 // Query a store and iterate over the results.
27069 //
27070 // | store.query({ prime: true }).forEach(function(item){
27071 // | // do something
27072 // | });
27073
27074 if(!results){
27075 return results;
27076 }
27077 // if it is a promise it may be frozen
27078 if(results.then){
27079 results = lang.delegate(results);
27080 }
27081 function addIterativeMethod(method){
27082 if(!results[method]){
27083 results[method] = function(){
27084 var args = arguments;
27085 return Deferred.when(results, function(results){
27086 Array.prototype.unshift.call(args, results);
27087 return util.QueryResults(array[method].apply(array, args));
27088 });
27089 };
a089699c 27090 }
1354d172
AD
27091 }
27092 addIterativeMethod("forEach");
27093 addIterativeMethod("filter");
27094 addIterativeMethod("map");
27095 if(!results.total){
27096 results.total = Deferred.when(results, function(results){
27097 return results.length;
27098 });
27099 }
27100 return results;
27101};
a089699c 27102
1354d172
AD
27103return util.QueryResults;
27104});
81bea17a 27105
1354d172
AD
27106},
27107'dijit/form/_ListBase':function(){
27108define("dijit/form/_ListBase", [
27109 "dojo/_base/declare", // declare
27110 "dojo/window" // winUtils.scrollIntoView
27111], function(declare, winUtils){
27112
27113// module:
27114// dijit/form/_ListBase
27115// summary:
27116// Focus-less menu to handle UI events consistently
81bea17a 27117
1354d172
AD
27118return declare( "dijit.form._ListBase", null, {
27119 // summary:
27120 // Focus-less menu to handle UI events consistently
27121 // Abstract methods that must be defined externally:
27122 // onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
27123 // onDeselect: cancels onSelect
27124 // tags:
27125 // private
27126
27127 // selected: DOMnode
27128 // currently selected node
27129 selected: null,
27130
27131 _getTarget: function(/*Event*/ evt){
27132 var tgt = evt.target;
27133 var container = this.containerNode;
27134 if(tgt == container || tgt == this.domNode){ return null; }
27135 while(tgt && tgt.parentNode != container){
27136 // recurse to the top
27137 tgt = tgt.parentNode;
a089699c 27138 }
1354d172
AD
27139 return tgt;
27140 },
81bea17a 27141
1354d172
AD
27142 selectFirstNode: function(){
27143 // summary:
27144 // Select the first displayed item in the list.
27145 var first = this.containerNode.firstChild;
27146 while(first && first.style.display == "none"){
27147 first = first.nextSibling;
a089699c 27148 }
1354d172 27149 this._setSelectedAttr(first);
a089699c 27150 },
81bea17a 27151
1354d172 27152 selectLastNode: function(){
a089699c 27153 // summary:
1354d172
AD
27154 // Select the last displayed item in the list
27155 var last = this.containerNode.lastChild;
27156 while(last && last.style.display == "none"){
27157 last = last.previousSibling;
27158 }
27159 this._setSelectedAttr(last);
27160 },
81bea17a 27161
1354d172
AD
27162 selectNextNode: function(){
27163 // summary:
27164 // Select the item just below the current selection.
27165 // If nothing selected, select first node.
27166 var selectedNode = this._getSelectedAttr();
27167 if(!selectedNode){
27168 this.selectFirstNode();
27169 }else{
27170 var next = selectedNode.nextSibling;
27171 while(next && next.style.display == "none"){
27172 next = next.nextSibling;
27173 }
27174 if(!next){
27175 this.selectFirstNode();
27176 }else{
27177 this._setSelectedAttr(next);
a089699c
AD
27178 }
27179 }
a089699c 27180 },
81bea17a 27181
1354d172 27182 selectPreviousNode: function(){
a089699c 27183 // summary:
1354d172
AD
27184 // Select the item just above the current selection.
27185 // If nothing selected, select last node (if
27186 // you select Previous and try to keep scrolling up the list).
27187 var selectedNode = this._getSelectedAttr();
27188 if(!selectedNode){
27189 this.selectLastNode();
81bea17a 27190 }else{
1354d172
AD
27191 var prev = selectedNode.previousSibling;
27192 while(prev && prev.style.display == "none"){
27193 prev = prev.previousSibling;
27194 }
27195 if(!prev){
27196 this.selectLastNode();
27197 }else{
27198 this._setSelectedAttr(prev);
81bea17a 27199 }
a089699c
AD
27200 }
27201 },
81bea17a 27202
1354d172 27203 _setSelectedAttr: function(/*DomNode*/ node){
a089699c 27204 // summary:
1354d172
AD
27205 // Does the actual select.
27206 if(this.selected != node){
27207 var selectedNode = this._getSelectedAttr();
27208 if(selectedNode){
27209 this.onDeselect(selectedNode);
27210 this.selected = null;
27211 }
27212 if(node && node.parentNode == this.containerNode){
27213 this.selected = node;
27214 winUtils.scrollIntoView(node);
27215 this.onSelect(node);
27216 }
27217 }else if(node){
27218 this.onSelect(node);
a089699c 27219 }
a089699c 27220 },
81bea17a 27221
1354d172 27222 _getSelectedAttr: function(){
a089699c 27223 // summary:
1354d172
AD
27224 // Returns the selected node.
27225 var v = this.selected;
27226 return (v && v.parentNode == this.containerNode) ? v : (this.selected = null);
27227 }
27228});
a089699c 27229
1354d172 27230});
a089699c 27231
1354d172
AD
27232},
27233'dijit/form/_FormWidget':function(){
27234define("dijit/form/_FormWidget", [
27235 "dojo/_base/declare", // declare
27236 "dojo/_base/kernel", // kernel.deprecated
27237 "dojo/ready",
27238 "../_Widget",
27239 "../_CssStateMixin",
27240 "../_TemplatedMixin",
27241 "./_FormWidgetMixin"
27242], function(declare, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){
a089699c 27243
1354d172
AD
27244/*=====
27245var _Widget = dijit._Widget;
27246var _TemplatedMixin = dijit._TemplatedMixin;
27247var _CssStateMixin = dijit._CssStateMixin;
27248var _FormWidgetMixin = dijit.form._FormWidgetMixin;
27249=====*/
a089699c 27250
1354d172
AD
27251// module:
27252// dijit/form/_FormWidget
27253// summary:
27254// FormWidget
a089699c 27255
81bea17a 27256
1354d172
AD
27257// Back compat w/1.6, remove for 2.0
27258if(!kernel.isAsync){
27259 ready(0, function(){
27260 var requires = ["dijit/form/_FormValueWidget"];
27261 require(requires); // use indirection so modules not rolled into a build
27262 });
27263}
81bea17a 27264
1354d172
AD
27265return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
27266 // summary:
27267 // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>,
27268 // which can be children of a <form> node or a `dijit.form.Form` widget.
27269 //
27270 // description:
27271 // Represents a single HTML element.
27272 // All these widgets should have these attributes just like native HTML input elements.
27273 // You can set them during widget construction or afterwards, via `dijit._Widget.attr`.
27274 //
27275 // They also share some common methods.
a089699c 27276
1354d172 27277 setDisabled: function(/*Boolean*/ disabled){
81bea17a 27278 // summary:
1354d172
AD
27279 // Deprecated. Use set('disabled', ...) instead.
27280 kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
27281 this.set('disabled', disabled);
81bea17a 27282 },
a089699c 27283
1354d172 27284 setValue: function(/*String*/ value){
81bea17a 27285 // summary:
1354d172
AD
27286 // Deprecated. Use set('value', ...) instead.
27287 kernel.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0");
27288 this.set('value', value);
81bea17a 27289 },
a089699c 27290
1354d172 27291 getValue: function(){
a089699c 27292 // summary:
1354d172
AD
27293 // Deprecated. Use get('value') instead.
27294 kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
27295 return this.get('value');
81bea17a 27296 },
a089699c 27297
1354d172
AD
27298 postMixInProperties: function(){
27299 // Setup name=foo string to be referenced from the template (but only if a name has been specified)
27300 // Unfortunately we can't use _setNameAttr to set the name due to IE limitations, see #8484, #8660.
27301 // Regarding escaping, see heading "Attribute values" in
27302 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
27303 this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, "&quot;") + '"') : '';
27304 this.inherited(arguments);
81bea17a
AD
27305 },
27306
1354d172
AD
27307 // Override automatic assigning type --> focusNode, it causes exception on IE.
27308 // Instead, type must be specified as ${type} in the template, as part of the original DOM
27309 _setTypeAttr: null
27310});
a089699c 27311
1354d172 27312});
a089699c 27313
1354d172
AD
27314},
27315'dojo/DeferredList':function(){
27316define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray) {
27317 // module:
27318 // dojo/DeferredList
27319 // summary:
27320 // TODOC
a089699c 27321
a089699c 27322
1354d172
AD
27323dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
27324 // summary:
27325 // Provides event handling for a group of Deferred objects.
27326 // description:
27327 // DeferredList takes an array of existing deferreds and returns a new deferred of its own
27328 // this new deferred will typically have its callback fired when all of the deferreds in
27329 // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
27330 // fireOnOneErrback, will fire before all the deferreds as appropriate
27331 //
27332 // list:
27333 // The list of deferreds to be synchronizied with this DeferredList
27334 // fireOnOneCallback:
27335 // Will cause the DeferredLists callback to be fired as soon as any
27336 // of the deferreds in its list have been fired instead of waiting until
27337 // the entire list has finished
27338 // fireonOneErrback:
27339 // Will cause the errback to fire upon any of the deferreds errback
27340 // canceller:
27341 // A deferred canceller function, see dojo.Deferred
27342 var resultList = [];
27343 Deferred.call(this);
27344 var self = this;
27345 if(list.length === 0 && !fireOnOneCallback){
27346 this.resolve([0, []]);
27347 }
27348 var finished = 0;
27349 darray.forEach(list, function(item, i){
27350 item.then(function(result){
27351 if(fireOnOneCallback){
27352 self.resolve([i, result]);
27353 }else{
27354 addResult(true, result);
81bea17a 27355 }
1354d172
AD
27356 },function(error){
27357 if(fireOnOneErrback){
27358 self.reject(error);
27359 }else{
27360 addResult(false, error);
27361 }
27362 if(consumeErrors){
27363 return null;
27364 }
27365 throw error;
27366 });
27367 function addResult(succeeded, result){
27368 resultList[i] = [succeeded, result];
27369 finished++;
27370 if(finished === list.length){
27371 self.resolve(resultList);
81bea17a 27372 }
a089699c 27373
81bea17a 27374 }
1354d172
AD
27375 });
27376};
27377dojo.DeferredList.prototype = new Deferred();
a089699c 27378
1354d172
AD
27379dojo.DeferredList.prototype.gatherResults = function(deferredList){
27380 // summary:
27381 // Gathers the results of the deferreds for packaging
27382 // as the parameters to the Deferred Lists' callback
27383 // deferredList: dojo.DeferredList
27384 // The deferred list from which this function gathers results.
27385 // returns: dojo.DeferredList
27386 // The newly created deferred list which packs results as
27387 // parameters to its callback.
a089699c 27388
1354d172
AD
27389 var d = new dojo.DeferredList(deferredList, false, true, false);
27390 d.addCallback(function(results){
27391 var ret = [];
27392 darray.forEach(results, function(result){
27393 ret.push(result[1]);
27394 });
27395 return ret;
27396 });
27397 return d;
27398};
a089699c 27399
1354d172
AD
27400return dojo.DeferredList;
27401});
a089699c 27402
1354d172
AD
27403},
27404'dojo/dnd/common':function(){
27405define("dojo/dnd/common", ["../main"], function(dojo) {
27406 // module:
27407 // dojo/dnd/common
27408 // summary:
27409 // TODOC
a089699c 27410
1354d172 27411dojo.getObject("dnd", true, dojo);
a089699c 27412
1354d172 27413dojo.dnd.getCopyKeyState = dojo.isCopyKey;
a089699c 27414
1354d172
AD
27415dojo.dnd._uniqueId = 0;
27416dojo.dnd.getUniqueId = function(){
27417 // summary:
27418 // returns a unique string for use with any DOM element
27419 var id;
27420 do{
27421 id = dojo._scopeName + "Unique" + (++dojo.dnd._uniqueId);
27422 }while(dojo.byId(id));
27423 return id;
27424};
a089699c 27425
1354d172 27426dojo.dnd._empty = {};
a089699c 27427
1354d172
AD
27428dojo.dnd.isFormElement = function(/*Event*/ e){
27429 // summary:
27430 // returns true if user clicked on a form element
27431 var t = e.target;
27432 if(t.nodeType == 3 /*TEXT_NODE*/){
27433 t = t.parentNode;
27434 }
27435 return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
27436};
a089699c 27437
1354d172
AD
27438return dojo.dnd;
27439});
a089699c 27440
1354d172
AD
27441},
27442'dijit/_base/place':function(){
27443define("dijit/_base/place", [
27444 "dojo/_base/array", // array.forEach
27445 "dojo/_base/lang", // lang.isArray
27446 "dojo/window", // windowUtils.getBox
27447 "../place",
27448 ".." // export to dijit namespace
27449], function(array, lang, windowUtils, place, dijit){
27450
27451 // module:
27452 // dijit/_base/place
27453 // summary:
27454 // Back compatibility module, new code should use dijit/place directly instead of using this module.
a089699c 27455
1354d172 27456 dijit.getViewport = function(){
a089699c 27457 // summary:
1354d172
AD
27458 // Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
27459 // New code should use windowUtils.getBox()
a089699c 27460
1354d172
AD
27461 return windowUtils.getBox();
27462 };
a089699c 27463
1354d172
AD
27464 /*=====
27465 dijit.placeOnScreen = function(node, pos, corners, padding){
81bea17a 27466 // summary:
1354d172
AD
27467 // Positions one of the node's corners at specified position
27468 // such that node is fully visible in viewport.
27469 // Deprecated, new code should use dijit.place.at() instead.
27470 };
27471 =====*/
27472 dijit.placeOnScreen = place.at;
a089699c 27473
1354d172
AD
27474 /*=====
27475 dijit.placeOnScreenAroundElement = function(node, aroundElement, aroundCorners, layoutNode){
27476 // summary:
27477 // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
27478 // for the "around" argument and finds a proper processor to place a node.
27479 // Deprecated, new code should use dijit.place.around() instead.
27480 };
27481 ====*/
27482 dijit.placeOnScreenAroundElement = function(node, aroundNode, aroundCorners, layoutNode){
27483 // Convert old style {"BL": "TL", "BR": "TR"} type argument
27484 // to style needed by dijit.place code:
27485 // [
27486 // {aroundCorner: "BL", corner: "TL" },
27487 // {aroundCorner: "BR", corner: "TR" }
27488 // ]
27489 var positions;
27490 if(lang.isArray(aroundCorners)){
27491 positions = aroundCorners;
27492 }else{
27493 positions = [];
27494 for(var key in aroundCorners){
27495 positions.push({aroundCorner: key, corner: aroundCorners[key]});
81bea17a 27496 }
81bea17a 27497 }
a089699c 27498
1354d172
AD
27499 return place.around(node, aroundNode, positions, true, layoutNode);
27500 };
a089699c 27501
1354d172
AD
27502 /*=====
27503 dijit.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
81bea17a 27504 // summary:
1354d172
AD
27505 // Position node adjacent or kitty-corner to aroundNode
27506 // such that it's fully visible in viewport.
27507 // Deprecated, new code should use dijit.place.around() instead.
27508 };
27509 =====*/
27510 dijit.placeOnScreenAroundNode = dijit.placeOnScreenAroundElement;
a089699c 27511
1354d172
AD
27512 /*=====
27513 dijit.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, layoutNode){
81bea17a 27514 // summary:
1354d172
AD
27515 // Like dijit.placeOnScreenAroundNode(), except that the "around"
27516 // parameter is an arbitrary rectangle on the screen (x, y, width, height)
27517 // instead of a dom node.
27518 // Deprecated, new code should use dijit.place.around() instead.
27519 };
27520 =====*/
27521 dijit.placeOnScreenAroundRectangle = dijit.placeOnScreenAroundElement;
a089699c 27522
1354d172 27523 dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
81bea17a 27524 // summary:
1354d172
AD
27525 // Deprecated method, unneeded when using dijit/place directly.
27526 // Transforms the passed array of preferred positions into a format suitable for
27527 // passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
27528 //
27529 // position: String[]
27530 // This variable controls the position of the drop down.
27531 // It's an array of strings with the following values:
27532 //
27533 // * before: places drop down to the left of the target node/widget, or to the right in
27534 // the case of RTL scripts like Hebrew and Arabic
27535 // * after: places drop down to the right of the target node/widget, or to the left in
27536 // the case of RTL scripts like Hebrew and Arabic
27537 // * above: drop down goes above target node
27538 // * below: drop down goes below target node
27539 //
27540 // The list is positions is tried, in order, until a position is found where the drop down fits
27541 // within the viewport.
27542 //
27543 // leftToRight: Boolean
27544 // Whether the popup will be displaying in leftToRight mode.
27545 //
27546 var align = {};
27547 array.forEach(position, function(pos){
27548 var ltr = leftToRight;
27549 switch(pos){
27550 case "after":
27551 align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
27552 break;
27553 case "before":
27554 align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
27555 break;
27556 case "below-alt":
27557 ltr = !ltr;
27558 // fall through
27559 case "below":
27560 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
27561 align[ltr ? "BL" : "BR"] = ltr ? "TL" : "TR";
27562 align[ltr ? "BR" : "BL"] = ltr ? "TR" : "TL";
27563 break;
27564 case "above-alt":
27565 ltr = !ltr;
27566 // fall through
27567 case "above":
27568 default:
27569 // first try to align left borders, next try to align right borders (or reverse for RTL mode)
27570 align[ltr ? "TL" : "TR"] = ltr ? "BL" : "BR";
27571 align[ltr ? "TR" : "TL"] = ltr ? "BR" : "BL";
27572 break;
27573 }
27574 });
27575 return align;
27576 };
a089699c 27577
1354d172
AD
27578 return dijit;
27579});
a089699c 27580
1354d172
AD
27581},
27582'dijit/MenuSeparator':function(){
27583require({cache:{
27584'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>"}});
27585define("dijit/MenuSeparator", [
27586 "dojo/_base/declare", // declare
27587 "dojo/dom", // dom.setSelectable
27588 "./_WidgetBase",
27589 "./_TemplatedMixin",
27590 "./_Contained",
27591 "dojo/text!./templates/MenuSeparator.html"
27592], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
a089699c 27593
1354d172
AD
27594/*=====
27595 var _WidgetBase = dijit._WidgetBase;
27596 var _TemplatedMixin = dijit._TemplatedMixin;
27597 var _Contained = dijit._Contained;
27598=====*/
a089699c 27599
1354d172
AD
27600 // module:
27601 // dijit/MenuSeparator
27602 // summary:
27603 // A line between two menu items
a089699c 27604
1354d172 27605 return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
81bea17a 27606 // summary:
1354d172 27607 // A line between two menu items
a089699c 27608
1354d172 27609 templateString: template,
a089699c 27610
1354d172
AD
27611 buildRendering: function(){
27612 this.inherited(arguments);
27613 dom.setSelectable(this.domNode, false);
27614 },
a089699c 27615
1354d172
AD
27616 isFocusable: function(){
27617 // summary:
27618 // Override to always return false
27619 // tags:
27620 // protected
a089699c 27621
1354d172 27622 return false; // Boolean
a089699c 27623 }
1354d172 27624 });
a089699c
AD
27625});
27626
1354d172
AD
27627},
27628'dijit/form/_ComboBoxMenu':function(){
27629define("dijit/form/_ComboBoxMenu", [
27630 "dojo/_base/declare", // declare
27631 "dojo/dom-class", // domClass.add domClass.remove
27632 "dojo/dom-construct", // domConstruct.create
27633 "dojo/dom-style", // domStyle.get
27634 "dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
27635 "../_WidgetBase",
27636 "../_TemplatedMixin",
27637 "./_ComboBoxMenuMixin",
27638 "./_ListMouseMixin"
27639], function(declare, domClass, domConstruct, domStyle, keys,
27640 _WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
81bea17a 27641
1354d172
AD
27642/*=====
27643 var _WidgetBase = dijit._WidgetBase;
27644 var _TemplatedMixin = dijit._TemplatedMixin;
27645 var _ComboBoxMenuMixin = dijit.form._ComboBoxMenuMixin;
27646 var _ListMouseMixin = dijit.form._ListMouseMixin;
27647=====*/
a089699c 27648
1354d172
AD
27649 // module:
27650 // dijit/form/_ComboBoxMenu
27651 // summary:
27652 // Focus-less menu for internal use in `dijit.form.ComboBox`
a089699c 27653
1354d172
AD
27654 return declare("dijit.form._ComboBoxMenu",[_WidgetBase, _TemplatedMixin, _ListMouseMixin, _ComboBoxMenuMixin], {
27655 // summary:
27656 // Focus-less menu for internal use in `dijit.form.ComboBox`
27657 // Abstract methods that must be defined externally:
27658 // onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
27659 // onPage: next(1) or previous(-1) button pressed
27660 // tags:
27661 // private
a089699c 27662
1354d172
AD
27663 templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;'>"
27664 +"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
27665 +"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
27666 +"</div>",
a089699c 27667
1354d172 27668 baseClass: "dijitComboBoxMenu",
a089699c 27669
1354d172
AD
27670 postCreate: function(){
27671 this.inherited(arguments);
27672 if(!this.isLeftToRight()){
27673 domClass.add(this.previousButton, "dijitMenuItemRtl");
27674 domClass.add(this.nextButton, "dijitMenuItemRtl");
27675 }
27676 },
a089699c 27677
1354d172
AD
27678 _createMenuItem: function(){
27679 return domConstruct.create("div", {
27680 "class": "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl"),
27681 role: "option"
27682 });
27683 },
27684
27685 onHover: function(/*DomNode*/ node){
27686 // summary:
27687 // Add hover CSS
27688 domClass.add(node, "dijitMenuItemHover");
27689 },
27690
27691 onUnhover: function(/*DomNode*/ node){
27692 // summary:
27693 // Remove hover CSS
27694 domClass.remove(node, "dijitMenuItemHover");
27695 },
27696
27697 onSelect: function(/*DomNode*/ node){
27698 // summary:
27699 // Add selected CSS
27700 domClass.add(node, "dijitMenuItemSelected");
27701 },
27702
27703 onDeselect: function(/*DomNode*/ node){
27704 // summary:
27705 // Remove selected CSS
27706 domClass.remove(node, "dijitMenuItemSelected");
27707 },
27708
27709 _page: function(/*Boolean*/ up){
27710 // summary:
27711 // Handles page-up and page-down keypresses
27712
27713 var scrollamount = 0;
27714 var oldscroll = this.domNode.scrollTop;
27715 var height = domStyle.get(this.domNode, "height");
27716 // if no item is highlighted, highlight the first option
27717 if(!this.getHighlightedOption()){
27718 this.selectNextNode();
27719 }
27720 while(scrollamount<height){
27721 var highlighted_option = this.getHighlightedOption();
27722 if(up){
27723 // stop at option 1
27724 if(!highlighted_option.previousSibling ||
27725 highlighted_option.previousSibling.style.display == "none"){
27726 break;
27727 }
27728 this.selectPreviousNode();
27729 }else{
27730 // stop at last option
27731 if(!highlighted_option.nextSibling ||
27732 highlighted_option.nextSibling.style.display == "none"){
27733 break;
27734 }
27735 this.selectNextNode();
a089699c 27736 }
1354d172
AD
27737 // going backwards
27738 var newscroll = this.domNode.scrollTop;
27739 scrollamount += (newscroll-oldscroll)*(up ? -1:1);
27740 oldscroll = newscroll;
a089699c 27741 }
1354d172
AD
27742 },
27743
27744 handleKey: function(evt){
27745 // summary:
27746 // Handle keystroke event forwarded from ComboBox, returning false if it's
27747 // a keystroke I recognize and process, true otherwise.
27748 switch(evt.charOrCode){
27749 case keys.DOWN_ARROW:
27750 this.selectNextNode();
27751 return false;
27752 case keys.PAGE_DOWN:
27753 this._page(false);
27754 return false;
27755 case keys.UP_ARROW:
27756 this.selectPreviousNode();
27757 return false;
27758 case keys.PAGE_UP:
27759 this._page(true);
27760 return false;
27761 default:
27762 return true;
a089699c 27763 }
a089699c 27764 }
1354d172 27765 });
a089699c
AD
27766});
27767
1354d172
AD
27768},
27769'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
27770'dijit/Dialog':function(){
27771require({cache:{
27772'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<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"></span>\n\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t</span>\n\t</div>\n\t\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
27773define("dijit/Dialog", [
27774 "require",
27775 "dojo/_base/array", // array.forEach array.indexOf array.map
27776 "dojo/_base/connect", // connect._keypress
27777 "dojo/_base/declare", // declare
27778 "dojo/_base/Deferred", // Deferred
27779 "dojo/dom", // dom.isDescendant
27780 "dojo/dom-class", // domClass.add domClass.contains
27781 "dojo/dom-geometry", // domGeometry.position
27782 "dojo/dom-style", // domStyle.set
27783 "dojo/_base/event", // event.stop
27784 "dojo/_base/fx", // fx.fadeIn fx.fadeOut
27785 "dojo/i18n", // i18n.getLocalization
27786 "dojo/_base/kernel", // kernel.isAsync
27787 "dojo/keys",
27788 "dojo/_base/lang", // lang.mixin lang.hitch
27789 "dojo/on",
27790 "dojo/ready",
27791 "dojo/_base/sniff", // has("ie") has("opera")
27792 "dojo/_base/window", // win.body
27793 "dojo/window", // winUtils.getBox
27794 "dojo/dnd/Moveable", // Moveable
27795 "dojo/dnd/TimedMoveable", // TimedMoveable
27796 "./focus",
27797 "./_base/manager", // manager.defaultDuration
27798 "./_Widget",
27799 "./_TemplatedMixin",
27800 "./_CssStateMixin",
27801 "./form/_FormMixin",
27802 "./_DialogMixin",
27803 "./DialogUnderlay",
27804 "./layout/ContentPane",
27805 "dojo/text!./templates/Dialog.html",
27806 ".", // for back-compat, exporting dijit._underlay (remove in 2.0)
27807 "dojo/i18n!./nls/common"
27808], function(require, array, connect, declare, Deferred,
27809 dom, domClass, domGeometry, domStyle, event, fx, i18n, kernel, keys, lang, on, ready, has, win, winUtils,
27810 Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
27811 DialogUnderlay, ContentPane, template, dijit){
27812
27813/*=====
27814 var _Widget = dijit._Widget;
27815 var _TemplatedMixin = dijit._TemplatedMixin;
27816 var _CssStateMixin = dijit._CssStateMixin;
27817 var _FormMixin = dijit.form._FormMixin;
27818 var _DialogMixin = dijit._DialogMixin;
27819=====*/
a089699c 27820
a089699c 27821
1354d172
AD
27822 // module:
27823 // dijit/Dialog
27824 // summary:
27825 // A modal dialog Widget
a089699c
AD
27826
27827
1354d172
AD
27828 /*=====
27829 dijit._underlay = function(kwArgs){
27830 // summary:
27831 // A shared instance of a `dijit.DialogUnderlay`
27832 //
27833 // description:
27834 // A shared instance of a `dijit.DialogUnderlay` created and
27835 // used by `dijit.Dialog`, though never created until some Dialog
27836 // or subclass thereof is shown.
27837 };
27838 =====*/
a089699c 27839
1354d172
AD
27840 var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
27841 // summary:
27842 // A modal dialog Widget
27843 //
27844 // description:
27845 // Pops up a modal dialog window, blocking access to the screen
27846 // and also graying out the screen Dialog is extended from
27847 // ContentPane so it supports all the same parameters (href, etc.)
27848 //
27849 // example:
27850 // | <div data-dojo-type="dijit.Dialog" data-dojo-props="href: 'test.html'"></div>
27851 //
27852 // example:
27853 // | var foo = new dijit.Dialog({ title: "test dialog", content: "test content" };
27854 // | dojo.body().appendChild(foo.domNode);
27855 // | foo.startup();
a089699c 27856
1354d172 27857 templateString: template,
a089699c 27858
1354d172 27859 baseClass: "dijitDialog",
a089699c 27860
1354d172
AD
27861 cssStateNodes: {
27862 closeButtonNode: "dijitDialogCloseIcon"
27863 },
a089699c 27864
1354d172
AD
27865 // Map widget attributes to DOMNode attributes.
27866 _setTitleAttr: [
27867 { node: "titleNode", type: "innerHTML" },
27868 { node: "titleBar", type: "attribute" }
27869 ],
a089699c 27870
1354d172
AD
27871 // open: [readonly] Boolean
27872 // True if Dialog is currently displayed on screen.
27873 open: false,
a089699c 27874
1354d172
AD
27875 // duration: Integer
27876 // The time in milliseconds it takes the dialog to fade in and out
27877 duration: manager.defaultDuration,
a089699c 27878
1354d172
AD
27879 // refocus: Boolean
27880 // A Toggle to modify the default focus behavior of a Dialog, which
27881 // is to re-focus the element which had focus before being opened.
27882 // False will disable refocusing. Default: true
27883 refocus: true,
a089699c 27884
1354d172
AD
27885 // autofocus: Boolean
27886 // A Toggle to modify the default focus behavior of a Dialog, which
27887 // is to focus on the first dialog element after opening the dialog.
27888 // False will disable autofocusing. Default: true
27889 autofocus: true,
a089699c 27890
1354d172
AD
27891 // _firstFocusItem: [private readonly] DomNode
27892 // The pointer to the first focusable node in the dialog.
27893 // Set by `dijit._DialogMixin._getFocusItems`.
27894 _firstFocusItem: null,
a089699c 27895
1354d172
AD
27896 // _lastFocusItem: [private readonly] DomNode
27897 // The pointer to which node has focus prior to our dialog.
27898 // Set by `dijit._DialogMixin._getFocusItems`.
27899 _lastFocusItem: null,
a089699c 27900
1354d172
AD
27901 // doLayout: [protected] Boolean
27902 // Don't change this parameter from the default value.
27903 // This ContentPane parameter doesn't make sense for Dialog, since Dialog
27904 // is never a child of a layout container, nor can you specify the size of
27905 // Dialog in order to control the size of an inner widget.
27906 doLayout: false,
a089699c 27907
1354d172
AD
27908 // draggable: Boolean
27909 // Toggles the moveable aspect of the Dialog. If true, Dialog
27910 // can be dragged by it's title. If false it will remain centered
27911 // in the viewport.
27912 draggable: true,
a089699c 27913
1354d172
AD
27914 //aria-describedby: String
27915 // Allows the user to add an aria-describedby attribute onto the dialog. The value should
27916 // be the id of the container element of text that describes the dialog purpose (usually
27917 // the first text in the dialog).
27918 // <div data-dojo-type="dijit.Dialog" aria-describedby="intro" .....>
27919 // <div id="intro">Introductory text</div>
27920 // <div>rest of dialog contents</div>
27921 // </div>
27922 "aria-describedby":"",
a089699c 27923
1354d172
AD
27924 postMixInProperties: function(){
27925 var _nlsResources = i18n.getLocalization("dijit", "common");
27926 lang.mixin(this, _nlsResources);
27927 this.inherited(arguments);
27928 },
a089699c 27929
1354d172
AD
27930 postCreate: function(){
27931 domStyle.set(this.domNode, {
27932 display: "none",
27933 position:"absolute"
27934 });
27935 win.body().appendChild(this.domNode);
a089699c 27936
1354d172 27937 this.inherited(arguments);
a089699c 27938
1354d172
AD
27939 this.connect(this, "onExecute", "hide");
27940 this.connect(this, "onCancel", "hide");
27941 this._modalconnects = [];
27942 },
a089699c 27943
1354d172
AD
27944 onLoad: function(){
27945 // summary:
27946 // Called when data has been loaded from an href.
27947 // Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
27948 // but should *not* be overridden.
27949 // tags:
27950 // callback
a089699c 27951
1354d172
AD
27952 // when href is specified we need to reposition the dialog after the data is loaded
27953 // and find the focusable elements
27954 this._position();
27955 if(this.autofocus && DialogLevelManager.isTop(this)){
27956 this._getFocusItems(this.domNode);
27957 focus.focus(this._firstFocusItem);
27958 }
27959 this.inherited(arguments);
27960 },
a089699c 27961
1354d172
AD
27962 _endDrag: function(){
27963 // summary:
27964 // Called after dragging the Dialog. Saves the position of the dialog in the viewport,
27965 // and also adjust position to be fully within the viewport, so user doesn't lose access to handle
27966 var nodePosition = domGeometry.position(this.domNode),
27967 viewport = winUtils.getBox();
27968 nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
27969 nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
27970 this._relativePosition = nodePosition;
27971 this._position();
27972 },
a089699c 27973
1354d172
AD
27974 _setup: function(){
27975 // summary:
27976 // Stuff we need to do before showing the Dialog for the first
27977 // time (but we defer it until right beforehand, for
27978 // performance reasons).
27979 // tags:
27980 // private
a089699c 27981
1354d172 27982 var node = this.domNode;
a089699c 27983
1354d172
AD
27984 if(this.titleBar && this.draggable){
27985 this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
27986 : Moveable)(node, { handle: this.titleBar });
27987 this.connect(this._moveable, "onMoveStop", "_endDrag");
27988 }else{
27989 domClass.add(node,"dijitDialogFixed");
27990 }
a089699c 27991
1354d172
AD
27992 this.underlayAttrs = {
27993 dialogId: this.id,
27994 "class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" ")
27995 };
27996 },
a089699c 27997
1354d172
AD
27998 _size: function(){
27999 // summary:
28000 // If necessary, shrink dialog contents so dialog fits in viewport
28001 // tags:
28002 // private
a089699c 28003
1354d172
AD
28004 this._checkIfSingleChild();
28005
28006 // If we resized the dialog contents earlier, reset them back to original size, so
28007 // that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
28008 // Need to do this before the domGeometry.position(this.domNode) call below.
28009 if(this._singleChild){
28010 if(this._singleChildOriginalStyle){
28011 this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
28012 }
28013 delete this._singleChildOriginalStyle;
28014 }else{
28015 domStyle.set(this.containerNode, {
28016 width:"auto",
28017 height:"auto"
28018 });
a089699c
AD
28019 }
28020
1354d172
AD
28021 var bb = domGeometry.position(this.domNode);
28022 var viewport = winUtils.getBox();
28023 if(bb.w >= viewport.w || bb.h >= viewport.h){
28024 // Reduce size of dialog contents so that dialog fits in viewport
28025
28026 var w = Math.min(bb.w, Math.floor(viewport.w * 0.75)),
28027 h = Math.min(bb.h, Math.floor(viewport.h * 0.75));
28028
28029 if(this._singleChild && this._singleChild.resize){
28030 this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
28031 this._singleChild.resize({w: w, h: h});
28032 }else{
28033 domStyle.set(this.containerNode, {
28034 width: w + "px",
28035 height: h + "px",
28036 overflow: "auto",
28037 position: "relative" // workaround IE bug moving scrollbar or dragging dialog
28038 });
28039 }
a089699c 28040 }else{
1354d172
AD
28041 if(this._singleChild && this._singleChild.resize){
28042 this._singleChild.resize();
28043 }
a089699c 28044 }
1354d172 28045 },
a089699c 28046
1354d172
AD
28047 _position: function(){
28048 // summary:
28049 // Position modal dialog in the viewport. If no relative offset
28050 // in the viewport has been determined (by dragging, for instance),
28051 // center the node. Otherwise, use the Dialog's stored relative offset,
28052 // and position the node to top: left: values based on the viewport.
28053 if(!domClass.contains(win.body(), "dojoMove")){ // don't do anything if called during auto-scroll
28054 var node = this.domNode,
28055 viewport = winUtils.getBox(),
28056 p = this._relativePosition,
28057 bb = p ? null : domGeometry.position(node),
28058 l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
28059 t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
28060 ;
28061 domStyle.set(node,{
28062 left: l + "px",
28063 top: t + "px"
28064 });
28065 }
28066 },
a089699c 28067
1354d172
AD
28068 _onKey: function(/*Event*/ evt){
28069 // summary:
28070 // Handles the keyboard events for accessibility reasons
28071 // tags:
28072 // private
28073
28074 if(evt.charOrCode){
28075 var node = evt.target;
28076 if(evt.charOrCode === keys.TAB){
28077 this._getFocusItems(this.domNode);
28078 }
28079 var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
28080 // see if we are shift-tabbing from first focusable item on dialog
28081 if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
28082 if(!singleFocusItem){
28083 focus.focus(this._lastFocusItem); // send focus to last item in dialog
28084 }
28085 event.stop(evt);
28086 }else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
28087 if(!singleFocusItem){
28088 focus.focus(this._firstFocusItem); // send focus to first item in dialog
28089 }
28090 event.stop(evt);
28091 }else{
28092 // see if the key is for the dialog
28093 while(node){
28094 if(node == this.domNode || domClass.contains(node, "dijitPopup")){
28095 if(evt.charOrCode == keys.ESCAPE){
28096 this.onCancel();
28097 }else{
28098 return; // just let it go
81bea17a 28099 }
81bea17a 28100 }
1354d172
AD
28101 node = node.parentNode;
28102 }
28103 // this key is for the disabled document window
28104 if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y
28105 event.stop(evt);
28106 // opera won't tab to a div
28107 }else if(!has("opera")){
28108 try{
28109 this._firstFocusItem.focus();
28110 }catch(e){ /*squelch*/ }
81bea17a 28111 }
a089699c
AD
28112 }
28113 }
1354d172 28114 },
a089699c 28115
1354d172
AD
28116 show: function(){
28117 // summary:
28118 // Display the dialog
28119 // returns: dojo.Deferred
28120 // Deferred object that resolves when the display animation is complete
a089699c 28121
1354d172 28122 if(this.open){ return; }
a089699c 28123
1354d172
AD
28124 if(!this._started){
28125 this.startup();
28126 }
a089699c 28127
1354d172
AD
28128 // first time we show the dialog, there's some initialization stuff to do
28129 if(!this._alreadyInitialized){
28130 this._setup();
28131 this._alreadyInitialized=true;
28132 }
a089699c 28133
1354d172
AD
28134 if(this._fadeOutDeferred){
28135 this._fadeOutDeferred.cancel();
28136 }
a089699c 28137
1354d172
AD
28138 this._modalconnects.push(on(window, "scroll", lang.hitch(this, "layout")));
28139 this._modalconnects.push(on(window, "resize", lang.hitch(this, function(){
28140 // IE gives spurious resize events and can actually get stuck
28141 // in an infinite loop if we don't ignore them
28142 var viewport = winUtils.getBox();
28143 if(!this._oldViewport ||
28144 viewport.h != this._oldViewport.h ||
28145 viewport.w != this._oldViewport.w){
28146 this.layout();
28147 this._oldViewport = viewport;
28148 }
28149 })));
28150 this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
a089699c 28151
1354d172
AD
28152 domStyle.set(this.domNode, {
28153 opacity:0,
28154 display:""
28155 });
a089699c 28156
1354d172
AD
28157 this._set("open", true);
28158 this._onShow(); // lazy load trigger
a089699c 28159
1354d172
AD
28160 this._size();
28161 this._position();
a089699c 28162
1354d172
AD
28163 // fade-in Animation object, setup below
28164 var fadeIn;
a089699c 28165
1354d172
AD
28166 this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
28167 fadeIn.stop();
28168 delete this._fadeInDeferred;
28169 }));
a089699c 28170
1354d172
AD
28171 fadeIn = fx.fadeIn({
28172 node: this.domNode,
28173 duration: this.duration,
28174 beforeBegin: lang.hitch(this, function(){
28175 DialogLevelManager.show(this, this.underlayAttrs);
28176 }),
28177 onEnd: lang.hitch(this, function(){
28178 if(this.autofocus && DialogLevelManager.isTop(this)){
28179 // find focusable items each time dialog is shown since if dialog contains a widget the
28180 // first focusable items can change
28181 this._getFocusItems(this.domNode);
28182 focus.focus(this._firstFocusItem);
28183 }
28184 this._fadeInDeferred.callback(true);
28185 delete this._fadeInDeferred;
28186 })
28187 }).play();
a089699c 28188
1354d172
AD
28189 return this._fadeInDeferred;
28190 },
a089699c 28191
1354d172
AD
28192 hide: function(){
28193 // summary:
28194 // Hide the dialog
28195 // returns: dojo.Deferred
28196 // Deferred object that resolves when the hide animation is complete
a089699c 28197
1354d172
AD
28198 // if we haven't been initialized yet then we aren't showing and we can just return
28199 if(!this._alreadyInitialized){
28200 return;
28201 }
28202 if(this._fadeInDeferred){
28203 this._fadeInDeferred.cancel();
a089699c
AD
28204 }
28205
1354d172
AD
28206 // fade-in Animation object, setup below
28207 var fadeOut;
a089699c 28208
1354d172
AD
28209 this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
28210 fadeOut.stop();
28211 delete this._fadeOutDeferred;
28212 }));
28213 // fire onHide when the promise resolves.
28214 this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
a089699c 28215
1354d172
AD
28216 fadeOut = fx.fadeOut({
28217 node: this.domNode,
28218 duration: this.duration,
28219 onEnd: lang.hitch(this, function(){
28220 this.domNode.style.display = "none";
28221 DialogLevelManager.hide(this);
28222 this._fadeOutDeferred.callback(true);
28223 delete this._fadeOutDeferred;
28224 })
28225 }).play();
a089699c 28226
1354d172
AD
28227 if(this._scrollConnected){
28228 this._scrollConnected = false;
28229 }
28230 var h;
28231 while(h = this._modalconnects.pop()){
28232 h.remove();
28233 }
a089699c 28234
1354d172
AD
28235 if(this._relativePosition){
28236 delete this._relativePosition;
28237 }
28238 this._set("open", false);
a089699c 28239
1354d172
AD
28240 return this._fadeOutDeferred;
28241 },
28242
28243 layout: function(){
28244 // summary:
28245 // Position the Dialog and the underlay
28246 // tags:
28247 // private
28248 if(this.domNode.style.display != "none"){
28249 if(dijit._underlay){ // avoid race condition during show()
28250 dijit._underlay.layout();
a089699c 28251 }
1354d172
AD
28252 this._position();
28253 }
28254 },
a089699c 28255
1354d172
AD
28256 destroy: function(){
28257 if(this._fadeInDeferred){
28258 this._fadeInDeferred.cancel();
28259 }
28260 if(this._fadeOutDeferred){
28261 this._fadeOutDeferred.cancel();
28262 }
28263 if(this._moveable){
28264 this._moveable.destroy();
28265 }
28266 var h;
28267 while(h = this._modalconnects.pop()){
28268 h.remove();
28269 }
28270
28271 DialogLevelManager.hide(this);
28272
28273 this.inherited(arguments);
a089699c 28274 }
1354d172 28275 });
a089699c 28276
1354d172
AD
28277 var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {});
28278 Dialog._DialogBase = _DialogBase; // for monkey patching
a089699c 28279
1354d172 28280 var DialogLevelManager = Dialog._DialogLevelManager = {
a089699c 28281 // summary:
1354d172
AD
28282 // Controls the various active "levels" on the page, starting with the
28283 // stuff initially visible on the page (at z-index 0), and then having an entry for
28284 // each Dialog shown.
a089699c 28285
1354d172 28286 _beginZIndex: 950,
a089699c 28287
1354d172
AD
28288 show: function(/*dijit._Widget*/ dialog, /*Object*/ underlayAttrs){
28289 // summary:
28290 // Call right before fade-in animation for new dialog.
28291 // Saves current focus, displays/adjusts underlay for new dialog,
28292 // and sets the z-index of the dialog itself.
28293 //
28294 // New dialog will be displayed on top of all currently displayed dialogs.
28295 //
28296 // Caller is responsible for setting focus in new dialog after the fade-in
28297 // animation completes.
a089699c 28298
1354d172
AD
28299 // Save current focus
28300 ds[ds.length-1].focus = focus.curNode;
a089699c 28301
1354d172
AD
28302 // Display the underlay, or if already displayed then adjust for this new dialog
28303 var underlay = dijit._underlay;
28304 if(!underlay || underlay._destroyed){
28305 underlay = dijit._underlay = new DialogUnderlay(underlayAttrs);
28306 }else{
28307 underlay.set(dialog.underlayAttrs);
28308 }
a089699c 28309
1354d172
AD
28310 // Set z-index a bit above previous dialog
28311 var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
28312 if(ds.length == 1){ // first dialog
28313 underlay.show();
28314 }
28315 domStyle.set(dijit._underlay.domNode, 'zIndex', zIndex - 1);
a089699c 28316
1354d172
AD
28317 // Dialog
28318 domStyle.set(dialog.domNode, 'zIndex', zIndex);
a089699c 28319
1354d172
AD
28320 ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
28321 },
a089699c 28322
1354d172
AD
28323 hide: function(/*dijit._Widget*/ dialog){
28324 // summary:
28325 // Called when the specified dialog is hidden/destroyed, after the fade-out
28326 // animation ends, in order to reset page focus, fix the underlay, etc.
28327 // If the specified dialog isn't open then does nothing.
28328 //
28329 // Caller is responsible for either setting display:none on the dialog domNode,
28330 // or calling dijit.popup.hide(), or removing it from the page DOM.
a089699c 28331
1354d172
AD
28332 if(ds[ds.length-1].dialog == dialog){
28333 // Removing the top (or only) dialog in the stack, return focus
28334 // to previous dialog
a089699c 28335
1354d172 28336 ds.pop();
a089699c 28337
1354d172 28338 var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
a089699c 28339
1354d172
AD
28340 // Adjust underlay
28341 if(ds.length == 1){
28342 // Returning to original page.
28343 // Hide the underlay, unless the underlay widget has already been destroyed
28344 // because we are being called during page unload (when all widgets are destroyed)
28345 if(!dijit._underlay._destroyed){
28346 dijit._underlay.hide();
28347 }
28348 }else{
28349 // Popping back to previous dialog, adjust underlay
28350 domStyle.set(dijit._underlay.domNode, 'zIndex', pd.zIndex - 1);
28351 dijit._underlay.set(pd.underlayAttrs);
28352 }
a089699c 28353
1354d172
AD
28354 // Adjust focus
28355 if(dialog.refocus){
28356 // If we are returning control to a previous dialog but for some reason
28357 // that dialog didn't have a focused field, set focus to first focusable item.
28358 // This situation could happen if two dialogs appeared at nearly the same time,
28359 // since a dialog doesn't set it's focus until the fade-in is finished.
28360 var focus = pd.focus;
28361 if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
28362 pd.dialog._getFocusItems(pd.dialog.domNode);
28363 focus = pd.dialog._firstFocusItem;
28364 }
a089699c 28365
1354d172
AD
28366 if(focus){
28367 // Refocus the button that spawned the Dialog. This will fail in corner cases including
28368 // page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
28369 // before this code runs. (#15058)
28370 try{
28371 focus.focus();
28372 }catch(e){}
28373 }
28374 }
28375 }else{
28376 // Removing a dialog out of order (#9944, #10705).
28377 // Don't need to mess with underlay or z-index or anything.
28378 var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
28379 if(idx != -1){
28380 ds.splice(idx, 1);
28381 }
28382 }
28383 },
a089699c 28384
1354d172
AD
28385 isTop: function(/*dijit._Widget*/ dialog){
28386 // summary:
28387 // Returns true if specified Dialog is the top in the task
28388 return ds[ds.length-1].dialog == dialog;
a089699c 28389 }
1354d172 28390 };
a089699c 28391
1354d172
AD
28392 // Stack representing the various active "levels" on the page, starting with the
28393 // stuff initially visible on the page (at z-index 0), and then having an entry for
28394 // each Dialog shown.
28395 // Each element in stack has form {
28396 // dialog: dialogWidget,
28397 // focus: returnFromGetFocus(),
28398 // underlayAttrs: attributes to set on underlay (when this widget is active)
28399 // }
28400 var ds = Dialog._dialogStack = [
28401 {dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
28402 ];
28403
28404 // Back compat w/1.6, remove for 2.0
28405 if(!kernel.isAsync){
28406 ready(0, function(){
28407 var requires = ["dijit/TooltipDialog"];
28408 require(requires); // use indirection so modules not rolled into a build
28409 });
28410 }
a089699c 28411
1354d172
AD
28412 return Dialog;
28413});
a089699c 28414
1354d172
AD
28415},
28416'dijit/_base/focus':function(){
28417define("dijit/_base/focus", [
28418 "dojo/_base/array", // array.forEach
28419 "dojo/dom", // dom.isDescendant
28420 "dojo/_base/lang", // lang.isArray
28421 "dojo/topic", // publish
28422 "dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
28423 "../focus",
28424 ".." // for exporting symbols to dijit
28425], function(array, dom, lang, topic, win, focus, dijit){
28426
28427 // module:
28428 // dijit/_base/focus
28429 // summary:
28430 // Deprecated module to monitor currently focused node and stack of currently focused widgets.
28431 // New code should access dijit/focus directly.
a089699c 28432
1354d172
AD
28433 lang.mixin(dijit, {
28434 // _curFocus: DomNode
28435 // Currently focused item on screen
28436 _curFocus: null,
a089699c 28437
1354d172
AD
28438 // _prevFocus: DomNode
28439 // Previously focused item on screen
28440 _prevFocus: null,
a089699c 28441
1354d172
AD
28442 isCollapsed: function(){
28443 // summary:
28444 // Returns true if there is no text selected
28445 return dijit.getBookmark().isCollapsed;
28446 },
a089699c 28447
1354d172
AD
28448 getBookmark: function(){
28449 // summary:
28450 // Retrieves a bookmark that can be used with moveToBookmark to return to the same range
28451 var bm, rg, tg, sel = win.doc.selection, cf = focus.curNode;
28452
28453 if(win.global.getSelection){
28454 //W3C Range API for selections.
28455 sel = win.global.getSelection();
28456 if(sel){
28457 if(sel.isCollapsed){
28458 tg = cf? cf.tagName : "";
28459 if(tg){
28460 //Create a fake rangelike item to restore selections.
28461 tg = tg.toLowerCase();
28462 if(tg == "textarea" ||
28463 (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){
28464 sel = {
28465 start: cf.selectionStart,
28466 end: cf.selectionEnd,
28467 node: cf,
28468 pRange: true
28469 };
28470 return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object.
28471 }
28472 }
28473 bm = {isCollapsed:true};
28474 if(sel.rangeCount){
28475 bm.mark = sel.getRangeAt(0).cloneRange();
28476 }
28477 }else{
28478 rg = sel.getRangeAt(0);
28479 bm = {isCollapsed: false, mark: rg.cloneRange()};
28480 }
28481 }
28482 }else if(sel){
28483 // If the current focus was a input of some sort and no selection, don't bother saving
28484 // a native bookmark. This is because it causes issues with dialog/page selection restore.
28485 // So, we need to create psuedo bookmarks to work with.
28486 tg = cf ? cf.tagName : "";
28487 tg = tg.toLowerCase();
28488 if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){
28489 if(sel.type && sel.type.toLowerCase() == "none"){
28490 return {
28491 isCollapsed: true,
28492 mark: null
28493 }
28494 }else{
28495 rg = sel.createRange();
28496 return {
28497 isCollapsed: rg.text && rg.text.length?false:true,
28498 mark: {
28499 range: rg,
28500 pRange: true
28501 }
28502 };
28503 }
28504 }
28505 bm = {};
a089699c 28506
1354d172
AD
28507 //'IE' way for selections.
28508 try{
28509 // createRange() throws exception when dojo in iframe
28510 //and nothing selected, see #9632
28511 rg = sel.createRange();
28512 bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length);
28513 }catch(e){
28514 bm.isCollapsed = true;
28515 return bm;
28516 }
28517 if(sel.type.toUpperCase() == 'CONTROL'){
28518 if(rg.length){
28519 bm.mark=[];
28520 var i=0,len=rg.length;
28521 while(i<len){
28522 bm.mark.push(rg.item(i++));
28523 }
28524 }else{
28525 bm.isCollapsed = true;
28526 bm.mark = null;
28527 }
28528 }else{
28529 bm.mark = rg.getBookmark();
28530 }
28531 }else{
28532 console.warn("No idea how to store the current selection for this browser!");
28533 }
28534 return bm; // Object
28535 },
a089699c 28536
1354d172
AD
28537 moveToBookmark: function(/*Object*/ bookmark){
28538 // summary:
28539 // Moves current selection to a bookmark
28540 // bookmark:
28541 // This should be a returned object from dijit.getBookmark()
28542
28543 var _doc = win.doc,
28544 mark = bookmark.mark;
28545 if(mark){
28546 if(win.global.getSelection){
28547 //W3C Rangi API (FF, WebKit, Opera, etc)
28548 var sel = win.global.getSelection();
28549 if(sel && sel.removeAllRanges){
28550 if(mark.pRange){
28551 var n = mark.node;
28552 n.selectionStart = mark.start;
28553 n.selectionEnd = mark.end;
28554 }else{
28555 sel.removeAllRanges();
28556 sel.addRange(mark);
28557 }
28558 }else{
28559 console.warn("No idea how to restore selection for this browser!");
28560 }
28561 }else if(_doc.selection && mark){
28562 //'IE' way.
28563 var rg;
28564 if(mark.pRange){
28565 rg = mark.range;
28566 }else if(lang.isArray(mark)){
28567 rg = _doc.body.createControlRange();
28568 //rg.addElement does not have call/apply method, so can not call it directly
28569 //rg is not available in "range.addElement(item)", so can't use that either
28570 array.forEach(mark, function(n){
28571 rg.addElement(n);
28572 });
28573 }else{
28574 rg = _doc.body.createTextRange();
28575 rg.moveToBookmark(mark);
28576 }
28577 rg.select();
28578 }
28579 }
28580 },
a089699c 28581
1354d172
AD
28582 getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){
28583 // summary:
28584 // Called as getFocus(), this returns an Object showing the current focus
28585 // and selected text.
28586 //
28587 // Called as getFocus(widget), where widget is a (widget representing) a button
28588 // that was just pressed, it returns where focus was before that button
28589 // was pressed. (Pressing the button may have either shifted focus to the button,
28590 // or removed focus altogether.) In this case the selected text is not returned,
28591 // since it can't be accurately determined.
28592 //
28593 // menu: dijit._Widget or {domNode: DomNode} structure
28594 // The button that was just pressed. If focus has disappeared or moved
28595 // to this button, returns the previous focus. In this case the bookmark
28596 // information is already lost, and null is returned.
28597 //
28598 // openedForWindow:
28599 // iframe in which menu was opened
28600 //
28601 // returns:
28602 // A handle to restore focus/selection, to be passed to `dijit.focus`
28603 var node = !focus.curNode || (menu && dom.isDescendant(focus.curNode, menu.domNode)) ? dijit._prevFocus : focus.curNode;
28604 return {
28605 node: node,
28606 bookmark: node && (node == focus.curNode) && win.withGlobal(openedForWindow || win.global, dijit.getBookmark),
28607 openedForWindow: openedForWindow
28608 }; // Object
28609 },
a089699c 28610
1354d172
AD
28611 // _activeStack: dijit._Widget[]
28612 // List of currently active widgets (focused widget and it's ancestors)
28613 _activeStack: [],
a089699c 28614
1354d172
AD
28615 registerIframe: function(/*DomNode*/ iframe){
28616 // summary:
28617 // Registers listeners on the specified iframe so that any click
28618 // or focus event on that iframe (or anything in it) is reported
28619 // as a focus/click event on the <iframe> itself.
28620 // description:
28621 // Currently only used by editor.
28622 // returns:
28623 // Handle to pass to unregisterIframe()
28624 return focus.registerIframe(iframe);
28625 },
a089699c 28626
1354d172
AD
28627 unregisterIframe: function(/*Object*/ handle){
28628 // summary:
28629 // Unregisters listeners on the specified iframe created by registerIframe.
28630 // After calling be sure to delete or null out the handle itself.
28631 // handle:
28632 // Handle returned by registerIframe()
a089699c 28633
1354d172
AD
28634 handle && handle.remove();
28635 },
a089699c 28636
1354d172
AD
28637 registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){
28638 // summary:
28639 // Registers listeners on the specified window (either the main
28640 // window or an iframe's window) to detect when the user has clicked somewhere
28641 // or focused somewhere.
28642 // description:
28643 // Users should call registerIframe() instead of this method.
28644 // targetWindow:
28645 // If specified this is the window associated with the iframe,
28646 // i.e. iframe.contentWindow.
28647 // effectiveNode:
28648 // If specified, report any focus events inside targetWindow as
28649 // an event on effectiveNode, rather than on evt.target.
28650 // returns:
28651 // Handle to pass to unregisterWin()
28652
28653 return focus.registerWin(targetWindow, effectiveNode);
28654 },
28655
28656 unregisterWin: function(/*Handle*/ handle){
28657 // summary:
28658 // Unregisters listeners on the specified window (either the main
28659 // window or an iframe's window) according to handle returned from registerWin().
28660 // After calling be sure to delete or null out the handle itself.
28661
28662 handle && handle.remove();
a089699c 28663 }
1354d172 28664 });
a089699c 28665
1354d172
AD
28666 // Override focus singleton's focus function so that dijit.focus()
28667 // has backwards compatible behavior of restoring selection (although
28668 // probably no one is using that).
28669 focus.focus = function(/*Object || DomNode */ handle){
28670 // summary:
28671 // Sets the focused node and the selection according to argument.
28672 // To set focus to an iframe's content, pass in the iframe itself.
28673 // handle:
28674 // object returned by get(), or a DomNode
a089699c 28675
1354d172 28676 if(!handle){ return; }
a089699c 28677
1354d172
AD
28678 var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
28679 bookmark = handle.bookmark,
28680 openedForWindow = handle.openedForWindow,
28681 collapsed = bookmark ? bookmark.isCollapsed : false;
a089699c 28682
1354d172
AD
28683 // Set the focus
28684 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
28685 // but we need to set focus to iframe.contentWindow
28686 if(node){
28687 var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node;
28688 if(focusNode && focusNode.focus){
28689 try{
28690 // Gecko throws sometimes if setting focus is impossible,
28691 // node not displayed or something like that
28692 focusNode.focus();
28693 }catch(e){/*quiet*/}
28694 }
28695 focus._onFocusNode(node);
a089699c 28696 }
a089699c 28697
1354d172
AD
28698 // set the selection
28699 // do not need to restore if current selection is not empty
28700 // (use keyboard to select a menu item) or if previous selection was collapsed
28701 // as it may cause focus shift (Esp in IE).
28702 if(bookmark && win.withGlobal(openedForWindow || win.global, dijit.isCollapsed) && !collapsed){
28703 if(openedForWindow){
28704 openedForWindow.focus();
28705 }
28706 try{
28707 win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
28708 }catch(e2){
28709 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
28710 }
a089699c 28711 }
1354d172 28712 };
a089699c 28713
1354d172
AD
28714 // For back compatibility, monitor changes to focused node and active widget stack,
28715 // publishing events and copying changes from focus manager variables into dijit (top level) variables
28716 focus.watch("curNode", function(name, oldVal, newVal){
28717 dijit._curFocus = newVal;
28718 dijit._prevFocus = oldVal;
28719 if(newVal){
28720 topic.publish("focusNode", newVal); // publish
28721 }
28722 });
28723 focus.watch("activeStack", function(name, oldVal, newVal){
28724 dijit._activeStack = newVal;
28725 });
a089699c 28726
1354d172
AD
28727 focus.on("widget-blur", function(widget, by){
28728 topic.publish("widgetBlur", widget, by); // publish
28729 });
28730 focus.on("widget-focus", function(widget, by){
28731 topic.publish("widgetFocus", widget, by); // publish
28732 });
a089699c 28733
1354d172
AD
28734 return dijit;
28735});
a089699c 28736
1354d172
AD
28737},
28738'dijit/tree/dndSource':function(){
28739define("dijit/tree/dndSource", [
28740 "dojo/_base/array", // array.forEach array.indexOf array.map
28741 "dojo/_base/connect", // isCopyKey
28742 "dojo/_base/declare", // declare
28743 "dojo/dom-class", // domClass.add
28744 "dojo/dom-geometry", // domGeometry.position
28745 "dojo/_base/lang", // lang.mixin lang.hitch
28746 "dojo/on", // subscribe
28747 "dojo/touch",
28748 "dojo/topic",
28749 "dojo/dnd/Manager", // DNDManager.manager
28750 "./_dndSelector"
28751], function(array, connect, declare, domClass, domGeometry, lang, on, touch, topic, DNDManager, _dndSelector){
28752
28753// module:
28754// dijit/tree/dndSource
28755// summary:
28756// Handles drag and drop operations (as a source or a target) for `dijit.Tree`
a089699c 28757
1354d172
AD
28758/*=====
28759dijit.tree.__SourceArgs = function(){
28760 // summary:
28761 // A dict of parameters for Tree source configuration.
28762 // isSource: Boolean?
28763 // Can be used as a DnD source. Defaults to true.
28764 // accept: String[]
28765 // List of accepted types (text strings) for a target; defaults to
28766 // ["text", "treeNode"]
28767 // copyOnly: Boolean?
28768 // Copy items, if true, use a state of Ctrl key otherwise,
28769 // dragThreshold: Number
28770 // The move delay in pixels before detecting a drag; 0 by default
28771 // betweenThreshold: Integer
28772 // Distance from upper/lower edge of node to allow drop to reorder nodes
28773 this.isSource = isSource;
28774 this.accept = accept;
28775 this.autoSync = autoSync;
28776 this.copyOnly = copyOnly;
28777 this.dragThreshold = dragThreshold;
28778 this.betweenThreshold = betweenThreshold;
28779}
28780=====*/
a089699c 28781
1354d172
AD
28782return declare("dijit.tree.dndSource", _dndSelector, {
28783 // summary:
28784 // Handles drag and drop operations (as a source or a target) for `dijit.Tree`
28785
28786 // isSource: [private] Boolean
28787 // Can be used as a DnD source.
28788 isSource: true,
28789
28790 // accept: String[]
28791 // List of accepted types (text strings) for the Tree; defaults to
28792 // ["text"]
28793 accept: ["text", "treeNode"],
28794
28795 // copyOnly: [private] Boolean
28796 // Copy items, if true, use a state of Ctrl key otherwise
28797 copyOnly: false,
28798
28799 // dragThreshold: Number
28800 // The move delay in pixels before detecting a drag; 5 by default
28801 dragThreshold: 5,
28802
28803 // betweenThreshold: Integer
28804 // Distance from upper/lower edge of node to allow drop to reorder nodes
28805 betweenThreshold: 0,
28806
28807 constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){
28808 // summary:
28809 // a constructor of the Tree DnD Source
28810 // tags:
28811 // private
28812 if(!params){ params = {}; }
28813 lang.mixin(this, params);
28814 this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
28815 var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
28816 this.accept = null;
28817 if(type.length){
28818 this.accept = {};
28819 for(var i = 0; i < type.length; ++i){
28820 this.accept[type[i]] = 1;
a089699c
AD
28821 }
28822 }
1354d172
AD
28823
28824 // class-specific variables
28825 this.isDragging = false;
28826 this.mouseDown = false;
28827 this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode
28828 this.targetBox = null; // coordinates of this.targetAnchor
28829 this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
28830 this._lastX = 0;
28831 this._lastY = 0;
28832
28833 // states
28834 this.sourceState = "";
28835 if(this.isSource){
28836 domClass.add(this.node, "dojoDndSource");
28837 }
28838 this.targetState = "";
28839 if(this.accept){
28840 domClass.add(this.node, "dojoDndTarget");
28841 }
28842
28843 // set up events
28844 this.topics = [
28845 topic.subscribe("/dnd/source/over", lang.hitch(this, "onDndSourceOver")),
28846 topic.subscribe("/dnd/start", lang.hitch(this, "onDndStart")),
28847 topic.subscribe("/dnd/drop", lang.hitch(this, "onDndDrop")),
28848 topic.subscribe("/dnd/cancel", lang.hitch(this, "onDndCancel"))
28849 ];
a089699c
AD
28850 },
28851
1354d172
AD
28852 // methods
28853 checkAcceptance: function(/*===== source, nodes =====*/){
28854 // summary:
28855 // Checks if the target can accept nodes from this source
28856 // source: dijit.tree.dndSource
28857 // The source which provides items
28858 // nodes: DOMNode[]
28859 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
28860 // source is a dijit.Tree.
28861 // tags:
28862 // extension
28863 return true; // Boolean
28864 },
28865
28866 copyState: function(keyPressed){
28867 // summary:
28868 // Returns true, if we need to copy items, false to move.
28869 // It is separated to be overwritten dynamically, if needed.
28870 // keyPressed: Boolean
28871 // The "copy" control key was pressed
28872 // tags:
28873 // protected
28874 return this.copyOnly || keyPressed; // Boolean
28875 },
28876 destroy: function(){
28877 // summary:
28878 // Prepares the object to be garbage-collected.
28879 this.inherited(arguments);
28880 var h;
28881 while(h = this.topics.pop()){ h.remove(); }
28882 this.targetAnchor = null;
28883 },
28884
28885 _onDragMouse: function(e){
28886 // summary:
28887 // Helper method for processing onmousemove/onmouseover events while drag is in progress.
28888 // Keeps track of current drop target.
28889
28890 var m = DNDManager.manager(),
28891 oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over
28892 newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over
28893 oldDropPosition = this.dropPosition; // the previous drop position (over/before/after)
28894
28895 // calculate if user is indicating to drop the dragged node before, after, or over
28896 // (i.e., to become a child of) the target node
28897 var newDropPosition = "Over";
28898 if(newTarget && this.betweenThreshold > 0){
28899 // If mouse is over a new TreeNode, then get new TreeNode's position and size
28900 if(!this.targetBox || oldTarget != newTarget){
28901 this.targetBox = domGeometry.position(newTarget.rowNode, true);
28902 }
28903 if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
28904 newDropPosition = "Before";
28905 }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
28906 newDropPosition = "After";
28907 }
28908 }
28909
28910 if(newTarget != oldTarget || newDropPosition != oldDropPosition){
28911 if(oldTarget){
28912 this._removeItemClass(oldTarget.rowNode, oldDropPosition);
28913 }
28914 if(newTarget){
28915 this._addItemClass(newTarget.rowNode, newDropPosition);
28916 }
28917
28918 // Check if it's ok to drop the dragged node on/before/after the target node.
28919 if(!newTarget){
28920 m.canDrop(false);
28921 }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
28922 // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
28923 m.canDrop(false);
28924 }else{
28925 // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
28926 var model = this.tree.model,
28927 sameId = false;
28928 if(m.source == this){
28929 for(var dragId in this.selection){
28930 var dragNode = this.selection[dragId];
28931 if(dragNode.item === newTarget.item){
28932 sameId = true;
28933 break;
28934 }
28935 }
28936 }
28937 if(sameId){
28938 m.canDrop(false);
28939 }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
28940 && !this._isParentChildDrop(m.source, newTarget.rowNode)){
28941 m.canDrop(true);
a089699c 28942 }else{
1354d172 28943 m.canDrop(false);
a089699c
AD
28944 }
28945 }
1354d172
AD
28946
28947 this.targetAnchor = newTarget;
28948 this.dropPosition = newDropPosition;
a089699c
AD
28949 }
28950 },
28951
1354d172
AD
28952 onMouseMove: function(e){
28953 // summary:
28954 // Called for any onmousemove/ontouchmove events over the Tree
28955 // e: Event
28956 // onmousemouse/ontouchmove event
28957 // tags:
28958 // private
28959 if(this.isDragging && this.targetState == "Disabled"){ return; }
28960 this.inherited(arguments);
28961 var m = DNDManager.manager();
28962 if(this.isDragging){
28963 this._onDragMouse(e);
28964 }else{
28965 if(this.mouseDown && this.isSource &&
28966 (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
28967 var nodes = this.getSelectedTreeNodes();
28968 if(nodes.length){
28969 if(nodes.length > 1){
28970 //filter out all selected items which has one of their ancestor selected as well
28971 var seen = this.selection, i = 0, r = [], n, p;
28972 nextitem: while((n = nodes[i++])){
28973 for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
28974 if(seen[p.id]){ //parent is already selected, skip this node
28975 continue nextitem;
28976 }
28977 }
28978 //this node does not have any ancestors selected, add it
28979 r.push(n);
28980 }
28981 nodes = r;
28982 }
28983 nodes = array.map(nodes, function(n){return n.domNode});
28984 m.startDrag(this, nodes, this.copyState(connect.isCopyKey(e)));
28985 }
28986 }
a089699c 28987 }
a089699c
AD
28988 },
28989
1354d172
AD
28990 onMouseDown: function(e){
28991 // summary:
28992 // Event processor for onmousedown/ontouchstart
28993 // e: Event
28994 // onmousedown/ontouchend event
28995 // tags:
28996 // private
28997 this.mouseDown = true;
28998 this.mouseButton = e.button;
28999 this._lastX = e.pageX;
29000 this._lastY = e.pageY;
29001 this.inherited(arguments);
29002 },
29003
29004 onMouseUp: function(e){
29005 // summary:
29006 // Event processor for onmouseup/ontouchend
29007 // e: Event
29008 // onmouseup/ontouchend event
29009 // tags:
29010 // private
29011 if(this.mouseDown){
29012 this.mouseDown = false;
29013 this.inherited(arguments);
29014 }
29015 },
29016
29017 onMouseOut: function(){
29018 // summary:
29019 // Event processor for when mouse is moved away from a TreeNode
29020 // tags:
29021 // private
29022 this.inherited(arguments);
29023 this._unmarkTargetAnchor();
29024 },
29025
29026 checkItemAcceptance: function(/*===== target, source, position =====*/){
29027 // summary:
29028 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
29029 // description:
29030 // In the base case, this is called to check if target can become a child of source.
29031 // When betweenThreshold is set, position="before" or "after" means that we
29032 // are asking if the source node can be dropped before/after the target node.
29033 // target: DOMNode
29034 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
29035 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
29036 // source: dijit.tree.dndSource
29037 // The (set of) nodes we are dropping
29038 // position: String
29039 // "over", "before", or "after"
29040 // tags:
29041 // extension
29042 return true;
29043 },
29044
29045 // topic event processors
29046 onDndSourceOver: function(source){
29047 // summary:
29048 // Topic event processor for /dnd/source/over, called when detected a current source.
29049 // source: Object
29050 // The dijit.tree.dndSource / dojo.dnd.Source which has the mouse over it
29051 // tags:
29052 // private
29053 if(this != source){
29054 this.mouseDown = false;
29055 this._unmarkTargetAnchor();
29056 }else if(this.isDragging){
29057 var m = DNDManager.manager();
29058 m.canDrop(false);
29059 }
29060 },
29061 onDndStart: function(source, nodes, copy){
29062 // summary:
29063 // Topic event processor for /dnd/start, called to initiate the DnD operation
29064 // source: Object
29065 // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
29066 // nodes: DomNode[]
29067 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
29068 // copy: Boolean
29069 // Copy items, if true, move items otherwise
29070 // tags:
29071 // private
29072
29073 if(this.isSource){
29074 this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
29075 }
29076 var accepted = this.checkAcceptance(source, nodes);
29077
29078 this._changeState("Target", accepted ? "" : "Disabled");
29079
29080 if(this == source){
29081 DNDManager.manager().overSource(this);
29082 }
29083
29084 this.isDragging = true;
29085 },
29086
29087 itemCreator: function(nodes /*===== , target, source =====*/){
29088 // summary:
29089 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
29090 // dropped onto the tree. Developer must override this method to enable
29091 // dropping from external sources onto this Tree, unless the Tree.model's items
29092 // happen to look like {id: 123, name: "Apple" } with no other attributes.
29093 // description:
29094 // For each node in nodes[], which came from source, create a hash of name/value
29095 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
29096 // nodes: DomNode[]
29097 // target: DomNode
29098 // source: dojo.dnd.Source
29099 // returns: Object[]
29100 // Array of name/value hashes for each new item to be added to the Tree, like:
29101 // | [
29102 // | { id: 123, label: "apple", foo: "bar" },
29103 // | { id: 456, label: "pear", zaz: "bam" }
29104 // | ]
29105 // tags:
29106 // extension
29107
29108 // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
29109 // make signature itemCreator(sourceItem, node, target) (or similar).
29110
29111 return array.map(nodes, function(node){
29112 return {
29113 "id": node.id,
29114 "name": node.textContent || node.innerText || ""
29115 };
29116 }); // Object[]
29117 },
29118
29119 onDndDrop: function(source, nodes, copy){
29120 // summary:
29121 // Topic event processor for /dnd/drop, called to finish the DnD operation.
29122 // description:
29123 // Updates data store items according to where node was dragged from and dropped
29124 // to. The tree will then respond to those data store updates and redraw itself.
29125 // source: Object
29126 // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items
29127 // nodes: DomNode[]
29128 // The list of transferred items, dndTreeNode nodes if dragging from a Tree
29129 // copy: Boolean
29130 // Copy items, if true, move items otherwise
29131 // tags:
29132 // protected
29133 if(this.containerState == "Over"){
29134 var tree = this.tree,
29135 model = tree.model,
29136 target = this.targetAnchor;
29137
29138 this.isDragging = false;
29139
29140 // Compute the new parent item
29141 var newParentItem;
29142 var insertIndex;
29143 newParentItem = (target && target.item) || tree.item;
29144 if(this.dropPosition == "Before" || this.dropPosition == "After"){
29145 // TODO: if there is no parent item then disallow the drop.
29146 // Actually this should be checked during onMouseMove too, to make the drag icon red.
29147 newParentItem = (target.getParent() && target.getParent().item) || tree.item;
29148 // Compute the insert index for reordering
29149 insertIndex = target.getIndexInParent();
29150 if(this.dropPosition == "After"){
29151 insertIndex = target.getIndexInParent() + 1;
29152 }
29153 }else{
29154 newParentItem = (target && target.item) || tree.item;
29155 }
29156
29157 // If necessary, use this variable to hold array of hashes to pass to model.newItem()
29158 // (one entry in the array for each dragged node).
29159 var newItemsParams;
29160
29161 array.forEach(nodes, function(node, idx){
29162 // dojo.dnd.Item representing the thing being dropped.
29163 // Don't confuse the use of item here (meaning a DnD item) with the
29164 // uses below where item means dojo.data item.
29165 var sourceItem = source.getItem(node.id);
29166
29167 // Information that's available if the source is another Tree
29168 // (possibly but not necessarily this tree, possibly but not
29169 // necessarily the same model as this Tree)
29170 if(array.indexOf(sourceItem.type, "treeNode") != -1){
29171 var childTreeNode = sourceItem.data,
29172 childItem = childTreeNode.item,
29173 oldParentItem = childTreeNode.getParent().item;
29174 }
29175
29176 if(source == this){
29177 // This is a node from my own tree, and we are moving it, not copying.
29178 // Remove item from old parent's children attribute.
29179 // TODO: dijit.tree.dndSelector should implement deleteSelectedNodes()
29180 // and this code should go there.
29181
29182 if(typeof insertIndex == "number"){
29183 if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
29184 insertIndex -= 1;
29185 }
29186 }
29187 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
29188 }else if(model.isItem(childItem)){
29189 // Item from same model
29190 // (maybe we should only do this branch if the source is a tree?)
29191 model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
29192 }else{
29193 // Get the hash to pass to model.newItem(). A single call to
29194 // itemCreator() returns an array of hashes, one for each drag source node.
29195 if(!newItemsParams){
29196 newItemsParams = this.itemCreator(nodes, target.rowNode, source);
29197 }
29198
29199 // Create new item in the tree, based on the drag source.
29200 model.newItem(newItemsParams[idx], newParentItem, insertIndex);
29201 }
29202 }, this);
29203
29204 // Expand the target node (if it's currently collapsed) so the user can see
29205 // where their node was dropped. In particular since that node is still selected.
29206 this.tree._expandNode(target);
29207 }
29208 this.onDndCancel();
29209 },
29210
29211 onDndCancel: function(){
29212 // summary:
29213 // Topic event processor for /dnd/cancel, called to cancel the DnD operation
29214 // tags:
29215 // private
29216 this._unmarkTargetAnchor();
29217 this.isDragging = false;
29218 this.mouseDown = false;
29219 delete this.mouseButton;
29220 this._changeState("Source", "");
29221 this._changeState("Target", "");
29222 },
29223
29224 // When focus moves in/out of the entire Tree
29225 onOverEvent: function(){
29226 // summary:
29227 // This method is called when mouse is moved over our container (like onmouseenter)
29228 // tags:
29229 // private
29230 this.inherited(arguments);
29231 DNDManager.manager().overSource(this);
29232 },
29233 onOutEvent: function(){
29234 // summary:
29235 // This method is called when mouse is moved out of our container (like onmouseleave)
29236 // tags:
29237 // private
29238 this._unmarkTargetAnchor();
29239 var m = DNDManager.manager();
29240 if(this.isDragging){
29241 m.canDrop(false);
29242 }
29243 m.outSource(this);
29244
29245 this.inherited(arguments);
29246 },
29247
29248 _isParentChildDrop: function(source, targetRow){
29249 // summary:
29250 // Checks whether the dragged items are parent rows in the tree which are being
29251 // dragged into their own children.
29252 //
29253 // source:
29254 // The DragSource object.
29255 //
29256 // targetRow:
29257 // The tree row onto which the dragged nodes are being dropped.
29258 //
29259 // tags:
29260 // private
29261
29262 // If the dragged object is not coming from the tree this widget belongs to,
29263 // it cannot be invalid.
29264 if(!source.tree || source.tree != this.tree){
29265 return false;
29266 }
29267
29268
29269 var root = source.tree.domNode;
29270 var ids = source.selection;
29271
29272 var node = targetRow.parentNode;
29273
29274 // Iterate up the DOM hierarchy from the target drop row,
29275 // checking of any of the dragged nodes have the same ID.
29276 while(node != root && !ids[node.id]){
29277 node = node.parentNode;
29278 }
29279
29280 return node.id && ids[node.id];
29281 },
29282
29283 _unmarkTargetAnchor: function(){
29284 // summary:
29285 // Removes hover class of the current target anchor
29286 // tags:
29287 // private
29288 if(!this.targetAnchor){ return; }
29289 this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
29290 this.targetAnchor = null;
29291 this.targetBox = null;
29292 this.dropPosition = null;
29293 },
29294
29295 _markDndStatus: function(copy){
29296 // summary:
29297 // Changes source's state based on "copy" status
29298 this._changeState("Source", copy ? "Copied" : "Moved");
29299 }
29300});
29301
29302});
29303
29304},
29305'dijit/a11y':function(){
29306define("dijit/a11y", [
29307 "dojo/_base/array", // array.forEach array.map
29308 "dojo/_base/config", // defaultDuration
29309 "dojo/_base/declare", // declare
29310 "dojo/dom", // dom.byId
29311 "dojo/dom-attr", // domAttr.attr domAttr.has
29312 "dojo/dom-style", // style.style
29313 "dojo/_base/sniff", // has("ie")
29314 "./_base/manager", // manager._isElementShown
29315 "." // for exporting methods to dijit namespace
29316], function(array, config, declare, dom, domAttr, domStyle, has, manager, dijit){
29317
29318 // module:
29319 // dijit/a11y
29320 // summary:
29321 // Accessibility utility functions (keyboard, tab stops, etc.)
29322
29323 var shown = (dijit._isElementShown = function(/*Element*/ elem){
29324 var s = domStyle.get(elem);
29325 return (s.visibility != "hidden")
29326 && (s.visibility != "collapsed")
29327 && (s.display != "none")
29328 && (domAttr.get(elem, "type") != "hidden");
29329 });
29330
29331 dijit.hasDefaultTabStop = function(/*Element*/ elem){
29332 // summary:
29333 // Tests if element is tab-navigable even without an explicit tabIndex setting
29334
29335 // No explicit tabIndex setting, need to investigate node type
29336 switch(elem.nodeName.toLowerCase()){
29337 case "a":
29338 // An <a> w/out a tabindex is only navigable if it has an href
29339 return domAttr.has(elem, "href");
29340 case "area":
29341 case "button":
29342 case "input":
29343 case "object":
29344 case "select":
29345 case "textarea":
29346 // These are navigable by default
29347 return true;
29348 case "iframe":
29349 // If it's an editor <iframe> then it's tab navigable.
29350 var body;
29351 try{
29352 // non-IE
29353 var contentDocument = elem.contentDocument;
29354 if("designMode" in contentDocument && contentDocument.designMode == "on"){
29355 return true;
29356 }
29357 body = contentDocument.body;
29358 }catch(e1){
29359 // contentWindow.document isn't accessible within IE7/8
29360 // if the iframe.src points to a foreign url and this
29361 // page contains an element, that could get focus
29362 try{
29363 body = elem.contentWindow.document.body;
29364 }catch(e2){
29365 return false;
29366 }
29367 }
29368 return body && (body.contentEditable == 'true' ||
29369 (body.firstChild && body.firstChild.contentEditable == 'true'));
29370 default:
29371 return elem.contentEditable == 'true';
29372 }
29373 };
29374
29375 var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){
29376 // summary:
29377 // Tests if an element is tab-navigable
29378
29379 // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable()
29380 if(domAttr.get(elem, "disabled")){
29381 return false;
29382 }else if(domAttr.has(elem, "tabIndex")){
29383 // Explicit tab index setting
29384 return domAttr.get(elem, "tabIndex") >= 0; // boolean
29385 }else{
29386 // No explicit tabIndex setting, so depends on node type
29387 return dijit.hasDefaultTabStop(elem);
29388 }
29389 });
29390
29391 dijit._getTabNavigable = function(/*DOMNode*/ root){
29392 // summary:
29393 // Finds descendants of the specified root node.
29394 //
29395 // description:
29396 // Finds the following descendants of the specified root node:
29397 // * the first tab-navigable element in document order
29398 // without a tabIndex or with tabIndex="0"
29399 // * the last tab-navigable element in document order
29400 // without a tabIndex or with tabIndex="0"
29401 // * the first element in document order with the lowest
29402 // positive tabIndex value
29403 // * the last element in document order with the highest
29404 // positive tabIndex value
29405 var first, last, lowest, lowestTabindex, highest, highestTabindex, radioSelected = {};
29406
29407 function radioName(node){
29408 // If this element is part of a radio button group, return the name for that group.
29409 return node && node.tagName.toLowerCase() == "input" &&
29410 node.type && node.type.toLowerCase() == "radio" &&
29411 node.name && node.name.toLowerCase();
29412 }
29413
29414 var walkTree = function(/*DOMNode*/parent){
29415 for(var child = parent.firstChild; child; child = child.nextSibling){
29416 // Skip text elements, hidden elements, and also non-HTML elements (those in custom namespaces) in IE,
29417 // since show() invokes getAttribute("type"), which crash on VML nodes in IE.
29418 if(child.nodeType != 1 || (has("ie") && child.scopeName !== "HTML") || !shown(child)){
29419 continue;
29420 }
29421
29422 if(isTabNavigable(child)){
29423 var tabindex = domAttr.get(child, "tabIndex");
29424 if(!domAttr.has(child, "tabIndex") || tabindex == 0){
29425 if(!first){
29426 first = child;
29427 }
29428 last = child;
29429 }else if(tabindex > 0){
29430 if(!lowest || tabindex < lowestTabindex){
29431 lowestTabindex = tabindex;
29432 lowest = child;
29433 }
29434 if(!highest || tabindex >= highestTabindex){
29435 highestTabindex = tabindex;
29436 highest = child;
29437 }
29438 }
29439 var rn = radioName(child);
29440 if(domAttr.get(child, "checked") && rn){
29441 radioSelected[rn] = child;
29442 }
29443 }
29444 if(child.nodeName.toUpperCase() != 'SELECT'){
29445 walkTree(child);
29446 }
29447 }
29448 };
29449 if(shown(root)){
29450 walkTree(root);
29451 }
29452 function rs(node){
29453 // substitute checked radio button for unchecked one, if there is a checked one with the same name.
29454 return radioSelected[radioName(node)] || node;
29455 }
29456
29457 return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
29458 };
29459 dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){
29460 // summary:
29461 // Finds the descendant of the specified root node
29462 // that is first in the tabbing order
29463 var elems = dijit._getTabNavigable(dom.byId(root));
29464 return elems.lowest ? elems.lowest : elems.first; // DomNode
29465 };
29466
29467 dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){
29468 // summary:
29469 // Finds the descendant of the specified root node
29470 // that is last in the tabbing order
29471 var elems = dijit._getTabNavigable(dom.byId(root));
29472 return elems.last ? elems.last : elems.highest; // DomNode
29473 };
29474
29475 return {
29476 hasDefaultTabStop: dijit.hasDefaultTabStop,
29477 isTabNavigable: dijit.isTabNavigable,
29478 _getTabNavigable: dijit._getTabNavigable,
29479 getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
29480 getLastInTabbingOrder: dijit.getLastInTabbingOrder
29481 };
29482});
29483
29484},
29485'dijit/form/_ToggleButtonMixin':function(){
29486define("dijit/form/_ToggleButtonMixin", [
29487 "dojo/_base/declare", // declare
29488 "dojo/dom-attr" // domAttr.set
29489], function(declare, domAttr){
29490
29491// module:
29492// dijit/form/_ToggleButtonMixin
29493// summary:
29494// A mixin to provide functionality to allow a button that can be in two states (checked or not).
29495
29496return declare("dijit.form._ToggleButtonMixin", null, {
29497 // summary:
29498 // A mixin to provide functionality to allow a button that can be in two states (checked or not).
29499
29500 // checked: Boolean
29501 // Corresponds to the native HTML <input> element's attribute.
29502 // In markup, specified as "checked='checked'" or just "checked".
29503 // True if the button is depressed, or the checkbox is checked,
29504 // or the radio button is selected, etc.
29505 checked: false,
29506
29507 // aria-pressed for toggle buttons, and aria-checked for checkboxes
29508 _aria_attr: "aria-pressed",
29509
29510 _onClick: function(/*Event*/ evt){
29511 var original = this.checked;
29512 this._set('checked', !original); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
29513 var ret = this.inherited(arguments); // the user could reset the value here
29514 this.set('checked', ret ? this.checked : original); // officially set the toggled or user value, or reset it back
29515 return ret;
29516 },
29517
29518 _setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
29519 this._set("checked", value);
29520 domAttr.set(this.focusNode || this.domNode, "checked", value);
29521 (this.focusNode || this.domNode).setAttribute(this._aria_attr, value ? "true" : "false"); // aria values should be strings
29522 this._handleOnChange(value, priorityChange);
29523 },
29524
29525 reset: function(){
29526 // summary:
29527 // Reset the widget's value to what it was at initialization time
29528
29529 this._hasBeenBlurred = false;
29530
29531 // set checked state to original setting
29532 this.set('checked', this.params.checked || false);
29533 }
29534});
29535
29536});
29537
29538},
29539'dijit/_Widget':function(){
29540define("dijit/_Widget", [
29541 "dojo/aspect", // aspect.around
29542 "dojo/_base/config", // config.isDebug
29543 "dojo/_base/connect", // connect.connect
29544 "dojo/_base/declare", // declare
29545 "dojo/_base/kernel", // kernel.deprecated
29546 "dojo/_base/lang", // lang.hitch
29547 "dojo/query",
29548 "dojo/ready",
29549 "./registry", // registry.byNode
29550 "./_WidgetBase",
29551 "./_OnDijitClickMixin",
29552 "./_FocusMixin",
29553 "dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
29554 "./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
29555], function(aspect, config, connect, declare, kernel, lang, query, ready,
29556 registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
29557
29558/*=====
29559 var _WidgetBase = dijit._WidgetBase;
29560 var _OnDijitClickMixin = dijit._OnDijitClickMixin;
29561 var _FocusMixin = dijit._FocusMixin;
29562=====*/
29563
29564
29565// module:
29566// dijit/_Widget
29567// summary:
29568// Old base for widgets. New widgets should extend _WidgetBase instead
29569
29570
29571function connectToDomNode(){
29572 // summary:
29573 // If user connects to a widget method === this function, then they will
29574 // instead actually be connecting the equivalent event on this.domNode
29575}
29576
29577// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
29578function aroundAdvice(originalConnect){
29579 return function(obj, event, scope, method){
29580 if(obj && typeof event == "string" && obj[event] == connectToDomNode){
29581 return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
29582 }
29583 return originalConnect.apply(connect, arguments);
29584 };
29585}
29586aspect.around(connect, "connect", aroundAdvice);
29587if(kernel.connect){
29588 aspect.around(kernel, "connect", aroundAdvice);
29589}
29590
29591var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
29592 // summary:
29593 // Base class for all Dijit widgets.
29594 //
29595 // Extends _WidgetBase, adding support for:
29596 // - declaratively/programatically specifying widget initialization parameters like
29597 // onMouseMove="foo" that call foo when this.domNode gets a mousemove event
29598 // - ondijitclick
29599 // Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
29600 // - focus related functions
29601 // In particular, the onFocus()/onBlur() callbacks. Driven internally by
29602 // dijit/_base/focus.js.
29603 // - deprecated methods
29604 // - onShow(), onHide(), onClose()
29605 //
29606 // Also, by loading code in dijit/_base, turns on:
29607 // - browser sniffing (putting browser id like .dj_ie on <html> node)
29608 // - high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)
29609
29610
29611 ////////////////// DEFERRED CONNECTS ///////////////////
29612
29613 onClick: connectToDomNode,
29614 /*=====
29615 onClick: function(event){
29616 // summary:
29617 // Connect to this function to receive notifications of mouse click events.
29618 // event:
29619 // mouse Event
29620 // tags:
29621 // callback
29622 },
29623 =====*/
29624 onDblClick: connectToDomNode,
29625 /*=====
29626 onDblClick: function(event){
29627 // summary:
29628 // Connect to this function to receive notifications of mouse double click events.
29629 // event:
29630 // mouse Event
29631 // tags:
29632 // callback
29633 },
29634 =====*/
29635 onKeyDown: connectToDomNode,
29636 /*=====
29637 onKeyDown: function(event){
29638 // summary:
29639 // Connect to this function to receive notifications of keys being pressed down.
29640 // event:
29641 // key Event
29642 // tags:
29643 // callback
29644 },
29645 =====*/
29646 onKeyPress: connectToDomNode,
29647 /*=====
29648 onKeyPress: function(event){
29649 // summary:
29650 // Connect to this function to receive notifications of printable keys being typed.
29651 // event:
29652 // key Event
29653 // tags:
29654 // callback
29655 },
29656 =====*/
29657 onKeyUp: connectToDomNode,
29658 /*=====
29659 onKeyUp: function(event){
29660 // summary:
29661 // Connect to this function to receive notifications of keys being released.
29662 // event:
29663 // key Event
29664 // tags:
29665 // callback
29666 },
29667 =====*/
29668 onMouseDown: connectToDomNode,
29669 /*=====
29670 onMouseDown: function(event){
29671 // summary:
29672 // Connect to this function to receive notifications of when the mouse button is pressed down.
29673 // event:
29674 // mouse Event
29675 // tags:
29676 // callback
29677 },
29678 =====*/
29679 onMouseMove: connectToDomNode,
29680 /*=====
29681 onMouseMove: function(event){
29682 // summary:
29683 // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
29684 // event:
29685 // mouse Event
29686 // tags:
29687 // callback
29688 },
29689 =====*/
29690 onMouseOut: connectToDomNode,
29691 /*=====
29692 onMouseOut: function(event){
29693 // summary:
29694 // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
29695 // event:
29696 // mouse Event
29697 // tags:
29698 // callback
29699 },
29700 =====*/
29701 onMouseOver: connectToDomNode,
29702 /*=====
29703 onMouseOver: function(event){
29704 // summary:
29705 // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
29706 // event:
29707 // mouse Event
29708 // tags:
29709 // callback
29710 },
29711 =====*/
29712 onMouseLeave: connectToDomNode,
29713 /*=====
29714 onMouseLeave: function(event){
29715 // summary:
29716 // Connect to this function to receive notifications of when the mouse moves off of this widget.
29717 // event:
29718 // mouse Event
29719 // tags:
29720 // callback
29721 },
29722 =====*/
29723 onMouseEnter: connectToDomNode,
29724 /*=====
29725 onMouseEnter: function(event){
29726 // summary:
29727 // Connect to this function to receive notifications of when the mouse moves onto this widget.
29728 // event:
29729 // mouse Event
29730 // tags:
29731 // callback
29732 },
29733 =====*/
29734 onMouseUp: connectToDomNode,
29735 /*=====
29736 onMouseUp: function(event){
29737 // summary:
29738 // Connect to this function to receive notifications of when the mouse button is released.
29739 // event:
29740 // mouse Event
29741 // tags:
29742 // callback
29743 },
29744 =====*/
29745
29746 constructor: function(params){
29747 // extract parameters like onMouseMove that should connect directly to this.domNode
29748 this._toConnect = {};
29749 for(var name in params){
29750 if(this[name] === connectToDomNode){
29751 this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
29752 delete params[name];
29753 }
29754 }
29755 },
29756
29757 postCreate: function(){
29758 this.inherited(arguments);
29759
29760 // perform connection from this.domNode to user specified handlers (ex: onMouseMove)
29761 for(var name in this._toConnect){
29762 this.on(name, this._toConnect[name]);
29763 }
29764 delete this._toConnect;
29765 },
29766
29767 on: function(/*String*/ type, /*Function*/ func){
29768 if(this[this._onMap(type)] === connectToDomNode){
29769 // Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
29770 // Also, need to specify context as "this" rather than the default context of the DOMNode
29771 return connect.connect(this.domNode, type.toLowerCase(), this, func);
29772 }
29773 return this.inherited(arguments);
29774 },
29775
29776 _setFocusedAttr: function(val){
29777 // Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
29778 // (but since it's a private variable we aren't required to keep supporting it).
29779 this._focused = val;
29780 this._set("focused", val);
29781 },
29782
29783 ////////////////// DEPRECATED METHODS ///////////////////
29784
29785 setAttribute: function(/*String*/ attr, /*anything*/ value){
29786 // summary:
29787 // Deprecated. Use set() instead.
29788 // tags:
29789 // deprecated
29790 kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
29791 this.set(attr, value);
29792 },
29793
29794 attr: function(/*String|Object*/name, /*Object?*/value){
29795 // summary:
29796 // Set or get properties on a widget instance.
29797 // name:
29798 // The property to get or set. If an object is passed here and not
29799 // a string, its keys are used as names of attributes to be set
29800 // and the value of the object as values to set in the widget.
29801 // value:
29802 // Optional. If provided, attr() operates as a setter. If omitted,
29803 // the current value of the named property is returned.
29804 // description:
29805 // This method is deprecated, use get() or set() directly.
29806
29807 // Print deprecation warning but only once per calling function
29808 if(config.isDebug){
29809 var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
29810 caller = (arguments.callee.caller || "unknown caller").toString();
29811 if(!alreadyCalledHash[caller]){
29812 kernel.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
29813 caller, "", "2.0");
29814 alreadyCalledHash[caller] = true;
29815 }
29816 }
29817
29818 var args = arguments.length;
29819 if(args >= 2 || typeof name === "object"){ // setter
29820 return this.set.apply(this, arguments);
29821 }else{ // getter
29822 return this.get(name);
29823 }
29824 },
29825
29826 getDescendants: function(){
29827 // summary:
29828 // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode.
29829 // This method should generally be avoided as it returns widgets declared in templates, which are
29830 // supposed to be internal/hidden, but it's left here for back-compat reasons.
29831
29832 kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
29833 return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit._Widget[]
29834 },
29835
29836 ////////////////// MISCELLANEOUS METHODS ///////////////////
29837
29838 _onShow: function(){
29839 // summary:
29840 // Internal method called when this widget is made visible.
29841 // See `onShow` for details.
29842 this.onShow();
29843 },
29844
29845 onShow: function(){
29846 // summary:
29847 // Called when this widget becomes the selected pane in a
29848 // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
29849 // `dijit.layout.AccordionContainer`, etc.
29850 //
29851 // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
29852 // tags:
29853 // callback
29854 },
29855
29856 onHide: function(){
29857 // summary:
29858 // Called when another widget becomes the selected pane in a
29859 // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
29860 // `dijit.layout.AccordionContainer`, etc.
29861 //
29862 // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
29863 // tags:
29864 // callback
29865 },
29866
29867 onClose: function(){
29868 // summary:
29869 // Called when this widget is being displayed as a popup (ex: a Calendar popped
29870 // up from a DateTextBox), and it is hidden.
29871 // This is called from the dijit.popup code, and should not be called directly.
29872 //
29873 // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
29874 // Callback if a user tries to close the child. Child will be closed if this function returns true.
29875 // tags:
29876 // extension
29877
29878 return true; // Boolean
29879 }
29880});
29881
29882// For back-compat, remove in 2.0.
29883if(!kernel.isAsync){
29884 ready(0, function(){
29885 var requires = ["dijit/_base"];
29886 require(requires); // use indirection so modules not rolled into a build
29887 });
29888}
29889return _Widget;
29890});
29891
29892},
29893'dojo/touch':function(){
29894define("dojo/touch", ["./_base/kernel", "./on", "./has", "./mouse"], function(dojo, on, has, mouse){
29895// module:
29896// dojo/touch
29897
29898/*=====
29899 dojo.touch = {
29900 // summary:
29901 // This module provides unified touch event handlers by exporting
29902 // press, move, release and cancel which can also run well on desktop.
29903 // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
29904 //
29905 // example:
29906 // 1. Used with dojo.connect()
29907 // | dojo.connect(node, dojo.touch.press, function(e){});
29908 // | dojo.connect(node, dojo.touch.move, function(e){});
29909 // | dojo.connect(node, dojo.touch.release, function(e){});
29910 // | dojo.connect(node, dojo.touch.cancel, function(e){});
29911 //
29912 // 2. Used with dojo.on
29913 // | define(["dojo/on", "dojo/touch"], function(on, touch){
29914 // | on(node, touch.press, function(e){});
29915 // | on(node, touch.move, function(e){});
29916 // | on(node, touch.release, function(e){});
29917 // | on(node, touch.cancel, function(e){});
29918 //
29919 // 3. Used with dojo.touch.* directly
29920 // | dojo.touch.press(node, function(e){});
29921 // | dojo.touch.move(node, function(e){});
29922 // | dojo.touch.release(node, function(e){});
29923 // | dojo.touch.cancel(node, function(e){});
29924
29925 press: function(node, listener){
29926 // summary:
29927 // Register a listener to 'touchstart'|'mousedown' for the given node
29928 // node: Dom
29929 // Target node to listen to
29930 // listener: Function
29931 // Callback function
29932 // returns:
29933 // A handle which will be used to remove the listener by handle.remove()
29934 },
29935 move: function(node, listener){
29936 // summary:
29937 // Register a listener to 'touchmove'|'mousemove' for the given node
29938 // node: Dom
29939 // Target node to listen to
29940 // listener: Function
29941 // Callback function
29942 // returns:
29943 // A handle which will be used to remove the listener by handle.remove()
29944 },
29945 release: function(node, listener){
29946 // summary:
29947 // Register a listener to 'touchend'|'mouseup' for the given node
29948 // node: Dom
29949 // Target node to listen to
29950 // listener: Function
29951 // Callback function
29952 // returns:
29953 // A handle which will be used to remove the listener by handle.remove()
29954 },
29955 cancel: function(node, listener){
29956 // summary:
29957 // Register a listener to 'touchcancel'|'mouseleave' for the given node
29958 // node: Dom
29959 // Target node to listen to
29960 // listener: Function
29961 // Callback function
29962 // returns:
29963 // A handle which will be used to remove the listener by handle.remove()
29964 }
29965 };
29966=====*/
29967
29968 function _handle(/*String - press | move | release | cancel*/type){
29969 return function(node, listener){//called by on(), see dojo.on
29970 return on(node, type, listener);
29971 };
29972 }
29973 var touch = has("touch");
29974 //device neutral events - dojo.touch.press|move|release|cancel
29975 dojo.touch = {
29976 press: _handle(touch ? "touchstart": "mousedown"),
29977 move: _handle(touch ? "touchmove": "mousemove"),
29978 release: _handle(touch ? "touchend": "mouseup"),
29979 cancel: touch ? _handle("touchcancel") : mouse.leave
29980 };
29981 return dojo.touch;
29982});
29983},
29984'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=\"combobox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents dijitButtonNode\" role=\"presentation\"\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\"></span\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton\"\n\t\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitArrowButtonInner\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitArrowButtonChar\" role=\"presentation\">&#9660;</div\n\t\t></td\n\t></tr></tbody\n></table>\n",
29985'dojo/fx':function(){
29986define("dojo/fx", [
29987 "./_base/lang",
29988 "./Evented",
29989 "./_base/kernel",
29990 "./_base/array",
29991 "./_base/connect",
29992 "./_base/fx",
29993 "./dom",
29994 "./dom-style",
29995 "./dom-geometry",
29996 "./ready",
29997 "require" // for context sensitive loading of Toggler
29998], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require) {
29999
30000 // module:
30001 // dojo/fx
30002 // summary:
30003 // TODOC
30004
30005
30006 /*=====
30007 dojo.fx = {
30008 // summary: Effects library on top of Base animations
30009 };
30010 var coreFx = dojo.fx;
30011 =====*/
30012
30013// For back-compat, remove in 2.0.
30014if(!dojo.isAsync){
30015 ready(0, function(){
30016 var requires = ["./fx/Toggler"];
30017 require(requires); // use indirection so modules not rolled into a build
30018 });
30019}
30020
30021 var coreFx = dojo.fx = {};
30022
30023 var _baseObj = {
30024 _fire: function(evt, args){
30025 if(this[evt]){
30026 this[evt].apply(this, args||[]);
30027 }
30028 return this;
30029 }
30030 };
30031
30032 var _chain = function(animations){
30033 this._index = -1;
30034 this._animations = animations||[];
30035 this._current = this._onAnimateCtx = this._onEndCtx = null;
30036
30037 this.duration = 0;
30038 arrayUtil.forEach(this._animations, function(a){
30039 this.duration += a.duration;
30040 if(a.delay){ this.duration += a.delay; }
30041 }, this);
30042 };
30043 _chain.prototype = new Evented();
30044 lang.extend(_chain, {
30045 _onAnimate: function(){
30046 this._fire("onAnimate", arguments);
30047 },
30048 _onEnd: function(){
30049 connect.disconnect(this._onAnimateCtx);
30050 connect.disconnect(this._onEndCtx);
30051 this._onAnimateCtx = this._onEndCtx = null;
30052 if(this._index + 1 == this._animations.length){
30053 this._fire("onEnd");
30054 }else{
30055 // switch animations
30056 this._current = this._animations[++this._index];
30057 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
30058 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
30059 this._current.play(0, true);
30060 }
30061 },
30062 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
30063 if(!this._current){ this._current = this._animations[this._index = 0]; }
30064 if(!gotoStart && this._current.status() == "playing"){ return this; }
30065 var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
30066 this._fire("beforeBegin");
30067 }),
30068 onBegin = connect.connect(this._current, "onBegin", this, function(arg){
30069 this._fire("onBegin", arguments);
30070 }),
30071 onPlay = connect.connect(this._current, "onPlay", this, function(arg){
30072 this._fire("onPlay", arguments);
30073 connect.disconnect(beforeBegin);
30074 connect.disconnect(onBegin);
30075 connect.disconnect(onPlay);
30076 });
30077 if(this._onAnimateCtx){
30078 connect.disconnect(this._onAnimateCtx);
30079 }
30080 this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
30081 if(this._onEndCtx){
30082 connect.disconnect(this._onEndCtx);
30083 }
30084 this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
30085 this._current.play.apply(this._current, arguments);
30086 return this;
30087 },
30088 pause: function(){
30089 if(this._current){
30090 var e = connect.connect(this._current, "onPause", this, function(arg){
30091 this._fire("onPause", arguments);
30092 connect.disconnect(e);
30093 });
30094 this._current.pause();
30095 }
30096 return this;
30097 },
30098 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
30099 this.pause();
30100 var offset = this.duration * percent;
30101 this._current = null;
30102 arrayUtil.some(this._animations, function(a){
30103 if(a.duration <= offset){
30104 this._current = a;
30105 return true;
30106 }
30107 offset -= a.duration;
30108 return false;
30109 });
30110 if(this._current){
30111 this._current.gotoPercent(offset / this._current.duration, andPlay);
30112 }
30113 return this;
30114 },
30115 stop: function(/*boolean?*/ gotoEnd){
30116 if(this._current){
30117 if(gotoEnd){
30118 for(; this._index + 1 < this._animations.length; ++this._index){
30119 this._animations[this._index].stop(true);
30120 }
30121 this._current = this._animations[this._index];
30122 }
30123 var e = connect.connect(this._current, "onStop", this, function(arg){
30124 this._fire("onStop", arguments);
30125 connect.disconnect(e);
30126 });
30127 this._current.stop();
30128 }
30129 return this;
30130 },
30131 status: function(){
30132 return this._current ? this._current.status() : "stopped";
30133 },
30134 destroy: function(){
30135 if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
30136 if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
30137 }
30138 });
30139 lang.extend(_chain, _baseObj);
30140
30141 coreFx.chain = /*===== dojo.fx.chain = =====*/ function(/*dojo.Animation[]*/ animations){
30142 // summary:
30143 // Chain a list of `dojo.Animation`s to run in sequence
30144 //
30145 // description:
30146 // Return a `dojo.Animation` which will play all passed
30147 // `dojo.Animation` instances in sequence, firing its own
30148 // synthesized events simulating a single animation. (eg:
30149 // onEnd of this animation means the end of the chain,
30150 // not the individual animations within)
30151 //
30152 // example:
30153 // Once `node` is faded out, fade in `otherNode`
30154 // | dojo.fx.chain([
30155 // | dojo.fadeIn({ node:node }),
30156 // | dojo.fadeOut({ node:otherNode })
30157 // | ]).play();
30158 //
30159 return new _chain(animations); // dojo.Animation
30160 };
30161
30162 var _combine = function(animations){
30163 this._animations = animations||[];
30164 this._connects = [];
30165 this._finished = 0;
30166
30167 this.duration = 0;
30168 arrayUtil.forEach(animations, function(a){
30169 var duration = a.duration;
30170 if(a.delay){ duration += a.delay; }
30171 if(this.duration < duration){ this.duration = duration; }
30172 this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
30173 }, this);
30174
30175 this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
30176 var self = this;
30177 arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
30178 function(evt){
30179 self._connects.push(connect.connect(self._pseudoAnimation, evt,
30180 function(){ self._fire(evt, arguments); }
30181 ));
30182 }
30183 );
30184 };
30185 lang.extend(_combine, {
30186 _doAction: function(action, args){
30187 arrayUtil.forEach(this._animations, function(a){
30188 a[action].apply(a, args);
30189 });
30190 return this;
30191 },
30192 _onEnd: function(){
30193 if(++this._finished > this._animations.length){
30194 this._fire("onEnd");
30195 }
30196 },
30197 _call: function(action, args){
30198 var t = this._pseudoAnimation;
30199 t[action].apply(t, args);
30200 },
30201 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
30202 this._finished = 0;
30203 this._doAction("play", arguments);
30204 this._call("play", arguments);
30205 return this;
30206 },
30207 pause: function(){
30208 this._doAction("pause", arguments);
30209 this._call("pause", arguments);
30210 return this;
30211 },
30212 gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
30213 var ms = this.duration * percent;
30214 arrayUtil.forEach(this._animations, function(a){
30215 a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
30216 });
30217 this._call("gotoPercent", arguments);
30218 return this;
30219 },
30220 stop: function(/*boolean?*/ gotoEnd){
30221 this._doAction("stop", arguments);
30222 this._call("stop", arguments);
30223 return this;
30224 },
30225 status: function(){
30226 return this._pseudoAnimation.status();
30227 },
30228 destroy: function(){
30229 arrayUtil.forEach(this._connects, connect.disconnect);
30230 }
30231 });
30232 lang.extend(_combine, _baseObj);
30233
30234 coreFx.combine = /*===== dojo.fx.combine = =====*/ function(/*dojo.Animation[]*/ animations){
30235 // summary:
30236 // Combine a list of `dojo.Animation`s to run in parallel
30237 //
30238 // description:
30239 // Combine an array of `dojo.Animation`s to run in parallel,
30240 // providing a new `dojo.Animation` instance encompasing each
30241 // animation, firing standard animation events.
30242 //
30243 // example:
30244 // Fade out `node` while fading in `otherNode` simultaneously
30245 // | dojo.fx.combine([
30246 // | dojo.fadeIn({ node:node }),
30247 // | dojo.fadeOut({ node:otherNode })
30248 // | ]).play();
30249 //
30250 // example:
30251 // When the longest animation ends, execute a function:
30252 // | var anim = dojo.fx.combine([
30253 // | dojo.fadeIn({ node: n, duration:700 }),
30254 // | dojo.fadeOut({ node: otherNode, duration: 300 })
30255 // | ]);
30256 // | dojo.connect(anim, "onEnd", function(){
30257 // | // overall animation is done.
30258 // | });
30259 // | anim.play(); // play the animation
30260 //
30261 return new _combine(animations); // dojo.Animation
30262 };
30263
30264 coreFx.wipeIn = /*===== dojo.fx.wipeIn = =====*/ function(/*Object*/ args){
30265 // summary:
30266 // Expand a node to it's natural height.
30267 //
30268 // description:
30269 // Returns an animation that will expand the
30270 // node defined in 'args' object from it's current height to
30271 // it's natural height (with no scrollbar).
30272 // Node must have no margin/border/padding.
30273 //
30274 // args: Object
30275 // A hash-map of standard `dojo.Animation` constructor properties
30276 // (such as easing: node: duration: and so on)
30277 //
30278 // example:
30279 // | dojo.fx.wipeIn({
30280 // | node:"someId"
30281 // | }).play()
30282 var node = args.node = dom.byId(args.node), s = node.style, o;
30283
30284 var anim = baseFx.animateProperty(lang.mixin({
30285 properties: {
30286 height: {
30287 // wrapped in functions so we wait till the last second to query (in case value has changed)
30288 start: function(){
30289 // start at current [computed] height, but use 1px rather than 0
30290 // because 0 causes IE to display the whole panel
30291 o = s.overflow;
30292 s.overflow = "hidden";
30293 if(s.visibility == "hidden" || s.display == "none"){
30294 s.height = "1px";
30295 s.display = "";
30296 s.visibility = "";
30297 return 1;
30298 }else{
30299 var height = domStyle.get(node, "height");
30300 return Math.max(height, 1);
30301 }
30302 },
30303 end: function(){
30304 return node.scrollHeight;
30305 }
30306 }
30307 }
30308 }, args));
30309
30310 var fini = function(){
30311 s.height = "auto";
30312 s.overflow = o;
30313 };
30314 connect.connect(anim, "onStop", fini);
30315 connect.connect(anim, "onEnd", fini);
30316
30317 return anim; // dojo.Animation
30318 };
30319
30320 coreFx.wipeOut = /*===== dojo.fx.wipeOut = =====*/ function(/*Object*/ args){
30321 // summary:
30322 // Shrink a node to nothing and hide it.
30323 //
30324 // description:
30325 // Returns an animation that will shrink node defined in "args"
30326 // from it's current height to 1px, and then hide it.
30327 //
30328 // args: Object
30329 // A hash-map of standard `dojo.Animation` constructor properties
30330 // (such as easing: node: duration: and so on)
30331 //
30332 // example:
30333 // | dojo.fx.wipeOut({ node:"someId" }).play()
30334
30335 var node = args.node = dom.byId(args.node), s = node.style, o;
30336
30337 var anim = baseFx.animateProperty(lang.mixin({
30338 properties: {
30339 height: {
30340 end: 1 // 0 causes IE to display the whole panel
30341 }
30342 }
30343 }, args));
30344
30345 connect.connect(anim, "beforeBegin", function(){
30346 o = s.overflow;
30347 s.overflow = "hidden";
30348 s.display = "";
30349 });
30350 var fini = function(){
30351 s.overflow = o;
30352 s.height = "auto";
30353 s.display = "none";
30354 };
30355 connect.connect(anim, "onStop", fini);
30356 connect.connect(anim, "onEnd", fini);
30357
30358 return anim; // dojo.Animation
30359 };
30360
30361 coreFx.slideTo = /*===== dojo.fx.slideTo = =====*/ function(/*Object*/ args){
30362 // summary:
30363 // Slide a node to a new top/left position
30364 //
30365 // description:
30366 // Returns an animation that will slide "node"
30367 // defined in args Object from its current position to
30368 // the position defined by (args.left, args.top).
30369 //
30370 // args: Object
30371 // A hash-map of standard `dojo.Animation` constructor properties
30372 // (such as easing: node: duration: and so on). Special args members
30373 // are `top` and `left`, which indicate the new position to slide to.
30374 //
30375 // example:
30376 // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
30377
30378 var node = args.node = dom.byId(args.node),
30379 top = null, left = null;
30380
30381 var init = (function(n){
30382 return function(){
30383 var cs = domStyle.getComputedStyle(n);
30384 var pos = cs.position;
30385 top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
30386 left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
30387 if(pos != 'absolute' && pos != 'relative'){
30388 var ret = geom.position(n, true);
30389 top = ret.y;
30390 left = ret.x;
30391 n.style.position="absolute";
30392 n.style.top=top+"px";
30393 n.style.left=left+"px";
30394 }
30395 };
30396 })(node);
30397 init();
30398
30399 var anim = baseFx.animateProperty(lang.mixin({
30400 properties: {
30401 top: args.top || 0,
30402 left: args.left || 0
30403 }
30404 }, args));
30405 connect.connect(anim, "beforeBegin", anim, init);
30406
30407 return anim; // dojo.Animation
30408 };
30409
30410 return coreFx;
30411});
30412
30413},
30414'dijit/_DialogMixin':function(){
30415define("dijit/_DialogMixin", [
30416 "dojo/_base/declare", // declare
30417 "./a11y" // _getTabNavigable
30418], function(declare, a11y){
30419
30420 // module:
30421 // dijit/_DialogMixin
30422 // summary:
30423 // _DialogMixin provides functions useful to Dialog and TooltipDialog
30424
30425 return declare("dijit._DialogMixin", null, {
30426 // summary:
30427 // This provides functions useful to Dialog and TooltipDialog
30428
30429 execute: function(/*Object*/ /*===== formContents =====*/){
30430 // summary:
30431 // Callback when the user hits the submit button.
30432 // Override this method to handle Dialog execution.
30433 // description:
30434 // After the user has pressed the submit button, the Dialog
30435 // first calls onExecute() to notify the container to hide the
30436 // dialog and restore focus to wherever it used to be.
30437 //
30438 // *Then* this method is called.
30439 // type:
30440 // callback
30441 },
30442
30443 onCancel: function(){
30444 // summary:
30445 // Called when user has pressed the Dialog's cancel button, to notify container.
30446 // description:
30447 // Developer shouldn't override or connect to this method;
30448 // it's a private communication device between the TooltipDialog
30449 // and the thing that opened it (ex: `dijit.form.DropDownButton`)
30450 // type:
30451 // protected
30452 },
30453
30454 onExecute: function(){
30455 // summary:
30456 // Called when user has pressed the dialog's OK button, to notify container.
30457 // description:
30458 // Developer shouldn't override or connect to this method;
30459 // it's a private communication device between the TooltipDialog
30460 // and the thing that opened it (ex: `dijit.form.DropDownButton`)
30461 // type:
30462 // protected
30463 },
30464
30465 _onSubmit: function(){
30466 // summary:
30467 // Callback when user hits submit button
30468 // type:
30469 // protected
30470 this.onExecute(); // notify container that we are about to execute
30471 this.execute(this.get('value'));
30472 },
30473
30474 _getFocusItems: function(){
30475 // summary:
30476 // Finds focusable items in dialog,
30477 // and sets this._firstFocusItem and this._lastFocusItem
30478 // tags:
30479 // protected
30480
30481 var elems = a11y._getTabNavigable(this.containerNode);
30482 this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
30483 this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
30484 }
30485 });
30486});
30487
30488},
30489'dijit/Tree':function(){
30490require({cache:{
30491'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow\" role=\"presentation\" data-dojo-attach-event=\"onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick\"\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\" data-dojo-attach-event=\"onfocus:_onLabelFocus\"></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",
30492'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\"\n\tdata-dojo-attach-event=\"onkeypress:_onKeyPress\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});
30493define("dijit/Tree", [
30494 "dojo/_base/array", // array.filter array.forEach array.map
30495 "dojo/_base/connect", // connect.isCopyKey()
30496 "dojo/cookie", // cookie
30497 "dojo/_base/declare", // declare
30498 "dojo/_base/Deferred", // Deferred
30499 "dojo/DeferredList", // DeferredList
30500 "dojo/dom", // dom.isDescendant
30501 "dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
30502 "dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
30503 "dojo/dom-style",// domStyle.set
30504 "dojo/_base/event", // event.stop
30505 "dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
30506 "dojo/_base/kernel", // kernel.deprecated
30507 "dojo/keys", // arrows etc.
30508 "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
30509 "dojo/topic",
30510 "./focus",
30511 "./registry", // registry.getEnclosingWidget(), manager.defaultDuration
30512 "./_base/manager", // manager.getEnclosingWidget(), manager.defaultDuration
30513 "./_Widget",
30514 "./_TemplatedMixin",
30515 "./_Container",
30516 "./_Contained",
30517 "./_CssStateMixin",
30518 "dojo/text!./templates/TreeNode.html",
30519 "dojo/text!./templates/Tree.html",
30520 "./tree/TreeStoreModel",
30521 "./tree/ForestStoreModel",
30522 "./tree/_dndSelector"
30523], function(array, connect, cookie, declare, Deferred, DeferredList,
30524 dom, domClass, domGeometry, domStyle, event, fxUtils, kernel, keys, lang, topic,
30525 focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
30526 treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
30527
30528/*=====
30529 var _Widget = dijit._Widget;
30530 var _TemplatedMixin = dijit._TemplatedMixin;
30531 var _CssStateMixin = dijit._CssStateMixin;
30532 var _Container = dijit._Container;
30533 var _Contained = dijit._Contained;
30534=====*/
30535
30536// module:
30537// dijit/Tree
30538// summary:
30539// dijit.Tree widget, and internal dijit._TreeNode widget
30540
30541
30542var TreeNode = declare(
30543 "dijit._TreeNode",
30544 [_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
30545{
30546 // summary:
30547 // Single node within a tree. This class is used internally
30548 // by Tree and should not be accessed directly.
30549 // tags:
30550 // private
30551
30552 // item: [const] Item
30553 // the dojo.data entry this tree represents
30554 item: null,
30555
30556 // isTreeNode: [protected] Boolean
30557 // Indicates that this is a TreeNode. Used by `dijit.Tree` only,
30558 // should not be accessed directly.
30559 isTreeNode: true,
30560
30561 // label: String
30562 // Text of this tree node
30563 label: "",
30564 _setLabelAttr: {node: "labelNode", type: "innerText"},
30565
30566 // isExpandable: [private] Boolean
30567 // This node has children, so show the expando node (+ sign)
30568 isExpandable: null,
30569
30570 // isExpanded: [readonly] Boolean
30571 // This node is currently expanded (ie, opened)
30572 isExpanded: false,
30573
30574 // state: [private] String
30575 // Dynamic loading-related stuff.
30576 // When an empty folder node appears, it is "UNCHECKED" first,
30577 // then after dojo.data query it becomes "LOADING" and, finally "LOADED"
30578 state: "UNCHECKED",
30579
30580 templateString: treeNodeTemplate,
30581
30582 baseClass: "dijitTreeNode",
30583
30584 // For hover effect for tree node, and focus effect for label
30585 cssStateNodes: {
30586 rowNode: "dijitTreeRow",
30587 labelNode: "dijitTreeLabel"
30588 },
30589
30590 // Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
30591 _setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
30592
30593 buildRendering: function(){
30594 this.inherited(arguments);
30595
30596 // set expand icon for leaf
30597 this._setExpando();
30598
30599 // set icon and label class based on item
30600 this._updateItemClasses(this.item);
30601
30602 if(this.isExpandable){
30603 this.labelNode.setAttribute("aria-expanded", this.isExpanded);
30604 }
30605
30606 //aria-selected should be false on all selectable elements.
30607 this.setSelected(false);
30608 },
30609
30610 _setIndentAttr: function(indent){
30611 // summary:
30612 // Tell this node how many levels it should be indented
30613 // description:
30614 // 0 for top level nodes, 1 for their children, 2 for their
30615 // grandchildren, etc.
30616
30617 // Math.max() is to prevent negative padding on hidden root node (when indent == -1)
30618 var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
30619
30620 domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px");
30621 domStyle.set(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
30622
30623 array.forEach(this.getChildren(), function(child){
30624 child.set("indent", indent+1);
30625 });
30626
30627 this._set("indent", indent);
30628 },
30629
30630 markProcessing: function(){
30631 // summary:
30632 // Visually denote that tree is loading data, etc.
30633 // tags:
30634 // private
30635 this.state = "LOADING";
30636 this._setExpando(true);
30637 },
30638
30639 unmarkProcessing: function(){
30640 // summary:
30641 // Clear markup from markProcessing() call
30642 // tags:
30643 // private
30644 this._setExpando(false);
30645 },
30646
30647 _updateItemClasses: function(item){
30648 // summary:
30649 // Set appropriate CSS classes for icon and label dom node
30650 // (used to allow for item updates to change respective CSS)
30651 // tags:
30652 // private
30653 var tree = this.tree, model = tree.model;
30654 if(tree._v10Compat && item === model.root){
30655 // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
30656 item = null;
30657 }
30658 this._applyClassAndStyle(item, "icon", "Icon");
30659 this._applyClassAndStyle(item, "label", "Label");
30660 this._applyClassAndStyle(item, "row", "Row");
30661 },
30662
30663 _applyClassAndStyle: function(item, lower, upper){
30664 // summary:
30665 // Set the appropriate CSS classes and styles for labels, icons and rows.
30666 //
30667 // item:
30668 // The data item.
30669 //
30670 // lower:
30671 // The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
30672 //
30673 // upper:
30674 // The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
30675 //
30676 // tags:
30677 // private
30678
30679 var clsName = "_" + lower + "Class";
30680 var nodeName = lower + "Node";
30681 var oldCls = this[clsName];
30682
30683 this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
30684 domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
30685
30686 domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
30687 },
30688
30689 _updateLayout: function(){
30690 // summary:
30691 // Set appropriate CSS classes for this.domNode
30692 // tags:
30693 // private
30694 var parent = this.getParent();
30695 if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
30696 /* if we are hiding the root node then make every first level child look like a root node */
30697 domClass.add(this.domNode, "dijitTreeIsRoot");
30698 }else{
30699 domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
30700 }
30701 },
30702
30703 _setExpando: function(/*Boolean*/ processing){
30704 // summary:
30705 // Set the right image for the expando node
30706 // tags:
30707 // private
30708
30709 var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
30710 "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
30711 _a11yStates = ["*","-","+","*"],
30712 idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
30713
30714 // apply the appropriate class to the expando node
30715 domClass.replace(this.expandoNode, styles[idx], styles);
30716
30717 // provide a non-image based indicator for images-off mode
30718 this.expandoNodeText.innerHTML = _a11yStates[idx];
30719
30720 },
30721
30722 expand: function(){
30723 // summary:
30724 // Show my children
30725 // returns:
30726 // Deferred that fires when expansion is complete
30727
30728 // If there's already an expand in progress or we are already expanded, just return
30729 if(this._expandDeferred){
30730 return this._expandDeferred; // dojo.Deferred
30731 }
30732
30733 // cancel in progress collapse operation
30734 this._wipeOut && this._wipeOut.stop();
30735
30736 // All the state information for when a node is expanded, maybe this should be
30737 // set when the animation completes instead
30738 this.isExpanded = true;
30739 this.labelNode.setAttribute("aria-expanded", "true");
30740 if(this.tree.showRoot || this !== this.tree.rootNode){
30741 this.containerNode.setAttribute("role", "group");
30742 }
30743 domClass.add(this.contentNode,'dijitTreeContentExpanded');
30744 this._setExpando();
30745 this._updateItemClasses(this.item);
30746 if(this == this.tree.rootNode){
30747 this.tree.domNode.setAttribute("aria-expanded", "true");
30748 }
30749
30750 var def,
30751 wipeIn = fxUtils.wipeIn({
30752 node: this.containerNode, duration: manager.defaultDuration,
30753 onEnd: function(){
30754 def.callback(true);
30755 }
30756 });
30757
30758 // Deferred that fires when expand is complete
30759 def = (this._expandDeferred = new Deferred(function(){
30760 // Canceller
30761 wipeIn.stop();
30762 }));
30763
30764 wipeIn.play();
30765
30766 return def; // dojo.Deferred
30767 },
30768
30769 collapse: function(){
30770 // summary:
30771 // Collapse this node (if it's expanded)
30772
30773 if(!this.isExpanded){ return; }
30774
30775 // cancel in progress expand operation
30776 if(this._expandDeferred){
30777 this._expandDeferred.cancel();
30778 delete this._expandDeferred;
30779 }
30780
30781 this.isExpanded = false;
30782 this.labelNode.setAttribute("aria-expanded", "false");
30783 if(this == this.tree.rootNode){
30784 this.tree.domNode.setAttribute("aria-expanded", "false");
30785 }
30786 domClass.remove(this.contentNode,'dijitTreeContentExpanded');
30787 this._setExpando();
30788 this._updateItemClasses(this.item);
30789
30790 if(!this._wipeOut){
30791 this._wipeOut = fxUtils.wipeOut({
30792 node: this.containerNode, duration: manager.defaultDuration
30793 });
30794 }
30795 this._wipeOut.play();
30796 },
30797
30798 // indent: Integer
30799 // Levels from this node to the root node
30800 indent: 0,
30801
30802 setChildItems: function(/* Object[] */ items){
30803 // summary:
30804 // Sets the child items of this node, removing/adding nodes
30805 // from current children to match specified items[] array.
30806 // Also, if this.persist == true, expands any children that were previously
30807 // opened.
30808 // returns:
30809 // Deferred object that fires after all previously opened children
30810 // have been expanded again (or fires instantly if there are no such children).
30811
30812 var tree = this.tree,
30813 model = tree.model,
30814 defs = []; // list of deferreds that need to fire before I am complete
30815
30816
30817 // Orphan all my existing children.
30818 // If items contains some of the same items as before then we will reattach them.
30819 // Don't call this.removeChild() because that will collapse the tree etc.
30820 array.forEach(this.getChildren(), function(child){
30821 _Container.prototype.removeChild.call(this, child);
30822 }, this);
30823
30824 this.state = "LOADED";
30825
30826 if(items && items.length > 0){
30827 this.isExpandable = true;
30828
30829 // Create _TreeNode widget for each specified tree node, unless one already
30830 // exists and isn't being used (presumably it's from a DnD move and was recently
30831 // released
30832 array.forEach(items, function(item){
30833 var id = model.getIdentity(item),
30834 existingNodes = tree._itemNodesMap[id],
30835 node;
30836 if(existingNodes){
30837 for(var i=0;i<existingNodes.length;i++){
30838 if(existingNodes[i] && !existingNodes[i].getParent()){
30839 node = existingNodes[i];
30840 node.set('indent', this.indent+1);
30841 break;
30842 }
30843 }
30844 }
30845 if(!node){
30846 node = this.tree._createTreeNode({
30847 item: item,
30848 tree: tree,
30849 isExpandable: model.mayHaveChildren(item),
30850 label: tree.getLabel(item),
30851 tooltip: tree.getTooltip(item),
30852 dir: tree.dir,
30853 lang: tree.lang,
30854 textDir: tree.textDir,
30855 indent: this.indent + 1
30856 });
30857 if(existingNodes){
30858 existingNodes.push(node);
30859 }else{
30860 tree._itemNodesMap[id] = [node];
30861 }
30862 }
30863 this.addChild(node);
30864
30865 // If node was previously opened then open it again now (this may trigger
30866 // more data store accesses, recursively)
30867 if(this.tree.autoExpand || this.tree._state(node)){
30868 defs.push(tree._expandNode(node));
30869 }
30870 }, this);
30871
30872 // note that updateLayout() needs to be called on each child after
30873 // _all_ the children exist
30874 array.forEach(this.getChildren(), function(child){
30875 child._updateLayout();
30876 });
30877 }else{
30878 this.isExpandable=false;
30879 }
30880
30881 if(this._setExpando){
30882 // change expando to/from dot or + icon, as appropriate
30883 this._setExpando(false);
30884 }
30885
30886 // Set leaf icon or folder icon, as appropriate
30887 this._updateItemClasses(this.item);
30888
30889 // On initial tree show, make the selected TreeNode as either the root node of the tree,
30890 // or the first child, if the root node is hidden
30891 if(this == tree.rootNode){
30892 var fc = this.tree.showRoot ? this : this.getChildren()[0];
30893 if(fc){
30894 fc.setFocusable(true);
30895 tree.lastFocused = fc;
30896 }else{
30897 // fallback: no nodes in tree so focus on Tree <div> itself
30898 tree.domNode.setAttribute("tabIndex", "0");
30899 }
30900 }
30901
30902 return new DeferredList(defs); // dojo.Deferred
30903 },
30904
30905 getTreePath: function(){
30906 var node = this;
30907 var path = [];
30908 while(node && node !== this.tree.rootNode){
30909 path.unshift(node.item);
30910 node = node.getParent();
30911 }
30912 path.unshift(this.tree.rootNode.item);
30913
30914 return path;
30915 },
30916
30917 getIdentity: function(){
30918 return this.tree.model.getIdentity(this.item);
30919 },
30920
30921 removeChild: function(/* treeNode */ node){
30922 this.inherited(arguments);
30923
30924 var children = this.getChildren();
30925 if(children.length == 0){
30926 this.isExpandable = false;
30927 this.collapse();
30928 }
30929
30930 array.forEach(children, function(child){
30931 child._updateLayout();
30932 });
30933 },
30934
30935 makeExpandable: function(){
30936 // summary:
30937 // if this node wasn't already showing the expando node,
30938 // turn it into one and call _setExpando()
30939
30940 // TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
30941
30942 this.isExpandable = true;
30943 this._setExpando(false);
30944 },
30945
30946 _onLabelFocus: function(){
30947 // summary:
30948 // Called when this row is focused (possibly programatically)
30949 // Note that we aren't using _onFocus() builtin to dijit
30950 // because it's called when focus is moved to a descendant TreeNode.
30951 // tags:
30952 // private
30953 this.tree._onNodeFocus(this);
30954 },
30955
30956 setSelected: function(/*Boolean*/ selected){
30957 // summary:
30958 // A Tree has a (single) currently selected node.
30959 // Mark that this node is/isn't that currently selected node.
30960 // description:
30961 // In particular, setting a node as selected involves setting tabIndex
30962 // so that when user tabs to the tree, focus will go to that node (only).
30963 this.labelNode.setAttribute("aria-selected", selected);
30964 domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
30965 },
30966
30967 setFocusable: function(/*Boolean*/ selected){
30968 // summary:
30969 // A Tree has a (single) node that's focusable.
30970 // Mark that this node is/isn't that currently focsuable node.
30971 // description:
30972 // In particular, setting a node as selected involves setting tabIndex
30973 // so that when user tabs to the tree, focus will go to that node (only).
a089699c 30974
1354d172 30975 this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
a089699c
AD
30976 },
30977
1354d172
AD
30978 _onClick: function(evt){
30979 // summary:
30980 // Handler for onclick event on a node
30981 // tags:
30982 // private
30983 this.tree._onClick(this, evt);
30984 },
30985 _onDblClick: function(evt){
30986 // summary:
30987 // Handler for ondblclick event on a node
30988 // tags:
30989 // private
30990 this.tree._onDblClick(this, evt);
30991 },
30992
30993 _onMouseEnter: function(evt){
30994 // summary:
30995 // Handler for onmouseenter event on a node
30996 // tags:
30997 // private
30998 this.tree._onNodeMouseEnter(this, evt);
30999 },
31000
31001 _onMouseLeave: function(evt){
31002 // summary:
31003 // Handler for onmouseenter event on a node
31004 // tags:
31005 // private
31006 this.tree._onNodeMouseLeave(this, evt);
31007 },
31008
31009 _setTextDirAttr: function(textDir){
31010 if(textDir &&((this.textDir != textDir) || !this._created)){
31011 this._set("textDir", textDir);
31012 this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
31013 array.forEach(this.getChildren(), function(childNode){
31014 childNode.set("textDir", textDir);
31015 }, this);
31016 }
31017 }
31018});
31019
31020var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
31021 // summary:
31022 // This widget displays hierarchical data from a store.
31023
31024 // store: [deprecated] String||dojo.data.Store
31025 // Deprecated. Use "model" parameter instead.
31026 // The store to get data to display in the tree.
31027 store: null,
31028
31029 // model: dijit.Tree.model
31030 // Interface to read tree data, get notifications of changes to tree data,
31031 // and for handling drop operations (i.e drag and drop onto the tree)
31032 model: null,
31033
31034 // query: [deprecated] anything
31035 // Deprecated. User should specify query to the model directly instead.
31036 // Specifies datastore query to return the root item or top items for the tree.
31037 query: null,
31038
31039 // label: [deprecated] String
31040 // Deprecated. Use dijit.tree.ForestStoreModel directly instead.
31041 // Used in conjunction with query parameter.
31042 // If a query is specified (rather than a root node id), and a label is also specified,
31043 // then a fake root node is created and displayed, with this label.
31044 label: "",
31045
31046 // showRoot: [const] Boolean
31047 // Should the root node be displayed, or hidden?
31048 showRoot: true,
31049
31050 // childrenAttr: [deprecated] String[]
31051 // Deprecated. This information should be specified in the model.
31052 // One ore more attributes that holds children of a tree node
31053 childrenAttr: ["children"],
31054
31055 // paths: String[][] or Item[][]
31056 // Full paths from rootNode to selected nodes expressed as array of items or array of ids.
31057 // Since setting the paths may be asynchronous (because ofwaiting on dojo.data), set("paths", ...)
31058 // returns a Deferred to indicate when the set is complete.
31059 paths: [],
31060
31061 // path: String[] or Item[]
31062 // Backward compatible singular variant of paths.
31063 path: [],
31064
31065 // selectedItems: [readonly] Item[]
31066 // The currently selected items in this tree.
31067 // This property can only be set (via set('selectedItems', ...)) when that item is already
31068 // visible in the tree. (I.e. the tree has already been expanded to show that node.)
31069 // Should generally use `paths` attribute to set the selected items instead.
31070 selectedItems: null,
31071
31072 // selectedItem: [readonly] Item
31073 // Backward compatible singular variant of selectedItems.
31074 selectedItem: null,
31075
31076 // openOnClick: Boolean
31077 // If true, clicking a folder node's label will open it, rather than calling onClick()
31078 openOnClick: false,
31079
31080 // openOnDblClick: Boolean
31081 // If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
31082 openOnDblClick: false,
31083
31084 templateString: treeTemplate,
31085
31086 // persist: Boolean
31087 // Enables/disables use of cookies for state saving.
31088 persist: true,
31089
31090 // autoExpand: Boolean
31091 // Fully expand the tree on load. Overrides `persist`.
31092 autoExpand: false,
31093
31094 // dndController: [protected] Function|String
31095 // Class to use as as the dnd controller. Specifying this class enables DnD.
31096 // Generally you should specify this as dijit.tree.dndSource.
31097 // Setting of dijit.tree._dndSelector handles selection only (no actual DnD).
31098 dndController: _dndSelector,
31099
31100 // parameters to pull off of the tree and pass on to the dndController as its params
31101 dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
31102
31103 //declare the above items so they can be pulled from the tree's markup
31104
31105 // onDndDrop: [protected] Function
31106 // Parameter to dndController, see `dijit.tree.dndSource.onDndDrop`.
31107 // Generally this doesn't need to be set.
31108 onDndDrop: null,
31109
31110 /*=====
31111 itemCreator: function(nodes, target, source){
31112 // summary:
31113 // Returns objects passed to `Tree.model.newItem()` based on DnD nodes
31114 // dropped onto the tree. Developer must override this method to enable
31115 // dropping from external sources onto this Tree, unless the Tree.model's items
31116 // happen to look like {id: 123, name: "Apple" } with no other attributes.
31117 // description:
31118 // For each node in nodes[], which came from source, create a hash of name/value
31119 // pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
31120 // nodes: DomNode[]
31121 // The DOMNodes dragged from the source container
31122 // target: DomNode
31123 // The target TreeNode.rowNode
31124 // source: dojo.dnd.Source
31125 // The source container the nodes were dragged from, perhaps another Tree or a plain dojo.dnd.Source
31126 // returns: Object[]
31127 // Array of name/value hashes for each new item to be added to the Tree, like:
31128 // | [
31129 // | { id: 123, label: "apple", foo: "bar" },
31130 // | { id: 456, label: "pear", zaz: "bam" }
31131 // | ]
31132 // tags:
31133 // extension
31134 return [{}];
31135 },
31136 =====*/
31137 itemCreator: null,
31138
31139 // onDndCancel: [protected] Function
31140 // Parameter to dndController, see `dijit.tree.dndSource.onDndCancel`.
31141 // Generally this doesn't need to be set.
31142 onDndCancel: null,
31143
31144/*=====
31145 checkAcceptance: function(source, nodes){
31146 // summary:
31147 // Checks if the Tree itself can accept nodes from this source
31148 // source: dijit.tree._dndSource
31149 // The source which provides items
31150 // nodes: DOMNode[]
31151 // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
31152 // source is a dijit.Tree.
31153 // tags:
31154 // extension
31155 return true; // Boolean
31156 },
31157=====*/
31158 checkAcceptance: null,
31159
31160/*=====
31161 checkItemAcceptance: function(target, source, position){
31162 // summary:
31163 // Stub function to be overridden if one wants to check for the ability to drop at the node/item level
31164 // description:
31165 // In the base case, this is called to check if target can become a child of source.
31166 // When betweenThreshold is set, position="before" or "after" means that we
31167 // are asking if the source node can be dropped before/after the target node.
31168 // target: DOMNode
31169 // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
31170 // Use dijit.getEnclosingWidget(target) to get the TreeNode.
31171 // source: dijit.tree.dndSource
31172 // The (set of) nodes we are dropping
31173 // position: String
31174 // "over", "before", or "after"
31175 // tags:
31176 // extension
31177 return true; // Boolean
31178 },
31179=====*/
31180 checkItemAcceptance: null,
31181
31182 // dragThreshold: Integer
31183 // Number of pixels mouse moves before it's considered the start of a drag operation
31184 dragThreshold: 5,
31185
31186 // betweenThreshold: Integer
31187 // Set to a positive value to allow drag and drop "between" nodes.
31188 //
31189 // If during DnD mouse is over a (target) node but less than betweenThreshold
31190 // pixels from the bottom edge, dropping the the dragged node will make it
31191 // the next sibling of the target node, rather than the child.
31192 //
31193 // Similarly, if mouse is over a target node but less that betweenThreshold
31194 // pixels from the top edge, dropping the dragged node will make it
31195 // the target node's previous sibling rather than the target node's child.
31196 betweenThreshold: 0,
31197
31198 // _nodePixelIndent: Integer
31199 // Number of pixels to indent tree nodes (relative to parent node).
31200 // Default is 19 but can be overridden by setting CSS class dijitTreeIndent
31201 // and calling resize() or startup() on tree after it's in the DOM.
31202 _nodePixelIndent: 19,
31203
31204 _publish: function(/*String*/ topicName, /*Object*/ message){
31205 // summary:
31206 // Publish a message for this widget/topic
31207 topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
31208 },
31209
31210 postMixInProperties: function(){
31211 this.tree = this;
31212
31213 if(this.autoExpand){
31214 // There's little point in saving opened/closed state of nodes for a Tree
31215 // that initially opens all it's nodes.
31216 this.persist = false;
a089699c 31217 }
1354d172
AD
31218
31219 this._itemNodesMap={};
31220
31221 if(!this.cookieName && this.id){
31222 this.cookieName = this.id + "SaveStateCookie";
a089699c
AD
31223 }
31224
1354d172 31225 this._loadDeferred = new Deferred();
a089699c 31226
1354d172
AD
31227 this.inherited(arguments);
31228 },
a089699c 31229
1354d172
AD
31230 postCreate: function(){
31231 this._initState();
a089699c 31232
1354d172
AD
31233 // Create glue between store and Tree, if not specified directly by user
31234 if(!this.model){
31235 this._store2model();
a089699c
AD
31236 }
31237
1354d172
AD
31238 // monitor changes to items
31239 this.connect(this.model, "onChange", "_onItemChange");
31240 this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
31241 this.connect(this.model, "onDelete", "_onItemDelete");
a089699c 31242
1354d172
AD
31243 this._load();
31244
31245 this.inherited(arguments);
31246
31247 if(this.dndController){
31248 if(lang.isString(this.dndController)){
31249 this.dndController = lang.getObject(this.dndController);
31250 }
31251 var params={};
31252 for(var i=0; i<this.dndParams.length;i++){
31253 if(this[this.dndParams[i]]){
31254 params[this.dndParams[i]] = this[this.dndParams[i]];
a089699c 31255 }
a089699c 31256 }
1354d172 31257 this.dndController = new this.dndController(this, params);
a089699c 31258 }
1354d172 31259 },
a089699c 31260
1354d172
AD
31261 _store2model: function(){
31262 // summary:
31263 // User specified a store&query rather than model, so create model from store/query
31264 this._v10Compat = true;
31265 kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
31266
31267 var modelParams = {
31268 id: this.id + "_ForestStoreModel",
31269 store: this.store,
31270 query: this.query,
31271 childrenAttrs: this.childrenAttr
31272 };
31273
31274 // Only override the model's mayHaveChildren() method if the user has specified an override
31275 if(this.params.mayHaveChildren){
31276 modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
a089699c 31277 }
1354d172
AD
31278
31279 if(this.params.getItemChildren){
31280 modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
31281 this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
31282 });
a089699c 31283 }
1354d172
AD
31284 this.model = new ForestStoreModel(modelParams);
31285
31286 // For backwards compatibility, the visibility of the root node is controlled by
31287 // whether or not the user has specified a label
31288 this.showRoot = Boolean(this.label);
31289 },
31290
31291 onLoad: function(){
31292 // summary:
31293 // Called when tree finishes loading and expanding.
31294 // description:
31295 // If persist == true the loading may encompass many levels of fetches
31296 // from the data store, each asynchronous. Waits for all to finish.
31297 // tags:
31298 // callback
31299 },
31300
31301 _load: function(){
31302 // summary:
31303 // Initial load of the tree.
31304 // Load root node (possibly hidden) and it's children.
31305 this.model.getRoot(
31306 lang.hitch(this, function(item){
31307 var rn = (this.rootNode = this.tree._createTreeNode({
31308 item: item,
31309 tree: this,
31310 isExpandable: true,
31311 label: this.label || this.getLabel(item),
31312 textDir: this.textDir,
31313 indent: this.showRoot ? 0 : -1
31314 }));
31315 if(!this.showRoot){
31316 rn.rowNode.style.display="none";
31317 // if root is not visible, move tree role to the invisible
31318 // root node's containerNode, see #12135
31319 this.domNode.setAttribute("role", "presentation");
31320
31321 rn.labelNode.setAttribute("role", "presentation");
31322 rn.containerNode.setAttribute("role", "tree");
31323 }
31324 this.domNode.appendChild(rn.domNode);
31325 var identity = this.model.getIdentity(item);
31326 if(this._itemNodesMap[identity]){
31327 this._itemNodesMap[identity].push(rn);
31328 }else{
31329 this._itemNodesMap[identity] = [rn];
31330 }
31331
31332 rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
31333
31334 // load top level children and then fire onLoad() event
31335 this._expandNode(rn).addCallback(lang.hitch(this, function(){
31336 this._loadDeferred.callback(true);
31337 this.onLoad();
31338 }));
31339 }),
31340 function(err){
31341 console.error(this, ": error loading root: ", err);
31342 }
31343 );
31344 },
31345
31346 getNodesByItem: function(/*Item or id*/ item){
31347 // summary:
31348 // Returns all tree nodes that refer to an item
31349 // returns:
31350 // Array of tree nodes that refer to passed item
31351
31352 if(!item){ return []; }
31353 var identity = lang.isString(item) ? item : this.model.getIdentity(item);
31354 // return a copy so widget don't get messed up by changes to returned array
31355 return [].concat(this._itemNodesMap[identity]);
31356 },
31357
31358 _setSelectedItemAttr: function(/*Item or id*/ item){
31359 this.set('selectedItems', [item]);
31360 },
31361
31362 _setSelectedItemsAttr: function(/*Items or ids*/ items){
31363 // summary:
31364 // Select tree nodes related to passed items.
31365 // WARNING: if model use multi-parented items or desired tree node isn't already loaded
31366 // behavior is undefined. Use set('paths', ...) instead.
31367 var tree = this;
31368 this._loadDeferred.addCallback( lang.hitch(this, function(){
31369 var identities = array.map(items, function(item){
31370 return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
31371 });
31372 var nodes = [];
31373 array.forEach(identities, function(id){
31374 nodes = nodes.concat(tree._itemNodesMap[id] || []);
31375 });
31376 this.set('selectedNodes', nodes);
31377 }));
31378 },
31379
31380 _setPathAttr: function(/*Item[] || String[]*/ path){
31381 // summary:
31382 // Singular variant of _setPathsAttr
31383 if(path.length){
31384 return this.set("paths", [path]);
31385 }else{
31386 // Empty list is interpreted as "select nothing"
31387 return this.set("paths", []);
a089699c 31388 }
1354d172 31389 },
a089699c 31390
1354d172
AD
31391 _setPathsAttr: function(/*Item[][] || String[][]*/ paths){
31392 // summary:
31393 // Select the tree nodes identified by passed paths.
31394 // paths:
31395 // Array of arrays of items or item id's
31396 // returns:
31397 // Deferred to indicate when the set is complete
31398 var tree = this;
a089699c 31399
1354d172
AD
31400 // We may need to wait for some nodes to expand, so setting
31401 // each path will involve a Deferred. We bring those deferreds
31402 // together witha DeferredList.
31403 return new DeferredList(array.map(paths, function(path){
31404 var d = new Deferred();
31405
31406 // normalize path to use identity
31407 path = array.map(path, function(item){
31408 return lang.isString(item) ? item : tree.model.getIdentity(item);
31409 });
31410
31411 if(path.length){
31412 // Wait for the tree to load, if it hasn't already.
31413 tree._loadDeferred.addCallback(function(){ selectPath(path, [tree.rootNode], d); });
31414 }else{
31415 d.errback("Empty path");
31416 }
31417 return d;
31418 })).addCallback(setNodes);
31419
31420 function selectPath(path, nodes, def){
31421 // Traverse path; the next path component should be among "nodes".
31422 var nextPath = path.shift();
31423 var nextNode = array.filter(nodes, function(node){
31424 return node.getIdentity() == nextPath;
31425 })[0];
31426 if(!!nextNode){
31427 if(path.length){
31428 tree._expandNode(nextNode).addCallback(function(){ selectPath(path, nextNode.getChildren(), def); });
a089699c 31429 }else{
1354d172
AD
31430 //Successfully reached the end of this path
31431 def.callback(nextNode);
a089699c 31432 }
1354d172
AD
31433 }else{
31434 def.errback("Could not expand path at " + nextPath);
a089699c 31435 }
a089699c
AD
31436 }
31437
1354d172
AD
31438 function setNodes(newNodes){
31439 //After all expansion is finished, set the selection to
31440 //the set of nodes successfully found.
31441 tree.set("selectedNodes", array.map(
31442 array.filter(newNodes,function(x){return x[0];}),
31443 function(x){return x[1];}));
a089699c 31444 }
1354d172 31445 },
a089699c 31446
1354d172
AD
31447 _setSelectedNodeAttr: function(node){
31448 this.set('selectedNodes', [node]);
31449 },
31450 _setSelectedNodesAttr: function(nodes){
31451 this._loadDeferred.addCallback( lang.hitch(this, function(){
31452 this.dndController.setSelection(nodes);
31453 }));
31454 },
31455
31456
31457 ////////////// Data store related functions //////////////////////
31458 // These just get passed to the model; they are here for back-compat
31459
31460 mayHaveChildren: function(/*dojo.data.Item*/ /*===== item =====*/){
31461 // summary:
31462 // Deprecated. This should be specified on the model itself.
a089699c 31463 //
1354d172
AD
31464 // Overridable function to tell if an item has or may have children.
31465 // Controls whether or not +/- expando icon is shown.
31466 // (For efficiency reasons we may not want to check if an element actually
31467 // has children until user clicks the expando node)
31468 // tags:
31469 // deprecated
31470 },
31471
31472 getItemChildren: function(/*===== parentItem, onComplete =====*/){
31473 // summary:
31474 // Deprecated. This should be specified on the model itself.
a089699c 31475 //
1354d172
AD
31476 // Overridable function that return array of child items of given parent item,
31477 // or if parentItem==null then return top items in tree
31478 // tags:
31479 // deprecated
a089699c
AD
31480 },
31481
1354d172
AD
31482 ///////////////////////////////////////////////////////
31483 // Functions for converting an item to a TreeNode
31484 getLabel: function(/*dojo.data.Item*/ item){
31485 // summary:
31486 // Overridable function to get the label for a tree node (given the item)
31487 // tags:
31488 // extension
31489 return this.model.getLabel(item); // String
a089699c
AD
31490 },
31491
1354d172
AD
31492 getIconClass: function(/*dojo.data.Item*/ item, /*Boolean*/ opened){
31493 // summary:
31494 // Overridable function to return CSS class name to display icon
31495 // tags:
31496 // extension
31497 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
a089699c
AD
31498 },
31499
1354d172
AD
31500 getLabelClass: function(/*===== item, opened =====*/){
31501 // summary:
31502 // Overridable function to return CSS class name to display label
31503 // item: dojo.data.Item
31504 // opened: Boolean
31505 // returns: String
31506 // CSS class name
31507 // tags:
31508 // extension
31509 },
a089699c 31510
1354d172
AD
31511 getRowClass: function(/*===== item, opened =====*/){
31512 // summary:
31513 // Overridable function to return CSS class name to display row
31514 // item: dojo.data.Item
31515 // opened: Boolean
31516 // returns: String
31517 // CSS class name
31518 // tags:
31519 // extension
31520 },
a089699c 31521
1354d172
AD
31522 getIconStyle: function(/*===== item, opened =====*/){
31523 // summary:
31524 // Overridable function to return CSS styles to display icon
31525 // item: dojo.data.Item
31526 // opened: Boolean
31527 // returns: Object
31528 // Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
31529 // tags:
31530 // extension
31531 },
a089699c 31532
1354d172
AD
31533 getLabelStyle: function(/*===== item, opened =====*/){
31534 // summary:
31535 // Overridable function to return CSS styles to display label
31536 // item: dojo.data.Item
31537 // opened: Boolean
31538 // returns:
31539 // Object suitable for input to dojo.style() like {color: "red", background: "green"}
31540 // tags:
31541 // extension
31542 },
31543
31544 getRowStyle: function(/*===== item, opened =====*/){
31545 // summary:
31546 // Overridable function to return CSS styles to display row
31547 // item: dojo.data.Item
31548 // opened: Boolean
31549 // returns:
31550 // Object suitable for input to dojo.style() like {background-color: "#bbb"}
31551 // tags:
31552 // extension
31553 },
a089699c 31554
1354d172
AD
31555 getTooltip: function(/*dojo.data.Item*/ /*===== item =====*/){
31556 // summary:
31557 // Overridable function to get the tooltip for a tree node (given the item)
31558 // tags:
31559 // extension
31560 return ""; // String
31561 },
31562
31563 /////////// Keyboard and Mouse handlers ////////////////////
31564
31565 _onKeyPress: function(/*Event*/ e){
31566 // summary:
31567 // Translates keypress events into commands for the controller
31568 if(e.altKey){ return; }
31569 var treeNode = registry.getEnclosingWidget(e.target);
31570 if(!treeNode){ return; }
31571
31572 var key = e.charOrCode;
31573 if(typeof key == "string" && key != " "){ // handle printables (letter navigation)
31574 // Check for key navigation.
31575 if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
31576 this._onLetterKeyNav( { node: treeNode, key: key.toLowerCase() } );
31577 event.stop(e);
81bea17a 31578 }
1354d172
AD
31579 }else{ // handle non-printables (arrow keys)
31580 // clear record of recent printables (being saved for multi-char letter navigation),
31581 // because "a", down-arrow, "b" shouldn't search for "ab"
31582 if(this._curSearch){
31583 clearTimeout(this._curSearch.timer);
31584 delete this._curSearch;
31585 }
31586
31587 var map = this._keyHandlerMap;
31588 if(!map){
31589 // setup table mapping keys to events
31590 map = {};
31591 map[keys.ENTER]="_onEnterKey";
31592 //On WebKit based browsers, the combination ctrl-enter
31593 //does not get passed through. To allow accessible
31594 //multi-select on those browsers, the space key is
31595 //also used for selection.
31596 map[keys.SPACE]= map[" "] = "_onEnterKey";
31597 map[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW]="_onLeftArrow";
31598 map[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW]="_onRightArrow";
31599 map[keys.UP_ARROW]="_onUpArrow";
31600 map[keys.DOWN_ARROW]="_onDownArrow";
31601 map[keys.HOME]="_onHomeKey";
31602 map[keys.END]="_onEndKey";
31603 this._keyHandlerMap = map;
31604 }
31605 if(this._keyHandlerMap[key]){
31606 this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
31607 event.stop(e);
a089699c
AD
31608 }
31609 }
31610 },
31611
1354d172
AD
31612 _onEnterKey: function(/*Object*/ message){
31613 this._publish("execute", { item: message.item, node: message.node } );
31614 this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
31615 this.onClick(message.item, message.node, message.evt);
a089699c
AD
31616 },
31617
1354d172
AD
31618 _onDownArrow: function(/*Object*/ message){
31619 // summary:
31620 // down arrow pressed; get next visible node, set focus there
31621 var node = this._getNextNode(message.node);
31622 if(node && node.isTreeNode){
31623 this.focusNode(node);
a089699c
AD
31624 }
31625 },
a089699c 31626
1354d172
AD
31627 _onUpArrow: function(/*Object*/ message){
31628 // summary:
31629 // Up arrow pressed; move to previous visible node
a089699c 31630
1354d172 31631 var node = message.node;
a089699c 31632
1354d172
AD
31633 // if younger siblings
31634 var previousSibling = node.getPreviousSibling();
31635 if(previousSibling){
31636 node = previousSibling;
31637 // if the previous node is expanded, dive in deep
31638 while(node.isExpandable && node.isExpanded && node.hasChildren()){
31639 // move to the last child
31640 var children = node.getChildren();
31641 node = children[children.length-1];
31642 }
31643 }else{
31644 // if this is the first child, return the parent
31645 // unless the parent is the root of a tree with a hidden root
31646 var parent = node.getParent();
31647 if(!(!this.showRoot && parent === this.rootNode)){
31648 node = parent;
31649 }
31650 }
a089699c 31651
1354d172
AD
31652 if(node && node.isTreeNode){
31653 this.focusNode(node);
31654 }
31655 },
a089699c 31656
1354d172
AD
31657 _onRightArrow: function(/*Object*/ message){
31658 // summary:
31659 // Right arrow pressed; go to child node
31660 var node = message.node;
a089699c 31661
1354d172
AD
31662 // if not expanded, expand, else move to 1st child
31663 if(node.isExpandable && !node.isExpanded){
31664 this._expandNode(node);
31665 }else if(node.hasChildren()){
31666 node = node.getChildren()[0];
31667 if(node && node.isTreeNode){
31668 this.focusNode(node);
31669 }
31670 }
31671 },
81bea17a 31672
1354d172
AD
31673 _onLeftArrow: function(/*Object*/ message){
31674 // summary:
31675 // Left arrow pressed.
31676 // If not collapsed, collapse, else move to parent.
a089699c 31677
1354d172 31678 var node = message.node;
a089699c 31679
1354d172
AD
31680 if(node.isExpandable && node.isExpanded){
31681 this._collapseNode(node);
31682 }else{
31683 var parent = node.getParent();
31684 if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
31685 this.focusNode(parent);
31686 }
a089699c 31687 }
1354d172 31688 },
a089699c 31689
1354d172
AD
31690 _onHomeKey: function(){
31691 // summary:
31692 // Home key pressed; get first visible node, and set focus there
31693 var node = this._getRootOrFirstNode();
31694 if(node){
31695 this.focusNode(node);
31696 }
a089699c
AD
31697 },
31698
1354d172
AD
31699 _onEndKey: function(){
31700 // summary:
31701 // End key pressed; go to last visible node.
a089699c 31702
1354d172
AD
31703 var node = this.rootNode;
31704 while(node.isExpanded){
31705 var c = node.getChildren();
31706 node = c[c.length - 1];
a089699c 31707 }
a089699c 31708
1354d172
AD
31709 if(node && node.isTreeNode){
31710 this.focusNode(node);
31711 }
a089699c 31712 },
a089699c 31713
1354d172
AD
31714 // multiCharSearchDuration: Number
31715 // If multiple characters are typed where each keystroke happens within
31716 // multiCharSearchDuration of the previous keystroke,
31717 // search for nodes matching all the keystrokes.
31718 //
31719 // For example, typing "ab" will search for entries starting with
31720 // "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
31721 multiCharSearchDuration: 250,
a089699c 31722
1354d172
AD
31723 _onLetterKeyNav: function(message){
31724 // summary:
31725 // Called when user presses a prinatable key; search for node starting with recently typed letters.
31726 // message: Object
31727 // Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
a089699c 31728
1354d172
AD
31729 // Branch depending on whether this key starts a new search, or modifies an existing search
31730 var cs = this._curSearch;
31731 if(cs){
31732 // We are continuing a search. Ex: user has pressed 'a', and now has pressed
31733 // 'b', so we want to search for nodes starting w/"ab".
31734 cs.pattern = cs.pattern + message.key;
31735 clearTimeout(cs.timer);
a089699c 31736 }else{
1354d172
AD
31737 // We are starting a new search
31738 cs = this._curSearch = {
31739 pattern: message.key,
31740 startNode: message.node
31741 };
a089699c 31742 }
a089699c 31743
1354d172
AD
31744 // set/reset timer to forget recent keystrokes
31745 var self = this;
31746 cs.timer = setTimeout(function(){
31747 delete self._curSearch;
31748 }, this.multiCharSearchDuration);
a089699c 31749
1354d172
AD
31750 // Navigate to TreeNode matching keystrokes [entered so far].
31751 var node = cs.startNode;
31752 do{
31753 node = this._getNextNode(node);
31754 //check for last node, jump to first node if necessary
31755 if(!node){
31756 node = this._getRootOrFirstNode();
a089699c 31757 }
1354d172
AD
31758 }while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
31759 if(node && node.isTreeNode){
31760 // no need to set focus if back where we started
31761 if(node !== cs.startNode){
31762 this.focusNode(node);
a089699c
AD
31763 }
31764 }
a089699c 31765 },
a089699c 31766
1354d172
AD
31767 isExpandoNode: function(node, widget){
31768 // summary:
31769 // check whether a dom node is the expandoNode for a particular TreeNode widget
31770 return dom.isDescendant(node, widget.expandoNode);
31771 },
31772 _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
31773 // summary:
31774 // Translates click events into commands for the controller to process
a089699c 31775
1354d172
AD
31776 var domElement = e.target,
31777 isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
a089699c 31778
1354d172
AD
31779 if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
31780 // expando node was clicked, or label of a folder node was clicked; open it
31781 if(nodeWidget.isExpandable){
31782 this._onExpandoClick({node:nodeWidget});
a089699c 31783 }
1354d172
AD
31784 }else{
31785 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
31786 this.onClick(nodeWidget.item, nodeWidget, e);
31787 this.focusNode(nodeWidget);
a089699c 31788 }
1354d172
AD
31789 event.stop(e);
31790 },
31791 _onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
31792 // summary:
31793 // Translates double-click events into commands for the controller to process
a089699c 31794
1354d172
AD
31795 var domElement = e.target,
31796 isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
a089699c 31797
1354d172
AD
31798 if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
31799 // expando node was clicked, or label of a folder node was clicked; open it
31800 if(nodeWidget.isExpandable){
31801 this._onExpandoClick({node:nodeWidget});
31802 }
31803 }else{
31804 this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
31805 this.onDblClick(nodeWidget.item, nodeWidget, e);
31806 this.focusNode(nodeWidget);
a089699c 31807 }
1354d172
AD
31808 event.stop(e);
31809 },
31810
31811 _onExpandoClick: function(/*Object*/ message){
31812 // summary:
31813 // User clicked the +/- icon; expand or collapse my children.
31814 var node = message.node;
31815
31816 // If we are collapsing, we might be hiding the currently focused node.
31817 // Also, clicking the expando node might have erased focus from the current node.
31818 // For simplicity's sake just focus on the node with the expando.
31819 this.focusNode(node);
31820
31821 if(node.isExpanded){
31822 this._collapseNode(node);
31823 }else{
31824 this._expandNode(node);
a089699c 31825 }
a089699c
AD
31826 },
31827
1354d172
AD
31828 onClick: function(/*===== item, node, evt =====*/){
31829 // summary:
31830 // Callback when a tree node is clicked
31831 // item: dojo.data.Item
31832 // node: TreeNode
31833 // evt: Event
31834 // tags:
31835 // callback
a089699c 31836 },
1354d172
AD
31837 onDblClick: function(/*===== item, node, evt =====*/){
31838 // summary:
31839 // Callback when a tree node is double-clicked
31840 // item: dojo.data.Item
31841 // node: TreeNode
31842 // evt: Event
31843 // tags:
31844 // callback
a089699c 31845 },
1354d172
AD
31846 onOpen: function(/*===== item, node =====*/){
31847 // summary:
31848 // Callback when a node is opened
31849 // item: dojo.data.Item
31850 // node: TreeNode
31851 // tags:
31852 // callback
31853 },
31854 onClose: function(/*===== item, node =====*/){
31855 // summary:
31856 // Callback when a node is closed
31857 // item: dojo.data.Item
31858 // node: TreeNode
31859 // tags:
31860 // callback
a089699c 31861 },
a089699c 31862
1354d172
AD
31863 _getNextNode: function(node){
31864 // summary:
31865 // Get next visible node
a089699c 31866
1354d172
AD
31867 if(node.isExpandable && node.isExpanded && node.hasChildren()){
31868 // if this is an expanded node, get the first child
31869 return node.getChildren()[0]; // _TreeNode
31870 }else{
31871 // find a parent node with a sibling
31872 while(node && node.isTreeNode){
31873 var returnNode = node.getNextSibling();
31874 if(returnNode){
31875 return returnNode; // _TreeNode
a089699c 31876 }
1354d172 31877 node = node.getParent();
a089699c 31878 }
1354d172 31879 return null;
a089699c 31880 }
1354d172 31881 },
a089699c 31882
1354d172
AD
31883 _getRootOrFirstNode: function(){
31884 // summary:
31885 // Get first visible node
31886 return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
31887 },
31888
31889 _collapseNode: function(/*_TreeNode*/ node){
31890 // summary:
31891 // Called when the user has requested to collapse the node
31892
31893 if(node._expandNodeDeferred){
31894 delete node._expandNodeDeferred;
31895 }
31896
31897 if(node.isExpandable){
31898 if(node.state == "LOADING"){
31899 // ignore clicks while we are in the process of loading data
31900 return;
a089699c
AD
31901 }
31902
1354d172
AD
31903 node.collapse();
31904 this.onClose(node.item, node);
31905
31906 this._state(node, false);
31907 }
31908 },
31909
31910 _expandNode: function(/*_TreeNode*/ node, /*Boolean?*/ recursive){
31911 // summary:
31912 // Called when the user has requested to expand the node
31913 // recursive:
31914 // Internal flag used when _expandNode() calls itself, don't set.
31915 // returns:
31916 // Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
31917 // that were previously opened too
31918
31919 if(node._expandNodeDeferred && !recursive){
31920 // there's already an expand in progress (or completed), so just return
31921 return node._expandNodeDeferred; // dojo.Deferred
31922 }
31923
31924 var model = this.model,
31925 item = node.item,
31926 _this = this;
31927
31928 switch(node.state){
31929 case "UNCHECKED":
31930 // need to load all the children, and then expand
31931 node.markProcessing();
31932
31933 // Setup deferred to signal when the load and expand are finished.
31934 // Save that deferred in this._expandDeferred as a flag that operation is in progress.
31935 var def = (node._expandNodeDeferred = new Deferred());
31936
31937 // Get the children
31938 model.getChildren(
31939 item,
31940 function(items){
31941 node.unmarkProcessing();
31942
31943 // Display the children and also start expanding any children that were previously expanded
31944 // (if this.persist == true). The returned Deferred will fire when those expansions finish.
31945 var scid = node.setChildItems(items);
31946
31947 // Call _expandNode() again but this time it will just to do the animation (default branch).
31948 // The returned Deferred will fire when the animation completes.
31949 // TODO: seems like I can avoid recursion and just use a deferred to sequence the events?
31950 var ed = _this._expandNode(node, true);
31951
31952 // After the above two tasks (setChildItems() and recursive _expandNode()) finish,
31953 // signal that I am done.
31954 scid.addCallback(function(){
31955 ed.addCallback(function(){
31956 def.callback();
31957 })
31958 });
31959 },
31960 function(err){
31961 console.error(_this, ": error loading root children: ", err);
a089699c 31962 }
1354d172
AD
31963 );
31964 break;
a089699c 31965
1354d172
AD
31966 default: // "LOADED"
31967 // data is already loaded; just expand node
31968 def = (node._expandNodeDeferred = node.expand());
31969
31970 this.onOpen(node.item, node);
31971
31972 this._state(node, true);
a089699c 31973 }
1354d172
AD
31974
31975 return def; // dojo.Deferred
a089699c
AD
31976 },
31977
1354d172 31978 ////////////////// Miscellaneous functions ////////////////
a089699c 31979
1354d172
AD
31980 focusNode: function(/* _tree.Node */ node){
31981 // summary:
31982 // Focus on the specified node (which must be visible)
31983 // tags:
31984 // protected
31985
31986 // set focus so that the label will be voiced using screen readers
31987 focus.focus(node.labelNode);
a089699c
AD
31988 },
31989
1354d172
AD
31990 _onNodeFocus: function(/*dijit._Widget*/ node){
31991 // summary:
31992 // Called when a TreeNode gets focus, either by user clicking
31993 // it, or programatically by arrow key handling code.
31994 // description:
31995 // It marks that the current node is the selected one, and the previously
31996 // selected node no longer is.
31997
31998 if(node && node != this.lastFocused){
31999 if(this.lastFocused && !this.lastFocused._destroyed){
32000 // mark that the previously focsable node is no longer focusable
32001 this.lastFocused.setFocusable(false);
a089699c 32002 }
1354d172
AD
32003
32004 // mark that the new node is the currently selected one
32005 node.setFocusable(true);
32006 this.lastFocused = node;
a089699c
AD
32007 }
32008 },
32009
1354d172
AD
32010 _onNodeMouseEnter: function(/*dijit._Widget*/ /*===== node =====*/){
32011 // summary:
32012 // Called when mouse is over a node (onmouseenter event),
32013 // this is monitored by the DND code
a089699c 32014 },
1354d172
AD
32015
32016 _onNodeMouseLeave: function(/*dijit._Widget*/ /*===== node =====*/){
32017 // summary:
32018 // Called when mouse leaves a node (onmouseleave event),
32019 // this is monitored by the DND code
a089699c 32020 },
1354d172
AD
32021
32022 //////////////// Events from the model //////////////////////////
32023
32024 _onItemChange: function(/*Item*/ item){
32025 // summary:
32026 // Processes notification of a change to an item's scalar values like label
32027 var model = this.model,
32028 identity = model.getIdentity(item),
32029 nodes = this._itemNodesMap[identity];
32030
32031 if(nodes){
32032 var label = this.getLabel(item),
32033 tooltip = this.getTooltip(item);
32034 array.forEach(nodes, function(node){
32035 node.set({
32036 item: item, // theoretically could be new JS Object representing same item
32037 label: label,
32038 tooltip: tooltip
32039 });
32040 node._updateItemClasses(item);
32041 });
a089699c
AD
32042 }
32043 },
1354d172
AD
32044
32045 _onItemChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){
81bea17a 32046 // summary:
1354d172
AD
32047 // Processes notification of a change to an item's children
32048 var model = this.model,
32049 identity = model.getIdentity(parent),
32050 parentNodes = this._itemNodesMap[identity];
32051
32052 if(parentNodes){
32053 array.forEach(parentNodes,function(parentNode){
32054 parentNode.setChildItems(newChildrenList);
32055 });
a089699c 32056 }
a089699c
AD
32057 },
32058
1354d172
AD
32059 _onItemDelete: function(/*Item*/ item){
32060 // summary:
32061 // Processes notification of a deletion of an item
32062 var model = this.model,
32063 identity = model.getIdentity(item),
32064 nodes = this._itemNodesMap[identity];
32065
32066 if(nodes){
32067 array.forEach(nodes,function(node){
32068 // Remove node from set of selected nodes (if it's selected)
32069 this.dndController.removeTreeNode(node);
32070
32071 var parent = node.getParent();
32072 if(parent){
32073 // if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
32074 parent.removeChild(node);
32075 }
32076 node.destroyRecursive();
32077 }, this);
32078 delete this._itemNodesMap[identity];
a089699c 32079 }
a089699c 32080 },
a089699c 32081
1354d172
AD
32082 /////////////// Miscellaneous funcs
32083
32084 _initState: function(){
32085 // summary:
32086 // Load in which nodes should be opened automatically
32087 this._openedNodes = {};
32088 if(this.persist && this.cookieName){
32089 var oreo = cookie(this.cookieName);
32090 if(oreo){
32091 array.forEach(oreo.split(','), function(item){
32092 this._openedNodes[item] = true;
32093 }, this);
a089699c 32094 }
a089699c
AD
32095 }
32096 },
1354d172
AD
32097 _state: function(node, expanded){
32098 // summary:
32099 // Query or set expanded state for an node
32100 if(!this.persist){
32101 return false;
32102 }
32103 var path = array.map(node.getTreePath(), function(item){
32104 return this.model.getIdentity(item);
32105 }, this).join("/");
32106 if(arguments.length === 1){
32107 return this._openedNodes[path];
32108 }else{
32109 if(expanded){
32110 this._openedNodes[path] = true;
a089699c 32111 }else{
1354d172 32112 delete this._openedNodes[path];
a089699c 32113 }
1354d172
AD
32114 var ary = [];
32115 for(var id in this._openedNodes){
32116 ary.push(id);
a089699c 32117 }
1354d172 32118 cookie(this.cookieName, ary.join(","), {expires:365});
a089699c 32119 }
1354d172 32120 },
a089699c 32121
1354d172
AD
32122 destroy: function(){
32123 if(this._curSearch){
32124 clearTimeout(this._curSearch.timer);
32125 delete this._curSearch;
a089699c 32126 }
1354d172
AD
32127 if(this.rootNode){
32128 this.rootNode.destroyRecursive();
a089699c 32129 }
1354d172
AD
32130 if(this.dndController && !lang.isString(this.dndController)){
32131 this.dndController.destroy();
a089699c 32132 }
1354d172
AD
32133 this.rootNode = null;
32134 this.inherited(arguments);
32135 },
a089699c 32136
1354d172
AD
32137 destroyRecursive: function(){
32138 // A tree is treated as a leaf, not as a node with children (like a grid),
32139 // but defining destroyRecursive for back-compat.
32140 this.destroy();
a089699c 32141 },
1354d172
AD
32142
32143 resize: function(changeSize){
32144 if(changeSize){
32145 domGeometry.setMarginBox(this.domNode, changeSize);
a089699c 32146 }
a089699c 32147
1354d172
AD
32148 // The only JS sizing involved w/tree is the indentation, which is specified
32149 // in CSS and read in through this dummy indentDetector node (tree must be
32150 // visible and attached to the DOM to read this)
32151 this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w;
a089699c 32152
1354d172
AD
32153 if(this.tree.rootNode){
32154 // If tree has already loaded, then reset indent for all the nodes
32155 this.tree.rootNode.set('indent', this.showRoot ? 0 : -1);
32156 }
a089699c
AD
32157 },
32158
1354d172
AD
32159 _createTreeNode: function(/*Object*/ args){
32160 // summary:
32161 // creates a TreeNode
32162 // description:
32163 // Developers can override this method to define their own TreeNode class;
32164 // However it will probably be removed in a future release in favor of a way
32165 // of just specifying a widget for the label, rather than one that contains
32166 // the children too.
32167 return new TreeNode(args);
a089699c
AD
32168 },
32169
1354d172
AD
32170 _setTextDirAttr: function(textDir){
32171 if(textDir && this.textDir!= textDir){
32172 this._set("textDir",textDir);
32173 this.rootNode.set("textDir", textDir);
32174 }
32175 }
32176});
a089699c 32177
1354d172 32178Tree._TreeNode = TreeNode; // for monkey patching
a089699c 32179
1354d172 32180return Tree;
a089699c
AD
32181});
32182
1354d172
AD
32183},
32184'dijit/form/_FormValueWidget':function(){
32185define("dijit/form/_FormValueWidget", [
32186 "dojo/_base/declare", // declare
32187 "dojo/_base/sniff", // has("ie")
32188 "./_FormWidget",
32189 "./_FormValueMixin"
32190], function(declare, has, _FormWidget, _FormValueMixin){
32191
32192/*=====
32193var _FormWidget = dijit.form._FormWidget;
32194var _FormValueMixin = dijit.form._FormValueMixin;
32195=====*/
32196
32197// module:
32198// dijit/form/_FormValueWidget
32199// summary:
32200// FormValueWidget
32201
32202
32203return declare("dijit.form._FormValueWidget", [_FormWidget, _FormValueMixin],
32204{
32205 // summary:
32206 // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values.
32207 // description:
32208 // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element,
32209 // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?)
32210 // works as expected.
32211
32212 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
32213 // directly in the template as read by the parser in order to function. IE is known to specifically
32214 // require the 'name' attribute at element creation time. See #8484, #8660.
32215
32216 _layoutHackIE7: function(){
32217 // summary:
32218 // Work around table sizing bugs on IE7 by forcing redraw
32219
32220 if(has("ie") == 7){ // fix IE7 layout bug when the widget is scrolled out of sight
32221 var domNode = this.domNode;
32222 var parent = domNode.parentNode;
32223 var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter
32224 var origFilter = pingNode.style.filter; // save custom filter, most likely nothing
32225 var _this = this;
32226 while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet
32227 (function ping(){
32228 var disconnectHandle = _this.connect(parent, "onscroll",
32229 function(){
32230 _this.disconnect(disconnectHandle); // only call once
32231 pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
32232 setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any
32233 }
32234 );
32235 })();
32236 parent = parent.parentNode;
32237 }
32238 }
32239 }
32240});
a089699c 32241
1354d172 32242});
a089699c 32243
1354d172
AD
32244},
32245'*now':function(r){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de-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"]']);}
32246}});
32247define("dojo/tt-rss-layer", [], 1);