/**
* Managers inputs, giving an easy to use interface.
* An input manager can only be listening to a single renderer
* at once.
*
* @param {!Grape2D.Renderer} renderer Renderer to listen.
* @constructor
*/
Grape2D.InputManager = function(renderer) {
/**
* Mouse up callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseUp = [];
/**
* Mouse down callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseDown = [];
/**
* Mouse move callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseMove = [];
/**
* Mouse over callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseOver = [];
/**
* Mouse out callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseOut = [];
/**
* Mouse wheel callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.mouseWheel = [];
/**
* Click callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.click = [];
/**
* Drag callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.drag = [];
/**
* Start dragging position.
*
* @type {!Grape2D.Vector}
* @private
*/
this.dragStart = new Grape2D.Vector();
/**
* Dragging state.
*
* @type {!boolean}
* @private
*/
this.isDragging = false;
/**
* Resize callback stack.
*
* @type {!Array.<Function>}
* @private
*/
this.resize = [];
/**
* Key down callback stack.
*
* @type {!Object.<number, !Array.<Function>>}
* @private
*/
this.keyDown = {};
/**
* Key up callback stack.
*
* @type {!Object.<number, !Array.<Function>>}
* @private
*/
this.keyUp = {};
/**
* Key press callback stack.
*
* @type {!Object.<number, !Array.<Function>>}
* @private
*/
this.keyPress = {};
/**
* Renderer
*
* @type {!Grape2D.Renderer}
* @private
*/
this.rendererBinding = renderer;
//binds this input manager to the renderer.
this.bindToRenderer(renderer);
};
Grape2D.InputManager.prototype = {
constructor: Grape2D.InputManager,
/**
* Binds the renderer to this input manager. Sets up helper functions
* and callbacks.
*
* @param {!Grape2D.Renderer} renderer The renderer to listen.
* @public
*/
bindToRenderer: function(renderer) {
//if there is a rendererBinding then remove events
this.rendererBinding = renderer;
/**
* DOM Element
*
* @type {!Element}
*/
var dom = this.rendererBinding.getDOMElement();
var that = this;
dom.addEventListener('mouseup', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseUp), false);
dom.addEventListener('mousedown', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseDown), false);
dom.addEventListener('mousemove', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseMove), false);
dom.addEventListener('mouseover', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseOver), false);
dom.addEventListener('mouseout', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseOut), false);
dom.addEventListener('mousewheel', Grape2D.InputManager.bindFn(this.rendererBinding, this.mouseWheel), false);
dom.addEventListener('click', Grape2D.InputManager.bindFn(this.rendererBinding, this.click), false);
//add helper fns for drag
this.addMouseDown(function(e) {
that.dragStart.set(e.getPosition());
that.isDragging = true;
});
this.addMouseMove(function(e) {
if (that.isDragging) {
var end = new Grape2D.Vector(e.getRaw().pageX, e.getRaw().pageY),
ev = new Grape2D.InputManagerDragEvent(e.getRaw(), that.dragStart),
list = that.getDragBindStack();
for (var i = 0; i < list.length; i++) {
list[i](ev);
}
that.dragStart.set(end);
}
});
this.addMouseUp(function(e) {
that.isDragging = false;
});
this.addMouseOut(function(e) {
that.isDragging = false;
});
},
/**
* Gets the mouse up callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseUpBindStack: function() {
return this.mouseUp;
},
/**
* Adds a callback to the mouse up event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseUp: function(callback) {
this.mouseUp.push(callback);
},
/**
* Removes a callback from the mouse up callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseUp: function(callback) {
var indx = this.mouseUp.indexOf(callback);
if (indx >= 0) {
this.mouseUp.splice(this.mouseUp.indexOf(callback), 1);
}
},
/**
* Gets the mouse down callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseDownBindStack: function() {
return this.mouseDown;
},
/**
* Adds a callback to the mouse down event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseDown: function(callback) {
this.mouseDown.push(callback);
},
/**
* Removes a callback from the mouse down callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseDown: function(callback) {
var indx = this.mouseDown.indexOf(callback);
if (indx >= 0) {
this.mouseDown.splice(this.mouseDown.indexOf(callback), 1);
}
},
/**
* Gets the mouse move callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseMoveBindStack: function() {
return this.mouseMove;
},
/**
* Adds a callback to the mouse move event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseMove: function(callback) {
this.mouseMove.push(callback);
},
/**
* Removes a callback from the mouse move callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseMove: function(callback) {
var indx = this.mouseMove.indexOf(callback);
if (indx >= 0) {
this.mouseMove.splice(this.mouseMove.indexOf(callback), 1);
}
},
/**
* Gets the mouse over callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseOverBindStack: function() {
return this.mouseOver;
},
/**
* Adds a callback to the mouse over event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseOver: function(callback) {
this.mouseOver.push(callback);
},
/**
* Removes a callback from the mouse over callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseOver: function(callback) {
var indx = this.mouseOver.indexOf(callback);
if (indx >= 0) {
this.mouseOver.splice(this.mouseOver.indexOf(callback), 1);
}
},
/**
* Gets the mouse out callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseOutBindStack: function() {
return this.mouseOut;
},
/**
* Adds a callback to the mouse out event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseOut: function(callback) {
this.mouseOut.push(callback);
},
/**
* Removes a callback from the mouse out callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseOut: function(callback) {
var indx = this.mouseOut.indexOf(callback);
if (indx >= 0) {
this.mouseOut.splice(this.mouseOut.indexOf(callback), 1);
}
},
/**
* Gets the mouse wheel callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getMouseWheelBindStack: function() {
return this.mouseWheel;
},
/**
* Adds a callback to the mouse wheel event.
*
* @param {!Function} callback Callback function.
* @public
*/
addMouseWheel: function(callback) {
this.mouseWheel.push(callback);
},
/**
* Removes a callback from the mouse wheel callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeMouseWheel: function(callback) {
var indx = this.mouseWheel.indexOf(callback);
if (indx >= 0) {
this.mouseWheel.splice(this.mouseWheel.indexOf(callback), 1);
}
},
/**
* Gets the resize callback stack, that is binded to
* the renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getResizeBindStack: function() {
return this.resize;
},
/**
* Adds a callback to the resize event.
*
* @param {!Function} callback Callback function.
* @public
*/
addResize: function(callback) {
this.resize.push(callback);
Grape2D.InputManager.registerGlobalResize(callback);
},
/**
* Removes a callback from the resize callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeResize: function(key, callback) {
var indx = this.resize.indexOf(callback);
if (indx >= 0) {
this.resize.splice(this.resize.indexOf(callback), 1);
}
Grape2D.InputManager.unregisterGlobalResize(callback);
},
/**
* Gets the key down callback stack, that is binded to the
* renderer.
*
* @return {!Object.<number, !Array.<Function>>} Callback stack.
* @public
*/
getKeyDownBindStack: function() {
return this.keyDown;
},
/**
* Adds a callback to the key down event.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback function.
* @public
*/
addKeyDown: function(key, callback) {
if (!this.keyDown[key]) {
this.keyDown[key] = [];
}
this.keyDown[key].push(callback);
Grape2D.InputManager.registerGlobalKeyDown(key, callback);
},
/**
* Removes a callback from the key down callback stack.
*
* @param {!number} key Key code of the key that triggers the
* callback.
* @param {!Function} callback Function to remove
* @public
*/
removeKeyDown: function(key, callback) {
if (!this.keyDown[key]) {
return;
}
var indx = this.keyDown[key].indexOf(callback);
if (indx >= 0) {
this.keyDown[key].splice(indx, 1);
Grape2D.InputManager.unregisterGlobalKeyDown(key, callback);
}
},
/**
* Gets the key up callback stack, that is binded to the
* renderer.
*
* @return {!Object.<number, !Array.<Function>>} Callback stack.
* @public
*/
getKeyUpBindStack: function() {
return this.keyDown;
},
/**
* Adds a callback to the key up event.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback function.
* @public
*/
addKeyUp: function(key, callback) {
if (!this.keyUp[key]) {
this.keyUp[key] = [];
}
this.keyUp[key].push(callback);
Grape2D.InputManager.registerGlobalKeyUp(key, callback);
},
/**
* Removes a callback from the key up callback stack.
*
* @param {!number} key Key code of the key that triggers the
* callback.
* @param {!Function} callback Function to remove
* @public
*/
removeKeyUp: function(key, callback) {
if (!this.keyUp[key]) {
return;
}
var indx = this.keyUp[key].indexOf(callback);
if (indx >= 0) {
this.keyUp[key].splice(indx, 1);
Grape2D.InputManager.unregisterGlobalKeyUp(key, callback);
}
},
/**
* Gets the key press callback stack, that is binded to the
* renderer.
*
* @return {!Object.<number, !Array.<Function>>} Callback stack.
* @public
*/
getKeyPressBindStack: function() {
return this.keyPress;
},
/**
* Adds a callback to the key press event.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback function.
* @public
*/
addKeyPress: function(key, callback) {
if (!this.keyPress[key]) {
this.keyPress[key] = [];
}
this.keyPress[key].push(callback);
Grape2D.InputManager.registerGlobalKeyPress(key, callback);
},
/**
* Removes a callback from the key press callback stack.
*
* @param {!number} key Key code of the key that triggers the
* callback.
* @param {!Function} callback Function to remove
* @public
*/
removeKeyPress: function(key, callback) {
if (!this.keyPress[key]) {
return;
}
var indx = this.keyPress[key].indexOf(callback);
if (indx >= 0) {
this.keyPress[key].splice(indx, 1);
Grape2D.InputManager.unregisterGlobalKeyPress(key, callback);
}
},
/**
* Gets the drag callback stack, that is binded to the
* renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getDragBindStack: function() {
return this.drag;
},
/**
* Adds a callback to the drag event.
*
* @param {!Function} callback Callback function.
* @public
*/
addDrag: function(callback) {
this.drag.push(callback);
},
/**
* Removes a callback from the drag callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeDrag: function(callback) {
var indx = this.drag.indexOf(callback);
if (indx >= 0) {
this.drag.splice(this.drag.indexOf(callback), 1);
}
},
/**
* Gets the click callback stack, that is binded to the
* renderer.
*
* @return {!Array.<Function>} Callback stack.
* @public
*/
getClickBindStack: function() {
return this.click;
},
/**
* Adds a callback to the click event.
*
* @param {!Function} callback Callback function.
* @public
*/
addClick: function(callback) {
this.click.push(callback);
},
/**
* Removes a callback from the click callback stack.
*
* @param {!Function} callback Function to remove
* @public
*/
removeClick: function(callback) {
var indx = this.click.indexOf(callback);
if (indx >= 0) {
this.click.splice(this.click.indexOf(callback), 1);
}
}
};
/**
* This function creates a callback function to be
* called when a keyboard event occurs.
*
* @param {!Grape2D.Renderer} binding Renderer where the input
* manager is bound.
* @param {!Array.<Function>} stck List of references to an input
* manager callback stack.
* @return {!Function} Callback to an event.
* @protected
* @static
*/
Grape2D.InputManager.bindFn = function(binding, stck) {
var fn = function(ev) {
var i = 0,
evnt = new Grape2D.InputManagerMouseEvent(ev);
for (; i < stck.length; i++) {
stck[i](evnt);
}
};
return fn;
};
/**
* Registers a global callback function, to the resize event. A
* resize event is triggered by the window, not the renderer, binded.
*
* @param {!Function} callback Callback to register.
* @private
*/
Grape2D.InputManager.registerGlobalResize = function(callback) {
Grape2D.InputManager.globalRegistry.resize.push(callback);
};
/**
* Unregisters a global callback function, to the resize event, that
* has been already registered.
*
* @param {!Function} callback Callback to unregister.
* @private
*/
Grape2D.InputManager.unregisterGlobalResize = function(callback) {
var indx = Grape2D.InputManager.globalRegistry.resize.indexOf(callback);
if (indx >= 0) {
Grape2D.InputManager.globalRegistry.resize.splice(indx, 1);
}
};
/**
* Registers a global callback function, to the key down event. A
* key down event is triggered by the window, not the renderer,
* binded.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to register.
* @private
*/
Grape2D.InputManager.registerGlobalKeyDown = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyDown[key]) {
Grape2D.InputManager.globalRegistry.keyDown[key] = [];
}
Grape2D.InputManager.globalRegistry.keyDown[key].push(callback);
};
/**
* Unregisters a global callback function, to the key down event, that
* has been already registered.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to unregister.
* @private
*/
Grape2D.InputManager.unregisterGlobalKeyDown = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyDown[key]) {
return;
}
var indx = Grape2D.InputManager.globalRegistry.keyDown[key].indexOf(callback);
if (indx >= 0) {
Grape2D.InputManager.globalRegistry.keyDown[key].splice(indx, 1);
}
};
/**
* Registers a global callback function, to the key up event. A
* key up event is triggered by the window, not the renderer,
* binded.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to register.
* @private
*/
Grape2D.InputManager.registerGlobalKeyUp = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyUp[key]) {
Grape2D.InputManager.globalRegistry.keyUp[key] = [];
}
Grape2D.InputManager.globalRegistry.keyUp[key].push(callback);
};
/**
* Unregisters a global callback function, to the key up event, that
* has been already registered.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to unregister.
* @private
*/
Grape2D.InputManager.unregisterGlobalKeyUp = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyUp[key]) {
return;
}
var indx = Grape2D.InputManager.globalRegistry.keyUp[key].indexOf(callback);
if (indx >= 0) {
Grape2D.InputManager.globalRegistry.keyUp[key].splice(indx, 1);
}
};
/**
* Registers a global callback function, to the key press event. A
* key press event is triggered by the window, not the renderer,
* binded.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to register.
* @private
*/
Grape2D.InputManager.registerGlobalKeyPress = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyPress[key]) {
Grape2D.InputManager.globalRegistry.keyPress[key] = [];
}
Grape2D.InputManager.globalRegistry.keyPress[key].push(callback);
};
/**
* Unregisters a global callback function, to the key press event, that
* has been already registered.
*
* @param {!number} key Key code that triggers the callback.
* @param {!Function} callback Callback to unregister.
* @private
*/
Grape2D.InputManager.unregisterGlobalKeyPress = function(key, callback) {
if (!Grape2D.InputManager.globalRegistry.keyPress[key]) {
return;
}
var indx = Grape2D.InputManager.globalRegistry.keyPress[key].indexOf(callback);
if (indx >= 0) {
Grape2D.InputManager.globalRegistry.keyPress[key].splice(indx, 1);
}
};
/**
* Returns a function that dispatches the callbacks associated with
* key codes.
*
* @param {!Object.<number, Function>} stck Object with the callbacks
* associated with the key codes.
* @return {!Function} Dispatcher function.
* @private
*/
Grape2D.InputManager.keyDispatcher = function(stck) {
var kDispFn = function(ev) {
var key = ev.keyCode;
if (stck[key]) {
for (var i = 0; i < stck[key].length; i++) {
stck[key][i](ev);
}
}
};
return kDispFn;
};
/**
* Returns a function that dispatches the callbacks associated with
* resize event.
*
* @return {!Function} Dispatcher function.
* @private
*/
Grape2D.InputManager.resizeDispatcher = function() {
var rDispFn = function(ev) {
for (var i = 0; i < Grape2D.InputManager.globalRegistry.resize.length; i++) {
Grape2D.InputManager.globalRegistry.resize[i](ev);
}
};
return rDispFn;
};
/**
* Setups globals callbacks. It must be called once for this to work.
*
* @public
*/
Grape2D.InputManager.setupGlobals = function() {
if (!Grape2D.NODE) {
window.addEventListener('resize', Grape2D.InputManager.resizeDispatcher(), false);
window.addEventListener('keyup', Grape2D.InputManager.keyDispatcher(Grape2D.InputManager.globalRegistry.keyUp), false);
window.addEventListener('keydown', Grape2D.InputManager.keyDispatcher(Grape2D.InputManager.globalRegistry.keyDown), false);
window.addEventListener('keypress', Grape2D.InputManager.keyDispatcher(Grape2D.InputManager.globalRegistry.keyPress), false);
}
};
/**
* Registry of non-specific events.
*
* @type {!Object.<string, !(Array.<!Function>|Object.<number, Function>)>}
* @private
* @static
*/
Grape2D.InputManager.globalRegistry = {
resize: [],
keyDown: {},
keyUp: {},
keyPress: {}
};
/**
* Key map partially from {@link https://github.com/bitwalker/keys.js}
*
* @constant {!Object.<string, number>}
* @public
*/
Grape2D.InputManager.KEY = {
'A': 65,
'B': 66,
'C': 67,
'D': 68,
'E': 69,
'F': 70,
'G': 71,
'H': 72,
'I': 73,
'J': 74,
'K': 75,
'L': 76,
'M': 77,
'N': 78,
'O': 79,
'P': 80,
'Q': 81,
'R': 82,
'S': 83,
'T': 84,
'U': 85,
'V': 86,
'W': 87,
'X': 88,
'Y': 89,
'Z': 90,
'0': 48,
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
'Numpad 0': 96,
'Numpad 1': 97,
'Numpad 2': 98,
'Numpad 3': 99,
'Numpad 4': 100,
'Numpad 5': 101,
'Numpad 6': 102,
'Numpad 7': 103,
'Numpad 8': 104,
'Numpad 9': 105,
'F1': 112,
'F2': 113,
'F3': 114,
'F4': 115,
'F5': 116,
'F6': 117,
'F7': 118,
'F8': 119,
'F9': 120,
'F10': 121,
'F11': 122,
'F12': 123,
'Backspace': 8,
'Tab': 9,
'Enter': 13,
'SHIFT': 16,
'CTRL': 17,
'ALT': 18,
'META': 91,
'META_RIGHT': 93,
'Caps Lock': 20,
'Esc': 27,
'Spacebar': 32,
'Page Up': 33,
'Page Down': 34,
'End': 35,
'Home': 36,
'Left': 37,
'Up': 38,
'Right': 39,
'Down': 40,
'Insert': 45,
'Delete': 46,
'Num Lock': 144,
'ScrLk': 145,
'Pause/Break': 19,
};
//setup the global callbacks
Grape2D.InputManager.setupGlobals();