-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
180 lines (131 loc) · 4.18 KB
/
index.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//------------------------------
// Constants
//------------------------------
var RE_SELECTOR = /(?:^|,\s+)(\w+)\s?([^,]+)?/gi;
var RE_POINTER = /pointer(\w+)/gi;
//------------------------------
// Pointer
//------------------------------
Pointer = {
/*
* A map relating pointer event names to native event names. A placeholder
* for selectors is provided in the native event strings for any DOM
* selectors that follow the original pointer event name to be copied into.
*/
EVENT_MAP: {
'pointerenter': 'mouseenter {{selector}}',
'pointerleave': 'mouseleave {{selector}}',
'pointerclick': 'click {{selector}}',
'pointerdown': 'mousedown {{selector}}, touchstart {{selector}}',
'pointermove': 'mousemove {{selector}}, touchmove {{selector}}',
'pointerup': 'mouseup {{selector}}, touchend {{selector}}'
},
/*
* Searches the provided event object for a populated TouchList and returns
* the result.
*/
getTouches: function(e) {
e = e.originalEvent || e;
return (
e.touches && e.touches.length ? e.touches :
e.targetTouches && e.targetTouches.length ? e.targetTouches :
e.changedTouches && e.changedTouches.length ? e.changedTouches :
null
);
},
/*
* Populates `x` and `y` properties on an event from either mouse or touch
* inputs.
*/
normalize: function(e) {
if (_.isNumber(e.clientX)) {
e.x = e.x || e.clientX;
e.y = e.y || e.clientY;
} else {
var touches = Pointer.getTouches(e);
e.x = touches[0].clientX;
e.y = touches[0].clientY;
}
return e;
}
};
//------------------------------
// Helpers
//------------------------------
/*
* Parses a Meteor eventMap key string and returns a dictionary where the keys
* are event types and the values are Arrays of DOM selectors for that event.
*/
function parseSelector(selector) {
RE_SELECTOR.lastIndex = 0;
var map = {};
var match = RE_SELECTOR.exec(selector);
while (match) {
var eventType = match[1];
var domSelector = match[2] || '';
map[eventType] = map[eventType] || [];
map[eventType].push(domSelector);
match = RE_SELECTOR.exec(selector);
}
return map;
}
/*
* Replaces event names in a Meteor eventMap string using the provided map.
* Either a raw or parsed string (using parseSelector) can be passed.
*/
function remapSelector(selector, map) {
if (_.isString(selector)) {
selector = parseSelector(selector);
}
var result = [];
_.each(selector, function(selectors, eventType) {
if (map[eventType]) {
_.each(selectors, function(selector) {
result.push(map[eventType].replace(/{{selector}}/gi, selector));
});
}
});
return result.join(', ');
}
/*
* Wraps an event handler such that the events input is normalized before the
* original handler is called.
*/
function normalizeHandler(handler, context) {
var originalHandler = $.proxy(handler, context);
return function(event) {
Pointer.normalize(event);
originalHandler.apply(context, arguments);
};
}
/*
* Extends `methodName` on the given prototype with the method provided. The
* original method will be called after the new one has executed.
*/
function extendPrototypeMethod(object, methodName, method) {
var parent = object.prototype[methodName];
object.prototype[methodName] = function() {
method.apply(this, arguments);
parent.apply(this, arguments);
};
}
//------------------------------
// Augment Template
//------------------------------
extendPrototypeMethod(Template, 'events', function(eventMap) {
// Traverse the event map to see if it contains pointer events
_.each(eventMap, function(handler, selector) {
// Reset regex since they're flagged as global and will advance
RE_POINTER.lastIndex = 0;
var parsed = parseSelector(selector);
// Process any pointer events
if (RE_POINTER.test(selector)) {
// Remove the current selector from the event map
delete eventMap[selector];
// Map the pointer event names to native
selector = remapSelector(parsed, Pointer.EVENT_MAP);
// Insert a new handler for the native events
eventMap[selector] = normalizeHandler(handler, this);
}
}, this);
});