Merge lp:~divmod-dev/divmod.org/athena-events-806545 into lp:divmod.org

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
Reviewer Review Type Date Requested Status
Divmod-dev Pending
Review via email: mp+67455@code.launchpad.net

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 getMouseButtonsFromEvent 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
=== modified file 'Nevow/nevow/athena.py'
--- Nevow/nevow/athena.py 2010-07-12 19:00:11 +0000
+++ Nevow/nevow/athena.py 2011-07-10 13:56:31 +0000
@@ -1511,7 +1511,7 @@
15111511
15121512
1513handler = stan.Proto('athena:handler')1513handler = stan.Proto('athena:handler')
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);"
15151515
1516def _rewriteEventHandlerToAttribute(tag):1516def _rewriteEventHandlerToAttribute(tag):
1517 """1517 """
15181518
=== modified file 'Nevow/nevow/js/Divmod/Runtime/__init__.js'
--- Nevow/nevow/js/Divmod/Runtime/__init__.js 2008-12-31 18:44:01 +0000
+++ Nevow/nevow/js/Divmod/Runtime/__init__.js 2011-07-10 13:56:31 +0000
@@ -709,6 +709,25 @@
709 */709 */
710 function addBeforeUnloadHandler(self, aWindow, handler) {710 function addBeforeUnloadHandler(self, aWindow, handler) {
711 Divmod.Base.addToCallStack(aWindow, 'onbeforeunload', handler);711 Divmod.Base.addToCallStack(aWindow, 'onbeforeunload', handler);
712 },
713
714
715 /**
716 * Determine which mouse buttons were pressed from a browser event object.
717 *
718 * The default implementation matches the W3C DOM Level 2 Events
719 * specifications.
720 *
721 * @return: A mapping of C{'left'}, C{'middle'}, C{'right'} to C{Boolean}
722 * values indicating the state of the named mouse buttons.
723 *
724 * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MouseEvent>
725 */
726 function getMouseButtonsFromEvent(self, event) {
727 return {
728 'left': event.button == 0,
729 'middle': event.button == 1,
730 'right': event.button == 2};
712 });731 });
713732
714733
@@ -1205,6 +1224,20 @@
1205 */1224 */
1206 function addBeforeUnloadHandler(self, aWindow, handler) {1225 function addBeforeUnloadHandler(self, aWindow, handler) {
1207 Divmod.Base.addToCallStack(aWindow.document.body, 'onbeforeunload', handler);1226 Divmod.Base.addToCallStack(aWindow.document.body, 'onbeforeunload', handler);
1227 },
1228
1229
1230 /**
1231 * Internet Explorer specific handling of the C{event.button} property.
1232 *
1233 * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent}
1234 * @see: <http://msdn.microsoft.com/en-us/library/ms533544%28v=vs.85%29.aspx>
1235 */
1236 function getMouseButtonsFromEvent(self, event) {
1237 return {
1238 'left': (event.button & 1) != 0,
1239 'middle': (event.button & 4) != 0,
1240 'right': (event.button & 2) != 0};
1208 });1241 });
12091242
12101243
12111244
=== modified file 'Nevow/nevow/js/Divmod/Test/TestRuntime.js'
--- Nevow/nevow/js/Divmod/Test/TestRuntime.js 2008-12-31 18:44:01 +0000
+++ Nevow/nevow/js/Divmod/Test/TestRuntime.js 2011-07-10 13:56:31 +0000
@@ -713,6 +713,100 @@
713 });713 });
714714
715715
716
717/**
718 * Tests for L{Divmod.Runtime.Platform}.
719 */
720Divmod.UnitTest.TestCase.subclass(Divmod.Test.TestRuntime,
721 'PlatformTests').methods(
722 function setUp(self) {
723 self.runtime = Divmod.Runtime.Platform('name');
724 },
725
726
727 /**
728 * Assert that C{evt.button} when processed with
729 * C{getMouseButtonsFromEvent} yields the same values for its C{'left'},
730 * C{'middle'} and C{'right'} attributes as the parameters with these same
731 * names.
732 */
733 function assertMouseButtons(self, evt, left, middle, right) {
734 var buttons = self.runtime.getMouseButtonsFromEvent(evt);
735 self.assertIdentical(buttons.left, left)
736 self.assertIdentical(buttons.middle, middle)
737 self.assertIdentical(buttons.right, right)
738 },
739
740
741 /**
742 * Most platforms follow the W3C definitions for C{event.button} values.
743 *
744 * These values cannot be combined to indicate multiple buttons being
745 * pushed.
746 */
747 function test_getMouseButtonsFromEvent(self) {
748 var evt = {};
749 // Unknown value.
750 evt.button = 99;
751 self.assertMouseButtons(evt, false, false, false);
752 // Left mouse button.
753 evt.button = 0;
754 self.assertMouseButtons(evt, true, false, false);
755 // Middle mouse button.
756 evt.button = 1;
757 self.assertMouseButtons(evt, false, true, false);
758 // Right mouse button.
759 evt.button = 2;
760 self.assertMouseButtons(evt, false, false, true);
761 });
762
763
764
765/**
766 * Tests for L{Divmod.Runtime.InternetExplorer}.
767 */
768Divmod.Test.TestRuntime.PlatformTests.subclass(
769 Divmod.Test.TestRuntime,
770 'InternetExplorerPlatformTests').methods(
771 function setUp(self) {
772 self.runtime = Divmod.Runtime.InternetExplorer();
773 },
774
775
776 /**
777 * Internet Explorer uses different values for L{event.button} and can
778 * combine these values to indicate multiple buttons being pressed.
779 */
780 function test_getMouseButtonsFromEvent(self) {
781 var evt = {};
782 // No buttons.
783 evt.button = 0;
784 self.assertMouseButtons(evt, false, false, false);
785 // Left mouse button.
786 evt.button = 1;
787 self.assertMouseButtons(evt, true, false, false);
788 // Right mouse button.
789 evt.button = 2;
790 self.assertMouseButtons(evt, false, false, true);
791 // Middle mouse button.
792 evt.button = 4;
793 self.assertMouseButtons(evt, false, true, false);
794 // Left and right mouse buttons.
795 evt.button = 3;
796 self.assertMouseButtons(evt, true, false, true);
797 // Left and middle mouse buttons.
798 evt.button = 5;
799 self.assertMouseButtons(evt, true, true, false);
800 // Right and middle mouse buttons.
801 evt.button = 6;
802 self.assertMouseButtons(evt, false, true, true);
803 // Left, middle and right mouse buttons.
804 evt.button = 7;
805 self.assertMouseButtons(evt, true, true, true);
806 });
807
808
809
716Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests = Divmod.UnitTest.TestCase.subclass(810Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests = Divmod.UnitTest.TestCase.subclass(
717 'Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests');811 'Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests');
718/**812/**
719813
=== modified file 'Nevow/nevow/js/Nevow/Athena/__init__.js'
--- Nevow/nevow/js/Nevow/Athena/__init__.js 2009-01-21 22:58:03 +0000
+++ Nevow/nevow/js/Nevow/Athena/__init__.js 2011-07-10 13:56:31 +0000
@@ -1176,7 +1176,8 @@
1176 */1176 */
1177Nevow.Athena.Widget._makeEventHandler = function (domEventName, methodName) {1177Nevow.Athena.Widget._makeEventHandler = function (domEventName, methodName) {
1178 return function () {1178 return function () {
1179 return Nevow.Athena.Widget.handleEvent(this, domEventName, methodName);1179 return Nevow.Athena.Widget.handleEvent(
1180 this, domEventName, methodName, event);
1180 };1181 };
1181};1182};
11821183
@@ -1205,15 +1206,233 @@
1205 return Nevow.Athena.page.dispatchEvent(widget, eventName, handlerName, callable);1206 return Nevow.Athena.page.dispatchEvent(widget, eventName, handlerName, callable);
1206};1207};
12071208
1208/**1209
1209 * Given a node and a method name in an event handling context, dispatch the1210
1210 * event to the named method on the widget which owns the given node. This1211/**
1211 * also sets up error handling and does return value translation as1212 * Lowest-common denominator DOM event wrapper.
1212 * appropriate for an event handler. It also pauses the outgoing message1213 *
1213 * queue to allow multiple messages from the event handler to be batched up1214 * DOM event objects should wrapped by calling
1214 * into a single request.1215 * L{Nevow.Athena.Event.fromDOMEvent}.
1215 */1216 *
1216Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName, handlerName) {1217 * @type knownEventTypes: C{Array} of C{String}
1218 * @cvar knownEventTypes: Array of event types that this event wrapper can wrap.
1219 *
1220 * @ivar event: Original DOM event object.
1221 *
1222 * @ivar type: C{String}
1223 * @ivar type: Event type.
1224 *
1225 * @ivar target: Element to which the DOM event was originally dispatched.
1226 *
1227 * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html>
1228 * @see: <http://www.quirksmode.org/js/introevents.html>
1229 */
1230Divmod.Class.subclass(Nevow.Athena, 'Event').methods(
1231 function __init__(self, event) {
1232 self.event = event;
1233 self.type = self.event.type;
1234 self.target = self.event.target;
1235 if (self.target === undefined) {
1236 self.target = self.event.srcElement;
1237 }
1238 },
1239
1240
1241 /**
1242 * Cancels the event if it is cancelable, without stopping further
1243 * propagation of the event.
1244 */
1245 function preventDefault(self) {
1246 if (self.event.preventDefault) {
1247 self.event.preventDefault();
1248 } else {
1249 self.event.returnValue = false;
1250 }
1251 },
1252
1253
1254 /**
1255 * Stops the propagation of events further along in the DOM.
1256 */
1257 function stopPropagation(self) {
1258 if (self.event.stopPropagation) {
1259 self.event.stopPropagation();
1260 } else {
1261 self.event.cancelBubble = true;
1262 }
1263 });
1264
1265
1266
1267/**
1268 * Specific subclass of L{Nevow.Athena.Event} relating to key events.
1269 *
1270 * @ivar altKey: Was the I{alt} key pressed when the event fired?
1271 *
1272 * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired?
1273 *
1274 * @ivar shiftKey: Was the I{shift} key pressed when the event fired?
1275 *
1276 * @ivar metaKey: Was the I{meta} key pressed when the event fire?
1277 */
1278Nevow.Athena.Event.subclass(Nevow.Athena, 'KeyEvent').methods(
1279 function __init__(self, event) {
1280 Nevow.Athena.KeyEvent.upcall(self, '__init__', event);
1281 self.altKey = !!self.event.altKey;
1282 self.ctrlKey = !!self.event.ctrlKey;
1283 self.shiftKey = !!self.event.shiftKey;
1284 self.metaKey = !!self.event.metaKey; // Not in IE < 9.
1285 },
1286
1287
1288 /**
1289 * Get the Unicode value of key press.
1290 *
1291 * For the I{keydown} or I{keyup} events this is the virtual key code of the
1292 * physical button pushed. For I{keypress} event this is the character code
1293 * for an alphanumeric key.
1294 *
1295 * @see: <https://developer.mozilla.org/en/DOM/event.keyCode>
1296 * @see: <http://msdn.microsoft.com/en-us/library/ms533927%28v=VS.85%29.aspx>
1297 */
1298 function getKeyCode(self) {
1299 return self.event.keyCode || self.event.which;
1300 },
1301
1302
1303 /**
1304 * Set the Unicode value of key press.
1305 */
1306 function setKeyCode(self, value) {
1307 self.event.keyCode = value;
1308 });
1309
1310Nevow.Athena.KeyEvent.knownEventTypes = ['keydown', 'keypress', 'keyup'];
1311
1312
1313
1314/**
1315 * Specific subclass of L{Nevow.Athena.Event} relating to mouse events.
1316 *
1317 * @ivar altKey: Was the I{alt} key pressed when the event fired?
1318 *
1319 * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired?
1320 *
1321 * @ivar shiftKey: Was the I{shift} key pressed when the event fired?
1322 *
1323 * @ivar metaKey: Was the I{meta} key pressed when the event fire?
1324 */
1325Nevow.Athena.Event.subclass(Nevow.Athena, 'MouseEvent').methods(
1326 function __init__(self, event) {
1327 Nevow.Athena.MouseEvent.upcall(self, '__init__', event);
1328 self.altKey = self.event.altKey;
1329 self.ctrlKey = self.event.ctrlKey;
1330 self.shiftKey = self.event.shiftKey;
1331 self.metaKey = self.event.metaKey; // Not in IE < 9.
1332 },
1333
1334
1335 /**
1336 * Determine which mouse buttons were pressed in this event.
1337 *
1338 * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent}
1339 */
1340 function getMouseButtons(self) {
1341 return Divmod.Runtime.theRuntime.getMouseButtonsFromEvent(self.event);
1342 },
1343
1344
1345 /**
1346 * Get the coordinates of the event relative to the whole document.
1347 *
1348 * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
1349 * coordinates respectively.
1350 */
1351 function getPagePosition(self) {
1352 return Divmod.Runtime.theRuntime.getEventCoords(self.event);
1353 },
1354
1355
1356 /**
1357 * Get the coordinates within the browser's client area at which the event
1358 * occurred (as opposed to the coordinates within the page).
1359 *
1360 * For example, clicking in the top-left corner of the client area will
1361 * always result in a mouse event with a clientX value of 0, regardless of
1362 * whether the page is scrolled horizontally.
1363 *
1364 * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
1365 * coordinates respectively.
1366 */
1367 function getClientPosition(self) {
1368 return {
1369 'x': self.event.clientX,
1370 'y': self.event.clientY};
1371 },
1372
1373
1374 /**
1375 * Get the coordinates of the event within the screen as a whole.
1376 *
1377 * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
1378 * coordinates respectively.
1379 */
1380 function getScreenPosition(self) {
1381 return {
1382 'x': self.event.screenX,
1383 'y': self.event.screenY};
1384 });
1385
1386Nevow.Athena.MouseEvent.knownEventTypes = [
1387 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
1388 'mousewheel'];
1389
1390
1391
1392/**
1393 * Mapping of event types to known event handlers.
1394 */
1395Nevow.Athena.Event._eventHandlerMapping = (function() {
1396 var handlers = [
1397 Nevow.Athena.KeyEvent,
1398 Nevow.Athena.MouseEvent];
1399 var mapping = {}
1400 for (var i = 0; i < handlers.length; ++i) {
1401 var handler = handlers[i];
1402 var knownEventTypes = handler.knownEventTypes;
1403 for (var j = 0; j < knownEventTypes.length; ++j) {
1404 mapping[knownEventTypes[j]] = handler;
1405 }
1406 }
1407 return mapping;
1408})();
1409
1410
1411
1412/**
1413 * Wrap a DOM event object with an appropriate L{Nevow.Athena.Event} subclass
1414 * or L{Nevow.Athena.Event} if there is no specific handler for the event type.
1415 */
1416Nevow.Athena.Event.fromDOMEvent = function fromDOMEvent(event) {
1417 var handler = Nevow.Athena.Event._eventHandlerMapping[event.type];
1418 if (handler === undefined) {
1419 handler = Nevow.Athena.Event;
1420 }
1421 return handler(event);
1422};
1423
1424
1425
1426/**
1427 * Given a node, a method name in an event handling context and an event
1428 * object, dispatch the event to the named method on the widget which owns the
1429 * given node. This also sets up error handling and does return value
1430 * translation as appropriate for an event handler. It also pauses the
1431 * outgoing message queue to allow multiple messages from the event handler to
1432 * be batched up into a single request.
1433 */
1434Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName,
1435 handlerName, event) {
1217 var widget = Nevow.Athena.Widget.get(node);1436 var widget = Nevow.Athena.Widget.get(node);
1218 var method = widget[handlerName];1437 var method = widget[handlerName];
1219 var result = false;1438 var result = false;
@@ -1223,7 +1442,8 @@
1223 result = Nevow.Athena.Widget.dispatchEvent(1442 result = Nevow.Athena.Widget.dispatchEvent(
1224 widget, eventName, handlerName,1443 widget, eventName, handlerName,
1225 function() {1444 function() {
1226 return method.call(widget, node);1445 return method.call(
1446 widget, node, Nevow.Athena.Event.fromDOMEvent(event));
1227 });1447 });
1228 }1448 }
1229 return result;1449 return result;
12301450
=== added file 'Nevow/nevow/js/Nevow/Test/TestEvent.js'
--- Nevow/nevow/js/Nevow/Test/TestEvent.js 1970-01-01 00:00:00 +0000
+++ Nevow/nevow/js/Nevow/Test/TestEvent.js 2011-07-10 13:56:31 +0000
@@ -0,0 +1,504 @@
1// import Divmod
2// import Divmod.UnitTest
3// import Nevow.Athena
4
5
6
7/**
8 * A mock DOM event that behaves according to the W3C guide that most browsers
9 * follow.
10 *
11 * @ivar type: Event type.
12 *
13 * @ivar target: Element to which the DOM event was originally dispatched.
14 *
15 * @ivar cancelled: Has the default action been cancelled?
16 *
17 * @ivar stopped: Has event propagation been stopped?
18 */
19Divmod.Class.subclass(Nevow.Test.TestEvent, 'W3CEvent').methods(
20 function __init__(self, type, target) {
21 self.type = type;
22 self.target = target;
23 self.cancelled = false;
24 self.stopped = false;
25 },
26
27
28 /**
29 * If an event is cancelable, the preventDefault method is used to signify
30 * that the event is to be canceled, meaning any default action normally
31 * taken by the implementation as a result of the event will not occur.
32 */
33 function preventDefault(self) {
34 self.cancelled = true;
35 },
36
37
38 /**
39 * The stopPropagation method is used prevent further propagation of an
40 * event during event flow.
41 */
42 function stopPropagation(self) {
43 self.stopped = true;
44 });
45
46
47
48/**
49 * A mock DOM event that behaves according to IE before IE9.
50 *
51 * @ivar type: Event type.
52 *
53 * @ivar srcElement: Element to which the DOM event was originally dispatched.
54 *
55 * @ivar returnValue: Has the default action been cancelled?
56 *
57 * @ivar cancelBubble: Has event propagation been stopped?
58 */
59Divmod.Class.subclass(Nevow.Test.TestEvent, 'IEEvent').methods(
60 function __init__(self, type, srcElement) {
61 self.type = type;
62 self.srcElement = srcElement;
63 self.returnValue = true;
64 self.cancelBubble = false;
65 });
66
67
68
69/**
70 * Tests for L{Nevow.Athena.Event} when using W3C-style event objects.
71 */
72Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent, 'TestEventW3C').methods(
73 function createDOMEvent(self, type, target) {
74 return Nevow.Test.TestEvent.W3CEvent(type, target);
75 },
76
77
78 /**
79 * L{Nevow.Athena.Event.fromDOMEvent} creates a specific I{Event} subclass
80 * if it supports the DOM event otherwise L{Nevow.Athena.Event} is used.
81 */
82 function test_fromDOMEvent(self) {
83 function assertInstanceOf(inst, type, msg) {
84 self.assertIdentical(
85 inst instanceof type,
86 true,
87 'Expected ' + inst.toString() +
88 ' to be an instance of ' + type.toString());
89 }
90
91 var handlers = [
92 Nevow.Athena.KeyEvent,
93 Nevow.Athena.MouseEvent];
94
95 for (var i = 0; i < handlers.length; ++i) {
96 var handler = handlers[i];
97 var knownEventTypes = handler.knownEventTypes;
98 for (var j = 0; j < knownEventTypes.length; ++j) {
99 var domEvent = self.createDOMEvent(
100 knownEventTypes[j], null);
101 var evt = Nevow.Athena.Event.fromDOMEvent(domEvent);
102 assertInstanceOf(evt, handler);
103 }
104 }
105
106 var evt = Nevow.Athena.Event.fromDOMEvent(
107 self.createDOMEvent('definitelyunknown', null));
108 assertInstanceOf(evt, Nevow.Athena.Event);
109 },
110
111
112 /**
113 * L{Nevow.Athena.Event} extracts information from different kinds of DOM
114 * events and puts them into a universal API.
115 */
116 function test_attributes(self) {
117 var eventType = 'eventType';
118 var target = 42;
119 var domEvent = self.createDOMEvent(eventType, target);
120 var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
121 self.assertIdentical(event.event, domEvent);
122 self.assertIdentical(event.type, eventType);
123 self.assertIdentical(event.target, target);
124 },
125
126
127 /**
128 * L{Nevow.Athena.Event.preventDefault} calls the method or sets the
129 * attribute on the underlying DOM event that prevents the event's default
130 * return value from being used.
131 */
132 function test_preventDefault(self) {
133 var domEvent = self.createDOMEvent('eventtype', null);
134 var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
135 self.assertIdentical(domEvent.cancelled, false);
136 event.preventDefault();
137 self.assertIdentical(domEvent.cancelled, true);
138 },
139
140
141 /**
142 * L{Nevow.Athena.Event.stopPropagation} calls the method or sets the
143 * attribute on the underlying DOM event that stops the event from
144 * propogating futher along in the DOM.
145 */
146 function test_stopPropagation(self) {
147 var domEvent = self.createDOMEvent('eventtype', null);
148 var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
149 self.assertIdentical(domEvent.stopped, false);
150 event.stopPropagation();
151 self.assertIdentical(domEvent.stopped, true);
152 });
153
154
155
156/**
157 * Tests for L{Nevow.Athena.Event} when using IE-style event objects.
158 */
159Nevow.Test.TestEvent.TestEventW3C.subclass(Nevow.Test.TestEvent,
160 'TestEventIE').methods(
161 function createDOMEvent(self, type, target) {
162 return Nevow.Test.TestEvent.IEEvent(type, target);
163 },
164
165
166 function test_preventDefault(self) {
167 var domEvent = self.createDOMEvent('eventtype', null);
168 var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
169 self.assertIdentical(domEvent.returnValue, true);
170 event.preventDefault();
171 self.assertIdentical(domEvent.returnValue, false);
172 },
173
174
175 function test_stopPropagation(self) {
176 var domEvent = self.createDOMEvent('eventtype', null);
177 var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
178 self.assertIdentical(domEvent.cancelBubble, false);
179 event.stopPropagation();
180 self.assertIdentical(domEvent.cancelBubble, true);
181 });
182
183
184
185/**
186 * Tests for L{Nevow.Athena.KeyEvent} when using W3C-style event objects.
187 */
188Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent,
189 'TestKeyEventW3C').methods(
190 function setUp(self) {
191 self.supportsMeta = true;
192 self.eventType = Nevow.Test.TestEvent.W3CEvent;
193 },
194
195
196 function createDOMEvent(self, type, target, args) {
197 var evt = self.eventType(type, target);
198 for (var key in args) {
199 evt[key] = args[key];
200 }
201 return evt;
202 },
203
204
205 /**
206 * Properties relating to modifier keys on the original DOM are accessible
207 * on L{Nevow.Athena.KeyEvent}.
208 */
209 function test_modifiers(self) {
210 function createEvent(altKey, ctrlKey, shiftKey, metaKey) {
211 return Nevow.Athena.Event.fromDOMEvent(
212 self.createDOMEvent('keypress', null, {
213 'altKey': !!altKey,
214 'ctrlKey': !!ctrlKey,
215 'shiftKey': !!shiftKey,
216 'metaKey': !!metaKey}));
217 }
218
219 function assertModifiers(evt, altKey, ctrlKey, shiftKey, metaKey) {
220 self.assertIdentical(evt.altKey, altKey);
221 self.assertIdentical(evt.ctrlKey, ctrlKey);
222 self.assertIdentical(evt.shiftKey, shiftKey);
223 self.assertIdentical(evt.metaKey, metaKey);
224 }
225
226 assertModifiers(
227 createEvent(true),
228 true, false, false, false);
229 assertModifiers(
230 createEvent(true, true),
231 true, true, false, false);
232 assertModifiers(
233 createEvent(true, true, true),
234 true, true, true, false);
235
236 if (self.supportsMeta) {
237 assertModifiers(
238 createEvent(true, true, true, true),
239 true, true, true, true);
240 } else {
241 assertModifiers(
242 createEvent(true, true, true, true),
243 true, true, true, false)
244 };
245 },
246
247
248 /**
249 * L{Nevow.Athena.KeyEvent.getKeyCode} returns the Unicode key code for the
250 * DOM event, preferring I{keyCode} over I{which}.
251 */
252 function test_getKeyCode(self) {
253 function createEvent(keyCode, which) {
254 return Nevow.Athena.Event.fromDOMEvent(
255 self.createDOMEvent('keypress', null, {
256 'keyCode': keyCode,
257 'which': which}));
258 }
259
260 function assertKeyCode(evt, keyCode) {
261 self.assertIdentical(evt.getKeyCode(), keyCode);
262 }
263
264 assertKeyCode(createEvent(65), 65);
265 assertKeyCode(createEvent(65, 97), 65);
266 assertKeyCode(createEvent(0, 65), 65);
267 },
268
269
270 /**
271 * L{Nevow.Athena.KeyEvent.retKeyCode} sets the Unicode key code for the
272 * DOM event.
273 */
274 function test_setKeyCode(self) {
275 var evt = Nevow.Athena.Event.fromDOMEvent(
276 self.createDOMEvent('keypress', null, {
277 'keyCode': 65}));
278
279 self.assertIdentical(evt.getKeyCode(), 65);
280 evt.setKeyCode(97);
281 self.assertIdentical(evt.event.keyCode, 97);
282 self.assertIdentical(evt.getKeyCode(), 97);
283 });
284
285
286
287/**
288 * Tests for L{Nevow.Athena.KeyEvent} when using IE-style event objects.
289 */
290Nevow.Test.TestEvent.TestKeyEventW3C.subclass(Nevow.Test.TestEvent,
291 'TestKeyEventIE').methods(
292 function setUp(self) {
293 Nevow.Test.TestEvent.TestKeyEventIE.upcall(self, 'setUp');
294 self.supportsMeta = false;
295 self.eventType = Nevow.Test.TestEvent.IEEvent;
296 },
297
298
299 function createDOMEvent(self, type, target, args) {
300 // IE < 9 doesn't support "metaKey".
301 delete args['metaKey'];
302 return Nevow.Test.TestEvent.TestKeyEventIE.upcall(
303 self, 'createDOMEvent', type, target, args);
304 });
305
306
307
308/**
309 * Tests for L{Nevow.Athena.MouseEvent} when using W3C-style event objects.
310 */
311Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent,
312 'TestMouseEventW3C').methods(
313 function setUp(self) {
314 self.eventType = Nevow.Test.TestEvent.W3CEvent;
315 },
316
317
318 function createDOMEvent(self, type, target, args) {
319 var evt = self.eventType(type, target);
320 for (var key in args) {
321 evt[key] = args[key];
322 }
323 return evt;
324 },
325
326
327 /**
328 * Get the platform-specific value for the specified mouse button
329 * configuration.
330 */
331 function getButtonValue(self, left, middle, right) {
332 if (left) {
333 return 0;
334 } else if (middle) {
335 return 1;
336 } else if (right) {
337 return 2;
338 }
339 },
340
341
342 /**
343 * Assert that L{Nevow.Athena.MouseEvent.getMouseButtons} produces a mapping
344 * that matches an expected mouse button configuration.
345 */
346 function assertMouseButtons(self, evt, left, middle, right) {
347 var buttons = evt.getMouseButtons();
348 self.assertIdentical(buttons.left, !!left);
349 self.assertIdentical(buttons.middle, !!middle);
350 self.assertIdentical(buttons.right, !!right);
351 },
352
353
354 /**
355 * L{Nevow.Athena.MouseEvent.getMouseButtons} decodes the platform-specific
356 * C{event.button} value into a mapping that expresses the mouse button
357 * configuration.
358 */
359 function test_getMouseButtons(self) {
360 function createEvent(left, middle, right) {
361 return Nevow.Athena.Event.fromDOMEvent(
362 self.createDOMEvent('mouseup', null, {
363 'button': self.getButtonValue(left, middle, right)}));
364 }
365
366 self.assertMouseButtons(
367 createEvent(true, false, false),
368 true, false, false);
369 self.assertMouseButtons(
370 createEvent(false, true, false),
371 false, true, false);
372 self.assertMouseButtons(
373 createEvent(false, false, true),
374 false, false, true);
375 },
376
377
378 /**
379 * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the
380 * event relative to the whole document.
381 */
382 function test_getPagePosition(self) {
383 var evt = Nevow.Athena.Event.fromDOMEvent(
384 self.createDOMEvent('mouseup', null, {
385 'pageX': 51,
386 'pageY': 44}));
387 var pt = evt.getPagePosition();
388 self.assertIdentical(pt.x, 51);
389 self.assertIdentical(pt.y, 44);
390 },
391
392
393 /**
394 * L{Nevow.Athena.MouseEvent.getClientPosition} gets the coordinates of the
395 * event within the browse's client area.
396 */
397 function test_getClientPosition(self) {
398 var evt = Nevow.Athena.Event.fromDOMEvent(
399 self.createDOMEvent('mouseup', null, {
400 'clientX': 51,
401 'clientY': 44}));
402 var pt = evt.getClientPosition();
403 self.assertIdentical(pt.x, 51);
404 self.assertIdentical(pt.y, 44);
405 },
406
407
408 /**
409 * L{Nevow.Athena.MouseEvent.getScreenPosition} gets the coordinates of the
410 * event within the screen as a whole.
411 */
412 function test_getScreenPosition(self) {
413 var evt = Nevow.Athena.Event.fromDOMEvent(
414 self.createDOMEvent('mouseup', null, {
415 'screenX': 51,
416 'screenY': 44}));
417 var pt = evt.getScreenPosition();
418 self.assertIdentical(pt.x, 51);
419 self.assertIdentical(pt.y, 44);
420 });
421
422
423
424/**
425 * Tests for L{Nevow.Athena.MouseEvent} when using IE-style event objects.
426 */
427Nevow.Test.TestEvent.TestMouseEventW3C.subclass(
428 Nevow.Test.TestEvent,
429 'TestMouseEventIE').methods(
430 function setUp(self) {
431 self.eventType = Nevow.Test.TestEvent.IEEvent;
432 self.oldRuntime = Divmod.Runtime.theRuntime;
433 Divmod.Runtime.theRuntime = Divmod.Runtime.InternetExplorer();
434 },
435
436
437 function tearDown(self) {
438 Divmod.Runtime.theRuntime = self.oldRuntime;
439 },
440
441
442 /**
443 * Get the platform-specific value for the specified mouse button
444 * configuration.
445 */
446 function getButtonValue(self, left, middle, right) {
447 var button = 0;
448 if (left) {
449 button |= 1;
450 }
451 if (middle) {
452 button |= 4;
453 }
454 if (right) {
455 button |= 2;
456 }
457 return button;
458 },
459
460
461 /**
462 * Internet Explorer can express configurations where multiple mouse
463 * buttons are pushed.
464 */
465 function test_getMouseButtonsMultiple(self) {
466 function createEvent(left, middle, right) {
467 return Nevow.Athena.Event.fromDOMEvent(
468 self.createDOMEvent('mouseup', null, {
469 'button': self.getButtonValue(left, middle, right)}));
470 }
471
472 self.assertMouseButtons(
473 createEvent(true, true, false),
474 true, true, false);
475 self.assertMouseButtons(
476 createEvent(false, true, true),
477 false, true, true);
478 self.assertMouseButtons(
479 createEvent(true, true, true),
480 true, true, true);
481 },
482
483
484 /**
485 * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the
486 * event relative to the whole document. In Internet Explorer < 9 there are
487 * no C{'pageX'} or C{'pageY'} attributes instead a page position is
488 * derived from client position and the document and body scroll offsets.
489 */
490 function test_getPagePosition(self) {
491 var evt = Nevow.Athena.Event.fromDOMEvent(
492 self.createDOMEvent('mouseup', null, {
493 'clientX': 41,
494 'clientY': 34}));
495 document.documentElement = {
496 'scrollLeft': 4,
497 'scrollTop': 6};
498 document.body.scrollLeft = 6;
499 document.body.scrollTop = 4;
500
501 var pt = evt.getPagePosition();
502 self.assertIdentical(pt.x, 51);
503 self.assertIdentical(pt.y, 44);
504 });
0505
=== modified file 'Nevow/nevow/js/Nevow/Test/TestWidget.js'
--- Nevow/nevow/js/Nevow/Test/TestWidget.js 2008-12-31 18:44:01 +0000
+++ Nevow/nevow/js/Nevow/Test/TestWidget.js 2011-07-10 13:56:31 +0000
@@ -128,6 +128,7 @@
128 * the browser, and the explicitly selected handler will be invoked.128 * the browser, and the explicitly selected handler will be invoked.
129 */129 */
130 function test_connectDOMEventCustomMethod(self) {130 function test_connectDOMEventCustomMethod(self) {
131 event = {};
131 self.widget.connectDOMEvent("onclick", "explicitClick");132 self.widget.connectDOMEvent("onclick", "explicitClick");
132 self.node.onclick();133 self.node.onclick();
133 self.assertIdentical(self.widget.clicked, "explicitly");134 self.assertIdentical(self.widget.clicked, "explicitly");
@@ -138,6 +139,7 @@
138 * the browser, and the explicitly selected node will be used.139 * the browser, and the explicitly selected node will be used.
139 */140 */
140 function test_connectDOMEventCustomNode(self) {141 function test_connectDOMEventCustomNode(self) {
142 event = {};
141 self.widget.connectDOMEvent("onclick", "explicitClick", self.otherNode);143 self.widget.connectDOMEvent("onclick", "explicitClick", self.otherNode);
142 self.otherNode.onclick();144 self.otherNode.onclick();
143 self.assertIdentical(self.widget.clicked, "explicitly");145 self.assertIdentical(self.widget.clicked, "explicitly");
144146
=== modified file 'Nevow/nevow/test/test_athena.py'
--- Nevow/nevow/test/test_athena.py 2010-07-12 19:00:11 +0000
+++ Nevow/nevow/test/test_athena.py 2011-07-10 13:56:31 +0000
@@ -554,7 +554,7 @@
554 """554 """
555 expectedOutput = (555 expectedOutput = (
556 'return Nevow.Athena.Widget.handleEvent('556 'return Nevow.Athena.Widget.handleEvent('
557 'this, &quot;onclick&quot;, &quot;bar&quot;);')557 'this, &quot;onclick&quot;, &quot;bar&quot;, event);')
558 tag = tags.span[athena.handler(event='onclick', handler='bar')]558 tag = tags.span[athena.handler(event='onclick', handler='bar')]
559 mutated = athena._rewriteEventHandlerToAttribute(tag)559 mutated = athena._rewriteEventHandlerToAttribute(tag)
560 output = flat.flatten(mutated)560 output = flat.flatten(mutated)
561561
=== modified file 'Nevow/nevow/test/test_javascript.py'
--- Nevow/nevow/test/test_javascript.py 2008-07-07 08:58:45 +0000
+++ Nevow/nevow/test/test_javascript.py 2011-07-10 13:56:31 +0000
@@ -51,3 +51,7 @@
5151
52 def test_tabbedPane(self):52 def test_tabbedPane(self):
53 return 'Nevow.Test.TestTabbedPane'53 return 'Nevow.Test.TestTabbedPane'
54
55
56 def test_event(self):
57 return 'Nevow.Test.TestEvent'

Subscribers

People subscribed via source and target branches

to all changes: