divmod-dev team mailing list archive
-
divmod-dev team
-
Mailing list archive
-
Message #00026
[Merge] lp:~divmod-dev/divmod.org/athena-events-806545 into lp:divmod.org
Jonathan Jacobs has proposed merging lp:~divmod-dev/divmod.org/athena-events-806545 into lp:divmod.org.
Requested reviews:
Divmod-dev (divmod-dev)
Related bugs:
Bug #806545 in nevow: "Expose browser-generated events to Athena event handlers"
https://bugs.launchpad.net/nevow/+bug/806545
For more details, see:
https://code.launchpad.net/~divmod-dev/divmod.org/athena-events-806545/+merge/67455
Wrap DOM events in a universal way and pass these to Athena event handlers.
--
https://code.launchpad.net/~divmod-dev/divmod.org/athena-events-806545/+merge/67455
Your team Divmod-dev is requested to review the proposed merge of lp:~divmod-dev/divmod.org/athena-events-806545 into lp:divmod.org.
=== 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:41:23 +0000
@@ -1511,7 +1511,7 @@
handler = stan.Proto('athena:handler')
-_handlerFormat = "return Nevow.Athena.Widget.handleEvent(this, %(event)s, %(handler)s);"
+_handlerFormat = "return Nevow.Athena.Widget.handleEvent(this, %(event)s, %(handler)s, event);"
def _rewriteEventHandlerToAttribute(tag):
"""
=== 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:41:23 +0000
@@ -709,6 +709,25 @@
*/
function addBeforeUnloadHandler(self, aWindow, handler) {
Divmod.Base.addToCallStack(aWindow, 'onbeforeunload', handler);
+ },
+
+
+ /**
+ * Determine which mouse buttons were pressed from a browser event object.
+ *
+ * The default implementation matches the W3C DOM Level 2 Events
+ * specifications.
+ *
+ * @return: A mapping of C{'left'}, C{'middle'}, C{'right'} to C{Boolean}
+ * values indicating the state of the named mouse buttons.
+ *
+ * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-MouseEvent>
+ */
+ function getMouseButtonsFromEvent(self, event) {
+ return {
+ 'left': event.button == 0,
+ 'middle': event.button == 1,
+ 'right': event.button == 2};
});
@@ -1205,6 +1224,20 @@
*/
function addBeforeUnloadHandler(self, aWindow, handler) {
Divmod.Base.addToCallStack(aWindow.document.body, 'onbeforeunload', handler);
+ },
+
+
+ /**
+ * Internet Explorer specific handling of the C{event.button} property.
+ *
+ * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent}
+ * @see: <http://msdn.microsoft.com/en-us/library/ms533544%28v=vs.85%29.aspx>
+ */
+ function getMouseButtonsFromEvent(self, event) {
+ return {
+ 'left': (event.button & 1) != 0,
+ 'middle': (event.button & 4) != 0,
+ 'right': (event.button & 2) != 0};
});
=== 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:41:23 +0000
@@ -713,6 +713,100 @@
});
+
+/**
+ * Tests for L{Divmod.Runtime.Platform}.
+ */
+Divmod.UnitTest.TestCase.subclass(Divmod.Test.TestRuntime,
+ 'PlatformTests').methods(
+ function setUp(self) {
+ self.runtime = Divmod.Runtime.Platform('name');
+ },
+
+
+ /**
+ * Assert that C{evt.button} when processed with
+ * C{getMouseButtonsFromEvent} yields the same values for its C{'left'},
+ * C{'middle'} and C{'right'} attributes as the parameters with these same
+ * names.
+ */
+ function assertMouseButtons(self, evt, left, middle, right) {
+ var buttons = self.runtime.getMouseButtonsFromEvent(evt);
+ self.assertIdentical(buttons.left, left)
+ self.assertIdentical(buttons.middle, middle)
+ self.assertIdentical(buttons.right, right)
+ },
+
+
+ /**
+ * Most platforms follow the W3C definitions for C{event.button} values.
+ *
+ * These values cannot be combined to indicate multiple buttons being
+ * pushed.
+ */
+ function test_getMouseButtonsFromEvent(self) {
+ var evt = {};
+ // Unknown value.
+ evt.button = 99;
+ self.assertMouseButtons(evt, false, false, false);
+ // Left mouse button.
+ evt.button = 0;
+ self.assertMouseButtons(evt, true, false, false);
+ // Middle mouse button.
+ evt.button = 1;
+ self.assertMouseButtons(evt, false, true, false);
+ // Right mouse button.
+ evt.button = 2;
+ self.assertMouseButtons(evt, false, false, true);
+ });
+
+
+
+/**
+ * Tests for L{Divmod.Runtime.InternetExplorer}.
+ */
+Divmod.Test.TestRuntime.PlatformTests.subclass(
+ Divmod.Test.TestRuntime,
+ 'InternetExplorerPlatformTests').methods(
+ function setUp(self) {
+ self.runtime = Divmod.Runtime.InternetExplorer();
+ },
+
+
+ /**
+ * Internet Explorer uses different values for L{event.button} and can
+ * combine these values to indicate multiple buttons being pressed.
+ */
+ function test_getMouseButtonsFromEvent(self) {
+ var evt = {};
+ // No buttons.
+ evt.button = 0;
+ self.assertMouseButtons(evt, false, false, false);
+ // Left mouse button.
+ evt.button = 1;
+ self.assertMouseButtons(evt, true, false, false);
+ // Right mouse button.
+ evt.button = 2;
+ self.assertMouseButtons(evt, false, false, true);
+ // Middle mouse button.
+ evt.button = 4;
+ self.assertMouseButtons(evt, false, true, false);
+ // Left and right mouse buttons.
+ evt.button = 3;
+ self.assertMouseButtons(evt, true, false, true);
+ // Left and middle mouse buttons.
+ evt.button = 5;
+ self.assertMouseButtons(evt, true, true, false);
+ // Right and middle mouse buttons.
+ evt.button = 6;
+ self.assertMouseButtons(evt, false, true, true);
+ // Left, middle and right mouse buttons.
+ evt.button = 7;
+ self.assertMouseButtons(evt, true, true, true);
+ });
+
+
+
Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests = Divmod.UnitTest.TestCase.subclass(
'Divmod.Test.TestRuntime.SpidermonkeyRuntimeTests');
/**
=== 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:41:23 +0000
@@ -1176,7 +1176,8 @@
*/
Nevow.Athena.Widget._makeEventHandler = function (domEventName, methodName) {
return function () {
- return Nevow.Athena.Widget.handleEvent(this, domEventName, methodName);
+ return Nevow.Athena.Widget.handleEvent(
+ this, domEventName, methodName, event);
};
};
@@ -1205,15 +1206,233 @@
return Nevow.Athena.page.dispatchEvent(widget, eventName, handlerName, callable);
};
-/**
- * Given a node and a method name in an event handling context, dispatch the
- * event to the named method on the widget which owns the given node. This
- * also sets up error handling and does return value translation as
- * appropriate for an event handler. It also pauses the outgoing message
- * queue to allow multiple messages from the event handler to be batched up
- * into a single request.
- */
-Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName, handlerName) {
+
+
+/**
+ * Lowest-common denominator DOM event wrapper.
+ *
+ * DOM event objects should wrapped by calling
+ * L{Nevow.Athena.Event.fromDOMEvent}.
+ *
+ * @type knownEventTypes: C{Array} of C{String}
+ * @cvar knownEventTypes: Array of event types that this event wrapper can wrap.
+ *
+ * @ivar event: Original DOM event object.
+ *
+ * @ivar type: C{String}
+ * @ivar type: Event type.
+ *
+ * @ivar target: Element to which the DOM event was originally dispatched.
+ *
+ * @see: <http://www.w3.org/TR/DOM-Level-2-Events/events.html>
+ * @see: <http://www.quirksmode.org/js/introevents.html>
+ */
+Divmod.Class.subclass(Nevow.Athena, 'Event').methods(
+ function __init__(self, event) {
+ self.event = event;
+ self.type = self.event.type;
+ self.target = self.event.target;
+ if (self.target === undefined) {
+ self.target = self.event.srcElement;
+ }
+ },
+
+
+ /**
+ * Cancels the event if it is cancelable, without stopping further
+ * propagation of the event.
+ */
+ function preventDefault(self) {
+ if (self.event.preventDefault) {
+ self.event.preventDefault();
+ } else {
+ self.event.returnValue = false;
+ }
+ },
+
+
+ /**
+ * Stops the propagation of events further along in the DOM.
+ */
+ function stopPropagation(self) {
+ if (self.event.stopPropagation) {
+ self.event.stopPropagation();
+ } else {
+ self.event.cancelBubble = true;
+ }
+ });
+
+
+
+/**
+ * Specific subclass of L{Nevow.Athena.Event} relating to key events.
+ *
+ * @ivar altKey: Was the I{alt} key pressed when the event fired?
+ *
+ * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired?
+ *
+ * @ivar shiftKey: Was the I{shift} key pressed when the event fired?
+ *
+ * @ivar metaKey: Was the I{meta} key pressed when the event fire?
+ */
+Nevow.Athena.Event.subclass(Nevow.Athena, 'KeyEvent').methods(
+ function __init__(self, event) {
+ Nevow.Athena.KeyEvent.upcall(self, '__init__', event);
+ self.altKey = !!self.event.altKey;
+ self.ctrlKey = !!self.event.ctrlKey;
+ self.shiftKey = !!self.event.shiftKey;
+ self.metaKey = !!self.event.metaKey; // Not in IE < 9.
+ },
+
+
+ /**
+ * Get the Unicode value of key press.
+ *
+ * For the I{keydown} or I{keyup} events this is the virtual key code of the
+ * physical button pushed. For I{keypress} event this is the character code
+ * for an alphanumeric key.
+ *
+ * @see: <https://developer.mozilla.org/en/DOM/event.keyCode>
+ * @see: <http://msdn.microsoft.com/en-us/library/ms533927%28v=VS.85%29.aspx>
+ */
+ function getKeyCode(self) {
+ return self.event.keyCode || self.event.which;
+ },
+
+
+ /**
+ * Set the Unicode value of key press.
+ */
+ function setKeyCode(self, value) {
+ self.event.keyCode = value;
+ });
+
+Nevow.Athena.KeyEvent.knownEventTypes = ['keydown', 'keypress', 'keyup'];
+
+
+
+/**
+ * Specific subclass of L{Nevow.Athena.Event} relating to mouse events.
+ *
+ * @ivar altKey: Was the I{alt} key pressed when the event fired?
+ *
+ * @ivar ctrlKey: Was the I{ctrl} key pressed when the event fired?
+ *
+ * @ivar shiftKey: Was the I{shift} key pressed when the event fired?
+ *
+ * @ivar metaKey: Was the I{meta} key pressed when the event fire?
+ */
+Nevow.Athena.Event.subclass(Nevow.Athena, 'MouseEvent').methods(
+ function __init__(self, event) {
+ Nevow.Athena.MouseEvent.upcall(self, '__init__', event);
+ self.altKey = self.event.altKey;
+ self.ctrlKey = self.event.ctrlKey;
+ self.shiftKey = self.event.shiftKey;
+ self.metaKey = self.event.metaKey; // Not in IE < 9.
+ },
+
+
+ /**
+ * Determine which mouse buttons were pressed in this event.
+ *
+ * @see: L{Divmod.Runtime.Platform.getMouseButtonsFromEvent}
+ */
+ function getMouseButtons(self) {
+ return Divmod.Runtime.theRuntime.getMouseButtonsFromEvent(self.event);
+ },
+
+
+ /**
+ * Get the coordinates of the event relative to the whole document.
+ *
+ * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
+ * coordinates respectively.
+ */
+ function getPagePosition(self) {
+ return Divmod.Runtime.theRuntime.getEventCoords(self.event);
+ },
+
+
+ /**
+ * Get the coordinates within the browser's client area at which the event
+ * occurred (as opposed to the coordinates within the page).
+ *
+ * For example, clicking in the top-left corner of the client area will
+ * always result in a mouse event with a clientX value of 0, regardless of
+ * whether the page is scrolled horizontally.
+ *
+ * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
+ * coordinates respectively.
+ */
+ function getClientPosition(self) {
+ return {
+ 'x': self.event.clientX,
+ 'y': self.event.clientY};
+ },
+
+
+ /**
+ * Get the coordinates of the event within the screen as a whole.
+ *
+ * @return: Mapping of C{'x'} and C{'y'} to the horizontal and vertical
+ * coordinates respectively.
+ */
+ function getScreenPosition(self) {
+ return {
+ 'x': self.event.screenX,
+ 'y': self.event.screenY};
+ });
+
+Nevow.Athena.MouseEvent.knownEventTypes = [
+ 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup',
+ 'mousewheel'];
+
+
+
+/**
+ * Mapping of event types to known event handlers.
+ */
+Nevow.Athena.Event._eventHandlerMapping = (function() {
+ var handlers = [
+ Nevow.Athena.KeyEvent,
+ Nevow.Athena.MouseEvent];
+ var mapping = {}
+ for (var i = 0; i < handlers.length; ++i) {
+ var handler = handlers[i];
+ var knownEventTypes = handler.knownEventTypes;
+ for (var j = 0; j < knownEventTypes.length; ++j) {
+ mapping[knownEventTypes[j]] = handler;
+ }
+ }
+ return mapping;
+})();
+
+
+
+/**
+ * Wrap a DOM event object with an appropriate L{Nevow.Athena.Event} subclass
+ * or L{Nevow.Athena.Event} if there is no specific handler for the event type.
+ */
+Nevow.Athena.Event.fromDOMEvent = function fromDOMEvent(event) {
+ var handler = Nevow.Athena.Event._eventHandlerMapping[event.type];
+ if (handler === undefined) {
+ handler = Nevow.Athena.Event;
+ }
+ return handler(event);
+};
+
+
+
+/**
+ * Given a node, a method name in an event handling context and an event
+ * object, dispatch the event to the named method on the widget which owns the
+ * given node. This also sets up error handling and does return value
+ * translation as appropriate for an event handler. It also pauses the
+ * outgoing message queue to allow multiple messages from the event handler to
+ * be batched up into a single request.
+ */
+Nevow.Athena.Widget.handleEvent = function handleEvent(node, eventName,
+ handlerName, event) {
var widget = Nevow.Athena.Widget.get(node);
var method = widget[handlerName];
var result = false;
@@ -1223,7 +1442,8 @@
result = Nevow.Athena.Widget.dispatchEvent(
widget, eventName, handlerName,
function() {
- return method.call(widget, node);
+ return method.call(
+ widget, node, Nevow.Athena.Event.fromDOMEvent(event));
});
}
return result;
=== 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:41:23 +0000
@@ -0,0 +1,504 @@
+// import Divmod
+// import Divmod.UnitTest
+// import Nevow.Athena
+
+
+
+/**
+ * A mock DOM event that behaves according to the W3C guide that most browsers
+ * follow.
+ *
+ * @ivar type: Event type.
+ *
+ * @ivar target: Element to which the DOM event was originally dispatched.
+ *
+ * @ivar cancelled: Has the default action been cancelled?
+ *
+ * @ivar stopped: Has event propagation been stopped?
+ */
+Divmod.Class.subclass(Nevow.Test.TestEvent, 'W3CEvent').methods(
+ function __init__(self, type, target) {
+ self.type = type;
+ self.target = target;
+ self.cancelled = false;
+ self.stopped = false;
+ },
+
+
+ /**
+ * If an event is cancelable, the preventDefault method is used to signify
+ * that the event is to be canceled, meaning any default action normally
+ * taken by the implementation as a result of the event will not occur.
+ */
+ function preventDefault(self) {
+ self.cancelled = true;
+ },
+
+
+ /**
+ * The stopPropagation method is used prevent further propagation of an
+ * event during event flow.
+ */
+ function stopPropagation(self) {
+ self.stopped = true;
+ });
+
+
+
+/**
+ * A mock DOM event that behaves according to IE before IE9.
+ *
+ * @ivar type: Event type.
+ *
+ * @ivar srcElement: Element to which the DOM event was originally dispatched.
+ *
+ * @ivar returnValue: Has the default action been cancelled?
+ *
+ * @ivar cancelBubble: Has event propagation been stopped?
+ */
+Divmod.Class.subclass(Nevow.Test.TestEvent, 'IEEvent').methods(
+ function __init__(self, type, srcElement) {
+ self.type = type;
+ self.srcElement = srcElement;
+ self.returnValue = true;
+ self.cancelBubble = false;
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.Event} when using W3C-style event objects.
+ */
+Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent, 'TestEventW3C').methods(
+ function createDOMEvent(self, type, target) {
+ return Nevow.Test.TestEvent.W3CEvent(type, target);
+ },
+
+
+ /**
+ * L{Nevow.Athena.Event.fromDOMEvent} creates a specific I{Event} subclass
+ * if it supports the DOM event otherwise L{Nevow.Athena.Event} is used.
+ */
+ function test_fromDOMEvent(self) {
+ function assertInstanceOf(inst, type, msg) {
+ self.assertIdentical(
+ evt instanceof type,
+ true,
+ 'Expected ' + inst.toString() +
+ ' to be an instance of ' + type.toString());
+ }
+
+ var handlers = [
+ Nevow.Athena.KeyEvent,
+ Nevow.Athena.MouseEvent];
+
+ for (var i = 0; i < handlers.length; ++i) {
+ var handler = handlers[i];
+ var knownEventTypes = handler.knownEventTypes;
+ for (var j = 0; j < knownEventTypes.length; ++j) {
+ var domEvent = self.createDOMEvent(
+ knownEventTypes[j], null);
+ var evt = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ assertInstanceOf(evt, handler);
+ }
+ }
+
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('definitelyunknown', null));
+ assertInstanceOf(evt, Nevow.Athena.Event);
+ },
+
+
+ /**
+ * L{Nevow.Athena.Event} extracts information from different kinds of DOM
+ * events and puts them into a universal API.
+ */
+ function test_attributes(self) {
+ var eventType = 'eventType';
+ var target = 42;
+ var domEvent = self.createDOMEvent(eventType, target);
+ var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ self.assertIdentical(event.event, domEvent);
+ self.assertIdentical(event.type, eventType);
+ self.assertIdentical(event.target, target);
+ },
+
+
+ /**
+ * L{Nevow.Athena.Event.preventDefault} calls the method or sets the
+ * attribute on the underlying DOM event that prevents the event's default
+ * return value from being used.
+ */
+ function test_preventDefault(self) {
+ var domEvent = self.createDOMEvent('eventtype', null);
+ var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ self.assertIdentical(domEvent.cancelled, false);
+ event.preventDefault();
+ self.assertIdentical(domEvent.cancelled, true);
+ },
+
+
+ /**
+ * L{Nevow.Athena.Event.stopPropagation} calls the method or sets the
+ * attribute on the underlying DOM event that stops the event from
+ * propogating futher along in the DOM.
+ */
+ function test_stopPropagation(self) {
+ var domEvent = self.createDOMEvent('eventtype', null);
+ var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ self.assertIdentical(domEvent.stopped, false);
+ event.stopPropagation();
+ self.assertIdentical(domEvent.stopped, true);
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.Event} when using IE-style event objects.
+ */
+Nevow.Test.TestEvent.TestEventW3C.subclass(Nevow.Test.TestEvent,
+ 'TestEventIE').methods(
+ function createDOMEvent(self, type, target) {
+ return Nevow.Test.TestEvent.IEEvent(type, target);
+ },
+
+
+ function test_preventDefault(self) {
+ var domEvent = self.createDOMEvent('eventtype', null);
+ var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ self.assertIdentical(domEvent.returnValue, true);
+ event.preventDefault();
+ self.assertIdentical(domEvent.returnValue, false);
+ },
+
+
+ function test_stopPropagation(self) {
+ var domEvent = self.createDOMEvent('eventtype', null);
+ var event = Nevow.Athena.Event.fromDOMEvent(domEvent);
+ self.assertIdentical(domEvent.cancelBubble, false);
+ event.stopPropagation();
+ self.assertIdentical(domEvent.cancelBubble, true);
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.KeyEvent} when using W3C-style event objects.
+ */
+Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent,
+ 'TestKeyEventW3C').methods(
+ function setUp(self) {
+ self.supportsMeta = true;
+ self.eventType = Nevow.Test.TestEvent.W3CEvent;
+ },
+
+
+ function createDOMEvent(self, type, target, args) {
+ var evt = self.eventType(type, target);
+ for (var key in args) {
+ evt[key] = args[key];
+ }
+ return evt;
+ },
+
+
+ /**
+ * Properties relating to modifier keys on the original DOM are accessible
+ * on L{Nevow.Athena.KeyEvent}.
+ */
+ function test_modifiers(self) {
+ function createEvent(altKey, ctrlKey, shiftKey, metaKey) {
+ return Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('keypress', null, {
+ 'altKey': !!altKey,
+ 'ctrlKey': !!ctrlKey,
+ 'shiftKey': !!shiftKey,
+ 'metaKey': !!metaKey}));
+ }
+
+ function assertModifiers(evt, altKey, ctrlKey, shiftKey, metaKey) {
+ self.assertIdentical(evt.altKey, altKey);
+ self.assertIdentical(evt.ctrlKey, ctrlKey);
+ self.assertIdentical(evt.shiftKey, shiftKey);
+ self.assertIdentical(evt.metaKey, metaKey);
+ }
+
+ assertModifiers(
+ createEvent(true),
+ true, false, false, false);
+ assertModifiers(
+ createEvent(true, true),
+ true, true, false, false);
+ assertModifiers(
+ createEvent(true, true, true),
+ true, true, true, false);
+
+ if (self.supportsMeta) {
+ assertModifiers(
+ createEvent(true, true, true, true),
+ true, true, true, true);
+ } else {
+ assertModifiers(
+ createEvent(true, true, true, true),
+ true, true, true, false)
+ };
+ },
+
+
+ /**
+ * L{Nevow.Athena.KeyEvent.getKeyCode} returns the Unicode key code for the
+ * DOM event, preferring I{keyCode} over I{which}.
+ */
+ function test_getKeyCode(self) {
+ function createEvent(keyCode, which) {
+ return Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('keypress', null, {
+ 'keyCode': keyCode,
+ 'which': which}));
+ }
+
+ function assertKeyCode(evt, keyCode) {
+ self.assertIdentical(evt.getKeyCode(), keyCode);
+ }
+
+ assertKeyCode(createEvent(65), 65);
+ assertKeyCode(createEvent(65, 97), 65);
+ assertKeyCode(createEvent(0, 65), 65);
+ },
+
+
+ /**
+ * L{Nevow.Athena.KeyEvent.retKeyCode} sets the Unicode key code for the
+ * DOM event.
+ */
+ function test_setKeyCode(self) {
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('keypress', null, {
+ 'keyCode': 65}));
+
+ self.assertIdentical(evt.getKeyCode(), 65);
+ evt.setKeyCode(97);
+ self.assertIdentical(evt.event.keyCode, 97);
+ self.assertIdentical(evt.getKeyCode(), 97);
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.KeyEvent} when using IE-style event objects.
+ */
+Nevow.Test.TestEvent.TestKeyEventW3C.subclass(Nevow.Test.TestEvent,
+ 'TestKeyEventIE').methods(
+ function setUp(self) {
+ Nevow.Test.TestEvent.TestKeyEventIE.upcall(self, 'setUp');
+ self.supportsMeta = false;
+ self.eventType = Nevow.Test.TestEvent.IEEvent;
+ },
+
+
+ function createDOMEvent(self, type, target, args) {
+ // IE < 9 doesn't support "metaKey".
+ delete args['metaKey'];
+ return Nevow.Test.TestEvent.TestKeyEventIE.upcall(
+ self, 'createDOMEvent', type, target, args);
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.MouseEvent} when using W3C-style event objects.
+ */
+Divmod.UnitTest.TestCase.subclass(Nevow.Test.TestEvent,
+ 'TestMouseEventW3C').methods(
+ function setUp(self) {
+ self.eventType = Nevow.Test.TestEvent.W3CEvent;
+ },
+
+
+ function createDOMEvent(self, type, target, args) {
+ var evt = self.eventType(type, target);
+ for (var key in args) {
+ evt[key] = args[key];
+ }
+ return evt;
+ },
+
+
+ /**
+ * Get the platform-specific value for the specified mouse button
+ * configuration.
+ */
+ function getButtonValue(self, left, middle, right) {
+ if (left) {
+ return 0;
+ } else if (middle) {
+ return 1;
+ } else if (right) {
+ return 2;
+ }
+ },
+
+
+ /**
+ * Assert that L{Nevow.Athena.MouseEvent.getMouseButtons} produces a mapping
+ * that matches an expected mouse button configuration.
+ */
+ function assertMouseButtons(self, evt, left, middle, right) {
+ var buttons = evt.getMouseButtons();
+ self.assertIdentical(buttons.left, !!left);
+ self.assertIdentical(buttons.middle, !!middle);
+ self.assertIdentical(buttons.right, !!right);
+ },
+
+
+ /**
+ * L{Nevow.Athena.MouseEvent.getMouseButtons} decodes the platform-specific
+ * C{event.button} value into a mapping that expresses the mouse button
+ * configuration.
+ */
+ function test_getMouseButtons(self) {
+ function createEvent(left, middle, right) {
+ return Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'button': self.getButtonValue(left, middle, right)}));
+ }
+
+ self.assertMouseButtons(
+ createEvent(true, false, false),
+ true, false, false);
+ self.assertMouseButtons(
+ createEvent(false, true, false),
+ false, true, false);
+ self.assertMouseButtons(
+ createEvent(false, false, true),
+ false, false, true);
+ },
+
+
+ /**
+ * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the
+ * event relative to the whole document.
+ */
+ function test_getPagePosition(self) {
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'pageX': 51,
+ 'pageY': 44}));
+ var pt = evt.getPagePosition();
+ self.assertIdentical(pt.x, 51);
+ self.assertIdentical(pt.y, 44);
+ },
+
+
+ /**
+ * L{Nevow.Athena.MouseEvent.getClientPosition} gets the coordinates of the
+ * event within the browse's client area.
+ */
+ function test_getClientPosition(self) {
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'clientX': 51,
+ 'clientY': 44}));
+ var pt = evt.getClientPosition();
+ self.assertIdentical(pt.x, 51);
+ self.assertIdentical(pt.y, 44);
+ },
+
+
+ /**
+ * L{Nevow.Athena.MouseEvent.getScreenPosition} gets the coordinates of the
+ * event within the screen as a whole.
+ */
+ function test_getScreenPosition(self) {
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'screenX': 51,
+ 'screenY': 44}));
+ var pt = evt.getScreenPosition();
+ self.assertIdentical(pt.x, 51);
+ self.assertIdentical(pt.y, 44);
+ });
+
+
+
+/**
+ * Tests for L{Nevow.Athena.MouseEvent} when using IE-style event objects.
+ */
+Nevow.Test.TestEvent.TestMouseEventW3C.subclass(
+ Nevow.Test.TestEvent,
+ 'TestMouseEventIE').methods(
+ function setUp(self) {
+ self.eventType = Nevow.Test.TestEvent.IEEvent;
+ self.oldRuntime = Divmod.Runtime.theRuntime;
+ Divmod.Runtime.theRuntime = Divmod.Runtime.InternetExplorer();
+ },
+
+
+ function tearDown(self) {
+ Divmod.Runtime.theRuntime = self.oldRuntime;
+ },
+
+
+ /**
+ * Get the platform-specific value for the specified mouse button
+ * configuration.
+ */
+ function getButtonValue(self, left, middle, right) {
+ var button = 0;
+ if (left) {
+ button |= 1;
+ }
+ if (middle) {
+ button |= 4;
+ }
+ if (right) {
+ button |= 2;
+ }
+ return button;
+ },
+
+
+ /**
+ * Internet Explorer can express configurations where multiple mouse
+ * buttons are pushed.
+ */
+ function test_getMouseButtonsMultiple(self) {
+ function createEvent(left, middle, right) {
+ return Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'button': self.getButtonValue(left, middle, right)}));
+ }
+
+ self.assertMouseButtons(
+ createEvent(true, true, false),
+ true, true, false);
+ self.assertMouseButtons(
+ createEvent(false, true, true),
+ false, true, true);
+ self.assertMouseButtons(
+ createEvent(true, true, true),
+ true, true, true);
+ },
+
+
+ /**
+ * L{Nevow.Athena.MouseEvent.getPagePosition} gets the coordinates of the
+ * event relative to the whole document. In Internet Explorer < 9 there are
+ * no C{'pageX'} or C{'pageY'} attributes instead a page position is
+ * derived from client position and the document and body scroll offsets.
+ */
+ function test_getPagePosition(self) {
+ var evt = Nevow.Athena.Event.fromDOMEvent(
+ self.createDOMEvent('mouseup', null, {
+ 'clientX': 41,
+ 'clientY': 34}));
+ document.documentElement = {
+ 'scrollLeft': 4,
+ 'scrollTop': 6};
+ document.body.scrollLeft = 6;
+ document.body.scrollTop = 4;
+
+ var pt = evt.getPagePosition();
+ self.assertIdentical(pt.x, 51);
+ self.assertIdentical(pt.y, 44);
+ });
=== 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:41:23 +0000
@@ -128,6 +128,7 @@
* the browser, and the explicitly selected handler will be invoked.
*/
function test_connectDOMEventCustomMethod(self) {
+ event = {};
self.widget.connectDOMEvent("onclick", "explicitClick");
self.node.onclick();
self.assertIdentical(self.widget.clicked, "explicitly");
@@ -138,6 +139,7 @@
* the browser, and the explicitly selected node will be used.
*/
function test_connectDOMEventCustomNode(self) {
+ event = {};
self.widget.connectDOMEvent("onclick", "explicitClick", self.otherNode);
self.otherNode.onclick();
self.assertIdentical(self.widget.clicked, "explicitly");
=== 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:41:23 +0000
@@ -51,3 +51,7 @@
def test_tabbedPane(self):
return 'Nevow.Test.TestTabbedPane'
+
+
+ def test_event(self):
+ return 'Nevow.Test.TestEvent'