forked from nervetattoo/backbone.touch
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbackbone.touch.js
136 lines (124 loc) · 5.44 KB
/
backbone.touch.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
// (c) 2012 Raymond Julin, Keyteq AS
// Backbone.touch may be freely distributed under the MIT license.
(function (factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['underscore', 'backbone'], factory);
} else {
// Browser globals
factory(_, Backbone);
}
}(function (_, Backbone) {
"use strict";
// The `getValue` and `delegateEventSplitter` is copied from
// Backbones source, unfortunately these are not available
// in any form from Backbone itself
var getValue = function(object, prop) {
if (!(object && object[prop])) return null;
return _.isFunction(object[prop]) ? object[prop]() : object[prop];
};
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
_.extend(Backbone.View.prototype, {
_touching : false,
touchPrevents : true,
touchThreshold : 10,
isTouch : 'ontouchstart' in document && !('callPhantom' in window),
// Drop in replacement for Backbone.View#delegateEvent
// Enables better touch support
//
// If the users device is touch enabled it replace any `click`
// event with listening for touch(start|move|end) in order to
// quickly trigger touch taps
delegateEvents: function(events) {
if (!(events || (events = getValue(this, 'events')))) return;
this.undelegateEvents();
var suffix = '.delegateEvents' + this.cid;
_(events).each(function(method, key) {
if (!_.isFunction(method)) method = this[events[key]];
if (!method) throw new Error('Method "' + events[key] + '" does not exist');
var match = key.match(delegateEventSplitter);
var eventName = match[1], selector = match[2];
var boundHandler = _.bind(this._touchHandler,this);
method = _.bind(method, this);
if (this._useTouchHandlers(eventName, selector)) {
this.$el.on('touchstart' + suffix, selector, boundHandler);
this.$el.on('touchend' + suffix, selector,
{method:method},
boundHandler
);
}
else {
eventName += suffix;
if (selector === '') {
this.$el.bind(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
}
}, this);
// If we think we are touch listen out for a mousemove event
// which could indicate we are a device with both touch and
// mouse, eg Chromebook Pixel
if (this.isTouch) {
var self = this;
var mouseMove = function(event) {
self.isTouch = false;
self.delegateEvents(events);
document.removeEventListener('mousemove', mouseMove);
};
var touchStart = function(event) {
document.removeEventListener('mousemove', mouseMove);
document.removeEventListener('touchstart', touchStart);
};
document.addEventListener('touchstart', touchStart);
document.addEventListener('mousemove', mouseMove);
}
},
// Detect if touch handlers should be used over listening for click
// Allows custom detection implementations
_useTouchHandlers : function(eventName, selector)
{
return this.isTouch && eventName === 'click';
},
// At the first touchstart we register touchevents as ongoing
// and as soon as a touch move happens we set touching to false,
// thus implying that a fastclick will not happen when
// touchend occurs. If no touchmove happened
// inbetween touchstart and touchend we trigger the event
//
// The `touchPrevents` toggle decides if Backbone.touch
// will stop propagation and prevent default
// for *button* and *a* elements
_touchHandler : function(e) {
if (!('changedTouches' in e.originalEvent)) return;
var touch = e.originalEvent.changedTouches[0];
var x = touch.clientX;
var y = touch.clientY;
switch (e.type) {
case 'touchstart':
this._touching = [x, y];
break;
case 'touchend':
var oldX = this._touching[0];
var oldY = this._touching[1];
var threshold = this.touchThreshold;
if (x < (oldX + threshold) && x > (oldX - threshold) &&
y < (oldY + threshold) && y > (oldY - threshold)) {
this._touching = false;
if (this.touchPrevents) {
var tagName = e.currentTarget.tagName;
if (tagName === 'BUTTON' ||
tagName === 'A') {
e.preventDefault();
e.stopPropagation();
}
}
e.data.method(e);
}
break;
}
}
});
return Backbone;
}));