define([ "dojo/_base/array", // array.forEach array.some "dojo/aspect", "dojo/_base/declare", // declare "dojo/dom", // dom.isDescendant "dojo/dom-attr", // domAttr.set "dojo/dom-construct", // domConstruct.create domConstruct.destroy "dojo/dom-geometry", // domGeometry.isBodyLtr "dojo/dom-style", // domStyle.set "dojo/has", // has("config-bgIframe") "dojo/keys", "dojo/_base/lang", // lang.hitch "dojo/on", "./place", "./BackgroundIframe", "./Viewport", "./main" // dijit (defining dijit.popup to match API doc) ], function(array, aspect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, has, keys, lang, on, place, BackgroundIframe, Viewport, dijit){ // module: // dijit/popup /*===== var __OpenArgs = { // 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. (Specify this *or* "x" and "y" parameters.) // x: Integer // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.) // y: Integer // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.) // orient: Object|String // When the around parameter is specified, orient should be a list of positions to try, ex: // | [ "below", "above" ] // For backwards compatibility it can also be an (ordered) hash of tuples of the form // (around-node-corner, popup-node-corner), ex: // | { "BL": "TL", "TL": "BL" } // where BL means "bottom left" and "TL" means "top left", etc. // // dijit/popup.open() tries to position the popup according to each specified position, in order, // until the popup appears fully within the viewport. // // The default value is ["below", "above"] // // When an (x,y) position is specified rather than an around node, orient is either // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse, // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner, // and the top-right corner. // 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); // i.e. 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) // padding: place.__Position // adding a buffer around the opening position. This is only useful when around is not set. // maxHeight: Integer // The max height for the popup. Any popup taller than this will have scrollbars. // Set to Infinity for no max height. Default is to limit height to available space in viewport, // above or below the aroundNode or specified x/y position. }; =====*/ function destroyWrapper(){ // summary: // Function to destroy wrapper when popup widget is destroyed. // Left in this scope to avoid memory leak on IE8 on refresh page, see #15206. if(this._popupWrapper){ domConstruct.destroy(this._popupWrapper); delete this._popupWrapper; } } var PopupManager = declare(null, { // summary: // Used to show drop downs (ex: the select list of a ComboBox) // or popups (ex: right-click context menus). // _stack: dijit/_WidgetBase[] // Stack of currently popped up widgets. // (someone opened _stack[0], and then it opened _stack[1], etc.) _stack: [], // _beginZIndex: Number // Z-index of the first popup. (If first popup opens other // popups they get a higher z-index.) _beginZIndex: 1000, _idGen: 1, _repositionAll: function(){ // summary: // If screen has been scrolled, reposition all the popups in the stack. // Then set timer to check again later. if(this._firstAroundNode){ // guard for when clearTimeout() on IE doesn't work var oldPos = this._firstAroundPosition, newPos = domGeometry.position(this._firstAroundNode, true), dx = newPos.x - oldPos.x, dy = newPos.y - oldPos.y; if(dx || dy){ this._firstAroundPosition = newPos; for(var i = 0; i < this._stack.length; i++){ var style = this._stack[i].wrapper.style; style.top = (parseFloat(style.top) + dy) + "px"; if(style.right == "auto"){ style.left = (parseFloat(style.left) + dx) + "px"; }else{ style.right = (parseFloat(style.right) - dx) + "px"; } } } this._aroundMoveListener = setTimeout(lang.hitch(this, "_repositionAll"), dx || dy ? 10 : 50); } }, _createWrapper: function(/*Widget*/ widget){ // summary: // Initialization for widgets that will be used as popups. // Puts widget inside a wrapper DIV (if not already in one), // and returns pointer to that wrapper DIV. var wrapper = widget._popupWrapper, node = widget.domNode; if(!wrapper){ // Create wrapper
for when this widget [in the future] will be used as a popup. // This is done early because of IE bugs where creating/moving DOM nodes causes focus // to go wonky, see tests/robot/Toolbar.html to reproduce wrapper = domConstruct.create("div", { "class": "dijitPopup", style: { display: "none"}, role: "region", "aria-label": widget["aria-label"] || widget.label || widget.name || widget.id }, widget.ownerDocumentBody); wrapper.appendChild(node); var s = node.style; s.display = ""; s.visibility = ""; s.position = ""; s.top = "0px"; widget._popupWrapper = wrapper; aspect.after(widget, "destroy", destroyWrapper, true); // Workaround iOS problem where clicking a Menu can focus an (or click a button) behind it. // Need to be careful though that you can still focus 's and click