]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | require({cache:{ |
2 | 'dijit/form/TextBox':function(){ | |
3 | require({cache:{ | |
4 | 'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}}); | |
5 | define("dijit/form/TextBox", [ | |
6 | "dojo/_base/declare", // declare | |
7 | "dojo/dom-construct", // domConstruct.create | |
8 | "dojo/dom-style", // domStyle.getComputedStyle | |
9 | "dojo/_base/kernel", // kernel.deprecated | |
10 | "dojo/_base/lang", // lang.hitch | |
11 | "dojo/_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(){ | |
181 | define("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(){ | |
201 | define("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,"""); //TODO: add &? 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(){ | |
508 | define("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 |
523 | return 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(){ |
779 | define("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(){ | |
892 | require({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=\"\">▼</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">◀</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>", | |
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>"}}); | |
895 | define("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 | /*===== |
921 | var _WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin; | |
922 | var Menu = dijit.Menu; | |
923 | var _HasDropDown = dijit._HasDropDown; | |
924 | var 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 |
935 | var 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 |
1332 | var 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 | /*===== | |
1346 | ScrollingTabControllerButtonMixin = dijit.layout._ScrollingTabControllerButtonMixin; | |
1347 | =====*/ | |
a089699c | 1348 | |
1354d172 AD |
1349 | // Class used in template |
1350 | declare("dijit.layout._ScrollingTabControllerButton", | |
1351 | [Button, ScrollingTabControllerButtonMixin]); | |
a089699c | 1352 | |
1354d172 AD |
1353 | // Class used in template |
1354 | declare( | |
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 |
1404 | return ScrollingTabController; |
1405 | }); | |
a089699c | 1406 | |
1354d172 AD |
1407 | }, |
1408 | 'dijit/place':function(){ | |
1409 | define("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(){ | |
1781 | define("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(){ | |
2286 | define("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(){ | |
2672 | define("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 |
2698 | return 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(){ | |
3066 | define("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(){ | |
3458 | define("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(){ | |
3975 | define("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(){ | |
4030 | define("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 | /*===== |
4039 | var 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 |
4048 | return 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(){ | |
4319 | define("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 |
4333 | return 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 = " "; // | |
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(){ | |
4513 | define( | |
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 | 4523 | new Date("X"); // workaround for #11279, new Date("") == NaN |
a089699c | 4524 | |
1354d172 AD |
4525 | var 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 | }; | |
4530 | function has(feature){ | |
4531 | return features[feature]; | |
4532 | } | |
a089699c | 4533 | |
a089699c | 4534 | |
1354d172 AD |
4535 | dojo.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. | |
5101 | if(dojo.config.parseOnLoad){ | |
5102 | dojo.ready(100, dojo.parser, "parse"); | |
5103 | } | |
a089699c | 5104 | |
1354d172 AD |
5105 | return 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\">▼</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(){ | |
5111 | define("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 |
5118 | var 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. | |
5311 | dojo.dnd._manager = null; | |
a089699c | 5312 | |
1354d172 | 5313 | Manager.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 |
5322 | return Manager; |
5323 | }); | |
81bea17a | 5324 | |
1354d172 AD |
5325 | }, |
5326 | 'dijit/form/ToggleButton':function(){ | |
5327 | define("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(){ | |
5363 | define("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 | 5369 | lang.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 |
5373 | dojo.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 |
5466 | dojo.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 |
5507 | return dojo.date.stamp; |
5508 | }); | |
a089699c | 5509 | |
1354d172 AD |
5510 | }, |
5511 | 'dojo/Stateful':function(){ | |
5512 | define("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 |
5518 | return 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(){ | |
5654 | require({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"}}); | |
5656 | define("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(){ | |
6212 | define("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, "&").replace(/</gm, "<") | |
6926 | .replace(/>/gm, ">").replace(/"/gm, """); //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(){ | |
6982 | define("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, """) + "'" : "") + "/>", 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(){ | |
7074 | require({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=\"▼ \" 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=\"Χ \" 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"}}); | |
7076 | define("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(){ | |
7225 | define("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 |
7240 | var _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 |
7631 | return _TextBoxMixin; |
7632 | }); | |
a089699c | 7633 | |
1354d172 AD |
7634 | }, |
7635 | 'dijit/form/SimpleTextarea':function(){ | |
7636 | define("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 | 7655 | return 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(){ | |
7740 | define("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(){ | |
7756 | define("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(){ | |
7782 | define("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(){ | |
7795 | define("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(){ | |
7923 | require({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"}}); | |
7925 | define("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 | ||
7965 | var 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 |
8173 | var 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;'>    ✍    </span>" : | |
8257 | "<span style='text-decoration: underline;'>    ✍    </span>", // //   == | |
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, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """).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 | 8546 | InlineEditBox._InlineEditor = InlineEditor; // for monkey patching |
a089699c | 8547 | |
1354d172 AD |
8548 | return InlineEditBox; |
8549 | }); | |
8550 | }, | |
8551 | 'dojo/selector/acme':function(){ | |
8552 | define("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(){ | |
10035 | define("dojo/dnd/autoscroll", ["../main", "../window"], function(dojo) { | |
10036 | // module: | |
10037 | // dojo/dnd/autoscroll | |
10038 | // summary: | |
10039 | // TODOC | |
81bea17a | 10040 | |
1354d172 | 10041 | dojo.getObject("dnd", true, dojo); |
81bea17a | 10042 | |
1354d172 | 10043 | dojo.dnd.getViewport = dojo.window.getBox; |
81bea17a | 10044 | |
1354d172 AD |
10045 | dojo.dnd.V_TRIGGER_AUTOSCROLL = 32; |
10046 | dojo.dnd.H_TRIGGER_AUTOSCROLL = 32; | |
81bea17a | 10047 | |
1354d172 AD |
10048 | dojo.dnd.V_AUTOSCROLL_VALUE = 16; |
10049 | dojo.dnd.H_AUTOSCROLL_VALUE = 16; | |
81bea17a | 10050 | |
1354d172 AD |
10051 | dojo.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 |
10073 | dojo.dnd._validNodes = {"div": 1, "p": 1, "td": 1}; |
10074 | dojo.dnd._validOverflow = {"auto": 1, "scroll": 1}; | |
81bea17a | 10075 | |
1354d172 | 10076 | dojo.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(){ | |
10156 | define("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; =====*/ |
10165 | return 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(){ | |
10964 | define("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(){ | |
11038 | define("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(){ | |
11110 | define("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 | /*===== |
11118 | dojo["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: |
11124 | NodeList = dojo.NodeList; | |
11125 | =====*/ | |
a089699c | 11126 | |
1354d172 AD |
11127 | lang.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 |
11330 | return NodeList; |
11331 | }); | |
a089699c | 11332 | |
1354d172 AD |
11333 | }, |
11334 | 'dijit/form/_ListMouseMixin':function(){ | |
11335 | define("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 | /*===== |
11343 | var _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 | ||
11351 | return 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(){ | |
11435 | define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo, regexp) { | |
11436 | // module: | |
11437 | // dojo/cookie | |
11438 | // summary: | |
11439 | // TODOC | |
a089699c | 11440 | |
a089699c | 11441 | |
1354d172 AD |
11442 | /*===== |
11443 | dojo.__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 |
11463 | dojo.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 |
11514 | dojo.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 |
11531 | return dojo.cookie; |
11532 | }); | |
a089699c | 11533 | |
1354d172 AD |
11534 | }, |
11535 | 'dojo/cache':function(){ | |
11536 | define("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=\"▼ \" 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=\"Χ \" 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(){ | |
11549 | require({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\"> </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"}}); | |
11551 | define("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 |
11574 | return 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 ? " " : 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(){ | |
11729 | define("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) | |
11742 | var origCreateWrapper = popup._createWrapper; | |
11743 | popup._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 |
11757 | var origOpen = popup.open; | |
11758 | popup.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 |
11777 | return popup; |
11778 | }); | |
a089699c | 11779 | |
1354d172 AD |
11780 | }, |
11781 | 'dijit/ColorPalette':function(){ | |
11782 | require({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"}}); | |
11784 | define("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 |
11813 | var 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 |
11880 | ColorPalette._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 |
11940 | return 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\">●</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(){ | |
11946 | define("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(){ | |
12060 | define("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 | /*===== |
12213 | dojo.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(){ | |
12277 | define("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(){ | |
12346 | require({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"}}); | |
12348 | define("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(){ | |
12848 | define("dojo/string", ["./_base/kernel", "./_base/lang"], function(dojo, lang) { | |
12849 | // module: | |
12850 | // dojo/string | |
12851 | // summary: | |
12852 | // TODOC | |
81bea17a | 12853 | |
1354d172 | 12854 | lang.getObject("string", true, dojo); |
81bea17a | 12855 | |
1354d172 AD |
12856 | /*===== |
12857 | dojo.string = { | |
12858 | // summary: String utilities for Dojo | |
12859 | }; | |
12860 | =====*/ | |
a089699c | 12861 | |
1354d172 AD |
12862 | dojo.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 |
12883 | dojo.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 | ||
12908 | dojo.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 | /*===== | |
12980 | dojo.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 | ||
12995 | dojo.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 |
13008 | return 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(){ | |
13014 | define("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(){ | |
13038 | require({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\">▼</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"}}); | |
13040 | define("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 | 13064 | return 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(){ | |
13148 | define("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(){ | |
13246 | define("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 |
13265 | return 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\"> </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(){ | |
13477 | define("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 | /*===== |
13494 | var _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 |
13505 | return 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(){ | |
13735 | define("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(){ | |
13967 | define("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 | /*===== |
13975 | dojo.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 |
13995 | dojo.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 |
14138 | return dojo.dnd.Moveable; |
14139 | }); | |
a089699c | 14140 | |
1354d172 AD |
14141 | }, |
14142 | 'dojo/store/util/SimpleQueryEngine':function(){ | |
14143 | define("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 | ||
14149 | return 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(){ | |
14254 | define("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 |
14273 | var 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 | 14457 | return typematic; |
a089699c | 14458 | |
1354d172 | 14459 | }); |
a089699c | 14460 | |
1354d172 AD |
14461 | }, |
14462 | 'dijit/MenuItem':function(){ | |
14463 | require({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"}}); | |
14465 | define("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(){ | |
14677 | require({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"}}); | |
14679 | define("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(){ | |
14852 | define("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(){ | |
15052 | define("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(){ | |
15434 | define("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(){ | |
15513 | define("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(){ | |
15872 | define("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 |
15879 | dojo.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 |
15989 | return dojo.dnd.Mover; |
15990 | }); | |
a089699c | 15991 | |
1354d172 AD |
15992 | }, |
15993 | 'dijit/layout/TabContainer':function(){ | |
15994 | define("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(){ | |
16076 | define("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(){ | |
16193 | define("dojo/dnd/Avatar", ["../main", "./common"], function(dojo) { | |
16194 | // module: | |
16195 | // dojo/dnd/Avatar | |
16196 | // summary: | |
16197 | // TODOC | |
a089699c | 16198 | |
a089699c | 16199 | |
1354d172 AD |
16200 | dojo.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 |
16302 | return dojo.dnd.Avatar; |
16303 | }); | |
a089699c | 16304 | |
1354d172 AD |
16305 | }, |
16306 | 'dijit/form/Button':function(){ | |
16307 | require({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\">●</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"}}); | |
16309 | define("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 |
16332 | if(!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 |
16339 | return 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(){ | |
16437 | define("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 | /*===== |
16445 | dojo.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 |
16457 | dojo.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 | /*===== |
16502 | dojo.declare("dojo.dnd.move.__boxConstrainedMoveableArgs", [dojo.dnd.move.__constrainedMoveableArgs], { | |
16503 | // box: Object | |
16504 | // a constraint box | |
16505 | box: {} | |
16506 | }); | |
16507 | =====*/ | |
a089699c | 16508 | |
1354d172 AD |
16509 | dojo.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 | /*===== |
16527 | dojo.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 |
16535 | dojo.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 |
16574 | dojo.dnd.constrainedMover = dojo.dnd.move.constrainedMover; |
16575 | dojo.dnd.boxConstrainedMover = dojo.dnd.move.boxConstrainedMover; | |
16576 | dojo.dnd.parentConstrainedMover = dojo.dnd.move.parentConstrainedMover; | |
a089699c | 16577 | |
1354d172 AD |
16578 | return dojo.dnd.move; |
16579 | }); | |
a089699c | 16580 | |
1354d172 AD |
16581 | }, |
16582 | 'dijit/_WidgetBase':function(){ | |
16583 | define("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 | /*===== |
16609 | var 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. |
16618 | if(!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": { ... | |
16627 | var tagAttrs = {}; | |
16628 | function 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 |
16636 | function 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 |
16647 | return 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(){ | |
17606 | define("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(){ | |
17781 | require({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"}}); | |
17783 | define("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 |
17807 | return 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(){ | |
17939 | define("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 | |
17946 | return 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(){ | |
18104 | define("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(){ | |
18113 | define("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(){ | |
18167 | define("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 | /*===== |
18184 | var _WidgetBase = dijit._WidgetBase; | |
18185 | var _LayoutWidget = dijit.layout._LayoutWidget; | |
18186 | var 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 |
18195 | if(!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.) | |
18205 | lang.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 |
18229 | return 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(){ | |
18538 | define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang) { | |
18539 | // module: | |
18540 | // dojo/regexp | |
18541 | // summary: | |
18542 | // TODOC | |
a089699c | 18543 | |
1354d172 | 18544 | lang.getObject("regexp", true, dojo); |
a089699c | 18545 | |
1354d172 AD |
18546 | /*===== |
18547 | dojo.regexp = { | |
18548 | // summary: Regular expressions and Builder resources | |
18549 | }; | |
18550 | =====*/ | |
a089699c | 18551 | |
1354d172 AD |
18552 | dojo.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 |
18566 | dojo.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 |
18600 | dojo.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 |
18609 | return dojo.regexp; |
18610 | }); | |
a089699c | 18611 | |
1354d172 AD |
18612 | }, |
18613 | 'dijit/form/_FormMixin':function(){ | |
18614 | define("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(){ | |
19092 | require({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"}}); | |
19094 | define("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(){ | |
19159 | define("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 | 19166 | var simpleFetch = lang.getObject("dojo.data.util.simpleFetch", true); |
81bea17a | 19167 | |
1354d172 AD |
19168 | simpleFetch.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 |
19252 | return simpleFetch; |
19253 | }); | |
a089699c | 19254 | |
1354d172 AD |
19255 | }, |
19256 | 'dijit/Menu':function(){ | |
19257 | define("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 |
19289 | if(!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 |
19296 | return 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(){ | |
19581 | define("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(){ | |
19661 | define("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 |
19692 | return 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=\"Χ \" 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(){ | |
20277 | define("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(){ | |
20421 | define("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(){ | |
20486 | define("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(){ | |
20753 | define("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(){ | |
20820 | require({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"}}); | |
20822 | define("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(){ | |
20943 | define("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(){ | |
21272 | define("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(){ | |
21382 | define("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 |
21391 | var 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. |
22323 | lang.extend(ItemFileReadStore,simpleFetch); | |
a089699c | 22324 | |
1354d172 AD |
22325 | return ItemFileReadStore; |
22326 | }); | |
a089699c | 22327 | |
1354d172 AD |
22328 | }, |
22329 | 'dojo/html':function(){ | |
22330 | define("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(){ | |
22675 | define("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 |
22697 | return 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 | /*===== |
22988 | declare("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(){ | |
23023 | require({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=\"Χ \" 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"}}); | |
23025 | define("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(){ | |
23322 | define("dijit/_base/typematic", ["../typematic"], function(){ | |
23323 | // for back-compat, just loads top level module | |
23324 | }); | |
a089699c | 23325 | |
1354d172 AD |
23326 | }, |
23327 | 'dijit/_base':function(){ | |
23328 | define("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(){ | |
23353 | define("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 |
23387 | var _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 |
23577 | var _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 | 23601 | var 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.) | |
23873 | lang.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 |
23904 | BorderContainer._Splitter = _Splitter; | |
23905 | BorderContainer._Gutter = _Gutter; | |
a089699c | 23906 | |
1354d172 AD |
23907 | return BorderContainer; |
23908 | }); | |
a089699c | 23909 | |
1354d172 AD |
23910 | }, |
23911 | 'dojo/window':function(){ | |
23912 | define("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 | 23920 | var window = lang.getObject("dojo.window", true); |
a089699c | 23921 | |
1354d172 AD |
23922 | /*===== |
23923 | dojo.window = { | |
23924 | // summary: | |
23925 | // TODO | |
23926 | }; | |
23927 | window = dojo.window; | |
23928 | =====*/ | |
a089699c | 23929 | |
1354d172 AD |
23930 | window.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 | 23959 | window.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 | ||
23983 | window.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 |
24079 | return window; |
24080 | }); | |
24081 | ||
24082 | }, | |
24083 | 'dojo/number':function(){ | |
24084 | define("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 | 24092 | lang.getObject("number", true, dojo); |
a089699c | 24093 | |
1354d172 AD |
24094 | /*===== |
24095 | dojo.number = { | |
24096 | // summary: localized formatting and parsing routines for Number | |
81bea17a | 24097 | } |
a089699c | 24098 | |
1354d172 AD |
24099 | dojo.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 |
24126 | dojo.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 |
24148 | dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough | |
a089699c | 24149 | |
1354d172 AD |
24150 | dojo.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 |
24199 | dojo.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 |
24225 | if((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 | /*===== |
24239 | dojo.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 |
24256 | dojo.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 | /*===== |
24342 | dojo.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 | =====*/ | |
24365 | dojo.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 |
24374 | dojo.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 | /*===== |
24463 | dojo.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 | =====*/ | |
24486 | dojo.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 | /*===== |
24523 | dojo.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 |
24551 | dojo.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 | /*===== |
24596 | dojo.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 |
24616 | dojo.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 |
24658 | return dojo.number; |
24659 | }); | |
a089699c | 24660 | |
1354d172 AD |
24661 | }, |
24662 | 'dijit/_FocusMixin':function(){ | |
24663 | define("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(){ | |
24739 | define("dojo/data/util/filter", ["dojo/_base/lang"], function(lang) { | |
24740 | // module: | |
24741 | // dojo/data/util/filter | |
24742 | // summary: | |
24743 | // TODOC | |
a089699c | 24744 | |
1354d172 | 24745 | var filter = lang.getObject("dojo.data.util.filter", true); |
a089699c | 24746 | |
1354d172 AD |
24747 | filter.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 |
24811 | return filter; |
24812 | }); | |
a089699c | 24813 | |
1354d172 AD |
24814 | }, |
24815 | 'dijit/_WidgetsInTemplateMixin':function(){ | |
24816 | define("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(){ | |
24880 | define("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 | 24887 | return 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(){ | |
24986 | define("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(){ | |
25229 | define("dojo/data/util/sorter", ["dojo/_base/lang"], function(lang) { | |
25230 | // module: | |
25231 | // dojo/data/util/sorter | |
25232 | // summary: | |
25233 | // TODOC | |
a089699c | 25234 | |
1354d172 | 25235 | var sorter = lang.getObject("dojo.data.util.sorter", true); |
a089699c | 25236 | |
1354d172 AD |
25237 | sorter.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 |
25265 | sorter.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 |
25326 | return sorter; |
25327 | }); | |
a089699c | 25328 | |
1354d172 AD |
25329 | }, |
25330 | 'dijit/form/_ButtonMixin':function(){ | |
25331 | define("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 |
25343 | return 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(){ | |
25419 | define("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(){ | |
25654 | define("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(){ | |
25831 | define("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(){ | |
25985 | define("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(){ | |
26093 | define("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 | /*===== |
26118 | dijit.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 |
26135 | return 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(){ | |
26691 | require({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\">▼</div\n\t\t></td\n\t></tr></tbody\n></table>\n"}}); | |
26693 | define("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 |
26731 | var _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 |
26782 | var 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: " ", // | |
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: " "}); | |
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 | ||
27034 | Select._Menu = _SelectMenu; // for monkey patching | |
27035 | ||
27036 | return Select; | |
27037 | }); | |
27038 | ||
27039 | }, | |
27040 | 'dojo/store/util/QueryResults':function(){ | |
27041 | define("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 | ||
27048 | var util = lang.getObject("dojo.store.util", true); | |
27049 | ||
27050 | util.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 |
27103 | return util.QueryResults; |
27104 | }); | |
81bea17a | 27105 | |
1354d172 AD |
27106 | }, |
27107 | 'dijit/form/_ListBase':function(){ | |
27108 | define("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 |
27118 | return 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(){ | |
27234 | define("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 | /*===== |
27245 | var _Widget = dijit._Widget; | |
27246 | var _TemplatedMixin = dijit._TemplatedMixin; | |
27247 | var _CssStateMixin = dijit._CssStateMixin; | |
27248 | var _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 |
27258 | if(!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 |
27265 | return 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, """) + '"') : ''; | |
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(){ | |
27316 | define("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 |
27323 | dojo.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 | }; | |
27377 | dojo.DeferredList.prototype = new Deferred(); | |
a089699c | 27378 | |
1354d172 AD |
27379 | dojo.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 |
27400 | return dojo.DeferredList; |
27401 | }); | |
a089699c | 27402 | |
1354d172 AD |
27403 | }, |
27404 | 'dojo/dnd/common':function(){ | |
27405 | define("dojo/dnd/common", ["../main"], function(dojo) { | |
27406 | // module: | |
27407 | // dojo/dnd/common | |
27408 | // summary: | |
27409 | // TODOC | |
a089699c | 27410 | |
1354d172 | 27411 | dojo.getObject("dnd", true, dojo); |
a089699c | 27412 | |
1354d172 | 27413 | dojo.dnd.getCopyKeyState = dojo.isCopyKey; |
a089699c | 27414 | |
1354d172 AD |
27415 | dojo.dnd._uniqueId = 0; |
27416 | dojo.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 | 27426 | dojo.dnd._empty = {}; |
a089699c | 27427 | |
1354d172 AD |
27428 | dojo.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 |
27438 | return dojo.dnd; |
27439 | }); | |
a089699c | 27440 | |
1354d172 AD |
27441 | }, |
27442 | 'dijit/_base/place':function(){ | |
27443 | define("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(){ | |
27583 | require({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>"}}); | |
27585 | define("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(){ | |
27629 | define("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=\"\">▼</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">◀</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">▶</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>", | |
27770 | 'dijit/Dialog':function(){ | |
27771 | require({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"}}); | |
27773 | define("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(){ | |
28417 | define("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(){ | |
28739 | define("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 | /*===== |
28759 | dijit.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 |
28782 | return 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(){ | |
29306 | define("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(){ | |
29486 | define("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 | ||
29496 | return 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(){ | |
29540 | define("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 | ||
29571 | function 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() | |
29578 | function 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 | } | |
29586 | aspect.around(connect, "connect", aroundAdvice); | |
29587 | if(kernel.connect){ | |
29588 | aspect.around(kernel, "connect", aroundAdvice); | |
29589 | } | |
29590 | ||
29591 | var _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. | |
29883 | if(!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 | } | |
29889 | return _Widget; | |
29890 | }); | |
29891 | ||
29892 | }, | |
29893 | 'dojo/touch':function(){ | |
29894 | define("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\">▼</div\n\t\t></td\n\t></tr></tbody\n></table>\n", | |
29985 | 'dojo/fx':function(){ | |
29986 | define("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. | |
30014 | if(!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(){ | |
30415 | define("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(){ | |
30490 | require({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"}}); | |
30493 | define("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 | ||
30542 | var 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 | ||
31020 | var 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 | 32178 | Tree._TreeNode = TreeNode; // for monkey patching |
a089699c | 32179 | |
1354d172 | 32180 | return Tree; |
a089699c AD |
32181 | }); |
32182 | ||
1354d172 AD |
32183 | }, |
32184 | 'dijit/form/_FormValueWidget':function(){ | |
32185 | define("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 | /*===== | |
32193 | var _FormWidget = dijit.form._FormWidget; | |
32194 | var _FormValueMixin = dijit.form._FormValueMixin; | |
32195 | =====*/ | |
32196 | ||
32197 | // module: | |
32198 | // dijit/form/_FormValueWidget | |
32199 | // summary: | |
32200 | // FormValueWidget | |
32201 | ||
32202 | ||
32203 | return 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 | }}); | |
32247 | define("dojo/tt-rss-layer", [], 1); |