Skip to content

Commit

Permalink
Made the MouseHandler disposable.
Browse files Browse the repository at this point in the history
- This involved tracking all dom bound events so that they could then be unbound on dispose.

#48
  • Loading branch information
NTaylorMullen committed Aug 1, 2013
1 parent 4e028be commit 5bb7d8f
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 24 deletions.
65 changes: 55 additions & 10 deletions EndGate/EndGate.Core.JS/Input/Mouse/MouseHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ var EndGate;
(function (EndGate) {
/// <reference path="../../Assets/Vectors/Vector2d.ts" />
/// <reference path="../../Utilities/EventHandler1.ts" />
/// <reference path="../../Interfaces/IDisposable.ts" />
/// <reference path="MouseButton.ts" />
/// <reference path="IMouseEvent.ts" />
/// <reference path="IMouseClickEvent.ts" />
Expand All @@ -18,6 +19,7 @@ var EndGate;
function MouseHandler(target) {
var _this = this;
this._target = target;
this._disposed = false;

this._onClick = new EndGate.EventHandler1();
this._onDoubleClick = new EndGate.EventHandler1();
Expand Down Expand Up @@ -153,29 +155,72 @@ var EndGate;
configurable: true
});

/**
* Disposes the MouseHandler and unbinds all bound events.
*/
MouseHandler.prototype.Dispose = function () {
if (!this._disposed) {
this._disposed = true;

this._onClick.Dispose();
this._onDoubleClick.Dispose();
this._onDown.Dispose();
this._onMove.Dispose();
this._onScroll.Dispose();
this._onUp.Dispose();

this.Unwire();

this._target = null;
} else {
throw new Error("MouseHandler cannot be disposed more than once");
}
};

MouseHandler.prototype.Wire = function () {
var _this = this;
this._target.addEventListener("click", this._target.oncontextmenu = this.BuildEvent(this._onClick, this.BuildMouseClickEvent), false);
this._target.addEventListener("dblclick", this.BuildEvent(this._onDoubleClick, this.BuildMouseClickEvent), false);
this._target.addEventListener("mousedown", this.BuildEvent(this._onDown, this.BuildMouseClickEvent), false);
this._target.addEventListener("mouseup", this.BuildEvent(this._onUp, this.BuildMouseClickEvent), false);
this._target.addEventListener("mousemove", this.BuildEvent(this._onMove, this.BuildMouseEvent), false);
this._clickWire = this._contextMenuWire = this.BuildEvent(this._onClick, this.BuildMouseClickEvent);
this._dblClickWire = this.BuildEvent(this._onDoubleClick, this.BuildMouseClickEvent);
this._mouseDownWire = this.BuildEvent(this._onDown, this.BuildMouseClickEvent);
this._mouseUpWire = this.BuildEvent(this._onUp, this.BuildMouseClickEvent);
this._mouseMoveWire = this.BuildEvent(this._onMove, this.BuildMouseEvent);

if ((/MSIE/i.test(navigator.userAgent))) {
this._target.addEventListener("wheel", this.BuildEvent(this._onScroll, function (e) {
this._mouseWheelWireName = "wheel";
this._mouseWheelWire = this.BuildEvent(this._onScroll, function (e) {
e.wheelDeltaX = -e.deltaX;
e.wheelDeltaY = -e.deltaY;
return _this.BuildMouseScrollEvent(e);
}), false);
});
} else if ((/Firefox/i.test(navigator.userAgent))) {
this._target.addEventListener("DOMMouseScroll", this.BuildEvent(this._onScroll, function (e) {
this._mouseWheelWireName = "DOMMouseScroll";
this._mouseWheelWire = this.BuildEvent(this._onScroll, function (e) {
e.wheelDeltaX = e.axis === 1 ? -e.detail : 0;
e.wheelDeltaY = e.axis === 2 ? -e.detail : 0;
return _this.BuildMouseScrollEvent(e);
}), false);
});
} else {
this._target.addEventListener("mousewheel", this.BuildEvent(this._onScroll, this.BuildMouseScrollEvent), false);
this._mouseWheelWireName = "mousewheel";
this._mouseWheelWire = this.BuildEvent(this._onScroll, this.BuildMouseScrollEvent);
}

this._target.addEventListener("click", this._clickWire, false);
this._target.addEventListener("contextmenu", this._contextMenuWire, false);
this._target.addEventListener("dblclick", this._dblClickWire, false);
this._target.addEventListener("mousedown", this._mouseDownWire, false);
this._target.addEventListener("mouseup", this._mouseUpWire, false);
this._target.addEventListener("mousemove", this._mouseMoveWire, false);
this._target.addEventListener(this._mouseWheelWireName, this._mouseWheelWire, false);
};

MouseHandler.prototype.Unwire = function () {
this._target.removeEventListener("click", this._clickWire, false);
this._target.removeEventListener("contextmenu", this._contextMenuWire, false);
this._target.removeEventListener("dblclick", this._dblClickWire, false);
this._target.removeEventListener("mousedown", this._mouseDownWire, false);
this._target.removeEventListener("mouseup", this._mouseUpWire, false);
this._target.removeEventListener("mousemove", this._mouseMoveWire, false);
this._target.removeEventListener(this._mouseWheelWireName, this._mouseWheelWire, false);
};

MouseHandler.prototype.BuildEvent = function (eventHandler, mouseEventBuilder, returnValue) {
Expand Down
85 changes: 71 additions & 14 deletions EndGate/EndGate.Core.JS/Input/Mouse/MouseHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference path="../../Assets/Vectors/Vector2d.ts" />
/// <reference path="../../Utilities/EventHandler1.ts" />
/// <reference path="../../Interfaces/IDisposable.ts" />
/// <reference path="MouseButton.ts" />
/// <reference path="IMouseEvent.ts" />
/// <reference path="IMouseClickEvent.ts" />
Expand All @@ -10,7 +11,7 @@ module EndGate.Input {
/**
* Defines a handler that will monitor mouse events over a specified area and will execute appropriate functions based on the events.
*/
export class MouseHandler {
export class MouseHandler implements IDisposable {
// Used to determine mouse buttons without using extra conditional statements, performance enhancer
private static MouseButtonArray = [null, _.MouseButton.Left, _.MouseButton.Middle, _.MouseButton.Right];

Expand All @@ -30,12 +31,24 @@ module EndGate.Input {

private _target: HTMLElement;

// For disposing
private _contextMenuWire: (e: MouseEvent) => void;
private _clickWire: (e: MouseEvent) => void;
private _dblClickWire: (e: MouseEvent) => void;
private _mouseDownWire: (e: MouseEvent) => void;
private _mouseUpWire: (e: MouseEvent) => void;
private _mouseMoveWire: (e: MouseEvent) => void;
private _mouseWheelWireName: string;
private _mouseWheelWire: (e: MouseEvent) => void;
private _disposed: boolean;

/**
* Creates a new instance of the MouseHandler object.
* @param target The object to monitor mouse events for.
*/
constructor(target: HTMLElement) {
this._target = target;
this._disposed = false;

this._onClick = new EventHandler1<IMouseClickEvent>();
this._onDoubleClick = new EventHandler1<IMouseClickEvent>();
Expand All @@ -46,7 +59,7 @@ module EndGate.Input {

// Generic flags to check mouse state
this._leftIsDown = false;
this._middleIsDown= false;
this._middleIsDown = false;
this._rightIsDown = false;

this.Wire();
Expand Down Expand Up @@ -132,32 +145,76 @@ module EndGate.Input {
return this._onScroll;
}

/**
* Disposes the MouseHandler and unbinds all bound events.
*/
public Dispose(): void {
if (!this._disposed) {
this._disposed = true;

this._onClick.Dispose();
this._onDoubleClick.Dispose();
this._onDown.Dispose();
this._onMove.Dispose();
this._onScroll.Dispose();
this._onUp.Dispose();

this.Unwire();

this._target = null;
}
else {
throw new Error("MouseHandler cannot be disposed more than once");
}
}

private Wire(): void {
this._target.addEventListener("click",this._target.oncontextmenu = this.BuildEvent<IMouseClickEvent>(this._onClick, this.BuildMouseClickEvent),false);
this._target.addEventListener("dblclick", this.BuildEvent<IMouseClickEvent>(this._onDoubleClick, this.BuildMouseClickEvent), false);
this._target.addEventListener("mousedown", this.BuildEvent<IMouseClickEvent>(this._onDown, this.BuildMouseClickEvent), false);
this._target.addEventListener("mouseup", this.BuildEvent<IMouseClickEvent>(this._onUp, this.BuildMouseClickEvent), false);
this._target.addEventListener("mousemove", this.BuildEvent<IMouseEvent>(this._onMove, this.BuildMouseEvent), false);
this._clickWire = this._contextMenuWire = this.BuildEvent<IMouseClickEvent>(this._onClick, this.BuildMouseClickEvent);
this._dblClickWire = this.BuildEvent<IMouseClickEvent>(this._onDoubleClick, this.BuildMouseClickEvent);
this._mouseDownWire = this.BuildEvent<IMouseClickEvent>(this._onDown, this.BuildMouseClickEvent);
this._mouseUpWire = this.BuildEvent<IMouseClickEvent>(this._onUp, this.BuildMouseClickEvent)
this._mouseMoveWire = this.BuildEvent<IMouseEvent>(this._onMove, this.BuildMouseEvent);

// OnScroll, in order to detect horizontal scrolling need to hack a bit (browser sniffing)
// if we were just doing vertical scrolling we could settle with the else statement in this block
if ((/MSIE/i.test(navigator.userAgent))) {
this._target.addEventListener("wheel", this.BuildEvent<IMouseScrollEvent>(this._onScroll, (e: any) => {
this._mouseWheelWireName = "wheel";
this._mouseWheelWire = this.BuildEvent<IMouseScrollEvent>(this._onScroll, (e: any) => {
e.wheelDeltaX = -e.deltaX;
e.wheelDeltaY = -e.deltaY;
return this.BuildMouseScrollEvent(e);
}), false);
});
}
else if ((/Firefox/i.test(navigator.userAgent))) {
this._target.addEventListener("DOMMouseScroll", this.BuildEvent<IMouseScrollEvent>(this._onScroll, (e: any) => {
this._mouseWheelWireName = "DOMMouseScroll";
this._mouseWheelWire = this.BuildEvent<IMouseScrollEvent>(this._onScroll, (e: any) => {
e.wheelDeltaX = e.axis === 1 ? -e.detail : 0;
e.wheelDeltaY = e.axis === 2 ? -e.detail : 0;
return this.BuildMouseScrollEvent(e);
}), false);
});
}
else {
this._target.addEventListener("mousewheel", this.BuildEvent<IMouseScrollEvent>(this._onScroll, this.BuildMouseScrollEvent), false);
this._mouseWheelWireName = "mousewheel";
this._mouseWheelWire = this.BuildEvent<IMouseScrollEvent>(this._onScroll, this.BuildMouseScrollEvent);
}

this._target.addEventListener("click", this._clickWire, false);
this._target.addEventListener("contextmenu", this._contextMenuWire, false);
this._target.addEventListener("dblclick", this._dblClickWire, false);
this._target.addEventListener("mousedown", this._mouseDownWire, false);
this._target.addEventListener("mouseup", this._mouseUpWire, false);
this._target.addEventListener("mousemove", this._mouseMoveWire, false);
this._target.addEventListener(this._mouseWheelWireName, this._mouseWheelWire, false);
}

private Unwire(): void {
this._target.removeEventListener("click", this._clickWire, false);
this._target.removeEventListener("contextmenu", this._contextMenuWire, false);
this._target.removeEventListener("dblclick", this._dblClickWire, false);
this._target.removeEventListener("mousedown", this._mouseDownWire, false);
this._target.removeEventListener("mouseup", this._mouseUpWire, false);
this._target.removeEventListener("mousemove", this._mouseMoveWire, false);
this._target.removeEventListener(this._mouseWheelWireName, this._mouseWheelWire, false);
}

private BuildEvent<T>(eventHandler: EventHandler1<T>, mouseEventBuilder: (mouseEvent: MouseEvent) => IMouseEvent, returnValue: boolean = false): (e: MouseEvent) => void {
Expand Down Expand Up @@ -195,7 +252,7 @@ module EndGate.Input {
return new Vector2d(
event.offsetX ? (event.offsetX) : event.pageX - this._target.offsetLeft,
event.offsetY ? (event.offsetY) : event.pageY - this._target.offsetTop
);
);
}

private GetMouseButton(event: MouseEvent): string {
Expand All @@ -206,7 +263,7 @@ module EndGate.Input {
return _.MouseButton.Right;
}

private GetMouseScrollDierction(event: any): Vector2d{
private GetMouseScrollDierction(event: any): Vector2d {
return new Vector2d(-Math.max(-1, Math.min(1, event.wheelDeltaX)), -Math.max(-1, Math.min(1, event.wheelDeltaY)));
}
}
Expand Down

0 comments on commit 5bb7d8f

Please sign in to comment.