| Index: third_party/polymer/v0_8/components/polymer/src/lib/gestures.html
|
| diff --git a/third_party/polymer/v0_8/components/polymer/src/lib/gestures.html b/third_party/polymer/v0_8/components/polymer/src/lib/gestures.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c8ac3fb99bfa58718c8211c0f1d7be5dc4815484
|
| --- /dev/null
|
| +++ b/third_party/polymer/v0_8/components/polymer/src/lib/gestures.html
|
| @@ -0,0 +1,284 @@
|
| +<!--
|
| +@license
|
| +Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
| +This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +Code distributed by Google as part of the polymer project is also
|
| +subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +-->
|
| +<script>
|
| +
|
| +(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);
|
| +
|
| +</script>
|
|
|