-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhit_regions.js
156 lines (130 loc) · 4.42 KB
/
hit_regions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
* Hand-rolled hit-region implementation, as the version in the Canvas spec isn't supported yet.
* Currently only supports rectangular regions.
*/
define([
'jquery'
],
function($) {
var hitRegions = function(canvas) {
/*
* The mouse events hit regions are sensitive to.
*
* TODO: MDN recommends only listening for mousemove when we actually care about it.
*/
var SUPPORTED_EVENTS = ['click', 'mousedown', 'mousemove', 'mouseup', 'mouseleave'];
// Define a type for hit regions.
var Region = (function() {
var Region = function(x, y, width, height) {
this.x1 = x;
this.y1 = y;
this.x2 = x + width;
this.y2 = y + height;
var listeners = {}
$.each(SUPPORTED_EVENTS, function(_index, event) {
listeners[event] = [];
});
this.listeners = listeners;
};
/*
* Add an event listener to the hit region.
* See SUPPORTED_EVENTS for available event names.
*
* Listeners will be passed a synthetic event object.
* Currently, its only properties are x and y, which are relative to the _canvas_
* (not to the document, as the underlying JQuery event's coordinates are).
*/
Region.prototype.addListener = function(eventName, listener) {
this.listeners[eventName].push(listener);
};
/*
* Fire an event with the given name.
*/
Region.prototype.fire = function(eventName, event) {
$.each(this.listeners[eventName], function(_index, listener) {
listener(event);
});
};
return Region;
})();
var hitRegionsKey = 'mapView.hitRegions';
var $canvas = $(canvas);
if ($canvas.data(hitRegionsKey) === undefined) {
$canvas.data(hitRegionsKey, {
regions: [],
// Set region coordinates to be blank, as we only really need the event handling.
// TODO: extracted a separate type for things that just dispatch events.
fallbackRegion: new Region(-1, -1, -1, -1)
});
}
var fireEvent = function(name, event) {
var offset = $canvas.offset();
var canvasX = event.pageX - offset.left;
var canvasY = event.pageY - offset.top;
_fire(name, canvasX, canvasY);
};
/**
* Fire an event as though it occurred at the specified point on the canvas.
* Exposed as its own method for testing.
*/
var _fire = function(name, canvasX, canvasY) {
var regionData = $canvas.data(hitRegionsKey);
var hit = false;
$.each(regionData.regions, function(_index, region) {
if (region.x1 < canvasX && canvasX < region.x2 &&
region.y1 < canvasY && canvasY < region.y2) {
region.fire(name, {x: canvasX, y: canvasY});
hit = true;
}
});
// Fire an event on the fallback region if nothing was clicked.
if (!hit) {
regionData.fallbackRegion.fire(name, {x: canvasX, y: canvasY});
};
};
/*
* Add a hit region with the specified dimensions.
* Returns an object representing that region.
*/
var add = function(x, y, width, height) {
var region = new Region(x, y, width, height);
$canvas.data(hitRegionsKey).regions.push(region);
return region;
};
/*
* Return a reference to the "fallback" hit region,
* which is notified of events no other region handles
* (i.e. clicks on something not in any region).
* TODO: use JS's properties support for this.
*/
var getFallback = function() {
return $canvas.data(hitRegionsKey).fallbackRegion;
};
/*
* Remove all hit regions, and re-create listeners.
* TODO: consider ways to only install the listener once.
*/
var reset = function() {
// TODO: duplicated from the initialisation code.
$canvas.data(hitRegionsKey, {
regions: [],
// Set region coordinates to be blank, as we only really need the event handling.
// TODO: extracted a separate type for things that just dispatch events.
fallbackRegion: new Region(-1, -1, -1, -1)
});
$.each(SUPPORTED_EVENTS, function(_index, eventName) {
$canvas.off(eventName);
$canvas.on(eventName, function(event) {
fireEvent(eventName, event);
});
});
};
return {
add : add,
reset : reset,
getFallback : getFallback,
_fire: _fire
};
};
return hitRegions;
});