From e44a7e37b6c7b5961adaffc62b9042b8d442938e Mon Sep 17 00:00:00 2001 From: mensonge Date: Thu, 13 Nov 2008 09:49:11 +0000 Subject: New feature: basic Ajax suggestion for tags and implementation of Dojo toolkit git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f --- includes/js/dijit/_base/bidi.js | 13 ++ includes/js/dijit/_base/focus.js | 342 +++++++++++++++++++++++++++++++++++ includes/js/dijit/_base/manager.js | 194 ++++++++++++++++++++ includes/js/dijit/_base/place.js | 213 ++++++++++++++++++++++ includes/js/dijit/_base/popup.js | 269 +++++++++++++++++++++++++++ includes/js/dijit/_base/scroll.js | 29 +++ includes/js/dijit/_base/sniff.js | 45 +++++ includes/js/dijit/_base/typematic.js | 139 ++++++++++++++ includes/js/dijit/_base/wai.js | 143 +++++++++++++++ includes/js/dijit/_base/window.js | 47 +++++ 10 files changed, 1434 insertions(+) create mode 100644 includes/js/dijit/_base/bidi.js create mode 100644 includes/js/dijit/_base/focus.js create mode 100644 includes/js/dijit/_base/manager.js create mode 100644 includes/js/dijit/_base/place.js create mode 100644 includes/js/dijit/_base/popup.js create mode 100644 includes/js/dijit/_base/scroll.js create mode 100644 includes/js/dijit/_base/sniff.js create mode 100644 includes/js/dijit/_base/typematic.js create mode 100644 includes/js/dijit/_base/wai.js create mode 100644 includes/js/dijit/_base/window.js (limited to 'includes/js/dijit/_base') diff --git a/includes/js/dijit/_base/bidi.js b/includes/js/dijit/_base/bidi.js new file mode 100644 index 0000000..7cd3f46 --- /dev/null +++ b/includes/js/dijit/_base/bidi.js @@ -0,0 +1,13 @@ +if(!dojo._hasResource["dijit._base.bidi"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.bidi"] = true; +dojo.provide("dijit._base.bidi"); + +// summary: applies a class to the top of the document for right-to-left stylesheet rules + +dojo.addOnLoad(function(){ + if(!dojo._isBodyLtr()){ + dojo.addClass(dojo.body(), "dijitRtl"); + } +}); + +} diff --git a/includes/js/dijit/_base/focus.js b/includes/js/dijit/_base/focus.js new file mode 100644 index 0000000..46230b5 --- /dev/null +++ b/includes/js/dijit/_base/focus.js @@ -0,0 +1,342 @@ +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 +}; + +} diff --git a/includes/js/dijit/_base/place.js b/includes/js/dijit/_base/place.js new file mode 100644 index 0000000..3165c11 --- /dev/null +++ b/includes/js/dijit/_base/place.js @@ -0,0 +1,213 @@ +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); +} + +} diff --git a/includes/js/dijit/_base/popup.js b/includes/js/dijit/_base/popup.js new file mode 100644 index 0000000..6cb4dfc --- /dev/null +++ b/includes/js/dijit/_base/popup.js @@ -0,0 +1,269 @@ +if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.popup"] = true; +dojo.provide("dijit._base.popup"); + +dojo.require("dijit._base.focus"); +dojo.require("dijit._base.place"); +dojo.require("dijit._base.window"); + +dijit.popup = new function(){ + // summary: + // This class is used to show/hide widgets as popups. + // + + var stack = [], + beginZIndex=1000, + idGen = 1; + + this.prepare = function(/*DomNode*/ node){ + // summary: + // Prepares a node to be used as a popup + // + // description: + // Attaches node to dojo.doc.body, and + // positions it off screen, but not display:none, so that + // the widget doesn't appear in the page flow and/or cause a blank + // area at the bottom of the viewport (making scrollbar longer), but + // initialization of contained widgets works correctly + + dojo.body().appendChild(node); + var s = node.style; + if(s.display == "none"){ + s.display=""; + } + s.visibility = "hidden"; // not needed for hiding, but used as flag that node is off-screen + s.position = "absolute"; + s.top = "-9999px"; + }; + + this.open = function(/*Object*/ args){ + // summary: + // Popup the widget at the specified position + // + // args: Object + // popup: Widget + // widget to display, + // parent: Widget + // the button etc. that is displaying this popup + // around: DomNode + // DOM node (typically a button); place popup relative to this node + // orient: Object + // structure specifying possible positions of popup relative to "around" node + // onCancel: Function + // callback when user has canceled the popup by + // 1. hitting ESC or + // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog); + // ie: whenever popupWidget.onCancel() is called, args.onCancel is called + // onClose: Function + // callback whenever this popup is closed + // onExecute: Function + // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only) + // + // examples: + // 1. opening at the mouse position + // dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY}); + // 2. opening the widget as a dropdown + // dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...} }); + // + // Note that whatever widget called dijit.popup.open() should also listen to it's own _onBlur callback + // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed. + + var widget = args.popup, + orient = args.orient || {'BL':'TL', 'TL':'BL'}, + around = args.around, + id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+idGen++); + + // make wrapper div to hold widget and possibly hold iframe behind it. + // we can't attach the iframe as a child of the widget.domNode because + // widget.domNode might be a ,
    , 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="