]>
git.wh0rd.org - tt-rss.git/blob - lib/dijit/Menu.js.uncompressed.js
3 "dojo/_base/array", // array.forEach
4 "dojo/_base/declare", // declare
5 "dojo/_base/event", // event.stop
6 "dojo/dom", // dom.byId dom.isDescendant
7 "dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
8 "dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
9 "dojo/dom-style", // domStyle.getComputedStyle
11 "dojo/keys", // keys.F10
12 "dojo/_base/lang", // lang.hitch
14 "dojo/_base/sniff", // has("ie"), has("quirks")
15 "dojo/_base/window", // win.body win.doc.documentElement win.doc.frames win.withGlobal
16 "dojo/window", // winUtils.get
20 ], function(require
, array
, declare
, event
, dom
, domAttr
, domGeometry
, domStyle
, kernel
, keys
, lang
, on
,
21 has
, win
, winUtils
, pm
, DropDownMenu
, ready
){
24 var DropDownMenu = dijit.DropDownMenu;
30 // Includes dijit.Menu widget and base class dijit._MenuBase
32 // Back compat w/1.6, remove for 2.0
35 var requires
= ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
36 require(requires
); // use indirection so modules not rolled into a build
40 return declare("dijit.Menu", DropDownMenu
, {
42 // A context menu you can assign to multiple elements
44 constructor: function(){
48 // targetNodeIds: [const] String[]
49 // Array of dom node ids of nodes to attach to.
50 // Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
53 // contextMenuForWindow: [const] Boolean
54 // If true, right clicking anywhere on the window will cause this context menu to open.
55 // If false, must specify targetNodeIds.
56 contextMenuForWindow
: false,
58 // leftClickToOpen: [const] Boolean
59 // If true, menu will open on left click instead of right click, similar to a file menu.
60 leftClickToOpen
: false,
63 // When this menu closes, re-focus the element which had focus before it was opened.
66 postCreate: function(){
67 if(this.contextMenuForWindow
){
68 this.bindDomNode(win
.body());
70 // TODO: should have _setTargetNodeIds() method to handle initialization and a possible
71 // later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
72 // gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
73 array
.forEach(this.targetNodeIds
, this.bindDomNode
, this);
75 this.inherited(arguments
);
79 _iframeContentWindow: function(/* HTMLIFrameElement */iframe_el
){
81 // Returns the window reference of the passed iframe
84 return winUtils
.get(this._iframeContentDocument(iframe_el
)) ||
85 // Moz. TODO: is this available when defaultView isn't?
86 this._iframeContentDocument(iframe_el
)['__parent__'] ||
87 (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
]) || null; // Window
90 _iframeContentDocument: function(/* HTMLIFrameElement */iframe_el
){
92 // Returns a reference to the document object inside iframe_el
95 return iframe_el
.contentDocument
// W3
96 || (iframe_el
.contentWindow
&& iframe_el
.contentWindow
.document
) // IE
97 || (iframe_el
.name
&& win
.doc
.frames
[iframe_el
.name
] && win
.doc
.frames
[iframe_el
.name
].document
)
98 || null; // HTMLDocument
101 bindDomNode: function(/*String|DomNode*/ node
){
103 // Attach menu to given node
104 node
= dom
.byId(node
);
106 var cn
; // Connect node
108 // Support context menus on iframes. Rather than binding to the iframe itself we need
109 // to bind to the <body> node inside the iframe.
110 if(node
.tagName
.toLowerCase() == "iframe"){
112 window
= this._iframeContentWindow(iframe
);
113 cn
= win
.withGlobal(window
, win
.body
);
116 // To capture these events at the top level, attach to <html>, not <body>.
117 // Otherwise right-click context menu just doesn't work.
118 cn
= (node
== win
.body() ? win
.doc
.documentElement
: node
);
122 // "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
128 // Save info about binding in _bindings[], and make node itself record index(+1) into
129 // _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
130 // start with a number, which fails on FF/safari.
131 domAttr
.set(node
, "_dijitMenu" + this.id
, this._bindings
.push(binding
));
133 // Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
134 // loading yet, in which case we need to wait for the onload event first, and then connect
135 // On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
136 // we need to monitor keyboard events in addition to the oncontextmenu event.
137 var doConnects
= lang
.hitch(this, function(cn
){
139 // TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
140 // rather than shift-F10?
141 on(cn
, this.leftClickToOpen
? "click" : "contextmenu", lang
.hitch(this, function(evt
){
142 // Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
144 this._scheduleOpen(evt
.target
, iframe
, {x
: evt
.pageX
, y
: evt
.pageY
});
146 on(cn
, "keydown", lang
.hitch(this, function(evt
){
147 if(evt
.shiftKey
&& evt
.keyCode
== keys
.F10
){
149 this._scheduleOpen(evt
.target
, iframe
); // no coords - open near target node
154 binding
.connects
= cn
? doConnects(cn
) : [];
157 // Setup handler to [re]bind to the iframe when the contents are initially loaded,
158 // and every time the contents change.
159 // Need to do this b/c we are actually binding to the iframe's <body> node.
160 // Note: can't use connect.connect(), see #9609.
162 binding
.onloadHandler
= lang
.hitch(this, function(){
163 // want to remove old connections, but IE throws exceptions when trying to
164 // access the <body> node because it's already gone, or at least in a state of limbo
166 var window
= this._iframeContentWindow(iframe
);
167 cn
= win
.withGlobal(window
, win
.body
);
168 binding
.connects
= doConnects(cn
);
170 if(iframe
.addEventListener
){
171 iframe
.addEventListener("load", binding
.onloadHandler
, false);
173 iframe
.attachEvent("onload", binding
.onloadHandler
);
178 unBindDomNode: function(/*String|DomNode*/ nodeName
){
180 // Detach menu from given node
184 node
= dom
.byId(nodeName
);
186 // On IE the dom.byId() call will get an exception if the attach point was
187 // the <body> node of an <iframe> that has since been reloaded (and thus the
188 // <body> node is in a limbo state of destruction.
192 // node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
193 var attrName
= "_dijitMenu" + this.id
;
194 if(node
&& domAttr
.has(node
, attrName
)){
195 var bid
= domAttr
.get(node
, attrName
)-1, b
= this._bindings
[bid
], h
;
196 while(h
= b
.connects
.pop()){
200 // Remove listener for iframe onload events
201 var iframe
= b
.iframe
;
203 if(iframe
.removeEventListener
){
204 iframe
.removeEventListener("load", b
.onloadHandler
, false);
206 iframe
.detachEvent("onload", b
.onloadHandler
);
210 domAttr
.remove(node
, attrName
);
211 delete this._bindings
[bid
];
215 _scheduleOpen: function(/*DomNode?*/ target
, /*DomNode?*/ iframe
, /*Object?*/ coords
){
217 // Set timer to display myself. Using a timer rather than displaying immediately solves
220 // 1. IE: without the delay, focus work in "open" causes the system
221 // context menu to appear in spite of stopEvent.
223 // 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
224 // even after a event.stop(e). (Shift-F10 on windows doesn't generate the
225 // oncontextmenu event.)
227 if(!this._openTimer
){
228 this._openTimer
= setTimeout(lang
.hitch(this, function(){
229 delete this._openTimer
;
239 _openMyself: function(args
){
241 // Internal function for opening myself when the user does a right-click or something similar.
243 // This is an Object containing:
245 // The node that is being clicked
247 // If an <iframe> is being clicked, iframe points to that iframe
249 // Put menu at specified x/y position in viewport, or if iframe is
250 // specified, then relative to iframe.
252 // _openMyself() formerly took the event object, and since various code references
253 // evt.target (after connecting to _openMyself()), using an Object for parameters
254 // (so that old code still works).
256 var target
= args
.target
,
257 iframe
= args
.iframe
,
258 coords
= args
.coords
;
260 // Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
261 // then near the node the menu is assigned to.
264 // Specified coordinates are on <body> node of an <iframe>, convert to match main document
265 var ifc
= domGeometry
.position(iframe
, true),
266 window
= this._iframeContentWindow(iframe
),
267 scroll
= win
.withGlobal(window
, "_docScroll", dojo
);
269 var cs
= domStyle
.getComputedStyle(iframe
),
270 tp
= domStyle
.toPixelValue
,
271 left
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingLeft
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderLeftWidth
) : 0),
272 top
= (has("ie") && has("quirks") ? 0 : tp(iframe
, cs
.paddingTop
)) + (has("ie") && has("quirks") ? tp(iframe
, cs
.borderTopWidth
) : 0);
274 coords
.x
+= ifc
.x
+ left
- scroll
.x
;
275 coords
.y
+= ifc
.y
+ top
- scroll
.y
;
278 coords
= domGeometry
.position(target
, true);
284 var prevFocusNode
= this._focusManager
.get("prevNode");
285 var curFocusNode
= this._focusManager
.get("curNode");
286 var savedFocusNode
= !curFocusNode
|| (dom
.isDescendant(curFocusNode
, this.domNode
)) ? prevFocusNode
: curFocusNode
;
288 function closeAndRestoreFocus(){
289 // user has clicked on a menu or popup
290 if(self
.refocus
&& savedFocusNode
){
291 savedFocusNode
.focus();
299 onExecute
: closeAndRestoreFocus
,
300 onCancel
: closeAndRestoreFocus
,
301 orient
: this.isLeftToRight() ? 'L' : 'R'
305 this._onBlur = function(){
306 this.inherited('_onBlur', arguments
);
307 // Usually the parent closes the child widget but if this is a context
308 // menu then there is no parent
310 // don't try to restore focus; user has clicked another part of the screen
311 // and set focus there
315 uninitialize: function(){
316 array
.forEach(this._bindings
, function(b
){ if(b
){ this.unBindDomNode(b
.node
); } }, this);
317 this.inherited(arguments
);