1 |
efrain |
1 |
YUI.add('event-contextmenu', function (Y, NAME) {
|
|
|
2 |
|
|
|
3 |
/**
|
|
|
4 |
* Provides extended keyboard support for the "contextmenu" event such that:
|
|
|
5 |
* <ul>
|
|
|
6 |
* <li>The browser's default context menu is suppressed regardless of how the event is triggered.</li>
|
|
|
7 |
* <li>On Windows the "contextmenu" event is fired consistently regardless of whether the user
|
|
|
8 |
* pressed the Menu key or Shift + F10.</li>
|
|
|
9 |
* <li>When the "contextmenu" event is fired via the keyboard, the pageX, pageY, clientX and clientY
|
|
|
10 |
* properties reference the center of the event target. This makes it easy for "contextmenu" event listeners
|
|
|
11 |
* to position an overlay in response to the event by not having to worry about special handling of the x
|
|
|
12 |
* and y coordinates based on the device that fired the event.</li>
|
|
|
13 |
* <li>For Webkit and Gecko on the Mac it enables the use of the Shift + Control + Option + M keyboard
|
|
|
14 |
* shortcut to fire the "contextmenu" event, which (by default) is only available when VoiceOver
|
|
|
15 |
* (the screen reader on the Mac) is enabled.</li>
|
|
|
16 |
* <li>For Opera on the Mac it ensures the "contextmenu" event is fired when the user presses
|
|
|
17 |
* Shift + Command + M (Opera's context menu keyboard shortcut).</li>
|
|
|
18 |
* </ul>
|
|
|
19 |
* @module event-contextmenu
|
|
|
20 |
* @requires event
|
|
|
21 |
*/
|
|
|
22 |
|
|
|
23 |
var Event = Y.Event,
|
|
|
24 |
DOM = Y.DOM,
|
|
|
25 |
UA = Y.UA,
|
|
|
26 |
OS = Y.UA.os,
|
|
|
27 |
|
|
|
28 |
ie = UA.ie,
|
|
|
29 |
gecko = UA.gecko,
|
|
|
30 |
webkit = UA.webkit,
|
|
|
31 |
opera = UA.opera,
|
|
|
32 |
|
|
|
33 |
isWin = (OS === "windows"),
|
|
|
34 |
isMac = (OS === "macintosh"),
|
|
|
35 |
|
|
|
36 |
eventData = {},
|
|
|
37 |
|
|
|
38 |
conf = {
|
|
|
39 |
|
|
|
40 |
on: function (node, subscription, notifier, filter) {
|
|
|
41 |
|
|
|
42 |
var handles = [];
|
|
|
43 |
|
|
|
44 |
handles.push(Event._attach(["contextmenu", function (e) {
|
|
|
45 |
|
|
|
46 |
// Any developer listening for the "contextmenu" event is likely
|
|
|
47 |
// going to call preventDefault() to prevent the display of
|
|
|
48 |
// the browser's context menu. So, you know, save them a step.
|
|
|
49 |
e.preventDefault();
|
|
|
50 |
|
|
|
51 |
var id = Y.stamp(node),
|
|
|
52 |
data = eventData[id];
|
|
|
53 |
|
|
|
54 |
if (data) {
|
|
|
55 |
e.clientX = data.clientX;
|
|
|
56 |
e.clientY = data.clientY;
|
|
|
57 |
e.pageX = data.pageX;
|
|
|
58 |
e.pageY = data.pageY;
|
|
|
59 |
delete eventData[id];
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
notifier.fire(e);
|
|
|
63 |
|
|
|
64 |
}, node]));
|
|
|
65 |
|
|
|
66 |
|
|
|
67 |
handles.push(node[filter ? "delegate" : "on"]("keydown", function (e) {
|
|
|
68 |
|
|
|
69 |
var target = this.getDOMNode(),
|
|
|
70 |
shiftKey = e.shiftKey,
|
|
|
71 |
keyCode = e.keyCode,
|
|
|
72 |
shiftF10 = (shiftKey && keyCode == 121),
|
|
|
73 |
menuKey = (isWin && keyCode == 93),
|
|
|
74 |
ctrlKey = e.ctrlKey,
|
|
|
75 |
mKey = (keyCode === 77),
|
|
|
76 |
macWebkitAndGeckoShortcut = (isMac && (webkit || gecko) && ctrlKey && shiftKey && e.altKey && mKey),
|
|
|
77 |
|
|
|
78 |
// Note: The context menu keyboard shortcut for Opera on the Mac is Shift + Cmd (metaKey) + M,
|
|
|
79 |
// but e.metaKey is false for Opera, and Opera sets e.ctrlKey to true instead.
|
|
|
80 |
macOperaShortcut = (isMac && opera && ctrlKey && shiftKey && mKey),
|
|
|
81 |
|
|
|
82 |
clientX = 0,
|
|
|
83 |
clientY = 0,
|
|
|
84 |
scrollX,
|
|
|
85 |
scrollY,
|
|
|
86 |
pageX,
|
|
|
87 |
pageY,
|
|
|
88 |
xy,
|
|
|
89 |
x,
|
|
|
90 |
y;
|
|
|
91 |
|
|
|
92 |
|
|
|
93 |
if ((isWin && (shiftF10 || menuKey)) ||
|
|
|
94 |
(macWebkitAndGeckoShortcut || macOperaShortcut)) {
|
|
|
95 |
|
|
|
96 |
// Need to call preventDefault() here b/c:
|
|
|
97 |
// 1) To prevent IE's menubar from gaining focus when the
|
|
|
98 |
// user presses Shift + F10
|
|
|
99 |
// 2) In Firefox and Opera for Win, Shift + F10 will display a
|
|
|
100 |
// context menu, but won't fire the "contextmenu" event. So, need
|
|
|
101 |
// to call preventDefault() to prevent the display of the
|
|
|
102 |
// browser's context menu
|
|
|
103 |
// 3) For Opera on the Mac the context menu keyboard shortcut
|
|
|
104 |
// (Shift + Cmd + M) will display a context menu, but like Firefox
|
|
|
105 |
// and Opera on windows, Opera doesn't fire a "contextmenu" event,
|
|
|
106 |
// so preventDefault() is just used to supress Opera's
|
|
|
107 |
// default context menu.
|
|
|
108 |
if (((ie || (isWin && (gecko || opera))) && shiftF10) || macOperaShortcut) {
|
|
|
109 |
e.preventDefault();
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
xy = DOM.getXY(target);
|
|
|
113 |
x = xy[0];
|
|
|
114 |
y = xy[1];
|
|
|
115 |
scrollX = DOM.docScrollX();
|
|
|
116 |
scrollY = DOM.docScrollY();
|
|
|
117 |
|
|
|
118 |
// Protect against instances where xy and might not be returned,
|
|
|
119 |
// for example if the target is the document.
|
|
|
120 |
if (!Y.Lang.isUndefined(x)) {
|
|
|
121 |
clientX = (x + (target.offsetWidth/2)) - scrollX;
|
|
|
122 |
clientY = (y + (target.offsetHeight/2)) - scrollY;
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
pageX = clientX + scrollX;
|
|
|
126 |
pageY = clientY + scrollY;
|
|
|
127 |
|
|
|
128 |
// When the "contextmenu" event is fired from the keyboard
|
|
|
129 |
// clientX, clientY, pageX or pageY aren't set to useful
|
|
|
130 |
// values. So, we follow Safari's model here of setting
|
|
|
131 |
// the x & x coords to the center of the event target.
|
|
|
132 |
|
|
|
133 |
if (menuKey || (isWin && webkit && shiftF10)) {
|
|
|
134 |
eventData[Y.stamp(node)] = {
|
|
|
135 |
clientX: clientX,
|
|
|
136 |
clientY: clientY,
|
|
|
137 |
pageX: pageX,
|
|
|
138 |
pageY: pageY
|
|
|
139 |
};
|
|
|
140 |
}
|
|
|
141 |
|
|
|
142 |
// Don't need to call notifier.fire(e) when the Menu key
|
|
|
143 |
// is pressed as it fires the "contextmenu" event by default.
|
|
|
144 |
//
|
|
|
145 |
// In IE the call to preventDefault() for Shift + F10
|
|
|
146 |
// prevents the "contextmenu" event from firing, so we need
|
|
|
147 |
// to call notifier.fire(e)
|
|
|
148 |
//
|
|
|
149 |
// Need to also call notifier.fire(e) for Gecko and Opera since
|
|
|
150 |
// neither Shift + F10 or Shift + Cmd + M fire the "contextmenu" event.
|
|
|
151 |
//
|
|
|
152 |
// Lastly, also need to call notifier.fire(e) for all Mac browsers
|
|
|
153 |
// since neither Shift + Ctrl + Option + M (Webkit and Gecko) or
|
|
|
154 |
// Shift + Command + M (Opera) fire the "contextmenu" event.
|
|
|
155 |
|
|
|
156 |
if (((ie || (isWin && (gecko || opera))) && shiftF10) || isMac) {
|
|
|
157 |
|
|
|
158 |
e.clientX = clientX;
|
|
|
159 |
e.clientY = clientY;
|
|
|
160 |
e.pageX = pageX;
|
|
|
161 |
e.pageY = pageY;
|
|
|
162 |
|
|
|
163 |
notifier.fire(e);
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
}, filter));
|
|
|
169 |
|
|
|
170 |
subscription._handles = handles;
|
|
|
171 |
|
|
|
172 |
},
|
|
|
173 |
|
|
|
174 |
detach: function (node, subscription, notifier) {
|
|
|
175 |
|
|
|
176 |
Y.each(subscription._handles, function (handle) {
|
|
|
177 |
handle.detach();
|
|
|
178 |
});
|
|
|
179 |
|
|
|
180 |
}
|
|
|
181 |
|
|
|
182 |
};
|
|
|
183 |
|
|
|
184 |
|
|
|
185 |
conf.delegate = conf.on;
|
|
|
186 |
conf.detachDelegate = conf.detach;
|
|
|
187 |
|
|
|
188 |
|
|
|
189 |
Event.define("contextmenu", conf, true);
|
|
|
190 |
|
|
|
191 |
|
|
|
192 |
}, '3.18.1', {"requires": ["event-synthetic", "dom-screen"]});
|