root / trunk / web / dojo / dijit / dijit.js.uncompressed.js @ 11
History | View | Annotate | Download (194 KB)
1 | 9 | andrej.cim | /*
|
---|---|---|---|
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 | } |