Skip to content

Commit

Permalink
Start to break events out of the svg renderer
Browse files Browse the repository at this point in the history
- for future reusability with other renderers
- to increase code organization and legibility
  • Loading branch information
David Aurelio committed Feb 26, 2013
1 parent 05f3e31 commit c327866
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/renderer/event.js
@@ -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
};
});
185 changes: 185 additions & 0 deletions test/renderer/event-spec.js
@@ -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
});
});
});
});
});
});
3 changes: 3 additions & 0 deletions test/runner.html
Expand Up @@ -82,6 +82,9 @@
// events
'./ui_event-spec',

// renderer
'./renderer/event-spec',

// svg renderer
'./renderer/svg-spec',
'./renderer/svg_filters-spec',
Expand Down

0 comments on commit c327866

Please sign in to comment.