Index: third_party/polymer/v0_8/components-chromium/polymer/src/lib/gestures-extracted.js |
diff --git a/third_party/polymer/v0_8/components-chromium/polymer/src/lib/gestures-extracted.js b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/gestures-extracted.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6eb6f7dd9f37c7e02f96117423cf4d43b88bcf88 |
--- /dev/null |
+++ b/third_party/polymer/v0_8/components-chromium/polymer/src/lib/gestures-extracted.js |
@@ -0,0 +1,274 @@ |
+ |
+ |
+(function(scope) { |
+ |
+ 'use strict'; |
+ |
+ var async = scope.Base.async; |
+ |
+ var Gestures = { |
+ gestures: {}, |
+ |
+ // automate the event listeners for the native events |
+ // TODO(dfreedm): add a way to remove handlers. |
+ add: function(evType, node, handler) { |
+ // listen for events in order to "recognize" this event |
+ var g = this.gestures[evType]; |
+ var gn = '_' + evType; |
+ var info = {started: false, abortTrack: false, oneshot: false}; |
+ if (g && !node[gn]) { |
+ if (g.touchaction) { |
+ this._setupTouchAction(node, g.touchaction, info); |
+ } |
+ for (var i = 0, n, sn, fn; i < g.deps.length; i++) { |
+ n = g.deps[i]; |
+ fn = g[n].bind(g, info); |
+ sn = '_' + evType + '-' + n; |
+ // store the handler on the node for future removal |
+ node[sn] = fn; |
+ node.addEventListener(n, fn); |
+ } |
+ node[gn] = 0; |
+ } |
+ // listen for the gesture event |
+ node[gn]++; |
+ node.addEventListener(evType, handler); |
+ }, |
+ |
+ remove: function(evType, node, handler) { |
+ var g = this.gestures[evType]; |
+ var gn = '_' + evType; |
+ if (g && node[gn]) { |
+ for (var i = 0, n, sn, fn; i < g.deps.length; i++) { |
+ n = g.deps[i]; |
+ sn = '_' + evType + '-' + n; |
+ fn = node[sn]; |
+ if (fn){ |
+ node.removeEventListener(n, fn); |
+ // remove stored handler to allow GC |
+ node[sn] = undefined; |
+ } |
+ } |
+ node[gn] = node[gn] ? (node[gn] - 1) : 0; |
+ node.removeEventListener(evType, handler); |
+ } |
+ }, |
+ |
+ register: function(recog) { |
+ this.gestures[recog.name] = recog; |
+ }, |
+ |
+ // touch will make synthetic mouse events |
+ // preventDefault on touchend will cancel them, |
+ // but this breaks <input> focus and link clicks |
+ // Disabling "mouse" handlers for 500ms is enough |
+ |
+ _cancelFunction: null, |
+ |
+ cancelNextClick: function(timeout) { |
+ if (!this._cancelFunction) { |
+ timeout = timeout || 500; |
+ var self = this; |
+ var reset = function() { |
+ var cfn = self._cancelFunction; |
+ if (cfn) { |
+ clearTimeout(cfn.id); |
+ document.removeEventListener('click', cfn, true); |
+ self._cancelFunction = null; |
+ } |
+ }; |
+ var canceller = function(e) { |
+ e.tapPrevented = true; |
+ reset(); |
+ }; |
+ canceller.id = setTimeout(reset, timeout); |
+ this._cancelFunction = canceller; |
+ document.addEventListener('click', canceller, true); |
+ } |
+ }, |
+ |
+ // try to use the native touch-action, if it exists |
+ _hasNativeTA: typeof document.head.style.touchAction === 'string', |
+ |
+ // set scrolling direction on node to check later on first move |
+ // must call this before adding event listeners! |
+ setTouchAction: function(node, value) { |
+ if (this._hasNativeTA) { |
+ node.style.touchAction = value; |
+ } |
+ node.touchAction = value; |
+ }, |
+ |
+ _setupTouchAction: function(node, value, info) { |
+ // reuse custom value on node if set |
+ var ta = node.touchAction; |
+ value = ta || value; |
+ // set an anchor point to see how far first move is |
+ node.addEventListener('touchstart', function(e) { |
+ var t = e.changedTouches[0]; |
+ info.initialTouch = {x: t.clientX, y: t.clientY}; |
+ info.abortTrack = false; |
+ info.oneshot = false; |
+ }); |
+ node.addEventListener('touchmove', function(e) { |
+ // only run this once |
+ if (info.oneshot) { |
+ return; |
+ } |
+ info.oneshot = true; |
+ // "none" means always track |
+ if (value === 'none') { |
+ return; |
+ } |
+ // "auto" is default, always scroll |
+ // bail-out if touch-action did its job |
+ // the touchevent is non-cancelable if the page/area is scrolling |
+ if (value === 'auto' || !value || (ta && !e.cancelable)) { |
+ info.abortTrack = true; |
+ return; |
+ } |
+ // check first move direction |
+ // unfortunately, we can only make the decision in the first move, |
+ // so we have to use whatever values are available. |
+ // Typically, this can be a really small amount, :( |
+ var t = e.changedTouches[0]; |
+ var x = t.clientX, y = t.clientY; |
+ var dx = Math.abs(info.initialTouch.x - x); |
+ var dy = Math.abs(info.initialTouch.y - y); |
+ // scroll in x axis, abort track if we move more in x direction |
+ if (value === 'pan-x') { |
+ info.abortTrack = dx >= dy; |
+ // scroll in y axis, abort track if we move more in y direction |
+ } else if (value === 'pan-y') { |
+ info.abortTrack = dy >= dx; |
+ } |
+ }); |
+ }, |
+ |
+ fire: function(target, type, detail, bubbles, cancelable) { |
+ return target.dispatchEvent( |
+ new CustomEvent(type, { |
+ detail: detail, |
+ bubbles: bubbles, |
+ cancelable: cancelable |
+ }) |
+ ); |
+ } |
+ |
+ }; |
+ |
+ Gestures.register({ |
+ name: 'track', |
+ touchaction: 'none', |
+ deps: ['mousedown', 'touchmove', 'touchend'], |
+ |
+ mousedown: function(info, e) { |
+ var t = e.currentTarget; |
+ var self = this; |
+ var movefn = function movefn(e, up) { |
+ if (!info.tracking && !up) { |
+ // set up tap prevention |
+ Gestures.cancelNextClick(); |
+ } |
+ // first move is 'start', subsequent moves are 'move', mouseup is 'end' |
+ var state = up ? 'end' : (!info.started ? 'start' : 'move'); |
+ info.started = true; |
+ self.fire(t, e, state); |
+ e.preventDefault(); |
+ }; |
+ var upfn = function upfn(e) { |
+ // call mousemove function with 'end' state |
+ movefn(e, true); |
+ info.started = false; |
+ // remove the temporary listeners |
+ document.removeEventListener('mousemove', movefn); |
+ document.removeEventListener('mouseup', upfn); |
+ }; |
+ // add temporary document listeners as mouse retargets |
+ document.addEventListener('mousemove', movefn); |
+ document.addEventListener('mouseup', upfn); |
+ }, |
+ |
+ touchmove: function(info, e) { |
+ var t = e.currentTarget; |
+ var ct = e.changedTouches[0]; |
+ // if track was aborted, stop tracking |
+ if (info.abortTrack) { |
+ return; |
+ } |
+ e.preventDefault(); |
+ // the first track event is sent after some hysteresis with touchmove. |
+ // Use `started` state variable to differentiate the "first" move from |
+ // the rest to make track.state == 'start' |
+ // first move is 'start', subsequent moves are 'move' |
+ var state = !info.started ? 'start' : 'move'; |
+ info.started = true; |
+ this.fire(t, ct, state); |
+ }, |
+ |
+ touchend: function(info, e) { |
+ var t = e.currentTarget; |
+ var ct = e.changedTouches[0]; |
+ // only trackend if track was started and not aborted |
+ if (info.started && !info.abortTrack) { |
+ // reset started state on up |
+ info.started = false; |
+ var ne = this.fire(t, ct, 'end'); |
+ // iff tracking, always prevent tap |
+ e.tapPrevented = true; |
+ } |
+ }, |
+ |
+ fire: function(target, touch, state) { |
+ return Gestures.fire(target, 'track', { |
+ state: state, |
+ x: touch.clientX, |
+ y: touch.clientY |
+ }); |
+ } |
+ |
+ }); |
+ |
+ // dispatch a *bubbling* "tap" only at the node that is the target of the |
+ // generating event. |
+ // dispatch *synchronously* so that we can implement prevention of native |
+ // actions like links being followed. |
+ // |
+ // TODO(dfreedm): a tap should not occur when there's too much movement. |
+ // Right now, a tap can occur when a touchend happens very far from the |
+ // generating touch. |
+ // This *should* obviate the need for tapPrevented via track. |
+ Gestures.register({ |
+ name: 'tap', |
+ deps: ['click', 'touchend'], |
+ |
+ click: function(info, e) { |
+ this.forward(e); |
+ }, |
+ |
+ touchend: function(info, e) { |
+ Gestures.cancelNextClick(); |
+ this.forward(e); |
+ }, |
+ |
+ forward: function(e) { |
+ // prevent taps from being generated from events that have been |
+ // canceled (e.g. via cancelNextClick) or already handled via |
+ // a listener lower in the tree. |
+ if (!e.tapPrevented) { |
+ e.tapPrevented = true; |
+ this.fire(e.target); |
+ } |
+ }, |
+ |
+ // fire a bubbling event from the generating target. |
+ fire: function(target) { |
+ Gestures.fire(target, 'tap', {}, true); |
+ } |
+ |
+ }); |
+ |
+ scope.Gestures = Gestures; |
+ |
+})(Polymer); |
+ |