define("dijit/Menu", [ "require", "dojo/_base/array", // array.forEach "dojo/_base/declare", // declare "dojo/_base/event", // event.stop "dojo/dom", // dom.byId dom.isDescendant "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position "dojo/dom-style", // domStyle.getComputedStyle "dojo/keys", // keys.F10 "dojo/_base/lang", // lang.hitch "dojo/on", "dojo/sniff", // has("ie"), has("quirks") "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames "dojo/window", // winUtils.get "./popup", "./DropDownMenu", "dojo/ready" ], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, keys, lang, on, has, win, winUtils, pm, DropDownMenu, ready){ // module: // dijit/Menu // Back compat w/1.6, remove for 2.0 if(has("dijit-legacy-requires")){ ready(0, function(){ var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"]; require(requires); // use indirection so modules not rolled into a build }); } return declare("dijit.Menu", DropDownMenu, { // summary: // A context menu you can assign to multiple elements constructor: function(/*===== params, srcNodeRef =====*/){ // summary: // Create the widget. // params: Object|null // Hash of initialization parameters for widget, including scalar values (like title, duration etc.) // and functions, typically callbacks like onClick. // The hash can contain any of the widget's properties, excluding read-only properties. // srcNodeRef: DOMNode|String? // If a srcNodeRef (DOM node) is specified: // // - use srcNodeRef.innerHTML as my contents // - replace srcNodeRef with my generated DOM tree this._bindings = []; }, // targetNodeIds: [const] String[] // Array of dom node ids of nodes to attach to. // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes. targetNodeIds: [], // selector: String? // CSS expression to apply this Menu to descendants of targetNodeIds, rather than to // the nodes specified by targetNodeIds themselves. Useful for applying a Menu to // a range of rows in a table, tree, etc. // // The application must require() an appropriate level of dojo/query to handle the selector. selector: "", // TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect. // So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc. /*===== // currentTarget: [readonly] DOMNode // For context menus, set to the current node that the Menu is being displayed for. // Useful so that the menu actions can be tailored according to the node currentTarget: null, =====*/ // contextMenuForWindow: [const] Boolean // If true, right clicking anywhere on the window will cause this context menu to open. // If false, must specify targetNodeIds. contextMenuForWindow: false, // leftClickToOpen: [const] Boolean // If true, menu will open on left click instead of right click, similar to a file menu. leftClickToOpen: false, // refocus: Boolean // When this menu closes, re-focus the element which had focus before it was opened. refocus: true, postCreate: function(){ if(this.contextMenuForWindow){ this.bindDomNode(this.ownerDocumentBody); }else{ // TODO: should have _setTargetNodeIds() method to handle initialization and a possible // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[] // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610) array.forEach(this.targetNodeIds, this.bindDomNode, this); } this.inherited(arguments); }, // thanks burstlib! _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){ // summary: // Returns the window reference of the passed iframe // tags: // private return winUtils.get(this._iframeContentDocument(iframe_el)) || // Moz. TODO: is this available when defaultView isn't? this._iframeContentDocument(iframe_el)['__parent__'] || (iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window }, _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){ // summary: // Returns a reference to the document object inside iframe_el // tags: // protected return iframe_el.contentDocument // W3 || (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE || (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document) || null; // HTMLDocument }, bindDomNode: function(/*String|DomNode*/ node){ // summary: // Attach menu to given node node = dom.byId(node, this.ownerDocument); var cn; // Connect node // Support context menus on iframes. Rather than binding to the iframe itself we need // to bind to the
node inside the iframe. if(node.tagName.toLowerCase() == "iframe"){ var iframe = node, window = this._iframeContentWindow(iframe); cn = win.body(window.document); }else{ // To capture these events at the top level, attach to , not . // Otherwise right-click context menu just doesn't work. cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node); } // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode()) var binding = { node: node, iframe: iframe }; // Save info about binding in _bindings[], and make node itself record index(+1) into // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may // start with a number, which fails on FF/safari. domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding)); // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished // loading yet, in which case we need to wait for the onload event first, and then connect // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so // we need to monitor keyboard events in addition to the oncontextmenu event. var doConnects = lang.hitch(this, function(cn){ var selector = this.selector, delegatedEvent = selector ? function(eventType){ return on.selector(selector, eventType); } : function(eventType){ return eventType; }, self = this; return [ // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu, // rather than shift-F10? on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){ // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler event.stop(evt); self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY}); }), on(cn, delegatedEvent("keydown"), function(evt){ if(evt.shiftKey && evt.keyCode == keys.F10){ event.stop(evt); self._scheduleOpen(this, iframe); // no coords - open near target node } }) ]; }); binding.connects = cn ? doConnects(cn) : []; if(iframe){ // Setup handler to [re]bind to the iframe when the contents are initially loaded, // and every time the contents change. // Need to do this b/c we are actually binding to the iframe's node. // Note: can't use connect.connect(), see #9609. binding.onloadHandler = lang.hitch(this, function(){ // want to remove old connections, but IE throws exceptions when trying to // access the node because it's already gone, or at least in a state of limbo var window = this._iframeContentWindow(iframe); cn = win.body(window.document) binding.connects = doConnects(cn); }); if(iframe.addEventListener){ iframe.addEventListener("load", binding.onloadHandler, false); }else{ iframe.attachEvent("onload", binding.onloadHandler); } } }, unBindDomNode: function(/*String|DomNode*/ nodeName){ // summary: // Detach menu from given node var node; try{ node = dom.byId(nodeName, this.ownerDocument); }catch(e){ // On IE the dom.byId() call will get an exception if the attach point was // the node of an