Merge lp:~divmod-dev/divmod.org/athena-events-806545 into lp:divmod.org
- athena-events-806545
- Merge into trunk
Proposed by
Jonathan Jacobs
Status: | Needs review |
---|---|
Proposed branch: | lp:~divmod-dev/divmod.org/athena-events-806545 |
Merge into: | lp:divmod.org |
Diff against target: |
989 lines (+870/-13) 8 files modified
Nevow/nevow/athena.py (+1/-1) Nevow/nevow/js/Divmod/Runtime/__init__.js (+33/-0) Nevow/nevow/js/Divmod/Test/TestRuntime.js (+94/-0) Nevow/nevow/js/Nevow/Athena/__init__.js (+231/-11) Nevow/nevow/js/Nevow/Test/TestEvent.js (+504/-0) Nevow/nevow/js/Nevow/Test/TestWidget.js (+2/-0) Nevow/nevow/test/test_athena.py (+1/-1) Nevow/nevow/test/test_javascript.py (+4/-0) |
To merge this branch: | bzr merge lp:~divmod-dev/divmod.org/athena-events-806545 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Divmod-dev | Pending | ||
Review via email: mp+67455@code.launchpad.net |
Commit message
Description of the change
Wrap DOM events in a universal way and pass these to Athena event handlers.
To post a comment you must log in.
- 2680. By Jonathan Jacobs
-
Fix typo.
- 2681. By Jonathan Jacobs
-
Fix failing test related to event changes.
Unmerged revisions
- 2681. By Jonathan Jacobs
-
Fix failing test related to event changes.
- 2680. By Jonathan Jacobs
-
Fix typo.
- 2679. By Jonathan Jacobs
-
Fix failing tests.
- 2678. By Jonathan Jacobs
-
Wrap DOM events in a universal event wrapper.
- 2677. By Jonathan Jacobs
-
Implement getMouseButtons
FromEvent for Platform and InternetExplorer in Divmod.Runtime. - 2676. By Jonathan Jacobs
-
Pass the raw browser event object to Athena event handlers.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Nevow/nevow/athena.py' | |||
2 | --- Nevow/nevow/athena.py 2010-07-12 19:00:11 +0000 | |||
3 | +++ Nevow/nevow/athena.py 2011-07-10 13:56:31 +0000 | |||
4 | @@ -1511,7 +1511,7 @@ | |||
5 | 1511 | 1511 | ||
6 | 1512 | 1512 | ||
7 | 1513 | handler = stan.Proto('athena:handler') | 1513 | handler = stan.Proto('athena:handler') |
9 | 1514 | _handlerFormat = "return Nevow.Athena.Widget.handleEvent(this, %(event)s, %(handler)s);" | 1514 | _handlerFormat = "return Nevow.Athena.Widget.handleEvent(this, %(event)s, %(handler)s, event);" |
10 | 1515 | 1515 | ||
11 | 1516 | def _rewriteEventHandlerToAttribute(tag): | 1516 | def _rewriteEventHandlerToAttribute(tag): |
12 | 1517 | """ | 1517 | """ |
13 | 1518 | 1518 | ||
14 | === modified file 'Nevow/nevow/js/Divmod/Runtime/__init__.js' | |||
15 | --- Nevow/nevow/js/Divmod/Runtime/__init__.js 2008-12-31 18:44:01 +0000 | |||
16 | +++ Nevow/nevow/js/Divmod/Runtime/__init__.js 2011-07-10 13:56:31 +0000 | |||
17 | @@ -709,6 +709,25 @@ | |||
18 | 709 | */ | 709 | */ |
19 | 710 | function addBeforeUnloadHandler(self, aWindow, handler) { | 710 | function addBeforeUnloadHandler(self, aWindow, handler) { |
20 | 711 | Divmod.Base.addToCallStack(aWindow, 'onbeforeunload', handler); | 711 | Divmod.Base.addToCallStack(aWindow, 'onbeforeunload', handler); |
21 | 712 | }, | ||
22 | 713 | |||
23 | 714 | |||
24 | 715 | /** | ||
25 | 716 | * Determine which mouse buttons were pressed from a browser event object. | ||
26 | 717 | * | ||
27 | 718 | * The default implementation matches the W3C DOM Level 2 Events | ||
28 | 719 | * specifications. | ||
29 | 720 | * | ||
30 | 721 | * @return: A mapping of C{'left'}, C{'middle'}, C{'right'} to C{Boolean} | ||
31 | 722 | * values indicating the state of the named mouse buttons. | ||
32 | 723 | * | ||
33 | 724 | * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MouseEvent> | ||
34 | 725 | */ | ||
35 | 726 | function getMouseButtonsFromEvent(self, event) { | ||
36 | 727 | return { | ||
37 | 728 | 'left': event.button == 0, | ||
38 | 729 | 'middle': event.button == 1, | ||
39 | 730 | 'right': event.button == 2}; | ||
40 | 712 | }); | 731 | }); |
41 | 713 | 732 | ||
42 | 714 | 733 | ||
43 | @@ -1205,6 +1224,20 @@ | |||
44 | 1205 | */ | 1224 | */ |
45 | 1206 | function addBeforeUnloadHandler(self, aWindow, handler) { | 1225 | function addBeforeUnloadHandler(self, aWindow, handler) { |
46 | 1207 | Divmod.Base.addToCallStack(aWindow.document.body, 'onbeforeunload', handler); | 1226 | Divmod.Base.addToCallStack(aWindow.document.body, 'onbeforeunload', handler); |
47 | 1227 | }, | ||
48 | 1228 | |||
49 | 1229 | |||
50 | 1230 | /** | ||
51 | 1231 | * Internet Explorer specific handling of the C{event.button} property. | ||
52 | 1232 | * | ||
53 | 1233 | * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent} | ||
54 | 1234 | * @see: <http://msdn.microsoft.com/en-us/library/ms533544%28v=vs.85%29.aspx> | ||
55 | 1235 | */ | ||
56 | 1236 | function getMouseButtonsFromEvent(self, event) { | ||
57 | 1237 | return { | ||
58 | 1238 | 'left': (event.button & 1) != 0, | ||
59 | 1239 | 'middle': (event.button & 4) != 0, | ||
60 | 1240 | 'right': (event.button & 2) != 0}; | ||
61 | 1208 | }); | 1241 | }); |
62 | 1209 | 1242 | ||
63 | 1210 | 1243 | ||
64 | 1211 | 1244 | ||
65 | === modified file 'Nevow/nevow/js/Divmod/Test/TestRuntime.js' | |||
66 | --- Nevow/nevow/js/Divmod/Test/TestRuntime.js 2008-12-31 18:44:01 +0000 | |||
67 | +++ Nevow/nevow/js/Divmod/Test/TestRuntime.js 2011-07-10 13:56:31 +0000 | |||
68 | @@ -713,6 +713,100 @@ | |||
69 | 713 | }); | 713 | }); |
70 | 714 | 714 | ||
71 | 715 | 715 | ||
72 | 716 | |||
73 | 717 | /** | ||
74 | 718 | * Tests for L{Divmod.Runtime.Platform}. | ||
75 | 719 | */ | ||
76 | 720 | Divmod.UnitTest.TestCase.subclass(Divmod.Test.TestRuntime, | ||
77 | 721 | 'PlatformTests').methods( | ||
78 | 722 | function setUp(self) { | ||
79 | 723 | self.runtime = Divmod.Runtime.Platform('name'); | ||
80 | 724 | }, | ||
81 | 725 | |||
82 | 726 | |||
83 | 727 | /** | ||
84 | 728 | * Assert that C{evt.button} when processed with | ||
85 | 729 | * C{getMouseButtonsFromEvent} yields the same values for its C{'left'}, | ||
86 | 730 | * C{'middle'} and C{'right'} attributes as the parameters with these same | ||
87 | 731 | * names. | ||
88 | 732 | */ | ||
89 | 733 | function assertMouseButtons(self, evt, left, middle, right) { | ||
90 | 734 | var buttons = self.runtime.getMouseButtonsFromEvent(evt); | ||
91 | 735 | self.assertIdentical(buttons.left, left) | ||
92 | 736 | self.assertIdentical(buttons.middle, middle) | ||
93 | 737 | self.assertIdentical(buttons.right, right) | ||
94 | 738 | }, | ||
95 | 739 | |||
96 | 740 | |||
97 | 741 | /** | ||
98 | 742 | * Most platforms follow the W3C definitions for C{event.button} values. | ||
99 | 743 | * | ||
100 | 744 | * These values cannot be combined to indicate multiple buttons being | ||
101 | 745 | * pushed. | ||
102 | 746 | */ | ||
103 | 747 | function test_getMouseButtonsFromEvent(self) { | ||
104 | 748 | var evt = {}; | ||
105 | 749 | // Unknown value. | ||
106 | 750 | evt.button = 99; | ||
107 | 751 | self.assertMouseButtons(evt, false, false, false); | ||
108 | 752 | // Left mouse button. | ||
109 | 753 | evt.button = 0; | ||
110 | 754 | self.assertMouseButtons(evt, true, false, false); | ||
111 | 755 | // Middle mouse button. | ||
112 | 756 | evt.button = 1; | ||
113 | 757 | self.assertMouseButtons(evt, false, true, false); | ||
114 | 758 | // Right mouse button. | ||
115 | 759 | evt.button = 2; | ||
116 | 760 | self.assertMouseButtons(evt, false, false, true); | ||
117 | 761 | }); | ||
118 | 762 | |||
119 | 763 | |||
120 | 764 | |||
121 | 765 | /** | ||
122 | 766 | * Tests for L{Divmod.Runtime.InternetExplorer}. | ||
123 | 767 | */ | ||
124 | 768 | Divmod.Test.TestRuntime.PlatformTests.subclass( | ||
125 | 769 | Divmod.Test.TestRuntime, | ||
126 | 770 | 'InternetExplorerPlatformTests').methods( | ||
127 | 771 | function setUp(self) { | ||
128 | 772 | self.runtime = Divmod.Runtime.InternetExplorer(); | ||
129 | 773 | }, | ||
130 | 774 | |||
131 | 775 | |||
132 | 776 | /** | ||
133 | 777 | * Internet Explorer uses different values for L{event.button} and can | ||
134 | 778 | * combine these values to indicate multiple buttons being pressed. | ||
135 | 779 | */ | ||
136 | 780 | function test_getMouseButtonsFromEvent(self) { | ||
137 | 781 | var evt = {}; | ||
138 | 782 | // No buttons. | ||
139 | 783 | evt.button = 0; | ||
140 | 784 | self.assertMouseButtons(evt, false, false, false); | ||
141 | 785 | // Left mouse button. | ||
142 | 786 | evt.button = 1; | ||
143 | 787 | self.assertMouseButtons(evt, true, false, false); | ||
144 | 788 | // Right mouse button. | ||
145 | 789 | evt.button = 2; | ||
146 | 790 | self.assertMouseButtons(evt, false, false, true); | ||
147 | 791 | // Middle mouse button. | ||
148 | 792 | evt.button = 4; | ||
149 | 793 | self.assertMouseButtons(evt, false, true, false); | ||
150 | 794 | // Left and right mouse buttons. | ||
151 | 795 | evt.button = 3; | ||
152 | 796 | self.assertMouseButtons(evt, true, false, true); | ||
153 | 797 | // Left and middle mouse buttons. | ||
154 | 798 | evt.button = 5; | ||
155 | 799 | self.assertMouseButtons(evt, true, true, false); | ||
156 | 800 | // Right and middle mouse buttons. | ||
157 | 801 | evt.button = 6; | ||
158 | 802 | self.assertMouseButtons(evt, false, true, true); | ||
159 | 803 | // Left, middle and right mouse buttons. | ||
160 | 804 | evt.button = 7; | ||
161 | 805 | self.assertMouseButtons(evt, true, true, true); | ||
162 | 806 | }); | ||
163 | 807 | |||
164 | 808 | |||
165 | 809 | |||
166 | 716 | Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests = Divmod.UnitTest.TestCase.subclass( | 810 | Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests = Divmod.UnitTest.TestCase.subclass( |
167 | 717 | 'Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests'); | 811 | 'Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests'); |
168 | 718 | /** | 812 | /** |
169 | 719 | 813 | ||
170 | === modified file 'Nevow/nevow/js/Nevow/Athena/__init__.js' | |||
171 | --- Nevow/nevow/js/Nevow/Athena/__init__.js 2009-01-21 22:58:03 +0000 | |||
172 | +++ Nevow/nevow/js/Nevow/Athena/__init__.js 2011-07-10 13:56:31 +0000 | |||
173 | @@ -1176,7 +1176,8 @@ | |||
174 | 1176 | */ | 1176 | */ |
175 | 1177 | Nevow.Athena.Widget._makeEventHandler = function (domEventName, methodName) { | 1177 | Nevow.Athena.Widget._makeEventHandler = function (domEventName, methodName) { |
176 | 1178 | return function () { | 1178 | return function () { |
178 | 1179 | return Nevow.Athena.Widget.handleEvent(this, domEventName, methodName); | 1179 | return Nevow.Athena.Widget.handleEvent( |
179 | 1180 | this, domEventName, methodName, event); | ||
180 | 1180 | }; | 1181 | }; |
181 | 1181 | }; | 1182 | }; |
182 | 1182 | 1183 | ||
183 | @@ -1205,15 +1206,233 @@ | |||
184 | 1205 | return Nevow.Athena.page.dispatchEvent(widget, eventName, handlerName, callable); | 1206 | return Nevow.Athena.page.dispatchEvent(widget, eventName, handlerName, callable); |
185 | 1206 | }; | 1207 | }; |
186 | 1207 | 1208 | ||
196 | 1208 | /** | 1209 | |
197 | 1209 | * Given a node and a method name in an event handling context, dispatch the | 1210 | |
198 | 1210 | * event to the named method on the widget which owns the given node. This | 1211 | /** |
199 | 1211 | * also sets up error handling and does return value translation as | 1212 | * Lowest-common denominator DOM event wrapper. |
200 | 1212 | * appropriate for an event handler. It also pauses the outgoing message | 1213 | * |
201 | 1213 | * queue to allow multiple messages from the event handler to be batched up | 1214 | * DOM event objects should wrapped by calling |
202 | 1214 | * into a single request. | 1215 | * L{Nevow.Athena.Event.fromDOMEvent}. |
203 | 1215 | */ | 1216 | * |
204 | 1216 | Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName, handlerName) { | 1217 | * @type knownEventTypes: C{Array} of C{String} |
205 | 1218 | * @cvar knownEventTypes: Array of event types that this event wrapper can wrap. | ||
206 | 1219 | * | ||
207 | 1220 | * @ivar event: Original DOM event object. | ||
208 | 1221 | * | ||
209 | 1222 | * @ivar type: C{String} | ||
210 | 1223 | * @ivar type: Event type. | ||
211 | 1224 | * | ||
212 | 1225 | * @ivar target: Element to which the DOM event was originally dispatched. | ||
213 | 1226 | * | ||
214 | 1227 | * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html> | ||
215 | 1228 | * @see: <http://www.quirksmode.org/js/introevents.html> | ||
216 | 1229 | */ | ||
217 | 1230 | Divmod.Class.subclass(Nevow.Athena, 'Event').methods( | ||
218 | 1231 | function __init__(self, event) { | ||
219 | 1232 | self.event = event; | ||
220 | 1233 | self.type = self.event.type; | ||
221 | 1234 | self.target = self.event.target; | ||
222 | 1235 | if (self.target === undefined) { | ||
223 | 1236 | self.target = self.event.srcElement; | ||
224 | 1237 | } | ||
225 | 1238 | }, | ||
226 | 1239 | |||
227 | 1240 | |||
228 | 1241 | /** | ||
229 | 1242 | * Cancels the event if it is cancelable, without stopping further | ||
230 | 1243 | * propagation of the event. | ||
231 | 1244 | */ | ||
232 | 1245 | function preventDefault(self) { | ||
233 | 1246 | if (self.event.preventDefault) { | ||
234 | 1247 | self.event.preventDefault(); | ||
235 | 1248 | } else { | ||
236 | 1249 | self.event.returnValue = false; | ||
237 | 1250 | } | ||
238 | 1251 | }, | ||
239 | 1252 | |||
240 | 1253 | |||
241 | 1254 | /** | ||
242 | 1255 | * Stops the propagation of events further along in the DOM. | ||
243 | 1256 | */ | ||
244 | 1257 | function stopPropagation(self) { | ||
245 | 1258 | if (self.event.stopPropagation) { | ||
246 | 1259 | self.event.stopPropagation(); | ||
247 | 1260 | } else { | ||
248 | 1261 | self.event.cancelBubble = true; | ||
249 | 1262 | } | ||
250 | 1263 | }); | ||
251 | 1264 | |||
252 | 1265 | |||
253 | 1266 | |||
254 | 1267 | /** | ||
255 | 1268 | * Specific subclass of L{Nevow.Athena.Event} relating to key events. | ||
256 | 1269 | * | ||
257 | 1270 | * @ivar altKey: Was the I{alt} key pressed when the event fired? | ||
258 | 1271 | * | ||
259 | 1272 | * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired? | ||
260 | 1273 | * | ||
261 | 1274 | * @ivar shiftKey: Was the I{shift} key pressed when the event fired? | ||
262 | 1275 | * | ||
263 | 1276 | * @ivar metaKey: Was the I{meta} key pressed when the event fire? | ||
264 | 1277 | */ | ||
265 | 1278 | Nevow.Athena.Event.subclass(Nevow.Athena, 'KeyEvent').methods( | ||
266 | 1279 | function __init__(self, event) { | ||
267 | 1280 | Nevow.Athena.KeyEvent.upcall(self, '__init__', event); | ||
268 | 1281 | self.altKey = !!self.event.altKey; | ||
269 | 1282 | self.ctrlKey = !!self.event.ctrlKey; | ||
270 | 1283 | self.shiftKey = !!self.event.shiftKey; | ||
271 | 1284 | self.metaKey = !!self.event.metaKey; // Not in IE < 9. | ||
272 | 1285 | }, | ||
273 | 1286 | |||
274 | 1287 | |||
275 | 1288 | /** | ||
276 | 1289 | * Get the Unicode value of key press. | ||
277 | 1290 | * | ||
278 | 1291 | * For the I{keydown} or I{keyup} events this is the virtual key code of the | ||
279 | 1292 | * physical button pushed. For I{keypress} event this is the character code | ||
280 | 1293 | * for an alphanumeric key. | ||
281 | 1294 | * | ||
282 | 1295 | * @see: <https://developer.mozilla.org/en/DOM/event.keyCode> | ||
283 | 1296 | * @see: <http://msdn.microsoft.com/en-us/library/ms533927%28v=VS.85%29.aspx> | ||
284 | 1297 | */ | ||
285 | 1298 | function getKeyCode(self) { | ||
286 | 1299 | return self.event.keyCode || self.event.which; | ||
287 | 1300 | }, | ||
288 | 1301 | |||
289 | 1302 | |||
290 | 1303 | /** | ||
291 | 1304 | * Set the Unicode value of key press. | ||
292 | 1305 | */ | ||
293 | 1306 | function setKeyCode(self, value) { | ||
294 | 1307 | self.event.keyCode = value; | ||
295 | 1308 | }); | ||
296 | 1309 | |||
297 | 1310 | Nevow.Athena.KeyEvent.knownEventTypes = ['keydown', 'keypress', 'keyup']; | ||
298 | 1311 | |||
299 | 1312 | |||
300 | 1313 | |||
301 | 1314 | /** | ||
302 | 1315 | * Specific subclass of L{Nevow.Athena.Event} relating to mouse events. | ||
303 | 1316 | * | ||
304 | 1317 | * @ivar altKey: Was the I{alt} key pressed when the event fired? | ||
305 | 1318 | * | ||
306 | 1319 | * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired? | ||
307 | 1320 | * | ||
308 | 1321 | * @ivar shiftKey: Was the I{shift} key pressed when the event fired? | ||
309 | 1322 | * | ||
310 | 1323 | * @ivar metaKey: Was the I{meta} key pressed when the event fire? | ||
311 | 1324 | */ | ||
312 | 1325 | Nevow.Athena.Event.subclass(Nevow.Athena, 'MouseEvent').methods( | ||
313 | 1326 | function __init__(self, event) { | ||
314 | 1327 | Nevow.Athena.MouseEvent.upcall(self, '__init__', event); | ||
315 | 1328 | self.altKey = self.event.altKey; | ||
316 | 1329 | self.ctrlKey = self.event.ctrlKey; | ||
317 | 1330 | self.shiftKey = self.event.shiftKey; | ||
318 | 1331 | self.metaKey = self.event.metaKey; // Not in IE < 9. | ||
319 | 1332 | }, | ||
320 | 1333 | |||
321 | 1334 | |||
322 | 1335 | /** | ||
323 | 1336 | * Determine which mouse buttons were pressed in this event. | ||
324 | 1337 | * | ||
325 | 1338 | * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent} | ||
326 | 1339 | */ | ||
327 | 1340 | function getMouseButtons(self) { | ||
328 | 1341 | return Divmod.Runtime.theRuntime.getMouseButtonsFromEvent(self.event); | ||
329 | 1342 | }, | ||
330 | 1343 | |||
331 | 1344 | |||
332 | 1345 | /** | ||
333 | 1346 | * Get the coordinates of the event relative to the whole document. | ||
334 | 1347 | * | ||
335 | 1348 | * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical | ||
336 | 1349 | * coordinates respectively. | ||
337 | 1350 | */ | ||
338 | 1351 | function getPagePosition(self) { | ||
339 | 1352 | return Divmod.Runtime.theRuntime.getEventCoords(self.event); | ||
340 | 1353 | }, | ||
341 | 1354 | |||
342 | 1355 | |||
343 | 1356 | /** | ||
344 | 1357 | * Get the coordinates within the browser's client area at which the event | ||
345 | 1358 | * occurred (as opposed to the coordinates within the page). | ||
346 | 1359 | * | ||
347 | 1360 | * For example, clicking in the top-left corner of the client area will | ||
348 | 1361 | * always result in a mouse event with a clientX value of 0, regardless of | ||
349 | 1362 | * whether the page is scrolled horizontally. | ||
350 | 1363 | * | ||
351 | 1364 | * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical | ||
352 | 1365 | * coordinates respectively. | ||
353 | 1366 | */ | ||
354 | 1367 | function getClientPosition(self) { | ||
355 | 1368 | return { | ||
356 | 1369 | 'x': self.event.clientX, | ||
357 | 1370 | 'y': self.event.clientY}; | ||
358 | 1371 | }, | ||
359 | 1372 | |||
360 | 1373 | |||
361 | 1374 | /** | ||
362 | 1375 | * Get the coordinates of the event within the screen as a whole. | ||
363 | 1376 | * | ||
364 | 1377 | * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical | ||
365 | 1378 | * coordinates respectively. | ||
366 | 1379 | */ | ||
367 | 1380 | function getScreenPosition(self) { | ||
368 | 1381 | return { | ||
369 | 1382 | 'x': self.event.screenX, | ||
370 | 1383 | 'y': self.event.screenY}; | ||
371 | 1384 | }); | ||
372 | 1385 | |||
373 | 1386 | Nevow.Athena.MouseEvent.knownEventTypes = [ | ||
374 | 1387 | 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', | ||
375 | 1388 | 'mousewheel']; | ||
376 | 1389 | |||
377 | 1390 | |||
378 | 1391 | |||
379 | 1392 | /** | ||
380 | 1393 | * Mapping of event types to known event handlers. | ||
381 | 1394 | */ | ||
382 | 1395 | Nevow.Athena.Event._eventHandlerMapping = (function() { | ||
383 | 1396 | var handlers = [ | ||
384 | 1397 | Nevow.Athena.KeyEvent, | ||
385 | 1398 | Nevow.Athena.MouseEvent]; | ||
386 | 1399 | var mapping = {} | ||
387 | 1400 | for (var i = 0; i < handlers.length; ++i) { | ||
388 | 1401 | var handler = handlers[i]; | ||
389 | 1402 | var knownEventTypes = handler.knownEventTypes; | ||
390 | 1403 | for (var j = 0; j < knownEventTypes.length; ++j) { | ||
391 | 1404 | mapping[knownEventTypes[j]] = handler; | ||
392 | 1405 | } | ||
393 | 1406 | } | ||
394 | 1407 | return mapping; | ||
395 | 1408 | })(); | ||
396 | 1409 | |||
397 | 1410 | |||
398 | 1411 | |||
399 | 1412 | /** | ||
400 | 1413 | * Wrap a DOM event object with an appropriate L{Nevow.Athena.Event} subclass | ||
401 | 1414 | * or L{Nevow.Athena.Event} if there is no specific handler for the event type. | ||
402 | 1415 | */ | ||
403 | 1416 | Nevow.Athena.Event.fromDOMEvent = function fromDOMEvent(event) { | ||
404 | 1417 | var handler = Nevow.Athena.Event._eventHandlerMapping[event.type]; | ||
405 | 1418 | if (handler === undefined) { | ||
406 | 1419 | handler = Nevow.Athena.Event; | ||
407 | 1420 | } | ||
408 | 1421 | return handler(event); | ||
409 | 1422 | }; | ||
410 | 1423 | |||
411 | 1424 | |||
412 | 1425 | |||
413 | 1426 | /** | ||
414 | 1427 | * Given a node, a method name in an event handling context and an event | ||
415 | 1428 | * object, dispatch the event to the named method on the widget which owns the | ||
416 | 1429 | * given node. This also sets up error handling and does return value | ||
417 | 1430 | * translation as appropriate for an event handler. It also pauses the | ||
418 | 1431 | * outgoing message queue to allow multiple messages from the event handler to | ||
419 | 1432 | * be batched up into a single request. | ||
420 | 1433 | */ | ||
421 | 1434 | Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName, | ||
422 | 1435 | handlerName, event) { | ||
423 | 1217 | var widget = Nevow.Athena.Widget.get(node); | 1436 | var widget = Nevow.Athena.Widget.get(node); |
424 | 1218 | var method = widget[handlerName]; | 1437 | var method = widget[handlerName]; |
425 | 1219 | var result = false; | 1438 | var result = false; |
426 | @@ -1223,7 +1442,8 @@ | |||
427 | 1223 | result = Nevow.Athena.Widget.dispatchEvent( | 1442 | result = Nevow.Athena.Widget.dispatchEvent( |
428 | 1224 | widget, eventName, handlerName, | 1443 | widget, eventName, handlerName, |
429 | 1225 | function() { | 1444 | function() { |
431 | 1226 | return method.call(widget, node); | 1445 | return method.call( |
432 | 1446 | widget, node, Nevow.Athena.Event.fromDOMEvent(event)); | ||
433 | 1227 | }); | 1447 | }); |
434 | 1228 | } | 1448 | } |
435 | 1229 | return result; | 1449 | return result; |
436 | 1230 | 1450 | ||
437 | === added file 'Nevow/nevow/js/Nevow/Test/TestEvent.js' | |||
438 | --- Nevow/nevow/js/Nevow/Test/TestEvent.js 1970-01-01 00:00:00 +0000 | |||
439 | +++ Nevow/nevow/js/Nevow/Test/TestEvent.js 2011-07-10 13:56:31 +0000 | |||
440 | @@ -0,0 +1,504 @@ | |||
441 | 1 | // import Divmod | ||
442 | 2 | // import Divmod.UnitTest | ||
443 | 3 | // import Nevow.Athena | ||
444 | 4 | |||
445 | 5 | |||
446 | 6 | |||
447 | 7 | /** | ||
448 | 8 | * A mock DOM event that behaves according to the W3C guide that most browsers | ||
449 | 9 | * follow. | ||
450 | 10 | * | ||
451 | 11 | * @ivar type: Event type. | ||
452 | 12 | * | ||
453 | 13 | * @ivar target: Element to which the DOM event was originally dispatched. | ||
454 | 14 | * | ||
455 | 15 | * @ivar cancelled: Has the default action been cancelled? | ||
456 | 16 | * | ||
457 | 17 | * @ivar stopped: Has event propagation been stopped? | ||
458 | 18 | */ | ||
459 | 19 | Divmod.Class.subclass(Nevow.Test.TestEvent, 'W3CEvent').methods( | ||
460 | 20 | function __init__(self, type, target) { | ||
461 | 21 | self.type = type; | ||
462 | 22 | self.target = target; | ||
463 | 23 | self.cancelled = false; | ||
464 | 24 | self.stopped = false; | ||
465 | 25 | }, | ||
466 | 26 | |||
467 | 27 | |||
468 | 28 | /** | ||
469 | 29 | * If an event is cancelable, the preventDefault method is used to signify | ||
470 | 30 | * that the event is to be canceled, meaning any default action normally | ||
471 | 31 | * taken by the implementation as a result of the event will not occur. | ||
472 | 32 | */ | ||
473 | 33 | function preventDefault(self) { | ||
474 | 34 | self.cancelled = true; | ||
475 | 35 | }, | ||
476 | 36 | |||
477 | 37 | |||
478 | 38 | /** | ||
479 | 39 | * The stopPropagation method is used prevent further propagation of an | ||
480 | 40 | * event during event flow. | ||
481 | 41 | */ | ||
482 | 42 | function stopPropagation(self) { | ||
483 | 43 | self.stopped = true; | ||
484 | 44 | }); | ||
485 | 45 | |||
486 | 46 | |||
487 | 47 | |||
488 | 48 | /** | ||
489 | 49 | * A mock DOM event that behaves according to IE before IE9. | ||
490 | 50 | * | ||
491 | 51 | * @ivar type: Event type. | ||
492 | 52 | * | ||
493 | 53 | * @ivar srcElement: Element to which the DOM event was originally dispatched. | ||
494 | 54 | * | ||
495 | 55 | * @ivar returnValue: Has the default action been cancelled? | ||
496 | 56 | * | ||
497 | 57 | * @ivar cancelBubble: Has event propagation been stopped? | ||
498 | 58 | */ | ||
499 | 59 | Divmod.Class.subclass(Nevow.Test.TestEvent, 'IEEvent').methods( | ||
500 | 60 | function __init__(self, type, srcElement) { | ||
501 | 61 | self.type = type; | ||
502 | 62 | self.srcElement = srcElement; | ||
503 | 63 | self.returnValue = true; | ||
504 | 64 | self.cancelBubble = false; | ||
505 | 65 | }); | ||
506 | 66 | |||
507 | 67 | |||
508 | 68 | |||
509 | 69 | /** | ||
510 | 70 | * Tests for L{Nevow.Athena.Event} when using W3C-style event objects. | ||
511 | 71 | */ | ||
512 | 72 | Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent, 'TestEventW3C').methods( | ||
513 | 73 | function createDOMEvent(self, type, target) { | ||
514 | 74 | return Nevow.Test.TestEvent.W3CEvent(type, target); | ||
515 | 75 | }, | ||
516 | 76 | |||
517 | 77 | |||
518 | 78 | /** | ||
519 | 79 | * L{Nevow.Athena.Event.fromDOMEvent} creates a specific I{Event} subclass | ||
520 | 80 | * if it supports the DOM event otherwise L{Nevow.Athena.Event} is used. | ||
521 | 81 | */ | ||
522 | 82 | function test_fromDOMEvent(self) { | ||
523 | 83 | function assertInstanceOf(inst, type, msg) { | ||
524 | 84 | self.assertIdentical( | ||
525 | 85 | inst instanceof type, | ||
526 | 86 | true, | ||
527 | 87 | 'Expected ' + inst.toString() + | ||
528 | 88 | ' to be an instance of ' + type.toString()); | ||
529 | 89 | } | ||
530 | 90 | |||
531 | 91 | var handlers = [ | ||
532 | 92 | Nevow.Athena.KeyEvent, | ||
533 | 93 | Nevow.Athena.MouseEvent]; | ||
534 | 94 | |||
535 | 95 | for (var i = 0; i < handlers.length; ++i) { | ||
536 | 96 | var handler = handlers[i]; | ||
537 | 97 | var knownEventTypes = handler.knownEventTypes; | ||
538 | 98 | for (var j = 0; j < knownEventTypes.length; ++j) { | ||
539 | 99 | var domEvent = self.createDOMEvent( | ||
540 | 100 | knownEventTypes[j], null); | ||
541 | 101 | var evt = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
542 | 102 | assertInstanceOf(evt, handler); | ||
543 | 103 | } | ||
544 | 104 | } | ||
545 | 105 | |||
546 | 106 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
547 | 107 | self.createDOMEvent('definitelyunknown', null)); | ||
548 | 108 | assertInstanceOf(evt, Nevow.Athena.Event); | ||
549 | 109 | }, | ||
550 | 110 | |||
551 | 111 | |||
552 | 112 | /** | ||
553 | 113 | * L{Nevow.Athena.Event} extracts information from different kinds of DOM | ||
554 | 114 | * events and puts them into a universal API. | ||
555 | 115 | */ | ||
556 | 116 | function test_attributes(self) { | ||
557 | 117 | var eventType = 'eventType'; | ||
558 | 118 | var target = 42; | ||
559 | 119 | var domEvent = self.createDOMEvent(eventType, target); | ||
560 | 120 | var event = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
561 | 121 | self.assertIdentical(event.event, domEvent); | ||
562 | 122 | self.assertIdentical(event.type, eventType); | ||
563 | 123 | self.assertIdentical(event.target, target); | ||
564 | 124 | }, | ||
565 | 125 | |||
566 | 126 | |||
567 | 127 | /** | ||
568 | 128 | * L{Nevow.Athena.Event.preventDefault} calls the method or sets the | ||
569 | 129 | * attribute on the underlying DOM event that prevents the event's default | ||
570 | 130 | * return value from being used. | ||
571 | 131 | */ | ||
572 | 132 | function test_preventDefault(self) { | ||
573 | 133 | var domEvent = self.createDOMEvent('eventtype', null); | ||
574 | 134 | var event = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
575 | 135 | self.assertIdentical(domEvent.cancelled, false); | ||
576 | 136 | event.preventDefault(); | ||
577 | 137 | self.assertIdentical(domEvent.cancelled, true); | ||
578 | 138 | }, | ||
579 | 139 | |||
580 | 140 | |||
581 | 141 | /** | ||
582 | 142 | * L{Nevow.Athena.Event.stopPropagation} calls the method or sets the | ||
583 | 143 | * attribute on the underlying DOM event that stops the event from | ||
584 | 144 | * propogating futher along in the DOM. | ||
585 | 145 | */ | ||
586 | 146 | function test_stopPropagation(self) { | ||
587 | 147 | var domEvent = self.createDOMEvent('eventtype', null); | ||
588 | 148 | var event = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
589 | 149 | self.assertIdentical(domEvent.stopped, false); | ||
590 | 150 | event.stopPropagation(); | ||
591 | 151 | self.assertIdentical(domEvent.stopped, true); | ||
592 | 152 | }); | ||
593 | 153 | |||
594 | 154 | |||
595 | 155 | |||
596 | 156 | /** | ||
597 | 157 | * Tests for L{Nevow.Athena.Event} when using IE-style event objects. | ||
598 | 158 | */ | ||
599 | 159 | Nevow.Test.TestEvent.TestEventW3C.subclass(Nevow.Test.TestEvent, | ||
600 | 160 | 'TestEventIE').methods( | ||
601 | 161 | function createDOMEvent(self, type, target) { | ||
602 | 162 | return Nevow.Test.TestEvent.IEEvent(type, target); | ||
603 | 163 | }, | ||
604 | 164 | |||
605 | 165 | |||
606 | 166 | function test_preventDefault(self) { | ||
607 | 167 | var domEvent = self.createDOMEvent('eventtype', null); | ||
608 | 168 | var event = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
609 | 169 | self.assertIdentical(domEvent.returnValue, true); | ||
610 | 170 | event.preventDefault(); | ||
611 | 171 | self.assertIdentical(domEvent.returnValue, false); | ||
612 | 172 | }, | ||
613 | 173 | |||
614 | 174 | |||
615 | 175 | function test_stopPropagation(self) { | ||
616 | 176 | var domEvent = self.createDOMEvent('eventtype', null); | ||
617 | 177 | var event = Nevow.Athena.Event.fromDOMEvent(domEvent); | ||
618 | 178 | self.assertIdentical(domEvent.cancelBubble, false); | ||
619 | 179 | event.stopPropagation(); | ||
620 | 180 | self.assertIdentical(domEvent.cancelBubble, true); | ||
621 | 181 | }); | ||
622 | 182 | |||
623 | 183 | |||
624 | 184 | |||
625 | 185 | /** | ||
626 | 186 | * Tests for L{Nevow.Athena.KeyEvent} when using W3C-style event objects. | ||
627 | 187 | */ | ||
628 | 188 | Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent, | ||
629 | 189 | 'TestKeyEventW3C').methods( | ||
630 | 190 | function setUp(self) { | ||
631 | 191 | self.supportsMeta = true; | ||
632 | 192 | self.eventType = Nevow.Test.TestEvent.W3CEvent; | ||
633 | 193 | }, | ||
634 | 194 | |||
635 | 195 | |||
636 | 196 | function createDOMEvent(self, type, target, args) { | ||
637 | 197 | var evt = self.eventType(type, target); | ||
638 | 198 | for (var key in args) { | ||
639 | 199 | evt[key] = args[key]; | ||
640 | 200 | } | ||
641 | 201 | return evt; | ||
642 | 202 | }, | ||
643 | 203 | |||
644 | 204 | |||
645 | 205 | /** | ||
646 | 206 | * Properties relating to modifier keys on the original DOM are accessible | ||
647 | 207 | * on L{Nevow.Athena.KeyEvent}. | ||
648 | 208 | */ | ||
649 | 209 | function test_modifiers(self) { | ||
650 | 210 | function createEvent(altKey, ctrlKey, shiftKey, metaKey) { | ||
651 | 211 | return Nevow.Athena.Event.fromDOMEvent( | ||
652 | 212 | self.createDOMEvent('keypress', null, { | ||
653 | 213 | 'altKey': !!altKey, | ||
654 | 214 | 'ctrlKey': !!ctrlKey, | ||
655 | 215 | 'shiftKey': !!shiftKey, | ||
656 | 216 | 'metaKey': !!metaKey})); | ||
657 | 217 | } | ||
658 | 218 | |||
659 | 219 | function assertModifiers(evt, altKey, ctrlKey, shiftKey, metaKey) { | ||
660 | 220 | self.assertIdentical(evt.altKey, altKey); | ||
661 | 221 | self.assertIdentical(evt.ctrlKey, ctrlKey); | ||
662 | 222 | self.assertIdentical(evt.shiftKey, shiftKey); | ||
663 | 223 | self.assertIdentical(evt.metaKey, metaKey); | ||
664 | 224 | } | ||
665 | 225 | |||
666 | 226 | assertModifiers( | ||
667 | 227 | createEvent(true), | ||
668 | 228 | true, false, false, false); | ||
669 | 229 | assertModifiers( | ||
670 | 230 | createEvent(true, true), | ||
671 | 231 | true, true, false, false); | ||
672 | 232 | assertModifiers( | ||
673 | 233 | createEvent(true, true, true), | ||
674 | 234 | true, true, true, false); | ||
675 | 235 | |||
676 | 236 | if (self.supportsMeta) { | ||
677 | 237 | assertModifiers( | ||
678 | 238 | createEvent(true, true, true, true), | ||
679 | 239 | true, true, true, true); | ||
680 | 240 | } else { | ||
681 | 241 | assertModifiers( | ||
682 | 242 | createEvent(true, true, true, true), | ||
683 | 243 | true, true, true, false) | ||
684 | 244 | }; | ||
685 | 245 | }, | ||
686 | 246 | |||
687 | 247 | |||
688 | 248 | /** | ||
689 | 249 | * L{Nevow.Athena.KeyEvent.getKeyCode} returns the Unicode key code for the | ||
690 | 250 | * DOM event, preferring I{keyCode} over I{which}. | ||
691 | 251 | */ | ||
692 | 252 | function test_getKeyCode(self) { | ||
693 | 253 | function createEvent(keyCode, which) { | ||
694 | 254 | return Nevow.Athena.Event.fromDOMEvent( | ||
695 | 255 | self.createDOMEvent('keypress', null, { | ||
696 | 256 | 'keyCode': keyCode, | ||
697 | 257 | 'which': which})); | ||
698 | 258 | } | ||
699 | 259 | |||
700 | 260 | function assertKeyCode(evt, keyCode) { | ||
701 | 261 | self.assertIdentical(evt.getKeyCode(), keyCode); | ||
702 | 262 | } | ||
703 | 263 | |||
704 | 264 | assertKeyCode(createEvent(65), 65); | ||
705 | 265 | assertKeyCode(createEvent(65, 97), 65); | ||
706 | 266 | assertKeyCode(createEvent(0, 65), 65); | ||
707 | 267 | }, | ||
708 | 268 | |||
709 | 269 | |||
710 | 270 | /** | ||
711 | 271 | * L{Nevow.Athena.KeyEvent.retKeyCode} sets the Unicode key code for the | ||
712 | 272 | * DOM event. | ||
713 | 273 | */ | ||
714 | 274 | function test_setKeyCode(self) { | ||
715 | 275 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
716 | 276 | self.createDOMEvent('keypress', null, { | ||
717 | 277 | 'keyCode': 65})); | ||
718 | 278 | |||
719 | 279 | self.assertIdentical(evt.getKeyCode(), 65); | ||
720 | 280 | evt.setKeyCode(97); | ||
721 | 281 | self.assertIdentical(evt.event.keyCode, 97); | ||
722 | 282 | self.assertIdentical(evt.getKeyCode(), 97); | ||
723 | 283 | }); | ||
724 | 284 | |||
725 | 285 | |||
726 | 286 | |||
727 | 287 | /** | ||
728 | 288 | * Tests for L{Nevow.Athena.KeyEvent} when using IE-style event objects. | ||
729 | 289 | */ | ||
730 | 290 | Nevow.Test.TestEvent.TestKeyEventW3C.subclass(Nevow.Test.TestEvent, | ||
731 | 291 | 'TestKeyEventIE').methods( | ||
732 | 292 | function setUp(self) { | ||
733 | 293 | Nevow.Test.TestEvent.TestKeyEventIE.upcall(self, 'setUp'); | ||
734 | 294 | self.supportsMeta = false; | ||
735 | 295 | self.eventType = Nevow.Test.TestEvent.IEEvent; | ||
736 | 296 | }, | ||
737 | 297 | |||
738 | 298 | |||
739 | 299 | function createDOMEvent(self, type, target, args) { | ||
740 | 300 | // IE < 9 doesn't support "metaKey". | ||
741 | 301 | delete args['metaKey']; | ||
742 | 302 | return Nevow.Test.TestEvent.TestKeyEventIE.upcall( | ||
743 | 303 | self, 'createDOMEvent', type, target, args); | ||
744 | 304 | }); | ||
745 | 305 | |||
746 | 306 | |||
747 | 307 | |||
748 | 308 | /** | ||
749 | 309 | * Tests for L{Nevow.Athena.MouseEvent} when using W3C-style event objects. | ||
750 | 310 | */ | ||
751 | 311 | Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent, | ||
752 | 312 | 'TestMouseEventW3C').methods( | ||
753 | 313 | function setUp(self) { | ||
754 | 314 | self.eventType = Nevow.Test.TestEvent.W3CEvent; | ||
755 | 315 | }, | ||
756 | 316 | |||
757 | 317 | |||
758 | 318 | function createDOMEvent(self, type, target, args) { | ||
759 | 319 | var evt = self.eventType(type, target); | ||
760 | 320 | for (var key in args) { | ||
761 | 321 | evt[key] = args[key]; | ||
762 | 322 | } | ||
763 | 323 | return evt; | ||
764 | 324 | }, | ||
765 | 325 | |||
766 | 326 | |||
767 | 327 | /** | ||
768 | 328 | * Get the platform-specific value for the specified mouse button | ||
769 | 329 | * configuration. | ||
770 | 330 | */ | ||
771 | 331 | function getButtonValue(self, left, middle, right) { | ||
772 | 332 | if (left) { | ||
773 | 333 | return 0; | ||
774 | 334 | } else if (middle) { | ||
775 | 335 | return 1; | ||
776 | 336 | } else if (right) { | ||
777 | 337 | return 2; | ||
778 | 338 | } | ||
779 | 339 | }, | ||
780 | 340 | |||
781 | 341 | |||
782 | 342 | /** | ||
783 | 343 | * Assert that L{Nevow.Athena.MouseEvent.getMouseButtons} produces a mapping | ||
784 | 344 | * that matches an expected mouse button configuration. | ||
785 | 345 | */ | ||
786 | 346 | function assertMouseButtons(self, evt, left, middle, right) { | ||
787 | 347 | var buttons = evt.getMouseButtons(); | ||
788 | 348 | self.assertIdentical(buttons.left, !!left); | ||
789 | 349 | self.assertIdentical(buttons.middle, !!middle); | ||
790 | 350 | self.assertIdentical(buttons.right, !!right); | ||
791 | 351 | }, | ||
792 | 352 | |||
793 | 353 | |||
794 | 354 | /** | ||
795 | 355 | * L{Nevow.Athena.MouseEvent.getMouseButtons} decodes the platform-specific | ||
796 | 356 | * C{event.button} value into a mapping that expresses the mouse button | ||
797 | 357 | * configuration. | ||
798 | 358 | */ | ||
799 | 359 | function test_getMouseButtons(self) { | ||
800 | 360 | function createEvent(left, middle, right) { | ||
801 | 361 | return Nevow.Athena.Event.fromDOMEvent( | ||
802 | 362 | self.createDOMEvent('mouseup', null, { | ||
803 | 363 | 'button': self.getButtonValue(left, middle, right)})); | ||
804 | 364 | } | ||
805 | 365 | |||
806 | 366 | self.assertMouseButtons( | ||
807 | 367 | createEvent(true, false, false), | ||
808 | 368 | true, false, false); | ||
809 | 369 | self.assertMouseButtons( | ||
810 | 370 | createEvent(false, true, false), | ||
811 | 371 | false, true, false); | ||
812 | 372 | self.assertMouseButtons( | ||
813 | 373 | createEvent(false, false, true), | ||
814 | 374 | false, false, true); | ||
815 | 375 | }, | ||
816 | 376 | |||
817 | 377 | |||
818 | 378 | /** | ||
819 | 379 | * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the | ||
820 | 380 | * event relative to the whole document. | ||
821 | 381 | */ | ||
822 | 382 | function test_getPagePosition(self) { | ||
823 | 383 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
824 | 384 | self.createDOMEvent('mouseup', null, { | ||
825 | 385 | 'pageX': 51, | ||
826 | 386 | 'pageY': 44})); | ||
827 | 387 | var pt = evt.getPagePosition(); | ||
828 | 388 | self.assertIdentical(pt.x, 51); | ||
829 | 389 | self.assertIdentical(pt.y, 44); | ||
830 | 390 | }, | ||
831 | 391 | |||
832 | 392 | |||
833 | 393 | /** | ||
834 | 394 | * L{Nevow.Athena.MouseEvent.getClientPosition} gets the coordinates of the | ||
835 | 395 | * event within the browse's client area. | ||
836 | 396 | */ | ||
837 | 397 | function test_getClientPosition(self) { | ||
838 | 398 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
839 | 399 | self.createDOMEvent('mouseup', null, { | ||
840 | 400 | 'clientX': 51, | ||
841 | 401 | 'clientY': 44})); | ||
842 | 402 | var pt = evt.getClientPosition(); | ||
843 | 403 | self.assertIdentical(pt.x, 51); | ||
844 | 404 | self.assertIdentical(pt.y, 44); | ||
845 | 405 | }, | ||
846 | 406 | |||
847 | 407 | |||
848 | 408 | /** | ||
849 | 409 | * L{Nevow.Athena.MouseEvent.getScreenPosition} gets the coordinates of the | ||
850 | 410 | * event within the screen as a whole. | ||
851 | 411 | */ | ||
852 | 412 | function test_getScreenPosition(self) { | ||
853 | 413 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
854 | 414 | self.createDOMEvent('mouseup', null, { | ||
855 | 415 | 'screenX': 51, | ||
856 | 416 | 'screenY': 44})); | ||
857 | 417 | var pt = evt.getScreenPosition(); | ||
858 | 418 | self.assertIdentical(pt.x, 51); | ||
859 | 419 | self.assertIdentical(pt.y, 44); | ||
860 | 420 | }); | ||
861 | 421 | |||
862 | 422 | |||
863 | 423 | |||
864 | 424 | /** | ||
865 | 425 | * Tests for L{Nevow.Athena.MouseEvent} when using IE-style event objects. | ||
866 | 426 | */ | ||
867 | 427 | Nevow.Test.TestEvent.TestMouseEventW3C.subclass( | ||
868 | 428 | Nevow.Test.TestEvent, | ||
869 | 429 | 'TestMouseEventIE').methods( | ||
870 | 430 | function setUp(self) { | ||
871 | 431 | self.eventType = Nevow.Test.TestEvent.IEEvent; | ||
872 | 432 | self.oldRuntime = Divmod.Runtime.theRuntime; | ||
873 | 433 | Divmod.Runtime.theRuntime = Divmod.Runtime.InternetExplorer(); | ||
874 | 434 | }, | ||
875 | 435 | |||
876 | 436 | |||
877 | 437 | function tearDown(self) { | ||
878 | 438 | Divmod.Runtime.theRuntime = self.oldRuntime; | ||
879 | 439 | }, | ||
880 | 440 | |||
881 | 441 | |||
882 | 442 | /** | ||
883 | 443 | * Get the platform-specific value for the specified mouse button | ||
884 | 444 | * configuration. | ||
885 | 445 | */ | ||
886 | 446 | function getButtonValue(self, left, middle, right) { | ||
887 | 447 | var button = 0; | ||
888 | 448 | if (left) { | ||
889 | 449 | button |= 1; | ||
890 | 450 | } | ||
891 | 451 | if (middle) { | ||
892 | 452 | button |= 4; | ||
893 | 453 | } | ||
894 | 454 | if (right) { | ||
895 | 455 | button |= 2; | ||
896 | 456 | } | ||
897 | 457 | return button; | ||
898 | 458 | }, | ||
899 | 459 | |||
900 | 460 | |||
901 | 461 | /** | ||
902 | 462 | * Internet Explorer can express configurations where multiple mouse | ||
903 | 463 | * buttons are pushed. | ||
904 | 464 | */ | ||
905 | 465 | function test_getMouseButtonsMultiple(self) { | ||
906 | 466 | function createEvent(left, middle, right) { | ||
907 | 467 | return Nevow.Athena.Event.fromDOMEvent( | ||
908 | 468 | self.createDOMEvent('mouseup', null, { | ||
909 | 469 | 'button': self.getButtonValue(left, middle, right)})); | ||
910 | 470 | } | ||
911 | 471 | |||
912 | 472 | self.assertMouseButtons( | ||
913 | 473 | createEvent(true, true, false), | ||
914 | 474 | true, true, false); | ||
915 | 475 | self.assertMouseButtons( | ||
916 | 476 | createEvent(false, true, true), | ||
917 | 477 | false, true, true); | ||
918 | 478 | self.assertMouseButtons( | ||
919 | 479 | createEvent(true, true, true), | ||
920 | 480 | true, true, true); | ||
921 | 481 | }, | ||
922 | 482 | |||
923 | 483 | |||
924 | 484 | /** | ||
925 | 485 | * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the | ||
926 | 486 | * event relative to the whole document. In Internet Explorer < 9 there are | ||
927 | 487 | * no C{'pageX'} or C{'pageY'} attributes instead a page position is | ||
928 | 488 | * derived from client position and the document and body scroll offsets. | ||
929 | 489 | */ | ||
930 | 490 | function test_getPagePosition(self) { | ||
931 | 491 | var evt = Nevow.Athena.Event.fromDOMEvent( | ||
932 | 492 | self.createDOMEvent('mouseup', null, { | ||
933 | 493 | 'clientX': 41, | ||
934 | 494 | 'clientY': 34})); | ||
935 | 495 | document.documentElement = { | ||
936 | 496 | 'scrollLeft': 4, | ||
937 | 497 | 'scrollTop': 6}; | ||
938 | 498 | document.body.scrollLeft = 6; | ||
939 | 499 | document.body.scrollTop = 4; | ||
940 | 500 | |||
941 | 501 | var pt = evt.getPagePosition(); | ||
942 | 502 | self.assertIdentical(pt.x, 51); | ||
943 | 503 | self.assertIdentical(pt.y, 44); | ||
944 | 504 | }); | ||
945 | 0 | 505 | ||
946 | === modified file 'Nevow/nevow/js/Nevow/Test/TestWidget.js' | |||
947 | --- Nevow/nevow/js/Nevow/Test/TestWidget.js 2008-12-31 18:44:01 +0000 | |||
948 | +++ Nevow/nevow/js/Nevow/Test/TestWidget.js 2011-07-10 13:56:31 +0000 | |||
949 | @@ -128,6 +128,7 @@ | |||
950 | 128 | * the browser, and the explicitly selected handler will be invoked. | 128 | * the browser, and the explicitly selected handler will be invoked. |
951 | 129 | */ | 129 | */ |
952 | 130 | function test_connectDOMEventCustomMethod(self) { | 130 | function test_connectDOMEventCustomMethod(self) { |
953 | 131 | event = {}; | ||
954 | 131 | self.widget.connectDOMEvent("onclick", "explicitClick"); | 132 | self.widget.connectDOMEvent("onclick", "explicitClick"); |
955 | 132 | self.node.onclick(); | 133 | self.node.onclick(); |
956 | 133 | self.assertIdentical(self.widget.clicked, "explicitly"); | 134 | self.assertIdentical(self.widget.clicked, "explicitly"); |
957 | @@ -138,6 +139,7 @@ | |||
958 | 138 | * the browser, and the explicitly selected node will be used. | 139 | * the browser, and the explicitly selected node will be used. |
959 | 139 | */ | 140 | */ |
960 | 140 | function test_connectDOMEventCustomNode(self) { | 141 | function test_connectDOMEventCustomNode(self) { |
961 | 142 | event = {}; | ||
962 | 141 | self.widget.connectDOMEvent("onclick", "explicitClick", self.otherNode); | 143 | self.widget.connectDOMEvent("onclick", "explicitClick", self.otherNode); |
963 | 142 | self.otherNode.onclick(); | 144 | self.otherNode.onclick(); |
964 | 143 | self.assertIdentical(self.widget.clicked, "explicitly"); | 145 | self.assertIdentical(self.widget.clicked, "explicitly"); |
965 | 144 | 146 | ||
966 | === modified file 'Nevow/nevow/test/test_athena.py' | |||
967 | --- Nevow/nevow/test/test_athena.py 2010-07-12 19:00:11 +0000 | |||
968 | +++ Nevow/nevow/test/test_athena.py 2011-07-10 13:56:31 +0000 | |||
969 | @@ -554,7 +554,7 @@ | |||
970 | 554 | """ | 554 | """ |
971 | 555 | expectedOutput = ( | 555 | expectedOutput = ( |
972 | 556 | 'return Nevow.Athena.Widget.handleEvent(' | 556 | 'return Nevow.Athena.Widget.handleEvent(' |
974 | 557 | 'this, "onclick", "bar");') | 557 | 'this, "onclick", "bar", event);') |
975 | 558 | tag = tags.span[athena.handler(event='onclick', handler='bar')] | 558 | tag = tags.span[athena.handler(event='onclick', handler='bar')] |
976 | 559 | mutated = athena._rewriteEventHandlerToAttribute(tag) | 559 | mutated = athena._rewriteEventHandlerToAttribute(tag) |
977 | 560 | output = flat.flatten(mutated) | 560 | output = flat.flatten(mutated) |
978 | 561 | 561 | ||
979 | === modified file 'Nevow/nevow/test/test_javascript.py' | |||
980 | --- Nevow/nevow/test/test_javascript.py 2008-07-07 08:58:45 +0000 | |||
981 | +++ Nevow/nevow/test/test_javascript.py 2011-07-10 13:56:31 +0000 | |||
982 | @@ -51,3 +51,7 @@ | |||
983 | 51 | 51 | ||
984 | 52 | def test_tabbedPane(self): | 52 | def test_tabbedPane(self): |
985 | 53 | return 'Nevow.Test.TestTabbedPane' | 53 | return 'Nevow.Test.TestTabbedPane' |
986 | 54 | |||
987 | 55 | |||
988 | 56 | def test_event(self): | ||
989 | 57 | return 'Nevow.Test.TestEvent' |