define([ "require", "dojo/_base/array", // array.forEach "dojo/_base/declare", // declare "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 "dojo/window", // winUtils.get "./popup", "./DropDownMenu", "dojo/ready" ], function(require, array, declare, 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{ 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 && document.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 && document.frames[iframe_el.name] && document.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){ evt.stopPropagation(); evt.preventDefault(); if((new Date()).getTime() < this._lastKeyDown + 500){ // Ignore contextmenu/click events that were already processed in keydown handler below. // But still call preventDefault() (above) so system context menu doesn't appear. return; } // Schedule context menu to be opened. // Note that this won't work will if the click was generated by the keyboard, while // focused on a