2 Copyright (c) 2004-2008, The Dojo Foundation
5 Licensed under the Academic Free License version 2.1 or above OR the
6 modified BSD license. For more information on Dojo licensing, see:
8 http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
12 This is a compiled version of Dojo, built for deployment and not for
13 development. To get an editable version, please visit:
15 http://dojotoolkit.org
17 for documentation and information on getting the source.
20 if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
21 dojo._hasResource["dijit._base.focus"] = true;
22 dojo.provide("dijit._base.focus");
25 // These functions are used to query or set the focus and selection.
27 // Also, they trace when widgets become actived/deactivated,
28 // so that the widget can fire _onFocus/_onBlur events.
29 // "Active" here means something similar to "focused", but
30 // "focus" isn't quite the right word because we keep track of
31 // a whole stack of "active" widgets. Example: Combobutton --> Menu -->
32 // MenuItem. The onBlur event for Combobutton doesn't fire due to focusing
33 // on the Menu or a MenuItem, since they are considered part of the
34 // Combobutton widget. It only happens when focus is shifted
35 // somewhere completely different.
40 // Currently focused item on screen
43 // _prevFocus: DomNode
44 // Previously focused item on screen
47 isCollapsed: function(){
48 // summary: tests whether the current selection is empty
49 var _window = dojo.global;
50 var _document = dojo.doc;
51 if(_document.selection){ // IE
52 return !_document.selection.createRange().text; // Boolean
54 var selection = _window.getSelection();
55 if(dojo.isString(selection)){ // Safari
56 return !selection; // Boolean
58 return selection.isCollapsed || !selection.toString(); // Boolean
63 getBookmark: function(){
64 // summary: Retrieves a bookmark that can be used with moveToBookmark to return to the same range
65 var bookmark, selection = dojo.doc.selection;
67 var range = selection.createRange();
68 if(selection.type.toUpperCase()=='CONTROL'){
71 var i=0,len=range.length;
73 bookmark.push(range.item(i++));
79 bookmark = range.getBookmark();
82 if(window.getSelection){
83 selection = dojo.global.getSelection();
85 range = selection.getRangeAt(0);
86 bookmark = range.cloneRange();
89 console.warn("No idea how to store the current selection for this browser!");
92 return bookmark; // Array
95 moveToBookmark: function(/*Object*/bookmark){
96 // summary: Moves current selection to a bookmark
97 // bookmark: This should be a returned object from dojo.html.selection.getBookmark()
98 var _document = dojo.doc;
99 if(_document.selection){ // IE
101 if(dojo.isArray(bookmark)){
102 range = _document.body.createControlRange();
103 dojo.forEach(bookmark, "range.addElement(item)"); //range.addElement does not have call/apply method, so can not call it directly
105 range = _document.selection.createRange();
106 range.moveToBookmark(bookmark);
110 var selection = dojo.global.getSelection && dojo.global.getSelection();
111 if(selection && selection.removeAllRanges){
112 selection.removeAllRanges();
113 selection.addRange(bookmark);
115 console.warn("No idea how to restore selection for this browser!");
120 getFocus: function(/*Widget?*/menu, /*Window?*/openedForWindow){
122 // Returns the current focus and selection.
123 // Called when a popup appears (either a top level menu or a dialog),
124 // or when a toolbar/menubar receives focus
127 // The menu that's being opened
130 // iframe in which menu was opened
133 // A handle to restore focus/selection
136 // Node to return focus to
137 node: menu && dojo.isDescendant(dijit._curFocus, menu.domNode) ? dijit._prevFocus : dijit._curFocus,
139 // Previously selected text
141 !dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed) ?
142 dojo.withGlobal(openedForWindow||dojo.global, dijit.getBookmark) :
145 openedForWindow: openedForWindow
149 focus: function(/*Object || DomNode */ handle){
151 // Sets the focused node and the selection according to argument.
152 // To set focus to an iframe's content, pass in the iframe itself.
154 // object returned by get(), or a DomNode
156 if(!handle){ return; }
158 var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object
159 bookmark = handle.bookmark,
160 openedForWindow = handle.openedForWindow;
163 // Note that for iframe's we need to use the <iframe> to follow the parentNode chain,
164 // but we need to set focus to iframe.contentWindow
166 var focusNode = (node.tagName.toLowerCase()=="iframe") ? node.contentWindow : node;
167 if(focusNode && focusNode.focus){
169 // Gecko throws sometimes if setting focus is impossible,
170 // node not displayed or something like that
174 dijit._onFocusNode(node);
178 // do not need to restore if current selection is not empty
179 // (use keyboard to select a menu item)
180 if(bookmark && dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed)){
182 openedForWindow.focus();
185 dojo.withGlobal(openedForWindow||dojo.global, dijit.moveToBookmark, null, [bookmark]);
187 /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
192 // _activeStack: Array
193 // List of currently active widgets (focused widget and it's ancestors)
196 registerWin: function(/*Window?*/targetWindow){
198 // Registers listeners on the specified window (either the main
199 // window or an iframe) to detect when the user has clicked somewhere.
200 // Anyone that creates an iframe should call this function.
203 targetWindow = window;
206 dojo.connect(targetWindow.document, "onmousedown", function(evt){
207 dijit._justMouseDowned = true;
208 setTimeout(function(){ dijit._justMouseDowned = false; }, 0);
209 dijit._onTouchNode(evt.target||evt.srcElement);
211 //dojo.connect(targetWindow, "onscroll", ???);
213 // Listen for blur and focus events on targetWindow's body
214 var body = targetWindow.document.body || targetWindow.document.getElementsByTagName("body")[0];
217 body.attachEvent('onactivate', function(evt){
218 if(evt.srcElement.tagName.toLowerCase() != "body"){
219 dijit._onFocusNode(evt.srcElement);
222 body.attachEvent('ondeactivate', function(evt){ dijit._onBlurNode(evt.srcElement); });
224 body.addEventListener('focus', function(evt){ dijit._onFocusNode(evt.target); }, true);
225 body.addEventListener('blur', function(evt){ dijit._onBlurNode(evt.target); }, true);
228 body = null; // prevent memory leak (apparent circular reference via closure)
231 _onBlurNode: function(/*DomNode*/ node){
233 // Called when focus leaves a node.
234 // Usually ignored, _unless_ it *isn't* follwed by touching another node,
235 // which indicates that we tabbed off the last field on the page,
236 // in which case every widget is marked inactive
237 dijit._prevFocus = dijit._curFocus;
238 dijit._curFocus = null;
240 if(dijit._justMouseDowned){
241 // the mouse down caused a new widget to be marked as active; this blur event
242 // is coming late, so ignore it.
246 // if the blur event isn't followed by a focus event then mark all widgets as inactive.
247 if(dijit._clearActiveWidgetsTimer){
248 clearTimeout(dijit._clearActiveWidgetsTimer);
250 dijit._clearActiveWidgetsTimer = setTimeout(function(){
251 delete dijit._clearActiveWidgetsTimer;
253 dijit._prevFocus = null;
257 _onTouchNode: function(/*DomNode*/ node){
259 // Callback when node is focused or mouse-downed
261 // ignore the recent blurNode event
262 if(dijit._clearActiveWidgetsTimer){
263 clearTimeout(dijit._clearActiveWidgetsTimer);
264 delete dijit._clearActiveWidgetsTimer;
267 // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
271 if(node.dijitPopupParent){
272 node=dijit.byId(node.dijitPopupParent).domNode;
273 }else if(node.tagName && node.tagName.toLowerCase()=="body"){
274 // is this the root of the document or just the root of an iframe?
275 if(node===dojo.body()){
276 // node is the root of the main document
279 // otherwise, find the iframe this node refers to (can't access it via parentNode,
280 // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit
281 node=dijit.getDocumentWindow(node.ownerDocument).frameElement;
283 var id = node.getAttribute && node.getAttribute("widgetId");
285 newStack.unshift(id);
287 node=node.parentNode;
290 }catch(e){ /* squelch */ }
292 dijit._setStack(newStack);
295 _onFocusNode: function(/*DomNode*/ node){
297 // Callback when node is focused
298 if(node && node.tagName && node.tagName.toLowerCase() == "body"){
301 dijit._onTouchNode(node);
303 if(node==dijit._curFocus){ return; }
305 dijit._prevFocus = dijit._curFocus;
307 dijit._curFocus = node;
308 dojo.publish("focusNode", [node]);
311 _setStack: function(newStack){
313 // The stack of active widgets has changed. Send out appropriate events and record new stack
315 var oldStack = dijit._activeStack;
316 dijit._activeStack = newStack;
318 // compare old stack to new stack to see how many elements they have in common
319 for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){
320 if(oldStack[nCommon] != newStack[nCommon]){
325 // for all elements that have gone out of focus, send blur event
326 for(var i=oldStack.length-1; i>=nCommon; i--){
327 var widget = dijit.byId(oldStack[i]);
329 widget._focused = false;
330 widget._hasBeenBlurred = true;
334 if (widget._setStateClass){
335 widget._setStateClass();
337 dojo.publish("widgetBlur", [widget]);
341 // for all element that have come into focus, send focus event
342 for(i=nCommon; i<newStack.length; i++){
343 widget = dijit.byId(newStack[i]);
345 widget._focused = true;
349 if (widget._setStateClass){
350 widget._setStateClass();
352 dojo.publish("widgetFocus", [widget]);
358 // register top window and all the iframes it contains
359 dojo.addOnLoad(dijit.registerWin);
363 if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
364 dojo._hasResource["dijit._base.manager"] = true;
365 dojo.provide("dijit._base.manager");
367 dojo.declare("dijit.WidgetSet", null, {
369 // A set of widgets indexed by id
371 constructor: function(){
375 add: function(/*Widget*/ widget){
376 if(this._hash[widget.id]){
377 throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
379 this._hash[widget.id]=widget;
382 remove: function(/*String*/ id){
383 delete this._hash[id];
386 forEach: function(/*Function*/ func){
387 for(var id in this._hash){
388 func(this._hash[id]);
392 filter: function(/*Function*/ filter){
393 var res = new dijit.WidgetSet();
394 this.forEach(function(widget){
395 if(filter(widget)){ res.add(widget); }
397 return res; // dijit.WidgetSet
400 byId: function(/*String*/ id){
401 return this._hash[id];
404 byClass: function(/*String*/ cls){
405 return this.filter(function(widget){ return widget.declaredClass==cls; }); // dijit.WidgetSet
411 // summary: A list of widgets on a page.
412 // description: Is an instance of dijit.WidgetSet
415 dijit.registry = new dijit.WidgetSet();
417 dijit._widgetTypeCtr = {};
419 dijit.getUniqueId = function(/*String*/widgetType){
421 // Generates a unique id for a given widgetType
425 id = widgetType + "_" +
426 (widgetType in dijit._widgetTypeCtr ?
427 ++dijit._widgetTypeCtr[widgetType] : dijit._widgetTypeCtr[widgetType] = 0);
428 }while(dijit.byId(id));
434 // Only run this for IE because we think it's only necessary in that case,
435 // and because it causes problems on FF. See bug #3531 for details.
436 dojo.addOnUnload(function(){
437 dijit.registry.forEach(function(widget){ widget.destroy(); });
441 dijit.byId = function(/*String|Widget*/id){
443 // Returns a widget by its id, or if passed a widget, no-op (like dojo.byId())
444 return (dojo.isString(id)) ? dijit.registry.byId(id) : id; // Widget
447 dijit.byNode = function(/* DOMNode */ node){
449 // Returns the widget as referenced by node
450 return dijit.registry.byId(node.getAttribute("widgetId")); // Widget
453 dijit.getEnclosingWidget = function(/* DOMNode */ node){
455 // Returns the widget whose dom tree contains node or null if
456 // the node is not contained within the dom tree of any widget
458 if(node.getAttribute && node.getAttribute("widgetId")){
459 return dijit.registry.byId(node.getAttribute("widgetId"));
461 node = node.parentNode;
466 // elements that are tab-navigable if they have no tabindex value set
467 // (except for "a", which must have an href attribute)
468 dijit._tabElements = {
477 dijit._isElementShown = function(/*Element*/elem){
478 var style = dojo.style(elem);
479 return (style.visibility != "hidden")
480 && (style.visibility != "collapsed")
481 && (style.display != "none");
484 dijit.isTabNavigable = function(/*Element*/elem){
486 // Tests if an element is tab-navigable
487 if(dojo.hasAttr(elem, "disabled")){ return false; }
488 var hasTabindex = dojo.hasAttr(elem, "tabindex");
489 var tabindex = dojo.attr(elem, "tabindex");
490 if(hasTabindex && tabindex >= 0) {
491 return true; // boolean
493 var name = elem.nodeName.toLowerCase();
494 if(((name == "a" && dojo.hasAttr(elem, "href"))
495 || dijit._tabElements[name])
496 && (!hasTabindex || tabindex >= 0)){
497 return true; // boolean
499 return false; // boolean
502 dijit._getTabNavigable = function(/*DOMNode*/root){
504 // Finds the following descendants of the specified root node:
505 // * the first tab-navigable element in document order
506 // without a tabindex or with tabindex="0"
507 // * the last tab-navigable element in document order
508 // without a tabindex or with tabindex="0"
509 // * the first element in document order with the lowest
510 // positive tabindex value
511 // * the last element in document order with the highest
512 // positive tabindex value
513 var first, last, lowest, lowestTabindex, highest, highestTabindex;
514 var walkTree = function(/*DOMNode*/parent){
515 dojo.query("> *", parent).forEach(function(child){
516 var isShown = dijit._isElementShown(child);
517 if(isShown && dijit.isTabNavigable(child)){
518 var tabindex = dojo.attr(child, "tabindex");
519 if(!dojo.hasAttr(child, "tabindex") || tabindex == 0){
520 if(!first){ first = child; }
522 }else if(tabindex > 0){
523 if(!lowest || tabindex < lowestTabindex){
524 lowestTabindex = tabindex;
527 if(!highest || tabindex >= highestTabindex){
528 highestTabindex = tabindex;
533 if(isShown){ walkTree(child) }
536 if(dijit._isElementShown(root)){ walkTree(root) }
537 return { first: first, last: last, lowest: lowest, highest: highest };
540 dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/root){
542 // Finds the descendant of the specified root node
543 // that is first in the tabbing order
544 var elems = dijit._getTabNavigable(dojo.byId(root));
545 return elems.lowest ? elems.lowest : elems.first; // Element
548 dijit.getLastInTabbingOrder = function(/*String|DOMNode*/root){
550 // Finds the descendant of the specified root node
551 // that is last in the tabbing order
552 var elems = dijit._getTabNavigable(dojo.byId(root));
553 return elems.last ? elems.last : elems.highest; // Element
558 if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
559 dojo._hasResource["dijit._base.place"] = true;
560 dojo.provide("dijit._base.place");
562 // ported from dojo.html.util
564 dijit.getViewport = function(){
566 // Returns the dimensions and scroll position of the viewable area of a browser window
568 var _window = dojo.global;
569 var _document = dojo.doc;
573 var de = _document.documentElement;
574 var dew = de.clientWidth, deh = de.clientHeight;
577 // _window.innerHeight includes the height taken by the scroll bar
578 // clientHeight is ideal but has DTD issues:
579 // #4539: FF reverses the roles of body.clientHeight/Width and documentElement.clientHeight/Width based on the DTD!
580 // check DTD to see whether body or documentElement returns the viewport dimensions using this algorithm:
581 var minw, minh, maxw, maxh;
582 var dbw = _document.body.clientWidth;
590 var dbh = _document.body.clientHeight;
598 w = (maxw > _window.innerWidth) ? minw : maxw;
599 h = (maxh > _window.innerHeight) ? minh : maxh;
600 }else if(!dojo.isOpera && _window.innerWidth){
601 //in opera9, dojo.body().clientWidth should be used, instead
602 //of window.innerWidth/document.documentElement.clientWidth
603 //so we have to check whether it is opera
604 w = _window.innerWidth;
605 h = _window.innerHeight;
606 }else if(dojo.isIE && de && deh){
609 }else if(dojo.body().clientWidth){
611 w = dojo.body().clientWidth;
612 h = dojo.body().clientHeight;
615 // get scroll position
616 var scroll = dojo._docScroll();
618 return { w: w, h: h, l: scroll.x, t: scroll.y }; // object
621 dijit.placeOnScreen = function(
624 /* Object */ corners,
625 /* boolean? */ tryOnly){
627 // Keeps 'node' in the visible area of the screen while trying to
628 // place closest to pos.x, pos.y. The input coordinates are
629 // expected to be the desired document position.
631 // Set which corner(s) you want to bind to, such as
633 // placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
635 // The desired x/y will be treated as the topleft(TL)/topright(TR) or
636 // BottomLeft(BL)/BottomRight(BR) corner of the node. Each corner is tested
637 // and if a perfect match is found, it will be used. Otherwise, it goes through
638 // all of the specified corners, and choose the most appropriate one.
640 // NOTE: node is assumed to be absolutely or relatively positioned.
642 var choices = dojo.map(corners, function(corner){ return { corner: corner, pos: pos }; });
644 return dijit._place(node, choices);
647 dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){
649 // Given a list of spots to put node, put it at the first spot where it fits,
650 // of if it doesn't fit anywhere then the place with the least overflow
652 // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
653 // Above example says to put the top-left corner of the node at (10,20)
654 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
655 // for things like tooltip, they are displayed differently (and have different dimensions)
656 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
658 // get {x: 10, y: 10, w: 100, h:100} type obj representing position of
659 // viewport over document
660 var view = dijit.getViewport();
662 // This won't work if the node is inside a <div style="position: relative">,
663 // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong
664 // and also it might get cutoff)
665 if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
666 dojo.body().appendChild(node);
670 dojo.some(choices, function(choice){
671 var corner = choice.corner;
672 var pos = choice.pos;
674 // configure node to be displayed in given position relative to button
675 // (need to do this in order to get an accurate size for the node, because
676 // a tooltips size changes based on position, due to triangle)
678 layoutNode(node, choice.aroundCorner, corner);
682 var style = node.style;
683 var oldDisplay = style.display;
684 var oldVis = style.visibility;
685 style.visibility = "hidden";
687 var mb = dojo.marginBox(node);
688 style.display = oldDisplay;
689 style.visibility = oldVis;
691 // coordinates and size of node with specified corner placed at pos,
692 // and clipped by viewport
693 var startX = (corner.charAt(1) == 'L' ? pos.x : Math.max(view.l, pos.x - mb.w)),
694 startY = (corner.charAt(0) == 'T' ? pos.y : Math.max(view.t, pos.y - mb.h)),
695 endX = (corner.charAt(1) == 'L' ? Math.min(view.l + view.w, startX + mb.w) : pos.x),
696 endY = (corner.charAt(0) == 'T' ? Math.min(view.t + view.h, startY + mb.h) : pos.y),
697 width = endX - startX,
698 height = endY - startY,
699 overflow = (mb.w - width) + (mb.h - height);
701 if(best == null || overflow < best.overflow){
704 aroundCorner: choice.aroundCorner,
715 node.style.left = best.x + "px";
716 node.style.top = best.y + "px";
717 if(best.overflow && layoutNode){
718 layoutNode(node, best.aroundCorner, best.corner);
723 dijit.placeOnScreenAroundElement = function(
725 /* DomNode */ aroundNode,
726 /* Object */ aroundCorners,
727 /* Function */ layoutNode){
730 // Like placeOnScreen, except it accepts aroundNode instead of x,y
731 // and attempts to place node around it. Uses margin box dimensions.
734 // specify Which corner of aroundNode should be
735 // used to place the node => which corner(s) of node to use (see the
736 // corners parameter in dijit.placeOnScreen)
737 // e.g. {'TL': 'BL', 'BL': 'TL'}
739 // layoutNode: Function(node, aroundNodeCorner, nodeCorner)
740 // for things like tooltip, they are displayed differently (and have different dimensions)
741 // based on their orientation relative to the parent. This adjusts the popup based on orientation.
744 // get coordinates of aroundNode
745 aroundNode = dojo.byId(aroundNode);
746 var oldDisplay = aroundNode.style.display;
747 aroundNode.style.display="";
748 // #3172: use the slightly tighter border box instead of marginBox
749 var aroundNodeW = aroundNode.offsetWidth; //mb.w;
750 var aroundNodeH = aroundNode.offsetHeight; //mb.h;
751 var aroundNodePos = dojo.coords(aroundNode, true);
752 aroundNode.style.display=oldDisplay;
754 // Generate list of possible positions for node
756 for(var nodeCorner in aroundCorners){
758 aroundCorner: nodeCorner,
759 corner: aroundCorners[nodeCorner],
761 x: aroundNodePos.x + (nodeCorner.charAt(1) == 'L' ? 0 : aroundNodeW),
762 y: aroundNodePos.y + (nodeCorner.charAt(0) == 'T' ? 0 : aroundNodeH)
767 return dijit._place(node, choices, layoutNode);
772 if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
773 dojo._hasResource["dijit._base.window"] = true;
774 dojo.provide("dijit._base.window");
776 dijit.getDocumentWindow = function(doc){
778 // Get window object associated with document doc
780 // With Safari, there is not way to retrieve the window from the document, so we must fix it.
781 if(dojo.isSafari && !doc._parentWindow){
783 This is a Safari specific function that fix the reference to the parent
784 window from the document object.
785 TODO: #5711: should the use of document below reference dojo.doc instead
786 in case they're not the same?
788 var fix=function(win){
789 win.document._parentWindow=win;
790 for(var i=0; i<win.frames.length; i++){
797 //In some IE versions (at least 6.0), document.parentWindow does not return a
798 //reference to the real window object (maybe a copy), so we must fix it as well
799 //We use IE specific execScript to attach the real window reference to
800 //document._parentWindow for later use
801 //TODO: #5711: should the use of document below reference dojo.doc instead in case they're not the same?
802 if(dojo.isIE && window !== document.parentWindow && !doc._parentWindow){
804 In IE 6, only the variable "window" can be used to connect events (others
807 doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
808 //to prevent memory leak, unset it after use
809 //another possibility is to add an onUnload handler which seems overkill to me (liucougar)
810 var win = doc._parentWindow;
811 doc._parentWindow = null;
812 return win; // Window
815 return doc._parentWindow || doc.parentWindow || doc.defaultView; // Window
820 if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
821 dojo._hasResource["dijit._base.popup"] = true;
822 dojo.provide("dijit._base.popup");
828 dijit.popup = new function(){
830 // This class is used to show/hide widgets as popups.
837 this.prepare = function(/*DomNode*/ node){
839 // Prepares a node to be used as a popup
842 // Attaches node to dojo.doc.body, and
843 // positions it off screen, but not display:none, so that
844 // the widget doesn't appear in the page flow and/or cause a blank
845 // area at the bottom of the viewport (making scrollbar longer), but
846 // initialization of contained widgets works correctly
848 dojo.body().appendChild(node);
850 if(s.display == "none"){
853 s.visibility = "hidden"; // not needed for hiding, but used as flag that node is off-screen
854 s.position = "absolute";
858 this.open = function(/*Object*/ args){
860 // Popup the widget at the specified position
864 // widget to display,
866 // the button etc. that is displaying this popup
868 // DOM node (typically a button); place popup relative to this node
870 // structure specifying possible positions of popup relative to "around" node
871 // onCancel: Function
872 // callback when user has canceled the popup by
874 // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog);
875 // ie: whenever popupWidget.onCancel() is called, args.onCancel is called
877 // callback whenever this popup is closed
878 // onExecute: Function
879 // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only)
882 // 1. opening at the mouse position
883 // dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
884 // 2. opening the widget as a dropdown
885 // dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...} });
887 // Note that whatever widget called dijit.popup.open() should also listen to it's own _onBlur callback
888 // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed.
890 var widget = args.popup,
891 orient = args.orient || {'BL':'TL', 'TL':'BL'},
892 around = args.around,
893 id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+idGen++);
895 // make wrapper div to hold widget and possibly hold iframe behind it.
896 // we can't attach the iframe as a child of the widget.domNode because
897 // widget.domNode might be a <table>, <ul>, etc.
898 var wrapper = dojo.doc.createElement("div");
899 dijit.setWaiRole(wrapper, "presentation");
901 wrapper.className="dijitPopup";
902 wrapper.style.zIndex = beginZIndex + stack.length;
903 wrapper.style.visibility = "hidden";
905 wrapper.dijitPopupParent=args.parent.id;
907 dojo.body().appendChild(wrapper);
909 var s = widget.domNode.style;
913 wrapper.appendChild(widget.domNode);
915 var iframe = new dijit.BackgroundIframe(wrapper);
917 // position the wrapper node
919 dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) :
920 dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR']);
922 wrapper.style.visibility = "visible";
923 // TODO: use effects to fade in wrapper
927 // Compute the closest ancestor popup that's *not* a child of another popup.
928 // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button.
929 var getTopPopup = function(){
930 for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){
931 /* do nothing, just trying to get right value for pi */
936 // provide default escape and tab key handling
937 // (this will work for any widget, not just menu)
938 handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){
939 if(evt.keyCode == dojo.keys.ESCAPE && args.onCancel){
942 }else if(evt.keyCode == dojo.keys.TAB){
944 var topPopup = getTopPopup();
945 if(topPopup && topPopup.onCancel){
951 // watch for cancel/execute events on the popup and notify the caller
952 // (for a menu, "execute" means clicking an item)
954 handlers.push(dojo.connect(widget, "onCancel", null, args.onCancel));
957 handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", null, function(){
958 var topPopup = getTopPopup();
959 if(topPopup && topPopup.onExecute){
960 topPopup.onExecute();
969 onExecute: args.onExecute,
970 onCancel: args.onCancel,
971 onClose: args.onClose,
982 this.close = function(/*Widget*/ popup){
984 // Close specified popup and any popups that it parented
985 while(dojo.some(stack, function(elem){return elem.widget == popup;})){
986 var top = stack.pop(),
987 wrapper = top.wrapper,
990 onClose = top.onClose;
995 dojo.forEach(top.handlers, dojo.disconnect);
997 // #2685: check if the widget still has a domNode so ContentPane can change its URL without getting an error
998 if(!widget||!widget.domNode){ return; }
1000 this.prepare(widget.domNode);
1003 dojo._destroyElement(wrapper);
1012 dijit._frames = new function(){
1013 // summary: cache of iframes
1016 this.pop = function(){
1019 iframe = queue.pop();
1020 iframe.style.display="";
1023 var html="<iframe src='javascript:\"\"'"
1024 + " style='position: absolute; left: 0px; top: 0px;"
1025 + "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
1026 iframe = dojo.doc.createElement(html);
1028 iframe = dojo.doc.createElement("iframe");
1029 iframe.src = 'javascript:""';
1030 iframe.className = "dijitBackgroundIframe";
1032 iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work.
1033 dojo.body().appendChild(iframe);
1038 this.push = function(iframe){
1039 iframe.style.display="";
1041 iframe.style.removeExpression("width");
1042 iframe.style.removeExpression("height");
1049 if(dojo.isIE && dojo.isIE < 7){
1050 dojo.addOnLoad(function(){
1051 var f = dijit._frames;
1052 dojo.forEach([f.pop()], f.push);
1057 dijit.BackgroundIframe = function(/* DomNode */node){
1059 // For IE z-index schenanigans. id attribute is required.
1062 // new dijit.BackgroundIframe(node)
1063 // Makes a background iframe as a child of node, that fills
1064 // area (and position) of node
1066 if(!node.id){ throw new Error("no id"); }
1067 if((dojo.isIE && dojo.isIE < 7) || (dojo.isFF && dojo.isFF < 3 && dojo.hasClass(dojo.body(), "dijit_a11y"))){
1068 var iframe = dijit._frames.pop();
1069 node.appendChild(iframe);
1071 iframe.style.setExpression("width", dojo._scopeName + ".doc.getElementById('" + node.id + "').offsetWidth");
1072 iframe.style.setExpression("height", dojo._scopeName + ".doc.getElementById('" + node.id + "').offsetHeight");
1074 this.iframe = iframe;
1078 dojo.extend(dijit.BackgroundIframe, {
1079 destroy: function(){
1080 // summary: destroy the iframe
1082 dijit._frames.push(this.iframe);
1090 if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1091 dojo._hasResource["dijit._base.scroll"] = true;
1092 dojo.provide("dijit._base.scroll");
1094 dijit.scrollIntoView = function(/* DomNode */node){
1096 // Scroll the passed node into view, if it is not.
1098 // don't rely on that node.scrollIntoView works just because the function is there
1099 // it doesnt work in Konqueror or Opera even though the function is there and probably
1100 // not safari either
1101 // native scrollIntoView() causes FF3's whole window to scroll if there is no scroll bar
1102 // on the immediate parent
1103 // dont like browser sniffs implementations but sometimes you have to use it
1104 // #6146: IE scrollIntoView is broken
1105 // It's not enough just to scroll the menu node into view if
1106 // node.scrollIntoView hides part of the parent's scrollbar,
1107 // so just manage the parent scrollbar ourselves
1108 var parent = node.parentNode;
1109 var parentBottom = parent.scrollTop + dojo.marginBox(parent).h; //PORT was getBorderBox
1110 var nodeBottom = node.offsetTop + dojo.marginBox(node).h;
1111 if(parentBottom < nodeBottom){
1112 parent.scrollTop += (nodeBottom - parentBottom);
1113 }else if(parent.scrollTop > node.offsetTop){
1114 parent.scrollTop -= (parent.scrollTop - node.offsetTop);
1120 if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1121 dojo._hasResource["dijit._base.sniff"] = true;
1122 dojo.provide("dijit._base.sniff");
1124 // ported from dojo.html.applyBrowserClass (style.js)
1127 // Applies pre-set class names based on browser & version to the
1128 // top-level HTML node. Simply doing a require on this module will
1129 // establish this CSS. Modified version of Morris' CSS hack.
1133 var opera = d.isOpera;
1134 var maj = Math.floor;
1138 // dj_ie55: ie == 5.5,
1139 dj_ie6: maj(ie) == 6,
1140 dj_ie7: maj(ie) == 7,
1141 dj_iequirks: ie && d.isQuirks,
1142 // NOTE: Opera not supported by dijit
1144 dj_opera8: maj(opera) == 8,
1145 dj_opera9: maj(opera) == 9,
1146 dj_khtml: d.isKhtml,
1147 dj_safari: d.isSafari,
1148 dj_gecko: d.isMozilla,
1149 dj_ff2: maj(ff) == 2
1150 }; // no dojo unsupported browsers
1152 for(var p in classes){
1154 var html = dojo.doc.documentElement; //TODO browser-specific DOM magic needed?
1156 html.className += " " + p;
1166 if(!dojo._hasResource["dijit._base.bidi"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1167 dojo._hasResource["dijit._base.bidi"] = true;
1168 dojo.provide("dijit._base.bidi");
1170 // summary: applies a class to the top of the document for right-to-left stylesheet rules
1172 dojo.addOnLoad(function(){
1173 if(!dojo._isBodyLtr()){
1174 dojo.addClass(dojo.body(), "dijitRtl");
1180 if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1181 dojo._hasResource["dijit._base.typematic"] = true;
1182 dojo.provide("dijit._base.typematic");
1186 // These functions are used to repetitively call a user specified callback
1187 // method when a specific key or mouse click over a specific DOM node is
1188 // held down for a specific amount of time.
1189 // Only 1 such event is allowed to occur on the browser page at 1 time.
1191 _fireEventAndReload: function(){
1193 this._callback(++this._count, this._node, this._evt);
1194 this._currentTimeout = (this._currentTimeout < 0) ? this._initialDelay : ((this._subsequentDelay > 1) ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay));
1195 this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout);
1198 trigger: function(/*Event*/ evt, /* Object */ _this, /*DOMNode*/ node, /* Function */ callback, /* Object */ obj, /* Number */ subsequentDelay, /* Number */ initialDelay){
1200 // Start a timed, repeating callback sequence.
1201 // If already started, the function call is ignored.
1202 // This method is not normally called by the user but can be
1203 // when the normal listener code is insufficient.
1205 // evt: key or mouse event object to pass to the user callback
1206 // _this: pointer to the user's widget space.
1207 // node: the DOM node object to pass the the callback function
1208 // callback: function to call until the sequence is stopped called with 3 parameters:
1209 // count: integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped
1210 // node: the DOM node object passed in
1211 // evt: key or mouse event object
1212 // obj: user space object used to uniquely identify each typematic sequence
1213 // subsequentDelay: if > 1, the number of milliseconds until the 3->n events occur
1214 // or else the fractional time multiplier for the next event's delay, default=0.9
1215 // initialDelay: the number of milliseconds until the 2nd event occurs, default=500ms
1216 if(obj != this._obj){
1218 this._initialDelay = initialDelay || 500;
1219 this._subsequentDelay = subsequentDelay || 0.90;
1223 this._currentTimeout = -1;
1225 this._callback = dojo.hitch(_this, callback);
1226 this._fireEventAndReload();
1232 // Stop an ongoing timed, repeating callback sequence.
1234 clearTimeout(this._timer);
1238 this._callback(-1, this._node, this._evt);
1243 addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay){
1244 // summary: Start listening for a specific typematic key.
1245 // keyObject: an object defining the key to listen for.
1246 // key: (mandatory) the keyCode (number) or character (string) to listen for.
1247 // ctrlKey: desired ctrl key state to initiate the calback sequence:
1250 // either (unspecified)
1251 // altKey: same as ctrlKey but for the alt key
1252 // shiftKey: same as ctrlKey but for the shift key
1253 // See the trigger method for other parameters.
1254 // Returns an array of dojo.connect handles
1256 dojo.connect(node, "onkeypress", this, function(evt){
1257 if(evt.keyCode == keyObject.keyCode && (!keyObject.charCode || keyObject.charCode == evt.charCode) &&
1258 (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) &&
1259 (keyObject.altKey === undefined || keyObject.altKey == evt.ctrlKey) &&
1260 (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.ctrlKey)){
1261 dojo.stopEvent(evt);
1262 dijit.typematic.trigger(keyObject, _this, node, callback, keyObject, subsequentDelay, initialDelay);
1263 }else if(dijit.typematic._obj == keyObject){
1264 dijit.typematic.stop();
1267 dojo.connect(node, "onkeyup", this, function(evt){
1268 if(dijit.typematic._obj == keyObject){
1269 dijit.typematic.stop();
1275 addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay){
1276 // summary: Start listening for a typematic mouse click.
1277 // See the trigger method for other parameters.
1278 // Returns an array of dojo.connect handles
1279 var dc = dojo.connect;
1281 dc(node, "mousedown", this, function(evt){
1282 dojo.stopEvent(evt);
1283 dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay);
1285 dc(node, "mouseup", this, function(evt){
1286 dojo.stopEvent(evt);
1287 dijit.typematic.stop();
1289 dc(node, "mouseout", this, function(evt){
1290 dojo.stopEvent(evt);
1291 dijit.typematic.stop();
1293 dc(node, "mousemove", this, function(evt){
1294 dojo.stopEvent(evt);
1296 dc(node, "dblclick", this, function(evt){
1297 dojo.stopEvent(evt);
1299 dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay);
1300 setTimeout(dojo.hitch(this, dijit.typematic.stop), 50);
1306 addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay){
1307 // summary: Start listening for a specific typematic key and mouseclick.
1308 // This is a thin wrapper to addKeyListener and addMouseListener.
1309 // mouseNode: the DOM node object to listen on for mouse events.
1310 // keyNode: the DOM node object to listen on for key events.
1311 // See the addMouseListener and addKeyListener methods for other parameters.
1312 // Returns an array of dojo.connect handles
1313 return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay).concat(
1314 this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay));
1320 if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1321 dojo._hasResource["dijit._base.wai"] = true;
1322 dojo.provide("dijit._base.wai");
1327 // Detects if we are in high-contrast mode or not
1329 // This must be a named function and not an anonymous
1330 // function, so that the widget parsing code can make sure it
1331 // registers its onload function after this function.
1332 // DO NOT USE "this" within this function.
1334 // create div for testing if high contrast mode is on or images are turned off
1335 var div = dojo.doc.createElement("div");
1336 div.id = "a11yTestNode";
1337 div.style.cssText = 'border: 1px solid;'
1338 + 'border-color:red green;'
1339 + 'position: absolute;'
1342 + 'background-image: url("' + dojo.moduleUrl("dojo", "resources/blank.gif") + '");';
1343 dojo.body().appendChild(div);
1346 var cs = dojo.getComputedStyle(div);
1348 var bkImg = cs.backgroundImage;
1349 var needsA11y = (cs.borderTopColor==cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
1350 dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y");
1351 dojo.body().removeChild(div);
1356 // Test if computer is in high contrast mode.
1357 // Make sure the a11y test runs first, before widgets are instantiated.
1358 if(dojo.isIE || dojo.isMoz){ // NOTE: checking in Safari messes things up
1359 dojo._loaders.unshift(dijit.wai.onload);
1364 hasWaiRole: function(/*Element*/ elem){
1365 // summary: Determines if an element has a role.
1366 // returns: true if elem has a role attribute and false if not.
1367 return elem.hasAttribute ? elem.hasAttribute("role") : !!elem.getAttribute("role");
1370 getWaiRole: function(/*Element*/ elem){
1371 // summary: Gets the role for an element.
1373 // The role of elem or an empty string if elem
1374 // does not have a role.
1375 var value = elem.getAttribute("role");
1377 var prefixEnd = value.indexOf(":");
1378 return prefixEnd == -1 ? value : value.substring(prefixEnd+1);
1384 setWaiRole: function(/*Element*/ elem, /*String*/ role){
1385 // summary: Sets the role on an element.
1387 // On Firefox 2 and below, "wairole:" is
1388 // prepended to the provided role value.
1389 elem.setAttribute("role", (dojo.isFF && dojo.isFF < 3) ? "wairole:" + role : role);
1392 removeWaiRole: function(/*Element*/ elem){
1393 // summary: Removes the role from an element.
1394 elem.removeAttribute("role");
1397 hasWaiState: function(/*Element*/ elem, /*String*/ state){
1398 // summary: Determines if an element has a given state.
1400 // On Firefox 2 and below, we check for an attribute in namespace
1401 // "http://www.w3.org/2005/07/aaa" with a name of the given state.
1402 // On all other browsers, we check for an attribute
1403 // called "aria-"+state.
1405 // true if elem has a value for the given state and
1406 // false if it does not.
1407 if(dojo.isFF && dojo.isFF < 3){
1408 return elem.hasAttributeNS("http://www.w3.org/2005/07/aaa", state);
1410 return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state);
1414 getWaiState: function(/*Element*/ elem, /*String*/ state){
1415 // summary: Gets the value of a state on an element.
1417 // On Firefox 2 and below, we check for an attribute in namespace
1418 // "http://www.w3.org/2005/07/aaa" with a name of the given state.
1419 // On all other browsers, we check for an attribute called
1422 // The value of the requested state on elem
1423 // or an empty string if elem has no value for state.
1424 if(dojo.isFF && dojo.isFF < 3){
1425 return elem.getAttributeNS("http://www.w3.org/2005/07/aaa", state);
1427 var value = elem.getAttribute("aria-"+state);
1428 return value ? value : "";
1432 setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){
1433 // summary: Sets a state on an element.
1435 // On Firefox 2 and below, we set an attribute in namespace
1436 // "http://www.w3.org/2005/07/aaa" with a name of the given state.
1437 // On all other browsers, we set an attribute called
1439 if(dojo.isFF && dojo.isFF < 3){
1440 elem.setAttributeNS("http://www.w3.org/2005/07/aaa",
1441 "aaa:"+state, value);
1443 elem.setAttribute("aria-"+state, value);
1447 removeWaiState: function(/*Element*/ elem, /*String*/ state){
1448 // summary: Removes a state from an element.
1450 // On Firefox 2 and below, we remove the attribute in namespace
1451 // "http://www.w3.org/2005/07/aaa" with a name of the given state.
1452 // On all other browsers, we remove the attribute called
1454 if(dojo.isFF && dojo.isFF < 3){
1455 elem.removeAttributeNS("http://www.w3.org/2005/07/aaa", state);
1457 elem.removeAttribute("aria-"+state);
1464 if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1465 dojo._hasResource["dijit._base"] = true;
1466 dojo.provide("dijit._base");
1479 // FIXME: Find a better way of solving this bug!
1481 // Ugly-ass hack to solve bug #5626 for 1.1; basically force Safari to re-layout.
1482 // Note that we can't reliably use dojo.addOnLoad here because this bug is basically
1483 // a timing / race condition; so instead we use window.onload.
1484 dojo.connect(window, "load", function(){
1485 window.resizeBy(1,0);
1486 setTimeout(function(){ window.resizeBy(-1,0); }, 10);
1492 if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1493 dojo._hasResource["dojo.date.stamp"] = true;
1494 dojo.provide("dojo.date.stamp");
1496 // Methods to convert dates to or from a wire (string) format using well-known conventions
1498 dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){
1500 // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard.
1503 // Accepts a string formatted according to a profile of ISO8601 as defined by
1504 // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed.
1505 // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime)
1506 // The following combinations are valid:
1512 // * times only, with an optional time zone appended
1515 // | * THH:mm:ss.SSS
1516 // * and "datetimes" which could be any combination of the above
1518 // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm
1519 // Assumes the local time zone if not specified. Does not validate. Improperly formatted
1520 // input may return null. Arguments which are out of bounds will be handled
1521 // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st)
1522 // Only years between 100 and 9999 are supported.
1525 // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00
1528 // Used for defaults for fields omitted in the formattedString.
1529 // Uses 1970-01-01T00:00:00.0Z by default.
1531 if(!dojo.date.stamp._isoRegExp){
1532 dojo.date.stamp._isoRegExp =
1533 //TODO: could be more restrictive and check for 00-59, etc.
1534 /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/;
1537 var match = dojo.date.stamp._isoRegExp.exec(formattedString);
1542 if(match[1]){match[1]--;} // Javascript Date months are 0-based
1543 if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds
1546 // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0
1547 defaultTime = new Date(defaultTime);
1548 dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){
1549 return defaultTime["get" + prop]();
1550 }).forEach(function(value, index){
1551 if(match[index] === undefined){
1552 match[index] = value;
1556 result = new Date(match[0]||1970, match[1]||0, match[2]||1, match[3]||0, match[4]||0, match[5]||0, match[6]||0);
1557 // result.setFullYear(match[0]||1970); // for year < 100
1560 var zoneSign = match[7] && match[7].charAt(0);
1561 if(zoneSign != 'Z'){
1562 offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0);
1563 if(zoneSign != '-'){ offset *= -1; }
1566 offset -= result.getTimezoneOffset();
1569 result.setTime(result.getTime() + offset * 60000);
1573 return result; // Date or null
1577 dojo.date.stamp.__Options = function(){
1579 // "date" or "time" for partial formatting of the Date object.
1580 // Both date and time will be formatted by default.
1582 // if true, UTC/GMT is used for a timezone
1583 // milliseconds: Boolean
1584 // if true, output milliseconds
1585 this.selector = selector;
1587 this.milliseconds = milliseconds;
1591 dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){
1593 // Format a Date object as a string according a subset of the ISO-8601 standard
1596 // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt)
1597 // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date)
1598 // Does not check bounds. Only years between 100 and 9999 are supported.
1603 var _ = function(n){ return (n < 10) ? "0" + n : n; };
1604 options = options || {};
1605 var formattedDate = [];
1606 var getter = options.zulu ? "getUTC" : "get";
1608 if(options.selector != "time"){
1609 var year = dateObject[getter+"FullYear"]();
1610 date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-');
1612 formattedDate.push(date);
1613 if(options.selector != "date"){
1614 var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':');
1615 var millis = dateObject[getter+"Milliseconds"]();
1616 if(options.milliseconds){
1617 time += "."+ (millis < 100 ? "0" : "") + _(millis);
1621 }else if(options.selector != "time"){
1622 var timezoneOffset = dateObject.getTimezoneOffset();
1623 var absOffset = Math.abs(timezoneOffset);
1624 time += (timezoneOffset > 0 ? "-" : "+") +
1625 _(Math.floor(absOffset/60)) + ":" + _(absOffset%60);
1627 formattedDate.push(time);
1629 return formattedDate.join('T'); // String
1634 if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1635 dojo._hasResource["dojo.parser"] = true;
1636 dojo.provide("dojo.parser");
1639 dojo.parser = new function(){
1640 // summary: The Dom/Widget parsing package
1643 var dtName = d._scopeName + "Type";
1644 var qry = "[" + dtName + "]";
1646 function val2type(/*Object*/ value){
1648 // Returns name of type of given value.
1650 if(d.isString(value)){ return "string"; }
1651 if(typeof value == "number"){ return "number"; }
1652 if(typeof value == "boolean"){ return "boolean"; }
1653 if(d.isFunction(value)){ return "function"; }
1654 if(d.isArray(value)){ return "array"; } // typeof [] == "object"
1655 if(value instanceof Date) { return "date"; } // assume timestamp
1656 if(value instanceof d._Url){ return "url"; }
1660 function str2obj(/*String*/ value, /*String*/ type){
1662 // Convert given string value to given type
1667 return value.length ? Number(value) : NaN;
1669 // for checked/disabled value might be "" or "checked". interpret as true.
1670 return typeof value == "boolean" ? value : !(value.toLowerCase()=="false");
1672 if(d.isFunction(value)){
1673 // IE gives us a function, even when we say something like onClick="foo"
1674 // (in which case it gives us an invalid function "function(){ foo }").
1675 // Therefore, convert to string
1676 value=value.toString();
1677 value=d.trim(value.substring(value.indexOf('{')+1, value.length-1));
1680 if(value.search(/[^\w\.]+/i) != -1){
1681 // TODO: "this" here won't work
1682 value = d.parser._nameAnonFunc(new Function(value), this);
1684 return d.getObject(value, false);
1685 }catch(e){ return new Function(); }
1687 return value.split(/\s*,\s*/);
1690 case "": return new Date(""); // the NaN of dates
1691 case "now": return new Date(); // current date
1692 default: return d.date.stamp.fromISOString(value);
1695 return d.baseUrl + value;
1697 return d.fromJson(value);
1701 var instanceClasses = {
1702 // map from fully qualified name (like "dijit.Button") to structure like
1703 // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} }
1706 function getClassInfo(/*String*/ className){
1708 // fully qualified name (like "dijit.Button")
1712 // cls: dijit.Button,
1713 // params: { label: "string", disabled: "boolean"}
1716 if(!instanceClasses[className]){
1717 // get pointer to widget class
1718 var cls = d.getObject(className);
1719 if(!d.isFunction(cls)){
1720 throw new Error("Could not load class '" + className +
1721 "'. Did you spell the name correctly and use a full path, like 'dijit.form.Button'?");
1723 var proto = cls.prototype;
1725 // get table of parameter names & types
1727 for(var name in proto){
1728 if(name.charAt(0)=="_"){ continue; } // skip internal properties
1729 var defVal = proto[name];
1730 params[name]=val2type(defVal);
1733 instanceClasses[className] = { cls: cls, params: params };
1735 return instanceClasses[className];
1738 this._functionFromScript = function(script){
1741 var argsStr = script.getAttribute("args");
1743 d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){
1744 preamble += "var "+part+" = arguments["+idx+"]; ";
1747 var withStr = script.getAttribute("with");
1748 if(withStr && withStr.length){
1749 d.forEach(withStr.split(/\s*,\s*/), function(part){
1750 preamble += "with("+part+"){";
1754 return new Function(preamble+script.innerHTML+suffix);
1757 this.instantiate = function(/* Array */nodes){
1759 // Takes array of nodes, and turns them into class instances and
1760 // potentially calls a layout method to allow them to connect with
1763 d.forEach(nodes, function(node){
1764 if(!node){ return; }
1765 var type = node.getAttribute(dtName);
1766 if((!type)||(!type.length)){ return; }
1767 var clsInfo = getClassInfo(type);
1768 var clazz = clsInfo.cls;
1769 var ps = clazz._noScript||clazz.prototype._noScript;
1771 // read parameters (ie, attributes).
1772 // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"}
1774 var attributes = node.attributes;
1775 for(var name in clsInfo.params){
1776 var item = attributes.getNamedItem(name);
1777 if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; }
1778 var value = item.value;
1779 // Deal with IE quirks for 'class' and 'style'
1782 value = node.className;
1785 value = node.style && node.style.cssText; // FIXME: Opera?
1787 var _type = clsInfo.params[name];
1788 params[name] = str2obj(value, _type);
1791 // Process <script type="dojo/*"> script tags
1792 // <script type="dojo/method" event="foo"> tags are added to params, and passed to
1793 // the widget on instantiation.
1794 // <script type="dojo/method"> tags (with no event) are executed after instantiation
1795 // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation
1796 // note: dojo/* script tags cannot exist in self closing widgets, like <input />
1798 var connects = [], // functions to connect after instantiation
1799 calls = []; // functions to call after instantiation
1801 d.query("> script[type^='dojo/']", node).orphan().forEach(function(script){
1802 var event = script.getAttribute("event"),
1803 type = script.getAttribute("type"),
1804 nf = d.parser._functionFromScript(script);
1806 if(type == "dojo/connect"){
1807 connects.push({event: event, func: nf});
1817 var markupFactory = clazz["markupFactory"];
1818 if(!markupFactory && clazz["prototype"]){
1819 markupFactory = clazz.prototype["markupFactory"];
1821 // create the instance
1822 var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node);
1823 thelist.push(instance);
1825 // map it to the JS namespace if that makes sense
1826 var jsname = node.getAttribute("jsId");
1828 d.setObject(jsname, instance);
1831 // process connections and startup functions
1833 d.forEach(connects, function(connect){
1834 d.connect(instance, connect.event, null, connect.func);
1836 d.forEach(calls, function(func){
1837 func.call(instance);
1842 // Call startup on each top level instance if it makes sense (as for
1843 // widgets). Parent widgets will recursively call startup on their
1844 // (non-top level) children
1845 d.forEach(thelist, function(instance){
1848 !instance._started &&
1849 (!instance.getParent || !instance.getParent())
1857 this.parse = function(/*DomNode?*/ rootNode){
1859 // Search specified node (or root node) recursively for class instances,
1860 // and instantiate them Searches for
1861 // dojoType="qualified.class.name"
1862 var list = d.query(qry, rootNode);
1863 // go build the object instances
1864 var instances = this.instantiate(list);
1869 //Register the parser callback. It should be the first callback
1870 //after the a11y test.
1873 var parseRunner = function(){
1874 if(dojo.config["parseOnLoad"] == true){
1875 dojo.parser.parse();
1879 // FIXME: need to clobber cross-dependency!!
1880 if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){
1881 dojo._loaders.splice(1, 0, parseRunner);
1883 dojo._loaders.unshift(parseRunner);
1887 //TODO: ported from 0.4.x Dojo. Can we reduce this?
1888 dojo.parser._anonCtr = 0;
1889 dojo.parser._anon = {}; // why is this property required?
1890 dojo.parser._nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj){
1892 // Creates a reference to anonFuncPtr in thisObj with a completely
1893 // unique name. The new name is returned as a String.
1894 var jpn = "$joinpoint";
1895 var nso = (thisObj|| dojo.parser._anon);
1897 var cn = anonFuncPtr["__dojoNameCache"];
1898 if(cn && nso[cn] === anonFuncPtr){
1899 return anonFuncPtr["__dojoNameCache"];
1902 var ret = "__"+dojo.parser._anonCtr++;
1903 while(typeof nso[ret] != "undefined"){
1904 ret = "__"+dojo.parser._anonCtr++;
1906 nso[ret] = anonFuncPtr;
1907 return ret; // String
1912 if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1913 dojo._hasResource["dijit._Widget"] = true;
1914 dojo.provide("dijit._Widget");
1916 dojo.require( "dijit._base" );
1918 dojo.declare("dijit._Widget", null, {
1920 // The foundation of dijit widgets.
1923 // a unique, opaque ID string that can be assigned by users or by the
1924 // system. If the developer passes an ID which is known not to be
1925 // unique, the specified ID is ignored and the system-generated ID is
1930 // Rarely used. Overrides the default Dojo locale used to render this widget,
1931 // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute.
1932 // Value must be among the list of locales specified during by the Dojo bootstrap,
1933 // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us).
1937 // Unsupported by Dijit, but here for completeness. Dijit only supports setting text direction on the
1939 // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir)
1940 // attribute. Either left-to-right "ltr" or right-to-left "rtl".
1944 // HTML class attribute
1948 // HTML style attribute
1952 // HTML title attribute
1955 // srcNodeRef: DomNode
1956 // pointer to original dom node
1960 // this is our visible representation of the widget! Other DOM
1961 // Nodes may by assigned to other properties, usually through the
1962 // template system's dojoAttachPonit syntax, but the domNode
1963 // property is the canonical "top level" node in widget UI.
1966 // attributeMap: Object
1967 // A map of attributes and attachpoints -- typically standard HTML attributes -- to set
1968 // on the widget's dom, at the "domNode" attach point, by default.
1969 // Other node references can be specified as properties of 'this'
1970 attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""}, // TODO: add on* handlers?
1972 //////////// INITIALIZATION METHODS ///////////////////////////////////////
1973 //TODOC: params and srcNodeRef need docs. Is srcNodeRef optional?
1974 //TODOC: summary needed for postscript
1975 postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
1976 this.create(params, srcNodeRef);
1979 create: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
1981 // Kick off the life-cycle of a widget
1983 // To understand the process by which widgets are instantiated, it
1984 // is critical to understand what other methods create calls and
1985 // which of them you'll want to override. Of course, adventurous
1986 // developers could override create entirely, but this should
1987 // only be done as a last resort.
1989 // Below is a list of the methods that are called, in the order
1990 // they are fired, along with notes about what they do and if/when
1991 // you should over-ride them in your widget:
1993 // * postMixInProperties:
1994 // | * a stub function that you can over-ride to modify
1995 // variables that may have been naively assigned by
1997 // * widget is added to manager object here
1998 // * buildRendering:
1999 // | * Subclasses use this method to handle all UI initialization
2000 // Sets this.domNode. Templated widgets do this automatically
2001 // and otherwise it just uses the source dom node.
2003 // | * a stub function that you can over-ride to modify take
2004 // actions once the widget has been placed in the UI
2006 // store pointer to original dom tree
2007 this.srcNodeRef = dojo.byId(srcNodeRef);
2009 // For garbage collection. An array of handles returned by Widget.connect()
2010 // Each handle returned from Widget.connect() is an array of handles from dojo.connect()
2013 // _attaches: String[]
2014 // names of all our dojoAttachPoint variables
2017 //mixin our passed parameters
2018 if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
2020 this.params = params;
2021 dojo.mixin(this,params);
2023 this.postMixInProperties();
2025 // generate an id for the widget if one wasn't specified
2026 // (be sure to do this before buildRendering() because that function might
2027 // expect the id to be there.
2029 this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
2031 dijit.registry.add(this);
2033 this.buildRendering();
2035 // Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
2036 // The placement of these attributes is according to the property mapping in attributeMap.
2037 // Note special handling for 'style' and 'class' attributes which are lists and can
2038 // have elements from both old and new structures, and some attributes like "type"
2039 // cannot be processed this way as they are not mutable.
2041 for(var attr in this.attributeMap){
2042 var value = this[attr];
2043 if(typeof value != "object" && ((value !== "" && value !== false) || (params && params[attr]))){
2044 this.setAttribute(attr, value);
2050 this.domNode.setAttribute("widgetId", this.id);
2054 // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
2055 if(this.srcNodeRef && !this.srcNodeRef.parentNode){
2056 delete this.srcNodeRef;
2060 postMixInProperties: function(){
2062 // Called after the parameters to the widget have been read-in,
2063 // but before the widget template is instantiated.
2064 // Especially useful to set properties that are referenced in the widget template.
2067 buildRendering: function(){
2069 // Construct the UI for this widget, setting this.domNode.
2070 // Most widgets will mixin TemplatedWidget, which overrides this method.
2071 this.domNode = this.srcNodeRef || dojo.doc.createElement('div');
2074 postCreate: function(){
2076 // Called after a widget's dom has been setup
2079 startup: function(){
2081 // Called after a widget's children, and other widgets on the page, have been created.
2082 // Provides an opportunity to manipulate any children before they are displayed.
2083 // This is useful for composite widgets that need to control or layout sub-widgets.
2084 // Many layout widgets can use this as a wiring phase.
2085 this._started = true;
2088 //////////// DESTROY FUNCTIONS ////////////////////////////////
2090 destroyRecursive: function(/*Boolean*/ finalize){
2092 // Destroy this widget and it's descendants. This is the generic
2093 // "destructor" function that all widget users should call to
2094 // cleanly discard with a widget. Once a widget is destroyed, it's
2095 // removed from the manager object.
2096 // finalize: Boolean
2097 // is this function being called part of global environment
2100 this.destroyDescendants();
2104 destroy: function(/*Boolean*/ finalize){
2106 // Destroy this widget, but not its descendants
2107 // finalize: Boolean
2108 // is this function being called part of global environment
2111 this.uninitialize();
2112 dojo.forEach(this._connects, function(array){
2113 dojo.forEach(array, dojo.disconnect);
2116 // destroy widgets created as part of template, etc.
2117 dojo.forEach(this._supportingWidgets || [], function(w){ w.destroy(); });
2119 this.destroyRendering(finalize);
2120 dijit.registry.remove(this.id);
2123 destroyRendering: function(/*Boolean*/ finalize){
2125 // Destroys the DOM nodes associated with this widget
2126 // finalize: Boolean
2127 // is this function being called part of global environment
2131 this.bgIframe.destroy();
2132 delete this.bgIframe;
2136 dojo._destroyElement(this.domNode);
2137 delete this.domNode;
2140 if(this.srcNodeRef){
2141 dojo._destroyElement(this.srcNodeRef);
2142 delete this.srcNodeRef;
2146 destroyDescendants: function(){
2148 // Recursively destroy the children of this widget and their
2151 // TODO: should I destroy in the reverse order, to go bottom up?
2152 dojo.forEach(this.getDescendants(), function(widget){ widget.destroy(); });
2155 uninitialize: function(){
2157 // stub function. Override to implement custom widget tear-down
2162 ////////////////// MISCELLANEOUS METHODS ///////////////////
2164 onFocus: function(){
2166 // stub function. Override or connect to this method to receive
2167 // notifications for when the widget moves into focus.
2172 // stub function. Override or connect to this method to receive
2173 // notifications for when the widget moves out of focus.
2176 _onFocus: function(e){
2180 _onBlur: function(){
2184 setAttribute: function(/*String*/ attr, /*anything*/ value){
2186 // Set native HTML attributes reflected in the widget,
2187 // such as readOnly, disabled, and maxLength in TextBox widgets.
2189 // In general, a widget's "value" is controlled via setValue()/getValue(),
2190 // rather than this method. The exception is for widgets where the
2191 // end user can't adjust the value, such as Button and CheckBox;
2192 // in the unusual case that you want to change the value attribute of
2193 // those widgets, use setAttribute().
2194 var mapNode = this[this.attributeMap[attr]||'domNode'];
2198 dojo.addClass(mapNode, value);
2201 if(mapNode.style.cssText){
2202 mapNode.style.cssText += "; " + value;// FIXME: Opera
2204 mapNode.style.cssText = value;
2208 if(/^on[A-Z]/.test(attr)){ // eg. onSubmit needs to be onsubmit
2209 attr = attr.toLowerCase();
2211 if(typeof value == "function"){ // functions execute in the context of the widget
2212 value = dojo.hitch(this, value);
2214 dojo.attr(mapNode, attr, value);
2218 toString: function(){
2220 // returns a string that represents the widget. When a widget is
2221 // cast to a string, this method will be used to generate the
2222 // output. Currently, it does not implement any sort of reversable
2224 return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
2227 getDescendants: function(){
2229 // Returns all the widgets that contained by this, i.e., all widgets underneath this.containerNode.
2230 if(this.containerNode){
2231 var list= dojo.query('[widgetId]', this.containerNode);
2232 return list.map(dijit.byNode); // Array
2239 nodesWithKeyClick: ["input", "button"],
2242 /*Object|null*/ obj,
2244 /*String|Function*/ method){
2246 // Connects specified obj/event to specified method of this object
2247 // and registers for disconnect() on widget destroy.
2248 // Special event: "ondijitclick" triggers on a click or enter-down or space-up
2249 // Similar to dojo.connect() but takes three arguments rather than four.
2251 if(event == "ondijitclick"){
2252 // add key based click activation for unsupported nodes.
2253 if(!this.nodesWithKeyClick[obj.nodeName]){
2254 handles.push(dojo.connect(obj, "onkeydown", this,
2256 if(e.keyCode == dojo.keys.ENTER){
2257 return (dojo.isString(method))?
2258 this[method](e) : method.call(this, e);
2259 }else if(e.keyCode == dojo.keys.SPACE){
2260 // stop space down as it causes IE to scroll
2261 // the browser window
2265 handles.push(dojo.connect(obj, "onkeyup", this,
2267 if(e.keyCode == dojo.keys.SPACE){
2268 return dojo.isString(method) ?
2269 this[method](e) : method.call(this, e);
2275 handles.push(dojo.connect(obj, event, this, method));
2277 // return handles for FormElement and ComboBox
2278 this._connects.push(handles);
2282 disconnect: function(/*Object*/ handles){
2284 // Disconnects handle created by this.connect.
2285 // Also removes handle from this widget's list of connects
2286 for(var i=0; i<this._connects.length; i++){
2287 if(this._connects[i]==handles){
2288 dojo.forEach(handles, dojo.disconnect);
2289 this._connects.splice(i, 1);
2295 isLeftToRight: function(){
2297 // Checks the DOM to for the text direction for bi-directional support
2299 // This method cannot be used during widget construction because the widget
2300 // must first be connected to the DOM tree. Parent nodes are searched for the
2301 // 'dir' attribute until one is found, otherwise left to right mode is assumed.
2302 // See HTML spec, DIR attribute for more information.
2304 if(!("_ltr" in this)){
2305 this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
2307 return this._ltr; //Boolean
2310 isFocusable: function(){
2312 // Return true if this widget can currently be focused
2314 return this.focus && (dojo.style(this.domNode, "display") != "none");
2320 if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2321 dojo._hasResource["dojo.string"] = true;
2322 dojo.provide("dojo.string");
2326 // summary: String utilities for Dojo
2330 dojo.string.pad = function(/*String*/text, /*int*/size, /*String?*/ch, /*boolean?*/end){
2332 // Pad a string to guarantee that it is at least `size` length by
2333 // filling with the character `ch` at either the start or end of the
2334 // string. Pads at the start, by default.
2335 // text: the string to pad
2336 // size: length to provide padding
2337 // ch: character to pad, defaults to '0'
2338 // end: adds padding at the end if true, otherwise pads at start
2340 var out = String(text);
2344 while(out.length < size){
2351 return out; // String
2354 dojo.string.substitute = function( /*String*/template,
2355 /*Object|Array*/map,
2356 /*Function?*/transform,
2357 /*Object?*/thisObject){
2359 // Performs parameterized substitutions on a string. Throws an
2360 // exception if any parameter is unmatched.
2363 // | dojo.string.substitute("File '${0}' is not found in directory '${1}'.",["foo.html","/temp"]);
2364 // | dojo.string.substitute("File '${name}' is not found in directory '${info.dir}'.",
2365 // | {name: "foo.html", info: {dir: "/temp"}});
2367 // | "File 'foo.html' is not found in directory '/temp'."
2369 // a string with expressions in the form `${key}` to be replaced or
2370 // `${key:format}` which specifies a format function.
2371 // map: hash to search for substitutions
2373 // a function to process all parameters before substitution takes
2374 // place, e.g. dojo.string.encodeXML
2376 // where to look for optional format function; default to the global
2379 return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, function(match, key, format){
2380 var value = dojo.getObject(key,false,map);
2381 if(format){ value = dojo.getObject(format,false,thisObject)(value);}
2382 if(transform){ value = transform(value, key); }
2383 return value.toString();
2387 dojo.string.trim = function(/*String*/ str){
2388 // summary: trims whitespaces from both sides of the string
2390 // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript).
2391 // The short yet performant version of this function is
2392 // dojo.trim(), which is part of Dojo base.
2393 str = str.replace(/^\s+/, '');
2394 for(var i = str.length - 1; i > 0; i--){
2395 if(/\S/.test(str.charAt(i))){
2396 str = str.substring(0, i + 1);
2400 return str; // String
2405 if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2406 dojo._hasResource["dijit._Templated"] = true;
2407 dojo.provide("dijit._Templated");
2413 dojo.declare("dijit._Templated",
2417 // Mixin for widgets that are instantiated from a template
2419 // templateNode: DomNode
2420 // a node that represents the widget template. Pre-empts both templateString and templatePath.
2423 // templateString: String
2424 // a string that represents the widget template. Pre-empts the
2425 // templatePath. In builds that have their strings "interned", the
2426 // templatePath is converted to an inline templateString, thereby
2427 // preventing a synchronous network call.
2428 templateString: null,
2430 // templatePath: String
2431 // Path to template (HTML file) for this widget relative to dojo.baseUrl
2434 // widgetsInTemplate: Boolean
2435 // should we parse the template to find widgets that might be
2436 // declared in markup inside it? false by default.
2437 widgetsInTemplate: false,
2439 // containerNode: DomNode
2440 // holds child elements. "containerNode" is generally set via a
2441 // dojoAttachPoint assignment and it designates where children of
2442 // the src dom node will be placed
2443 containerNode: null,
2445 // skipNodeCache: Boolean
2446 // if using a cached widget template node poses issues for a
2447 // particular widget class, it can set this property to ensure
2448 // that its template is always re-built from a string
2449 _skipNodeCache: false,
2451 _stringRepl: function(tmpl){
2452 var className = this.declaredClass, _this = this;
2453 // Cache contains a string because we need to do property replacement
2454 // do the property replacement
2455 return dojo.string.substitute(tmpl, this, function(value, key){
2456 if(key.charAt(0) == '!'){ value = _this[key.substr(1)]; }
2457 if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide
2458 if(!value){ return ""; }
2460 // Substitution keys beginning with ! will skip the transform step,
2461 // in case a user wishes to insert unescaped markup, e.g. ${!foo}
2462 return key.charAt(0) == "!" ? value :
2463 // Safer substitution, see heading "Attribute values" in
2464 // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
2465 value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method?
2470 buildRendering: function(){
2472 // Construct the UI for this widget from a template, setting this.domNode.
2474 // Lookup cached version of template, and download to cache if it
2475 // isn't there already. Returns either a DomNode or a string, depending on
2476 // whether or not the template contains ${foo} replacement parameters.
2477 var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache);
2480 if(dojo.isString(cached)){
2481 node = dijit._Templated._createNodesFromText(this._stringRepl(cached))[0];
2483 // if it's a node, all we have to do is clone it
2484 node = cached.cloneNode(true);
2487 // recurse through the node, looking for, and attaching to, our
2488 // attachment points which should be defined on the template node.
2489 this._attachTemplateNodes(node);
2491 var source = this.srcNodeRef;
2492 if(source && source.parentNode){
2493 source.parentNode.replaceChild(node, source);
2496 this.domNode = node;
2497 if(this.widgetsInTemplate){
2498 var cw = this._supportingWidgets = dojo.parser.parse(node);
2499 this._attachTemplateNodes(cw, function(n,p){
2504 this._fillContent(source);
2507 _fillContent: function(/*DomNode*/ source){
2509 // relocate source contents to templated container node
2510 // this.containerNode must be able to receive children, or exceptions will be thrown
2511 var dest = this.containerNode;
2513 while(source.hasChildNodes()){
2514 dest.appendChild(source.firstChild);
2519 _attachTemplateNodes: function(rootNode, getAttrFunc){
2520 // summary: Iterate through the template and attach functions and nodes accordingly.
2522 // Map widget properties and functions to the handlers specified in
2523 // the dom node and it's descendants. This function iterates over all
2524 // nodes and looks for these properties:
2525 // * dojoAttachPoint
2526 // * dojoAttachEvent
2529 // rootNode: DomNode|Array[Widgets]
2530 // the node to search for properties. All children will be searched.
2531 // getAttrFunc: function?
2532 // a function which will be used to obtain property for a given
2535 getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); };
2537 var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
2538 var x=dojo.isArray(rootNode)?0:-1;
2539 for(; x<nodes.length; x++){
2540 var baseNode = (x == -1) ? rootNode : nodes[x];
2541 if(this.widgetsInTemplate && getAttrFunc(baseNode,'dojoType')){
2544 // Process dojoAttachPoint
2545 var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint");
2547 var point, points = attachPoint.split(/\s*,\s*/);
2548 while((point = points.shift())){
2549 if(dojo.isArray(this[point])){
2550 this[point].push(baseNode);
2552 this[point]=baseNode;
2557 // Process dojoAttachEvent
2558 var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent");
2560 // NOTE: we want to support attributes that have the form
2561 // "domEvent: nativeEvent; ..."
2562 var event, events = attachEvent.split(/\s*,\s*/);
2563 var trim = dojo.trim;
2564 while((event = events.shift())){
2566 var thisFunc = null;
2567 if(event.indexOf(":") != -1){
2568 // oh, if only JS had tuple assignment
2569 var funcNameArr = event.split(":");
2570 event = trim(funcNameArr[0]);
2571 thisFunc = trim(funcNameArr[1]);
2573 event = trim(event);
2578 this.connect(baseNode, event, thisFunc);
2583 // waiRole, waiState
2584 var role = getAttrFunc(baseNode, "waiRole");
2586 dijit.setWaiRole(baseNode, role);
2588 var values = getAttrFunc(baseNode, "waiState");
2590 dojo.forEach(values.split(/\s*,\s*/), function(stateValue){
2591 if(stateValue.indexOf('-') != -1){
2592 var pair = stateValue.split('-');
2593 dijit.setWaiState(baseNode, pair[0], pair[1]);
2603 // key is either templatePath or templateString; object is either string or DOM tree
2604 dijit._Templated._templateCache = {};
2606 dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){
2608 // Static method to get a template based on the templatePath or
2609 // templateString key
2610 // templatePath: String
2611 // The URL to get the template from. dojo.uri.Uri is often passed as well.
2612 // templateString: String?
2613 // a string to use in lieu of fetching the template from a URL. Takes precedence
2614 // over templatePath
2616 // Either string (if there are ${} variables that need to be replaced) or just
2617 // a DOM tree (if the node can be cloned directly)
2619 // is it already cached?
2620 var tmplts = dijit._Templated._templateCache;
2621 var key = templateString || templatePath;
2622 var cached = tmplts[key];
2627 // If necessary, load template string from template path
2628 if(!templateString){
2629 templateString = dijit._Templated._sanitizeTemplateString(dojo._getText(templatePath));
2632 templateString = dojo.string.trim(templateString);
2634 if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){
2635 // there are variables in the template so all we can do is cache the string
2636 return (tmplts[key] = templateString); //String
2638 // there are no variables in the template so we can cache the DOM tree
2639 return (tmplts[key] = dijit._Templated._createNodesFromText(templateString)[0]); //Node
2643 dijit._Templated._sanitizeTemplateString = function(/*String*/tString){
2645 // Strips <?xml ...?> declarations so that external SVG and XML
2646 // documents can be added to a document without worry. Also, if the string
2647 // is an HTML document, only the part inside the body tag is returned.
2649 tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
2650 var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
2652 tString = matches[1];
2657 return tString; //String
2662 dojo.addOnUnload(function(){
2663 var cache = dijit._Templated._templateCache;
2664 for(var key in cache){
2665 var value = cache[key];
2666 if(!isNaN(value.nodeType)){ // isNode equivalent
2667 dojo._destroyElement(value);
2676 cell: {re: /^<t[dh][\s\r\n>]/i, pre: "<table><tbody><tr>", post: "</tr></tbody></table>"},
2677 row: {re: /^<tr[\s\r\n>]/i, pre: "<table><tbody>", post: "</tbody></table>"},
2678 section: {re: /^<(thead|tbody|tfoot)[\s\r\n>]/i, pre: "<table>", post: "</table>"}
2681 // dummy container node used temporarily to hold nodes being created
2684 dijit._Templated._createNodesFromText = function(/*String*/text){
2686 // Attempts to create a set of nodes based on the structure of the passed text.
2689 tn = dojo.doc.createElement("div");
2690 tn.style.display="none";
2691 dojo.body().appendChild(tn);
2693 var tableType = "none";
2694 var rtext = text.replace(/^\s+/, "");
2695 for(var type in tagMap){
2696 var map = tagMap[type];
2697 if(map.re.test(rtext)){
2699 text = map.pre + text + map.post;
2704 tn.innerHTML = text;
2709 var tag = { cell: "tr", row: "tbody", section: "table" }[tableType];
2710 var _parent = (typeof tag != "undefined") ?
2711 tn.getElementsByTagName(tag)[0] :
2715 while(_parent.firstChild){
2716 nodes.push(_parent.removeChild(_parent.firstChild));
2719 return nodes; // Array
2723 // These arguments can be specified for widgets which are used in templates.
2724 // Since any widget can be specified as sub widgets in template, mix it
2725 // into the base widget class. (This is a hack, but it's effective.)
2726 dojo.extend(dijit._Widget,{
2727 dojoAttachEvent: "",
2728 dojoAttachPoint: "",
2735 if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2736 dojo._hasResource["dijit._Container"] = true;
2737 dojo.provide("dijit._Container");
2739 dojo.declare("dijit._Contained",
2743 // Mixin for widgets that are children of a container widget
2746 // | // make a basic custom widget that knows about it's parents
2747 // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{});
2749 getParent: function(){
2751 // Returns the parent widget of this widget, assuming the parent
2752 // implements dijit._Container
2753 for(var p=this.domNode.parentNode; p; p=p.parentNode){
2754 var id = p.getAttribute && p.getAttribute("widgetId");
2756 var parent = dijit.byId(id);
2757 return parent.isContainer ? parent : null;
2763 _getSibling: function(which){
2764 var node = this.domNode;
2766 node = node[which+"Sibling"];
2767 }while(node && node.nodeType != 1);
2768 if(!node){ return null; } // null
2769 var id = node.getAttribute("widgetId");
2770 return dijit.byId(id);
2773 getPreviousSibling: function(){
2775 // Returns null if this is the first child of the parent,
2776 // otherwise returns the next element sibling to the "left".
2778 return this._getSibling("previous"); // Mixed
2781 getNextSibling: function(){
2783 // Returns null if this is the last child of the parent,
2784 // otherwise returns the next element sibling to the "right".
2786 return this._getSibling("next"); // Mixed
2791 dojo.declare("dijit._Container",
2795 // Mixin for widgets that contain a list of children.
2797 // Use this mixin when the widget needs to know about and
2798 // keep track of it's widget children. Widgets like SplitContainer
2799 // and TabContainer.
2803 addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
2805 // Process the given child widget, inserting it's dom node as
2806 // a child of our dom node
2808 if(insertIndex === undefined){
2809 insertIndex = "last";
2811 var refNode = this.containerNode || this.domNode;
2812 if(insertIndex && typeof insertIndex == "number"){
2813 var children = dojo.query("> [widgetid]", refNode);
2814 if(children && children.length >= insertIndex){
2815 refNode = children[insertIndex-1]; insertIndex = "after";
2818 dojo.place(widget.domNode, refNode, insertIndex);
2820 // If I've been started but the child widget hasn't been started,
2821 // start it now. Make sure to do this after widget has been
2822 // inserted into the DOM tree, so it can see that it's being controlled by me,
2823 // so it doesn't try to size itself.
2824 if(this._started && !widget._started){
2829 removeChild: function(/*Widget*/ widget){
2831 // Removes the passed widget instance from this widget but does
2833 var node = widget.domNode;
2834 node.parentNode.removeChild(node); // detach but don't destroy
2837 _nextElement: function(node){
2839 node = node.nextSibling;
2840 }while(node && node.nodeType != 1);
2844 _firstElement: function(node){
2845 node = node.firstChild;
2846 if(node && node.nodeType != 1){
2847 node = this._nextElement(node);
2852 getChildren: function(){
2854 // Returns array of children widgets
2855 return dojo.query("> [widgetId]", this.containerNode || this.domNode).map(dijit.byNode); // Array
2858 hasChildren: function(){
2860 // Returns true if widget has children
2861 var cn = this.containerNode || this.domNode;
2862 return !!this._firstElement(cn); // Boolean
2865 _getSiblingOfChild: function(/*Widget*/ child, /*int*/ dir){
2867 // Get the next or previous widget sibling of child
2869 // if 1, get the next sibling
2870 // if -1, get the previous sibling
2871 var node = child.domNode;
2872 var which = (dir>0 ? "nextSibling" : "previousSibling");
2875 }while(node && (node.nodeType != 1 || !dijit.byNode(node)));
2876 return node ? dijit.byNode(node) : null;
2881 dojo.declare("dijit._KeyNavContainer",
2885 // summary: A _Container with keyboard navigation of its children.
2887 // To use this mixin, call connectKeyNavHandlers() in
2888 // postCreate() and call startupKeyNavChildren() in startup().
2889 // It provides normalized keyboard and focusing code for Container
2892 // focusedChild: Widget
2893 // The currently focused child widget, or null if there isn't one
2899 connectKeyNavHandlers: function(/*Array*/ prevKeyCodes, /*Array*/ nextKeyCodes){
2901 // Call in postCreate() to attach the keyboard handlers
2902 // to the container.
2903 // preKeyCodes: Array
2904 // Key codes for navigating to the previous child.
2905 // nextKeyCodes: Array
2906 // Key codes for navigating to the next child.
2908 var keyCodes = this._keyNavCodes = {};
2909 var prev = dojo.hitch(this, this.focusPrev);
2910 var next = dojo.hitch(this, this.focusNext);
2911 dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev });
2912 dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next });
2913 this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
2914 this.connect(this.domNode, "onfocus", "_onContainerFocus");
2917 startupKeyNavChildren: function(){
2919 // Call in startup() to set child tabindexes to -1
2920 dojo.forEach(this.getChildren(), dojo.hitch(this, "_startupChild"));
2923 addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
2924 // summary: Add a child to our _Container
2925 dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
2926 this._startupChild(widget);
2930 // summary: Default focus() implementation: focus the first child.
2931 this.focusFirstChild();
2934 focusFirstChild: function(){
2935 // summary: Focus the first focusable child in the container.
2936 this.focusChild(this._getFirstFocusableChild());
2939 focusNext: function(){
2940 // summary: Focus the next widget or focal node (for widgets
2941 // with multiple focal nodes) within this container.
2942 if(this.focusedChild && this.focusedChild.hasNextFocalNode
2943 && this.focusedChild.hasNextFocalNode()){
2944 this.focusedChild.focusNext();
2947 var child = this._getNextFocusableChild(this.focusedChild, 1);
2948 if(child.getFocalNodes){
2949 this.focusChild(child, child.getFocalNodes()[0]);
2951 this.focusChild(child);
2955 focusPrev: function(){
2956 // summary: Focus the previous widget or focal node (for widgets
2957 // with multiple focal nodes) within this container.
2958 if(this.focusedChild && this.focusedChild.hasPrevFocalNode
2959 && this.focusedChild.hasPrevFocalNode()){
2960 this.focusedChild.focusPrev();
2963 var child = this._getNextFocusableChild(this.focusedChild, -1);
2964 if(child.getFocalNodes){
2965 var nodes = child.getFocalNodes();
2966 this.focusChild(child, nodes[nodes.length-1]);
2968 this.focusChild(child);
2972 focusChild: function(/*Widget*/ widget, /*Node?*/ node){
2973 // summary: Focus widget. Optionally focus 'node' within widget.
2975 if(this.focusedChild && widget !== this.focusedChild){
2976 this._onChildBlur(this.focusedChild);
2978 this.focusedChild = widget;
2979 if(node && widget.focusFocalNode){
2980 widget.focusFocalNode(node);
2987 _startupChild: function(/*Widget*/ widget){
2989 // Set tabindex="-1" on focusable widgets so that we
2990 // can focus them programmatically and by clicking.
2991 // Connect focus and blur handlers.
2992 if(widget.getFocalNodes){
2993 dojo.forEach(widget.getFocalNodes(), function(node){
2994 dojo.attr(node, "tabindex", -1);
2995 this._connectNode(node);
2998 var node = widget.focusNode || widget.domNode;
2999 if(widget.isFocusable()){
3000 dojo.attr(node, "tabindex", -1);
3002 this._connectNode(node);
3006 _connectNode: function(/*Element*/ node){
3007 this.connect(node, "onfocus", "_onNodeFocus");
3008 this.connect(node, "onblur", "_onNodeBlur");
3011 _onContainerFocus: function(evt){
3012 // focus bubbles on Firefox,
3013 // so just make sure that focus has really gone to the container
3014 if(evt.target === this.domNode){
3015 this.focusFirstChild();
3019 _onContainerKeypress: function(evt){
3020 if(evt.ctrlKey || evt.altKey){ return; }
3021 var func = this._keyNavCodes[evt.keyCode];
3024 dojo.stopEvent(evt);
3028 _onNodeFocus: function(evt){
3029 // while focus is on a child,
3030 // take the container out of the tab order so that
3031 // we can shift-tab to the element before the container
3032 dojo.attr(this.domNode, "tabindex", -1);
3033 // record the child that has been focused
3034 var widget = dijit.getEnclosingWidget(evt.target);
3035 if(widget && widget.isFocusable()){
3036 this.focusedChild = widget;
3038 dojo.stopEvent(evt);
3041 _onNodeBlur: function(evt){
3042 // when focus leaves a child,
3043 // reinstate the container's tabindex
3045 dojo.attr(this.domNode, "tabindex", this.tabIndex);
3047 dojo.stopEvent(evt);
3050 _onChildBlur: function(/*Widget*/ widget){
3052 // Called when focus leaves a child widget to go
3053 // to a sibling widget.
3056 _getFirstFocusableChild: function(){
3057 return this._getNextFocusableChild(null, 1);
3060 _getNextFocusableChild: function(child, dir){
3062 child = this._getSiblingOfChild(child, dir);
3064 var children = this.getChildren();
3065 for(var i=0; i < children.length; i++){
3067 child = children[(dir>0) ? 0 : (children.length-1)];
3069 if(child.isFocusable()){
3072 child = this._getSiblingOfChild(child, dir);
3074 // no focusable child found
3082 if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3083 dojo._hasResource["dijit.layout._LayoutWidget"] = true;
3084 dojo.provide("dijit.layout._LayoutWidget");
3089 dojo.declare("dijit.layout._LayoutWidget",
3090 [dijit._Widget, dijit._Container, dijit._Contained],
3093 // Mixin for widgets that contain a list of children like SplitContainer.
3094 // Widgets which mixin this code must define layout() to lay out the children
3096 isLayoutContainer: true,
3098 postCreate: function(){
3099 dojo.addClass(this.domNode, "dijitContainer");
3102 startup: function(){
3104 // Called after all the widgets have been instantiated and their
3105 // dom nodes have been inserted somewhere under dojo.doc.body.
3107 // Widgets should override this method to do any initialization
3108 // dependent on other widgets existing, and then call
3109 // this superclass method to finish things off.
3111 // startup() in subclasses shouldn't do anything
3112 // size related because the size of the widget hasn't been set yet.
3114 if(this._started){ return; }
3116 dojo.forEach(this.getChildren(), function(child){ child.startup(); });
3118 // If I am a top level widget
3119 if(!this.getParent || !this.getParent()){
3120 // Do recursive sizing and layout of all my descendants
3121 // (passing in no argument to resize means that it has to glean the size itself)
3124 // since my parent isn't a layout container, and my style is width=height=100% (or something similar),
3125 // then I need to watch when the window resizes, and size myself accordingly
3126 // (passing in no argument to resize means that it has to glean the size itself)
3127 this.connect(window, 'onresize', function(){this.resize();});
3130 this.inherited(arguments);
3133 resize: function(args){
3135 // Explicitly set this widget's size (in pixels),
3136 // and then call layout() to resize contents (and maybe adjust child widgets)
3139 // {w: int, h: int, l: int, t: int}
3141 var node = this.domNode;
3143 // set margin box size, unless it wasn't specified, in which case use current size
3145 dojo.marginBox(node, args);
3147 // set offset of the node
3148 if(args.t){ node.style.top = args.t + "px"; }
3149 if(args.l){ node.style.left = args.l + "px"; }
3151 // If either height or width wasn't specified by the user, then query node for it.
3152 // But note that setting the margin box and then immediately querying dimensions may return
3153 // inaccurate results, so try not to depend on it.
3154 var mb = dojo.mixin(dojo.marginBox(node), args||{});
3156 // console.log(this, ": setting size to ", mb);
3158 // Save the size of my content box.
3159 this._contentBox = dijit.layout.marginBox2contentBox(node, mb);
3161 // Callback for widget to adjust size of it's children
3167 // Widgets override this method to size & position their contents/children.
3168 // When this is called this._contentBox is guaranteed to be set (see resize()).
3170 // This is called after startup(), and also when the widget's size has been
3176 dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
3178 // Given the margin-box size of a node, return it's content box size.
3179 // Functions like dojo.contentBox() but is more reliable since it doesn't have
3180 // to wait for the browser to compute sizes.
3181 var cs = dojo.getComputedStyle(node);
3182 var me=dojo._getMarginExtents(node, cs);
3183 var pb=dojo._getPadBorderExtents(node, cs);
3185 l: dojo._toPixelValue(node, cs.paddingLeft),
3186 t: dojo._toPixelValue(node, cs.paddingTop),
3187 w: mb.w - (me.w + pb.w),
3188 h: mb.h - (me.h + pb.h)
3193 var capitalize = function(word){
3194 return word.substring(0,1).toUpperCase() + word.substring(1);
3197 var size = function(widget, dim){
3199 widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim);
3201 // record child's size, but favor our own numbers when we have them.
3202 // the browser lies sometimes
3203 dojo.mixin(widget, dojo.marginBox(widget.domNode));
3204 dojo.mixin(widget, dim);
3207 dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Object[]*/ children){
3210 * Layout a bunch of child dom nodes within a parent dom node
3214 * {l, t, w, h} object specifying dimensions of container into which to place children
3216 * an array like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ]
3219 // copy dim because we are going to modify it
3220 dim = dojo.mixin({}, dim);
3222 dojo.addClass(container, "dijitLayoutContainer");
3224 // Move "client" elements to the end of the array for layout. a11y dictates that the author
3225 // needs to be able to put them in the document in tab-order, but this algorithm requires that
3227 children = dojo.filter(children, function(item){ return item.layoutAlign != "client"; })
3228 .concat(dojo.filter(children, function(item){ return item.layoutAlign == "client"; }));
3230 // set positions/sizes
3231 dojo.forEach(children, function(child){
3232 var elm = child.domNode,
3233 pos = child.layoutAlign;
3235 // set elem to upper left corner of unused space; may move it later
3236 var elmStyle = elm.style;
3237 elmStyle.left = dim.l+"px";
3238 elmStyle.top = dim.t+"px";
3239 elmStyle.bottom = elmStyle.right = "auto";
3241 dojo.addClass(elm, "dijitAlign" + capitalize(pos));
3243 // set size && adjust record of remaining space.
3244 // note that setting the width of a <div> may affect it's height.
3245 if(pos=="top" || pos=="bottom"){
3246 size(child, { w: dim.w });
3251 elmStyle.top = dim.t + dim.h + "px";
3253 }else if(pos=="left" || pos=="right"){
3254 size(child, { h: dim.h });
3259 elmStyle.left = dim.l + dim.w + "px";
3261 }else if(pos=="client"){
3271 if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3272 dojo._hasResource["dijit.form._FormWidget"] = true;
3273 dojo.provide("dijit.form._FormWidget");
3278 dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated],
3282 _FormWidget's correspond to native HTML elements such as <checkbox> or <button>.
3283 Each _FormWidget represents a single HTML element.
3285 All these widgets should have these attributes just like native HTML input elements.
3286 You can set them during widget construction.
3288 They also share some common methods.
3291 // baseClass: String
3292 // Root CSS class of the widget (ex: dijitTextBox), used to add CSS classes of widget
3293 // (ex: "dijitTextBox dijitTextBoxInvalid dijitTextBoxFocused dijitTextBoxInvalidFocused")
3294 // See _setStateClass().
3298 // Name used when submitting form; same as "name" attribute or plain HTML elements
3302 // Corresponds to the native HTML <input> element's attribute.
3306 // Corresponds to the native HTML <input> element's attribute.
3310 // Corresponds to the native HTML <input> element's attribute.
3313 // tabIndex: Integer
3314 // Order fields are traversed when user hits the tab key
3317 // disabled: Boolean
3318 // Should this widget respond to user input?
3319 // In markup, this is specified as "disabled='disabled'", or just "disabled".
3322 // readOnly: Boolean
3323 // Should this widget respond to user input?
3324 // In markup, this is specified as "readOnly".
3325 // Similar to disabled except readOnly form values are submitted
3328 // intermediateChanges: Boolean
3329 // Fires onChange for each value change or only on demand
3330 intermediateChanges: false,
3332 // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are.
3333 // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared
3334 // directly in the template as read by the parser in order to function. IE is known to specifically
3335 // require the 'name' attribute at element creation time.
3336 attributeMap: dojo.mixin(dojo.clone(dijit._Widget.prototype.attributeMap),
3337 {value:"focusNode", disabled:"focusNode", readOnly:"focusNode", id:"focusNode", tabIndex:"focusNode", alt:"focusNode"}),
3339 setAttribute: function(/*String*/ attr, /*anything*/ value){
3340 this.inherited(arguments);
3343 var tabIndexNode = this[this.attributeMap['tabIndex']||'domNode'];
3345 //reset those, because after the domNode is disabled, we can no longer receive
3346 //mouse related events, see #4200
3347 this._hovering = false;
3348 this._active = false;
3349 // remove the tabIndex, especially for FF
3350 tabIndexNode.removeAttribute('tabIndex');
3352 tabIndexNode.setAttribute('tabIndex', this.tabIndex);
3354 dijit.setWaiState(this[this.attributeMap['disabled']||'domNode'], "disabled", value);
3355 this._setStateClass();
3359 setDisabled: function(/*Boolean*/ disabled){
3361 // Set disabled state of widget (Deprecated).
3362 dojo.deprecated("setDisabled("+disabled+") is deprecated. Use setAttribute('disabled',"+disabled+") instead.", "", "2.0");
3363 this.setAttribute('disabled', disabled);
3367 _onMouse : function(/*Event*/ event){
3369 // Sets _hovering, _active, and stateModifier properties depending on mouse state,
3370 // then calls setStateClass() to set appropriate CSS classes for this.domNode.
3372 // To get a different CSS class for hover, send onmouseover and onmouseout events to this method.
3373 // To get a different CSS class while mouse button is depressed, send onmousedown to this method.
3375 var mouseNode = event.currentTarget;
3376 if(mouseNode && mouseNode.getAttribute){
3377 this.stateModifier = mouseNode.getAttribute("stateModifier") || "";
3384 this._hovering = true;
3385 this._active = this._mouseDown;
3390 this._hovering = false;
3391 this._active = false;
3395 this._active = true;
3396 this._mouseDown = true;
3397 // set a global event to handle mouseup, so it fires properly
3398 // even if the cursor leaves the button
3399 var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){
3400 this._active = false;
3401 this._mouseDown = false;
3402 this._setStateClass();
3403 this.disconnect(mouseUpConnector);
3405 if(this.isFocusable()){ this.focus(); }
3408 this._setStateClass();
3412 isFocusable: function(){
3413 return !this.disabled && !this.readOnly && this.focusNode && (dojo.style(this.domNode, "display") != "none");
3417 setTimeout(dojo.hitch(this, dijit.focus, this.focusNode), 0); // cannot call focus() from an event handler directly
3420 _setStateClass: function(){
3422 // Update the visual state of the widget by setting the css classes on this.domNode
3423 // (or this.stateNode if defined) by combining this.baseClass with
3424 // various suffixes that represent the current widget state(s).
3426 // In the case where a widget has multiple
3427 // states, it sets the class based on all possible
3428 // combinations. For example, an invalid form widget that is being hovered
3429 // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover".
3431 // For complex widgets with multiple regions, there can be various hover/active states,
3432 // such as "Hover" or "CloseButtonHover" (for tab buttons).
3433 // This is controlled by a stateModifier="CloseButton" attribute on the close button node.
3435 // The widget may have one or more of the following states, determined
3436 // by this.state, this.checked, this.valid, and this.selected:
3437 // Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid
3438 // Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true
3439 // Selected - ex: currently selected tab will have this.selected==true
3441 // In addition, it may have one or more of the following states,
3442 // based on this.disabled and flags set in _onMouse (this._active, this._hovering, this._focused):
3443 // Disabled - if the widget is disabled
3444 // Active - if the mouse (or space/enter key?) is being pressed down
3445 // Focused - if the widget has focus
3446 // Hover - if the mouse is over the widget
3448 // Get original (non state related, non baseClass related) class specified in template
3449 if(!("staticClass" in this)){
3450 this.staticClass = (this.stateNode||this.domNode).className;
3453 // Compute new set of classes
3454 var classes = [ this.baseClass ];
3456 function multiply(modifier){
3457 classes=classes.concat(dojo.map(classes, function(c){ return c+modifier; }), "dijit"+modifier);
3461 multiply("Checked");
3464 multiply(this.state);
3467 multiply("Selected");
3471 multiply("Disabled");
3472 }else if(this.readOnly){
3473 multiply("ReadOnly");
3474 }else if(this._active){
3475 multiply(this.stateModifier+"Active");
3478 multiply("Focused");
3481 multiply(this.stateModifier+"Hover");
3485 (this.stateNode || this.domNode).className = this.staticClass + " " + classes.join(" ");
3488 onChange: function(newValue){
3489 // summary: callback when value is changed
3492 _onChangeMonitor: 'value',
3493 _onChangeActive: false,
3495 _handleOnChange: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
3496 // summary: set the value of the widget.
3497 this._lastValue = newValue;
3498 if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){
3499 this._resetValue = this._lastValueReported = newValue;
3501 if((this.intermediateChanges || priorityChange || priorityChange === undefined) &&
3502 ((newValue && newValue.toString)?newValue.toString():newValue) !== ((this._lastValueReported && this._lastValueReported.toString)?this._lastValueReported.toString():this._lastValueReported)){
3503 this._lastValueReported = newValue;
3504 if(this._onChangeActive){ this.onChange(newValue); }
3509 this._hasBeenBlurred = false;
3510 if(this.setValue && !this._getValueDeprecated){
3511 this.setValue(this._resetValue, true);
3512 }else if(this._onChangeMonitor){
3513 this.setAttribute(this._onChangeMonitor, (this._resetValue !== undefined && this._resetValue !== null)? this._resetValue : '');
3518 this.inherited(arguments);
3519 this._onChangeActive = true;
3520 this._setStateClass();
3523 destroy: function(){
3524 if(this._layoutHackHandle){
3525 clearTimeout(this._layoutHackHandle);
3527 this.inherited(arguments);
3530 setValue: function(/*String*/ value){
3531 dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use setAttribute('value',"+value+") instead.", "", "2.0");
3532 this.setAttribute('value', value);
3535 _getValueDeprecated: true, // Form uses this, remove when getValue is removed
3536 getValue: function(){
3537 dojo.deprecated("dijit.form._FormWidget:getValue() is deprecated. Use widget.value instead.", "", "2.0");
3541 _layoutHack: function(){
3542 // summary: work around table sizing bugs on FF2 by forcing redraw
3544 var node=this.domNode;
3545 var old = node.style.opacity;
3546 node.style.opacity = "0.999";
3547 this._layoutHackHandle = setTimeout(dojo.hitch(this, function(){
3548 this._layoutHackHandle = null;
3549 node.style.opacity = old;
3555 dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget,
3559 _FormValueWidget's correspond to native HTML elements such as <input> or <select> that have user changeable values.
3560 Each _ValueWidget represents a single input value, and has a (possibly hidden) <input> element,
3561 to which it serializes its input value, so that form submission (either normal submission or via FormBind?)
3565 attributeMap: dojo.mixin(dojo.clone(dijit.form._FormWidget.prototype.attributeMap),
3568 postCreate: function(){
3569 this.setValue(this.value, null);
3572 setValue: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){
3573 // summary: set the value of the widget.
3574 this.value = newValue;
3575 this._handleOnChange(newValue, priorityChange);
3578 _getValueDeprecated: false, // remove when _FormWidget:getValue is removed
3579 getValue: function(){
3580 // summary: get the value of the widget.
3581 return this._lastValue;
3585 // summary: restore the value to the last value passed to onChange
3586 this.setValue(this._lastValueReported, false);
3589 _valueChanged: function(){
3590 var v = this.getValue();
3591 var lv = this._lastValueReported;
3592 // Equality comparison of objects such as dates are done by reference so
3593 // two distinct objects are != even if they have the same data. So use
3594 // toStrings in case the values are objects.
3595 return ((v !== null && (v !== undefined) && v.toString)?v.toString():'') !== ((lv !== null && (lv !== undefined) && lv.toString)?lv.toString():'');
3598 _onKeyPress: function(e){
3599 if(e.keyCode == dojo.keys.ESCAPE && !e.shiftKey && !e.ctrlKey && !e.altKey){
3600 if(this._valueChanged()){
3612 if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3613 dojo._hasResource["dijit.dijit"] = true;
3614 dojo.provide("dijit.dijit");
3618 // summary: A roll-up for common dijit methods
3620 // A rollup file for the build system including the core and common
3624 // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script>
3629 // All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require)
3632 // And some other stuff that we tend to pull in all the time anyway