Permalink
Browse files

Start to break events out of the svg renderer

- for future reusability with other renderers
- to increase code organization and legibility
  • Loading branch information...
1 parent 05f3e31 commit c327866502ff0e076ef43c3ebe964971a1415604 @davidaurelio davidaurelio committed Feb 26, 2013
Showing with 276 additions and 0 deletions.
  1. +88 −0 src/renderer/event.js
  2. +185 −0 test/renderer/event-spec.js
  3. +3 −0 test/runner.html
View
@@ -0,0 +1,88 @@
+define(function() {
+ 'use strict';
+
+ /** @const */
+ var NO_MODIFIER = 0, ALT_KEY = 1 << 0, CTRL_KEY = 1 << 1, META_KEY = 1 << 2, SHIFT_KEY = 1 << 3;
+
+ /**
+ * Represents a keyboard event.
+ *
+ * @param {string} type The event type
+ * @param {number} keyCode The scan code of the key
+ * @param {number} [modifiers] Modifiers pressed during the key event
+ * @param {string} [targetValue] The current value of an associated input
+ * @constructor
+ */
+ function KeyboardEvent(type, keyCode, modifiers, targetValue) {
+ this.type = type;
+ this.keyCode = keyCode;
+ this.inputValue = targetValue;
+
+ this.altKey = !!(modifiers & ALT_KEY);
+ this.ctrlKey = !!(modifiers & CTRL_KEY);
+ this.metaKey = !!(modifiers & META_KEY);
+ this.shiftKey = !!(modifiers & SHIFT_KEY);
+ }
+
+ /**
+ * Creates a KeyboardEvent from a DOM event
+ *
+ * @param {string} type The event type
+ * @param {Object} keys A keyboard event
+ * @param {string} [targetValue] The current value of an associated input
+ * @returns {KeyboardEvent}
+ */
+ KeyboardEvent.fromDomEvent = function(type, keys, targetValue) {
+ var modifiers =
+ !!keys.altKey * ALT_KEY |
+ !!keys.ctrlKey * CTRL_KEY |
+ !!keys.metaKey * META_KEY |
+ !!keys.shiftKey * SHIFT_KEY;
+ return new KeyboardEvent(undefined, keys.keyCode, modifiers, targetValue);
+ };
+ KeyboardEvent.NO_MODIFIER = NO_MODIFIER;
+ KeyboardEvent.ALT_KEY = ALT_KEY;
+ KeyboardEvent.CTRL_KEY = CTRL_KEY;
+ KeyboardEvent.META_KEY = META_KEY;
+ KeyboardEvent.SHIFT_KEY = SHIFT_KEY;
+
+ /**
+ * Represents a pointer event (either mouse or touch)
+ *
+ * @param {string} type The event type
+ * @param {number} stageX The x offset relative to the bonsai stage
+ * @param {number} stageY The y offset relative to the bonsai stage
+ * @param {number} clientX The x offset relative to the viewport
+ * @param {number} clientY The y offset relative to the viewport
+ * @constructor
+ */
+ function PointerEvent(type, stageX, stageY, clientX, clientY) {
+ this.type = type;
+ this.stageX = this.x = stageX;
+ this.stageY = this.y = stageY;
+ this.clientX = clientX;
+ this.clientY = clientY;
+ this.deltaX = this.deltaY =
+ this.diffX = this.diffY =
+ this.touchId = this.touchIndex = undefined;
+ }
+ /**
+ * Creates a PointerEvent from a DOM MouseEvent or TouchEvent
+ *
+ * @param {string} domType The type of the DOM event
+ * @param {Object} clientOffsets An object with "clientX" and "clientY" properties
+ * @param {number} stageClientX The x offset of the stage relative to the viewport.
+ * @param {number} stageClientY The y offset of the stage relative to the viewport.
+ * @return {PointerEvent}
+ */
+ PointerEvent.fromDomEvent = function(domType, clientOffsets, stageClientX, stageClientY) {
+ var clientX = clientOffsets.clientX;
+ var clientY = clientOffsets.clientY;
+ return new PointerEvent(undefined, clientX - stageClientX, clientY - stageClientY, clientX, clientY);
+ };
+
+ return {
+ KeyboardEvent: KeyboardEvent,
+ PointerEvent: PointerEvent
+ };
+});
@@ -0,0 +1,185 @@
+define([
+ 'bonsai/renderer/event'
+], function(event) {
+ 'use strict';
+
+ function memoize(func) {
+ var cache = {};
+ return function(param) {
+ return (param in cache) ? cache[param] : (cache[param] = func(param));
+ }
+ }
+
+ var getConstructCall = memoize(function(numParameters) {
+ var parameters = [];
+ for (var i = 0; i < numParameters; i += 1) {
+ parameters[i] = 'args[' + i + ']';
+ }
+ var body = 'return new Constructor(' + parameters.join(', ') + ')';
+ return Function('Constructor', 'args', body);
+ });
+
+ function parameterToPropertyTest(Constructor, args, propertyName, parameterIndex) {
+ var construct = getConstructCall(args.length);
+ expect(construct(Constructor, args)[propertyName]).toEqual(args[parameterIndex]);
+ }
+
+ describe('renderer events', function() {
+ describe('PointerEvent', function() {
+ var PointerEvent = event.PointerEvent;
+
+ describe('Constructor', function() {
+ var type = 'arbitrary';
+ var stageX = 15;
+ var stageY = 107;
+ var clientX = 32;
+ var clientY = 1028;
+ it('should initialize properties from parameters', function() {
+ expect(new PointerEvent(type, stageX, stageY, clientX, clientY))
+ .toHaveOwnProperties({
+ type: type,
+ stageX: stageX,
+ x: stageX,
+ stageY: stageY,
+ y: stageY,
+ clientX: clientX,
+ clientY: clientY
+ });
+ });
+
+ it('should initialize the "touchId" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.touchId).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('touchId')
+ });
+
+ it('should initialize the "touchIndex" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.touchIndex).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('touchIndex')
+ });
+
+ it('should initialize the "diffX" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.diffX).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('diffX')
+ });
+
+ it('should initialize the "diffY" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.diffY).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('diffY')
+ });
+
+ it('should initialize the "deltaX" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.deltaX).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('deltaX')
+ });
+
+ it('should initialize the "deltaY" property to undefined', function() {
+ var pointerEvent = new PointerEvent(type, stageX, stageY, clientX, clientY);
+ expect(pointerEvent.deltaY).toBe(undefined);
+ expect(pointerEvent).toHaveOwnProperties('deltaY')
+ });
+ });
+
+ describe('.fromDomEvent', function() {
+ var clientX = 123, clientY = 456;
+ var stageOffsetX = 78, stageOffsetY = 90;
+
+ it('should initialize a PointerEvent from an object that has client offsets (like a mouse event or a touch)', function() {
+ var mouseEventOrTouch = {clientX: clientX, clientY: clientY};
+ expect(PointerEvent.fromDomEvent('arbitrary', mouseEventOrTouch, stageOffsetX, stageOffsetY))
+ .toHaveOwnProperties({
+ type: undefined,
+ stageX: clientX - stageOffsetX,
+ x: clientX - stageOffsetX,
+ stageY: clientY - stageOffsetY,
+ y: clientY - stageOffsetY,
+ clientX: clientX,
+ clientY: clientY
+ });
+ });
+ });
+ });
+
+ describe('KeyboardEvent', function() {
+ var KeyboardEvent = event.KeyboardEvent;
+ var modifiers = 0;
+ describe('Constructor', function() {
+ it('should initialize properties from parameters', function() {
+ var type = 'arbitrary', keyCode = 0x123;
+ var modifiers = KeyboardEvent.NO_MODIFIER, targetValue = 'arbitrary value';
+ expect(new KeyboardEvent(type, keyCode, modifiers, targetValue)).toHaveOwnProperties({
+ type: type,
+ keyCode: keyCode,
+ inputValue: targetValue
+ });
+ });
+
+ describe('modifier keys', function() {
+ function testModifiers(modifiers, property0, property1) {
+ var expectedProperties = {
+ altKey: false,
+ ctrlKey: false,
+ metaKey: false,
+ shiftKey: false
+ };
+ for (var i = 1, property; (property = arguments[i]); i += 1) {
+ expectedProperties[property] = true;
+ }
+
+ expect(new KeyboardEvent('arbitrary', 0x123, modifiers))
+ .toHaveOwnProperties(expectedProperties);
+ }
+
+ it('should initialize the "altKey" property from the "modifiers" parameter', function() {
+ testModifiers(KeyboardEvent.ALT_KEY, 'altKey');
+ });
+ it('should initialize the "ctrlKey" property from the "modifiers" parameter', function() {
+ testModifiers(KeyboardEvent.CTRL_KEY, 'ctrlKey');
+ });
+ it('should initialize the "metaKey" property from the "modifiers" parameter', function() {
+ testModifiers(KeyboardEvent.META_KEY, 'metaKey');
+ });
+ it('should initialize the "shiftKey" property from the "modifiers" parameter', function() {
+ testModifiers(KeyboardEvent.SHIFT_KEY, 'shiftKey');
+ });
+ it('should initialize a combination of two modifiers as expected', function() {
+ testModifiers(KeyboardEvent.SHIFT_KEY | KeyboardEvent.META_KEY, 'metaKey', 'shiftKey')
+ });
+ it('should initialize the combination of all modifiers correctly', function() {
+ var modifiers =
+ KeyboardEvent.ALT_KEY| KeyboardEvent.CTRL_KEY |
+ KeyboardEvent.META_KEY | KeyboardEvent.SHIFT_KEY;
+ testModifiers(modifiers, 'altKey', 'ctrlKey', 'metaKey', 'shiftKey');
+ });
+ });
+ });
+
+ describe('.fromDomEvent', function() {
+ it('should populate the event with the corresponding properties from the dom event', function() {
+ var targetValue = 'arbitrary value';
+ var keyCode = 0x123;
+ var altKey = true;
+ var ctrlKey = false;
+ var metaKey = true;
+ var shiftKey = false;
+ var keys = {keyCode: keyCode, altKey: altKey, ctrlKey: ctrlKey, metaKey: metaKey, shiftKey: shiftKey};
+
+ expect(KeyboardEvent.fromDomEvent('arbitrary', keys, targetValue))
+ .toHaveOwnProperties({
+ type: undefined,
+ keyCode: keyCode,
+ altKey: altKey,
+ ctrlKey: ctrlKey,
+ metaKey: metaKey,
+ shiftKey: shiftKey,
+ inputValue: targetValue
+ });
+ });
+ });
+ });
+ });
+});
View
@@ -82,6 +82,9 @@
// events
'./ui_event-spec',
+ // renderer
+ './renderer/event-spec',
+
// svg renderer
'./renderer/svg-spec',
'./renderer/svg_filters-spec',

0 comments on commit c327866

Please sign in to comment.