|
|
@@ -3,24 +3,33 @@
|
|
|
Listens to document and window-level user-interaction events, like touch events and mouse events,
|
|
|
and fires these events as-is to whoever is observing a GlobalEmitter.
|
|
|
Best when used as a singleton via GlobalEmitter.get()
|
|
|
+
|
|
|
+Normalizes mouse/touch events. For examples:
|
|
|
+- ignores the the simulated mouse events that happen after a quick tap: mousemove+mousedown+mouseup+click
|
|
|
+- compensates for various buggy scenarios where a touchend does not fire
|
|
|
*/
|
|
|
+
|
|
|
+FC.touchMouseIgnoreWait = 500;
|
|
|
+
|
|
|
var GlobalEmitter = Class.extend(ListenerMixin, EmitterMixin, {
|
|
|
|
|
|
+ isTouching: false,
|
|
|
+ mouseIgnoreDepth: 0,
|
|
|
handleScrollProxy: null,
|
|
|
|
|
|
|
|
|
bind: function() {
|
|
|
- var _this = this;
|
|
|
- var doc = $(document);
|
|
|
-
|
|
|
- $.each([
|
|
|
- 'mousedown', 'mousemove', 'mouseup',
|
|
|
- 'touchstart', 'touchmove', 'touchcancel', 'touchend',
|
|
|
- 'selectstart', 'contextmenu'
|
|
|
- ], function(i, eventName) {
|
|
|
- _this.listenTo(doc, eventName, function(ev) {
|
|
|
- _this.trigger(eventName, ev);
|
|
|
- });
|
|
|
+ this.listenTo($(document), {
|
|
|
+ touchstart: this.handleTouchStart,
|
|
|
+ touchmove: this.handleTouchMove,
|
|
|
+ touchcancel: this.handleTouchCancel,
|
|
|
+ touchend: this.handleTouchEnd,
|
|
|
+ mousedown: this.handleMouseDown,
|
|
|
+ mousemove: this.handleMouseMove,
|
|
|
+ mouseup: this.handleMouseUp,
|
|
|
+ click: this.handleClick,
|
|
|
+ selectstart: this.handleSelectStart,
|
|
|
+ contextmenu: this.handleContextMenu
|
|
|
});
|
|
|
|
|
|
// attach a handler to get called when ANY scroll action happens on the page.
|
|
|
@@ -33,7 +42,6 @@ var GlobalEmitter = Class.extend(ListenerMixin, EmitterMixin, {
|
|
|
);
|
|
|
},
|
|
|
|
|
|
-
|
|
|
unbind: function() {
|
|
|
this.stopListeningTo($(document));
|
|
|
|
|
|
@@ -45,8 +53,124 @@ var GlobalEmitter = Class.extend(ListenerMixin, EmitterMixin, {
|
|
|
},
|
|
|
|
|
|
|
|
|
+ // Touch Handlers
|
|
|
+ // -----------------------------------------------------------------------------------------------------------------
|
|
|
+
|
|
|
+ handleTouchStart: function(ev) {
|
|
|
+
|
|
|
+ // if a previous touch interaction never ended with a touchend, then implicitly end it,
|
|
|
+ // but since a new touch interaction is about to begin, don't start the mouse ignore period.
|
|
|
+ this.stopTouch(ev, true); // skipMouseIgnore=true
|
|
|
+
|
|
|
+ this.isTouching = true;
|
|
|
+ this.trigger('touchstart', ev);
|
|
|
+ },
|
|
|
+
|
|
|
+ handleTouchMove: function(ev) {
|
|
|
+ if (this.isTouching) {
|
|
|
+ this.trigger('touchmove', ev);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleTouchCancel: function(ev) {
|
|
|
+ if (this.isTouching) {
|
|
|
+ this.trigger('touchcancel', ev);
|
|
|
+
|
|
|
+ // Have touchcancel fire an artificial touchend. That way, handlers won't need to listen to both.
|
|
|
+ // If touchend fires later, it won't have any effect b/c isTouching will be false.
|
|
|
+ this.stopTouch(ev);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleTouchEnd: function(ev) {
|
|
|
+ this.stopTouch(ev);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Mouse Handlers
|
|
|
+ // -----------------------------------------------------------------------------------------------------------------
|
|
|
+
|
|
|
+ handleMouseDown: function(ev) {
|
|
|
+ if (!this.shouldIgnoreMouse()) {
|
|
|
+ this.trigger('mousedown', ev);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.stopTouch(ev); // kill touch interaction if touchend neglected to fire
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleMouseMove: function(ev) {
|
|
|
+ if (!this.shouldIgnoreMouse()) {
|
|
|
+ this.trigger('mousemove', ev);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.stopTouch(ev); // kill touch interaction if touchend neglected to fire
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleMouseUp: function(ev) {
|
|
|
+ if (!this.shouldIgnoreMouse()) {
|
|
|
+ this.trigger('mouseup', ev);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.stopTouch(ev); // kill touch interaction if touchend neglected to fire
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ handleClick: function(ev) {
|
|
|
+ if (!this.shouldIgnoreMouse()) {
|
|
|
+ this.trigger('click', ev);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.stopTouch(ev); // kill touch interaction if touchend neglected to fire
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Misc Handlers
|
|
|
+ // -----------------------------------------------------------------------------------------------------------------
|
|
|
+
|
|
|
+ handleSelectStart: function(ev) {
|
|
|
+ this.trigger('selectstart', ev);
|
|
|
+ },
|
|
|
+
|
|
|
+ handleContextMenu: function(ev) {
|
|
|
+ this.trigger('contextmenu', ev);
|
|
|
+ },
|
|
|
+
|
|
|
handleScroll: function(ev) {
|
|
|
this.trigger('scroll', ev);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // Utils
|
|
|
+ // -----------------------------------------------------------------------------------------------------------------
|
|
|
+
|
|
|
+ stopTouch: function(ev, skipMouseIgnore) {
|
|
|
+ if (this.isTouching) {
|
|
|
+ this.isTouching = false;
|
|
|
+ this.trigger('touchend', ev);
|
|
|
+
|
|
|
+ if (!skipMouseIgnore) {
|
|
|
+ this.startTouchMouseIgnore();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ startTouchMouseIgnore: function() {
|
|
|
+ var _this = this;
|
|
|
+ var wait = FC.touchMouseIgnoreWait;
|
|
|
+
|
|
|
+ if (wait) {
|
|
|
+ this.mouseIgnoreDepth++;
|
|
|
+ setTimeout(function() {
|
|
|
+ _this.mouseIgnoreDepth--;
|
|
|
+ }, wait);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ shouldIgnoreMouse: function() {
|
|
|
+ return this.isTouching || Boolean(this.mouseIgnoreDepth);
|
|
|
}
|
|
|
|
|
|
});
|