/* Copyright (c) 2004-2008, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing */ /* This is a compiled version of Dojo, built for deployment and not for development. To get an editable version, please visit: http://dojotoolkit.org for documentation and information on getting the source. */ if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit._base.focus"] = true; dojo.provide("dijit._base.focus"); // summary: // These functions are used to query or set the focus and selection. // // Also, they trace when widgets become actived/deactivated, // so that the widget can fire _onFocus/_onBlur events. // "Active" here means something similar to "focused", but // "focus" isn't quite the right word because we keep track of // a whole stack of "active" widgets. Example: Combobutton --> Menu --> // MenuItem. The onBlur event for Combobutton doesn't fire due to focusing // on the Menu or a MenuItem, since they are considered part of the // Combobutton widget. It only happens when focus is shifted // somewhere completely different. dojo.mixin(dijit, { // _curFocus: DomNode // Currently focused item on screen _curFocus: null, // _prevFocus: DomNode // Previously focused item on screen _prevFocus: null, isCollapsed: function(){ // summary: tests whether the current selection is empty var _window = dojo.global; var _document = dojo.doc; if(_document.selection){ // IE return !_document.selection.createRange().text; // Boolean }else{ var selection = _window.getSelection(); if(dojo.isString(selection)){ // Safari return !selection; // Boolean }else{ // Mozilla/W3 return selection.isCollapsed || !selection.toString(); // Boolean } } }, getBookmark: function(){ // summary: Retrieves a bookmark that can be used with moveToBookmark to return to the same range var bookmark, selection = dojo.doc.selection; if(selection){ // IE var range = selection.createRange(); if(selection.type.toUpperCase()=='CONTROL'){ if(range.length){ bookmark=[]; var i=0,len=range.length; while(i to follow the parentNode chain, // but we need to set focus to iframe.contentWindow if(node){ var focusNode = (node.tagName.toLowerCase()=="iframe") ? node.contentWindow : node; if(focusNode && focusNode.focus){ try{ // Gecko throws sometimes if setting focus is impossible, // node not displayed or something like that focusNode.focus(); }catch(e){/*quiet*/} } dijit._onFocusNode(node); } // set the selection // do not need to restore if current selection is not empty // (use keyboard to select a menu item) if(bookmark && dojo.withGlobal(openedForWindow||dojo.global, dijit.isCollapsed)){ if(openedForWindow){ openedForWindow.focus(); } try{ dojo.withGlobal(openedForWindow||dojo.global, dijit.moveToBookmark, null, [bookmark]); }catch(e){ /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ } } }, // _activeStack: Array // List of currently active widgets (focused widget and it's ancestors) _activeStack: [], registerWin: function(/*Window?*/targetWindow){ // summary: // Registers listeners on the specified window (either the main // window or an iframe) to detect when the user has clicked somewhere. // Anyone that creates an iframe should call this function. if(!targetWindow){ targetWindow = window; } dojo.connect(targetWindow.document, "onmousedown", function(evt){ dijit._justMouseDowned = true; setTimeout(function(){ dijit._justMouseDowned = false; }, 0); dijit._onTouchNode(evt.target||evt.srcElement); }); //dojo.connect(targetWindow, "onscroll", ???); // Listen for blur and focus events on targetWindow's body var body = targetWindow.document.body || targetWindow.document.getElementsByTagName("body")[0]; if(body){ if(dojo.isIE){ body.attachEvent('onactivate', function(evt){ if(evt.srcElement.tagName.toLowerCase() != "body"){ dijit._onFocusNode(evt.srcElement); } }); body.attachEvent('ondeactivate', function(evt){ dijit._onBlurNode(evt.srcElement); }); }else{ body.addEventListener('focus', function(evt){ dijit._onFocusNode(evt.target); }, true); body.addEventListener('blur', function(evt){ dijit._onBlurNode(evt.target); }, true); } } body = null; // prevent memory leak (apparent circular reference via closure) }, _onBlurNode: function(/*DomNode*/ node){ // summary: // Called when focus leaves a node. // Usually ignored, _unless_ it *isn't* follwed by touching another node, // which indicates that we tabbed off the last field on the page, // in which case every widget is marked inactive dijit._prevFocus = dijit._curFocus; dijit._curFocus = null; if(dijit._justMouseDowned){ // the mouse down caused a new widget to be marked as active; this blur event // is coming late, so ignore it. return; } // if the blur event isn't followed by a focus event then mark all widgets as inactive. if(dijit._clearActiveWidgetsTimer){ clearTimeout(dijit._clearActiveWidgetsTimer); } dijit._clearActiveWidgetsTimer = setTimeout(function(){ delete dijit._clearActiveWidgetsTimer; dijit._setStack([]); dijit._prevFocus = null; }, 100); }, _onTouchNode: function(/*DomNode*/ node){ // summary: // Callback when node is focused or mouse-downed // ignore the recent blurNode event if(dijit._clearActiveWidgetsTimer){ clearTimeout(dijit._clearActiveWidgetsTimer); delete dijit._clearActiveWidgetsTimer; } // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem) var newStack=[]; try{ while(node){ if(node.dijitPopupParent){ node=dijit.byId(node.dijitPopupParent).domNode; }else if(node.tagName && node.tagName.toLowerCase()=="body"){ // is this the root of the document or just the root of an iframe? if(node===dojo.body()){ // node is the root of the main document break; } // otherwise, find the iframe this node refers to (can't access it via parentNode, // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit node=dijit.getDocumentWindow(node.ownerDocument).frameElement; }else{ var id = node.getAttribute && node.getAttribute("widgetId"); if(id){ newStack.unshift(id); } node=node.parentNode; } } }catch(e){ /* squelch */ } dijit._setStack(newStack); }, _onFocusNode: function(/*DomNode*/ node){ // summary // Callback when node is focused if(node && node.tagName && node.tagName.toLowerCase() == "body"){ return; } dijit._onTouchNode(node); if(node==dijit._curFocus){ return; } if(dijit._curFocus){ dijit._prevFocus = dijit._curFocus; } dijit._curFocus = node; dojo.publish("focusNode", [node]); }, _setStack: function(newStack){ // summary // The stack of active widgets has changed. Send out appropriate events and record new stack var oldStack = dijit._activeStack; dijit._activeStack = newStack; // compare old stack to new stack to see how many elements they have in common for(var nCommon=0; nCommon=nCommon; i--){ var widget = dijit.byId(oldStack[i]); if(widget){ widget._focused = false; widget._hasBeenBlurred = true; if(widget._onBlur){ widget._onBlur(); } if (widget._setStateClass){ widget._setStateClass(); } dojo.publish("widgetBlur", [widget]); } } // for all element that have come into focus, send focus event for(i=nCommon; i= 0) { return true; // boolean } var name = elem.nodeName.toLowerCase(); if(((name == "a" && dojo.hasAttr(elem, "href")) || dijit._tabElements[name]) && (!hasTabindex || tabindex >= 0)){ return true; // boolean } return false; // boolean }; dijit._getTabNavigable = function(/*DOMNode*/root){ // summary: // Finds the following descendants of the specified root node: // * the first tab-navigable element in document order // without a tabindex or with tabindex="0" // * the last tab-navigable element in document order // without a tabindex or with tabindex="0" // * the first element in document order with the lowest // positive tabindex value // * the last element in document order with the highest // positive tabindex value var first, last, lowest, lowestTabindex, highest, highestTabindex; var walkTree = function(/*DOMNode*/parent){ dojo.query("> *", parent).forEach(function(child){ var isShown = dijit._isElementShown(child); if(isShown && dijit.isTabNavigable(child)){ var tabindex = dojo.attr(child, "tabindex"); if(!dojo.hasAttr(child, "tabindex") || tabindex == 0){ if(!first){ first = child; } last = child; }else if(tabindex > 0){ if(!lowest || tabindex < lowestTabindex){ lowestTabindex = tabindex; lowest = child; } if(!highest || tabindex >= highestTabindex){ highestTabindex = tabindex; highest = child; } } } if(isShown){ walkTree(child) } }); }; if(dijit._isElementShown(root)){ walkTree(root) } return { first: first, last: last, lowest: lowest, highest: highest }; } dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/root){ // summary: // Finds the descendant of the specified root node // that is first in the tabbing order var elems = dijit._getTabNavigable(dojo.byId(root)); return elems.lowest ? elems.lowest : elems.first; // Element }; dijit.getLastInTabbingOrder = function(/*String|DOMNode*/root){ // summary: // Finds the descendant of the specified root node // that is last in the tabbing order var elems = dijit._getTabNavigable(dojo.byId(root)); return elems.last ? elems.last : elems.highest; // Element }; } if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit._base.place"] = true; dojo.provide("dijit._base.place"); // ported from dojo.html.util dijit.getViewport = function(){ // summary // Returns the dimensions and scroll position of the viewable area of a browser window var _window = dojo.global; var _document = dojo.doc; // get viewport size var w = 0, h = 0; var de = _document.documentElement; var dew = de.clientWidth, deh = de.clientHeight; if(dojo.isMozilla){ // mozilla // _window.innerHeight includes the height taken by the scroll bar // clientHeight is ideal but has DTD issues: // #4539: FF reverses the roles of body.clientHeight/Width and documentElement.clientHeight/Width based on the DTD! // check DTD to see whether body or documentElement returns the viewport dimensions using this algorithm: var minw, minh, maxw, maxh; var dbw = _document.body.clientWidth; if(dbw > dew){ minw = dew; maxw = dbw; }else{ maxw = dew; minw = dbw; } var dbh = _document.body.clientHeight; if(dbh > deh){ minh = deh; maxh = dbh; }else{ maxh = deh; minh = dbh; } w = (maxw > _window.innerWidth) ? minw : maxw; h = (maxh > _window.innerHeight) ? minh : maxh; }else if(!dojo.isOpera && _window.innerWidth){ //in opera9, dojo.body().clientWidth should be used, instead //of window.innerWidth/document.documentElement.clientWidth //so we have to check whether it is opera w = _window.innerWidth; h = _window.innerHeight; }else if(dojo.isIE && de && deh){ w = dew; h = deh; }else if(dojo.body().clientWidth){ // IE5, Opera w = dojo.body().clientWidth; h = dojo.body().clientHeight; } // get scroll position var scroll = dojo._docScroll(); return { w: w, h: h, l: scroll.x, t: scroll.y }; // object }; dijit.placeOnScreen = function( /* DomNode */ node, /* Object */ pos, /* Object */ corners, /* boolean? */ tryOnly){ // summary: // Keeps 'node' in the visible area of the screen while trying to // place closest to pos.x, pos.y. The input coordinates are // expected to be the desired document position. // // Set which corner(s) you want to bind to, such as // // placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"]) // // The desired x/y will be treated as the topleft(TL)/topright(TR) or // BottomLeft(BL)/BottomRight(BR) corner of the node. Each corner is tested // and if a perfect match is found, it will be used. Otherwise, it goes through // all of the specified corners, and choose the most appropriate one. // // NOTE: node is assumed to be absolutely or relatively positioned. var choices = dojo.map(corners, function(corner){ return { corner: corner, pos: pos }; }); return dijit._place(node, choices); } dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){ // summary: // Given a list of spots to put node, put it at the first spot where it fits, // of if it doesn't fit anywhere then the place with the least overflow // choices: Array // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} } // Above example says to put the top-left corner of the node at (10,20) // layoutNode: Function(node, aroundNodeCorner, nodeCorner) // for things like tooltip, they are displayed differently (and have different dimensions) // based on their orientation relative to the parent. This adjusts the popup based on orientation. // get {x: 10, y: 10, w: 100, h:100} type obj representing position of // viewport over document var view = dijit.getViewport(); // This won't work if the node is inside a
, // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong // and also it might get cutoff) if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){ dojo.body().appendChild(node); } var best = null; dojo.some(choices, function(choice){ var corner = choice.corner; var pos = choice.pos; // configure node to be displayed in given position relative to button // (need to do this in order to get an accurate size for the node, because // a tooltips size changes based on position, due to triangle) if(layoutNode){ layoutNode(node, choice.aroundCorner, corner); } // get node's size var style = node.style; var oldDisplay = style.display; var oldVis = style.visibility; style.visibility = "hidden"; style.display = ""; var mb = dojo.marginBox(node); style.display = oldDisplay; style.visibility = oldVis; // coordinates and size of node with specified corner placed at pos, // and clipped by viewport var startX = (corner.charAt(1) == 'L' ? pos.x : Math.max(view.l, pos.x - mb.w)), startY = (corner.charAt(0) == 'T' ? pos.y : Math.max(view.t, pos.y - mb.h)), endX = (corner.charAt(1) == 'L' ? Math.min(view.l + view.w, startX + mb.w) : pos.x), endY = (corner.charAt(0) == 'T' ? Math.min(view.t + view.h, startY + mb.h) : pos.y), width = endX - startX, height = endY - startY, overflow = (mb.w - width) + (mb.h - height); if(best == null || overflow < best.overflow){ best = { corner: corner, aroundCorner: choice.aroundCorner, x: startX, y: startY, w: width, h: height, overflow: overflow }; } return !overflow; }); node.style.left = best.x + "px"; node.style.top = best.y + "px"; if(best.overflow && layoutNode){ layoutNode(node, best.aroundCorner, best.corner); } return best; } dijit.placeOnScreenAroundElement = function( /* DomNode */ node, /* DomNode */ aroundNode, /* Object */ aroundCorners, /* Function */ layoutNode){ // summary // Like placeOnScreen, except it accepts aroundNode instead of x,y // and attempts to place node around it. Uses margin box dimensions. // // aroundCorners // specify Which corner of aroundNode should be // used to place the node => which corner(s) of node to use (see the // corners parameter in dijit.placeOnScreen) // e.g. {'TL': 'BL', 'BL': 'TL'} // // layoutNode: Function(node, aroundNodeCorner, nodeCorner) // for things like tooltip, they are displayed differently (and have different dimensions) // based on their orientation relative to the parent. This adjusts the popup based on orientation. // get coordinates of aroundNode aroundNode = dojo.byId(aroundNode); var oldDisplay = aroundNode.style.display; aroundNode.style.display=""; // #3172: use the slightly tighter border box instead of marginBox var aroundNodeW = aroundNode.offsetWidth; //mb.w; var aroundNodeH = aroundNode.offsetHeight; //mb.h; var aroundNodePos = dojo.coords(aroundNode, true); aroundNode.style.display=oldDisplay; // Generate list of possible positions for node var choices = []; for(var nodeCorner in aroundCorners){ choices.push( { aroundCorner: nodeCorner, corner: aroundCorners[nodeCorner], pos: { x: aroundNodePos.x + (nodeCorner.charAt(1) == 'L' ? 0 : aroundNodeW), y: aroundNodePos.y + (nodeCorner.charAt(0) == 'T' ? 0 : aroundNodeH) } }); } return dijit._place(node, choices, layoutNode); } } if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit._base.window"] = true; dojo.provide("dijit._base.window"); dijit.getDocumentWindow = function(doc){ // summary // Get window object associated with document doc // With Safari, there is not way to retrieve the window from the document, so we must fix it. if(dojo.isSafari && !doc._parentWindow){ /* This is a Safari specific function that fix the reference to the parent window from the document object. TODO: #5711: should the use of document below reference dojo.doc instead in case they're not the same? */ var fix=function(win){ win.document._parentWindow=win; for(var i=0; i,
    , etc. var wrapper = dojo.doc.createElement("div"); dijit.setWaiRole(wrapper, "presentation"); wrapper.id = id; wrapper.className="dijitPopup"; wrapper.style.zIndex = beginZIndex + stack.length; wrapper.style.visibility = "hidden"; if(args.parent){ wrapper.dijitPopupParent=args.parent.id; } dojo.body().appendChild(wrapper); var s = widget.domNode.style; s.display = ""; s.visibility = ""; s.position = ""; wrapper.appendChild(widget.domNode); var iframe = new dijit.BackgroundIframe(wrapper); // position the wrapper node var best = around ? dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) : dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR']); wrapper.style.visibility = "visible"; // TODO: use effects to fade in wrapper var handlers = []; // Compute the closest ancestor popup that's *not* a child of another popup. // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button. var getTopPopup = function(){ for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){ /* do nothing, just trying to get right value for pi */ } return stack[pi]; } // provide default escape and tab key handling // (this will work for any widget, not just menu) handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){ if(evt.keyCode == dojo.keys.ESCAPE && args.onCancel){ dojo.stopEvent(evt); args.onCancel(); }else if(evt.keyCode == dojo.keys.TAB){ dojo.stopEvent(evt); var topPopup = getTopPopup(); if(topPopup && topPopup.onCancel){ topPopup.onCancel(); } } })); // watch for cancel/execute events on the popup and notify the caller // (for a menu, "execute" means clicking an item) if(widget.onCancel){ handlers.push(dojo.connect(widget, "onCancel", null, args.onCancel)); } handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", null, function(){ var topPopup = getTopPopup(); if(topPopup && topPopup.onExecute){ topPopup.onExecute(); } })); stack.push({ wrapper: wrapper, iframe: iframe, widget: widget, parent: args.parent, onExecute: args.onExecute, onCancel: args.onCancel, onClose: args.onClose, handlers: handlers }); if(widget.onOpen){ widget.onOpen(best); } return best; }; this.close = function(/*Widget*/ popup){ // summary: // Close specified popup and any popups that it parented while(dojo.some(stack, function(elem){return elem.widget == popup;})){ var top = stack.pop(), wrapper = top.wrapper, iframe = top.iframe, widget = top.widget, onClose = top.onClose; if(widget.onClose){ widget.onClose(); } dojo.forEach(top.handlers, dojo.disconnect); // #2685: check if the widget still has a domNode so ContentPane can change its URL without getting an error if(!widget||!widget.domNode){ return; } this.prepare(widget.domNode); iframe.destroy(); dojo._destroyElement(wrapper); if(onClose){ onClose(); } } }; }(); dijit._frames = new function(){ // summary: cache of iframes var queue = []; this.pop = function(){ var iframe; if(queue.length){ iframe = queue.pop(); iframe.style.display=""; }else{ if(dojo.isIE){ var html="