]>
Commit | Line | Data |
---|---|---|
2f01fe57 AD |
1 | /* |
2 | Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. | |
3 | Available via Academic Free License >= 2.1 OR the modified BSD license. | |
4 | see: http://dojotoolkit.org/license for details | |
5 | */ | |
6 | ||
7 | /* | |
8 | This is an optimized version of Dojo, built for deployment and not for | |
9 | development. To get sources and documentation, please visit: | |
10 | ||
11 | http://dojotoolkit.org | |
12 | */ | |
13 | ||
14 | if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
15 | dojo._hasResource["dojo.window"] = true; | |
16 | dojo.provide("dojo.window"); | |
17 | ||
18 | dojo.window.getBox = function(){ | |
19 | // summary: | |
20 | // Returns the dimensions and scroll position of the viewable area of a browser window | |
21 | ||
22 | var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement; | |
23 | ||
24 | // get scroll position | |
25 | var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work | |
26 | return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y }; | |
27 | }; | |
28 | ||
29 | dojo.window.get = function(doc){ | |
30 | // summary: | |
31 | // Get window object associated with document doc | |
32 | ||
33 | // In some IE versions (at least 6.0), document.parentWindow does not return a | |
34 | // reference to the real window object (maybe a copy), so we must fix it as well | |
35 | // We use IE specific execScript to attach the real window reference to | |
36 | // document._parentWindow for later use | |
37 | if(dojo.isIE && window !== document.parentWindow){ | |
38 | /* | |
39 | In IE 6, only the variable "window" can be used to connect events (others | |
40 | may be only copies). | |
41 | */ | |
42 | doc.parentWindow.execScript("document._parentWindow = window;", "Javascript"); | |
43 | //to prevent memory leak, unset it after use | |
44 | //another possibility is to add an onUnload handler which seems overkill to me (liucougar) | |
45 | var win = doc._parentWindow; | |
46 | doc._parentWindow = null; | |
47 | return win; // Window | |
48 | } | |
49 | ||
50 | return doc.parentWindow || doc.defaultView; // Window | |
51 | }; | |
52 | ||
53 | dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){ | |
54 | // summary: | |
55 | // Scroll the passed node into view, if it is not already. | |
56 | ||
57 | // don't rely on node.scrollIntoView working just because the function is there | |
58 | ||
59 | try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method | |
60 | node = dojo.byId(node); | |
61 | var doc = node.ownerDocument || dojo.doc, | |
62 | body = doc.body || dojo.body(), | |
63 | html = doc.documentElement || body.parentNode, | |
64 | isIE = dojo.isIE, isWK = dojo.isWebKit; | |
65 | // if an untested browser, then use the native method | |
66 | if((!(dojo.isMoz || isIE || isWK || dojo.isOpera) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){ | |
67 | node.scrollIntoView(false); // short-circuit to native if possible | |
68 | return; | |
69 | } | |
70 | var backCompat = doc.compatMode == 'BackCompat', | |
71 | clientAreaRoot = backCompat? body : html, | |
72 | scrollRoot = isWK ? body : clientAreaRoot, | |
73 | rootWidth = clientAreaRoot.clientWidth, | |
74 | rootHeight = clientAreaRoot.clientHeight, | |
75 | rtl = !dojo._isBodyLtr(), | |
76 | nodePos = pos || dojo.position(node), | |
77 | el = node.parentNode, | |
78 | isFixed = function(el){ | |
79 | return ((isIE <= 6 || (isIE && backCompat))? false : (dojo.style(el, 'position').toLowerCase() == "fixed")); | |
80 | }; | |
81 | if(isFixed(node)){ return; } // nothing to do | |
82 | ||
83 | while(el){ | |
84 | if(el == body){ el = scrollRoot; } | |
85 | var elPos = dojo.position(el), | |
86 | fixedPos = isFixed(el); | |
87 | ||
88 | if(el == scrollRoot){ | |
89 | elPos.w = rootWidth; elPos.h = rootHeight; | |
90 | if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x | |
91 | if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0 | |
92 | if(elPos.y < 0 || !isIE){ elPos.y = 0; } | |
93 | }else{ | |
94 | var pb = dojo._getPadBorderExtents(el); | |
95 | elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t; | |
96 | } | |
97 | ||
98 | if(el != scrollRoot){ // body, html sizes already have the scrollbar removed | |
99 | var clientSize = el.clientWidth, | |
100 | scrollBarSize = elPos.w - clientSize; | |
101 | if(clientSize > 0 && scrollBarSize > 0){ | |
102 | elPos.w = clientSize; | |
103 | if(isIE && rtl){ elPos.x += scrollBarSize; } | |
104 | } | |
105 | clientSize = el.clientHeight; | |
106 | scrollBarSize = elPos.h - clientSize; | |
107 | if(clientSize > 0 && scrollBarSize > 0){ | |
108 | elPos.h = clientSize; | |
109 | } | |
110 | } | |
111 | if(fixedPos){ // bounded by viewport, not parents | |
112 | if(elPos.y < 0){ | |
113 | elPos.h += elPos.y; elPos.y = 0; | |
114 | } | |
115 | if(elPos.x < 0){ | |
116 | elPos.w += elPos.x; elPos.x = 0; | |
117 | } | |
118 | if(elPos.y + elPos.h > rootHeight){ | |
119 | elPos.h = rootHeight - elPos.y; | |
120 | } | |
121 | if(elPos.x + elPos.w > rootWidth){ | |
122 | elPos.w = rootWidth - elPos.x; | |
123 | } | |
124 | } | |
125 | // calculate overflow in all 4 directions | |
126 | var l = nodePos.x - elPos.x, // beyond left: < 0 | |
127 | t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0 | |
128 | r = l + nodePos.w - elPos.w, // beyond right: > 0 | |
129 | bot = t + nodePos.h - elPos.h; // beyond bottom: > 0 | |
130 | if(r * l > 0){ | |
131 | var s = Math[l < 0? "max" : "min"](l, r); | |
132 | nodePos.x += el.scrollLeft; | |
133 | el.scrollLeft += (isIE >= 8 && !backCompat && rtl)? -s : s; | |
134 | nodePos.x -= el.scrollLeft; | |
135 | } | |
136 | if(bot * t > 0){ | |
137 | nodePos.y += el.scrollTop; | |
138 | el.scrollTop += Math[t < 0? "max" : "min"](t, bot); | |
139 | nodePos.y -= el.scrollTop; | |
140 | } | |
141 | el = (el != scrollRoot) && !fixedPos && el.parentNode; | |
142 | } | |
143 | }catch(error){ | |
144 | console.error('scrollIntoView: ' + error); | |
145 | node.scrollIntoView(false); | |
146 | } | |
147 | }; | |
148 | ||
149 | } | |
150 | ||
151 | if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
152 | dojo._hasResource["dijit._base.manager"] = true; | |
153 | dojo.provide("dijit._base.manager"); | |
154 | ||
155 | dojo.declare("dijit.WidgetSet", null, { | |
156 | // summary: | |
157 | // A set of widgets indexed by id. A default instance of this class is | |
158 | // available as `dijit.registry` | |
159 | // | |
160 | // example: | |
161 | // Create a small list of widgets: | |
162 | // | var ws = new dijit.WidgetSet(); | |
163 | // | ws.add(dijit.byId("one")); | |
164 | // | ws.add(dijit.byId("two")); | |
165 | // | // destroy both: | |
166 | // | ws.forEach(function(w){ w.destroy(); }); | |
167 | // | |
168 | // example: | |
169 | // Using dijit.registry: | |
170 | // | dijit.registry.forEach(function(w){ /* do something */ }); | |
171 | ||
172 | constructor: function(){ | |
173 | this._hash = {}; | |
174 | this.length = 0; | |
175 | }, | |
176 | ||
177 | add: function(/*dijit._Widget*/ widget){ | |
178 | // summary: | |
179 | // Add a widget to this list. If a duplicate ID is detected, a error is thrown. | |
180 | // | |
181 | // widget: dijit._Widget | |
182 | // Any dijit._Widget subclass. | |
183 | if(this._hash[widget.id]){ | |
184 | throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered"); | |
185 | } | |
186 | this._hash[widget.id] = widget; | |
187 | this.length++; | |
188 | }, | |
189 | ||
190 | remove: function(/*String*/ id){ | |
191 | // summary: | |
192 | // Remove a widget from this WidgetSet. Does not destroy the widget; simply | |
193 | // removes the reference. | |
194 | if(this._hash[id]){ | |
195 | delete this._hash[id]; | |
196 | this.length--; | |
197 | } | |
198 | }, | |
199 | ||
200 | forEach: function(/*Function*/ func, /* Object? */thisObj){ | |
201 | // summary: | |
202 | // Call specified function for each widget in this set. | |
203 | // | |
204 | // func: | |
205 | // A callback function to run for each item. Is passed the widget, the index | |
206 | // in the iteration, and the full hash, similar to `dojo.forEach`. | |
207 | // | |
208 | // thisObj: | |
209 | // An optional scope parameter | |
210 | // | |
211 | // example: | |
212 | // Using the default `dijit.registry` instance: | |
213 | // | dijit.registry.forEach(function(widget){ | |
214 | // | console.log(widget.declaredClass); | |
215 | // | }); | |
216 | // | |
217 | // returns: | |
218 | // Returns self, in order to allow for further chaining. | |
219 | ||
220 | thisObj = thisObj || dojo.global; | |
221 | var i = 0, id; | |
222 | for(id in this._hash){ | |
223 | func.call(thisObj, this._hash[id], i++, this._hash); | |
224 | } | |
225 | return this; // dijit.WidgetSet | |
226 | }, | |
227 | ||
228 | filter: function(/*Function*/ filter, /* Object? */thisObj){ | |
229 | // summary: | |
230 | // Filter down this WidgetSet to a smaller new WidgetSet | |
231 | // Works the same as `dojo.filter` and `dojo.NodeList.filter` | |
232 | // | |
233 | // filter: | |
234 | // Callback function to test truthiness. Is passed the widget | |
235 | // reference and the pseudo-index in the object. | |
236 | // | |
237 | // thisObj: Object? | |
238 | // Option scope to use for the filter function. | |
239 | // | |
240 | // example: | |
241 | // Arbitrary: select the odd widgets in this list | |
242 | // | dijit.registry.filter(function(w, i){ | |
243 | // | return i % 2 == 0; | |
244 | // | }).forEach(function(w){ /* odd ones */ }); | |
245 | ||
246 | thisObj = thisObj || dojo.global; | |
247 | var res = new dijit.WidgetSet(), i = 0, id; | |
248 | for(id in this._hash){ | |
249 | var w = this._hash[id]; | |
250 | if(filter.call(thisObj, w, i++, this._hash)){ | |
251 | res.add(w); | |
252 | } | |
253 | } | |
254 | return res; // dijit.WidgetSet | |
255 | }, | |
256 | ||
257 | byId: function(/*String*/ id){ | |
258 | // summary: | |
259 | // Find a widget in this list by it's id. | |
260 | // example: | |
261 | // Test if an id is in a particular WidgetSet | |
262 | // | var ws = new dijit.WidgetSet(); | |
263 | // | ws.add(dijit.byId("bar")); | |
264 | // | var t = ws.byId("bar") // returns a widget | |
265 | // | var x = ws.byId("foo"); // returns undefined | |
266 | ||
267 | return this._hash[id]; // dijit._Widget | |
268 | }, | |
269 | ||
270 | byClass: function(/*String*/ cls){ | |
271 | // summary: | |
272 | // Reduce this widgetset to a new WidgetSet of a particular `declaredClass` | |
273 | // | |
274 | // cls: String | |
275 | // The Class to scan for. Full dot-notated string. | |
276 | // | |
277 | // example: | |
278 | // Find all `dijit.TitlePane`s in a page: | |
279 | // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); }); | |
280 | ||
281 | var res = new dijit.WidgetSet(), id, widget; | |
282 | for(id in this._hash){ | |
283 | widget = this._hash[id]; | |
284 | if(widget.declaredClass == cls){ | |
285 | res.add(widget); | |
286 | } | |
287 | } | |
288 | return res; // dijit.WidgetSet | |
289 | }, | |
290 | ||
291 | toArray: function(){ | |
292 | // summary: | |
293 | // Convert this WidgetSet into a true Array | |
294 | // | |
295 | // example: | |
296 | // Work with the widget .domNodes in a real Array | |
297 | // | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; }); | |
298 | ||
299 | var ar = []; | |
300 | for(var id in this._hash){ | |
301 | ar.push(this._hash[id]); | |
302 | } | |
303 | return ar; // dijit._Widget[] | |
304 | }, | |
305 | ||
306 | map: function(/* Function */func, /* Object? */thisObj){ | |
307 | // summary: | |
308 | // Create a new Array from this WidgetSet, following the same rules as `dojo.map` | |
309 | // example: | |
310 | // | var nodes = dijit.registry.map(function(w){ return w.domNode; }); | |
311 | // | |
312 | // returns: | |
313 | // A new array of the returned values. | |
314 | return dojo.map(this.toArray(), func, thisObj); // Array | |
315 | }, | |
316 | ||
317 | every: function(func, thisObj){ | |
318 | // summary: | |
319 | // A synthetic clone of `dojo.every` acting explicitly on this WidgetSet | |
320 | // | |
321 | // func: Function | |
322 | // A callback function run for every widget in this list. Exits loop | |
323 | // when the first false return is encountered. | |
324 | // | |
325 | // thisObj: Object? | |
326 | // Optional scope parameter to use for the callback | |
327 | ||
328 | thisObj = thisObj || dojo.global; | |
329 | var x = 0, i; | |
330 | for(i in this._hash){ | |
331 | if(!func.call(thisObj, this._hash[i], x++, this._hash)){ | |
332 | return false; // Boolean | |
333 | } | |
334 | } | |
335 | return true; // Boolean | |
336 | }, | |
337 | ||
338 | some: function(func, thisObj){ | |
339 | // summary: | |
340 | // A synthetic clone of `dojo.some` acting explictly on this WidgetSet | |
341 | // | |
342 | // func: Function | |
343 | // A callback function run for every widget in this list. Exits loop | |
344 | // when the first true return is encountered. | |
345 | // | |
346 | // thisObj: Object? | |
347 | // Optional scope parameter to use for the callback | |
348 | ||
349 | thisObj = thisObj || dojo.global; | |
350 | var x = 0, i; | |
351 | for(i in this._hash){ | |
352 | if(func.call(thisObj, this._hash[i], x++, this._hash)){ | |
353 | return true; // Boolean | |
354 | } | |
355 | } | |
356 | return false; // Boolean | |
357 | } | |
358 | ||
359 | }); | |
360 | ||
361 | (function(){ | |
362 | ||
363 | /*===== | |
364 | dijit.registry = { | |
365 | // summary: | |
366 | // A list of widgets on a page. | |
367 | // description: | |
368 | // Is an instance of `dijit.WidgetSet` | |
369 | }; | |
370 | =====*/ | |
371 | dijit.registry = new dijit.WidgetSet(); | |
372 | ||
373 | var hash = dijit.registry._hash, | |
374 | attr = dojo.attr, | |
375 | hasAttr = dojo.hasAttr, | |
376 | style = dojo.style; | |
377 | ||
378 | dijit.byId = function(/*String|dijit._Widget*/ id){ | |
379 | // summary: | |
380 | // Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId()) | |
381 | return typeof id == "string" ? hash[id] : id; // dijit._Widget | |
382 | }; | |
383 | ||
384 | var _widgetTypeCtr = {}; | |
385 | dijit.getUniqueId = function(/*String*/widgetType){ | |
386 | // summary: | |
387 | // Generates a unique id for a given widgetType | |
388 | ||
389 | var id; | |
390 | do{ | |
391 | id = widgetType + "_" + | |
392 | (widgetType in _widgetTypeCtr ? | |
393 | ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0); | |
394 | }while(hash[id]); | |
395 | return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String | |
396 | }; | |
397 | ||
398 | dijit.findWidgets = function(/*DomNode*/ root){ | |
399 | // summary: | |
400 | // Search subtree under root returning widgets found. | |
401 | // Doesn't search for nested widgets (ie, widgets inside other widgets). | |
402 | ||
403 | var outAry = []; | |
404 | ||
405 | function getChildrenHelper(root){ | |
406 | for(var node = root.firstChild; node; node = node.nextSibling){ | |
407 | if(node.nodeType == 1){ | |
408 | var widgetId = node.getAttribute("widgetId"); | |
409 | if(widgetId){ | |
410 | outAry.push(hash[widgetId]); | |
411 | }else{ | |
412 | getChildrenHelper(node); | |
413 | } | |
414 | } | |
415 | } | |
416 | } | |
417 | ||
418 | getChildrenHelper(root); | |
419 | return outAry; | |
420 | }; | |
421 | ||
422 | dijit._destroyAll = function(){ | |
423 | // summary: | |
424 | // Code to destroy all widgets and do other cleanup on page unload | |
425 | ||
426 | // Clean up focus manager lingering references to widgets and nodes | |
427 | dijit._curFocus = null; | |
428 | dijit._prevFocus = null; | |
429 | dijit._activeStack = []; | |
430 | ||
431 | // Destroy all the widgets, top down | |
432 | dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){ | |
433 | // Avoid double destroy of widgets like Menu that are attached to <body> | |
434 | // even though they are logically children of other widgets. | |
435 | if(!widget._destroyed){ | |
436 | if(widget.destroyRecursive){ | |
437 | widget.destroyRecursive(); | |
438 | }else if(widget.destroy){ | |
439 | widget.destroy(); | |
440 | } | |
441 | } | |
442 | }); | |
443 | }; | |
444 | ||
445 | if(dojo.isIE){ | |
446 | // Only run _destroyAll() for IE because we think it's only necessary in that case, | |
447 | // and because it causes problems on FF. See bug #3531 for details. | |
448 | dojo.addOnWindowUnload(function(){ | |
449 | dijit._destroyAll(); | |
450 | }); | |
451 | } | |
452 | ||
453 | dijit.byNode = function(/*DOMNode*/ node){ | |
454 | // summary: | |
455 | // Returns the widget corresponding to the given DOMNode | |
456 | return hash[node.getAttribute("widgetId")]; // dijit._Widget | |
457 | }; | |
458 | ||
459 | dijit.getEnclosingWidget = function(/*DOMNode*/ node){ | |
460 | // summary: | |
461 | // Returns the widget whose DOM tree contains the specified DOMNode, or null if | |
462 | // the node is not contained within the DOM tree of any widget | |
463 | while(node){ | |
464 | var id = node.getAttribute && node.getAttribute("widgetId"); | |
465 | if(id){ | |
466 | return hash[id]; | |
467 | } | |
468 | node = node.parentNode; | |
469 | } | |
470 | return null; | |
471 | }; | |
472 | ||
473 | var shown = (dijit._isElementShown = function(/*Element*/ elem){ | |
474 | var s = style(elem); | |
475 | return (s.visibility != "hidden") | |
476 | && (s.visibility != "collapsed") | |
477 | && (s.display != "none") | |
478 | && (attr(elem, "type") != "hidden"); | |
479 | }); | |
480 | ||
481 | dijit.hasDefaultTabStop = function(/*Element*/ elem){ | |
482 | // summary: | |
483 | // Tests if element is tab-navigable even without an explicit tabIndex setting | |
484 | ||
485 | // No explicit tabIndex setting, need to investigate node type | |
486 | switch(elem.nodeName.toLowerCase()){ | |
487 | case "a": | |
488 | // An <a> w/out a tabindex is only navigable if it has an href | |
489 | return hasAttr(elem, "href"); | |
490 | case "area": | |
491 | case "button": | |
492 | case "input": | |
493 | case "object": | |
494 | case "select": | |
495 | case "textarea": | |
496 | // These are navigable by default | |
497 | return true; | |
498 | case "iframe": | |
499 | // If it's an editor <iframe> then it's tab navigable. | |
500 | //TODO: feature detect "designMode" in elem.contentDocument? | |
501 | if(dojo.isMoz){ | |
502 | try{ | |
503 | return elem.contentDocument.designMode == "on"; | |
504 | }catch(err){ | |
505 | return false; | |
506 | } | |
507 | }else if(dojo.isWebKit){ | |
508 | var doc = elem.contentDocument, | |
509 | body = doc && doc.body; | |
510 | return body && body.contentEditable == 'true'; | |
511 | }else{ | |
512 | // contentWindow.document isn't accessible within IE7/8 | |
513 | // if the iframe.src points to a foreign url and this | |
514 | // page contains an element, that could get focus | |
515 | try{ | |
516 | doc = elem.contentWindow.document; | |
517 | body = doc && doc.body; | |
518 | return body && body.firstChild && body.firstChild.contentEditable == 'true'; | |
519 | }catch(e){ | |
520 | return false; | |
521 | } | |
522 | } | |
523 | default: | |
524 | return elem.contentEditable == 'true'; | |
525 | } | |
526 | }; | |
527 | ||
528 | var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){ | |
529 | // summary: | |
530 | // Tests if an element is tab-navigable | |
531 | ||
532 | // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable() | |
533 | if(attr(elem, "disabled")){ | |
534 | return false; | |
535 | }else if(hasAttr(elem, "tabIndex")){ | |
536 | // Explicit tab index setting | |
537 | return attr(elem, "tabIndex") >= 0; // boolean | |
538 | }else{ | |
539 | // No explicit tabIndex setting, so depends on node type | |
540 | return dijit.hasDefaultTabStop(elem); | |
541 | } | |
542 | }); | |
543 | ||
544 | dijit._getTabNavigable = function(/*DOMNode*/ root){ | |
545 | // summary: | |
546 | // Finds descendants of the specified root node. | |
547 | // | |
548 | // description: | |
549 | // Finds the following descendants of the specified root node: | |
550 | // * the first tab-navigable element in document order | |
551 | // without a tabIndex or with tabIndex="0" | |
552 | // * the last tab-navigable element in document order | |
553 | // without a tabIndex or with tabIndex="0" | |
554 | // * the first element in document order with the lowest | |
555 | // positive tabIndex value | |
556 | // * the last element in document order with the highest | |
557 | // positive tabIndex value | |
558 | var first, last, lowest, lowestTabindex, highest, highestTabindex; | |
559 | var walkTree = function(/*DOMNode*/parent){ | |
560 | dojo.query("> *", parent).forEach(function(child){ | |
561 | // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE, | |
562 | // since show() invokes getAttribute("type"), which crash on VML nodes in IE. | |
563 | if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){ | |
564 | return; | |
565 | } | |
566 | ||
567 | if(isTabNavigable(child)){ | |
568 | var tabindex = attr(child, "tabIndex"); | |
569 | if(!hasAttr(child, "tabIndex") || tabindex == 0){ | |
570 | if(!first){ first = child; } | |
571 | last = child; | |
572 | }else if(tabindex > 0){ | |
573 | if(!lowest || tabindex < lowestTabindex){ | |
574 | lowestTabindex = tabindex; | |
575 | lowest = child; | |
576 | } | |
577 | if(!highest || tabindex >= highestTabindex){ | |
578 | highestTabindex = tabindex; | |
579 | highest = child; | |
580 | } | |
581 | } | |
582 | } | |
583 | if(child.nodeName.toUpperCase() != 'SELECT'){ | |
584 | walkTree(child); | |
585 | } | |
586 | }); | |
587 | }; | |
588 | if(shown(root)){ walkTree(root) } | |
589 | return { first: first, last: last, lowest: lowest, highest: highest }; | |
590 | } | |
591 | dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){ | |
592 | // summary: | |
593 | // Finds the descendant of the specified root node | |
594 | // that is first in the tabbing order | |
595 | var elems = dijit._getTabNavigable(dojo.byId(root)); | |
596 | return elems.lowest ? elems.lowest : elems.first; // DomNode | |
597 | }; | |
598 | ||
599 | dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){ | |
600 | // summary: | |
601 | // Finds the descendant of the specified root node | |
602 | // that is last in the tabbing order | |
603 | var elems = dijit._getTabNavigable(dojo.byId(root)); | |
604 | return elems.last ? elems.last : elems.highest; // DomNode | |
605 | }; | |
606 | ||
607 | /*===== | |
608 | dojo.mixin(dijit, { | |
609 | // defaultDuration: Integer | |
610 | // The default animation speed (in ms) to use for all Dijit | |
611 | // transitional animations, unless otherwise specified | |
612 | // on a per-instance basis. Defaults to 200, overrided by | |
613 | // `djConfig.defaultDuration` | |
614 | defaultDuration: 200 | |
615 | }); | |
616 | =====*/ | |
617 | ||
618 | dijit.defaultDuration = dojo.config["defaultDuration"] || 200; | |
619 | ||
620 | })(); | |
621 | ||
622 | } | |
623 | ||
624 | if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
625 | dojo._hasResource["dijit._base.focus"] = true; | |
626 | dojo.provide("dijit._base.focus"); | |
627 | ||
628 | ||
629 | // for dijit.isTabNavigable() | |
630 | ||
631 | // summary: | |
632 | // These functions are used to query or set the focus and selection. | |
633 | // | |
634 | // Also, they trace when widgets become activated/deactivated, | |
635 | // so that the widget can fire _onFocus/_onBlur events. | |
636 | // "Active" here means something similar to "focused", but | |
637 | // "focus" isn't quite the right word because we keep track of | |
638 | // a whole stack of "active" widgets. Example: ComboButton --> Menu --> | |
639 | // MenuItem. The onBlur event for ComboButton doesn't fire due to focusing | |
640 | // on the Menu or a MenuItem, since they are considered part of the | |
641 | // ComboButton widget. It only happens when focus is shifted | |
642 | // somewhere completely different. | |
643 | ||
644 | dojo.mixin(dijit, { | |
645 | // _curFocus: DomNode | |
646 | // Currently focused item on screen | |
647 | _curFocus: null, | |
648 | ||
649 | // _prevFocus: DomNode | |
650 | // Previously focused item on screen | |
651 | _prevFocus: null, | |
652 | ||
653 | isCollapsed: function(){ | |
654 | // summary: | |
655 | // Returns true if there is no text selected | |
656 | return dijit.getBookmark().isCollapsed; | |
657 | }, | |
658 | ||
659 | getBookmark: function(){ | |
660 | // summary: | |
661 | // Retrieves a bookmark that can be used with moveToBookmark to return to the same range | |
662 | var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus; | |
663 | ||
664 | if(dojo.global.getSelection){ | |
665 | //W3C Range API for selections. | |
666 | sel = dojo.global.getSelection(); | |
667 | if(sel){ | |
668 | if(sel.isCollapsed){ | |
669 | tg = cf? cf.tagName : ""; | |
670 | if(tg){ | |
671 | //Create a fake rangelike item to restore selections. | |
672 | tg = tg.toLowerCase(); | |
673 | if(tg == "textarea" || | |
674 | (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){ | |
675 | sel = { | |
676 | start: cf.selectionStart, | |
677 | end: cf.selectionEnd, | |
678 | node: cf, | |
679 | pRange: true | |
680 | }; | |
681 | return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object. | |
682 | } | |
683 | } | |
684 | bm = {isCollapsed:true}; | |
685 | }else{ | |
686 | rg = sel.getRangeAt(0); | |
687 | bm = {isCollapsed: false, mark: rg.cloneRange()}; | |
688 | } | |
689 | } | |
690 | }else if(sel){ | |
691 | // If the current focus was a input of some sort and no selection, don't bother saving | |
692 | // a native bookmark. This is because it causes issues with dialog/page selection restore. | |
693 | // So, we need to create psuedo bookmarks to work with. | |
694 | tg = cf ? cf.tagName : ""; | |
695 | tg = tg.toLowerCase(); | |
696 | if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){ | |
697 | if(sel.type && sel.type.toLowerCase() == "none"){ | |
698 | return { | |
699 | isCollapsed: true, | |
700 | mark: null | |
701 | } | |
702 | }else{ | |
703 | rg = sel.createRange(); | |
704 | return { | |
705 | isCollapsed: rg.text && rg.text.length?false:true, | |
706 | mark: { | |
707 | range: rg, | |
708 | pRange: true | |
709 | } | |
710 | }; | |
711 | } | |
712 | } | |
713 | bm = {}; | |
714 | ||
715 | //'IE' way for selections. | |
716 | try{ | |
717 | // createRange() throws exception when dojo in iframe | |
718 | //and nothing selected, see #9632 | |
719 | rg = sel.createRange(); | |
720 | bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length); | |
721 | }catch(e){ | |
722 | bm.isCollapsed = true; | |
723 | return bm; | |
724 | } | |
725 | if(sel.type.toUpperCase() == 'CONTROL'){ | |
726 | if(rg.length){ | |
727 | bm.mark=[]; | |
728 | var i=0,len=rg.length; | |
729 | while(i<len){ | |
730 | bm.mark.push(rg.item(i++)); | |
731 | } | |
732 | }else{ | |
733 | bm.isCollapsed = true; | |
734 | bm.mark = null; | |
735 | } | |
736 | }else{ | |
737 | bm.mark = rg.getBookmark(); | |
738 | } | |
739 | }else{ | |
740 | console.warn("No idea how to store the current selection for this browser!"); | |
741 | } | |
742 | return bm; // Object | |
743 | }, | |
744 | ||
745 | moveToBookmark: function(/*Object*/bookmark){ | |
746 | // summary: | |
747 | // Moves current selection to a bookmark | |
748 | // bookmark: | |
749 | // This should be a returned object from dijit.getBookmark() | |
750 | ||
751 | var _doc = dojo.doc, | |
752 | mark = bookmark.mark; | |
753 | if(mark){ | |
754 | if(dojo.global.getSelection){ | |
755 | //W3C Rangi API (FF, WebKit, Opera, etc) | |
756 | var sel = dojo.global.getSelection(); | |
757 | if(sel && sel.removeAllRanges){ | |
758 | if(mark.pRange){ | |
759 | var r = mark; | |
760 | var n = r.node; | |
761 | n.selectionStart = r.start; | |
762 | n.selectionEnd = r.end; | |
763 | }else{ | |
764 | sel.removeAllRanges(); | |
765 | sel.addRange(mark); | |
766 | } | |
767 | }else{ | |
768 | console.warn("No idea how to restore selection for this browser!"); | |
769 | } | |
770 | }else if(_doc.selection && mark){ | |
771 | //'IE' way. | |
772 | var rg; | |
773 | if(mark.pRange){ | |
774 | rg = mark.range; | |
775 | }else if(dojo.isArray(mark)){ | |
776 | rg = _doc.body.createControlRange(); | |
777 | //rg.addElement does not have call/apply method, so can not call it directly | |
778 | //rg is not available in "range.addElement(item)", so can't use that either | |
779 | dojo.forEach(mark, function(n){ | |
780 | rg.addElement(n); | |
781 | }); | |
782 | }else{ | |
783 | rg = _doc.body.createTextRange(); | |
784 | rg.moveToBookmark(mark); | |
785 | } | |
786 | rg.select(); | |
787 | } | |
788 | } | |
789 | }, | |
790 | ||
791 | getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){ | |
792 | // summary: | |
793 | // Called as getFocus(), this returns an Object showing the current focus | |
794 | // and selected text. | |
795 | // | |
796 | // Called as getFocus(widget), where widget is a (widget representing) a button | |
797 | // that was just pressed, it returns where focus was before that button | |
798 | // was pressed. (Pressing the button may have either shifted focus to the button, | |
799 | // or removed focus altogether.) In this case the selected text is not returned, | |
800 | // since it can't be accurately determined. | |
801 | // | |
802 | // menu: dijit._Widget or {domNode: DomNode} structure | |
803 | // The button that was just pressed. If focus has disappeared or moved | |
804 | // to this button, returns the previous focus. In this case the bookmark | |
805 | // information is already lost, and null is returned. | |
806 | // | |
807 | // openedForWindow: | |
808 | // iframe in which menu was opened | |
809 | // | |
810 | // returns: | |
811 | // A handle to restore focus/selection, to be passed to `dijit.focus` | |
812 | var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus; | |
813 | return { | |
814 | node: node, | |
815 | bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark), | |
816 | openedForWindow: openedForWindow | |
817 | }; // Object | |
818 | }, | |
819 | ||
820 | focus: function(/*Object || DomNode */ handle){ | |
821 | // summary: | |
822 | // Sets the focused node and the selection according to argument. | |
823 | // To set focus to an iframe's content, pass in the iframe itself. | |
824 | // handle: | |
825 | // object returned by get(), or a DomNode | |
826 | ||
827 | if(!handle){ return; } | |
828 | ||
829 | var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object | |
830 | bookmark = handle.bookmark, | |
831 | openedForWindow = handle.openedForWindow, | |
832 | collapsed = bookmark ? bookmark.isCollapsed : false; | |
833 | ||
834 | // Set the focus | |
835 | // Note that for iframe's we need to use the <iframe> to follow the parentNode chain, | |
836 | // but we need to set focus to iframe.contentWindow | |
837 | if(node){ | |
838 | var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node; | |
839 | if(focusNode && focusNode.focus){ | |
840 | try{ | |
841 | // Gecko throws sometimes if setting focus is impossible, | |
842 | // node not displayed or something like that | |
843 | focusNode.focus(); | |
844 | }catch(e){/*quiet*/} | |
845 | } | |
846 | dijit._onFocusNode(node); | |
847 | } | |
848 | ||
849 | // set the selection | |
850 | // do not need to restore if current selection is not empty | |
851 | // (use keyboard to select a menu item) or if previous selection was collapsed | |
852 | // as it may cause focus shift (Esp in IE). | |
853 | if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){ | |
854 | if(openedForWindow){ | |
855 | openedForWindow.focus(); | |
856 | } | |
857 | try{ | |
858 | dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]); | |
859 | }catch(e2){ | |
860 | /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ | |
861 | } | |
862 | } | |
863 | }, | |
864 | ||
865 | // _activeStack: dijit._Widget[] | |
866 | // List of currently active widgets (focused widget and it's ancestors) | |
867 | _activeStack: [], | |
868 | ||
869 | registerIframe: function(/*DomNode*/ iframe){ | |
870 | // summary: | |
871 | // Registers listeners on the specified iframe so that any click | |
872 | // or focus event on that iframe (or anything in it) is reported | |
873 | // as a focus/click event on the <iframe> itself. | |
874 | // description: | |
875 | // Currently only used by editor. | |
876 | // returns: | |
877 | // Handle to pass to unregisterIframe() | |
878 | return dijit.registerWin(iframe.contentWindow, iframe); | |
879 | }, | |
880 | ||
881 | unregisterIframe: function(/*Object*/ handle){ | |
882 | // summary: | |
883 | // Unregisters listeners on the specified iframe created by registerIframe. | |
884 | // After calling be sure to delete or null out the handle itself. | |
885 | // handle: | |
886 | // Handle returned by registerIframe() | |
887 | ||
888 | dijit.unregisterWin(handle); | |
889 | }, | |
890 | ||
891 | registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){ | |
892 | // summary: | |
893 | // Registers listeners on the specified window (either the main | |
894 | // window or an iframe's window) to detect when the user has clicked somewhere | |
895 | // or focused somewhere. | |
896 | // description: | |
897 | // Users should call registerIframe() instead of this method. | |
898 | // targetWindow: | |
899 | // If specified this is the window associated with the iframe, | |
900 | // i.e. iframe.contentWindow. | |
901 | // effectiveNode: | |
902 | // If specified, report any focus events inside targetWindow as | |
903 | // an event on effectiveNode, rather than on evt.target. | |
904 | // returns: | |
905 | // Handle to pass to unregisterWin() | |
906 | ||
907 | // TODO: make this function private in 2.0; Editor/users should call registerIframe(), | |
908 | ||
909 | var mousedownListener = function(evt){ | |
910 | dijit._justMouseDowned = true; | |
911 | setTimeout(function(){ dijit._justMouseDowned = false; }, 0); | |
912 | ||
913 | // workaround weird IE bug where the click is on an orphaned node | |
914 | // (first time clicking a Select/DropDownButton inside a TooltipDialog) | |
915 | if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){ | |
916 | return; | |
917 | } | |
918 | ||
919 | dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse"); | |
920 | }; | |
921 | //dojo.connect(targetWindow, "onscroll", ???); | |
922 | ||
923 | // Listen for blur and focus events on targetWindow's document. | |
924 | // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble | |
925 | // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers | |
926 | // fire. | |
927 | // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because | |
928 | // (at least for FF) the focus event doesn't fire on <html> or <body>. | |
929 | var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document; | |
930 | if(doc){ | |
931 | if(dojo.isIE){ | |
932 | doc.attachEvent('onmousedown', mousedownListener); | |
933 | var activateListener = function(evt){ | |
934 | // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1, | |
935 | // Should consider those more like a mouse-click than a focus.... | |
936 | if(evt.srcElement.tagName.toLowerCase() != "#document" && | |
937 | dijit.isTabNavigable(evt.srcElement)){ | |
938 | dijit._onFocusNode(effectiveNode || evt.srcElement); | |
939 | }else{ | |
940 | dijit._onTouchNode(effectiveNode || evt.srcElement); | |
941 | } | |
942 | }; | |
943 | doc.attachEvent('onactivate', activateListener); | |
944 | var deactivateListener = function(evt){ | |
945 | dijit._onBlurNode(effectiveNode || evt.srcElement); | |
946 | }; | |
947 | doc.attachEvent('ondeactivate', deactivateListener); | |
948 | ||
949 | return function(){ | |
950 | doc.detachEvent('onmousedown', mousedownListener); | |
951 | doc.detachEvent('onactivate', activateListener); | |
952 | doc.detachEvent('ondeactivate', deactivateListener); | |
953 | doc = null; // prevent memory leak (apparent circular reference via closure) | |
954 | }; | |
955 | }else{ | |
956 | doc.addEventListener('mousedown', mousedownListener, true); | |
957 | var focusListener = function(evt){ | |
958 | dijit._onFocusNode(effectiveNode || evt.target); | |
959 | }; | |
960 | doc.addEventListener('focus', focusListener, true); | |
961 | var blurListener = function(evt){ | |
962 | dijit._onBlurNode(effectiveNode || evt.target); | |
963 | }; | |
964 | doc.addEventListener('blur', blurListener, true); | |
965 | ||
966 | return function(){ | |
967 | doc.removeEventListener('mousedown', mousedownListener, true); | |
968 | doc.removeEventListener('focus', focusListener, true); | |
969 | doc.removeEventListener('blur', blurListener, true); | |
970 | doc = null; // prevent memory leak (apparent circular reference via closure) | |
971 | }; | |
972 | } | |
973 | } | |
974 | }, | |
975 | ||
976 | unregisterWin: function(/*Handle*/ handle){ | |
977 | // summary: | |
978 | // Unregisters listeners on the specified window (either the main | |
979 | // window or an iframe's window) according to handle returned from registerWin(). | |
980 | // After calling be sure to delete or null out the handle itself. | |
981 | ||
982 | // Currently our handle is actually a function | |
983 | handle && handle(); | |
984 | }, | |
985 | ||
986 | _onBlurNode: function(/*DomNode*/ node){ | |
987 | // summary: | |
988 | // Called when focus leaves a node. | |
989 | // Usually ignored, _unless_ it *isn't* follwed by touching another node, | |
990 | // which indicates that we tabbed off the last field on the page, | |
991 | // in which case every widget is marked inactive | |
992 | dijit._prevFocus = dijit._curFocus; | |
993 | dijit._curFocus = null; | |
994 | ||
995 | if(dijit._justMouseDowned){ | |
996 | // the mouse down caused a new widget to be marked as active; this blur event | |
997 | // is coming late, so ignore it. | |
998 | return; | |
999 | } | |
1000 | ||
1001 | // if the blur event isn't followed by a focus event then mark all widgets as inactive. | |
1002 | if(dijit._clearActiveWidgetsTimer){ | |
1003 | clearTimeout(dijit._clearActiveWidgetsTimer); | |
1004 | } | |
1005 | dijit._clearActiveWidgetsTimer = setTimeout(function(){ | |
1006 | delete dijit._clearActiveWidgetsTimer; | |
1007 | dijit._setStack([]); | |
1008 | dijit._prevFocus = null; | |
1009 | }, 100); | |
1010 | }, | |
1011 | ||
1012 | _onTouchNode: function(/*DomNode*/ node, /*String*/ by){ | |
1013 | // summary: | |
1014 | // Callback when node is focused or mouse-downed | |
1015 | // node: | |
1016 | // The node that was touched. | |
1017 | // by: | |
1018 | // "mouse" if the focus/touch was caused by a mouse down event | |
1019 | ||
1020 | // ignore the recent blurNode event | |
1021 | if(dijit._clearActiveWidgetsTimer){ | |
1022 | clearTimeout(dijit._clearActiveWidgetsTimer); | |
1023 | delete dijit._clearActiveWidgetsTimer; | |
1024 | } | |
1025 | ||
1026 | // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem) | |
1027 | var newStack=[]; | |
1028 | try{ | |
1029 | while(node){ | |
1030 | var popupParent = dojo.attr(node, "dijitPopupParent"); | |
1031 | if(popupParent){ | |
1032 | node=dijit.byId(popupParent).domNode; | |
1033 | }else if(node.tagName && node.tagName.toLowerCase() == "body"){ | |
1034 | // is this the root of the document or just the root of an iframe? | |
1035 | if(node === dojo.body()){ | |
1036 | // node is the root of the main document | |
1037 | break; | |
1038 | } | |
1039 | // otherwise, find the iframe this node refers to (can't access it via parentNode, | |
1040 | // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit | |
1041 | node=dojo.window.get(node.ownerDocument).frameElement; | |
1042 | }else{ | |
1043 | // if this node is the root node of a widget, then add widget id to stack, | |
1044 | // except ignore clicks on disabled widgets (actually focusing a disabled widget still works, | |
1045 | // to support MenuItem) | |
1046 | var id = node.getAttribute && node.getAttribute("widgetId"), | |
1047 | widget = id && dijit.byId(id); | |
1048 | if(widget && !(by == "mouse" && widget.get("disabled"))){ | |
1049 | newStack.unshift(id); | |
1050 | } | |
1051 | node=node.parentNode; | |
1052 | } | |
1053 | } | |
1054 | }catch(e){ /* squelch */ } | |
1055 | ||
1056 | dijit._setStack(newStack, by); | |
1057 | }, | |
1058 | ||
1059 | _onFocusNode: function(/*DomNode*/ node){ | |
1060 | // summary: | |
1061 | // Callback when node is focused | |
1062 | ||
1063 | if(!node){ | |
1064 | return; | |
1065 | } | |
1066 | ||
1067 | if(node.nodeType == 9){ | |
1068 | // Ignore focus events on the document itself. This is here so that | |
1069 | // (for example) clicking the up/down arrows of a spinner | |
1070 | // (which don't get focus) won't cause that widget to blur. (FF issue) | |
1071 | return; | |
1072 | } | |
1073 | ||
1074 | dijit._onTouchNode(node); | |
1075 | ||
1076 | if(node == dijit._curFocus){ return; } | |
1077 | if(dijit._curFocus){ | |
1078 | dijit._prevFocus = dijit._curFocus; | |
1079 | } | |
1080 | dijit._curFocus = node; | |
1081 | dojo.publish("focusNode", [node]); | |
1082 | }, | |
1083 | ||
1084 | _setStack: function(/*String[]*/ newStack, /*String*/ by){ | |
1085 | // summary: | |
1086 | // The stack of active widgets has changed. Send out appropriate events and records new stack. | |
1087 | // newStack: | |
1088 | // array of widget id's, starting from the top (outermost) widget | |
1089 | // by: | |
1090 | // "mouse" if the focus/touch was caused by a mouse down event | |
1091 | ||
1092 | var oldStack = dijit._activeStack; | |
1093 | dijit._activeStack = newStack; | |
1094 | ||
1095 | // compare old stack to new stack to see how many elements they have in common | |
1096 | for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){ | |
1097 | if(oldStack[nCommon] != newStack[nCommon]){ | |
1098 | break; | |
1099 | } | |
1100 | } | |
1101 | ||
1102 | var widget; | |
1103 | // for all elements that have gone out of focus, send blur event | |
1104 | for(var i=oldStack.length-1; i>=nCommon; i--){ | |
1105 | widget = dijit.byId(oldStack[i]); | |
1106 | if(widget){ | |
1107 | widget._focused = false; | |
1108 | widget._hasBeenBlurred = true; | |
1109 | if(widget._onBlur){ | |
1110 | widget._onBlur(by); | |
1111 | } | |
1112 | dojo.publish("widgetBlur", [widget, by]); | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | // for all element that have come into focus, send focus event | |
1117 | for(i=nCommon; i<newStack.length; i++){ | |
1118 | widget = dijit.byId(newStack[i]); | |
1119 | if(widget){ | |
1120 | widget._focused = true; | |
1121 | if(widget._onFocus){ | |
1122 | widget._onFocus(by); | |
1123 | } | |
1124 | dojo.publish("widgetFocus", [widget, by]); | |
1125 | } | |
1126 | } | |
1127 | } | |
1128 | }); | |
1129 | ||
1130 | // register top window and all the iframes it contains | |
1131 | dojo.addOnLoad(function(){ | |
1132 | var handle = dijit.registerWin(window); | |
1133 | if(dojo.isIE){ | |
1134 | dojo.addOnWindowUnload(function(){ | |
1135 | dijit.unregisterWin(handle); | |
1136 | handle = null; | |
1137 | }) | |
1138 | } | |
1139 | }); | |
1140 | ||
1141 | } | |
1142 | ||
1143 | if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1144 | dojo._hasResource["dojo.AdapterRegistry"] = true; | |
1145 | dojo.provide("dojo.AdapterRegistry"); | |
1146 | ||
1147 | dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){ | |
1148 | // summary: | |
1149 | // A registry to make contextual calling/searching easier. | |
1150 | // description: | |
1151 | // Objects of this class keep list of arrays in the form [name, check, | |
1152 | // wrap, directReturn] that are used to determine what the contextual | |
1153 | // result of a set of checked arguments is. All check/wrap functions | |
1154 | // in this registry should be of the same arity. | |
1155 | // example: | |
1156 | // | // create a new registry | |
1157 | // | var reg = new dojo.AdapterRegistry(); | |
1158 | // | reg.register("handleString", | |
1159 | // | dojo.isString, | |
1160 | // | function(str){ | |
1161 | // | // do something with the string here | |
1162 | // | } | |
1163 | // | ); | |
1164 | // | reg.register("handleArr", | |
1165 | // | dojo.isArray, | |
1166 | // | function(arr){ | |
1167 | // | // do something with the array here | |
1168 | // | } | |
1169 | // | ); | |
1170 | // | | |
1171 | // | // now we can pass reg.match() *either* an array or a string and | |
1172 | // | // the value we pass will get handled by the right function | |
1173 | // | reg.match("someValue"); // will call the first function | |
1174 | // | reg.match(["someValue"]); // will call the second | |
1175 | ||
1176 | this.pairs = []; | |
1177 | this.returnWrappers = returnWrappers || false; // Boolean | |
1178 | } | |
1179 | ||
1180 | dojo.extend(dojo.AdapterRegistry, { | |
1181 | register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){ | |
1182 | // summary: | |
1183 | // register a check function to determine if the wrap function or | |
1184 | // object gets selected | |
1185 | // name: | |
1186 | // a way to identify this matcher. | |
1187 | // check: | |
1188 | // a function that arguments are passed to from the adapter's | |
1189 | // match() function. The check function should return true if the | |
1190 | // given arguments are appropriate for the wrap function. | |
1191 | // directReturn: | |
1192 | // If directReturn is true, the value passed in for wrap will be | |
1193 | // returned instead of being called. Alternately, the | |
1194 | // AdapterRegistry can be set globally to "return not call" using | |
1195 | // the returnWrappers property. Either way, this behavior allows | |
1196 | // the registry to act as a "search" function instead of a | |
1197 | // function interception library. | |
1198 | // override: | |
1199 | // If override is given and true, the check function will be given | |
1200 | // highest priority. Otherwise, it will be the lowest priority | |
1201 | // adapter. | |
1202 | this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]); | |
1203 | }, | |
1204 | ||
1205 | match: function(/* ... */){ | |
1206 | // summary: | |
1207 | // Find an adapter for the given arguments. If no suitable adapter | |
1208 | // is found, throws an exception. match() accepts any number of | |
1209 | // arguments, all of which are passed to all matching functions | |
1210 | // from the registered pairs. | |
1211 | for(var i = 0; i < this.pairs.length; i++){ | |
1212 | var pair = this.pairs[i]; | |
1213 | if(pair[1].apply(this, arguments)){ | |
1214 | if((pair[3])||(this.returnWrappers)){ | |
1215 | return pair[2]; | |
1216 | }else{ | |
1217 | return pair[2].apply(this, arguments); | |
1218 | } | |
1219 | } | |
1220 | } | |
1221 | throw new Error("No match found"); | |
1222 | }, | |
1223 | ||
1224 | unregister: function(name){ | |
1225 | // summary: Remove a named adapter from the registry | |
1226 | ||
1227 | // FIXME: this is kind of a dumb way to handle this. On a large | |
1228 | // registry this will be slow-ish and we can use the name as a lookup | |
1229 | // should we choose to trade memory for speed. | |
1230 | for(var i = 0; i < this.pairs.length; i++){ | |
1231 | var pair = this.pairs[i]; | |
1232 | if(pair[0] == name){ | |
1233 | this.pairs.splice(i, 1); | |
1234 | return true; | |
1235 | } | |
1236 | } | |
1237 | return false; | |
1238 | } | |
1239 | }); | |
1240 | ||
1241 | } | |
1242 | ||
1243 | if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1244 | dojo._hasResource["dijit._base.place"] = true; | |
1245 | dojo.provide("dijit._base.place"); | |
1246 | ||
1247 | ||
1248 | ||
1249 | ||
1250 | ||
1251 | dijit.getViewport = function(){ | |
1252 | // summary: | |
1253 | // Returns the dimensions and scroll position of the viewable area of a browser window | |
1254 | ||
1255 | return dojo.window.getBox(); | |
1256 | }; | |
1257 | ||
1258 | /*===== | |
1259 | dijit.__Position = function(){ | |
1260 | // x: Integer | |
1261 | // horizontal coordinate in pixels, relative to document body | |
1262 | // y: Integer | |
1263 | // vertical coordinate in pixels, relative to document body | |
1264 | ||
1265 | thix.x = x; | |
1266 | this.y = y; | |
1267 | } | |
1268 | =====*/ | |
1269 | ||
1270 | ||
1271 | dijit.placeOnScreen = function( | |
1272 | /* DomNode */ node, | |
1273 | /* dijit.__Position */ pos, | |
1274 | /* String[] */ corners, | |
1275 | /* dijit.__Position? */ padding){ | |
1276 | // summary: | |
1277 | // Positions one of the node's corners at specified position | |
1278 | // such that node is fully visible in viewport. | |
1279 | // description: | |
1280 | // NOTE: node is assumed to be absolutely or relatively positioned. | |
1281 | // pos: | |
1282 | // Object like {x: 10, y: 20} | |
1283 | // corners: | |
1284 | // Array of Strings representing order to try corners in, like ["TR", "BL"]. | |
1285 | // Possible values are: | |
1286 | // * "BL" - bottom left | |
1287 | // * "BR" - bottom right | |
1288 | // * "TL" - top left | |
1289 | // * "TR" - top right | |
1290 | // padding: | |
1291 | // set padding to put some buffer around the element you want to position. | |
1292 | // example: | |
1293 | // Try to place node's top right corner at (10,20). | |
1294 | // If that makes node go (partially) off screen, then try placing | |
1295 | // bottom left corner at (10,20). | |
1296 | // | placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"]) | |
1297 | ||
1298 | var choices = dojo.map(corners, function(corner){ | |
1299 | var c = { corner: corner, pos: {x:pos.x,y:pos.y} }; | |
1300 | if(padding){ | |
1301 | c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x; | |
1302 | c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y; | |
1303 | } | |
1304 | return c; | |
1305 | }); | |
1306 | ||
1307 | return dijit._place(node, choices); | |
1308 | } | |
1309 | ||
1310 | dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){ | |
1311 | // summary: | |
1312 | // Given a list of spots to put node, put it at the first spot where it fits, | |
1313 | // of if it doesn't fit anywhere then the place with the least overflow | |
1314 | // choices: Array | |
1315 | // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} } | |
1316 | // Above example says to put the top-left corner of the node at (10,20) | |
1317 | // layoutNode: Function(node, aroundNodeCorner, nodeCorner) | |
1318 | // for things like tooltip, they are displayed differently (and have different dimensions) | |
1319 | // based on their orientation relative to the parent. This adjusts the popup based on orientation. | |
1320 | ||
1321 | // get {x: 10, y: 10, w: 100, h:100} type obj representing position of | |
1322 | // viewport over document | |
1323 | var view = dojo.window.getBox(); | |
1324 | ||
1325 | // This won't work if the node is inside a <div style="position: relative">, | |
1326 | // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong | |
1327 | // and also it might get cutoff) | |
1328 | if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){ | |
1329 | dojo.body().appendChild(node); | |
1330 | } | |
1331 | ||
1332 | var best = null; | |
1333 | dojo.some(choices, function(choice){ | |
1334 | var corner = choice.corner; | |
1335 | var pos = choice.pos; | |
1336 | ||
1337 | // configure node to be displayed in given position relative to button | |
1338 | // (need to do this in order to get an accurate size for the node, because | |
1339 | // a tooltips size changes based on position, due to triangle) | |
1340 | if(layoutNode){ | |
1341 | layoutNode(node, choice.aroundCorner, corner); | |
1342 | } | |
1343 | ||
1344 | // get node's size | |
1345 | var style = node.style; | |
1346 | var oldDisplay = style.display; | |
1347 | var oldVis = style.visibility; | |
1348 | style.visibility = "hidden"; | |
1349 | style.display = ""; | |
1350 | var mb = dojo.marginBox(node); | |
1351 | style.display = oldDisplay; | |
1352 | style.visibility = oldVis; | |
1353 | ||
1354 | // coordinates and size of node with specified corner placed at pos, | |
1355 | // and clipped by viewport | |
1356 | var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)), | |
1357 | startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)), | |
1358 | endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x), | |
1359 | endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y), | |
1360 | width = endX - startX, | |
1361 | height = endY - startY, | |
1362 | overflow = (mb.w - width) + (mb.h - height); | |
1363 | ||
1364 | if(best == null || overflow < best.overflow){ | |
1365 | best = { | |
1366 | corner: corner, | |
1367 | aroundCorner: choice.aroundCorner, | |
1368 | x: startX, | |
1369 | y: startY, | |
1370 | w: width, | |
1371 | h: height, | |
1372 | overflow: overflow | |
1373 | }; | |
1374 | } | |
1375 | return !overflow; | |
1376 | }); | |
1377 | ||
1378 | node.style.left = best.x + "px"; | |
1379 | node.style.top = best.y + "px"; | |
1380 | if(best.overflow && layoutNode){ | |
1381 | layoutNode(node, best.aroundCorner, best.corner); | |
1382 | } | |
1383 | return best; | |
1384 | } | |
1385 | ||
1386 | dijit.placeOnScreenAroundNode = function( | |
1387 | /* DomNode */ node, | |
1388 | /* DomNode */ aroundNode, | |
1389 | /* Object */ aroundCorners, | |
1390 | /* Function? */ layoutNode){ | |
1391 | ||
1392 | // summary: | |
1393 | // Position node adjacent or kitty-corner to aroundNode | |
1394 | // such that it's fully visible in viewport. | |
1395 | // | |
1396 | // description: | |
1397 | // Place node such that corner of node touches a corner of | |
1398 | // aroundNode, and that node is fully visible. | |
1399 | // | |
1400 | // aroundCorners: | |
1401 | // Ordered list of pairs of corners to try matching up. | |
1402 | // Each pair of corners is represented as a key/value in the hash, | |
1403 | // where the key corresponds to the aroundNode's corner, and | |
1404 | // the value corresponds to the node's corner: | |
1405 | // | |
1406 | // | { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...} | |
1407 | // | |
1408 | // The following strings are used to represent the four corners: | |
1409 | // * "BL" - bottom left | |
1410 | // * "BR" - bottom right | |
1411 | // * "TL" - top left | |
1412 | // * "TR" - top right | |
1413 | // | |
1414 | // layoutNode: Function(node, aroundNodeCorner, nodeCorner) | |
1415 | // For things like tooltip, they are displayed differently (and have different dimensions) | |
1416 | // based on their orientation relative to the parent. This adjusts the popup based on orientation. | |
1417 | // | |
1418 | // example: | |
1419 | // | dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'}); | |
1420 | // This will try to position node such that node's top-left corner is at the same position | |
1421 | // as the bottom left corner of the aroundNode (ie, put node below | |
1422 | // aroundNode, with left edges aligned). If that fails it will try to put | |
1423 | // the bottom-right corner of node where the top right corner of aroundNode is | |
1424 | // (ie, put node above aroundNode, with right edges aligned) | |
1425 | // | |
1426 | ||
1427 | // get coordinates of aroundNode | |
1428 | aroundNode = dojo.byId(aroundNode); | |
1429 | var oldDisplay = aroundNode.style.display; | |
1430 | aroundNode.style.display=""; | |
1431 | // #3172: use the slightly tighter border box instead of marginBox | |
1432 | var aroundNodePos = dojo.position(aroundNode, true); | |
1433 | aroundNode.style.display=oldDisplay; | |
1434 | ||
1435 | // place the node around the calculated rectangle | |
1436 | return dijit._placeOnScreenAroundRect(node, | |
1437 | aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h, // rectangle | |
1438 | aroundCorners, layoutNode); | |
1439 | }; | |
1440 | ||
1441 | /*===== | |
1442 | dijit.__Rectangle = function(){ | |
1443 | // x: Integer | |
1444 | // horizontal offset in pixels, relative to document body | |
1445 | // y: Integer | |
1446 | // vertical offset in pixels, relative to document body | |
1447 | // width: Integer | |
1448 | // width in pixels | |
1449 | // height: Integer | |
1450 | // height in pixels | |
1451 | ||
1452 | this.x = x; | |
1453 | this.y = y; | |
1454 | this.width = width; | |
1455 | this.height = height; | |
1456 | } | |
1457 | =====*/ | |
1458 | ||
1459 | ||
1460 | dijit.placeOnScreenAroundRectangle = function( | |
1461 | /* DomNode */ node, | |
1462 | /* dijit.__Rectangle */ aroundRect, | |
1463 | /* Object */ aroundCorners, | |
1464 | /* Function */ layoutNode){ | |
1465 | ||
1466 | // summary: | |
1467 | // Like dijit.placeOnScreenAroundNode(), except that the "around" | |
1468 | // parameter is an arbitrary rectangle on the screen (x, y, width, height) | |
1469 | // instead of a dom node. | |
1470 | ||
1471 | return dijit._placeOnScreenAroundRect(node, | |
1472 | aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height, // rectangle | |
1473 | aroundCorners, layoutNode); | |
1474 | }; | |
1475 | ||
1476 | dijit._placeOnScreenAroundRect = function( | |
1477 | /* DomNode */ node, | |
1478 | /* Number */ x, | |
1479 | /* Number */ y, | |
1480 | /* Number */ width, | |
1481 | /* Number */ height, | |
1482 | /* Object */ aroundCorners, | |
1483 | /* Function */ layoutNode){ | |
1484 | ||
1485 | // summary: | |
1486 | // Like dijit.placeOnScreenAroundNode(), except it accepts coordinates | |
1487 | // of a rectangle to place node adjacent to. | |
1488 | ||
1489 | // TODO: combine with placeOnScreenAroundRectangle() | |
1490 | ||
1491 | // Generate list of possible positions for node | |
1492 | var choices = []; | |
1493 | for(var nodeCorner in aroundCorners){ | |
1494 | choices.push( { | |
1495 | aroundCorner: nodeCorner, | |
1496 | corner: aroundCorners[nodeCorner], | |
1497 | pos: { | |
1498 | x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width), | |
1499 | y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height) | |
1500 | } | |
1501 | }); | |
1502 | } | |
1503 | ||
1504 | return dijit._place(node, choices, layoutNode); | |
1505 | }; | |
1506 | ||
1507 | dijit.placementRegistry= new dojo.AdapterRegistry(); | |
1508 | dijit.placementRegistry.register("node", | |
1509 | function(n, x){ | |
1510 | return typeof x == "object" && | |
1511 | typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined"; | |
1512 | }, | |
1513 | dijit.placeOnScreenAroundNode); | |
1514 | dijit.placementRegistry.register("rect", | |
1515 | function(n, x){ | |
1516 | return typeof x == "object" && | |
1517 | "x" in x && "y" in x && "width" in x && "height" in x; | |
1518 | }, | |
1519 | dijit.placeOnScreenAroundRectangle); | |
1520 | ||
1521 | dijit.placeOnScreenAroundElement = function( | |
1522 | /* DomNode */ node, | |
1523 | /* Object */ aroundElement, | |
1524 | /* Object */ aroundCorners, | |
1525 | /* Function */ layoutNode){ | |
1526 | ||
1527 | // summary: | |
1528 | // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object | |
1529 | // for the "around" argument and finds a proper processor to place a node. | |
1530 | ||
1531 | return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments); | |
1532 | }; | |
1533 | ||
1534 | dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){ | |
1535 | // summary: | |
1536 | // Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement. | |
1537 | // | |
1538 | // position: String[] | |
1539 | // This variable controls the position of the drop down. | |
1540 | // It's an array of strings with the following values: | |
1541 | // | |
1542 | // * before: places drop down to the left of the target node/widget, or to the right in | |
1543 | // the case of RTL scripts like Hebrew and Arabic | |
1544 | // * after: places drop down to the right of the target node/widget, or to the left in | |
1545 | // the case of RTL scripts like Hebrew and Arabic | |
1546 | // * above: drop down goes above target node | |
1547 | // * below: drop down goes below target node | |
1548 | // | |
1549 | // The list is positions is tried, in order, until a position is found where the drop down fits | |
1550 | // within the viewport. | |
1551 | // | |
1552 | // leftToRight: Boolean | |
1553 | // Whether the popup will be displaying in leftToRight mode. | |
1554 | // | |
1555 | var align = {}; | |
1556 | dojo.forEach(position, function(pos){ | |
1557 | switch(pos){ | |
1558 | case "after": | |
1559 | align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR"; | |
1560 | break; | |
1561 | case "before": | |
1562 | align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL"; | |
1563 | break; | |
1564 | case "below": | |
1565 | // first try to align left borders, next try to align right borders (or reverse for RTL mode) | |
1566 | align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR"; | |
1567 | align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL"; | |
1568 | break; | |
1569 | case "above": | |
1570 | default: | |
1571 | // first try to align left borders, next try to align right borders (or reverse for RTL mode) | |
1572 | align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR"; | |
1573 | align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL"; | |
1574 | break; | |
1575 | } | |
1576 | }); | |
1577 | return align; | |
1578 | }; | |
1579 | ||
1580 | } | |
1581 | ||
1582 | if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1583 | dojo._hasResource["dijit._base.window"] = true; | |
1584 | dojo.provide("dijit._base.window"); | |
1585 | ||
1586 | ||
1587 | ||
1588 | dijit.getDocumentWindow = function(doc){ | |
1589 | return dojo.window.get(doc); | |
1590 | }; | |
1591 | ||
1592 | } | |
1593 | ||
1594 | if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1595 | dojo._hasResource["dijit._base.popup"] = true; | |
1596 | dojo.provide("dijit._base.popup"); | |
1597 | ||
1598 | ||
1599 | ||
1600 | ||
1601 | ||
1602 | /*===== | |
1603 | dijit.popup.__OpenArgs = function(){ | |
1604 | // popup: Widget | |
1605 | // widget to display | |
1606 | // parent: Widget | |
1607 | // the button etc. that is displaying this popup | |
1608 | // around: DomNode | |
1609 | // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.) | |
1610 | // x: Integer | |
1611 | // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.) | |
1612 | // y: Integer | |
1613 | // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.) | |
1614 | // orient: Object|String | |
1615 | // When the around parameter is specified, orient should be an | |
1616 | // ordered list of tuples of the form (around-node-corner, popup-node-corner). | |
1617 | // dijit.popup.open() tries to position the popup according to each tuple in the list, in order, | |
1618 | // until the popup appears fully within the viewport. | |
1619 | // | |
1620 | // The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples: | |
1621 | // 1. (BL, TL) | |
1622 | // 2. (TL, BL) | |
1623 | // where BL means "bottom left" and "TL" means "top left". | |
1624 | // So by default, it first tries putting the popup below the around node, left-aligning them, | |
1625 | // and then tries to put it above the around node, still left-aligning them. Note that the | |
1626 | // default is horizontally reversed when in RTL mode. | |
1627 | // | |
1628 | // When an (x,y) position is specified rather than an around node, orient is either | |
1629 | // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse, | |
1630 | // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't | |
1631 | // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner, | |
1632 | // and the top-right corner. | |
1633 | // onCancel: Function | |
1634 | // callback when user has canceled the popup by | |
1635 | // 1. hitting ESC or | |
1636 | // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog); | |
1637 | // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called | |
1638 | // onClose: Function | |
1639 | // callback whenever this popup is closed | |
1640 | // onExecute: Function | |
1641 | // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only) | |
1642 | // padding: dijit.__Position | |
1643 | // adding a buffer around the opening position. This is only useful when around is not set. | |
1644 | this.popup = popup; | |
1645 | this.parent = parent; | |
1646 | this.around = around; | |
1647 | this.x = x; | |
1648 | this.y = y; | |
1649 | this.orient = orient; | |
1650 | this.onCancel = onCancel; | |
1651 | this.onClose = onClose; | |
1652 | this.onExecute = onExecute; | |
1653 | this.padding = padding; | |
1654 | } | |
1655 | =====*/ | |
1656 | ||
1657 | dijit.popup = { | |
1658 | // summary: | |
1659 | // This singleton is used to show/hide widgets as popups. | |
1660 | ||
1661 | // _stack: dijit._Widget[] | |
1662 | // Stack of currently popped up widgets. | |
1663 | // (someone opened _stack[0], and then it opened _stack[1], etc.) | |
1664 | _stack: [], | |
1665 | ||
1666 | // _beginZIndex: Number | |
1667 | // Z-index of the first popup. (If first popup opens other | |
1668 | // popups they get a higher z-index.) | |
1669 | _beginZIndex: 1000, | |
1670 | ||
1671 | _idGen: 1, | |
1672 | ||
1673 | moveOffScreen: function(/*DomNode*/ node){ | |
1674 | // summary: | |
1675 | // Initialization for nodes that will be used as popups | |
1676 | // | |
1677 | // description: | |
1678 | // Puts node inside a wrapper <div>, and | |
1679 | // positions wrapper div off screen, but not display:none, so that | |
1680 | // the widget doesn't appear in the page flow and/or cause a blank | |
1681 | // area at the bottom of the viewport (making scrollbar longer), but | |
1682 | // initialization of contained widgets works correctly | |
1683 | ||
1684 | var wrapper = node.parentNode; | |
1685 | ||
1686 | // Create a wrapper widget for when this node (in the future) will be used as a popup. | |
1687 | // This is done early because of IE bugs where creating/moving DOM nodes causes focus | |
1688 | // to go wonky, see tests/robot/Toolbar.html to reproduce | |
1689 | if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){ | |
1690 | wrapper = dojo.create("div",{ | |
1691 | "class":"dijitPopup", | |
1692 | style:{ | |
1693 | visibility:"hidden", | |
1694 | top: "-9999px" | |
1695 | } | |
1696 | }, dojo.body()); | |
1697 | dijit.setWaiRole(wrapper, "presentation"); | |
1698 | wrapper.appendChild(node); | |
1699 | } | |
1700 | ||
1701 | ||
1702 | var s = node.style; | |
1703 | s.display = ""; | |
1704 | s.visibility = ""; | |
1705 | s.position = ""; | |
1706 | s.top = "0px"; | |
1707 | ||
1708 | dojo.style(wrapper, { | |
1709 | visibility: "hidden", | |
1710 | // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111) | |
1711 | top: "-9999px" | |
1712 | }); | |
1713 | }, | |
1714 | ||
1715 | getTopPopup: function(){ | |
1716 | // summary: | |
1717 | // Compute the closest ancestor popup that's *not* a child of another popup. | |
1718 | // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button. | |
1719 | var stack = this._stack; | |
1720 | for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){ | |
1721 | /* do nothing, just trying to get right value for pi */ | |
1722 | } | |
1723 | return stack[pi]; | |
1724 | }, | |
1725 | ||
1726 | open: function(/*dijit.popup.__OpenArgs*/ args){ | |
1727 | // summary: | |
1728 | // Popup the widget at the specified position | |
1729 | // | |
1730 | // example: | |
1731 | // opening at the mouse position | |
1732 | // | dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY}); | |
1733 | // | |
1734 | // example: | |
1735 | // opening the widget as a dropdown | |
1736 | // | dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}}); | |
1737 | // | |
1738 | // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback | |
1739 | // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed. | |
1740 | ||
1741 | var stack = this._stack, | |
1742 | widget = args.popup, | |
1743 | orient = args.orient || ( | |
1744 | (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ? | |
1745 | {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} : | |
1746 | {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'} | |
1747 | ), | |
1748 | around = args.around, | |
1749 | id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++); | |
1750 | ||
1751 | ||
1752 | // The wrapper may have already been created, but in case it wasn't, create here | |
1753 | var wrapper = widget.domNode.parentNode; | |
1754 | if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){ | |
1755 | this.moveOffScreen(widget.domNode); | |
1756 | wrapper = widget.domNode.parentNode; | |
1757 | } | |
1758 | ||
1759 | dojo.attr(wrapper, { | |
1760 | id: id, | |
1761 | style: { | |
1762 | zIndex: this._beginZIndex + stack.length | |
1763 | }, | |
1764 | "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup", | |
1765 | dijitPopupParent: args.parent ? args.parent.id : "" | |
1766 | }); | |
1767 | ||
1768 | if(dojo.isIE || dojo.isMoz){ | |
1769 | var iframe = wrapper.childNodes[1]; | |
1770 | if(!iframe){ | |
1771 | iframe = new dijit.BackgroundIframe(wrapper); | |
1772 | } | |
1773 | } | |
1774 | ||
1775 | // position the wrapper node and make it visible | |
1776 | var best = around ? | |
1777 | dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) : | |
1778 | dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding); | |
1779 | ||
1780 | wrapper.style.visibility = "visible"; | |
1781 | widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown | |
1782 | ||
1783 | var handlers = []; | |
1784 | ||
1785 | // provide default escape and tab key handling | |
1786 | // (this will work for any widget, not just menu) | |
1787 | handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){ | |
1788 | if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){ | |
1789 | dojo.stopEvent(evt); | |
1790 | args.onCancel(); | |
1791 | }else if(evt.charOrCode === dojo.keys.TAB){ | |
1792 | dojo.stopEvent(evt); | |
1793 | var topPopup = this.getTopPopup(); | |
1794 | if(topPopup && topPopup.onCancel){ | |
1795 | topPopup.onCancel(); | |
1796 | } | |
1797 | } | |
1798 | })); | |
1799 | ||
1800 | // watch for cancel/execute events on the popup and notify the caller | |
1801 | // (for a menu, "execute" means clicking an item) | |
1802 | if(widget.onCancel){ | |
1803 | handlers.push(dojo.connect(widget, "onCancel", args.onCancel)); | |
1804 | } | |
1805 | ||
1806 | handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){ | |
1807 | var topPopup = this.getTopPopup(); | |
1808 | if(topPopup && topPopup.onExecute){ | |
1809 | topPopup.onExecute(); | |
1810 | } | |
1811 | })); | |
1812 | ||
1813 | stack.push({ | |
1814 | wrapper: wrapper, | |
1815 | iframe: iframe, | |
1816 | widget: widget, | |
1817 | parent: args.parent, | |
1818 | onExecute: args.onExecute, | |
1819 | onCancel: args.onCancel, | |
1820 | onClose: args.onClose, | |
1821 | handlers: handlers | |
1822 | }); | |
1823 | ||
1824 | if(widget.onOpen){ | |
1825 | // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here) | |
1826 | widget.onOpen(best); | |
1827 | } | |
1828 | ||
1829 | return best; | |
1830 | }, | |
1831 | ||
1832 | close: function(/*dijit._Widget*/ popup){ | |
1833 | // summary: | |
1834 | // Close specified popup and any popups that it parented | |
1835 | ||
1836 | var stack = this._stack; | |
1837 | ||
1838 | // Basically work backwards from the top of the stack closing popups | |
1839 | // until we hit the specified popup, but IIRC there was some issue where closing | |
1840 | // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C] | |
1841 | // closing C might close B indirectly and then the while() condition will run where stack==[A]... | |
1842 | // so the while condition is constructed defensively. | |
1843 | while(dojo.some(stack, function(elem){return elem.widget == popup;})){ | |
1844 | var top = stack.pop(), | |
1845 | wrapper = top.wrapper, | |
1846 | iframe = top.iframe, | |
1847 | widget = top.widget, | |
1848 | onClose = top.onClose; | |
1849 | ||
1850 | if(widget.onClose){ | |
1851 | // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here) | |
1852 | widget.onClose(); | |
1853 | } | |
1854 | dojo.forEach(top.handlers, dojo.disconnect); | |
1855 | ||
1856 | // Move the widget plus it's wrapper off screen, unless it has already been destroyed in above onClose() etc. | |
1857 | if(widget && widget.domNode){ | |
1858 | this.moveOffScreen(widget.domNode); | |
1859 | }else{ | |
1860 | dojo.destroy(wrapper); | |
1861 | } | |
1862 | ||
1863 | if(onClose){ | |
1864 | onClose(); | |
1865 | } | |
1866 | } | |
1867 | } | |
1868 | }; | |
1869 | ||
1870 | dijit._frames = new function(){ | |
1871 | // summary: | |
1872 | // cache of iframes | |
1873 | var queue = []; | |
1874 | ||
1875 | this.pop = function(){ | |
1876 | var iframe; | |
1877 | if(queue.length){ | |
1878 | iframe = queue.pop(); | |
1879 | iframe.style.display=""; | |
1880 | }else{ | |
1881 | if(dojo.isIE){ | |
1882 | var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\""; | |
1883 | var html="<iframe src='" + burl + "'" | |
1884 | + " style='position: absolute; left: 0px; top: 0px;" | |
1885 | + "z-index: -1; filter:Alpha(Opacity=\"0\");'>"; | |
1886 | iframe = dojo.doc.createElement(html); | |
1887 | }else{ | |
1888 | iframe = dojo.create("iframe"); | |
1889 | iframe.src = 'javascript:""'; | |
1890 | iframe.className = "dijitBackgroundIframe"; | |
1891 | dojo.style(iframe, "opacity", 0.1); | |
1892 | } | |
1893 | iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work. | |
1894 | dijit.setWaiRole(iframe,"presentation"); | |
1895 | } | |
1896 | return iframe; | |
1897 | }; | |
1898 | ||
1899 | this.push = function(iframe){ | |
1900 | iframe.style.display="none"; | |
1901 | queue.push(iframe); | |
1902 | } | |
1903 | }(); | |
1904 | ||
1905 | ||
1906 | dijit.BackgroundIframe = function(/* DomNode */node){ | |
1907 | // summary: | |
1908 | // For IE/FF z-index schenanigans. id attribute is required. | |
1909 | // | |
1910 | // description: | |
1911 | // new dijit.BackgroundIframe(node) | |
1912 | // Makes a background iframe as a child of node, that fills | |
1913 | // area (and position) of node | |
1914 | ||
1915 | if(!node.id){ throw new Error("no id"); } | |
1916 | if(dojo.isIE || dojo.isMoz){ | |
1917 | var iframe = dijit._frames.pop(); | |
1918 | node.appendChild(iframe); | |
1919 | if(dojo.isIE<7){ | |
1920 | this.resize(node); | |
1921 | this._conn = dojo.connect(node, 'onresize', this, function(){ | |
1922 | this.resize(node); | |
1923 | }); | |
1924 | }else{ | |
1925 | dojo.style(iframe, { | |
1926 | width: '100%', | |
1927 | height: '100%' | |
1928 | }); | |
1929 | } | |
1930 | this.iframe = iframe; | |
1931 | } | |
1932 | }; | |
1933 | ||
1934 | dojo.extend(dijit.BackgroundIframe, { | |
1935 | resize: function(node){ | |
1936 | // summary: | |
1937 | // resize the iframe so its the same size as node | |
1938 | // description: | |
1939 | // this function is a no-op in all browsers except | |
1940 | // IE6, which does not support 100% width/height | |
1941 | // of absolute positioned iframes | |
1942 | if(this.iframe && dojo.isIE<7){ | |
1943 | dojo.style(this.iframe, { | |
1944 | width: node.offsetWidth + 'px', | |
1945 | height: node.offsetHeight + 'px' | |
1946 | }); | |
1947 | } | |
1948 | }, | |
1949 | destroy: function(){ | |
1950 | // summary: | |
1951 | // destroy the iframe | |
1952 | if(this._conn){ | |
1953 | dojo.disconnect(this._conn); | |
1954 | this._conn = null; | |
1955 | } | |
1956 | if(this.iframe){ | |
1957 | dijit._frames.push(this.iframe); | |
1958 | delete this.iframe; | |
1959 | } | |
1960 | } | |
1961 | }); | |
1962 | ||
1963 | } | |
1964 | ||
1965 | if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1966 | dojo._hasResource["dijit._base.scroll"] = true; | |
1967 | dojo.provide("dijit._base.scroll"); | |
1968 | ||
1969 | ||
1970 | ||
1971 | dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){ | |
1972 | // summary: | |
1973 | // Scroll the passed node into view, if it is not already. | |
1974 | // Deprecated, use `dojo.window.scrollIntoView` instead. | |
1975 | ||
1976 | dojo.window.scrollIntoView(node, pos); | |
1977 | }; | |
1978 | ||
1979 | } | |
1980 | ||
1981 | if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
1982 | dojo._hasResource["dojo.uacss"] = true; | |
1983 | dojo.provide("dojo.uacss"); | |
1984 | ||
1985 | (function(){ | |
1986 | // summary: | |
1987 | // Applies pre-set CSS classes to the top-level HTML node, based on: | |
1988 | // - browser (ex: dj_ie) | |
1989 | // - browser version (ex: dj_ie6) | |
1990 | // - box model (ex: dj_contentBox) | |
1991 | // - text direction (ex: dijitRtl) | |
1992 | // | |
1993 | // In addition, browser, browser version, and box model are | |
1994 | // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl. | |
1995 | ||
1996 | var d = dojo, | |
1997 | html = d.doc.documentElement, | |
1998 | ie = d.isIE, | |
1999 | opera = d.isOpera, | |
2000 | maj = Math.floor, | |
2001 | ff = d.isFF, | |
2002 | boxModel = d.boxModel.replace(/-/,''), | |
2003 | ||
2004 | classes = { | |
2005 | dj_ie: ie, | |
2006 | dj_ie6: maj(ie) == 6, | |
2007 | dj_ie7: maj(ie) == 7, | |
2008 | dj_ie8: maj(ie) == 8, | |
2009 | dj_quirks: d.isQuirks, | |
2010 | dj_iequirks: ie && d.isQuirks, | |
2011 | ||
2012 | // NOTE: Opera not supported by dijit | |
2013 | dj_opera: opera, | |
2014 | ||
2015 | dj_khtml: d.isKhtml, | |
2016 | ||
2017 | dj_webkit: d.isWebKit, | |
2018 | dj_safari: d.isSafari, | |
2019 | dj_chrome: d.isChrome, | |
2020 | ||
2021 | dj_gecko: d.isMozilla, | |
2022 | dj_ff3: maj(ff) == 3 | |
2023 | }; // no dojo unsupported browsers | |
2024 | ||
2025 | classes["dj_" + boxModel] = true; | |
2026 | ||
2027 | // apply browser, browser version, and box model class names | |
2028 | var classStr = ""; | |
2029 | for(var clz in classes){ | |
2030 | if(classes[clz]){ | |
2031 | classStr += clz + " "; | |
2032 | } | |
2033 | } | |
2034 | html.className = d.trim(html.className + " " + classStr); | |
2035 | ||
2036 | // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension. | |
2037 | // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl). | |
2038 | // Unshift() is to run sniff code before the parser. | |
2039 | dojo._loaders.unshift(function(){ | |
2040 | if(!dojo._isBodyLtr()){ | |
2041 | var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ") | |
2042 | html.className = d.trim(html.className + " " + rtlClassStr); | |
2043 | } | |
2044 | }); | |
2045 | })(); | |
2046 | ||
2047 | } | |
2048 | ||
2049 | if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2050 | dojo._hasResource["dijit._base.sniff"] = true; | |
2051 | // summary: | |
2052 | // Applies pre-set CSS classes to the top-level HTML node, see | |
2053 | // `dojo.uacss` for details. | |
2054 | // | |
2055 | // Simply doing a require on this module will | |
2056 | // establish this CSS. Modified version of Morris' CSS hack. | |
2057 | ||
2058 | dojo.provide("dijit._base.sniff"); | |
2059 | ||
2060 | ||
2061 | ||
2062 | } | |
2063 | ||
2064 | if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2065 | dojo._hasResource["dijit._base.typematic"] = true; | |
2066 | dojo.provide("dijit._base.typematic"); | |
2067 | ||
2068 | dijit.typematic = { | |
2069 | // summary: | |
2070 | // These functions are used to repetitively call a user specified callback | |
2071 | // method when a specific key or mouse click over a specific DOM node is | |
2072 | // held down for a specific amount of time. | |
2073 | // Only 1 such event is allowed to occur on the browser page at 1 time. | |
2074 | ||
2075 | _fireEventAndReload: function(){ | |
2076 | this._timer = null; | |
2077 | this._callback(++this._count, this._node, this._evt); | |
2078 | ||
2079 | // Schedule next event, timer is at most minDelay (default 10ms) to avoid | |
2080 | // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup) | |
2081 | this._currentTimeout = Math.max( | |
2082 | this._currentTimeout < 0 ? this._initialDelay : | |
2083 | (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)), | |
2084 | this._minDelay); | |
2085 | this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout); | |
2086 | }, | |
2087 | ||
2088 | trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ | |
2089 | // summary: | |
2090 | // Start a timed, repeating callback sequence. | |
2091 | // If already started, the function call is ignored. | |
2092 | // This method is not normally called by the user but can be | |
2093 | // when the normal listener code is insufficient. | |
2094 | // evt: | |
2095 | // key or mouse event object to pass to the user callback | |
2096 | // _this: | |
2097 | // pointer to the user's widget space. | |
2098 | // node: | |
2099 | // the DOM node object to pass the the callback function | |
2100 | // callback: | |
2101 | // function to call until the sequence is stopped called with 3 parameters: | |
2102 | // count: | |
2103 | // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped | |
2104 | // node: | |
2105 | // the DOM node object passed in | |
2106 | // evt: | |
2107 | // key or mouse event object | |
2108 | // obj: | |
2109 | // user space object used to uniquely identify each typematic sequence | |
2110 | // subsequentDelay (optional): | |
2111 | // if > 1, the number of milliseconds until the 3->n events occur | |
2112 | // or else the fractional time multiplier for the next event's delay, default=0.9 | |
2113 | // initialDelay (optional): | |
2114 | // the number of milliseconds until the 2nd event occurs, default=500ms | |
2115 | // minDelay (optional): | |
2116 | // the maximum delay in milliseconds for event to fire, default=10ms | |
2117 | if(obj != this._obj){ | |
2118 | this.stop(); | |
2119 | this._initialDelay = initialDelay || 500; | |
2120 | this._subsequentDelay = subsequentDelay || 0.90; | |
2121 | this._minDelay = minDelay || 10; | |
2122 | this._obj = obj; | |
2123 | this._evt = evt; | |
2124 | this._node = node; | |
2125 | this._currentTimeout = -1; | |
2126 | this._count = -1; | |
2127 | this._callback = dojo.hitch(_this, callback); | |
2128 | this._fireEventAndReload(); | |
2129 | this._evt = dojo.mixin({faux: true}, evt); | |
2130 | } | |
2131 | }, | |
2132 | ||
2133 | stop: function(){ | |
2134 | // summary: | |
2135 | // Stop an ongoing timed, repeating callback sequence. | |
2136 | if(this._timer){ | |
2137 | clearTimeout(this._timer); | |
2138 | this._timer = null; | |
2139 | } | |
2140 | if(this._obj){ | |
2141 | this._callback(-1, this._node, this._evt); | |
2142 | this._obj = null; | |
2143 | } | |
2144 | }, | |
2145 | ||
2146 | addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ | |
2147 | // summary: | |
2148 | // Start listening for a specific typematic key. | |
2149 | // See also the trigger method for other parameters. | |
2150 | // keyObject: | |
2151 | // an object defining the key to listen for: | |
2152 | // charOrCode: | |
2153 | // the printable character (string) or keyCode (number) to listen for. | |
2154 | // keyCode: | |
2155 | // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0). | |
2156 | // charCode: | |
2157 | // (deprecated - use charOrCode) the charCode (number) to listen for. | |
2158 | // ctrlKey: | |
2159 | // desired ctrl key state to initiate the callback sequence: | |
2160 | // - pressed (true) | |
2161 | // - released (false) | |
2162 | // - either (unspecified) | |
2163 | // altKey: | |
2164 | // same as ctrlKey but for the alt key | |
2165 | // shiftKey: | |
2166 | // same as ctrlKey but for the shift key | |
2167 | // returns: | |
2168 | // an array of dojo.connect handles | |
2169 | if(keyObject.keyCode){ | |
2170 | keyObject.charOrCode = keyObject.keyCode; | |
2171 | dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0"); | |
2172 | }else if(keyObject.charCode){ | |
2173 | keyObject.charOrCode = String.fromCharCode(keyObject.charCode); | |
2174 | dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0"); | |
2175 | } | |
2176 | return [ | |
2177 | dojo.connect(node, "onkeypress", this, function(evt){ | |
2178 | if(evt.charOrCode == keyObject.charOrCode && | |
2179 | (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) && | |
2180 | (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) && | |
2181 | (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey | |
2182 | (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){ | |
2183 | dojo.stopEvent(evt); | |
2184 | dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay); | |
2185 | }else if(dijit.typematic._obj == keyObject){ | |
2186 | dijit.typematic.stop(); | |
2187 | } | |
2188 | }), | |
2189 | dojo.connect(node, "onkeyup", this, function(evt){ | |
2190 | if(dijit.typematic._obj == keyObject){ | |
2191 | dijit.typematic.stop(); | |
2192 | } | |
2193 | }) | |
2194 | ]; | |
2195 | }, | |
2196 | ||
2197 | addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ | |
2198 | // summary: | |
2199 | // Start listening for a typematic mouse click. | |
2200 | // See the trigger method for other parameters. | |
2201 | // returns: | |
2202 | // an array of dojo.connect handles | |
2203 | var dc = dojo.connect; | |
2204 | return [ | |
2205 | dc(node, "mousedown", this, function(evt){ | |
2206 | dojo.stopEvent(evt); | |
2207 | dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay); | |
2208 | }), | |
2209 | dc(node, "mouseup", this, function(evt){ | |
2210 | dojo.stopEvent(evt); | |
2211 | dijit.typematic.stop(); | |
2212 | }), | |
2213 | dc(node, "mouseout", this, function(evt){ | |
2214 | dojo.stopEvent(evt); | |
2215 | dijit.typematic.stop(); | |
2216 | }), | |
2217 | dc(node, "mousemove", this, function(evt){ | |
2218 | evt.preventDefault(); | |
2219 | }), | |
2220 | dc(node, "dblclick", this, function(evt){ | |
2221 | dojo.stopEvent(evt); | |
2222 | if(dojo.isIE){ | |
2223 | dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay); | |
2224 | setTimeout(dojo.hitch(this, dijit.typematic.stop), 50); | |
2225 | } | |
2226 | }) | |
2227 | ]; | |
2228 | }, | |
2229 | ||
2230 | addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ | |
2231 | // summary: | |
2232 | // Start listening for a specific typematic key and mouseclick. | |
2233 | // This is a thin wrapper to addKeyListener and addMouseListener. | |
2234 | // See the addMouseListener and addKeyListener methods for other parameters. | |
2235 | // mouseNode: | |
2236 | // the DOM node object to listen on for mouse events. | |
2237 | // keyNode: | |
2238 | // the DOM node object to listen on for key events. | |
2239 | // returns: | |
2240 | // an array of dojo.connect handles | |
2241 | return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat( | |
2242 | this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)); | |
2243 | } | |
2244 | }; | |
2245 | ||
2246 | } | |
2247 | ||
2248 | if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2249 | dojo._hasResource["dijit._base.wai"] = true; | |
2250 | dojo.provide("dijit._base.wai"); | |
2251 | ||
2252 | dijit.wai = { | |
2253 | onload: function(){ | |
2254 | // summary: | |
2255 | // Detects if we are in high-contrast mode or not | |
2256 | ||
2257 | // This must be a named function and not an anonymous | |
2258 | // function, so that the widget parsing code can make sure it | |
2259 | // registers its onload function after this function. | |
2260 | // DO NOT USE "this" within this function. | |
2261 | ||
2262 | // create div for testing if high contrast mode is on or images are turned off | |
2263 | var div = dojo.create("div",{ | |
2264 | id: "a11yTestNode", | |
2265 | style:{ | |
2266 | cssText:'border: 1px solid;' | |
2267 | + 'border-color:red green;' | |
2268 | + 'position: absolute;' | |
2269 | + 'height: 5px;' | |
2270 | + 'top: -999px;' | |
2271 | + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");' | |
2272 | } | |
2273 | }, dojo.body()); | |
2274 | ||
2275 | // test it | |
2276 | var cs = dojo.getComputedStyle(div); | |
2277 | if(cs){ | |
2278 | var bkImg = cs.backgroundImage; | |
2279 | var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" )); | |
2280 | dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y"); | |
2281 | if(dojo.isIE){ | |
2282 | div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014 | |
2283 | }else{ | |
2284 | dojo.body().removeChild(div); | |
2285 | } | |
2286 | } | |
2287 | } | |
2288 | }; | |
2289 | ||
2290 | // Test if computer is in high contrast mode. | |
2291 | // Make sure the a11y test runs first, before widgets are instantiated. | |
2292 | if(dojo.isIE || dojo.isMoz){ // NOTE: checking in Safari messes things up | |
2293 | dojo._loaders.unshift(dijit.wai.onload); | |
2294 | } | |
2295 | ||
2296 | dojo.mixin(dijit, { | |
2297 | _XhtmlRoles: /banner|contentinfo|definition|main|navigation|search|note|secondary|seealso/, | |
2298 | ||
2299 | hasWaiRole: function(/*Element*/ elem, /*String*/ role){ | |
2300 | // summary: | |
2301 | // Determines if an element has a particular non-XHTML role. | |
2302 | // returns: | |
2303 | // True if elem has the specific non-XHTML role attribute and false if not. | |
2304 | // For backwards compatibility if role parameter not provided, | |
2305 | // returns true if has non XHTML role | |
2306 | var waiRole = this.getWaiRole(elem); | |
2307 | return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0); | |
2308 | }, | |
2309 | ||
2310 | getWaiRole: function(/*Element*/ elem){ | |
2311 | // summary: | |
2312 | // Gets the non-XHTML role for an element (which should be a wai role). | |
2313 | // returns: | |
2314 | // The non-XHTML role of elem or an empty string if elem | |
2315 | // does not have a role. | |
2316 | return dojo.trim((dojo.attr(elem, "role") || "").replace(this._XhtmlRoles,"").replace("wairole:","")); | |
2317 | }, | |
2318 | ||
2319 | setWaiRole: function(/*Element*/ elem, /*String*/ role){ | |
2320 | // summary: | |
2321 | // Sets the role on an element. | |
2322 | // description: | |
2323 | // Replace existing role attribute with new role. | |
2324 | // If elem already has an XHTML role, append this role to XHTML role | |
2325 | // and remove other ARIA roles. | |
2326 | ||
2327 | var curRole = dojo.attr(elem, "role") || ""; | |
2328 | if(!this._XhtmlRoles.test(curRole)){ | |
2329 | dojo.attr(elem, "role", role); | |
2330 | }else{ | |
2331 | if((" "+ curRole +" ").indexOf(" " + role + " ") < 0){ | |
2332 | var clearXhtml = dojo.trim(curRole.replace(this._XhtmlRoles, "")); | |
2333 | var cleanRole = dojo.trim(curRole.replace(clearXhtml, "")); | |
2334 | dojo.attr(elem, "role", cleanRole + (cleanRole ? ' ' : '') + role); | |
2335 | } | |
2336 | } | |
2337 | }, | |
2338 | ||
2339 | removeWaiRole: function(/*Element*/ elem, /*String*/ role){ | |
2340 | // summary: | |
2341 | // Removes the specified non-XHTML role from an element. | |
2342 | // Removes role attribute if no specific role provided (for backwards compat.) | |
2343 | ||
2344 | var roleValue = dojo.attr(elem, "role"); | |
2345 | if(!roleValue){ return; } | |
2346 | if(role){ | |
2347 | var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " ")); | |
2348 | dojo.attr(elem, "role", t); | |
2349 | }else{ | |
2350 | elem.removeAttribute("role"); | |
2351 | } | |
2352 | }, | |
2353 | ||
2354 | hasWaiState: function(/*Element*/ elem, /*String*/ state){ | |
2355 | // summary: | |
2356 | // Determines if an element has a given state. | |
2357 | // description: | |
2358 | // Checks for an attribute called "aria-"+state. | |
2359 | // returns: | |
2360 | // true if elem has a value for the given state and | |
2361 | // false if it does not. | |
2362 | ||
2363 | return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state); | |
2364 | }, | |
2365 | ||
2366 | getWaiState: function(/*Element*/ elem, /*String*/ state){ | |
2367 | // summary: | |
2368 | // Gets the value of a state on an element. | |
2369 | // description: | |
2370 | // Checks for an attribute called "aria-"+state. | |
2371 | // returns: | |
2372 | // The value of the requested state on elem | |
2373 | // or an empty string if elem has no value for state. | |
2374 | ||
2375 | return elem.getAttribute("aria-"+state) || ""; | |
2376 | }, | |
2377 | ||
2378 | setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){ | |
2379 | // summary: | |
2380 | // Sets a state on an element. | |
2381 | // description: | |
2382 | // Sets an attribute called "aria-"+state. | |
2383 | ||
2384 | elem.setAttribute("aria-"+state, value); | |
2385 | }, | |
2386 | ||
2387 | removeWaiState: function(/*Element*/ elem, /*String*/ state){ | |
2388 | // summary: | |
2389 | // Removes a state from an element. | |
2390 | // description: | |
2391 | // Sets an attribute called "aria-"+state. | |
2392 | ||
2393 | elem.removeAttribute("aria-"+state); | |
2394 | } | |
2395 | }); | |
2396 | ||
2397 | } | |
2398 | ||
2399 | if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2400 | dojo._hasResource["dijit._base"] = true; | |
2401 | dojo.provide("dijit._base"); | |
2402 | ||
2403 | ||
2404 | ||
2405 | ||
2406 | ||
2407 | ||
2408 | ||
2409 | ||
2410 | ||
2411 | ||
2412 | ||
2413 | } | |
2414 | ||
2415 | if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2416 | dojo._hasResource["dojo.date.stamp"] = true; | |
2417 | dojo.provide("dojo.date.stamp"); | |
2418 | ||
2419 | // Methods to convert dates to or from a wire (string) format using well-known conventions | |
2420 | ||
2421 | dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){ | |
2422 | // summary: | |
2423 | // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard. | |
2424 | // | |
2425 | // description: | |
2426 | // Accepts a string formatted according to a profile of ISO8601 as defined by | |
2427 | // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed. | |
2428 | // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime) | |
2429 | // The following combinations are valid: | |
2430 | // | |
2431 | // * dates only | |
2432 | // | * yyyy | |
2433 | // | * yyyy-MM | |
2434 | // | * yyyy-MM-dd | |
2435 | // * times only, with an optional time zone appended | |
2436 | // | * THH:mm | |
2437 | // | * THH:mm:ss | |
2438 | // | * THH:mm:ss.SSS | |
2439 | // * and "datetimes" which could be any combination of the above | |
2440 | // | |
2441 | // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm | |
2442 | // Assumes the local time zone if not specified. Does not validate. Improperly formatted | |
2443 | // input may return null. Arguments which are out of bounds will be handled | |
2444 | // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st) | |
2445 | // Only years between 100 and 9999 are supported. | |
2446 | // | |
2447 | // formattedString: | |
2448 | // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00 | |
2449 | // | |
2450 | // defaultTime: | |
2451 | // Used for defaults for fields omitted in the formattedString. | |
2452 | // Uses 1970-01-01T00:00:00.0Z by default. | |
2453 | ||
2454 | if(!dojo.date.stamp._isoRegExp){ | |
2455 | dojo.date.stamp._isoRegExp = | |
2456 | //TODO: could be more restrictive and check for 00-59, etc. | |
2457 | /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/; | |
2458 | } | |
2459 | ||
2460 | var match = dojo.date.stamp._isoRegExp.exec(formattedString), | |
2461 | result = null; | |
2462 | ||
2463 | if(match){ | |
2464 | match.shift(); | |
2465 | if(match[1]){match[1]--;} // Javascript Date months are 0-based | |
2466 | if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds | |
2467 | ||
2468 | if(defaultTime){ | |
2469 | // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0 | |
2470 | defaultTime = new Date(defaultTime); | |
2471 | dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){ | |
2472 | return defaultTime["get" + prop](); | |
2473 | }), function(value, index){ | |
2474 | match[index] = match[index] || value; | |
2475 | }); | |
2476 | } | |
2477 | 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 | |
2478 | if(match[0] < 100){ | |
2479 | result.setFullYear(match[0] || 1970); | |
2480 | } | |
2481 | ||
2482 | var offset = 0, | |
2483 | zoneSign = match[7] && match[7].charAt(0); | |
2484 | if(zoneSign != 'Z'){ | |
2485 | offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0); | |
2486 | if(zoneSign != '-'){ offset *= -1; } | |
2487 | } | |
2488 | if(zoneSign){ | |
2489 | offset -= result.getTimezoneOffset(); | |
2490 | } | |
2491 | if(offset){ | |
2492 | result.setTime(result.getTime() + offset * 60000); | |
2493 | } | |
2494 | } | |
2495 | ||
2496 | return result; // Date or null | |
2497 | } | |
2498 | ||
2499 | /*===== | |
2500 | dojo.date.stamp.__Options = function(){ | |
2501 | // selector: String | |
2502 | // "date" or "time" for partial formatting of the Date object. | |
2503 | // Both date and time will be formatted by default. | |
2504 | // zulu: Boolean | |
2505 | // if true, UTC/GMT is used for a timezone | |
2506 | // milliseconds: Boolean | |
2507 | // if true, output milliseconds | |
2508 | this.selector = selector; | |
2509 | this.zulu = zulu; | |
2510 | this.milliseconds = milliseconds; | |
2511 | } | |
2512 | =====*/ | |
2513 | ||
2514 | dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){ | |
2515 | // summary: | |
2516 | // Format a Date object as a string according a subset of the ISO-8601 standard | |
2517 | // | |
2518 | // description: | |
2519 | // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt) | |
2520 | // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date) | |
2521 | // Does not check bounds. Only years between 100 and 9999 are supported. | |
2522 | // | |
2523 | // dateObject: | |
2524 | // A Date object | |
2525 | ||
2526 | var _ = function(n){ return (n < 10) ? "0" + n : n; }; | |
2527 | options = options || {}; | |
2528 | var formattedDate = [], | |
2529 | getter = options.zulu ? "getUTC" : "get", | |
2530 | date = ""; | |
2531 | if(options.selector != "time"){ | |
2532 | var year = dateObject[getter+"FullYear"](); | |
2533 | date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-'); | |
2534 | } | |
2535 | formattedDate.push(date); | |
2536 | if(options.selector != "date"){ | |
2537 | var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':'); | |
2538 | var millis = dateObject[getter+"Milliseconds"](); | |
2539 | if(options.milliseconds){ | |
2540 | time += "."+ (millis < 100 ? "0" : "") + _(millis); | |
2541 | } | |
2542 | if(options.zulu){ | |
2543 | time += "Z"; | |
2544 | }else if(options.selector != "time"){ | |
2545 | var timezoneOffset = dateObject.getTimezoneOffset(); | |
2546 | var absOffset = Math.abs(timezoneOffset); | |
2547 | time += (timezoneOffset > 0 ? "-" : "+") + | |
2548 | _(Math.floor(absOffset/60)) + ":" + _(absOffset%60); | |
2549 | } | |
2550 | formattedDate.push(time); | |
2551 | } | |
2552 | return formattedDate.join('T'); // String | |
2553 | } | |
2554 | ||
2555 | } | |
2556 | ||
2557 | if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
2558 | dojo._hasResource["dojo.parser"] = true; | |
2559 | dojo.provide("dojo.parser"); | |
2560 | ||
2561 | ||
2562 | new Date("X"); // workaround for #11279, new Date("") == NaN | |
2563 | ||
2564 | dojo.parser = new function(){ | |
2565 | // summary: The Dom/Widget parsing package | |
2566 | ||
2567 | var d = dojo; | |
2568 | this._attrName = d._scopeName + "Type"; | |
2569 | this._query = "[" + this._attrName + "]"; | |
2570 | ||
2571 | function val2type(/*Object*/ value){ | |
2572 | // summary: | |
2573 | // Returns name of type of given value. | |
2574 | ||
2575 | if(d.isString(value)){ return "string"; } | |
2576 | if(typeof value == "number"){ return "number"; } | |
2577 | if(typeof value == "boolean"){ return "boolean"; } | |
2578 | if(d.isFunction(value)){ return "function"; } | |
2579 | if(d.isArray(value)){ return "array"; } // typeof [] == "object" | |
2580 | if(value instanceof Date) { return "date"; } // assume timestamp | |
2581 | if(value instanceof d._Url){ return "url"; } | |
2582 | return "object"; | |
2583 | } | |
2584 | ||
2585 | function str2obj(/*String*/ value, /*String*/ type){ | |
2586 | // summary: | |
2587 | // Convert given string value to given type | |
2588 | switch(type){ | |
2589 | case "string": | |
2590 | return value; | |
2591 | case "number": | |
2592 | return value.length ? Number(value) : NaN; | |
2593 | case "boolean": | |
2594 | // for checked/disabled value might be "" or "checked". interpret as true. | |
2595 | return typeof value == "boolean" ? value : !(value.toLowerCase()=="false"); | |
2596 | case "function": | |
2597 | if(d.isFunction(value)){ | |
2598 | // IE gives us a function, even when we say something like onClick="foo" | |
2599 | // (in which case it gives us an invalid function "function(){ foo }"). | |
2600 | // Therefore, convert to string | |
2601 | value=value.toString(); | |
2602 | value=d.trim(value.substring(value.indexOf('{')+1, value.length-1)); | |
2603 | } | |
2604 | try{ | |
2605 | if(value === "" || value.search(/[^\w\.]+/i) != -1){ | |
2606 | // The user has specified some text for a function like "return x+5" | |
2607 | return new Function(value); | |
2608 | }else{ | |
2609 | // The user has specified the name of a function like "myOnClick" | |
2610 | // or a single word function "return" | |
2611 | return d.getObject(value, false) || new Function(value); | |
2612 | } | |
2613 | }catch(e){ return new Function(); } | |
2614 | case "array": | |
2615 | return value ? value.split(/\s*,\s*/) : []; | |
2616 | case "date": | |
2617 | switch(value){ | |
2618 | case "": return new Date(""); // the NaN of dates | |
2619 | case "now": return new Date(); // current date | |
2620 | default: return d.date.stamp.fromISOString(value); | |
2621 | } | |
2622 | case "url": | |
2623 | return d.baseUrl + value; | |
2624 | default: | |
2625 | return d.fromJson(value); | |
2626 | } | |
2627 | } | |
2628 | ||
2629 | var instanceClasses = { | |
2630 | // map from fully qualified name (like "dijit.Button") to structure like | |
2631 | // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} } | |
2632 | }; | |
2633 | ||
2634 | // Widgets like BorderContainer add properties to _Widget via dojo.extend(). | |
2635 | // If BorderContainer is loaded after _Widget's parameter list has been cached, | |
2636 | // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). | |
2637 | dojo.connect(dojo, "extend", function(){ | |
2638 | instanceClasses = {}; | |
2639 | }); | |
2640 | ||
2641 | function getClassInfo(/*String*/ className){ | |
2642 | // className: | |
2643 | // fully qualified name (like "dijit.form.Button") | |
2644 | // returns: | |
2645 | // structure like | |
2646 | // { | |
2647 | // cls: dijit.Button, | |
2648 | // params: { label: "string", disabled: "boolean"} | |
2649 | // } | |
2650 | ||
2651 | if(!instanceClasses[className]){ | |
2652 | // get pointer to widget class | |
2653 | var cls = d.getObject(className); | |
2654 | if(!cls){ return null; } // class not defined [yet] | |
2655 | ||
2656 | var proto = cls.prototype; | |
2657 | ||
2658 | // get table of parameter names & types | |
2659 | var params = {}, dummyClass = {}; | |
2660 | for(var name in proto){ | |
2661 | if(name.charAt(0)=="_"){ continue; } // skip internal properties | |
2662 | if(name in dummyClass){ continue; } // skip "constructor" and "toString" | |
2663 | var defVal = proto[name]; | |
2664 | params[name]=val2type(defVal); | |
2665 | } | |
2666 | ||
2667 | instanceClasses[className] = { cls: cls, params: params }; | |
2668 | } | |
2669 | return instanceClasses[className]; | |
2670 | } | |
2671 | ||
2672 | this._functionFromScript = function(script){ | |
2673 | var preamble = ""; | |
2674 | var suffix = ""; | |
2675 | var argsStr = script.getAttribute("args"); | |
2676 | if(argsStr){ | |
2677 | d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){ | |
2678 | preamble += "var "+part+" = arguments["+idx+"]; "; | |
2679 | }); | |
2680 | } | |
2681 | var withStr = script.getAttribute("with"); | |
2682 | if(withStr && withStr.length){ | |
2683 | d.forEach(withStr.split(/\s*,\s*/), function(part){ | |
2684 | preamble += "with("+part+"){"; | |
2685 | suffix += "}"; | |
2686 | }); | |
2687 | } | |
2688 | return new Function(preamble+script.innerHTML+suffix); | |
2689 | } | |
2690 | ||
2691 | this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){ | |
2692 | // summary: | |
2693 | // Takes array of nodes, and turns them into class instances and | |
2694 | // potentially calls a startup method to allow them to connect with | |
2695 | // any children. | |
2696 | // nodes: Array | |
2697 | // Array of nodes or objects like | |
2698 | // | { | |
2699 | // | type: "dijit.form.Button", | |
2700 | // | node: DOMNode, | |
2701 | // | scripts: [ ... ], // array of <script type="dojo/..."> children of node | |
2702 | // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. | |
2703 | // | } | |
2704 | // mixin: Object? | |
2705 | // An object that will be mixed in with each node in the array. | |
2706 | // Values in the mixin will override values in the node, if they | |
2707 | // exist. | |
2708 | // args: Object? | |
2709 | // An object used to hold kwArgs for instantiation. | |
2710 | // Supports 'noStart' and inherited. | |
2711 | var thelist = [], dp = dojo.parser; | |
2712 | mixin = mixin||{}; | |
2713 | args = args||{}; | |
2714 | ||
2715 | d.forEach(nodes, function(obj){ | |
2716 | if(!obj){ return; } | |
2717 | ||
2718 | // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.s | |
2719 | var node, type, clsInfo, clazz, scripts; | |
2720 | if(obj.node){ | |
2721 | // new format of nodes[] array, object w/lots of properties pre-computed for me | |
2722 | node = obj.node; | |
2723 | type = obj.type; | |
2724 | clsInfo = obj.clsInfo || (type && getClassInfo(type)); | |
2725 | clazz = clsInfo && clsInfo.cls; | |
2726 | scripts = obj.scripts; | |
2727 | }else{ | |
2728 | // old (backwards compatible) format of nodes[] array, simple array of DOMNodes | |
2729 | node = obj; | |
2730 | type = dp._attrName in mixin ? mixin[dp._attrName] : node.getAttribute(dp._attrName); | |
2731 | clsInfo = type && getClassInfo(type); | |
2732 | clazz = clsInfo && clsInfo.cls; | |
2733 | scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] : | |
2734 | d.query("> script[type^='dojo/']", node)); | |
2735 | } | |
2736 | if(!clsInfo){ | |
2737 | throw new Error("Could not load class '" + type); | |
2738 | } | |
2739 | ||
2740 | // Setup hash to hold parameter settings for this widget. Start with the parameter | |
2741 | // settings inherited from ancestors ("dir" and "lang"). | |
2742 | // Inherited setting may later be overridden by explicit settings on node itself. | |
2743 | var params = {}, | |
2744 | attributes = node.attributes; | |
2745 | if(args.defaults){ | |
2746 | // settings for the document itself (or whatever subtree is being parsed) | |
2747 | dojo.mixin(params, args.defaults); | |
2748 | } | |
2749 | if(obj.inherited){ | |
2750 | // settings from dir=rtl or lang=... on a node above this node | |
2751 | dojo.mixin(params, obj.inherited); | |
2752 | } | |
2753 | ||
2754 | // read parameters (ie, attributes) specified on DOMNode | |
2755 | // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"} | |
2756 | for(var name in clsInfo.params){ | |
2757 | var item = name in mixin?{value:mixin[name],specified:true}:attributes.getNamedItem(name); | |
2758 | if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; } | |
2759 | var value = item.value; | |
2760 | // Deal with IE quirks for 'class' and 'style' | |
2761 | switch(name){ | |
2762 | case "class": | |
2763 | value = "className" in mixin?mixin.className:node.className; | |
2764 | break; | |
2765 | case "style": | |
2766 | value = "style" in mixin?mixin.style:(node.style && node.style.cssText); // FIXME: Opera? | |
2767 | } | |
2768 | var _type = clsInfo.params[name]; | |
2769 | if(typeof value == "string"){ | |
2770 | params[name] = str2obj(value, _type); | |
2771 | }else{ | |
2772 | params[name] = value; | |
2773 | } | |
2774 | } | |
2775 | ||
2776 | // Process <script type="dojo/*"> script tags | |
2777 | // <script type="dojo/method" event="foo"> tags are added to params, and passed to | |
2778 | // the widget on instantiation. | |
2779 | // <script type="dojo/method"> tags (with no event) are executed after instantiation | |
2780 | // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation | |
2781 | // note: dojo/* script tags cannot exist in self closing widgets, like <input /> | |
2782 | var connects = [], // functions to connect after instantiation | |
2783 | calls = []; // functions to call after instantiation | |
2784 | ||
2785 | d.forEach(scripts, function(script){ | |
2786 | node.removeChild(script); | |
2787 | var event = script.getAttribute("event"), | |
2788 | type = script.getAttribute("type"), | |
2789 | nf = d.parser._functionFromScript(script); | |
2790 | if(event){ | |
2791 | if(type == "dojo/connect"){ | |
2792 | connects.push({event: event, func: nf}); | |
2793 | }else{ | |
2794 | params[event] = nf; | |
2795 | } | |
2796 | }else{ | |
2797 | calls.push(nf); | |
2798 | } | |
2799 | }); | |
2800 | ||
2801 | var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory; | |
2802 | // create the instance | |
2803 | var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node); | |
2804 | thelist.push(instance); | |
2805 | ||
2806 | // map it to the JS namespace if that makes sense | |
2807 | var jsname = node.getAttribute("jsId"); | |
2808 | if(jsname){ | |
2809 | d.setObject(jsname, instance); | |
2810 | } | |
2811 | ||
2812 | // process connections and startup functions | |
2813 | d.forEach(connects, function(connect){ | |
2814 | d.connect(instance, connect.event, null, connect.func); | |
2815 | }); | |
2816 | d.forEach(calls, function(func){ | |
2817 | func.call(instance); | |
2818 | }); | |
2819 | }); | |
2820 | ||
2821 | // Call startup on each top level instance if it makes sense (as for | |
2822 | // widgets). Parent widgets will recursively call startup on their | |
2823 | // (non-top level) children | |
2824 | if(!mixin._started){ | |
2825 | // TODO: for 2.0, when old instantiate() API is desupported, store parent-child | |
2826 | // relationships in the nodes[] array so that no getParent() call is needed. | |
2827 | // Note that will require a parse() call from ContentPane setting a param that the | |
2828 | // ContentPane is the parent widget (so that the parse doesn't call startup() on the | |
2829 | // ContentPane's children) | |
2830 | d.forEach(thelist, function(instance){ | |
2831 | if( !args.noStart && instance && | |
2832 | instance.startup && | |
2833 | !instance._started && | |
2834 | (!instance.getParent || !instance.getParent()) | |
2835 | ){ | |
2836 | instance.startup(); | |
2837 | } | |
2838 | }); | |
2839 | } | |
2840 | return thelist; | |
2841 | }; | |
2842 | ||
2843 | this.parse = function(/*DomNode?*/ rootNode, /* Object? */ args){ | |
2844 | // summary: | |
2845 | // Scan the DOM for class instances, and instantiate them. | |
2846 | // | |
2847 | // description: | |
2848 | // Search specified node (or root node) recursively for class instances, | |
2849 | // and instantiate them Searches for | |
2850 | // dojoType="qualified.class.name" | |
2851 | // | |
2852 | // rootNode: DomNode? | |
2853 | // A default starting root node from which to start the parsing. Can be | |
2854 | // omitted, defaulting to the entire document. If omitted, the `args` | |
2855 | // object can be passed in this place. If the `args` object has a | |
2856 | // `rootNode` member, that is used. | |
2857 | // | |
2858 | // args: | |
2859 | // a kwArgs object passed along to instantiate() | |
2860 | // | |
2861 | // * noStart: Boolean? | |
2862 | // when set will prevent the parser from calling .startup() | |
2863 | // when locating the nodes. | |
2864 | // * rootNode: DomNode? | |
2865 | // identical to the function's `rootNode` argument, though | |
2866 | // allowed to be passed in via this `args object. | |
2867 | // * inherited: Object | |
2868 | // Hash possibly containing dir and lang settings to be applied to | |
2869 | // parsed widgets, unless there's another setting on a sub-node that overrides | |
2870 | // | |
2871 | // | |
2872 | // example: | |
2873 | // Parse all widgets on a page: | |
2874 | // | dojo.parser.parse(); | |
2875 | // | |
2876 | // example: | |
2877 | // Parse all classes within the node with id="foo" | |
2878 | // | dojo.parser.parse(dojo.byId(foo)); | |
2879 | // | |
2880 | // example: | |
2881 | // Parse all classes in a page, but do not call .startup() on any | |
2882 | // child | |
2883 | // | dojo.parser.parse({ noStart: true }) | |
2884 | // | |
2885 | // example: | |
2886 | // Parse all classes in a node, but do not call .startup() | |
2887 | // | dojo.parser.parse(someNode, { noStart:true }); | |
2888 | // | // or | |
2889 | // | dojo.parser.parse({ noStart:true, rootNode: someNode }); | |
2890 | ||
2891 | // determine the root node based on the passed arguments. | |
2892 | var root; | |
2893 | if(!args && rootNode && rootNode.rootNode){ | |
2894 | args = rootNode; | |
2895 | root = args.rootNode; | |
2896 | }else{ | |
2897 | root = rootNode; | |
2898 | } | |
2899 | ||
2900 | var attrName = this._attrName; | |
2901 | function scan(parent, list){ | |
2902 | // summary: | |
2903 | // Parent is an Object representing a DOMNode, with or without a dojoType specified. | |
2904 | // Scan parent's children looking for nodes with dojoType specified, storing in list[]. | |
2905 | // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[]. | |
2906 | // parent: Object | |
2907 | // Object representing the parent node, like | |
2908 | // | { | |
2909 | // | node: DomNode, // scan children of this node | |
2910 | // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node | |
2911 | // | | |
2912 | // | // attributes only set if node has dojoType specified | |
2913 | // | scripts: [], // empty array, put <script type=dojo/*> in here | |
2914 | // | clsInfo: { cls: dijit.form.Button, ...} | |
2915 | // | } | |
2916 | // list: DomNode[] | |
2917 | // Output array of objects (same format as parent) representing nodes to be turned into widgets | |
2918 | ||
2919 | // Effective dir and lang settings on parent node, either set directly or inherited from grandparent | |
2920 | var inherited = dojo.clone(parent.inherited); | |
2921 | dojo.forEach(["dir", "lang"], function(name){ | |
2922 | var val = parent.node.getAttribute(name); | |
2923 | if(val){ | |
2924 | inherited[name] = val; | |
2925 | } | |
2926 | }); | |
2927 | ||
2928 | // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[]. | |
2929 | var scripts = parent.scripts; | |
2930 | ||
2931 | // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively | |
2932 | var recurse = !parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser; | |
2933 | ||
2934 | // scan parent's children looking for dojoType and <script type=dojo/*> | |
2935 | for(var child = parent.node.firstChild; child; child = child.nextSibling){ | |
2936 | if(child.nodeType == 1){ | |
2937 | var type = recurse && child.getAttribute(attrName); | |
2938 | if(type){ | |
2939 | // if dojoType specified, add to output array of nodes to instantiate | |
2940 | var params = { | |
2941 | "type": type, | |
2942 | clsInfo: getClassInfo(type), // note: won't find classes declared via dojo.Declaration | |
2943 | node: child, | |
2944 | scripts: [], // <script> nodes that are parent's children | |
2945 | inherited: inherited // dir & lang attributes inherited from parent | |
2946 | }; | |
2947 | list.push(params); | |
2948 | ||
2949 | // Recurse, collecting <script type="dojo/..."> children, and also looking for | |
2950 | // descendant nodes with dojoType specified (unless the widget has the stopParser flag), | |
2951 | scan(params, list); | |
2952 | }else if(scripts && child.nodeName.toLowerCase() == "script"){ | |
2953 | // if <script type="dojo/...">, save in scripts[] | |
2954 | type = child.getAttribute("type"); | |
2955 | if (type && /^dojo\//i.test(type)) { | |
2956 | scripts.push(child); | |
2957 | } | |
2958 | }else if(recurse){ | |
2959 | // Recurse, looking for grandchild nodes with dojoType specified | |
2960 | scan({ | |
2961 | node: child, | |
2962 | inherited: inherited | |
2963 | }, list); | |
2964 | } | |
2965 | } | |
2966 | } | |
2967 | } | |
2968 | ||
2969 | // Make list of all nodes on page w/dojoType specified | |
2970 | var list = []; | |
2971 | scan({ | |
2972 | node: root ? dojo.byId(root) : dojo.body(), | |
2973 | inherited: (args && args.inherited) || { | |
2974 | dir: dojo._isBodyLtr() ? "ltr" : "rtl" | |
2975 | } | |
2976 | }, list); | |
2977 | ||
2978 | // go build the object instances | |
2979 | return this.instantiate(list, null, args); // Array | |
2980 | }; | |
2981 | }(); | |
2982 | ||
2983 | //Register the parser callback. It should be the first callback | |
2984 | //after the a11y test. | |
2985 | ||
2986 | (function(){ | |
2987 | var parseRunner = function(){ | |
2988 | if(dojo.config.parseOnLoad){ | |
2989 | dojo.parser.parse(); | |
2990 | } | |
2991 | }; | |
2992 | ||
2993 | // FIXME: need to clobber cross-dependency!! | |
2994 | if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){ | |
2995 | dojo._loaders.splice(1, 0, parseRunner); | |
2996 | }else{ | |
2997 | dojo._loaders.unshift(parseRunner); | |
2998 | } | |
2999 | })(); | |
3000 | ||
3001 | } | |
3002 | ||
3003 | if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
3004 | dojo._hasResource["dijit._Widget"] = true; | |
3005 | dojo.provide("dijit._Widget"); | |
3006 | ||
3007 | dojo.require( "dijit._base" ); | |
3008 | ||
3009 | ||
3010 | // This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets' | |
3011 | // DOM nodes) until someone actually needs to monitor that event. | |
3012 | dojo.connect(dojo, "_connect", | |
3013 | function(/*dijit._Widget*/ widget, /*String*/ event){ | |
3014 | if(widget && dojo.isFunction(widget._onConnect)){ | |
3015 | widget._onConnect(event); | |
3016 | } | |
3017 | }); | |
3018 | ||
3019 | dijit._connectOnUseEventHandler = function(/*Event*/ event){}; | |
3020 | ||
3021 | // Keep track of where the last keydown event was, to help avoid generating | |
3022 | // spurious ondijitclick events when: | |
3023 | // 1. focus is on a <button> or <a> | |
3024 | // 2. user presses then releases the ENTER key | |
3025 | // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler | |
3026 | // 4. onkeyup event fires, causing the ondijitclick handler to fire | |
3027 | dijit._lastKeyDownNode = null; | |
3028 | if(dojo.isIE){ | |
3029 | (function(){ | |
3030 | var keydownCallback = function(evt){ | |
3031 | dijit._lastKeyDownNode = evt.srcElement; | |
3032 | }; | |
3033 | dojo.doc.attachEvent('onkeydown', keydownCallback); | |
3034 | dojo.addOnWindowUnload(function(){ | |
3035 | dojo.doc.detachEvent('onkeydown', keydownCallback); | |
3036 | }); | |
3037 | })(); | |
3038 | }else{ | |
3039 | dojo.doc.addEventListener('keydown', function(evt){ | |
3040 | dijit._lastKeyDownNode = evt.target; | |
3041 | }, true); | |
3042 | } | |
3043 | ||
3044 | (function(){ | |
3045 | ||
3046 | var _attrReg = {}, // cached results from getSetterAttributes | |
3047 | getSetterAttributes = function(widget){ | |
3048 | // summary: | |
3049 | // Returns list of attributes with custom setters for specified widget | |
3050 | var dc = widget.declaredClass; | |
3051 | if(!_attrReg[dc]){ | |
3052 | var r = [], | |
3053 | attrs, | |
3054 | proto = widget.constructor.prototype; | |
3055 | for(var fxName in proto){ | |
3056 | if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){ | |
3057 | r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1)); | |
3058 | } | |
3059 | } | |
3060 | _attrReg[dc] = r; | |
3061 | } | |
3062 | return _attrReg[dc] || []; // String[] | |
3063 | }; | |
3064 | ||
3065 | dojo.declare("dijit._Widget", null, { | |
3066 | // summary: | |
3067 | // Base class for all Dijit widgets. | |
3068 | ||
3069 | // id: [const] String | |
3070 | // A unique, opaque ID string that can be assigned by users or by the | |
3071 | // system. If the developer passes an ID which is known not to be | |
3072 | // unique, the specified ID is ignored and the system-generated ID is | |
3073 | // used instead. | |
3074 | id: "", | |
3075 | ||
3076 | // lang: [const] String | |
3077 | // Rarely used. Overrides the default Dojo locale used to render this widget, | |
3078 | // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute. | |
3079 | // Value must be among the list of locales specified during by the Dojo bootstrap, | |
3080 | // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us). | |
3081 | lang: "", | |
3082 | ||
3083 | // dir: [const] String | |
3084 | // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir) | |
3085 | // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's | |
3086 | // default direction. | |
3087 | dir: "", | |
3088 | ||
3089 | // class: String | |
3090 | // HTML class attribute | |
3091 | "class": "", | |
3092 | ||
3093 | // style: String||Object | |
3094 | // HTML style attributes as cssText string or name/value hash | |
3095 | style: "", | |
3096 | ||
3097 | // title: String | |
3098 | // HTML title attribute. | |
3099 | // | |
3100 | // For form widgets this specifies a tooltip to display when hovering over | |
3101 | // the widget (just like the native HTML title attribute). | |
3102 | // | |
3103 | // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer, | |
3104 | // etc., it's used to specify the tab label, accordion pane title, etc. | |
3105 | title: "", | |
3106 | ||
3107 | // tooltip: String | |
3108 | // When this widget's title attribute is used to for a tab label, accordion pane title, etc., | |
3109 | // this specifies the tooltip to appear when the mouse is hovered over that text. | |
3110 | tooltip: "", | |
3111 | ||
3112 | // baseClass: [protected] String | |
3113 | // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate | |
3114 | // widget state. | |
3115 | baseClass: "", | |
3116 | ||
3117 | // srcNodeRef: [readonly] DomNode | |
3118 | // pointer to original DOM node | |
3119 | srcNodeRef: null, | |
3120 | ||
3121 | // domNode: [readonly] DomNode | |
3122 | // This is our visible representation of the widget! Other DOM | |
3123 | // Nodes may by assigned to other properties, usually through the | |
3124 | // template system's dojoAttachPoint syntax, but the domNode | |
3125 | // property is the canonical "top level" node in widget UI. | |
3126 | domNode: null, | |
3127 | ||
3128 | // containerNode: [readonly] DomNode | |
3129 | // Designates where children of the source DOM node will be placed. | |
3130 | // "Children" in this case refers to both DOM nodes and widgets. | |
3131 | // For example, for myWidget: | |
3132 | // | |
3133 | // | <div dojoType=myWidget> | |
3134 | // | <b> here's a plain DOM node | |
3135 | // | <span dojoType=subWidget>and a widget</span> | |
3136 | // | <i> and another plain DOM node </i> | |
3137 | // | </div> | |
3138 | // | |
3139 | // containerNode would point to: | |
3140 | // | |
3141 | // | <b> here's a plain DOM node | |
3142 | // | <span dojoType=subWidget>and a widget</span> | |
3143 | // | <i> and another plain DOM node </i> | |
3144 | // | |
3145 | // In templated widgets, "containerNode" is set via a | |
3146 | // dojoAttachPoint assignment. | |
3147 | // | |
3148 | // containerNode must be defined for any widget that accepts innerHTML | |
3149 | // (like ContentPane or BorderContainer or even Button), and conversely | |
3150 | // is null for widgets that don't, like TextBox. | |
3151 | containerNode: null, | |
3152 | ||
3153 | /*===== | |
3154 | // _started: Boolean | |
3155 | // startup() has completed. | |
3156 | _started: false, | |
3157 | =====*/ | |
3158 | ||
3159 | // attributeMap: [protected] Object | |
3160 | // attributeMap sets up a "binding" between attributes (aka properties) | |
3161 | // of the widget and the widget's DOM. | |
3162 | // Changes to widget attributes listed in attributeMap will be | |
3163 | // reflected into the DOM. | |
3164 | // | |
3165 | // For example, calling attr('title', 'hello') | |
3166 | // on a TitlePane will automatically cause the TitlePane's DOM to update | |
3167 | // with the new title. | |
3168 | // | |
3169 | // attributeMap is a hash where the key is an attribute of the widget, | |
3170 | // and the value reflects a binding to a: | |
3171 | // | |
3172 | // - DOM node attribute | |
3173 | // | focus: {node: "focusNode", type: "attribute"} | |
3174 | // Maps this.focus to this.focusNode.focus | |
3175 | // | |
3176 | // - DOM node innerHTML | |
3177 | // | title: { node: "titleNode", type: "innerHTML" } | |
3178 | // Maps this.title to this.titleNode.innerHTML | |
3179 | // | |
3180 | // - DOM node innerText | |
3181 | // | title: { node: "titleNode", type: "innerText" } | |
3182 | // Maps this.title to this.titleNode.innerText | |
3183 | // | |
3184 | // - DOM node CSS class | |
3185 | // | myClass: { node: "domNode", type: "class" } | |
3186 | // Maps this.myClass to this.domNode.className | |
3187 | // | |
3188 | // If the value is an array, then each element in the array matches one of the | |
3189 | // formats of the above list. | |
3190 | // | |
3191 | // There are also some shorthands for backwards compatibility: | |
3192 | // - string --> { node: string, type: "attribute" }, for example: | |
3193 | // | "focusNode" ---> { node: "focusNode", type: "attribute" } | |
3194 | // - "" --> { node: "domNode", type: "attribute" } | |
3195 | attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""}, | |
3196 | ||
3197 | // _deferredConnects: [protected] Object | |
3198 | // attributeMap addendum for event handlers that should be connected only on first use | |
3199 | _deferredConnects: { | |
3200 | onClick: "", | |
3201 | onDblClick: "", | |
3202 | onKeyDown: "", | |
3203 | onKeyPress: "", | |
3204 | onKeyUp: "", | |
3205 | onMouseMove: "", | |
3206 | onMouseDown: "", | |
3207 | onMouseOut: "", | |
3208 | onMouseOver: "", | |
3209 | onMouseLeave: "", | |
3210 | onMouseEnter: "", | |
3211 | onMouseUp: "" | |
3212 | }, | |
3213 | ||
3214 | onClick: dijit._connectOnUseEventHandler, | |
3215 | /*===== | |
3216 | onClick: function(event){ | |
3217 | // summary: | |
3218 | // Connect to this function to receive notifications of mouse click events. | |
3219 | // event: | |
3220 | // mouse Event | |
3221 | // tags: | |
3222 | // callback | |
3223 | }, | |
3224 | =====*/ | |
3225 | onDblClick: dijit._connectOnUseEventHandler, | |
3226 | /*===== | |
3227 | onDblClick: function(event){ | |
3228 | // summary: | |
3229 | // Connect to this function to receive notifications of mouse double click events. | |
3230 | // event: | |
3231 | // mouse Event | |
3232 | // tags: | |
3233 | // callback | |
3234 | }, | |
3235 | =====*/ | |
3236 | onKeyDown: dijit._connectOnUseEventHandler, | |
3237 | /*===== | |
3238 | onKeyDown: function(event){ | |
3239 | // summary: | |
3240 | // Connect to this function to receive notifications of keys being pressed down. | |
3241 | // event: | |
3242 | // key Event | |
3243 | // tags: | |
3244 | // callback | |
3245 | }, | |
3246 | =====*/ | |
3247 | onKeyPress: dijit._connectOnUseEventHandler, | |
3248 | /*===== | |
3249 | onKeyPress: function(event){ | |
3250 | // summary: | |
3251 | // Connect to this function to receive notifications of printable keys being typed. | |
3252 | // event: | |
3253 | // key Event | |
3254 | // tags: | |
3255 | // callback | |
3256 | }, | |
3257 | =====*/ | |
3258 | onKeyUp: dijit._connectOnUseEventHandler, | |
3259 | /*===== | |
3260 | onKeyUp: function(event){ | |
3261 | // summary: | |
3262 | // Connect to this function to receive notifications of keys being released. | |
3263 | // event: | |
3264 | // key Event | |
3265 | // tags: | |
3266 | // callback | |
3267 | }, | |
3268 | =====*/ | |
3269 | onMouseDown: dijit._connectOnUseEventHandler, | |
3270 | /*===== | |
3271 | onMouseDown: function(event){ | |
3272 | // summary: | |
3273 | // Connect to this function to receive notifications of when the mouse button is pressed down. | |
3274 | // event: | |
3275 | // mouse Event | |
3276 | // tags: | |
3277 | // callback | |
3278 | }, | |
3279 | =====*/ | |
3280 | onMouseMove: dijit._connectOnUseEventHandler, | |
3281 | /*===== | |
3282 | onMouseMove: function(event){ | |
3283 | // summary: | |
3284 | // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget. | |
3285 | // event: | |
3286 | // mouse Event | |
3287 | // tags: | |
3288 | // callback | |
3289 | }, | |
3290 | =====*/ | |
3291 | onMouseOut: dijit._connectOnUseEventHandler, | |
3292 | /*===== | |
3293 | onMouseOut: function(event){ | |
3294 | // summary: | |
3295 | // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget. | |
3296 | // event: | |
3297 | // mouse Event | |
3298 | // tags: | |
3299 | // callback | |
3300 | }, | |
3301 | =====*/ | |
3302 | onMouseOver: dijit._connectOnUseEventHandler, | |
3303 | /*===== | |
3304 | onMouseOver: function(event){ | |
3305 | // summary: | |
3306 | // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget. | |
3307 | // event: | |
3308 | // mouse Event | |
3309 | // tags: | |
3310 | // callback | |
3311 | }, | |
3312 | =====*/ | |
3313 | onMouseLeave: dijit._connectOnUseEventHandler, | |
3314 | /*===== | |
3315 | onMouseLeave: function(event){ | |
3316 | // summary: | |
3317 | // Connect to this function to receive notifications of when the mouse moves off of this widget. | |
3318 | // event: | |
3319 | // mouse Event | |
3320 | // tags: | |
3321 | // callback | |
3322 | }, | |
3323 | =====*/ | |
3324 | onMouseEnter: dijit._connectOnUseEventHandler, | |
3325 | /*===== | |
3326 | onMouseEnter: function(event){ | |
3327 | // summary: | |
3328 | // Connect to this function to receive notifications of when the mouse moves onto this widget. | |
3329 | // event: | |
3330 | // mouse Event | |
3331 | // tags: | |
3332 | // callback | |
3333 | }, | |
3334 | =====*/ | |
3335 | onMouseUp: dijit._connectOnUseEventHandler, | |
3336 | /*===== | |
3337 | onMouseUp: function(event){ | |
3338 | // summary: | |
3339 | // Connect to this function to receive notifications of when the mouse button is released. | |
3340 | // event: | |
3341 | // mouse Event | |
3342 | // tags: | |
3343 | // callback | |
3344 | }, | |
3345 | =====*/ | |
3346 | ||
3347 | // Constants used in templates | |
3348 | ||
3349 | // _blankGif: [protected] String | |
3350 | // Path to a blank 1x1 image. | |
3351 | // Used by <img> nodes in templates that really get their image via CSS background-image. | |
3352 | _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(), | |
3353 | ||
3354 | //////////// INITIALIZATION METHODS /////////////////////////////////////// | |
3355 | ||
3356 | postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){ | |
3357 | // summary: | |
3358 | // Kicks off widget instantiation. See create() for details. | |
3359 | // tags: | |
3360 | // private | |
3361 | this.create(params, srcNodeRef); | |
3362 | }, | |
3363 | ||
3364 | create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){ | |
3365 | // summary: | |
3366 | // Kick off the life-cycle of a widget | |
3367 | // params: | |
3368 | // Hash of initialization parameters for widget, including | |
3369 | // scalar values (like title, duration etc.) and functions, | |
3370 | // typically callbacks like onClick. | |
3371 | // srcNodeRef: | |
3372 | // If a srcNodeRef (DOM node) is specified: | |
3373 | // - use srcNodeRef.innerHTML as my contents | |
3374 | // - if this is a behavioral widget then apply behavior | |
3375 | // to that srcNodeRef | |
3376 | // - otherwise, replace srcNodeRef with my generated DOM | |
3377 | // tree | |
3378 | // description: | |
3379 | // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate, | |
3380 | // etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget | |
3381 | // for a discussion of the widget creation lifecycle. | |
3382 | // | |
3383 | // Of course, adventurous developers could override create entirely, but this should | |
3384 | // only be done as a last resort. | |
3385 | // tags: | |
3386 | // private | |
3387 | ||
3388 | // store pointer to original DOM tree | |
3389 | this.srcNodeRef = dojo.byId(srcNodeRef); | |
3390 | ||
3391 | // For garbage collection. An array of handles returned by Widget.connect() | |
3392 | // Each handle returned from Widget.connect() is an array of handles from dojo.connect() | |
3393 | this._connects = []; | |
3394 | ||
3395 | // For garbage collection. An array of handles returned by Widget.subscribe() | |
3396 | // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe() | |
3397 | this._subscribes = []; | |
3398 | ||
3399 | // To avoid double-connects, remove entries from _deferredConnects | |
3400 | // that have been setup manually by a subclass (ex, by dojoAttachEvent). | |
3401 | // If a subclass has redefined a callback (ex: onClick) then assume it's being | |
3402 | // connected to manually. | |
3403 | this._deferredConnects = dojo.clone(this._deferredConnects); | |
3404 | for(var attr in this.attributeMap){ | |
3405 | delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects | |
3406 | } | |
3407 | for(attr in this._deferredConnects){ | |
3408 | if(this[attr] !== dijit._connectOnUseEventHandler){ | |
3409 | delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists | |
3410 | } | |
3411 | } | |
3412 | ||
3413 | //mixin our passed parameters | |
3414 | if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; } | |
3415 | if(params){ | |
3416 | this.params = params; | |
3417 | dojo.mixin(this,params); | |
3418 | } | |
3419 | this.postMixInProperties(); | |
3420 | ||
3421 | // generate an id for the widget if one wasn't specified | |
3422 | // (be sure to do this before buildRendering() because that function might | |
3423 | // expect the id to be there.) | |
3424 | if(!this.id){ | |
3425 | this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_")); | |
3426 | } | |
3427 | dijit.registry.add(this); | |
3428 | ||
3429 | this.buildRendering(); | |
3430 | ||
3431 | if(this.domNode){ | |
3432 | // Copy attributes listed in attributeMap into the [newly created] DOM for the widget. | |
3433 | this._applyAttributes(); | |
3434 | ||
3435 | var source = this.srcNodeRef; | |
3436 | if(source && source.parentNode){ | |
3437 | source.parentNode.replaceChild(this.domNode, source); | |
3438 | } | |
3439 | ||
3440 | // If the developer has specified a handler as a widget parameter | |
3441 | // (ex: new Button({onClick: ...}) | |
3442 | // then naturally need to connect from DOM node to that handler immediately, | |
3443 | for(attr in this.params){ | |
3444 | this._onConnect(attr); | |
3445 | } | |
3446 | } | |
3447 | ||
3448 | if(this.domNode){ | |
3449 | this.domNode.setAttribute("widgetId", this.id); | |
3450 | } | |
3451 | this.postCreate(); | |
3452 | ||
3453 | // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC. | |
3454 | if(this.srcNodeRef && !this.srcNodeRef.parentNode){ | |
3455 | delete this.srcNodeRef; | |
3456 | } | |
3457 | ||
3458 | this._created = true; | |
3459 | }, | |
3460 | ||
3461 | _applyAttributes: function(){ | |
3462 | // summary: | |
3463 | // Step during widget creation to copy all widget attributes to the | |
3464 | // DOM as per attributeMap and _setXXXAttr functions. | |
3465 | // description: | |
3466 | // Skips over blank/false attribute values, unless they were explicitly specified | |
3467 | // as parameters to the widget, since those are the default anyway, | |
3468 | // and setting tabIndex="" is different than not setting tabIndex at all. | |
3469 | // | |
3470 | // It processes the attributes in the attribute map first, and then | |
3471 | // it goes through and processes the attributes for the _setXXXAttr | |
3472 | // functions that have been specified | |
3473 | // tags: | |
3474 | // private | |
3475 | var condAttrApply = function(attr, scope){ | |
3476 | if((scope.params && attr in scope.params) || scope[attr]){ | |
3477 | scope.set(attr, scope[attr]); | |
3478 | } | |
3479 | }; | |
3480 | ||
3481 | // Do the attributes in attributeMap | |
3482 | for(var attr in this.attributeMap){ | |
3483 | condAttrApply(attr, this); | |
3484 | } | |
3485 | ||
3486 | // And also any attributes with custom setters | |
3487 | dojo.forEach(getSetterAttributes(this), function(a){ | |
3488 | if(!(a in this.attributeMap)){ | |
3489 | condAttrApply(a, this); | |
3490 | } | |
3491 | }, this); | |
3492 | }, | |
3493 | ||
3494 | postMixInProperties: function(){ | |
3495 | // summary: | |
3496 | // Called after the parameters to the widget have been read-in, | |
3497 | // but before the widget template is instantiated. Especially | |
3498 | // useful to set properties that are referenced in the widget | |
3499 | // template. | |
3500 | // tags: | |
3501 | // protected | |
3502 | }, | |
3503 | ||
3504 | buildRendering: function(){ | |
3505 | // summary: | |
3506 | // Construct the UI for this widget, setting this.domNode | |
3507 | // description: | |
3508 | // Most widgets will mixin `dijit._Templated`, which implements this | |
3509 | // method. | |
3510 | // tags: | |
3511 | // protected | |
3512 | this.domNode = this.srcNodeRef || dojo.create('div'); | |
3513 | }, | |
3514 | ||
3515 | postCreate: function(){ | |
3516 | // summary: | |
3517 | // Processing after the DOM fragment is created | |
3518 | // description: | |
3519 | // Called after the DOM fragment has been created, but not necessarily | |
3520 | // added to the document. Do not include any operations which rely on | |
3521 | // node dimensions or placement. | |
3522 | // tags: | |
3523 | // protected | |
3524 | ||
3525 | // baseClass is a single class name or occasionally a space-separated list of names. | |
3526 | // Add those classes to the DOMNod. If RTL mode then also add with Rtl suffix. | |
3527 | if(this.baseClass){ | |
3528 | var classes = this.baseClass.split(" "); | |
3529 | if(!this.isLeftToRight()){ | |
3530 | classes = classes.concat( dojo.map(classes, function(name){ return name+"Rtl"; })); | |
3531 | } | |
3532 | dojo.addClass(this.domNode, classes); | |
3533 | } | |
3534 | }, | |
3535 | ||
3536 | startup: function(){ | |
3537 | // summary: | |
3538 | // Processing after the DOM fragment is added to the document | |
3539 | // description: | |
3540 | // Called after a widget and its children have been created and added to the page, | |
3541 | // and all related widgets have finished their create() cycle, up through postCreate(). | |
3542 | // This is useful for composite widgets that need to control or layout sub-widgets. | |
3543 | // Many layout widgets can use this as a wiring phase. | |
3544 | this._started = true; | |
3545 | }, | |
3546 | ||
3547 | //////////// DESTROY FUNCTIONS //////////////////////////////// | |
3548 | ||
3549 | destroyRecursive: function(/*Boolean?*/ preserveDom){ | |
3550 | // summary: | |
3551 | // Destroy this widget and its descendants | |
3552 | // description: | |
3553 | // This is the generic "destructor" function that all widget users | |
3554 | // should call to cleanly discard with a widget. Once a widget is | |
3555 | // destroyed, it is removed from the manager object. | |
3556 | // preserveDom: | |
3557 | // If true, this method will leave the original DOM structure | |
3558 | // alone of descendant Widgets. Note: This will NOT work with | |
3559 | // dijit._Templated widgets. | |
3560 | ||
3561 | this._beingDestroyed = true; | |
3562 | this.destroyDescendants(preserveDom); | |
3563 | this.destroy(preserveDom); | |
3564 | }, | |
3565 | ||
3566 | destroy: function(/*Boolean*/ preserveDom){ | |
3567 | // summary: | |
3568 | // Destroy this widget, but not its descendants. | |
3569 | // This method will, however, destroy internal widgets such as those used within a template. | |
3570 | // preserveDom: Boolean | |
3571 | // If true, this method will leave the original DOM structure alone. | |
3572 | // Note: This will not yet work with _Templated widgets | |
3573 | ||
3574 | this._beingDestroyed = true; | |
3575 | this.uninitialize(); | |
3576 | var d = dojo, | |
3577 | dfe = d.forEach, | |
3578 | dun = d.unsubscribe; | |
3579 | dfe(this._connects, function(array){ | |
3580 | dfe(array, d.disconnect); | |
3581 | }); | |
3582 | dfe(this._subscribes, function(handle){ | |
3583 | dun(handle); | |
3584 | }); | |
3585 | ||
3586 | // destroy widgets created as part of template, etc. | |
3587 | dfe(this._supportingWidgets || [], function(w){ | |
3588 | if(w.destroyRecursive){ | |
3589 | w.destroyRecursive(); | |
3590 | }else if(w.destroy){ | |
3591 | w.destroy(); | |
3592 | } | |
3593 | }); | |
3594 | ||
3595 | this.destroyRendering(preserveDom); | |
3596 | dijit.registry.remove(this.id); | |
3597 | this._destroyed = true; | |
3598 | }, | |
3599 | ||
3600 | destroyRendering: function(/*Boolean?*/ preserveDom){ | |
3601 | // summary: | |
3602 | // Destroys the DOM nodes associated with this widget | |
3603 | // preserveDom: | |
3604 | // If true, this method will leave the original DOM structure alone | |
3605 | // during tear-down. Note: this will not work with _Templated | |
3606 | // widgets yet. | |
3607 | // tags: | |
3608 | // protected | |
3609 | ||
3610 | if(this.bgIframe){ | |
3611 | this.bgIframe.destroy(preserveDom); | |
3612 | delete this.bgIframe; | |
3613 | } | |
3614 | ||
3615 | if(this.domNode){ | |
3616 | if(preserveDom){ | |
3617 | dojo.removeAttr(this.domNode, "widgetId"); | |
3618 | }else{ | |
3619 | dojo.destroy(this.domNode); | |
3620 | } | |
3621 | delete this.domNode; | |
3622 | } | |
3623 | ||
3624 | if(this.srcNodeRef){ | |
3625 | if(!preserveDom){ | |
3626 | dojo.destroy(this.srcNodeRef); | |
3627 | } | |
3628 | delete this.srcNodeRef; | |
3629 | } | |
3630 | }, | |
3631 | ||
3632 | destroyDescendants: function(/*Boolean?*/ preserveDom){ | |
3633 | // summary: | |
3634 | // Recursively destroy the children of this widget and their | |
3635 | // descendants. | |
3636 | // preserveDom: | |
3637 | // If true, the preserveDom attribute is passed to all descendant | |
3638 | // widget's .destroy() method. Not for use with _Templated | |
3639 | // widgets. | |
3640 | ||
3641 | // get all direct descendants and destroy them recursively | |
3642 | dojo.forEach(this.getChildren(), function(widget){ | |
3643 | if(widget.destroyRecursive){ | |
3644 | widget.destroyRecursive(preserveDom); | |
3645 | } | |
3646 | }); | |
3647 | }, | |
3648 | ||
3649 | ||
3650 | uninitialize: function(){ | |
3651 | // summary: | |
3652 | // Stub function. Override to implement custom widget tear-down | |
3653 | // behavior. | |
3654 | // tags: | |
3655 | // protected | |
3656 | return false; | |
3657 | }, | |
3658 | ||
3659 | ////////////////// MISCELLANEOUS METHODS /////////////////// | |
3660 | ||
3661 | onFocus: function(){ | |
3662 | // summary: | |
3663 | // Called when the widget becomes "active" because | |
3664 | // it or a widget inside of it either has focus, or has recently | |
3665 | // been clicked. | |
3666 | // tags: | |
3667 | // callback | |
3668 | }, | |
3669 | ||
3670 | onBlur: function(){ | |
3671 | // summary: | |
3672 | // Called when the widget stops being "active" because | |
3673 | // focus moved to something outside of it, or the user | |
3674 | // clicked somewhere outside of it, or the widget was | |
3675 | // hidden. | |
3676 | // tags: | |
3677 | // callback | |
3678 | }, | |
3679 | ||
3680 | _onFocus: function(e){ | |
3681 | // summary: | |
3682 | // This is where widgets do processing for when they are active, | |
3683 | // such as changing CSS classes. See onFocus() for more details. | |
3684 | // tags: | |
3685 | // protected | |
3686 | this.onFocus(); | |
3687 | }, | |
3688 | ||
3689 | _onBlur: function(){ | |
3690 | // summary: | |
3691 | // This is where widgets do processing for when they stop being active, | |
3692 | // such as changing CSS classes. See onBlur() for more details. | |
3693 | // tags: | |
3694 | // protected | |
3695 | this.onBlur(); | |
3696 | }, | |
3697 | ||
3698 | _onConnect: function(/*String*/ event){ | |
3699 | // summary: | |
3700 | // Called when someone connects to one of my handlers. | |
3701 | // "Turn on" that handler if it isn't active yet. | |
3702 | // | |
3703 | // This is also called for every single initialization parameter | |
3704 | // so need to do nothing for parameters like "id". | |
3705 | // tags: | |
3706 | // private | |
3707 | if(event in this._deferredConnects){ | |
3708 | var mapNode = this[this._deferredConnects[event] || 'domNode']; | |
3709 | this.connect(mapNode, event.toLowerCase(), event); | |
3710 | delete this._deferredConnects[event]; | |
3711 | } | |
3712 | }, | |
3713 | ||
3714 | _setClassAttr: function(/*String*/ value){ | |
3715 | // summary: | |
3716 | // Custom setter for the CSS "class" attribute | |
3717 | // tags: | |
3718 | // protected | |
3719 | var mapNode = this[this.attributeMap["class"] || 'domNode']; | |
3720 | dojo.removeClass(mapNode, this["class"]) | |
3721 | this["class"] = value; | |
3722 | dojo.addClass(mapNode, value); | |
3723 | }, | |
3724 | ||
3725 | _setStyleAttr: function(/*String||Object*/ value){ | |
3726 | // summary: | |
3727 | // Sets the style attribut of the widget according to value, | |
3728 | // which is either a hash like {height: "5px", width: "3px"} | |
3729 | // or a plain string | |
3730 | // description: | |
3731 | // Determines which node to set the style on based on style setting | |
3732 | // in attributeMap. | |
3733 | // tags: | |
3734 | // protected | |
3735 | ||
3736 | var mapNode = this[this.attributeMap.style || 'domNode']; | |
3737 | ||
3738 | // Note: technically we should revert any style setting made in a previous call | |
3739 | // to his method, but that's difficult to keep track of. | |
3740 | ||
3741 | if(dojo.isObject(value)){ | |
3742 | dojo.style(mapNode, value); | |
3743 | }else{ | |
3744 | if(mapNode.style.cssText){ | |
3745 | mapNode.style.cssText += "; " + value; | |
3746 | }else{ | |
3747 | mapNode.style.cssText = value; | |
3748 | } | |
3749 | } | |
3750 | ||
3751 | this.style = value; | |
3752 | }, | |
3753 | ||
3754 | setAttribute: function(/*String*/ attr, /*anything*/ value){ | |
3755 | // summary: | |
3756 | // Deprecated. Use set() instead. | |
3757 | // tags: | |
3758 | // deprecated | |
3759 | dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0"); | |
3760 | this.set(attr, value); | |
3761 | }, | |
3762 | ||
3763 | _attrToDom: function(/*String*/ attr, /*String*/ value){ | |
3764 | // summary: | |
3765 | // Reflect a widget attribute (title, tabIndex, duration etc.) to | |
3766 | // the widget DOM, as specified in attributeMap. | |
3767 | // | |
3768 | // description: | |
3769 | // Also sets this["attr"] to the new value. | |
3770 | // Note some attributes like "type" | |
3771 | // cannot be processed this way as they are not mutable. | |
3772 | // | |
3773 | // tags: | |
3774 | // private | |
3775 | ||
3776 | var commands = this.attributeMap[attr]; | |
3777 | dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){ | |
3778 | ||
3779 | // Get target node and what we are doing to that node | |
3780 | var mapNode = this[command.node || command || "domNode"]; // DOM node | |
3781 | var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute | |
3782 | ||
3783 | switch(type){ | |
3784 | case "attribute": | |
3785 | if(dojo.isFunction(value)){ // functions execute in the context of the widget | |
3786 | value = dojo.hitch(this, value); | |
3787 | } | |
3788 | ||
3789 | // Get the name of the DOM node attribute; usually it's the same | |
3790 | // as the name of the attribute in the widget (attr), but can be overridden. | |
3791 | // Also maps handler names to lowercase, like onSubmit --> onsubmit | |
3792 | var attrName = command.attribute ? command.attribute : | |
3793 | (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr); | |
3794 | ||
3795 | dojo.attr(mapNode, attrName, value); | |
3796 | break; | |
3797 | case "innerText": | |
3798 | mapNode.innerHTML = ""; | |
3799 | mapNode.appendChild(dojo.doc.createTextNode(value)); | |
3800 | break; | |
3801 | case "innerHTML": | |
3802 | mapNode.innerHTML = value; | |
3803 | break; | |
3804 | case "class": | |
3805 | dojo.removeClass(mapNode, this[attr]); | |
3806 | dojo.addClass(mapNode, value); | |
3807 | break; | |
3808 | } | |
3809 | }, this); | |
3810 | this[attr] = value; | |
3811 | }, | |
3812 | ||
3813 | attr: function(/*String|Object*/name, /*Object?*/value){ | |
3814 | // summary: | |
3815 | // Set or get properties on a widget instance. | |
3816 | // name: | |
3817 | // The property to get or set. If an object is passed here and not | |
3818 | // a string, its keys are used as names of attributes to be set | |
3819 | // and the value of the object as values to set in the widget. | |
3820 | // value: | |
3821 | // Optional. If provided, attr() operates as a setter. If omitted, | |
3822 | // the current value of the named property is returned. | |
3823 | // description: | |
3824 | // This method is deprecated, use get() or set() directly. | |
3825 | ||
3826 | // Print deprecation warning but only once per calling function | |
3827 | if(dojo.config.isDebug){ | |
3828 | var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}), | |
3829 | caller = (arguments.callee.caller || "unknown caller").toString(); | |
3830 | if(!alreadyCalledHash[caller]){ | |
3831 | dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " + | |
3832 | caller, "", "2.0"); | |
3833 | alreadyCalledHash[caller] = true; | |
3834 | } | |
3835 | } | |
3836 | ||
3837 | var args = arguments.length; | |
3838 | if(args >= 2 || typeof name === "object"){ // setter | |
3839 | return this.set.apply(this, arguments); | |
3840 | }else{ // getter | |
3841 | return this.get(name); | |
3842 | } | |
3843 | }, | |
3844 | ||
3845 | get: function(name){ | |
3846 | // summary: | |
3847 | // Get a property from a widget. | |
3848 | // name: | |
3849 | // The property to get. | |
3850 | // description: | |
3851 | // Get a named property from a widget. The property may | |
3852 | // potentially be retrieved via a getter method. If no getter is defined, this | |
3853 | // just retrieves the object's property. | |
3854 | // For example, if the widget has a properties "foo" | |
3855 | // and "bar" and a method named "_getFooAttr", calling: | |
3856 | // | myWidget.get("foo"); | |
3857 | // would be equivalent to writing: | |
3858 | // | widget._getFooAttr(); | |
3859 | // and: | |
3860 | // | myWidget.get("bar"); | |
3861 | // would be equivalent to writing: | |
3862 | // | widget.bar; | |
3863 | var names = this._getAttrNames(name); | |
3864 | return this[names.g] ? this[names.g]() : this[name]; | |
3865 | }, | |
3866 | ||
3867 | set: function(name, value){ | |
3868 | // summary: | |
3869 | // Set a property on a widget | |
3870 | // name: | |
3871 | // The property to set. | |
3872 | // value: | |
3873 | // The value to set in the property. | |
3874 | // description: | |
3875 | // Sets named properties on a widget which may potentially be handled by a | |
3876 | // setter in the widget. | |
3877 | // For example, if the widget has a properties "foo" | |
3878 | // and "bar" and a method named "_setFooAttr", calling: | |
3879 | // | myWidget.set("foo", "Howdy!"); | |
3880 | // would be equivalent to writing: | |
3881 | // | widget._setFooAttr("Howdy!"); | |
3882 | // and: | |
3883 | // | myWidget.set("bar", 3); | |
3884 | // would be equivalent to writing: | |
3885 | // | widget.bar = 3; | |
3886 | // | |
3887 | // set() may also be called with a hash of name/value pairs, ex: | |
3888 | // | myWidget.set({ | |
3889 | // | foo: "Howdy", | |
3890 | // | bar: 3 | |
3891 | // | }) | |
3892 | // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) | |
3893 | ||
3894 | if(typeof name === "object"){ | |
3895 | for(var x in name){ | |
3896 | this.set(x, name[x]); | |
3897 | } | |
3898 | return this; | |
3899 | } | |
3900 | var names = this._getAttrNames(name); | |
3901 | if(this[names.s]){ | |
3902 | // use the explicit setter | |
3903 | var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1)); | |
3904 | }else{ | |
3905 | // if param is specified as DOM node attribute, copy it | |
3906 | if(name in this.attributeMap){ | |
3907 | this._attrToDom(name, value); | |
3908 | } | |
3909 | var oldValue = this[name]; | |
3910 | // FIXME: what about function assignments? Any way to connect() here? | |
3911 | this[name] = value; | |
3912 | } | |
3913 | return result || this; | |
3914 | }, | |
3915 | ||
3916 | _attrPairNames: {}, // shared between all widgets | |
3917 | _getAttrNames: function(name){ | |
3918 | // summary: | |
3919 | // Helper function for get() and set(). | |
3920 | // Caches attribute name values so we don't do the string ops every time. | |
3921 | // tags: | |
3922 | // private | |
3923 | ||
3924 | var apn = this._attrPairNames; | |
3925 | if(apn[name]){ return apn[name]; } | |
3926 | var uc = name.charAt(0).toUpperCase() + name.substr(1); | |
3927 | return (apn[name] = { | |
3928 | n: name+"Node", | |
3929 | s: "_set"+uc+"Attr", | |
3930 | g: "_get"+uc+"Attr" | |
3931 | }); | |
3932 | }, | |
3933 | ||
3934 | toString: function(){ | |
3935 | // summary: | |
3936 | // Returns a string that represents the widget | |
3937 | // description: | |
3938 | // When a widget is cast to a string, this method will be used to generate the | |
3939 | // output. Currently, it does not implement any sort of reversible | |
3940 | // serialization. | |
3941 | return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String | |
3942 | }, | |
3943 | ||
3944 | getDescendants: function(){ | |
3945 | // summary: | |
3946 | // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. | |
3947 | // This method should generally be avoided as it returns widgets declared in templates, which are | |
3948 | // supposed to be internal/hidden, but it's left here for back-compat reasons. | |
3949 | ||
3950 | return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[] | |
3951 | }, | |
3952 | ||
3953 | getChildren: function(){ | |
3954 | // summary: | |
3955 | // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. | |
3956 | // Does not return nested widgets, nor widgets that are part of this widget's template. | |
3957 | return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[] | |
3958 | }, | |
3959 | ||
3960 | // nodesWithKeyClick: [private] String[] | |
3961 | // List of nodes that correctly handle click events via native browser support, | |
3962 | // and don't need dijit's help | |
3963 | nodesWithKeyClick: ["input", "button"], | |
3964 | ||
3965 | connect: function( | |
3966 | /*Object|null*/ obj, | |
3967 | /*String|Function*/ event, | |
3968 | /*String|Function*/ method){ | |
3969 | // summary: | |
3970 | // Connects specified obj/event to specified method of this object | |
3971 | // and registers for disconnect() on widget destroy. | |
3972 | // description: | |
3973 | // Provide widget-specific analog to dojo.connect, except with the | |
3974 | // implicit use of this widget as the target object. | |
3975 | // This version of connect also provides a special "ondijitclick" | |
3976 | // event which triggers on a click or space or enter keyup | |
3977 | // returns: | |
3978 | // A handle that can be passed to `disconnect` in order to disconnect before | |
3979 | // the widget is destroyed. | |
3980 | // example: | |
3981 | // | var btn = new dijit.form.Button(); | |
3982 | // | // when foo.bar() is called, call the listener we're going to | |
3983 | // | // provide in the scope of btn | |
3984 | // | btn.connect(foo, "bar", function(){ | |
3985 | // | console.debug(this.toString()); | |
3986 | // | }); | |
3987 | // tags: | |
3988 | // protected | |
3989 | ||
3990 | var d = dojo, | |
3991 | dc = d._connect, | |
3992 | handles = []; | |
3993 | if(event == "ondijitclick"){ | |
3994 | // add key based click activation for unsupported nodes. | |
3995 | // do all processing onkey up to prevent spurious clicks | |
3996 | // for details see comments at top of this file where _lastKeyDownNode is defined | |
3997 | if(dojo.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button | |
3998 | var m = d.hitch(this, method); | |
3999 | handles.push( | |
4000 | dc(obj, "onkeydown", this, function(e){ | |
4001 | //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode)); | |
4002 | if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) && | |
4003 | !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ | |
4004 | // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work | |
4005 | dijit._lastKeyDownNode = e.target; | |
4006 | e.preventDefault(); // stop event to prevent scrolling on space key in IE | |
4007 | } | |
4008 | }), | |
4009 | dc(obj, "onkeyup", this, function(e){ | |
4010 | //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode)); | |
4011 | if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) && | |
4012 | e.target === dijit._lastKeyDownNode && | |
4013 | !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ | |
4014 | //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert | |
4015 | dijit._lastKeyDownNode = null; | |
4016 | return m(e); | |
4017 | } | |
4018 | }) | |
4019 | ); | |
4020 | } | |
4021 | event = "onclick"; | |
4022 | } | |
4023 | handles.push(dc(obj, event, this, method)); | |
4024 | ||
4025 | this._connects.push(handles); | |
4026 | return handles; // _Widget.Handle | |
4027 | }, | |
4028 | ||
4029 | disconnect: function(/* _Widget.Handle */ handles){ | |
4030 | // summary: | |
4031 | // Disconnects handle created by `connect`. | |
4032 | // Also removes handle from this widget's list of connects. | |
4033 | // tags: | |
4034 | // protected | |
4035 | for(var i=0; i<this._connects.length; i++){ | |
4036 | if(this._connects[i] == handles){ | |
4037 | dojo.forEach(handles, dojo.disconnect); | |
4038 | this._connects.splice(i, 1); | |
4039 | return; | |
4040 | } | |
4041 | } | |
4042 | }, | |
4043 | ||
4044 | subscribe: function( | |
4045 | /*String*/ topic, | |
4046 | /*String|Function*/ method){ | |
4047 | // summary: | |
4048 | // Subscribes to the specified topic and calls the specified method | |
4049 | // of this object and registers for unsubscribe() on widget destroy. | |
4050 | // description: | |
4051 | // Provide widget-specific analog to dojo.subscribe, except with the | |
4052 | // implicit use of this widget as the target object. | |
4053 | // example: | |
4054 | // | var btn = new dijit.form.Button(); | |
4055 | // | // when /my/topic is published, this button changes its label to | |
4056 | // | // be the parameter of the topic. | |
4057 | // | btn.subscribe("/my/topic", function(v){ | |
4058 | // | this.set("label", v); | |
4059 | // | }); | |
4060 | var d = dojo, | |
4061 | handle = d.subscribe(topic, this, method); | |
4062 | ||
4063 | // return handles for Any widget that may need them | |
4064 | this._subscribes.push(handle); | |
4065 | return handle; | |
4066 | }, | |
4067 | ||
4068 | unsubscribe: function(/*Object*/ handle){ | |
4069 | // summary: | |
4070 | // Unsubscribes handle created by this.subscribe. | |
4071 | // Also removes handle from this widget's list of subscriptions | |
4072 | for(var i=0; i<this._subscribes.length; i++){ | |
4073 | if(this._subscribes[i] == handle){ | |
4074 | dojo.unsubscribe(handle); | |
4075 | this._subscribes.splice(i, 1); | |
4076 | return; | |
4077 | } | |
4078 | } | |
4079 | }, | |
4080 | ||
4081 | isLeftToRight: function(){ | |
4082 | // summary: | |
4083 | // Return this widget's explicit or implicit orientation (true for LTR, false for RTL) | |
4084 | // tags: | |
4085 | // protected | |
4086 | return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean | |
4087 | }, | |
4088 | ||
4089 | isFocusable: function(){ | |
4090 | // summary: | |
4091 | // Return true if this widget can currently be focused | |
4092 | // and false if not | |
4093 | return this.focus && (dojo.style(this.domNode, "display") != "none"); | |
4094 | }, | |
4095 | ||
4096 | placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){ | |
4097 | // summary: | |
4098 | // Place this widget's domNode reference somewhere in the DOM based | |
4099 | // on standard dojo.place conventions, or passing a Widget reference that | |
4100 | // contains and addChild member. | |
4101 | // | |
4102 | // description: | |
4103 | // A convenience function provided in all _Widgets, providing a simple | |
4104 | // shorthand mechanism to put an existing (or newly created) Widget | |
4105 | // somewhere in the dom, and allow chaining. | |
4106 | // | |
4107 | // reference: | |
4108 | // The String id of a domNode, a domNode reference, or a reference to a Widget posessing | |
4109 | // an addChild method. | |
4110 | // | |
4111 | // position: | |
4112 | // If passed a string or domNode reference, the position argument | |
4113 | // accepts a string just as dojo.place does, one of: "first", "last", | |
4114 | // "before", or "after". | |
4115 | // | |
4116 | // If passed a _Widget reference, and that widget reference has an ".addChild" method, | |
4117 | // it will be called passing this widget instance into that method, supplying the optional | |
4118 | // position index passed. | |
4119 | // | |
4120 | // returns: | |
4121 | // dijit._Widget | |
4122 | // Provides a useful return of the newly created dijit._Widget instance so you | |
4123 | // can "chain" this function by instantiating, placing, then saving the return value | |
4124 | // to a variable. | |
4125 | // | |
4126 | // example: | |
4127 | // | // create a Button with no srcNodeRef, and place it in the body: | |
4128 | // | var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body()); | |
4129 | // | // now, 'button' is still the widget reference to the newly created button | |
4130 | // | dojo.connect(button, "onClick", function(e){ console.log('click'); }); | |
4131 | // | |
4132 | // example: | |
4133 | // | // create a button out of a node with id="src" and append it to id="wrapper": | |
4134 | // | var button = new dijit.form.Button({},"src").placeAt("wrapper"); | |
4135 | // | |
4136 | // example: | |
4137 | // | // place a new button as the first element of some div | |
4138 | // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first"); | |
4139 | // | |
4140 | // example: | |
4141 | // | // create a contentpane and add it to a TabContainer | |
4142 | // | var tc = dijit.byId("myTabs"); | |
4143 | // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc) | |
4144 | ||
4145 | if(reference.declaredClass && reference.addChild){ | |
4146 | reference.addChild(this, position); | |
4147 | }else{ | |
4148 | dojo.place(this.domNode, reference, position); | |
4149 | } | |
4150 | return this; | |
4151 | }, | |
4152 | ||
4153 | _onShow: function(){ | |
4154 | // summary: | |
4155 | // Internal method called when this widget is made visible. | |
4156 | // See `onShow` for details. | |
4157 | this.onShow(); | |
4158 | }, | |
4159 | ||
4160 | onShow: function(){ | |
4161 | // summary: | |
4162 | // Called when this widget becomes the selected pane in a | |
4163 | // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, | |
4164 | // `dijit.layout.AccordionContainer`, etc. | |
4165 | // | |
4166 | // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. | |
4167 | // tags: | |
4168 | // callback | |
4169 | }, | |
4170 | ||
4171 | onHide: function(){ | |
4172 | // summary: | |
4173 | // Called when another widget becomes the selected pane in a | |
4174 | // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, | |
4175 | // `dijit.layout.AccordionContainer`, etc. | |
4176 | // | |
4177 | // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. | |
4178 | // tags: | |
4179 | // callback | |
4180 | }, | |
4181 | ||
4182 | onClose: function(){ | |
4183 | // summary: | |
4184 | // Called when this widget is being displayed as a popup (ex: a Calendar popped | |
4185 | // up from a DateTextBox), and it is hidden. | |
4186 | // This is called from the dijit.popup code, and should not be called directly. | |
4187 | // | |
4188 | // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses. | |
4189 | // Callback if a user tries to close the child. Child will be closed if this function returns true. | |
4190 | // tags: | |
4191 | // extension | |
4192 | ||
4193 | return true; // Boolean | |
4194 | } | |
4195 | }); | |
4196 | ||
4197 | })(); | |
4198 | ||
4199 | } | |
4200 | ||
4201 | if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
4202 | dojo._hasResource["dojo.string"] = true; | |
4203 | dojo.provide("dojo.string"); | |
4204 | ||
4205 | /*===== | |
4206 | dojo.string = { | |
4207 | // summary: String utilities for Dojo | |
4208 | }; | |
4209 | =====*/ | |
4210 | ||
4211 | dojo.string.rep = function(/*String*/str, /*Integer*/num){ | |
4212 | // summary: | |
4213 | // Efficiently replicate a string `n` times. | |
4214 | // str: | |
4215 | // the string to replicate | |
4216 | // num: | |
4217 | // number of times to replicate the string | |
4218 | ||
4219 | if(num <= 0 || !str){ return ""; } | |
4220 | ||
4221 | var buf = []; | |
4222 | for(;;){ | |
4223 | if(num & 1){ | |
4224 | buf.push(str); | |
4225 | } | |
4226 | if(!(num >>= 1)){ break; } | |
4227 | str += str; | |
4228 | } | |
4229 | return buf.join(""); // String | |
4230 | }; | |
4231 | ||
4232 | dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){ | |
4233 | // summary: | |
4234 | // Pad a string to guarantee that it is at least `size` length by | |
4235 | // filling with the character `ch` at either the start or end of the | |
4236 | // string. Pads at the start, by default. | |
4237 | // text: | |
4238 | // the string to pad | |
4239 | // size: | |
4240 | // length to provide padding | |
4241 | // ch: | |
4242 | // character to pad, defaults to '0' | |
4243 | // end: | |
4244 | // adds padding at the end if true, otherwise pads at start | |
4245 | // example: | |
4246 | // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++". | |
4247 | // | dojo.string.pad("Dojo", 10, "+", true); | |
4248 | ||
4249 | if(!ch){ | |
4250 | ch = '0'; | |
4251 | } | |
4252 | var out = String(text), | |
4253 | pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length)); | |
4254 | return end ? out + pad : pad + out; // String | |
4255 | }; | |
4256 | ||
4257 | dojo.string.substitute = function( /*String*/ template, | |
4258 | /*Object|Array*/map, | |
4259 | /*Function?*/ transform, | |
4260 | /*Object?*/ thisObject){ | |
4261 | // summary: | |
4262 | // Performs parameterized substitutions on a string. Throws an | |
4263 | // exception if any parameter is unmatched. | |
4264 | // template: | |
4265 | // a string with expressions in the form `${key}` to be replaced or | |
4266 | // `${key:format}` which specifies a format function. keys are case-sensitive. | |
4267 | // map: | |
4268 | // hash to search for substitutions | |
4269 | // transform: | |
4270 | // a function to process all parameters before substitution takes | |
4271 | // place, e.g. mylib.encodeXML | |
4272 | // thisObject: | |
4273 | // where to look for optional format function; default to the global | |
4274 | // namespace | |
4275 | // example: | |
4276 | // Substitutes two expressions in a string from an Array or Object | |
4277 | // | // returns "File 'foo.html' is not found in directory '/temp'." | |
4278 | // | // by providing substitution data in an Array | |
4279 | // | dojo.string.substitute( | |
4280 | // | "File '${0}' is not found in directory '${1}'.", | |
4281 | // | ["foo.html","/temp"] | |
4282 | // | ); | |
4283 | // | | |
4284 | // | // also returns "File 'foo.html' is not found in directory '/temp'." | |
4285 | // | // but provides substitution data in an Object structure. Dotted | |
4286 | // | // notation may be used to traverse the structure. | |
4287 | // | dojo.string.substitute( | |
4288 | // | "File '${name}' is not found in directory '${info.dir}'.", | |
4289 | // | { name: "foo.html", info: { dir: "/temp" } } | |
4290 | // | ); | |
4291 | // example: | |
4292 | // Use a transform function to modify the values: | |
4293 | // | // returns "file 'foo.html' is not found in directory '/temp'." | |
4294 | // | dojo.string.substitute( | |
4295 | // | "${0} is not found in ${1}.", | |
4296 | // | ["foo.html","/temp"], | |
4297 | // | function(str){ | |
4298 | // | // try to figure out the type | |
4299 | // | var prefix = (str.charAt(0) == "/") ? "directory": "file"; | |
4300 | // | return prefix + " '" + str + "'"; | |
4301 | // | } | |
4302 | // | ); | |
4303 | // example: | |
4304 | // Use a formatter | |
4305 | // | // returns "thinger -- howdy" | |
4306 | // | dojo.string.substitute( | |
4307 | // | "${0:postfix}", ["thinger"], null, { | |
4308 | // | postfix: function(value, key){ | |
4309 | // | return value + " -- howdy"; | |
4310 | // | } | |
4311 | // | } | |
4312 | // | ); | |
4313 | ||
4314 | thisObject = thisObject || dojo.global; | |
4315 | transform = transform ? | |
4316 | dojo.hitch(thisObject, transform) : function(v){ return v; }; | |
4317 | ||
4318 | return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, | |
4319 | function(match, key, format){ | |
4320 | var value = dojo.getObject(key, false, map); | |
4321 | if(format){ | |
4322 | value = dojo.getObject(format, false, thisObject).call(thisObject, value, key); | |
4323 | } | |
4324 | return transform(value, key).toString(); | |
4325 | }); // String | |
4326 | }; | |
4327 | ||
4328 | /*===== | |
4329 | dojo.string.trim = function(str){ | |
4330 | // summary: | |
4331 | // Trims whitespace from both sides of the string | |
4332 | // str: String | |
4333 | // String to be trimmed | |
4334 | // returns: String | |
4335 | // Returns the trimmed string | |
4336 | // description: | |
4337 | // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript). | |
4338 | // The short yet performant version of this function is dojo.trim(), | |
4339 | // which is part of Dojo base. Uses String.prototype.trim instead, if available. | |
4340 | return ""; // String | |
4341 | } | |
4342 | =====*/ | |
4343 | ||
4344 | dojo.string.trim = String.prototype.trim ? | |
4345 | dojo.trim : // aliasing to the native function | |
4346 | function(str){ | |
4347 | str = str.replace(/^\s+/, ''); | |
4348 | for(var i = str.length - 1; i >= 0; i--){ | |
4349 | if(/\S/.test(str.charAt(i))){ | |
4350 | str = str.substring(0, i + 1); | |
4351 | break; | |
4352 | } | |
4353 | } | |
4354 | return str; | |
4355 | }; | |
4356 | ||
4357 | } | |
4358 | ||
4359 | if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
4360 | dojo._hasResource["dojo.cache"] = true; | |
4361 | dojo.provide("dojo.cache"); | |
4362 | ||
4363 | /*===== | |
4364 | dojo.cache = { | |
4365 | // summary: | |
4366 | // A way to cache string content that is fetchable via `dojo.moduleUrl`. | |
4367 | }; | |
4368 | =====*/ | |
4369 | ||
4370 | (function(){ | |
4371 | var cache = {}; | |
4372 | dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){ | |
4373 | // summary: | |
4374 | // A getter and setter for storing the string content associated with the | |
4375 | // module and url arguments. | |
4376 | // description: | |
4377 | // module and url are used to call `dojo.moduleUrl()` to generate a module URL. | |
4378 | // If value is specified, the cache value for the moduleUrl will be set to | |
4379 | // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it | |
4380 | // in its internal cache and return that cached value for the URL. To clear | |
4381 | // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the | |
4382 | // the URL contents, only modules on the same domain of the page can use this capability. | |
4383 | // The build system can inline the cache values though, to allow for xdomain hosting. | |
4384 | // module: String||Object | |
4385 | // If a String, the module name to use for the base part of the URL, similar to module argument | |
4386 | // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that | |
4387 | // generates a valid path for the cache item. For example, a dojo._Url object. | |
4388 | // url: String | |
4389 | // The rest of the path to append to the path derived from the module argument. If | |
4390 | // module is an object, then this second argument should be the "value" argument instead. | |
4391 | // value: String||Object? | |
4392 | // If a String, the value to use in the cache for the module/url combination. | |
4393 | // If an Object, it can have two properties: value and sanitize. The value property | |
4394 | // should be the value to use in the cache, and sanitize can be set to true or false, | |
4395 | // to indicate if XML declarations should be removed from the value and if the HTML | |
4396 | // inside a body tag in the value should be extracted as the real value. The value argument | |
4397 | // or the value property on the value argument are usually only used by the build system | |
4398 | // as it inlines cache content. | |
4399 | // example: | |
4400 | // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style | |
4401 | // of call is used to avoid an issue with the build system erroneously trying to intern | |
4402 | // this example. To get the build system to intern your dojo.cache calls, use the | |
4403 | // "dojo.cache" style of call): | |
4404 | // | //If template.html contains "<h1>Hello</h1>" that will be | |
4405 | // | //the value for the text variable. | |
4406 | // | var text = dojo["cache"]("my.module", "template.html"); | |
4407 | // example: | |
4408 | // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input | |
4409 | // (the dojo["cache"] style of call is used to avoid an issue with the build system | |
4410 | // erroneously trying to intern this example. To get the build system to intern your | |
4411 | // dojo.cache calls, use the "dojo.cache" style of call): | |
4412 | // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the | |
4413 | // | //text variable will contain just "<h1>Hello</h1>". | |
4414 | // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true}); | |
4415 | // example: | |
4416 | // Same example as previous, but demostrates how an object can be passed in as | |
4417 | // the first argument, then the value argument can then be the second argument. | |
4418 | // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the | |
4419 | // | //text variable will contain just "<h1>Hello</h1>". | |
4420 | // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true}); | |
4421 | ||
4422 | //Module could be a string, or an object that has a toString() method | |
4423 | //that will return a useful path. If it is an object, then the "url" argument | |
4424 | //will actually be the value argument. | |
4425 | if(typeof module == "string"){ | |
4426 | var pathObj = dojo.moduleUrl(module, url); | |
4427 | }else{ | |
4428 | pathObj = module; | |
4429 | value = url; | |
4430 | } | |
4431 | var key = pathObj.toString(); | |
4432 | ||
4433 | var val = value; | |
4434 | if(value != undefined && !dojo.isString(value)){ | |
4435 | val = ("value" in value ? value.value : undefined); | |
4436 | } | |
4437 | ||
4438 | var sanitize = value && value.sanitize ? true : false; | |
4439 | ||
4440 | if(typeof val == "string"){ | |
4441 | //We have a string, set cache value | |
4442 | val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val; | |
4443 | }else if(val === null){ | |
4444 | //Remove cached value | |
4445 | delete cache[key]; | |
4446 | }else{ | |
4447 | //Allow cache values to be empty strings. If key property does | |
4448 | //not exist, fetch it. | |
4449 | if(!(key in cache)){ | |
4450 | val = dojo._getText(key); | |
4451 | cache[key] = sanitize ? dojo.cache._sanitize(val) : val; | |
4452 | } | |
4453 | val = cache[key]; | |
4454 | } | |
4455 | return val; //String | |
4456 | }; | |
4457 | ||
4458 | dojo.cache._sanitize = function(/*String*/val){ | |
4459 | // summary: | |
4460 | // Strips <?xml ...?> declarations so that external SVG and XML | |
4461 | // documents can be added to a document without worry. Also, if the string | |
4462 | // is an HTML document, only the part inside the body tag is returned. | |
4463 | // description: | |
4464 | // Copied from dijit._Templated._sanitizeTemplateString. | |
4465 | if(val){ | |
4466 | val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, ""); | |
4467 | var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); | |
4468 | if(matches){ | |
4469 | val = matches[1]; | |
4470 | } | |
4471 | }else{ | |
4472 | val = ""; | |
4473 | } | |
4474 | return val; //String | |
4475 | }; | |
4476 | })(); | |
4477 | ||
4478 | } | |
4479 | ||
4480 | if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
4481 | dojo._hasResource["dijit._Templated"] = true; | |
4482 | dojo.provide("dijit._Templated"); | |
4483 | ||
4484 | ||
4485 | ||
4486 | ||
4487 | ||
4488 | ||
4489 | dojo.declare("dijit._Templated", | |
4490 | null, | |
4491 | { | |
4492 | // summary: | |
4493 | // Mixin for widgets that are instantiated from a template | |
4494 | ||
4495 | // templateString: [protected] String | |
4496 | // A string that represents the widget template. Pre-empts the | |
4497 | // templatePath. In builds that have their strings "interned", the | |
4498 | // templatePath is converted to an inline templateString, thereby | |
4499 | // preventing a synchronous network call. | |
4500 | // | |
4501 | // Use in conjunction with dojo.cache() to load from a file. | |
4502 | templateString: null, | |
4503 | ||
4504 | // templatePath: [protected deprecated] String | |
4505 | // Path to template (HTML file) for this widget relative to dojo.baseUrl. | |
4506 | // Deprecated: use templateString with dojo.cache() instead. | |
4507 | templatePath: null, | |
4508 | ||
4509 | // widgetsInTemplate: [protected] Boolean | |
4510 | // Should we parse the template to find widgets that might be | |
4511 | // declared in markup inside it? False by default. | |
4512 | widgetsInTemplate: false, | |
4513 | ||
4514 | // skipNodeCache: [protected] Boolean | |
4515 | // If using a cached widget template node poses issues for a | |
4516 | // particular widget class, it can set this property to ensure | |
4517 | // that its template is always re-built from a string | |
4518 | _skipNodeCache: false, | |
4519 | ||
4520 | // _earlyTemplatedStartup: Boolean | |
4521 | // A fallback to preserve the 1.0 - 1.3 behavior of children in | |
4522 | // templates having their startup called before the parent widget | |
4523 | // fires postCreate. Defaults to 'false', causing child widgets to | |
4524 | // have their .startup() called immediately before a parent widget | |
4525 | // .startup(), but always after the parent .postCreate(). Set to | |
4526 | // 'true' to re-enable to previous, arguably broken, behavior. | |
4527 | _earlyTemplatedStartup: false, | |
4528 | ||
4529 | // _attachPoints: [private] String[] | |
4530 | // List of widget attribute names associated with dojoAttachPoint=... in the | |
4531 | // template, ex: ["containerNode", "labelNode"] | |
4532 | /*===== | |
4533 | _attachPoints: [], | |
4534 | =====*/ | |
4535 | ||
4536 | constructor: function(){ | |
4537 | this._attachPoints = []; | |
4538 | }, | |
4539 | ||
4540 | _stringRepl: function(tmpl){ | |
4541 | // summary: | |
4542 | // Does substitution of ${foo} type properties in template string | |
4543 | // tags: | |
4544 | // private | |
4545 | var className = this.declaredClass, _this = this; | |
4546 | // Cache contains a string because we need to do property replacement | |
4547 | // do the property replacement | |
4548 | return dojo.string.substitute(tmpl, this, function(value, key){ | |
4549 | if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); } | |
4550 | if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide | |
4551 | if(value == null){ return ""; } | |
4552 | ||
4553 | // Substitution keys beginning with ! will skip the transform step, | |
4554 | // in case a user wishes to insert unescaped markup, e.g. ${!foo} | |
4555 | return key.charAt(0) == "!" ? value : | |
4556 | // Safer substitution, see heading "Attribute values" in | |
4557 | // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 | |
4558 | value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method? | |
4559 | }, this); | |
4560 | }, | |
4561 | ||
4562 | // method over-ride | |
4563 | buildRendering: function(){ | |
4564 | // summary: | |
4565 | // Construct the UI for this widget from a template, setting this.domNode. | |
4566 | // tags: | |
4567 | // protected | |
4568 | ||
4569 | // Lookup cached version of template, and download to cache if it | |
4570 | // isn't there already. Returns either a DomNode or a string, depending on | |
4571 | // whether or not the template contains ${foo} replacement parameters. | |
4572 | var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache); | |
4573 | ||
4574 | var node; | |
4575 | if(dojo.isString(cached)){ | |
4576 | node = dojo._toDom(this._stringRepl(cached)); | |
4577 | if(node.nodeType != 1){ | |
4578 | // Flag common problems such as templates with multiple top level nodes (nodeType == 11) | |
4579 | throw new Error("Invalid template: " + cached); | |
4580 | } | |
4581 | }else{ | |
4582 | // if it's a node, all we have to do is clone it | |
4583 | node = cached.cloneNode(true); | |
4584 | } | |
4585 | ||
4586 | this.domNode = node; | |
4587 | ||
4588 | // recurse through the node, looking for, and attaching to, our | |
4589 | // attachment points and events, which should be defined on the template node. | |
4590 | this._attachTemplateNodes(node); | |
4591 | ||
4592 | if(this.widgetsInTemplate){ | |
4593 | // Make sure dojoType is used for parsing widgets in template. | |
4594 | // The dojo.parser.query could be changed from multiversion support. | |
4595 | var parser = dojo.parser, qry, attr; | |
4596 | if(parser._query != "[dojoType]"){ | |
4597 | qry = parser._query; | |
4598 | attr = parser._attrName; | |
4599 | parser._query = "[dojoType]"; | |
4600 | parser._attrName = "dojoType"; | |
4601 | } | |
4602 | ||
4603 | // Store widgets that we need to start at a later point in time | |
4604 | var cw = (this._startupWidgets = dojo.parser.parse(node, { | |
4605 | noStart: !this._earlyTemplatedStartup, | |
4606 | inherited: {dir: this.dir, lang: this.lang} | |
4607 | })); | |
4608 | ||
4609 | // Restore the query. | |
4610 | if(qry){ | |
4611 | parser._query = qry; | |
4612 | parser._attrName = attr; | |
4613 | } | |
4614 | ||
4615 | this._supportingWidgets = dijit.findWidgets(node); | |
4616 | ||
4617 | this._attachTemplateNodes(cw, function(n,p){ | |
4618 | return n[p]; | |
4619 | }); | |
4620 | } | |
4621 | ||
4622 | this._fillContent(this.srcNodeRef); | |
4623 | }, | |
4624 | ||
4625 | _fillContent: function(/*DomNode*/ source){ | |
4626 | // summary: | |
4627 | // Relocate source contents to templated container node. | |
4628 | // this.containerNode must be able to receive children, or exceptions will be thrown. | |
4629 | // tags: | |
4630 | // protected | |
4631 | var dest = this.containerNode; | |
4632 | if(source && dest){ | |
4633 | while(source.hasChildNodes()){ | |
4634 | dest.appendChild(source.firstChild); | |
4635 | } | |
4636 | } | |
4637 | }, | |
4638 | ||
4639 | _attachTemplateNodes: function(rootNode, getAttrFunc){ | |
4640 | // summary: | |
4641 | // Iterate through the template and attach functions and nodes accordingly. | |
4642 | // description: | |
4643 | // Map widget properties and functions to the handlers specified in | |
4644 | // the dom node and it's descendants. This function iterates over all | |
4645 | // nodes and looks for these properties: | |
4646 | // * dojoAttachPoint | |
4647 | // * dojoAttachEvent | |
4648 | // * waiRole | |
4649 | // * waiState | |
4650 | // rootNode: DomNode|Array[Widgets] | |
4651 | // the node to search for properties. All children will be searched. | |
4652 | // getAttrFunc: Function? | |
4653 | // a function which will be used to obtain property for a given | |
4654 | // DomNode/Widget | |
4655 | // tags: | |
4656 | // private | |
4657 | ||
4658 | getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); }; | |
4659 | ||
4660 | var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); | |
4661 | var x = dojo.isArray(rootNode) ? 0 : -1; | |
4662 | for(; x<nodes.length; x++){ | |
4663 | var baseNode = (x == -1) ? rootNode : nodes[x]; | |
4664 | if(this.widgetsInTemplate && getAttrFunc(baseNode, "dojoType")){ | |
4665 | continue; | |
4666 | } | |
4667 | // Process dojoAttachPoint | |
4668 | var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint"); | |
4669 | if(attachPoint){ | |
4670 | var point, points = attachPoint.split(/\s*,\s*/); | |
4671 | while((point = points.shift())){ | |
4672 | if(dojo.isArray(this[point])){ | |
4673 | this[point].push(baseNode); | |
4674 | }else{ | |
4675 | this[point]=baseNode; | |
4676 | } | |
4677 | this._attachPoints.push(point); | |
4678 | } | |
4679 | } | |
4680 | ||
4681 | // Process dojoAttachEvent | |
4682 | var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent"); | |
4683 | if(attachEvent){ | |
4684 | // NOTE: we want to support attributes that have the form | |
4685 | // "domEvent: nativeEvent; ..." | |
4686 | var event, events = attachEvent.split(/\s*,\s*/); | |
4687 | var trim = dojo.trim; | |
4688 | while((event = events.shift())){ | |
4689 | if(event){ | |
4690 | var thisFunc = null; | |
4691 | if(event.indexOf(":") != -1){ | |
4692 | // oh, if only JS had tuple assignment | |
4693 | var funcNameArr = event.split(":"); | |
4694 | event = trim(funcNameArr[0]); | |
4695 | thisFunc = trim(funcNameArr[1]); | |
4696 | }else{ | |
4697 | event = trim(event); | |
4698 | } | |
4699 | if(!thisFunc){ | |
4700 | thisFunc = event; | |
4701 | } | |
4702 | this.connect(baseNode, event, thisFunc); | |
4703 | } | |
4704 | } | |
4705 | } | |
4706 | ||
4707 | // waiRole, waiState | |
4708 | var role = getAttrFunc(baseNode, "waiRole"); | |
4709 | if(role){ | |
4710 | dijit.setWaiRole(baseNode, role); | |
4711 | } | |
4712 | var values = getAttrFunc(baseNode, "waiState"); | |
4713 | if(values){ | |
4714 | dojo.forEach(values.split(/\s*,\s*/), function(stateValue){ | |
4715 | if(stateValue.indexOf('-') != -1){ | |
4716 | var pair = stateValue.split('-'); | |
4717 | dijit.setWaiState(baseNode, pair[0], pair[1]); | |
4718 | } | |
4719 | }); | |
4720 | } | |
4721 | } | |
4722 | }, | |
4723 | ||
4724 | startup: function(){ | |
4725 | dojo.forEach(this._startupWidgets, function(w){ | |
4726 | if(w && !w._started && w.startup){ | |
4727 | w.startup(); | |
4728 | } | |
4729 | }); | |
4730 | this.inherited(arguments); | |
4731 | }, | |
4732 | ||
4733 | destroyRendering: function(){ | |
4734 | // Delete all attach points to prevent IE6 memory leaks. | |
4735 | dojo.forEach(this._attachPoints, function(point){ | |
4736 | delete this[point]; | |
4737 | }, this); | |
4738 | this._attachPoints = []; | |
4739 | ||
4740 | this.inherited(arguments); | |
4741 | } | |
4742 | } | |
4743 | ); | |
4744 | ||
4745 | // key is either templatePath or templateString; object is either string or DOM tree | |
4746 | dijit._Templated._templateCache = {}; | |
4747 | ||
4748 | dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){ | |
4749 | // summary: | |
4750 | // Static method to get a template based on the templatePath or | |
4751 | // templateString key | |
4752 | // templatePath: String||dojo.uri.Uri | |
4753 | // The URL to get the template from. | |
4754 | // templateString: String? | |
4755 | // a string to use in lieu of fetching the template from a URL. Takes precedence | |
4756 | // over templatePath | |
4757 | // returns: Mixed | |
4758 | // Either string (if there are ${} variables that need to be replaced) or just | |
4759 | // a DOM tree (if the node can be cloned directly) | |
4760 | ||
4761 | // is it already cached? | |
4762 | var tmplts = dijit._Templated._templateCache; | |
4763 | var key = templateString || templatePath; | |
4764 | var cached = tmplts[key]; | |
4765 | if(cached){ | |
4766 | try{ | |
4767 | // 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 | |
4768 | if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){ | |
4769 | // string or node of the same document | |
4770 | return cached; | |
4771 | } | |
4772 | }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded | |
4773 | dojo.destroy(cached); | |
4774 | } | |
4775 | ||
4776 | // If necessary, load template string from template path | |
4777 | if(!templateString){ | |
4778 | templateString = dojo.cache(templatePath, {sanitize: true}); | |
4779 | } | |
4780 | templateString = dojo.string.trim(templateString); | |
4781 | ||
4782 | if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){ | |
4783 | // there are variables in the template so all we can do is cache the string | |
4784 | return (tmplts[key] = templateString); //String | |
4785 | }else{ | |
4786 | // there are no variables in the template so we can cache the DOM tree | |
4787 | var node = dojo._toDom(templateString); | |
4788 | if(node.nodeType != 1){ | |
4789 | throw new Error("Invalid template: " + templateString); | |
4790 | } | |
4791 | return (tmplts[key] = node); //Node | |
4792 | } | |
4793 | }; | |
4794 | ||
4795 | if(dojo.isIE){ | |
4796 | dojo.addOnWindowUnload(function(){ | |
4797 | var cache = dijit._Templated._templateCache; | |
4798 | for(var key in cache){ | |
4799 | var value = cache[key]; | |
4800 | if(typeof value == "object"){ // value is either a string or a DOM node template | |
4801 | dojo.destroy(value); | |
4802 | } | |
4803 | delete cache[key]; | |
4804 | } | |
4805 | }); | |
4806 | } | |
4807 | ||
4808 | // These arguments can be specified for widgets which are used in templates. | |
4809 | // Since any widget can be specified as sub widgets in template, mix it | |
4810 | // into the base widget class. (This is a hack, but it's effective.) | |
4811 | dojo.extend(dijit._Widget,{ | |
4812 | dojoAttachEvent: "", | |
4813 | dojoAttachPoint: "", | |
4814 | waiRole: "", | |
4815 | waiState:"" | |
4816 | }); | |
4817 | ||
4818 | } | |
4819 | ||
4820 | if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
4821 | dojo._hasResource["dijit._Container"] = true; | |
4822 | dojo.provide("dijit._Container"); | |
4823 | ||
4824 | dojo.declare("dijit._Container", | |
4825 | null, | |
4826 | { | |
4827 | // summary: | |
4828 | // Mixin for widgets that contain a set of widget children. | |
4829 | // description: | |
4830 | // Use this mixin for widgets that needs to know about and | |
4831 | // keep track of their widget children. Suitable for widgets like BorderContainer | |
4832 | // and TabContainer which contain (only) a set of child widgets. | |
4833 | // | |
4834 | // It's not suitable for widgets like ContentPane | |
4835 | // which contains mixed HTML (plain DOM nodes in addition to widgets), | |
4836 | // and where contained widgets are not necessarily directly below | |
4837 | // this.containerNode. In that case calls like addChild(node, position) | |
4838 | // wouldn't make sense. | |
4839 | ||
4840 | // isContainer: [protected] Boolean | |
4841 | // Indicates that this widget acts as a "parent" to the descendant widgets. | |
4842 | // When the parent is started it will call startup() on the child widgets. | |
4843 | // See also `isLayoutContainer`. | |
4844 | isContainer: true, | |
4845 | ||
4846 | buildRendering: function(){ | |
4847 | this.inherited(arguments); | |
4848 | if(!this.containerNode){ | |
4849 | // all widgets with descendants must set containerNode | |
4850 | this.containerNode = this.domNode; | |
4851 | } | |
4852 | }, | |
4853 | ||
4854 | addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){ | |
4855 | // summary: | |
4856 | // Makes the given widget a child of this widget. | |
4857 | // description: | |
4858 | // Inserts specified child widget's dom node as a child of this widget's | |
4859 | // container node, and possibly does other processing (such as layout). | |
4860 | ||
4861 | var refNode = this.containerNode; | |
4862 | if(insertIndex && typeof insertIndex == "number"){ | |
4863 | var children = this.getChildren(); | |
4864 | if(children && children.length >= insertIndex){ | |
4865 | refNode = children[insertIndex-1].domNode; | |
4866 | insertIndex = "after"; | |
4867 | } | |
4868 | } | |
4869 | dojo.place(widget.domNode, refNode, insertIndex); | |
4870 | ||
4871 | // If I've been started but the child widget hasn't been started, | |
4872 | // start it now. Make sure to do this after widget has been | |
4873 | // inserted into the DOM tree, so it can see that it's being controlled by me, | |
4874 | // so it doesn't try to size itself. | |
4875 | if(this._started && !widget._started){ | |
4876 | widget.startup(); | |
4877 | } | |
4878 | }, | |
4879 | ||
4880 | removeChild: function(/*Widget or int*/ widget){ | |
4881 | // summary: | |
4882 | // Removes the passed widget instance from this widget but does | |
4883 | // not destroy it. You can also pass in an integer indicating | |
4884 | // the index within the container to remove | |
4885 | ||
4886 | if(typeof widget == "number" && widget > 0){ | |
4887 | widget = this.getChildren()[widget]; | |
4888 | } | |
4889 | ||
4890 | if(widget){ | |
4891 | var node = widget.domNode; | |
4892 | if(node && node.parentNode){ | |
4893 | node.parentNode.removeChild(node); // detach but don't destroy | |
4894 | } | |
4895 | } | |
4896 | }, | |
4897 | ||
4898 | hasChildren: function(){ | |
4899 | // summary: | |
4900 | // Returns true if widget has children, i.e. if this.containerNode contains something. | |
4901 | return this.getChildren().length > 0; // Boolean | |
4902 | }, | |
4903 | ||
4904 | destroyDescendants: function(/*Boolean*/ preserveDom){ | |
4905 | // summary: | |
4906 | // Destroys all the widgets inside this.containerNode, | |
4907 | // but not this widget itself | |
4908 | dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); }); | |
4909 | }, | |
4910 | ||
4911 | _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){ | |
4912 | // summary: | |
4913 | // Get the next or previous widget sibling of child | |
4914 | // dir: | |
4915 | // if 1, get the next sibling | |
4916 | // if -1, get the previous sibling | |
4917 | // tags: | |
4918 | // private | |
4919 | var node = child.domNode, | |
4920 | which = (dir>0 ? "nextSibling" : "previousSibling"); | |
4921 | do{ | |
4922 | node = node[which]; | |
4923 | }while(node && (node.nodeType != 1 || !dijit.byNode(node))); | |
4924 | return node && dijit.byNode(node); // dijit._Widget | |
4925 | }, | |
4926 | ||
4927 | getIndexOfChild: function(/*dijit._Widget*/ child){ | |
4928 | // summary: | |
4929 | // Gets the index of the child in this container or -1 if not found | |
4930 | return dojo.indexOf(this.getChildren(), child); // int | |
4931 | }, | |
4932 | ||
4933 | startup: function(){ | |
4934 | // summary: | |
4935 | // Called after all the widgets have been instantiated and their | |
4936 | // dom nodes have been inserted somewhere under dojo.doc.body. | |
4937 | // | |
4938 | // Widgets should override this method to do any initialization | |
4939 | // dependent on other widgets existing, and then call | |
4940 | // this superclass method to finish things off. | |
4941 | // | |
4942 | // startup() in subclasses shouldn't do anything | |
4943 | // size related because the size of the widget hasn't been set yet. | |
4944 | ||
4945 | if(this._started){ return; } | |
4946 | ||
4947 | // Startup all children of this widget | |
4948 | dojo.forEach(this.getChildren(), function(child){ child.startup(); }); | |
4949 | ||
4950 | this.inherited(arguments); | |
4951 | } | |
4952 | } | |
4953 | ); | |
4954 | ||
4955 | } | |
4956 | ||
4957 | if(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
4958 | dojo._hasResource["dijit._Contained"] = true; | |
4959 | dojo.provide("dijit._Contained"); | |
4960 | ||
4961 | dojo.declare("dijit._Contained", | |
4962 | null, | |
4963 | { | |
4964 | // summary: | |
4965 | // Mixin for widgets that are children of a container widget | |
4966 | // | |
4967 | // example: | |
4968 | // | // make a basic custom widget that knows about it's parents | |
4969 | // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{}); | |
4970 | ||
4971 | getParent: function(){ | |
4972 | // summary: | |
4973 | // Returns the parent widget of this widget, assuming the parent | |
4974 | // specifies isContainer | |
4975 | var parent = dijit.getEnclosingWidget(this.domNode.parentNode); | |
4976 | return parent && parent.isContainer ? parent : null; | |
4977 | }, | |
4978 | ||
4979 | _getSibling: function(/*String*/ which){ | |
4980 | // summary: | |
4981 | // Returns next or previous sibling | |
4982 | // which: | |
4983 | // Either "next" or "previous" | |
4984 | // tags: | |
4985 | // private | |
4986 | var node = this.domNode; | |
4987 | do{ | |
4988 | node = node[which+"Sibling"]; | |
4989 | }while(node && node.nodeType != 1); | |
4990 | return node && dijit.byNode(node); // dijit._Widget | |
4991 | }, | |
4992 | ||
4993 | getPreviousSibling: function(){ | |
4994 | // summary: | |
4995 | // Returns null if this is the first child of the parent, | |
4996 | // otherwise returns the next element sibling to the "left". | |
4997 | ||
4998 | return this._getSibling("previous"); // dijit._Widget | |
4999 | }, | |
5000 | ||
5001 | getNextSibling: function(){ | |
5002 | // summary: | |
5003 | // Returns null if this is the last child of the parent, | |
5004 | // otherwise returns the next element sibling to the "right". | |
5005 | ||
5006 | return this._getSibling("next"); // dijit._Widget | |
5007 | }, | |
5008 | ||
5009 | getIndexInParent: function(){ | |
5010 | // summary: | |
5011 | // Returns the index of this widget within its container parent. | |
5012 | // It returns -1 if the parent does not exist, or if the parent | |
5013 | // is not a dijit._Container | |
5014 | ||
5015 | var p = this.getParent(); | |
5016 | if(!p || !p.getIndexOfChild){ | |
5017 | return -1; // int | |
5018 | } | |
5019 | return p.getIndexOfChild(this); // int | |
5020 | } | |
5021 | } | |
5022 | ); | |
5023 | ||
5024 | ||
5025 | } | |
5026 | ||
5027 | if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
5028 | dojo._hasResource["dijit.layout._LayoutWidget"] = true; | |
5029 | dojo.provide("dijit.layout._LayoutWidget"); | |
5030 | ||
5031 | ||
5032 | ||
5033 | ||
5034 | ||
5035 | dojo.declare("dijit.layout._LayoutWidget", | |
5036 | [dijit._Widget, dijit._Container, dijit._Contained], | |
5037 | { | |
5038 | // summary: | |
5039 | // Base class for a _Container widget which is responsible for laying out its children. | |
5040 | // Widgets which mixin this code must define layout() to manage placement and sizing of the children. | |
5041 | ||
5042 | // baseClass: [protected extension] String | |
5043 | // This class name is applied to the widget's domNode | |
5044 | // and also may be used to generate names for sub nodes, | |
5045 | // for example dijitTabContainer-content. | |
5046 | baseClass: "dijitLayoutContainer", | |
5047 | ||
5048 | // isLayoutContainer: [protected] Boolean | |
5049 | // Indicates that this widget is going to call resize() on its | |
5050 | // children widgets, setting their size, when they become visible. | |
5051 | isLayoutContainer: true, | |
5052 | ||
5053 | postCreate: function(){ | |
5054 | dojo.addClass(this.domNode, "dijitContainer"); | |
5055 | ||
5056 | this.inherited(arguments); | |
5057 | }, | |
5058 | ||
5059 | startup: function(){ | |
5060 | // summary: | |
5061 | // Called after all the widgets have been instantiated and their | |
5062 | // dom nodes have been inserted somewhere under dojo.doc.body. | |
5063 | // | |
5064 | // Widgets should override this method to do any initialization | |
5065 | // dependent on other widgets existing, and then call | |
5066 | // this superclass method to finish things off. | |
5067 | // | |
5068 | // startup() in subclasses shouldn't do anything | |
5069 | // size related because the size of the widget hasn't been set yet. | |
5070 | ||
5071 | if(this._started){ return; } | |
5072 | ||
5073 | // Need to call inherited first - so that child widgets get started | |
5074 | // up correctly | |
5075 | this.inherited(arguments); | |
5076 | ||
5077 | // If I am a not being controlled by a parent layout widget... | |
5078 | var parent = this.getParent && this.getParent() | |
5079 | if(!(parent && parent.isLayoutContainer)){ | |
5080 | // Do recursive sizing and layout of all my descendants | |
5081 | // (passing in no argument to resize means that it has to glean the size itself) | |
5082 | this.resize(); | |
5083 | ||
5084 | // Since my parent isn't a layout container, and my style *may be* width=height=100% | |
5085 | // or something similar (either set directly or via a CSS class), | |
5086 | // monitor when my size changes so that I can re-layout. | |
5087 | // For browsers where I can't directly monitor when my size changes, | |
5088 | // monitor when the viewport changes size, which *may* indicate a size change for me. | |
5089 | this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){ | |
5090 | // Using function(){} closure to ensure no arguments to resize. | |
5091 | this.resize(); | |
5092 | }); | |
5093 | } | |
5094 | }, | |
5095 | ||
5096 | resize: function(changeSize, resultSize){ | |
5097 | // summary: | |
5098 | // Call this to resize a widget, or after its size has changed. | |
5099 | // description: | |
5100 | // Change size mode: | |
5101 | // When changeSize is specified, changes the marginBox of this widget | |
5102 | // and forces it to relayout its contents accordingly. | |
5103 | // changeSize may specify height, width, or both. | |
5104 | // | |
5105 | // If resultSize is specified it indicates the size the widget will | |
5106 | // become after changeSize has been applied. | |
5107 | // | |
5108 | // Notification mode: | |
5109 | // When changeSize is null, indicates that the caller has already changed | |
5110 | // the size of the widget, or perhaps it changed because the browser | |
5111 | // window was resized. Tells widget to relayout its contents accordingly. | |
5112 | // | |
5113 | // If resultSize is also specified it indicates the size the widget has | |
5114 | // become. | |
5115 | // | |
5116 | // In either mode, this method also: | |
5117 | // 1. Sets this._borderBox and this._contentBox to the new size of | |
5118 | // the widget. Queries the current domNode size if necessary. | |
5119 | // 2. Calls layout() to resize contents (and maybe adjust child widgets). | |
5120 | // | |
5121 | // changeSize: Object? | |
5122 | // Sets the widget to this margin-box size and position. | |
5123 | // May include any/all of the following properties: | |
5124 | // | {w: int, h: int, l: int, t: int} | |
5125 | // | |
5126 | // resultSize: Object? | |
5127 | // The margin-box size of this widget after applying changeSize (if | |
5128 | // changeSize is specified). If caller knows this size and | |
5129 | // passes it in, we don't need to query the browser to get the size. | |
5130 | // | {w: int, h: int} | |
5131 | ||
5132 | var node = this.domNode; | |
5133 | ||
5134 | // set margin box size, unless it wasn't specified, in which case use current size | |
5135 | if(changeSize){ | |
5136 | dojo.marginBox(node, changeSize); | |
5137 | ||
5138 | // set offset of the node | |
5139 | if(changeSize.t){ node.style.top = changeSize.t + "px"; } | |
5140 | if(changeSize.l){ node.style.left = changeSize.l + "px"; } | |
5141 | } | |
5142 | ||
5143 | // If either height or width wasn't specified by the user, then query node for it. | |
5144 | // But note that setting the margin box and then immediately querying dimensions may return | |
5145 | // inaccurate results, so try not to depend on it. | |
5146 | var mb = resultSize || {}; | |
5147 | dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize | |
5148 | if( !("h" in mb) || !("w" in mb) ){ | |
5149 | mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values | |
5150 | } | |
5151 | ||
5152 | // Compute and save the size of my border box and content box | |
5153 | // (w/out calling dojo.contentBox() since that may fail if size was recently set) | |
5154 | var cs = dojo.getComputedStyle(node); | |
5155 | var me = dojo._getMarginExtents(node, cs); | |
5156 | var be = dojo._getBorderExtents(node, cs); | |
5157 | var bb = (this._borderBox = { | |
5158 | w: mb.w - (me.w + be.w), | |
5159 | h: mb.h - (me.h + be.h) | |
5160 | }); | |
5161 | var pe = dojo._getPadExtents(node, cs); | |
5162 | this._contentBox = { | |
5163 | l: dojo._toPixelValue(node, cs.paddingLeft), | |
5164 | t: dojo._toPixelValue(node, cs.paddingTop), | |
5165 | w: bb.w - pe.w, | |
5166 | h: bb.h - pe.h | |
5167 | }; | |
5168 | ||
5169 | // Callback for widget to adjust size of its children | |
5170 | this.layout(); | |
5171 | }, | |
5172 | ||
5173 | layout: function(){ | |
5174 | // summary: | |
5175 | // Widgets override this method to size and position their contents/children. | |
5176 | // When this is called this._contentBox is guaranteed to be set (see resize()). | |
5177 | // | |
5178 | // This is called after startup(), and also when the widget's size has been | |
5179 | // changed. | |
5180 | // tags: | |
5181 | // protected extension | |
5182 | }, | |
5183 | ||
5184 | _setupChild: function(/*dijit._Widget*/child){ | |
5185 | // summary: | |
5186 | // Common setup for initial children and children which are added after startup | |
5187 | // tags: | |
5188 | // protected extension | |
5189 | ||
5190 | dojo.addClass(child.domNode, this.baseClass+"-child"); | |
5191 | if(child.baseClass){ | |
5192 | dojo.addClass(child.domNode, this.baseClass+"-"+child.baseClass); | |
5193 | } | |
5194 | }, | |
5195 | ||
5196 | addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ | |
5197 | // Overrides _Container.addChild() to call _setupChild() | |
5198 | this.inherited(arguments); | |
5199 | if(this._started){ | |
5200 | this._setupChild(child); | |
5201 | } | |
5202 | }, | |
5203 | ||
5204 | removeChild: function(/*dijit._Widget*/ child){ | |
5205 | // Overrides _Container.removeChild() to remove class added by _setupChild() | |
5206 | dojo.removeClass(child.domNode, this.baseClass+"-child"); | |
5207 | if(child.baseClass){ | |
5208 | dojo.removeClass(child.domNode, this.baseClass+"-"+child.baseClass); | |
5209 | } | |
5210 | this.inherited(arguments); | |
5211 | } | |
5212 | } | |
5213 | ); | |
5214 | ||
5215 | dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ | |
5216 | // summary: | |
5217 | // Given the margin-box size of a node, return its content box size. | |
5218 | // Functions like dojo.contentBox() but is more reliable since it doesn't have | |
5219 | // to wait for the browser to compute sizes. | |
5220 | var cs = dojo.getComputedStyle(node); | |
5221 | var me = dojo._getMarginExtents(node, cs); | |
5222 | var pb = dojo._getPadBorderExtents(node, cs); | |
5223 | return { | |
5224 | l: dojo._toPixelValue(node, cs.paddingLeft), | |
5225 | t: dojo._toPixelValue(node, cs.paddingTop), | |
5226 | w: mb.w - (me.w + pb.w), | |
5227 | h: mb.h - (me.h + pb.h) | |
5228 | }; | |
5229 | }; | |
5230 | ||
5231 | (function(){ | |
5232 | var capitalize = function(word){ | |
5233 | return word.substring(0,1).toUpperCase() + word.substring(1); | |
5234 | }; | |
5235 | ||
5236 | var size = function(widget, dim){ | |
5237 | // size the child | |
5238 | widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim); | |
5239 | ||
5240 | // record child's size, but favor our own numbers when we have them. | |
5241 | // the browser lies sometimes | |
5242 | dojo.mixin(widget, dojo.marginBox(widget.domNode)); | |
5243 | dojo.mixin(widget, dim); | |
5244 | }; | |
5245 | ||
5246 | dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Object[]*/ children){ | |
5247 | // summary | |
5248 | // Layout a bunch of child dom nodes within a parent dom node | |
5249 | // container: | |
5250 | // parent node | |
5251 | // dim: | |
5252 | // {l, t, w, h} object specifying dimensions of container into which to place children | |
5253 | // children: | |
5254 | // an array like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ] | |
5255 | ||
5256 | // copy dim because we are going to modify it | |
5257 | dim = dojo.mixin({}, dim); | |
5258 | ||
5259 | dojo.addClass(container, "dijitLayoutContainer"); | |
5260 | ||
5261 | // Move "client" elements to the end of the array for layout. a11y dictates that the author | |
5262 | // needs to be able to put them in the document in tab-order, but this algorithm requires that | |
5263 | // client be last. | |
5264 | children = dojo.filter(children, function(item){ return item.layoutAlign != "client"; }) | |
5265 | .concat(dojo.filter(children, function(item){ return item.layoutAlign == "client"; })); | |
5266 | ||
5267 | // set positions/sizes | |
5268 | dojo.forEach(children, function(child){ | |
5269 | var elm = child.domNode, | |
5270 | pos = child.layoutAlign; | |
5271 | ||
5272 | // set elem to upper left corner of unused space; may move it later | |
5273 | var elmStyle = elm.style; | |
5274 | elmStyle.left = dim.l+"px"; | |
5275 | elmStyle.top = dim.t+"px"; | |
5276 | elmStyle.bottom = elmStyle.right = "auto"; | |
5277 | ||
5278 | dojo.addClass(elm, "dijitAlign" + capitalize(pos)); | |
5279 | ||
5280 | // set size && adjust record of remaining space. | |
5281 | // note that setting the width of a <div> may affect its height. | |
5282 | if(pos == "top" || pos == "bottom"){ | |
5283 | size(child, { w: dim.w }); | |
5284 | dim.h -= child.h; | |
5285 | if(pos == "top"){ | |
5286 | dim.t += child.h; | |
5287 | }else{ | |
5288 | elmStyle.top = dim.t + dim.h + "px"; | |
5289 | } | |
5290 | }else if(pos == "left" || pos == "right"){ | |
5291 | size(child, { h: dim.h }); | |
5292 | dim.w -= child.w; | |
5293 | if(pos == "left"){ | |
5294 | dim.l += child.w; | |
5295 | }else{ | |
5296 | elmStyle.left = dim.l + dim.w + "px"; | |
5297 | } | |
5298 | }else if(pos == "client"){ | |
5299 | size(child, dim); | |
5300 | } | |
5301 | }); | |
5302 | }; | |
5303 | ||
5304 | })(); | |
5305 | ||
5306 | } | |
5307 | ||
5308 | if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
5309 | dojo._hasResource["dijit._CssStateMixin"] = true; | |
5310 | dojo.provide("dijit._CssStateMixin"); | |
5311 | ||
5312 | ||
5313 | dojo.declare("dijit._CssStateMixin", [], { | |
5314 | // summary: | |
5315 | // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus | |
5316 | // state changes, and also higher-level state changes such becoming disabled or selected. | |
5317 | // | |
5318 | // description: | |
5319 | // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically | |
5320 | // maintain CSS classes on the widget root node (this.domNode) depending on hover, | |
5321 | // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes | |
5322 | // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it. | |
5323 | // | |
5324 | // It also sets CSS like dijitButtonDisabled based on widget semantic state. | |
5325 | // | |
5326 | // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons | |
5327 | // within the widget). | |
5328 | ||
5329 | // cssStateNodes: [protected] Object | |
5330 | // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus | |
5331 | //. | |
5332 | // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names | |
5333 | // (like "dijitUpArrowButton"). Example: | |
5334 | // | { | |
5335 | // | "upArrowButton": "dijitUpArrowButton", | |
5336 | // | "downArrowButton": "dijitDownArrowButton" | |
5337 | // | } | |
5338 | // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it | |
5339 | // is hovered, etc. | |
5340 | cssStateNodes: {}, | |
5341 | ||
5342 | postCreate: function(){ | |
5343 | this.inherited(arguments); | |
5344 | ||
5345 | // Automatically monitor mouse events (essentially :hover and :active) on this.domNode | |
5346 | dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){ | |
5347 | this.connect(this.domNode, e, "_cssMouseEvent"); | |
5348 | }, this); | |
5349 | ||
5350 | // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node | |
5351 | this.connect(this, "set", function(name, value){ | |
5352 | if(arguments.length >= 2 && {disabled: true, readOnly: true, checked:true, selected:true}[name]){ | |
5353 | this._setStateClass(); | |
5354 | } | |
5355 | }); | |
5356 | ||
5357 | // The widget coming in/out of the focus change affects it's state | |
5358 | dojo.forEach(["_onFocus", "_onBlur"], function(ap){ | |
5359 | this.connect(this, ap, "_setStateClass"); | |
5360 | }, this); | |
5361 | ||
5362 | // Events on sub nodes within the widget | |
5363 | for(var ap in this.cssStateNodes){ | |
5364 | this._trackMouseState(this[ap], this.cssStateNodes[ap]); | |
5365 | } | |
5366 | // Set state initially; there's probably no hover/active/focus state but widget might be | |
5367 | // disabled/readonly so we want to set CSS classes for those conditions. | |
5368 | this._setStateClass(); | |
5369 | }, | |
5370 | ||
5371 | _cssMouseEvent: function(/*Event*/ event){ | |
5372 | // summary: | |
5373 | // Sets _hovering and _active properties depending on mouse state, | |
5374 | // then calls _setStateClass() to set appropriate CSS classes for this.domNode. | |
5375 | ||
5376 | if(!this.disabled){ | |
5377 | switch(event.type){ | |
5378 | case "mouseenter": | |
5379 | case "mouseover": // generated on non-IE browsers even though we connected to mouseenter | |
5380 | this._hovering = true; | |
5381 | this._active = this._mouseDown; | |
5382 | break; | |
5383 | ||
5384 | case "mouseleave": | |
5385 | case "mouseout": // generated on non-IE browsers even though we connected to mouseleave | |
5386 | this._hovering = false; | |
5387 | this._active = false; | |
5388 | break; | |
5389 | ||
5390 | case "mousedown" : | |
5391 | this._active = true; | |
5392 | this._mouseDown = true; | |
5393 | // Set a global event to handle mouseup, so it fires properly | |
5394 | // even if the cursor leaves this.domNode before the mouse up event. | |
5395 | // Alternately could set active=false on mouseout. | |
5396 | var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){ | |
5397 | this._active = false; | |
5398 | this._mouseDown = false; | |
5399 | this._setStateClass(); | |
5400 | this.disconnect(mouseUpConnector); | |
5401 | }); | |
5402 | break; | |
5403 | } | |
5404 | this._setStateClass(); | |
5405 | } | |
5406 | }, | |
5407 | ||
5408 | _setStateClass: function(){ | |
5409 | // summary: | |
5410 | // Update the visual state of the widget by setting the css classes on this.domNode | |
5411 | // (or this.stateNode if defined) by combining this.baseClass with | |
5412 | // various suffixes that represent the current widget state(s). | |
5413 | // | |
5414 | // description: | |
5415 | // In the case where a widget has multiple | |
5416 | // states, it sets the class based on all possible | |
5417 | // combinations. For example, an invalid form widget that is being hovered | |
5418 | // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover". | |
5419 | // | |
5420 | // The widget may have one or more of the following states, determined | |
5421 | // by this.state, this.checked, this.valid, and this.selected: | |
5422 | // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid | |
5423 | // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true | |
5424 | // - Selected - ex: currently selected tab will have this.selected==true | |
5425 | // | |
5426 | // In addition, it may have one or more of the following states, | |
5427 | // based on this.disabled and flags set in _onMouse (this._active, this._hovering, this._focused): | |
5428 | // - Disabled - if the widget is disabled | |
5429 | // - Active - if the mouse (or space/enter key?) is being pressed down | |
5430 | // - Focused - if the widget has focus | |
5431 | // - Hover - if the mouse is over the widget | |
5432 | ||
5433 | // Compute new set of classes | |
5434 | var newStateClasses = this.baseClass.split(" "); | |
5435 | ||
5436 | function multiply(modifier){ | |
5437 | newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier); | |
5438 | } | |
5439 | ||
5440 | if(!this.isLeftToRight()){ | |
5441 | // For RTL mode we need to set an addition class like dijitTextBoxRtl. | |
5442 | multiply("Rtl"); | |
5443 | } | |
5444 | ||
5445 | if(this.checked){ | |
5446 | multiply("Checked"); | |
5447 | } | |
5448 | if(this.state){ | |
5449 | multiply(this.state); | |
5450 | } | |
5451 | if(this.selected){ | |
5452 | multiply("Selected"); | |
5453 | } | |
5454 | ||
5455 | if(this.disabled){ | |
5456 | multiply("Disabled"); | |
5457 | }else if(this.readOnly){ | |
5458 | multiply("ReadOnly"); | |
5459 | }else{ | |
5460 | if(this._active){ | |
5461 | multiply("Active"); | |
5462 | }else if(this._hovering){ | |
5463 | multiply("Hover"); | |
5464 | } | |
5465 | } | |
5466 | ||
5467 | if(this._focused){ | |
5468 | multiply("Focused"); | |
5469 | } | |
5470 | ||
5471 | // Remove old state classes and add new ones. | |
5472 | // For performance concerns we only write into domNode.className once. | |
5473 | var tn = this.stateNode || this.domNode, | |
5474 | classHash = {}; // set of all classes (state and otherwise) for node | |
5475 | ||
5476 | dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; }); | |
5477 | ||
5478 | if("_stateClasses" in this){ | |
5479 | dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; }); | |
5480 | } | |
5481 | ||
5482 | dojo.forEach(newStateClasses, function(c){ classHash[c] = true; }); | |
5483 | ||
5484 | var newClasses = []; | |
5485 | for(var c in classHash){ | |
5486 | newClasses.push(c); | |
5487 | } | |
5488 | tn.className = newClasses.join(" "); | |
5489 | ||
5490 | this._stateClasses = newStateClasses; | |
5491 | }, | |
5492 | ||
5493 | _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){ | |
5494 | // summary: | |
5495 | // Track mouse/focus events on specified node and set CSS class on that node to indicate | |
5496 | // current state. Usually not called directly, but via cssStateNodes attribute. | |
5497 | // description: | |
5498 | // Given class=foo, will set the following CSS class on the node | |
5499 | // - fooActive: if the user is currently pressing down the mouse button while over the node | |
5500 | // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button | |
5501 | // - fooFocus: if the node is focused | |
5502 | // | |
5503 | // Note that it won't set any classes if the widget is disabled. | |
5504 | // node: DomNode | |
5505 | // Should be a sub-node of the widget, not the top node (this.domNode), since the top node | |
5506 | // is handled specially and automatically just by mixing in this class. | |
5507 | // clazz: String | |
5508 | // CSS class name (ex: dijitSliderUpArrow). | |
5509 | ||
5510 | // Current state of node (initially false) | |
5511 | // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg | |
5512 | var hovering=false, active=false, focused=false; | |
5513 | ||
5514 | var self = this, | |
5515 | cn = dojo.hitch(this, "connect", node); | |
5516 | ||
5517 | function setClass(){ | |
5518 | var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly); | |
5519 | dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled); | |
5520 | dojo.toggleClass(node, clazz+"Active", active && !disabled); | |
5521 | dojo.toggleClass(node, clazz+"Focused", focused && !disabled); | |
5522 | } | |
5523 | ||
5524 | // Mouse | |
5525 | cn("onmouseenter", function(){ | |
5526 | hovering = true; | |
5527 | setClass(); | |
5528 | }); | |
5529 | cn("onmouseleave", function(){ | |
5530 | hovering = false; | |
5531 | active = false; | |
5532 | setClass(); | |
5533 | }); | |
5534 | cn("onmousedown", function(){ | |
5535 | active = true; | |
5536 | setClass(); | |
5537 | }); | |
5538 | cn("onmouseup", function(){ | |
5539 | active = false; | |
5540 | setClass(); | |
5541 | }); | |
5542 | ||
5543 | // Focus | |
5544 | cn("onfocus", function(){ | |
5545 | focused = true; | |
5546 | setClass(); | |
5547 | }); | |
5548 | cn("onblur", function(){ | |
5549 | focused = false; | |
5550 | setClass(); | |
5551 | }); | |
5552 | ||
5553 | // Just in case widget is enabled/disabled while it has focus/hover/active state. | |
5554 | // Maybe this is overkill. | |
5555 | this.connect(this, "set", function(name, value){ | |
5556 | if(name == "disabled" || name == "readOnly"){ | |
5557 | setClass(); | |
5558 | } | |
5559 | }); | |
5560 | } | |
5561 | }); | |
5562 | ||
5563 | } | |
5564 | ||
5565 | if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
5566 | dojo._hasResource["dijit.form._FormWidget"] = true; | |
5567 | dojo.provide("dijit.form._FormWidget"); | |
5568 | ||
5569 | ||
5570 | ||
5571 | ||
5572 | ||
5573 | ||
5574 | ||
5575 | dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], | |
5576 | { | |
5577 | // summary: | |
5578 | // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>, | |
5579 | // which can be children of a <form> node or a `dijit.form.Form` widget. | |
5580 | // | |
5581 | // description: | |
5582 | // Represents a single HTML element. | |
5583 | // All these widgets should have these attributes just like native HTML input elements. | |
5584 | // You can set them during widget construction or afterwards, via `dijit._Widget.attr`. | |
5585 | // | |
5586 | // They also share some common methods. | |
5587 | ||
5588 | // name: String | |
5589 | // Name used when submitting form; same as "name" attribute or plain HTML elements | |
5590 | name: "", | |
5591 | ||
5592 | // alt: String | |
5593 | // Corresponds to the native HTML <input> element's attribute. | |
5594 | alt: "", | |
5595 | ||
5596 | // value: String | |
5597 | // Corresponds to the native HTML <input> element's attribute. | |
5598 | value: "", | |
5599 | ||
5600 | // type: String | |
5601 | // Corresponds to the native HTML <input> element's attribute. | |
5602 | type: "text", | |
5603 | ||
5604 | // tabIndex: Integer | |
5605 | // Order fields are traversed when user hits the tab key | |
5606 | tabIndex: "0", | |
5607 | ||
5608 | // disabled: Boolean | |
5609 | // Should this widget respond to user input? | |
5610 | // In markup, this is specified as "disabled='disabled'", or just "disabled". | |
5611 | disabled: false, | |
5612 | ||
5613 | // intermediateChanges: Boolean | |
5614 | // Fires onChange for each value change or only on demand | |
5615 | intermediateChanges: false, | |
5616 | ||
5617 | // scrollOnFocus: Boolean | |
5618 | // On focus, should this widget scroll into view? | |
5619 | scrollOnFocus: true, | |
5620 | ||
5621 | // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are. | |
5622 | attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { | |
5623 | value: "focusNode", | |
5624 | id: "focusNode", | |
5625 | tabIndex: "focusNode", | |
5626 | alt: "focusNode", | |
5627 | title: "focusNode" | |
5628 | }), | |
5629 | ||
5630 | postMixInProperties: function(){ | |
5631 | // Setup name=foo string to be referenced from the template (but only if a name has been specified) | |
5632 | // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660 | |
5633 | // Regarding escaping, see heading "Attribute values" in | |
5634 | // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 | |
5635 | this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, """) + '"') : ''; | |
5636 | this.inherited(arguments); | |
5637 | }, | |
5638 | ||
5639 | postCreate: function(){ | |
5640 | this.inherited(arguments); | |
5641 | this.connect(this.domNode, "onmousedown", "_onMouseDown"); | |
5642 | }, | |
5643 | ||
5644 | _setDisabledAttr: function(/*Boolean*/ value){ | |
5645 | this.disabled = value; | |
5646 | dojo.attr(this.focusNode, 'disabled', value); | |
5647 | if(this.valueNode){ | |
5648 | dojo.attr(this.valueNode, 'disabled', value); | |
5649 | } | |
5650 | dijit.setWaiState(this.focusNode, "disabled", value); | |
5651 | ||
5652 | if(value){ | |
5653 | // reset these, because after the domNode is disabled, we can no longer receive | |
5654 | // mouse related events, see #4200 | |
5655 | this._hovering = false; | |
5656 | this._active = false; | |
5657 | ||
5658 | // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes) | |
5659 | var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode"; | |
5660 | dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){ | |
5661 | var node = this[attachPointName]; | |
5662 | // complex code because tabIndex=-1 on a <div> doesn't work on FF | |
5663 | if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug | |
5664 | node.setAttribute('tabIndex', "-1"); | |
5665 | }else{ | |
5666 | node.removeAttribute('tabIndex'); | |
5667 | } | |
5668 | }, this); | |
5669 | }else{ | |
5670 | this.focusNode.setAttribute('tabIndex', this.tabIndex); | |
5671 | } | |
5672 | }, | |
5673 | ||
5674 | setDisabled: function(/*Boolean*/ disabled){ | |
5675 | // summary: | |
5676 | // Deprecated. Use set('disabled', ...) instead. | |
5677 | dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0"); | |
5678 | this.set('disabled', disabled); | |
5679 | }, | |
5680 | ||
5681 | _onFocus: function(e){ | |
5682 | if(this.scrollOnFocus){ | |
5683 | dojo.window.scrollIntoView(this.domNode); | |
5684 | } | |
5685 | this.inherited(arguments); | |
5686 | }, | |
5687 | ||
5688 | isFocusable: function(){ | |
5689 | // summary: | |
5690 | // Tells if this widget is focusable or not. Used internally by dijit. | |
5691 | // tags: | |
5692 | // protected | |
5693 | return !this.disabled && !this.readOnly && this.focusNode && (dojo.style(this.domNode, "display") != "none"); | |
5694 | }, | |
5695 | ||
5696 | focus: function(){ | |
5697 | // summary: | |
5698 | // Put focus on this widget | |
5699 | dijit.focus(this.focusNode); | |
5700 | }, | |
5701 | ||
5702 | compare: function(/*anything*/val1, /*anything*/val2){ | |
5703 | // summary: | |
5704 | // Compare 2 values (as returned by attr('value') for this widget). | |
5705 | // tags: | |
5706 | // protected | |
5707 | if(typeof val1 == "number" && typeof val2 == "number"){ | |
5708 | return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2; | |
5709 | }else if(val1 > val2){ | |
5710 | return 1; | |
5711 | }else if(val1 < val2){ | |
5712 | return -1; | |
5713 | }else{ | |
5714 | return 0; | |
5715 | } | |
5716 | }, | |
5717 | ||
5718 | onChange: function(newValue){ | |
5719 | // summary: | |
5720 | // Callback when this widget's value is changed. | |
5721 | // tags: | |
5722 | // callback | |
5723 | }, | |
5724 | ||
5725 | // _onChangeActive: [private] Boolean | |
5726 | // Indicates that changes to the value should call onChange() callback. | |
5727 | // This is false during widget initialization, to avoid calling onChange() | |
5728 | // when the initial value is set. | |
5729 | _onChangeActive: false, | |
5730 | ||
5731 | _handleOnChange: function(/*anything*/ newValue, /* Boolean? */ priorityChange){ | |
5732 | // summary: | |
5733 | // Called when the value of the widget is set. Calls onChange() if appropriate | |
5734 | // newValue: | |
5735 | // the new value | |
5736 | // priorityChange: | |
5737 | // For a slider, for example, dragging the slider is priorityChange==false, | |
5738 | // but on mouse up, it's priorityChange==true. If intermediateChanges==true, | |
5739 | // onChange is only called form priorityChange=true events. | |
5740 | // tags: | |
5741 | // private | |
5742 | this._lastValue = newValue; | |
5743 | if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){ | |
5744 | // this block executes not for a change, but during initialization, | |
5745 | // and is used to store away the original value (or for ToggleButton, the original checked state) | |
5746 | this._resetValue = this._lastValueReported = newValue; | |
5747 | } | |
5748 | if((this.intermediateChanges || priorityChange || priorityChange === undefined) && | |
5749 | ((typeof newValue != typeof this._lastValueReported) || | |
5750 | this.compare(newValue, this._lastValueReported) != 0)){ | |
5751 | this._lastValueReported = newValue; | |
5752 | if(this._onChangeActive){ | |
5753 | if(this._onChangeHandle){ | |
5754 | clearTimeout(this._onChangeHandle); | |
5755 | } | |
5756 | // setTimout allows hidden value processing to run and | |
5757 | // also the onChange handler can safely adjust focus, etc | |
5758 | this._onChangeHandle = setTimeout(dojo.hitch(this, | |
5759 | function(){ | |
5760 | this._onChangeHandle = null; | |
5761 | this.onChange(newValue); | |
5762 | }), 0); // try to collapse multiple onChange's fired faster than can be processed | |
5763 | } | |
5764 | } | |
5765 | }, | |
5766 | ||
5767 | create: function(){ | |
5768 | // Overrides _Widget.create() | |
5769 | this.inherited(arguments); | |
5770 | this._onChangeActive = true; | |
5771 | }, | |
5772 | ||
5773 | destroy: function(){ | |
5774 | if(this._onChangeHandle){ // destroy called before last onChange has fired | |
5775 | clearTimeout(this._onChangeHandle); | |
5776 | this.onChange(this._lastValueReported); | |
5777 | } | |
5778 | this.inherited(arguments); | |
5779 | }, | |
5780 | ||
5781 | setValue: function(/*String*/ value){ | |
5782 | // summary: | |
5783 | // Deprecated. Use set('value', ...) instead. | |
5784 | dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0"); | |
5785 | this.set('value', value); | |
5786 | }, | |
5787 | ||
5788 | getValue: function(){ | |
5789 | // summary: | |
5790 | // Deprecated. Use get('value') instead. | |
5791 | dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0"); | |
5792 | return this.get('value'); | |
5793 | }, | |
5794 | ||
5795 | _onMouseDown: function(e){ | |
5796 | // If user clicks on the button, even if the mouse is released outside of it, | |
5797 | // this button should get focus (to mimics native browser buttons). | |
5798 | // This is also needed on chrome because otherwise buttons won't get focus at all, | |
5799 | // which leads to bizarre focus restore on Dialog close etc. | |
5800 | if(!e.ctrlKey && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac | |
5801 | // Set a global event to handle mouseup, so it fires properly | |
5802 | // even if the cursor leaves this.domNode before the mouse up event. | |
5803 | var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){ | |
5804 | if (this.isFocusable()) { | |
5805 | this.focus(); | |
5806 | } | |
5807 | this.disconnect(mouseUpConnector); | |
5808 | }); | |
5809 | } | |
5810 | } | |
5811 | }); | |
5812 | ||
5813 | dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget, | |
5814 | { | |
5815 | // summary: | |
5816 | // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values. | |
5817 | // description: | |
5818 | // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element, | |
5819 | // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?) | |
5820 | // works as expected. | |
5821 | ||
5822 | // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared | |
5823 | // directly in the template as read by the parser in order to function. IE is known to specifically | |
5824 | // require the 'name' attribute at element creation time. See #8484, #8660. | |
5825 | // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode, | |
5826 | // so maybe {value: ""} is so the value *doesn't* get copied to focusNode? | |
5827 | // Seems like we really want value removed from attributeMap altogether | |
5828 | // (although there's no easy way to do that now) | |
5829 | ||
5830 | // readOnly: Boolean | |
5831 | // Should this widget respond to user input? | |
5832 | // In markup, this is specified as "readOnly". | |
5833 | // Similar to disabled except readOnly form values are submitted. | |
5834 | readOnly: false, | |
5835 | ||
5836 | attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { | |
5837 | value: "", | |
5838 | readOnly: "focusNode" | |
5839 | }), | |
5840 | ||
5841 | _setReadOnlyAttr: function(/*Boolean*/ value){ | |
5842 | this.readOnly = value; | |
5843 | dojo.attr(this.focusNode, 'readOnly', value); | |
5844 | dijit.setWaiState(this.focusNode, "readonly", value); | |
5845 | }, | |
5846 | ||
5847 | postCreate: function(){ | |
5848 | this.inherited(arguments); | |
5849 | ||
5850 | if(dojo.isIE){ // IE won't stop the event with keypress | |
5851 | this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown); | |
5852 | } | |
5853 | // Update our reset value if it hasn't yet been set (because this.set() | |
5854 | // is only called when there *is* a value) | |
5855 | if(this._resetValue === undefined){ | |
5856 | this._resetValue = this.value; | |
5857 | } | |
5858 | }, | |
5859 | ||
5860 | _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){ | |
5861 | // summary: | |
5862 | // Hook so attr('value', value) works. | |
5863 | // description: | |
5864 | // Sets the value of the widget. | |
5865 | // If the value has changed, then fire onChange event, unless priorityChange | |
5866 | // is specified as null (or false?) | |
5867 | this.value = newValue; | |
5868 | this._handleOnChange(newValue, priorityChange); | |
5869 | }, | |
5870 | ||
5871 | _getValueAttr: function(){ | |
5872 | // summary: | |
5873 | // Hook so attr('value') works. | |
5874 | return this._lastValue; | |
5875 | }, | |
5876 | ||
5877 | undo: function(){ | |
5878 | // summary: | |
5879 | // Restore the value to the last value passed to onChange | |
5880 | this._setValueAttr(this._lastValueReported, false); | |
5881 | }, | |
5882 | ||
5883 | reset: function(){ | |
5884 | // summary: | |
5885 | // Reset the widget's value to what it was at initialization time | |
5886 | this._hasBeenBlurred = false; | |
5887 | this._setValueAttr(this._resetValue, true); | |
5888 | }, | |
5889 | ||
5890 | _onKeyDown: function(e){ | |
5891 | if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){ | |
5892 | var te; | |
5893 | if(dojo.isIE){ | |
5894 | e.preventDefault(); // default behavior needs to be stopped here since keypress is too late | |
5895 | te = document.createEventObject(); | |
5896 | te.keyCode = dojo.keys.ESCAPE; | |
5897 | te.shiftKey = e.shiftKey; | |
5898 | e.srcElement.fireEvent('onkeypress', te); | |
5899 | } | |
5900 | } | |
5901 | }, | |
5902 | ||
5903 | _layoutHackIE7: function(){ | |
5904 | // summary: | |
5905 | // Work around table sizing bugs on IE7 by forcing redraw | |
5906 | ||
5907 | if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight | |
5908 | var domNode = this.domNode; | |
5909 | var parent = domNode.parentNode; | |
5910 | var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter | |
5911 | var origFilter = pingNode.style.filter; // save custom filter, most likely nothing | |
5912 | var _this = this; | |
5913 | while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet | |
5914 | (function ping(){ | |
5915 | var disconnectHandle = _this.connect(parent, "onscroll", | |
5916 | function(e){ | |
5917 | _this.disconnect(disconnectHandle); // only call once | |
5918 | pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique | |
5919 | setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any | |
5920 | } | |
5921 | ); | |
5922 | })(); | |
5923 | parent = parent.parentNode; | |
5924 | } | |
5925 | } | |
5926 | } | |
5927 | }); | |
5928 | ||
5929 | } | |
5930 | ||
5931 | if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. | |
5932 | dojo._hasResource["dijit.dijit"] = true; | |
5933 | dojo.provide("dijit.dijit"); | |
5934 | ||
5935 | /*===== | |
5936 | dijit.dijit = { | |
5937 | // summary: | |
5938 | // A roll-up for common dijit methods | |
5939 | // description: | |
5940 | // A rollup file for the build system including the core and common | |
5941 | // dijit files. | |
5942 | // | |
5943 | // example: | |
5944 | // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script> | |
5945 | // | |
5946 | }; | |
5947 | =====*/ | |
5948 | ||
5949 | // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require) | |
5950 | ||
5951 | ||
5952 | // And some other stuff that we tend to pull in all the time anyway | |
5953 | ||
5954 | ||
5955 | ||
5956 | ||
5957 | ||
5958 | ||
5959 | ||
5960 | } | |
5961 |