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