From df294792aad1ee5335cf0492f33a329a6355574a Mon Sep 17 00:00:00 2001 From: Bartlomiej Kozal Date: Mon, 19 Dec 2016 18:21:42 +0100 Subject: [PATCH] Interacjs: fix the Chrome bug, ignore events from dropdown --- .../components/sidebar/item_document.js | 3 +- vendor/assets/javascripts/interact.js | 11358 +++++++--------- 2 files changed, 5233 insertions(+), 6128 deletions(-) diff --git a/app/assets/javascripts/components/sidebar/item_document.js b/app/assets/javascripts/components/sidebar/item_document.js index f7bd977..c7b7cb7 100644 --- a/app/assets/javascripts/components/sidebar/item_document.js +++ b/app/assets/javascripts/components/sidebar/item_document.js @@ -12,7 +12,8 @@ Vue.component('sidebar-item-document', { mounted() { const self = this; interact(self.$el, { - styleCursor: false + styleCursor: false, + ignoreFrom: '.dropdown' }).draggable({ autoScroll: true, onstart() { diff --git a/vendor/assets/javascripts/interact.js b/vendor/assets/javascripts/interact.js index 8a08101..b4a788f 100644 --- a/vendor/assets/javascripts/interact.js +++ b/vendor/assets/javascripts/interact.js @@ -1,6874 +1,5978 @@ /** - * interact.js 1.3.0 + * interact.js v1.2.6 * - * Copyright (c) 2012-2016 Taye Adeyemi + * Copyright (c) 2012-2015 Taye Adeyemi * Open source under the MIT License. * https://raw.github.com/taye/interact.js/master/LICENSE */ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.interact = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 1 || sy >= 1) { + if (isWindow(container)) { + container.scrollBy(autoScroll.x * sx, autoScroll.y * sy); + } + else if (container) { + container.scrollLeft += autoScroll.x * sx; + container.scrollTop += autoScroll.y * sy; + } + + if (sx >=1) autoScroll.prevTimeX = now; + if (sy >= 1) autoScroll.prevTimeY = now; + } + + if (autoScroll.isScrolling) { + cancelFrame(autoScroll.i); + autoScroll.i = reqFrame(autoScroll.scroll); + } + }, + + isScrolling: false, + prevTimeX: 0, + prevTimeY: 0, + + start: function (interaction) { + autoScroll.isScrolling = true; + cancelFrame(autoScroll.i); + + autoScroll.interaction = interaction; + autoScroll.prevTimeX = new Date().getTime(); + autoScroll.prevTimeY = new Date().getTime(); + autoScroll.i = reqFrame(autoScroll.scroll); + }, + + stop: function () { + autoScroll.isScrolling = false; + cancelFrame(autoScroll.i); + } + }, + + // Does the browser support touch input? + supportsTouch = (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + + // Does the browser support PointerEvents + supportsPointerEvent = !!PointerEvent, + + // Less Precision with touch input + margin = supportsTouch || supportsPointerEvent? 20: 10, + + pointerMoveTolerance = 1, + + // for ignoring browser's simulated mouse events + prevTouchTime = 0, + + // Allow this many interactions to happen simultaneously + maxInteractions = Infinity, + + // Check if is IE9 or older + actionCursors = (document.all && !window.atob) ? { + drag : 'move', + resizex : 'e-resize', + resizey : 's-resize', + resizexy: 'se-resize', + + resizetop : 'n-resize', + resizeleft : 'w-resize', + resizebottom : 's-resize', + resizeright : 'e-resize', + resizetopleft : 'se-resize', + resizebottomright: 'se-resize', + resizetopright : 'ne-resize', + resizebottomleft : 'ne-resize', + + gesture : '' + } : { + drag : 'move', + resizex : 'ew-resize', + resizey : 'ns-resize', + resizexy: 'nwse-resize', + + resizetop : 'ns-resize', + resizeleft : 'ew-resize', + resizebottom : 'ns-resize', + resizeright : 'ew-resize', + resizetopleft : 'nwse-resize', + resizebottomright: 'nwse-resize', + resizetopright : 'nesw-resize', + resizebottomleft : 'nesw-resize', + + gesture : '' + }, + + actionIsEnabled = { + drag : true, + resize : true, + gesture: true + }, + + // because Webkit and Opera still use 'mousewheel' event type + wheelEvent = 'onmousewheel' in document? 'mousewheel': 'wheel', + + eventTypes = [ + 'dragstart', + 'dragmove', + 'draginertiastart', + 'dragend', + 'dragenter', + 'dragleave', + 'dropactivate', + 'dropdeactivate', + 'dropmove', + 'drop', + 'resizestart', + 'resizemove', + 'resizeinertiastart', + 'resizeend', + 'gesturestart', + 'gesturemove', + 'gestureinertiastart', + 'gestureend', + + 'down', + 'move', + 'up', + 'cancel', + 'tap', + 'doubletap', + 'hold' + ], + + globalEvents = {}, + + // Opera Mobile must be handled differently + isOperaMobile = navigator.appName == 'Opera' && + supportsTouch && + navigator.userAgent.match('Presto'), + + // scrolling doesn't change the result of getClientRects on iOS 7 + isIOS7 = (/iP(hone|od|ad)/.test(navigator.platform) + && /OS 7[^\d]/.test(navigator.appVersion)), + + // prefix matchesSelector + prefixedMatchesSelector = 'matches' in Element.prototype? + 'matches': 'webkitMatchesSelector' in Element.prototype? + 'webkitMatchesSelector': 'mozMatchesSelector' in Element.prototype? + 'mozMatchesSelector': 'oMatchesSelector' in Element.prototype? + 'oMatchesSelector': 'msMatchesSelector', + + // will be polyfill function if browser is IE8 + ie8MatchesSelector, + + // native requestAnimationFrame or polyfill + reqFrame = realWindow.requestAnimationFrame, + cancelFrame = realWindow.cancelAnimationFrame, + + // Events wrapper + events = (function () { + var useAttachEvent = ('attachEvent' in window) && !('addEventListener' in window), + addEvent = useAttachEvent? 'attachEvent': 'addEventListener', + removeEvent = useAttachEvent? 'detachEvent': 'removeEventListener', + on = useAttachEvent? 'on': '', + + elements = [], + targets = [], + attachedListeners = []; + + function add (element, type, listener, useCapture) { + var elementIndex = indexOf(elements, element), + target = targets[elementIndex]; + + if (!target) { + target = { + events: {}, + typeCount: 0 + }; + + elementIndex = elements.push(element) - 1; + targets.push(target); + + attachedListeners.push((useAttachEvent ? { + supplied: [], + wrapped : [], + useCount: [] + } : null)); + } + + if (!target.events[type]) { + target.events[type] = []; + target.typeCount++; + } + + if (!contains(target.events[type], listener)) { + var ret; + + if (useAttachEvent) { + var listeners = attachedListeners[elementIndex], + listenerIndex = indexOf(listeners.supplied, listener); + + var wrapped = listeners.wrapped[listenerIndex] || function (event) { + if (!event.immediatePropagationStopped) { + event.target = event.srcElement; + event.currentTarget = element; + + event.preventDefault = event.preventDefault || preventDef; + event.stopPropagation = event.stopPropagation || stopProp; + event.stopImmediatePropagation = event.stopImmediatePropagation || stopImmProp; + + if (/mouse|click/.test(event.type)) { + event.pageX = event.clientX + getWindow(element).document.documentElement.scrollLeft; + event.pageY = event.clientY + getWindow(element).document.documentElement.scrollTop; + } + + listener(event); + } + }; + + ret = element[addEvent](on + type, wrapped, Boolean(useCapture)); + + if (listenerIndex === -1) { + listeners.supplied.push(listener); + listeners.wrapped.push(wrapped); + listeners.useCount.push(1); + } + else { + listeners.useCount[listenerIndex]++; + } + } + else { + ret = element[addEvent](type, listener, useCapture || false); + } + target.events[type].push(listener); + + return ret; + } + } -var _require = require('./utils/arr'); + function remove (element, type, listener, useCapture) { + var i, + elementIndex = indexOf(elements, element), + target = targets[elementIndex], + listeners, + listenerIndex, + wrapped = listener; + + if (!target || !target.events) { + return; + } + + if (useAttachEvent) { + listeners = attachedListeners[elementIndex]; + listenerIndex = indexOf(listeners.supplied, listener); + wrapped = listeners.wrapped[listenerIndex]; + } + + if (type === 'all') { + for (type in target.events) { + if (target.events.hasOwnProperty(type)) { + remove(element, type, 'all'); + } + } + return; + } + + if (target.events[type]) { + var len = target.events[type].length; + + if (listener === 'all') { + for (i = 0; i < len; i++) { + remove(element, type, target.events[type][i], Boolean(useCapture)); + } + return; + } else { + for (i = 0; i < len; i++) { + if (target.events[type][i] === listener) { + element[removeEvent](on + type, wrapped, useCapture || false); + target.events[type].splice(i, 1); + + if (useAttachEvent && listeners) { + listeners.useCount[listenerIndex]--; + if (listeners.useCount[listenerIndex] === 0) { + listeners.supplied.splice(listenerIndex, 1); + listeners.wrapped.splice(listenerIndex, 1); + listeners.useCount.splice(listenerIndex, 1); + } + } + + break; + } + } + } + + if (target.events[type] && target.events[type].length === 0) { + target.events[type] = null; + target.typeCount--; + } + } + + if (!target.typeCount) { + targets.splice(elementIndex, 1); + elements.splice(elementIndex, 1); + attachedListeners.splice(elementIndex, 1); + } + } -var indexOf = _require.indexOf; + function preventDef () { + this.returnValue = false; + } -var extend = require('./utils/extend.js'); + function stopProp () { + this.cancelBubble = true; + } -function fireUntilImmediateStopped(event, listeners) { - for (var i = 0, len = listeners.length; i < len && !event.immediatePropagationStopped; i++) { - listeners[i](event); - } -} + function stopImmProp () { + this.cancelBubble = true; + this.immediatePropagationStopped = true; + } -var Eventable = (function () { - function Eventable(options) { - _classCallCheck(this, Eventable); + return { + add: add, + remove: remove, + useAttachEvent: useAttachEvent, - this.options = extend({}, options || {}); - } + _elements: elements, + _targets: targets, + _attachedListeners: attachedListeners + }; + }()); - Eventable.prototype.fire = function fire(event) { - var listeners = undefined; - var onEvent = 'on' + event.type; - var global = this.global; + function blank () {} - // Interactable#on() listeners - if (listeners = this[event.type]) { - fireUntilImmediateStopped(event, listeners); - } + function isElement (o) { + if (!o || (typeof o !== 'object')) { return false; } - // interactable.onevent listener - if (this[onEvent]) { - this[onEvent](event); - } + var _window = getWindow(o) || window; - // interact.on() listeners - if (!event.propagationStopped && global && (listeners = global[event.type])) { - fireUntilImmediateStopped(event, listeners); + return (/object|function/.test(typeof _window.Element) + ? o instanceof _window.Element //DOM2 + : o.nodeType === 1 && typeof o.nodeName === "string"); } - }; - - Eventable.prototype.on = function on(eventType, listener) { - // if this type of event was never bound - if (!(eventType in this)) { - this[eventType] = [listener]; - } else { - this[eventType].push(listener); + function isWindow (thing) { return thing === window || !!(thing && thing.Window) && (thing instanceof thing.Window); } + function isDocFrag (thing) { return !!thing && thing instanceof DocumentFragment; } + function isArray (thing) { + return isObject(thing) + && (typeof thing.length !== undefined) + && isFunction(thing.splice); } - }; + function isObject (thing) { return !!thing && (typeof thing === 'object'); } + function isFunction (thing) { return typeof thing === 'function'; } + function isNumber (thing) { return typeof thing === 'number' ; } + function isBool (thing) { return typeof thing === 'boolean' ; } + function isString (thing) { return typeof thing === 'string' ; } - Eventable.prototype.off = function off(eventType, listener) { - // if it is an action event type - var eventList = this[eventType]; - var index = eventList ? indexOf(eventList, listener) : -1; + function trySelector (value) { + if (!isString(value)) { return false; } - if (index !== -1) { - this[eventType].splice(index, 1); + // an exception will be raised if it is invalid + document.querySelector(value); + return true; } - }; - - return Eventable; -})(); - -module.exports = Eventable; - -},{"./utils/arr":31,"./utils/extend.js":36}],3:[function(require,module,exports){ -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } - -var extend = require('./utils/extend'); -var getOriginXY = require('./utils/getOriginXY'); -var defaults = require('./defaultOptions'); -var signals = require('./utils/Signals')['new'](); - -var InteractEvent = (function () { - function InteractEvent(interaction, event, action, phase, element, related) { - _classCallCheck(this, InteractEvent); - - var target = interaction.target; - var deltaSource = (target && target.options || defaults).deltaSource; - var origin = getOriginXY(target, element, action); - var starting = phase === 'start'; - var ending = phase === 'end'; - var coords = starting ? interaction.startCoords : interaction.curCoords; - var prevEvent = interaction.prevEvent; - - element = element || interaction.element; - - var page = extend({}, coords.page); - var client = extend({}, coords.client); - - page.x -= origin.x; - page.y -= origin.y; - - client.x -= origin.x; - client.y -= origin.y; - - this.ctrlKey = event.ctrlKey; - this.altKey = event.altKey; - this.shiftKey = event.shiftKey; - this.metaKey = event.metaKey; - this.button = event.button; - this.buttons = event.buttons; - this.target = element; - this.currentTarget = element; - this.relatedTarget = related || null; - this.type = action + (phase || ''); - this.interaction = interaction; - this.interactable = target; - - this.t0 = starting ? interaction.downTimes[interaction.downTimes.length - 1] : prevEvent.t0; - - var signalArg = { - interaction: interaction, - event: event, - action: action, - phase: phase, - element: element, - related: related, - page: page, - client: client, - coords: coords, - starting: starting, - ending: ending, - deltaSource: deltaSource, - iEvent: this - }; - signals.fire('set-xy', signalArg); - - if (ending) { - // use previous coords when ending - this.pageX = prevEvent.pageX; - this.pageY = prevEvent.pageY; - this.clientX = prevEvent.clientX; - this.clientY = prevEvent.clientY; - } else { - this.pageX = page.x; - this.pageY = page.y; - this.clientX = client.x; - this.clientY = client.y; + function extend (dest, source) { + for (var prop in source) { + dest[prop] = source[prop]; + } + return dest; } - this.x0 = interaction.startCoords.page.x - origin.x; - this.y0 = interaction.startCoords.page.y - origin.y; - this.clientX0 = interaction.startCoords.client.x - origin.x; - this.clientY0 = interaction.startCoords.client.y - origin.y; - - signals.fire('set-delta', signalArg); + var prefixedPropREs = { + webkit: /(Movement[XY]|Radius[XY]|RotationAngle|Force)$/ + }; - this.timeStamp = coords.timeStamp; - this.dt = interaction.pointerDelta.timeStamp; - this.duration = this.timeStamp - this.t0; + function pointerExtend (dest, source) { + for (var prop in source) { + var deprecated = false; - // speed and velocity in pixels per second - this.speed = interaction.pointerDelta[deltaSource].speed; - this.velocityX = interaction.pointerDelta[deltaSource].vx; - this.velocityY = interaction.pointerDelta[deltaSource].vy; + // skip deprecated prefixed properties + for (var vendor in prefixedPropREs) { + if (prop.indexOf(vendor) === 0 && prefixedPropREs[vendor].test(prop)) { + deprecated = true; + break; + } + } - this.swipe = ending || phase === 'inertiastart' ? this.getSwipe() : null; + if (!deprecated) { + dest[prop] = source[prop]; + } + } + return dest; + } - signals.fire('new', signalArg); - } + function copyCoords (dest, src) { + dest.page = dest.page || {}; + dest.page.x = src.page.x; + dest.page.y = src.page.y; - InteractEvent.prototype.getSwipe = function getSwipe() { - var interaction = this.interaction; + dest.client = dest.client || {}; + dest.client.x = src.client.x; + dest.client.y = src.client.y; - if (interaction.prevEvent.speed < 600 || this.timeStamp - interaction.prevEvent.timeStamp > 150) { - return null; + dest.timeStamp = src.timeStamp; } - var angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI; - var overlap = 22.5; + function setEventXY (targetObj, pointers, interaction) { + var pointer = (pointers.length > 1 + ? pointerAverage(pointers) + : pointers[0]); + + getPageXY(pointer, tmpXY, interaction); + targetObj.page.x = tmpXY.x; + targetObj.page.y = tmpXY.y; - if (angle < 0) { - angle += 360; + getClientXY(pointer, tmpXY, interaction); + targetObj.client.x = tmpXY.x; + targetObj.client.y = tmpXY.y; + + targetObj.timeStamp = new Date().getTime(); } - var left = 135 - overlap <= angle && angle < 225 + overlap; - var up = 225 - overlap <= angle && angle < 315 + overlap; - - var right = !left && (315 - overlap <= angle || angle < 45 + overlap); - var down = !up && 45 - overlap <= angle && angle < 135 + overlap; - - return { - up: up, - down: down, - left: left, - right: right, - angle: angle, - speed: interaction.prevEvent.speed, - velocity: { - x: interaction.prevEvent.velocityX, - y: interaction.prevEvent.velocityY - } - }; - }; + function setEventDeltas (targetObj, prev, cur) { + targetObj.page.x = cur.page.x - prev.page.x; + targetObj.page.y = cur.page.y - prev.page.y; + targetObj.client.x = cur.client.x - prev.client.x; + targetObj.client.y = cur.client.y - prev.client.y; + targetObj.timeStamp = new Date().getTime() - prev.timeStamp; - InteractEvent.prototype.preventDefault = function preventDefault() {}; + // set pointer velocity + var dt = Math.max(targetObj.timeStamp / 1000, 0.001); + targetObj.page.speed = hypot(targetObj.page.x, targetObj.page.y) / dt; + targetObj.page.vx = targetObj.page.x / dt; + targetObj.page.vy = targetObj.page.y / dt; - InteractEvent.prototype.stopImmediatePropagation = function stopImmediatePropagation() { - this.immediatePropagationStopped = this.propagationStopped = true; - }; + targetObj.client.speed = hypot(targetObj.client.x, targetObj.page.y) / dt; + targetObj.client.vx = targetObj.client.x / dt; + targetObj.client.vy = targetObj.client.y / dt; + } - InteractEvent.prototype.stopPropagation = function stopPropagation() { - this.propagationStopped = true; - }; + function isNativePointer (pointer) { + return (pointer instanceof window.Event + || (supportsTouch && window.Touch && pointer instanceof window.Touch)); + } - return InteractEvent; -})(); + // Get specified X/Y coords for mouse or event.touches[0] + function getXY (type, pointer, xy) { + xy = xy || {}; + type = type || 'page'; -signals.on('set-delta', function (_ref) { - var iEvent = _ref.iEvent; - var interaction = _ref.interaction; - var starting = _ref.starting; - var deltaSource = _ref.deltaSource; + xy.x = pointer[type + 'X']; + xy.y = pointer[type + 'Y']; - var prevEvent = starting ? iEvent : interaction.prevEvent; + return xy; + } - if (deltaSource === 'client') { - iEvent.dx = iEvent.clientX - prevEvent.clientX; - iEvent.dy = iEvent.clientY - prevEvent.clientY; - } else { - iEvent.dx = iEvent.pageX - prevEvent.pageX; - iEvent.dy = iEvent.pageY - prevEvent.pageY; - } -}); + function getPageXY (pointer, page) { + page = page || {}; -InteractEvent.signals = signals; + // Opera Mobile handles the viewport and scrolling oddly + if (isOperaMobile && isNativePointer(pointer)) { + getXY('screen', pointer, page); -module.exports = InteractEvent; + page.x += window.scrollX; + page.y += window.scrollY; + } + else { + getXY('page', pointer, page); + } -},{"./defaultOptions":17,"./utils/Signals":30,"./utils/extend":36,"./utils/getOriginXY":37}],4:[function(require,module,exports){ -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + return page; + } -var isType = require('./utils/isType'); -var events = require('./utils/events'); -var extend = require('./utils/extend'); -var domUtils = require('./utils/domUtils'); -var actions = require('./actions'); -var scope = require('./scope'); -var Eventable = require('./Eventable'); -var defaults = require('./defaultOptions'); -var signals = require('./utils/Signals')['new'](); + function getClientXY (pointer, client) { + client = client || {}; -var _require = require('./utils/domUtils'); + if (isOperaMobile && isNativePointer(pointer)) { + // Opera Mobile handles the viewport and scrolling oddly + getXY('screen', pointer, client); + } + else { + getXY('client', pointer, client); + } -var getElementRect = _require.getElementRect; -var nodeContains = _require.nodeContains; + return client; + } -var _require2 = require('./utils/arr'); + function getScrollXY (win) { + win = win || window; + return { + x: win.scrollX || win.document.documentElement.scrollLeft, + y: win.scrollY || win.document.documentElement.scrollTop + }; + } -var indexOf = _require2.indexOf; -var contains = _require2.contains; + function getPointerId (pointer) { + return isNumber(pointer.pointerId)? pointer.pointerId : pointer.identifier; + } -var _require3 = require('./utils/browser'); + function getActualElement (element) { + return (element instanceof SVGElementInstance + ? element.correspondingUseElement + : element); + } -var wheelEvent = _require3.wheelEvent; + function getWindow (node) { + if (isWindow(node)) { + return node; + } -// all set interactables -scope.interactables = []; + var rootNode = (node.ownerDocument || node); -/*\ - * Interactable - [ property ] - ** - * Object type returned by @interact -\*/ + return rootNode.defaultView || rootNode.parentWindow || window; + } -var Interactable = (function () { - function Interactable(target, options) { - _classCallCheck(this, Interactable); + function getElementClientRect (element) { + var clientRect = (element instanceof SVGElement + ? element.getBoundingClientRect() + : element.getClientRects()[0]); - options = options || {}; + return clientRect && { + left : clientRect.left, + right : clientRect.right, + top : clientRect.top, + bottom: clientRect.bottom, + width : clientRect.width || clientRect.right - clientRect.left, + height: clientRect.height || clientRect.bottom - clientRect.top + }; + } - this.target = target; - this.events = new Eventable(); - this._context = options.context || scope.document; - this._win = scope.getWindow(isType.trySelector(target) ? this._context : target); - this._doc = this._win.document; + function getElementRect (element) { + var clientRect = getElementClientRect(element); - signals.fire('new', { - target: target, - options: options, - interactable: this, - win: this._win - }); + if (!isIOS7 && clientRect) { + var scroll = getScrollXY(getWindow(element)); - scope.addDocument(this._doc, this._win); + clientRect.left += scroll.x; + clientRect.right += scroll.x; + clientRect.top += scroll.y; + clientRect.bottom += scroll.y; + } - scope.interactables.push(this); + return clientRect; + } - this.set(options); - } + function getTouchPair (event) { + var touches = []; - Interactable.prototype.setOnEvents = function setOnEvents(action, phases) { - var onAction = 'on' + action; + // array of touches is supplied + if (isArray(event)) { + touches[0] = event[0]; + touches[1] = event[1]; + } + // an event + else { + if (event.type === 'touchend') { + if (event.touches.length === 1) { + touches[0] = event.touches[0]; + touches[1] = event.changedTouches[0]; + } + else if (event.touches.length === 0) { + touches[0] = event.changedTouches[0]; + touches[1] = event.changedTouches[1]; + } + } + else { + touches[0] = event.touches[0]; + touches[1] = event.touches[1]; + } + } - if (isType.isFunction(phases.onstart)) { - this.events[onAction + 'start'] = phases.onstart; - } - if (isType.isFunction(phases.onmove)) { - this.events[onAction + 'move'] = phases.onmove; - } - if (isType.isFunction(phases.onend)) { - this.events[onAction + 'end'] = phases.onend; - } - if (isType.isFunction(phases.oninertiastart)) { - this.events[onAction + 'inertiastart'] = phases.oninertiastart; + return touches; } - return this; - }; - - Interactable.prototype.setPerAction = function setPerAction(action, options) { - // for all the default per-action options - for (var option in options) { - // if this option exists for this action - if (option in defaults[action]) { - // if the option in the options arg is an object value - if (isType.isObject(options[option])) { - // duplicate the object - this.options[action][option] = extend(this.options[action][option] || {}, options[option]); - - if (isType.isObject(defaults.perAction[option]) && 'enabled' in defaults.perAction[option]) { - this.options[action][option].enabled = options[option].enabled === false ? false : true; - } - } else if (isType.isBool(options[option]) && isType.isObject(defaults.perAction[option])) { - this.options[action][option].enabled = options[option]; - } else if (options[option] !== undefined) { - // or if it's not undefined, do a plain assignment - this.options[action][option] = options[option]; + function pointerAverage (pointers) { + var average = { + pageX : 0, + pageY : 0, + clientX: 0, + clientY: 0, + screenX: 0, + screenY: 0 + }; + var prop; + + for (var i = 0; i < pointers.length; i++) { + for (prop in average) { + average[prop] += pointers[i][prop]; + } } - } - } - }; - - /*\ - * Interactable.getRect - [ method ] - * - * The default function to get an Interactables bounding rect. Can be - * overridden using @Interactable.rectChecker. - * - - element (Element) #optional The element to measure. - = (object) The object's bounding rectangle. - o { - o top : 0, - o left : 0, - o bottom: 0, - o right : 0, - o width : 0, - o height: 0 - o } - \*/ - - Interactable.prototype.getRect = function getRect(element) { - element = element || this.target; - - if (isType.isString(this.target) && !isType.isElement(element)) { - element = this._context.querySelector(this.target); + for (prop in average) { + average[prop] /= pointers.length; + } + + return average; } - return getElementRect(element); - }; - - /*\ - * Interactable.rectChecker - [ method ] - * - * Returns or sets the function used to calculate the interactable's - * element's rectangle - * - - checker (function) #optional A function which returns this Interactable's bounding rectangle. See @Interactable.getRect - = (function | object) The checker function or this Interactable - \*/ - - Interactable.prototype.rectChecker = function rectChecker(checker) { - if (isType.isFunction(checker)) { - this.getRect = checker; - - return this; + function touchBBox (event) { + if (!event.length && !(event.touches && event.touches.length > 1)) { + return; + } + + var touches = getTouchPair(event), + minX = Math.min(touches[0].pageX, touches[1].pageX), + minY = Math.min(touches[0].pageY, touches[1].pageY), + maxX = Math.max(touches[0].pageX, touches[1].pageX), + maxY = Math.max(touches[0].pageY, touches[1].pageY); + + return { + x: minX, + y: minY, + left: minX, + top: minY, + width: maxX - minX, + height: maxY - minY + }; } - if (checker === null) { - delete this.options.getRect; + function touchDistance (event, deltaSource) { + deltaSource = deltaSource || defaultOptions.deltaSource; + + var sourceX = deltaSource + 'X', + sourceY = deltaSource + 'Y', + touches = getTouchPair(event); + - return this; + var dx = touches[0][sourceX] - touches[1][sourceX], + dy = touches[0][sourceY] - touches[1][sourceY]; + + return hypot(dx, dy); } - return this.getRect; - }; + function touchAngle (event, prevAngle, deltaSource) { + deltaSource = deltaSource || defaultOptions.deltaSource; + + var sourceX = deltaSource + 'X', + sourceY = deltaSource + 'Y', + touches = getTouchPair(event), + dx = touches[0][sourceX] - touches[1][sourceX], + dy = touches[0][sourceY] - touches[1][sourceY], + angle = 180 * Math.atan(dy / dx) / Math.PI; + + if (isNumber(prevAngle)) { + var dr = angle - prevAngle, + drClamped = dr % 360; + + if (drClamped > 315) { + angle -= 360 + (angle / 360)|0 * 360; + } + else if (drClamped > 135) { + angle -= 180 + (angle / 360)|0 * 360; + } + else if (drClamped < -315) { + angle += 360 + (angle / 360)|0 * 360; + } + else if (drClamped < -135) { + angle += 180 + (angle / 360)|0 * 360; + } + } + + return angle; + } - Interactable.prototype._backCompatOption = function _backCompatOption(optionName, newValue) { - if (isType.trySelector(newValue) || isType.isObject(newValue)) { - this.options[optionName] = newValue; + function getOriginXY (interactable, element) { + var origin = interactable + ? interactable.options.origin + : defaultOptions.origin; - for (var _iterator = actions.names, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + if (origin === 'parent') { + origin = parentElement(element); + } + else if (origin === 'self') { + origin = interactable.getRect(element); + } + else if (trySelector(origin)) { + origin = closest(element, origin) || { x: 0, y: 0 }; + } - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; + if (isFunction(origin)) { + origin = origin(interactable && element); } - var action = _ref; + if (isElement(origin)) { + origin = getElementRect(origin); + } - this.options[action][optionName] = newValue; - } + origin.x = ('x' in origin)? origin.x : origin.left; + origin.y = ('y' in origin)? origin.y : origin.top; - return this; + return origin; } - return this.options[optionName]; - }; - - /*\ - * Interactable.origin - [ method ] - * - * Gets or sets the origin of the Interactable's element. The x and y - * of the origin will be subtracted from action event coordinates. - * - - origin (object | string) #optional An object eg. { x: 0, y: 0 } or string 'parent', 'self' or any CSS selector - * OR - - origin (Element) #optional An HTML or SVG Element whose rect will be used - ** - = (object) The current origin or this Interactable - \*/ - - Interactable.prototype.origin = function origin(newValue) { - return this._backCompatOption('origin', newValue); - }; - - /*\ - * Interactable.deltaSource - [ method ] - * - * Returns or sets the mouse coordinate types used to calculate the - * movement of the pointer. - * - - newValue (string) #optional Use 'client' if you will be scrolling while interacting; Use 'page' if you want autoScroll to work - = (string | object) The current deltaSource or this Interactable - \*/ - - Interactable.prototype.deltaSource = function deltaSource(newValue) { - if (newValue === 'page' || newValue === 'client') { - this.options.deltaSource = newValue; - - return this; + // http://stackoverflow.com/a/5634528/2280888 + function _getQBezierValue(t, p1, p2, p3) { + var iT = 1 - t; + return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; } - return this.options.deltaSource; - }; - - /*\ - * Interactable.context - [ method ] - * - * Gets the selector context Node of the Interactable. The default is `window.document`. - * - = (Node) The context Node of this Interactable - ** - \*/ - - Interactable.prototype.context = function context() { - return this._context; - }; - - Interactable.prototype.inContext = function inContext(element) { - return this._context === element.ownerDocument || nodeContains(this._context, element); - }; - - /*\ - * Interactable.ignoreFrom - [ method ] - * - * If the target of the `mousedown`, `pointerdown` or `touchstart` - * event or any of it's parents match the given CSS selector or - * Element, no drag/resize/gesture is started. - * - - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to not ignore any elements - = (string | Element | object) The current ignoreFrom value or this Interactable - ** - | interact(element, { ignoreFrom: document.getElementById('no-action') }); - | // or - | interact(element).ignoreFrom('input, textarea, a'); - \*/ - - Interactable.prototype.ignoreFrom = function ignoreFrom(newValue) { - return this._backCompatOption('ignoreFrom', newValue); - }; - - /*\ - * Interactable.allowFrom - [ method ] - * - * A drag/resize/gesture is started only If the target of the - * `mousedown`, `pointerdown` or `touchstart` event or any of it's - * parents match the given CSS selector or Element. - * - - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to allow from any element - = (string | Element | object) The current allowFrom value or this Interactable - ** - | interact(element, { allowFrom: document.getElementById('drag-handle') }); - | // or - | interact(element).allowFrom('.handle'); - \*/ - - Interactable.prototype.allowFrom = function allowFrom(newValue) { - return this._backCompatOption('allowFrom', newValue); - }; - - Interactable.prototype.testIgnore = function testIgnore(ignoreFrom, interactableElement, element) { - if (!ignoreFrom || !isType.isElement(element)) { - return false; + function getQuadraticCurvePoint(startX, startY, cpX, cpY, endX, endY, position) { + return { + x: _getQBezierValue(position, startX, cpX, endX), + y: _getQBezierValue(position, startY, cpY, endY) + }; } - if (isType.isString(ignoreFrom)) { - return domUtils.matchesUpTo(element, ignoreFrom, interactableElement); - } else if (isType.isElement(ignoreFrom)) { - return domUtils.nodeContains(ignoreFrom, element); + // http://gizma.com/easing/ + function easeOutQuad (t, b, c, d) { + t /= d; + return -c * t*(t-2) + b; } - return false; - }; + function nodeContains (parent, child) { + while (child) { + if (child === parent) { + return true; + } - Interactable.prototype.testAllow = function testAllow(allowFrom, interactableElement, element) { - if (!allowFrom) { - return true; - } + child = child.parentNode; + } - if (!isType.isElement(element)) { - return false; + return false; } - if (isType.isString(allowFrom)) { - return domUtils.matchesUpTo(element, allowFrom, interactableElement); - } else if (isType.isElement(allowFrom)) { - return domUtils.nodeContains(allowFrom, element); - } + function closest (child, selector) { + var parent = parentElement(child); - return false; - }; - - Interactable.prototype.testIgnoreAllow = function testIgnoreAllow(options, interactableElement, element) { - return !this.testIgnore(options.ignoreFrom, interactableElement, element) && this.testAllow(options.allowFrom, interactableElement, element); - }; - - /*\ - * Interactable.fire - [ method ] - * - * Calls listeners for the given InteractEvent type bound globally - * and directly to this Interactable - * - - iEvent (InteractEvent) The InteractEvent object to be fired on this Interactable - = (Interactable) this Interactable - \*/ - - Interactable.prototype.fire = function fire(iEvent) { - this.events.fire(iEvent); - - return this; - }; - - Interactable.prototype._onOffMultiple = function _onOffMultiple(method, eventType, listener, useCapture) { - if (isType.isString(eventType) && eventType.search(' ') !== -1) { - eventType = eventType.trim().split(/ +/); - } + while (isElement(parent)) { + if (matchesSelector(parent, selector)) { return parent; } - if (isType.isArray(eventType)) { - for (var i = 0; i < eventType.length; i++) { - this[method](eventType[i], listener, useCapture); - } + parent = parentElement(parent); + } - return true; + return null; } - if (isType.isObject(eventType)) { - for (var prop in eventType) { - this[method](prop, eventType[prop], listener); - } + function parentElement (node) { + var parent = node.parentNode; - return true; - } - }; - - /*\ - * Interactable.on - [ method ] - * - * Binds a listener for an InteractEvent, pointerEvent or DOM event. - * - - eventType (string | array | object) The types of events to listen for - - listener (function) The function event (s) - - useCapture (boolean) #optional useCapture flag for addEventListener - = (object) This Interactable - \*/ - - Interactable.prototype.on = function on(eventType, listener, useCapture) { - // convert to boolean - useCapture = !!useCapture; - - if (this._onOffMultiple('on', eventType, listener, useCapture)) { - return this; - } + if (isDocFrag(parent)) { + // skip past #shado-root fragments + while ((parent = parent.host) && isDocFrag(parent)) {} - if (eventType === 'wheel') { - eventType = wheelEvent; - } + return parent; + } - if (contains(Interactable.eventTypes, eventType)) { - this.events.on(eventType, listener); - } - // delegated event for selector - else if (isType.isString(this.target)) { - events.addDelegate(this.target, this._context, eventType, listener, useCapture); - } else { - events.add(this.target, eventType, listener, useCapture); - } - - return this; - }; - - /*\ - * Interactable.off - [ method ] - * - * Removes an InteractEvent, pointerEvent or DOM event listener - * - - eventType (string | array | object) The types of events that were listened for - - listener (function) The listener function to be removed - - useCapture (boolean) #optional useCapture flag for removeEventListener - = (object) This Interactable - \*/ - - Interactable.prototype.off = function off(eventType, listener, useCapture) { - // convert to boolean - useCapture = !!useCapture; - - if (this._onOffMultiple('off', eventType, listener, useCapture)) { - return this; + return parent; } - if (eventType === 'wheel') { - eventType = wheelEvent; + function inContext (interactable, element) { + return interactable._context === element.ownerDocument + || nodeContains(interactable._context, element); } - // if it is an action event type - if (contains(Interactable.eventTypes, eventType)) { - this.events.on(eventType, listener); - } - // delegated event - else if (isType.isString(this.target)) { - events.removeDelegate(this.target, this._context, eventType, listener, useCapture); - } - // remove listener from this Interatable's element - else { - events.remove(this.target, eventType, listener, useCapture); + function testIgnore (interactable, interactableElement, element) { + var ignoreFrom = interactable.options.ignoreFrom; + + if (!ignoreFrom || !isElement(element)) { return false; } + + if (isString(ignoreFrom)) { + return matchesUpTo(element, ignoreFrom, interactableElement); + } + else if (isElement(ignoreFrom)) { + return nodeContains(ignoreFrom, element); } - return this; - }; - - /*\ - * Interactable.set - [ method ] - * - * Reset the options of this Interactable - - options (object) The new settings to apply - = (object) This Interactable - \*/ - - Interactable.prototype.set = function set(options) { - if (!isType.isObject(options)) { - options = {}; + return false; } - this.options = extend({}, defaults.base); + function testAllow (interactable, interactableElement, element) { + var allowFrom = interactable.options.allowFrom; + + if (!allowFrom) { return true; } + + if (!isElement(element)) { return false; } - var perActions = extend({}, defaults.perAction); + if (isString(allowFrom)) { + return matchesUpTo(element, allowFrom, interactableElement); + } + else if (isElement(allowFrom)) { + return nodeContains(allowFrom, element); + } - for (var actionName in actions.methodDict) { - var methodName = actions.methodDict[actionName]; + return false; + } - this.options[actionName] = extend({}, defaults[actionName]); + function checkAxis (axis, interactable) { + if (!interactable) { return false; } - this.setPerAction(actionName, perActions); + var thisAxis = interactable.options.drag.axis; - this[methodName](options[actionName]); + return (axis === 'xy' || thisAxis === 'xy' || thisAxis === axis); } - for (var _iterator2 = Interactable.settingsMethods, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; + function checkSnap (interactable, action) { + var options = interactable.options; - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } + if (/^resize/.test(action)) { + action = 'resize'; + } + + return options[action].snap && options[action].snap.enabled; + } - var setting = _ref2; + function checkRestrict (interactable, action) { + var options = interactable.options; - this.options[setting] = defaults.base[setting]; + if (/^resize/.test(action)) { + action = 'resize'; + } - if (setting in options) { - this[setting](options[setting]); - } + return options[action].restrict && options[action].restrict.enabled; } - signals.fire('set', { - options: options, - interactable: this - }); - - return this; - }; - - /*\ - * Interactable.unset - [ method ] - * - * Remove this interactable from the list of interactables and remove - * it's action capabilities and event listeners - * - = (object) @interact - \*/ - - Interactable.prototype.unset = function unset() { - events.remove(this.target, 'all'); - - if (isType.isString(this.target)) { - // remove delegated events - for (var type in events.delegatedEvents) { - var delegated = events.delegatedEvents[type]; - - if (delegated.selectors[0] === this.target && delegated.contexts[0] === this._context) { - - delegated.selectors.splice(0, 1); - delegated.contexts.splice(0, 1); - delegated.listeners.splice(0, 1); - - // remove the arrays if they are empty - if (!delegated.selectors.length) { - delegated[type] = null; - } + function checkAutoScroll (interactable, action) { + var options = interactable.options; + + if (/^resize/.test(action)) { + action = 'resize'; } - events.remove(this._context, type, events.delegateListener); - events.remove(this._context, type, events.delegateUseCapture, true); - } - } else { - events.remove(this, 'all'); + return options[action].autoScroll && options[action].autoScroll.enabled; } - signals.fire('unset', { interactable: this }); + function withinInteractionLimit (interactable, element, action) { + var options = interactable.options, + maxActions = options[action.name].max, + maxPerElement = options[action.name].maxPerElement, + activeInteractions = 0, + targetCount = 0, + targetElementCount = 0; - scope.interactables.splice(indexOf(scope.interactables, this), 1); + for (var i = 0, len = interactions.length; i < len; i++) { + var interaction = interactions[i], + otherAction = interaction.prepared.name, + active = interaction.interacting(); - // Stop related interactions when an Interactable is unset - for (var _iterator3 = scope.interactions || [], _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; + if (!active) { continue; } - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } + activeInteractions++; - var interaction = _ref3; + if (activeInteractions >= maxInteractions) { + return false; + } - if (interaction.target === this && interaction.interacting()) { - interaction.stop(); - } - } + if (interaction.target !== interactable) { continue; } - return scope.interact; - }; + targetCount += (otherAction === action.name)|0; - return Interactable; -})(); + if (targetCount >= maxActions) { + return false; + } -scope.interactables.indexOfElement = function indexOfElement(target, context) { - context = context || scope.document; + if (interaction.element === element) { + targetElementCount++; - for (var i = 0; i < this.length; i++) { - var interactable = this[i]; + if (otherAction !== action.name || targetElementCount >= maxPerElement) { + return false; + } + } + } - if (interactable.target === target && (!isType.isString(target) || interactable._context === context)) { - return i; + return maxInteractions > 0; } - } - return -1; -}; -scope.interactables.get = function interactableGet(element, options, dontCheckInContext) { - var ret = this[this.indexOfElement(element, options && options.context)]; + // Test for the element that's "above" all other qualifiers + function indexOfDeepestElement (elements) { + var dropzone, + deepestZone = elements[0], + index = deepestZone? 0: -1, + parent, + deepestZoneParents = [], + dropzoneParents = [], + child, + i, + n; + + for (i = 1; i < elements.length; i++) { + dropzone = elements[i]; - return ret && (dontCheckInContext || ret.inContext(element)) ? ret : null; -}; + // an element might belong to multiple selector dropzones + if (!dropzone || dropzone === deepestZone) { + continue; + } -scope.interactables.forEachSelector = function (callback, element) { - for (var i = 0; i < this.length; i++) { - var interactable = this[i]; + if (!deepestZone) { + deepestZone = dropzone; + index = i; + continue; + } - // skip non CSS selector targets and out of context elements - if (!isType.isString(interactable.target) || element && !interactable.inContext(element)) { - continue; - } + // check if the deepest or current are document.documentElement or document.rootElement + // - if the current dropzone is, do nothing and continue + if (dropzone.parentNode === dropzone.ownerDocument) { + continue; + } + // - if deepest is, update with the current dropzone and continue to next + else if (deepestZone.parentNode === dropzone.ownerDocument) { + deepestZone = dropzone; + index = i; + continue; + } - var ret = callback(interactable, interactable.target, interactable._context, i, this); + if (!deepestZoneParents.length) { + parent = deepestZone; + while (parent.parentNode && parent.parentNode !== parent.ownerDocument) { + deepestZoneParents.unshift(parent); + parent = parent.parentNode; + } + } - if (ret !== undefined) { - return ret; - } - } -}; + // if this element is an svg element and the current deepest is + // an HTMLElement + if (deepestZone instanceof HTMLElement + && dropzone instanceof SVGElement + && !(dropzone instanceof SVGSVGElement)) { -// all interact.js eventTypes -Interactable.eventTypes = scope.eventTypes = []; + if (dropzone === deepestZone.parentNode) { + continue; + } -Interactable.signals = signals; + parent = dropzone.ownerSVGElement; + } + else { + parent = dropzone; + } -Interactable.settingsMethods = ['deltaSource', 'origin', 'preventDefault', 'rectChecker']; + dropzoneParents = []; -module.exports = Interactable; + while (parent.parentNode !== parent.ownerDocument) { + dropzoneParents.unshift(parent); + parent = parent.parentNode; + } -},{"./Eventable":2,"./actions":9,"./defaultOptions":17,"./scope":29,"./utils/Signals":30,"./utils/arr":31,"./utils/browser":32,"./utils/domUtils":34,"./utils/events":35,"./utils/extend":36,"./utils/isType":41}],5:[function(require,module,exports){ -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + n = 0; -var scope = require('./scope'); -var utils = require('./utils'); -var events = require('./utils/events'); -var browser = require('./utils/browser'); -var finder = require('./utils/interactionFinder'); -var signals = require('./utils/Signals')['new'](); + // get (position of last common ancestor) + 1 + while (dropzoneParents[n] && dropzoneParents[n] === deepestZoneParents[n]) { + n++; + } -var listeners = {}; -var methodNames = ['pointerDown', 'pointerMove', 'pointerUp', 'updatePointer', 'removePointer']; + var parents = [ + dropzoneParents[n - 1], + dropzoneParents[n], + deepestZoneParents[n] + ]; -// for ignoring browser's simulated mouse events -var prevTouchTime = 0; + child = parents[0].lastChild; -// all active and idle interactions -scope.interactions = []; + while (child) { + if (child === parents[1]) { + deepestZone = dropzone; + index = i; + deepestZoneParents = []; -var Interaction = (function () { - function Interaction() { - _classCallCheck(this, Interaction); + break; + } + else if (child === parents[2]) { + break; + } - this.target = null; // current interactable being interacted with - this.element = null; // the target element of the interactable + child = child.previousSibling; + } + } - this.prepared = { // action that's ready to be fired on next move event - name: null, - axis: null, - edges: null - }; + return index; + } - // keep track of added pointers - this.pointers = []; - this.pointerIds = []; - this.downTargets = []; - this.downTimes = []; - this.holdTimers = []; - - // Previous native pointer move event coordinates - this.prevCoords = { - page: { x: 0, y: 0 }, - client: { x: 0, y: 0 }, - timeStamp: 0 - }; - // current native pointer move event coordinates - this.curCoords = { - page: { x: 0, y: 0 }, - client: { x: 0, y: 0 }, - timeStamp: 0 - }; + function Interaction () { + this.target = null; // current interactable being interacted with + this.element = null; // the target element of the interactable + this.dropTarget = null; // the dropzone a drag target might be dropped into + this.dropElement = null; // the element at the time of checking + this.prevDropTarget = null; // the dropzone that was recently dragged away from + this.prevDropElement = null; // the element at the time of checking - // Starting InteractEvent pointer coordinates - this.startCoords = { - page: { x: 0, y: 0 }, - client: { x: 0, y: 0 }, - timeStamp: 0 - }; + this.prepared = { // action that's ready to be fired on next move event + name : null, + axis : null, + edges: null + }; - // Change in coordinates and time of the pointer - this.pointerDelta = { - page: { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, - client: { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, - timeStamp: 0 - }; + this.matches = []; // all selectors that are matched by target element + this.matchElements = []; // corresponding elements - this.downEvent = null; // pointerdown/mousedown/touchstart event - this.downPointer = {}; + this.inertiaStatus = { + active : false, + smoothEnd : false, + ending : false, - this._eventTarget = null; - this._curEventTarget = null; + startEvent: null, + upCoords: {}, - this.prevEvent = null; // previous action event + xe: 0, ye: 0, + sx: 0, sy: 0, - this.pointerIsDown = false; - this.pointerWasMoved = false; - this._interacting = false; + t0: 0, + vx0: 0, vys: 0, + duration: 0, - this.mouse = false; + resumeDx: 0, + resumeDy: 0, - signals.fire('new', this); + lambda_v0: 0, + one_ve_v0: 0, + i : null + }; - scope.interactions.push(this); - } + if (isFunction(Function.prototype.bind)) { + this.boundInertiaFrame = this.inertiaFrame.bind(this); + this.boundSmoothEndFrame = this.smoothEndFrame.bind(this); + } + else { + var that = this; - Interaction.prototype.pointerDown = function pointerDown(pointer, event, eventTarget) { - var pointerIndex = this.updatePointer(pointer); + this.boundInertiaFrame = function () { return that.inertiaFrame(); }; + this.boundSmoothEndFrame = function () { return that.smoothEndFrame(); }; + } - this.pointerIsDown = true; + this.activeDrops = { + dropzones: [], // the dropzones that are mentioned below + elements : [], // elements of dropzones that accept the target draggable + rects : [] // the rects of the elements mentioned above + }; - if (!this.interacting()) { - utils.setCoords(this.startCoords, this.pointers); + // keep track of added pointers + this.pointers = []; + this.pointerIds = []; + this.downTargets = []; + this.downTimes = []; + this.holdTimers = []; + + // Previous native pointer move event coordinates + this.prevCoords = { + page : { x: 0, y: 0 }, + client : { x: 0, y: 0 }, + timeStamp: 0 + }; + // current native pointer move event coordinates + this.curCoords = { + page : { x: 0, y: 0 }, + client : { x: 0, y: 0 }, + timeStamp: 0 + }; - utils.copyCoords(this.curCoords, this.startCoords); - utils.copyCoords(this.prevCoords, this.startCoords); + // Starting InteractEvent pointer coordinates + this.startCoords = { + page : { x: 0, y: 0 }, + client : { x: 0, y: 0 }, + timeStamp: 0 + }; - this.downEvent = event; + // Change in coordinates and time of the pointer + this.pointerDelta = { + page : { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, + client : { x: 0, y: 0, vx: 0, vy: 0, speed: 0 }, + timeStamp: 0 + }; - this.downTimes[pointerIndex] = this.curCoords.timeStamp; - this.downTargets[pointerIndex] = eventTarget; + this.downEvent = null; // pointerdown/mousedown/touchstart event + this.downPointer = {}; - this.pointerWasMoved = false; + this._eventTarget = null; + this._curEventTarget = null; - utils.pointerExtend(this.downPointer, pointer); - } + this.prevEvent = null; // previous action event + this.tapTime = 0; // time of the most recent tap event + this.prevTap = null; - signals.fire('down', { - pointer: pointer, - event: event, - eventTarget: eventTarget, - pointerIndex: pointerIndex, - interaction: this - }); - }; - - /*\ - * Interaction.start - [ method ] - * - * Start an action with the given Interactable and Element as tartgets. The - * action must be enabled for the target Interactable and an appropriate number - * of pointers must be held down - 1 for drag/resize, 2 for gesture. - * - * Use it with `interactable.able({ manualStart: false })` to always - * [start actions manually](https://github.com/taye/interact.js/issues/114) - * - - action (object) The action to be performed - drag, resize, etc. - - target (Interactable) The Interactable to target - - element (Element) The DOM Element to target - = (object) interact - ** - | interact(target) - | .draggable({ - | // disable the default drag start by down->move - | manualStart: true - | }) - | // start dragging after the user holds the pointer down - | .on('hold', function (event) { - | var interaction = event.interaction; - | - | if (!interaction.interacting()) { - | interaction.start({ name: 'drag' }, - | event.interactable, - | event.currentTarget); - | } - | }); - \*/ - - Interaction.prototype.start = function start(action, target, element) { - if (this.interacting() || !this.pointerIsDown || this.pointerIds.length < (action.name === 'gesture' ? 2 : 1)) { - return; - } + this.startOffset = { left: 0, right: 0, top: 0, bottom: 0 }; + this.restrictOffset = { left: 0, right: 0, top: 0, bottom: 0 }; + this.snapOffsets = []; - // if this interaction had been removed after stopping - // add it back - if (utils.indexOf(scope.interactions, this) === -1) { - scope.interactions.push(this); - } + this.gesture = { + start: { x: 0, y: 0 }, - utils.copyAction(this.prepared, action); - this.target = target; - this.element = element; + startDistance: 0, // distance between two touches of touchStart + prevDistance : 0, + distance : 0, - signals.fire('action-start', { - interaction: this, - event: this.downEvent - }); - }; + scale: 1, // gesture.distance / gesture.startDistance - Interaction.prototype.pointerMove = function pointerMove(pointer, event, eventTarget) { - if (!this.simulation) { - this.updatePointer(pointer); - utils.setCoords(this.curCoords, this.pointers); - } + startAngle: 0, // angle of line joining two touches + prevAngle : 0 // angle of the previous gesture event + }; - var duplicateMove = this.curCoords.page.x === this.prevCoords.page.x && this.curCoords.page.y === this.prevCoords.page.y && this.curCoords.client.x === this.prevCoords.client.x && this.curCoords.client.y === this.prevCoords.client.y; + this.snapStatus = { + x : 0, y : 0, + dx : 0, dy : 0, + realX : 0, realY : 0, + snappedX: 0, snappedY: 0, + targets : [], + locked : false, + changed : false + }; - var dx = undefined; - var dy = undefined; + this.restrictStatus = { + dx : 0, dy : 0, + restrictedX: 0, restrictedY: 0, + snap : null, + restricted : false, + changed : false + }; - // register movement greater than pointerMoveTolerance - if (this.pointerIsDown && !this.pointerWasMoved) { - dx = this.curCoords.client.x - this.startCoords.client.x; - dy = this.curCoords.client.y - this.startCoords.client.y; + this.restrictStatus.snap = this.snapStatus; - this.pointerWasMoved = utils.hypot(dx, dy) > Interaction.pointerMoveTolerance; - } + this.pointerIsDown = false; + this.pointerWasMoved = false; + this.gesturing = false; + this.dragging = false; + this.resizing = false; + this.resizeAxes = 'xy'; - var signalArg = { - pointer: pointer, - pointerIndex: this.getPointerIndex(pointer), - event: event, - eventTarget: eventTarget, - dx: dx, - dy: dy, - duplicate: duplicateMove, - interaction: this, - interactingBeforeMove: this.interacting() - }; + this.mouse = false; - if (!duplicateMove) { - // set pointer coordinate, time changes and speeds - utils.setCoordDeltas(this.pointerDelta, this.prevCoords, this.curCoords); + interactions.push(this); } - signals.fire('move', signalArg); + Interaction.prototype = { + getPageXY : function (pointer, xy) { return getPageXY(pointer, xy, this); }, + getClientXY: function (pointer, xy) { return getClientXY(pointer, xy, this); }, + setEventXY : function (target, ptr) { return setEventXY(target, ptr, this); }, - if (!duplicateMove) { - // if interacting, fire an 'action-move' signal etc - if (this.interacting()) { - this.doMove(signalArg); - } + pointerOver: function (pointer, event, eventTarget) { + if (this.prepared.name || !this.mouse) { return; } - if (this.pointerWasMoved) { - utils.copyCoords(this.prevCoords, this.curCoords); - } - } - }; - - /*\ - * Interaction.doMove - [ method ] - * - * Force a move of the current action at the same coordinates. Useful if - * snap/restrict has been changed and you want a movement with the new - * settings. - * - ** - | interact(target) - | .draggable(true) - | .on('dragmove', function (event) { - | if (someCondition) { - | // change the snap settings - | event.interactable.draggable({ snap: { targets: [] }}); - | // fire another move event with re-calculated snap - | event.interaction.doMove(); - | } - | }); - \*/ - - Interaction.prototype.doMove = function doMove(signalArg) { - signalArg = utils.extend({ - pointer: this.pointers[0], - event: this.prevEvent, - eventTarget: this._eventTarget, - interaction: this - }, signalArg || {}); - - signals.fire('before-action-move', signalArg); - - if (!this._dontFireMove) { - signals.fire('action-move', signalArg); - } + var curMatches = [], + curMatchElements = [], + prevTargetElement = this.element; - this._dontFireMove = false; - }; + this.addPointer(pointer); - // End interact move events and stop auto-scroll unless simulation is running + if (this.target + && (testIgnore(this.target, this.element, eventTarget) + || !testAllow(this.target, this.element, eventTarget))) { + // if the eventTarget should be ignored or shouldn't be allowed + // clear the previous target + this.target = null; + this.element = null; + this.matches = []; + this.matchElements = []; + } - Interaction.prototype.pointerUp = function pointerUp(pointer, event, eventTarget, curEventTarget) { - var pointerIndex = this.getPointerIndex(pointer); + var elementInteractable = interactables.get(eventTarget), + elementAction = (elementInteractable + && !testIgnore(elementInteractable, eventTarget, eventTarget) + && testAllow(elementInteractable, eventTarget, eventTarget) + && validateAction( + elementInteractable.getAction(pointer, event, this, eventTarget), + elementInteractable)); - signals.fire(/cancel$/i.test(event.type) ? 'cancel' : 'up', { - pointer: pointer, - pointerIndex: pointerIndex, - event: event, - eventTarget: eventTarget, - curEventTarget: curEventTarget, - interaction: this - }); + if (elementAction && !withinInteractionLimit(elementInteractable, eventTarget, elementAction)) { + elementAction = null; + } - if (!this.simulation) { - this.end(event); - } + function pushCurMatches (interactable, selector) { + if (interactable + && inContext(interactable, eventTarget) + && !testIgnore(interactable, eventTarget, eventTarget) + && testAllow(interactable, eventTarget, eventTarget) + && matchesSelector(eventTarget, selector)) { - this.pointerIsDown = false; - this.removePointer(pointer); - }; - - /*\ - * Interaction.end - [ method ] - * - * Stop the current action and fire an end event. Inertial movement does - * not happen. - * - - event (PointerEvent) #optional - ** - | interact(target) - | .draggable(true) - | .on('move', function (event) { - | if (event.pageX > 1000) { - | // end the current action - | event.interaction.end(); - | // stop all further listeners from being called - | event.stopImmediatePropagation(); - | } - | }); - \*/ - - Interaction.prototype.end = function end(event) { - event = event || this.prevEvent; - - if (this.interacting()) { - signals.fire('action-end', { - event: event, - interaction: this - }); - } + curMatches.push(interactable); + curMatchElements.push(eventTarget); + } + } - this.stop(); - }; + if (elementAction) { + this.target = elementInteractable; + this.element = eventTarget; + this.matches = []; + this.matchElements = []; + } + else { + interactables.forEachSelector(pushCurMatches); + + if (this.validateSelector(pointer, event, curMatches, curMatchElements)) { + this.matches = curMatches; + this.matchElements = curMatchElements; + + this.pointerHover(pointer, event, this.matches, this.matchElements); + events.add(eventTarget, + PointerEvent? pEventTypes.move : 'mousemove', + listeners.pointerHover); + } + else if (this.target) { + if (nodeContains(prevTargetElement, eventTarget)) { + this.pointerHover(pointer, event, this.matches, this.matchElements); + events.add(this.element, + PointerEvent? pEventTypes.move : 'mousemove', + listeners.pointerHover); + } + else { + this.target = null; + this.element = null; + this.matches = []; + this.matchElements = []; + } + } + } + }, + + // Check what action would be performed on pointerMove target if a mouse + // button were pressed and change the cursor accordingly + pointerHover: function (pointer, event, eventTarget, curEventTarget, matches, matchElements) { + var target = this.target; + + if (!this.prepared.name && this.mouse) { + + var action; + + // update pointer coords for defaultActionChecker to use + this.setEventXY(this.curCoords, [pointer]); + + if (matches) { + action = this.validateSelector(pointer, event, matches, matchElements); + } + else if (target) { + action = validateAction(target.getAction(this.pointers[0], event, this, this.element), this.target); + } + + if (target && target.options.styleCursor) { + if (action) { + target._doc.documentElement.style.cursor = getActionCursor(action); + } + else { + target._doc.documentElement.style.cursor = ''; + } + } + } + else if (this.prepared.name) { + this.checkAndPreventDefault(event, target, this.element); + } + }, - Interaction.prototype.currentAction = function currentAction() { - return this._interacting ? this.prepared.name : null; - }; + pointerOut: function (pointer, event, eventTarget) { + if (this.prepared.name) { return; } - Interaction.prototype.interacting = function interacting() { - return this._interacting; - }; + // Remove temporary event listeners for selector Interactables + if (!interactables.get(eventTarget)) { + events.remove(eventTarget, + PointerEvent? pEventTypes.move : 'mousemove', + listeners.pointerHover); + } - Interaction.prototype.stop = function stop() { - signals.fire('stop', { interaction: this }); + if (this.target && this.target.options.styleCursor && !this.interacting()) { + this.target._doc.documentElement.style.cursor = ''; + } + }, + + selectorDown: function (pointer, event, eventTarget, curEventTarget) { + var that = this, + // copy event to be used in timeout for IE8 + eventCopy = events.useAttachEvent? extend({}, event) : event, + element = eventTarget, + pointerIndex = this.addPointer(pointer), + action; + + this.holdTimers[pointerIndex] = setTimeout(function () { + that.pointerHold(events.useAttachEvent? eventCopy : pointer, eventCopy, eventTarget, curEventTarget); + }, defaultOptions._holdDuration); + + this.pointerIsDown = true; + + // Check if the down event hits the current inertia target + if (this.inertiaStatus.active && this.target.selector) { + // climb up the DOM tree from the event target + while (isElement(element)) { + + // if this element is the current inertia target element + if (element === this.element + // and the prospective action is the same as the ongoing one + && validateAction(this.target.getAction(pointer, event, this, this.element), this.target).name === this.prepared.name) { + + // stop inertia so that the next move will be a normal one + cancelFrame(this.inertiaStatus.i); + this.inertiaStatus.active = false; + + this.collectEventTargets(pointer, event, eventTarget, 'down'); + return; + } + element = parentElement(element); + } + } - if (this._interacting) { - signals.fire('stop-active', { interaction: this }); - signals.fire('stop-' + this.prepared.name, { interaction: this }); - } + // do nothing if interacting + if (this.interacting()) { + this.collectEventTargets(pointer, event, eventTarget, 'down'); + return; + } - this.target = this.element = null; + function pushMatches (interactable, selector, context) { + var elements = ie8MatchesSelector + ? context.querySelectorAll(selector) + : undefined; - this.pointerIsDown = this._interacting = false; - this.prepared.name = this.prevEvent = null; - }; + if (inContext(interactable, element) + && !testIgnore(interactable, element, eventTarget) + && testAllow(interactable, element, eventTarget) + && matchesSelector(element, selector, elements)) { - Interaction.prototype.getPointerIndex = function getPointerIndex(pointer) { - return this.mouse ? 0 : utils.indexOf(this.pointerIds, utils.getPointerId(pointer)); - }; + that.matches.push(interactable); + that.matchElements.push(element); + } + } - Interaction.prototype.updatePointer = function updatePointer(pointer) { - var id = utils.getPointerId(pointer); - var index = this.getPointerIndex(pointer); + // update pointer coords for defaultActionChecker to use + this.setEventXY(this.curCoords, [pointer]); + this.downEvent = event; - if (index === -1) { - index = this.pointerIds.length; - } + while (isElement(element) && !action) { + this.matches = []; + this.matchElements = []; - this.pointerIds[index] = id; - this.pointers[index] = pointer; + interactables.forEachSelector(pushMatches); - return index; - }; + action = this.validateSelector(pointer, event, this.matches, this.matchElements); + element = parentElement(element); + } - Interaction.prototype.removePointer = function removePointer(pointer) { - var id = utils.getPointerId(pointer); - var index = this.mouse ? 0 : utils.indexOf(this.pointerIds, id); + if (action) { + this.prepared.name = action.name; + this.prepared.axis = action.axis; + this.prepared.edges = action.edges; - if (index === -1) { - return; - } + this.collectEventTargets(pointer, event, eventTarget, 'down'); - this.pointers.splice(index, 1); - this.pointerIds.splice(index, 1); - this.downTargets.splice(index, 1); - this.downTimes.splice(index, 1); - this.holdTimers.splice(index, 1); - }; - - Interaction.prototype._updateEventTargets = function _updateEventTargets(target, currentTarget) { - this._eventTarget = target; - this._curEventTarget = currentTarget; - }; - - return Interaction; -})(); - -for (var i = 0, len = methodNames.length; i < len; i++) { - var method = methodNames[i]; - - listeners[method] = doOnInteractions(method); -} - -function doOnInteractions(method) { - return function (event) { - var eventTarget = utils.getActualElement(event.path ? event.path[0] : event.target); - var curEventTarget = utils.getActualElement(event.currentTarget); - var matches = []; // [ [pointer, interaction], ...] - - if (browser.supportsTouch && /touch/.test(event.type)) { - prevTouchTime = new Date().getTime(); - - for (var i = 0; i < event.changedTouches.length; i++) { - var pointer = event.changedTouches[i]; - var interaction = finder.search(pointer, event.type, eventTarget); - - matches.push([pointer, interaction || new Interaction()]); - } - } else { - var invalidPointer = false; - - if (!browser.supportsPointerEvent && /mouse/.test(event.type)) { - // ignore mouse events while touch interactions are active - for (var i = 0; i < scope.interactions.length && !invalidPointer; i++) { - invalidPointer = !scope.interactions[i].mouse && scope.interactions[i].pointerIsDown; - } + return this.pointerDown(pointer, event, eventTarget, curEventTarget, action); + } + else { + // do these now since pointerDown isn't being called from here + this.downTimes[pointerIndex] = new Date().getTime(); + this.downTargets[pointerIndex] = eventTarget; + pointerExtend(this.downPointer, pointer); + + copyCoords(this.prevCoords, this.curCoords); + this.pointerWasMoved = false; + } - // try to ignore mouse events that are simulated by the browser - // after a touch event - invalidPointer = invalidPointer || new Date().getTime() - prevTouchTime < 500; - } + this.collectEventTargets(pointer, event, eventTarget, 'down'); + }, - if (!invalidPointer) { - var interaction = finder.search(event, event.type, eventTarget); + // Determine action to be performed on next pointerMove and add appropriate + // style and event Listeners + pointerDown: function (pointer, event, eventTarget, curEventTarget, forceAction) { + if (!forceAction && !this.inertiaStatus.active && this.pointerWasMoved && this.prepared.name) { + this.checkAndPreventDefault(event, this.target, this.element); - if (!interaction) { + return; + } - interaction = new Interaction(); - interaction.mouse = /mouse/i.test(event.pointerType || event.type) - // MSPointerEvent.MSPOINTER_TYPE_MOUSE - || event.pointerType === 4 || !event.pointerType; - } + this.pointerIsDown = true; + this.downEvent = event; - matches.push([event, interaction]); - } - } + var pointerIndex = this.addPointer(pointer), + action; - for (var _iterator = matches, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + // If it is the second touch of a multi-touch gesture, keep the + // target the same and get a new action if a target was set by the + // first touch + if (this.pointerIds.length > 1 && this.target._element === this.element) { + var newAction = validateAction(forceAction || this.target.getAction(pointer, event, this, this.element), this.target); - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + if (withinInteractionLimit(this.target, this.element, newAction)) { + action = newAction; + } - var pointer = _ref[0]; - var interaction = _ref[1]; + this.prepared.name = null; + } + // Otherwise, set the target if there is no action prepared + else if (!this.prepared.name) { + var interactable = interactables.get(curEventTarget); + + if (interactable + && !testIgnore(interactable, curEventTarget, eventTarget) + && testAllow(interactable, curEventTarget, eventTarget) + && (action = validateAction(forceAction || interactable.getAction(pointer, event, this, curEventTarget), interactable, eventTarget)) + && withinInteractionLimit(interactable, curEventTarget, action)) { + this.target = interactable; + this.element = curEventTarget; + } + } - interaction._updateEventTargets(eventTarget, curEventTarget); - interaction[method](pointer, event, eventTarget, curEventTarget); - } - }; -} - -function endAll(event) { - for (var i = 0; i < scope.interactions.length; i++) { - var interaction = scope.interactions[i]; - - interaction.end(event); - signals.fire('endall', { event: event, interaction: interaction }); - } -} - -var docEvents = {/* 'eventType': listenerFunc */}; -var pEventTypes = browser.pEventTypes; - -if (scope.PointerEvent) { - docEvents[pEventTypes.down] = listeners.pointerDown; - docEvents[pEventTypes.move] = listeners.pointerMove; - docEvents[pEventTypes.up] = listeners.pointerUp; - docEvents[pEventTypes.cancel] = listeners.pointerUp; -} else { - docEvents.mousedown = listeners.pointerDown; - docEvents.mousemove = listeners.pointerMove; - docEvents.mouseup = listeners.pointerUp; - - docEvents.touchstart = listeners.pointerDown; - docEvents.touchmove = listeners.pointerMove; - docEvents.touchend = listeners.pointerUp; - docEvents.touchcancel = listeners.pointerUp; -} - -docEvents.blur = endAll; - -function onDocSignal(_ref2, signalName) { - var doc = _ref2.doc; - - var eventMethod = signalName.indexOf('add') === 0 ? events.add : events.remove; - - // delegate event listener - for (var eventType in scope.delegatedEvents) { - eventMethod(doc, eventType, events.delegateListener); - eventMethod(doc, eventType, events.delegateUseCapture, true); - } - - for (var eventType in docEvents) { - eventMethod(doc, eventType, docEvents[eventType]); - } -} - -scope.signals.on('add-document', onDocSignal); -scope.signals.on('remove-document', onDocSignal); - -Interaction.pointerMoveTolerance = 1; -Interaction.doOnInteractions = doOnInteractions; -Interaction.endAll = endAll; -Interaction.signals = signals; -Interaction.docEvents = docEvents; - -scope.endAllInteractions = endAll; - -module.exports = Interaction; - -},{"./scope":29,"./utils":39,"./utils/Signals":30,"./utils/browser":32,"./utils/events":35,"./utils/interactionFinder":40}],6:[function(require,module,exports){ -var actions = require('./index'); -var utils = require('../utils'); -var InteractEvent = require('../InteractEvent'); -var Interactable = require('../Interactable'); -var Interaction = require('../Interaction'); -var defaultOptions = require('../defaultOptions'); - -var drag = { - defaults: { - enabled: false, - mouseButtons: null, - - origin: null, - snap: null, - restrict: null, - inertia: null, - autoScroll: null, - - startAxis: 'xy', - lockAxis: 'xy' - }, - - checker: function (pointer, event, interactable) { - var dragOptions = interactable.options.drag; - - return dragOptions.enabled ? { name: 'drag', axis: dragOptions.lockAxis === 'start' ? dragOptions.startAxis : dragOptions.lockAxis } : null; - }, - - getCursor: function () { - return 'move'; - } -}; - -Interaction.signals.on('action-start', function (_ref) { - var interaction = _ref.interaction; - var event = _ref.event; - - if (interaction.prepared.name !== 'drag') { - return; - } - - var dragEvent = new InteractEvent(interaction, event, 'drag', 'start', interaction.element); - - interaction._interacting = true; - interaction.target.fire(dragEvent); - interaction.prevEvent = dragEvent; -}); - -Interaction.signals.on('before-action-move', function (_ref2) { - var interaction = _ref2.interaction; - - if (interaction.prepared.name !== 'drag') { - return; - } - - var axis = interaction.prepared.axis; - - if (axis === 'x') { - interaction.curCoords.page.y = interaction.startCoords.page.y; - interaction.curCoords.client.y = interaction.startCoords.client.y; - - interaction.pointerDelta.page.speed = Math.abs(interaction.pointerDelta.page.vx); - interaction.pointerDelta.client.speed = Math.abs(interaction.pointerDelta.client.vx); - interaction.pointerDelta.client.vy = 0; - interaction.pointerDelta.page.vy = 0; - } else if (axis === 'y') { - interaction.curCoords.page.x = interaction.startCoords.page.x; - interaction.curCoords.client.x = interaction.startCoords.client.x; - - interaction.pointerDelta.page.speed = Math.abs(interaction.pointerDelta.page.vy); - interaction.pointerDelta.client.speed = Math.abs(interaction.pointerDelta.client.vy); - interaction.pointerDelta.client.vx = 0; - interaction.pointerDelta.page.vx = 0; - } -}); - -Interaction.signals.on('action-move', function (_ref3) { - var interaction = _ref3.interaction; - var event = _ref3.event; - - if (interaction.prepared.name !== 'drag') { - return; - } - - var dragEvent = new InteractEvent(interaction, event, 'drag', 'move', interaction.element); - - var axis = interaction.prepared.axis; - - if (axis === 'x') { - dragEvent.pageY = interaction.startCoords.page.y; - dragEvent.clientY = interaction.startCoords.client.y; - dragEvent.dy = 0; - } else if (axis === 'y') { - dragEvent.pageX = interaction.startCoords.page.x; - dragEvent.clientX = interaction.startCoords.client.x; - dragEvent.dx = 0; - } - - interaction.target.fire(dragEvent); - interaction.prevEvent = dragEvent; - - // if the action was ended in a dragmove listener - if (!interaction.interacting()) { - return false; - } -}); - -Interaction.signals.on('action-end', function (_ref4) { - var interaction = _ref4.interaction; - var event = _ref4.event; - - if (interaction.prepared.name !== 'drag') { - return; - } - - var dragEvent = new InteractEvent(interaction, event, 'drag', 'end', interaction.element); - - interaction.target.fire(dragEvent); - interaction.prevEvent = dragEvent; -}); - -/*\ - * Interactable.draggable - [ method ] - * - * Gets or sets whether drag actions can be performed on the - * Interactable - * - = (boolean) Indicates if this can be the target of drag events - | var isDraggable = interact('ul li').draggable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on drag events (object makes the Interactable draggable) - = (object) This Interactable - | interact(element).draggable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | // the axis in which the first movement must be - | // for the drag sequence to start - | // 'xy' by default - any direction - | startAxis: 'x' || 'y' || 'xy', - | - | // 'xy' by default - don't restrict to one axis (move in any direction) - | // 'x' or 'y' to restrict movement to either axis - | // 'start' to restrict movement to the axis the drag started in - | lockAxis: 'x' || 'y' || 'xy' || 'start', - | - | // max number of drags that can happen concurrently - | // with elements of this Interactable. Infinity by default - | max: Infinity, - | - | // max number of drags that can target the same element+Interactable - | // 1 by default - | maxPerElement: 2 - | }); -\*/ -Interactable.prototype.draggable = function (options) { - if (utils.isObject(options)) { - this.options.drag.enabled = options.enabled === false ? false : true; - this.setPerAction('drag', options); - this.setOnEvents('drag', options); - - if (/^(xy|x|y|start)$/.test(options.lockAxis)) { - this.options.drag.lockAxis = options.lockAxis; - } - if (/^(xy|x|y)$/.test(options.startAxis)) { - this.options.drag.startAxis = options.startAxis; - } + var target = this.target, + options = target && target.options; - return this; - } + if (target && (forceAction || !this.prepared.name)) { + action = action || validateAction(forceAction || target.getAction(pointer, event, this, curEventTarget), target, this.element); - if (utils.isBool(options)) { - this.options.drag.enabled = options; + this.setEventXY(this.startCoords, this.pointers); - if (!options) { - this.ondragstart = this.ondragstart = this.ondragend = null; - } + if (!action) { return; } - return this; - } + if (options.styleCursor) { + target._doc.documentElement.style.cursor = getActionCursor(action); + } - return this.options.drag; -}; + this.resizeAxes = action.name === 'resize'? action.axis : null; -actions.drag = drag; -actions.names.push('drag'); -utils.merge(Interactable.eventTypes, ['dragstart', 'dragmove', 'draginertiastart', 'draginertiaresume', 'dragend']); -actions.methodDict.drag = 'draggable'; + if (action === 'gesture' && this.pointerIds.length < 2) { + action = null; + } -defaultOptions.drag = drag.defaults; + this.prepared.name = action.name; + this.prepared.axis = action.axis; + this.prepared.edges = action.edges; -module.exports = drag; + this.snapStatus.snappedX = this.snapStatus.snappedY = + this.restrictStatus.restrictedX = this.restrictStatus.restrictedY = NaN; -},{"../InteractEvent":3,"../Interactable":4,"../Interaction":5,"../defaultOptions":17,"../utils":39,"./index":9}],7:[function(require,module,exports){ -var actions = require('./index'); -var utils = require('../utils'); -var scope = require('../scope'); -var interact = require('../interact'); -var InteractEvent = require('../InteractEvent'); -var Interactable = require('../Interactable'); -var Interaction = require('../Interaction'); -var defaultOptions = require('../defaultOptions'); + this.downTimes[pointerIndex] = new Date().getTime(); + this.downTargets[pointerIndex] = eventTarget; + pointerExtend(this.downPointer, pointer); -var drop = { - defaults: { - enabled: false, - accept: null, - overlap: 'pointer' - } -}; + copyCoords(this.prevCoords, this.startCoords); + this.pointerWasMoved = false; -var dynamicDrop = false; + this.checkAndPreventDefault(event, target, this.element); + } + // if inertia is active try to resume action + else if (this.inertiaStatus.active + && curEventTarget === this.element + && validateAction(target.getAction(pointer, event, this, this.element), target).name === this.prepared.name) { -Interaction.signals.on('action-start', function (_ref2) { - var interaction = _ref2.interaction; - var event = _ref2.event; + cancelFrame(this.inertiaStatus.i); + this.inertiaStatus.active = false; - if (interaction.prepared.name !== 'drag') { - return; - } + this.checkAndPreventDefault(event, target, this.element); + } + }, - // reset active dropzones - interaction.activeDrops.dropzones = []; - interaction.activeDrops.elements = []; - interaction.activeDrops.rects = []; + setModifications: function (coords, preEnd) { + var target = this.target, + shouldMove = true, + shouldSnap = checkSnap(target, this.prepared.name) && (!target.options[this.prepared.name].snap.endOnly || preEnd), + shouldRestrict = checkRestrict(target, this.prepared.name) && (!target.options[this.prepared.name].restrict.endOnly || preEnd); - interaction.dropEvents = null; + if (shouldSnap ) { this.setSnapping (coords); } else { this.snapStatus .locked = false; } + if (shouldRestrict) { this.setRestriction(coords); } else { this.restrictStatus.restricted = false; } - if (!interaction.dynamicDrop) { - setActiveDrops(interaction, interaction.element); - } + if (shouldSnap && this.snapStatus.locked && !this.snapStatus.changed) { + shouldMove = shouldRestrict && this.restrictStatus.restricted && this.restrictStatus.changed; + } + else if (shouldRestrict && this.restrictStatus.restricted && !this.restrictStatus.changed) { + shouldMove = false; + } - var dragEvent = interaction.prevEvent; - var dropEvents = getDropEvents(interaction, event, dragEvent); + return shouldMove; + }, - if (dropEvents.activate) { - fireActiveDrops(interaction, dropEvents.activate); - } -}); + setStartOffsets: function (action, interactable, element) { + var rect = interactable.getRect(element), + origin = getOriginXY(interactable, element), + snap = interactable.options[this.prepared.name].snap, + restrict = interactable.options[this.prepared.name].restrict, + width, height; -InteractEvent.signals.on('new', function (_ref3) { - var interaction = _ref3.interaction; - var iEvent = _ref3.iEvent; - var event = _ref3.event; + if (rect) { + this.startOffset.left = this.startCoords.page.x - rect.left; + this.startOffset.top = this.startCoords.page.y - rect.top; - if (iEvent.type !== 'dragmove' && iEvent.type !== 'dragend') { - return; - } + this.startOffset.right = rect.right - this.startCoords.page.x; + this.startOffset.bottom = rect.bottom - this.startCoords.page.y; - var draggableElement = interaction.element; - var dragEvent = iEvent; - var dropResult = getDrop(dragEvent, event, draggableElement); + if ('width' in rect) { width = rect.width; } + else { width = rect.right - rect.left; } + if ('height' in rect) { height = rect.height; } + else { height = rect.bottom - rect.top; } + } + else { + this.startOffset.left = this.startOffset.top = this.startOffset.right = this.startOffset.bottom = 0; + } - interaction.dropTarget = dropResult.dropzone; - interaction.dropElement = dropResult.element; + this.snapOffsets.splice(0); + + var snapOffset = snap && snap.offset === 'startCoords' + ? { + x: this.startCoords.page.x - origin.x, + y: this.startCoords.page.y - origin.y + } + : snap && snap.offset || { x: 0, y: 0 }; + + if (rect && snap && snap.relativePoints && snap.relativePoints.length) { + for (var i = 0; i < snap.relativePoints.length; i++) { + this.snapOffsets.push({ + x: this.startOffset.left - (width * snap.relativePoints[i].x) + snapOffset.x, + y: this.startOffset.top - (height * snap.relativePoints[i].y) + snapOffset.y + }); + } + } + else { + this.snapOffsets.push(snapOffset); + } - interaction.dropEvents = getDropEvents(interaction, event, dragEvent); -}); + if (rect && restrict.elementRect) { + this.restrictOffset.left = this.startOffset.left - (width * restrict.elementRect.left); + this.restrictOffset.top = this.startOffset.top - (height * restrict.elementRect.top); -Interaction.signals.on('action-move', function (_ref4) { - var interaction = _ref4.interaction; + this.restrictOffset.right = this.startOffset.right - (width * (1 - restrict.elementRect.right)); + this.restrictOffset.bottom = this.startOffset.bottom - (height * (1 - restrict.elementRect.bottom)); + } + else { + this.restrictOffset.left = this.restrictOffset.top = this.restrictOffset.right = this.restrictOffset.bottom = 0; + } + }, + + /*\ + * Interaction.start + [ method ] + * + * Start an action with the given Interactable and Element as tartgets. The + * action must be enabled for the target Interactable and an appropriate number + * of pointers must be held down – 1 for drag/resize, 2 for gesture. + * + * Use it with `interactable.able({ manualStart: false })` to always + * [start actions manually](https://github.com/taye/interact.js/issues/114) + * + - action (object) The action to be performed - drag, resize, etc. + - interactable (Interactable) The Interactable to target + - element (Element) The DOM Element to target + = (object) interact + ** + | interact(target) + | .draggable({ + | // disable the default drag start by down->move + | manualStart: true + | }) + | // start dragging after the user holds the pointer down + | .on('hold', function (event) { + | var interaction = event.interaction; + | + | if (!interaction.interacting()) { + | interaction.start({ name: 'drag' }, + | event.interactable, + | event.currentTarget); + | } + | }); + \*/ + start: function (action, interactable, element) { + if (this.interacting() + || !this.pointerIsDown + || this.pointerIds.length < (action.name === 'gesture'? 2 : 1)) { + return; + } - if (interaction.prepared.name !== 'drag') { - return; - } + // if this interaction had been removed after stopping + // add it back + if (indexOf(interactions, this) === -1) { + interactions.push(this); + } - fireDropEvents(interaction, interaction.dropEvents); -}); + // set the startCoords if there was no prepared action + if (!this.prepared.name) { + this.setEventXY(this.startCoords); + } -Interaction.signals.on('action-end', function (_ref5) { - var interaction = _ref5.interaction; + this.prepared.name = action.name; + this.prepared.axis = action.axis; + this.prepared.edges = action.edges; + this.target = interactable; + this.element = element; - if (interaction.prepared.name === 'drag') { - fireDropEvents(interaction, interaction.dropEvents); - } -}); + this.setStartOffsets(action.name, interactable, element); + this.setModifications(this.startCoords.page); -Interaction.signals.on('stop-drag', function (_ref6) { - var interaction = _ref6.interaction; + this.prevEvent = this[this.prepared.name + 'Start'](this.downEvent); + }, - interaction.activeDrops.dropzones = interaction.activeDrops.elements = interaction.activeDrops.rects = interaction.dropEvents = null; -}); + pointerMove: function (pointer, event, eventTarget, curEventTarget, preEnd) { + if (this.inertiaStatus.active) { + var pageUp = this.inertiaStatus.upCoords.page; + var clientUp = this.inertiaStatus.upCoords.client; -function collectDrops(interaction, element) { - var drops = []; - var elements = []; + var inertiaPosition = { + pageX : pageUp.x + this.inertiaStatus.sx, + pageY : pageUp.y + this.inertiaStatus.sy, + clientX: clientUp.x + this.inertiaStatus.sx, + clientY: clientUp.y + this.inertiaStatus.sy + }; - element = element || interaction.element; + this.setEventXY(this.curCoords, [inertiaPosition]); + } + else { + this.recordPointer(pointer); + this.setEventXY(this.curCoords, this.pointers); + } - // collect all dropzones and their elements which qualify for a drop - for (var _iterator = scope.interactables, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + var duplicateMove = (this.curCoords.page.x === this.prevCoords.page.x + && this.curCoords.page.y === this.prevCoords.page.y + && this.curCoords.client.x === this.prevCoords.client.x + && this.curCoords.client.y === this.prevCoords.client.y); - var current = _ref; + var dx, dy, + pointerIndex = this.mouse? 0 : indexOf(this.pointerIds, getPointerId(pointer)); - if (!current.options.drop.enabled) { - continue; - } + // register movement greater than pointerMoveTolerance + if (this.pointerIsDown && !this.pointerWasMoved) { + dx = this.curCoords.client.x - this.startCoords.client.x; + dy = this.curCoords.client.y - this.startCoords.client.y; - var accept = current.options.drop.accept; + this.pointerWasMoved = hypot(dx, dy) > pointerMoveTolerance; + } - // test the draggable element against the dropzone's accept setting - if (utils.isElement(accept) && accept !== element || utils.isString(accept) && !utils.matchesSelector(element, accept)) { + if (!duplicateMove && (!this.pointerIsDown || this.pointerWasMoved)) { + if (this.pointerIsDown) { + clearTimeout(this.holdTimers[pointerIndex]); + } - continue; - } + this.collectEventTargets(pointer, event, eventTarget, 'move'); + } - // query for new elements if necessary - var dropElements = utils.isString(current.target) ? current._context.querySelectorAll(current.target) : [current.target]; + if (!this.pointerIsDown) { return; } - for (var i = 0; i < dropElements.length; i++) { - var currentElement = dropElements[i]; + if (duplicateMove && this.pointerWasMoved && !preEnd) { + this.checkAndPreventDefault(event, this.target, this.element); + return; + } - if (currentElement !== element) { - drops.push(current); - elements.push(currentElement); - } - } - } - - return { - elements: elements, - dropzones: drops - }; -} - -function fireActiveDrops(interaction, event) { - var prevElement = undefined; - - // loop through all active dropzones and trigger event - for (var i = 0; i < interaction.activeDrops.dropzones.length; i++) { - var current = interaction.activeDrops.dropzones[i]; - var currentElement = interaction.activeDrops.elements[i]; - - // prevent trigger of duplicate events on same element - if (currentElement !== prevElement) { - // set current element as event target - event.target = currentElement; - current.fire(event); - } - prevElement = currentElement; - } -} - -// Collect a new set of possible drops and save them in activeDrops. -// setActiveDrops should always be called when a drag has just started or a -// drag event happens while dynamicDrop is true -function setActiveDrops(interaction, dragElement) { - // get dropzones and their elements that could receive the draggable - var possibleDrops = collectDrops(interaction, dragElement, true); - - interaction.activeDrops.dropzones = possibleDrops.dropzones; - interaction.activeDrops.elements = possibleDrops.elements; - interaction.activeDrops.rects = []; - - for (var i = 0; i < interaction.activeDrops.dropzones.length; i++) { - interaction.activeDrops.rects[i] = interaction.activeDrops.dropzones[i].getRect(interaction.activeDrops.elements[i]); - } -} - -function getDrop(dragEvent, event, dragElement) { - var interaction = dragEvent.interaction; - var validDrops = []; - - if (dynamicDrop) { - setActiveDrops(interaction, dragElement); - } - - // collect all dropzones and their elements which qualify for a drop - for (var j = 0; j < interaction.activeDrops.dropzones.length; j++) { - var current = interaction.activeDrops.dropzones[j]; - var currentElement = interaction.activeDrops.elements[j]; - var rect = interaction.activeDrops.rects[j]; - - validDrops.push(current.dropCheck(dragEvent, event, interaction.target, dragElement, currentElement, rect) ? currentElement : null); - } - - // get the most appropriate dropzone based on DOM depth and order - var dropIndex = utils.indexOfDeepestElement(validDrops); - - return { - dropzone: interaction.activeDrops.dropzones[dropIndex] || null, - element: interaction.activeDrops.elements[dropIndex] || null - }; -} - -function getDropEvents(interaction, pointerEvent, dragEvent) { - var dropEvents = { - enter: null, - leave: null, - activate: null, - deactivate: null, - move: null, - drop: null - }; - - var tmpl = { - dragEvent: dragEvent, - interaction: interaction, - target: interaction.dropElement, - dropzone: interaction.dropTarget, - relatedTarget: dragEvent.target, - draggable: dragEvent.interactable, - timeStamp: dragEvent.timeStamp - }; - - if (interaction.dropElement !== interaction.prevDropElement) { - // if there was a prevDropTarget, create a dragleave event - if (interaction.prevDropTarget) { - dropEvents.leave = utils.extend({ type: 'dragleave' }, tmpl); - - dragEvent.dragLeave = dropEvents.leave.target = interaction.prevDropElement; - dragEvent.prevDropzone = dropEvents.leave.dropzone = interaction.prevDropTarget; - } - // if the dropTarget is not null, create a dragenter event - if (interaction.dropTarget) { - dropEvents.enter = { - dragEvent: dragEvent, - interaction: interaction, - target: interaction.dropElement, - dropzone: interaction.dropTarget, - relatedTarget: dragEvent.target, - draggable: dragEvent.interactable, - timeStamp: dragEvent.timeStamp, - type: 'dragenter' - }; - - dragEvent.dragEnter = interaction.dropElement; - dragEvent.dropzone = interaction.dropTarget; - } - } - - if (dragEvent.type === 'dragend' && interaction.dropTarget) { - dropEvents.drop = utils.extend({ type: 'drop' }, tmpl); - - dragEvent.dropzone = interaction.dropTarget; - dragEvent.relatedTarget = interaction.dropElement; - } - if (dragEvent.type === 'dragstart') { - dropEvents.activate = utils.extend({ type: 'dropactivate' }, tmpl); - - dropEvents.activate.target = null; - dropEvents.activate.dropzone = null; - } - if (dragEvent.type === 'dragend') { - dropEvents.deactivate = utils.extend({ type: 'dropdeactivate' }, tmpl); - - dropEvents.deactivate.target = null; - dropEvents.deactivate.dropzone = null; - } - if (dragEvent.type === 'dragmove' && interaction.dropTarget) { - dropEvents.move = utils.extend({ - dragmove: dragEvent, - type: 'dropmove' - }, tmpl); - - dragEvent.dropzone = interaction.dropTarget; - } - - return dropEvents; -} - -function fireDropEvents(interaction, dropEvents) { - if (dropEvents.leave) { - interaction.prevDropTarget.fire(dropEvents.leave); - } - if (dropEvents.move) { - interaction.dropTarget.fire(dropEvents.move); - } - if (dropEvents.enter) { - interaction.dropTarget.fire(dropEvents.enter); - } - if (dropEvents.drop) { - interaction.dropTarget.fire(dropEvents.drop); - } - if (dropEvents.move) { - interaction.dropTarget.fire(dropEvents.move); - } - if (dropEvents.deactivate) { - fireActiveDrops(interaction, dropEvents.deactivate); - } - - interaction.prevDropTarget = interaction.dropTarget; - interaction.prevDropElement = interaction.dropElement; -} - -/*\ - * Interactable.dropzone - [ method ] - * - * Returns or sets whether elements can be dropped onto this - * Interactable to trigger drop events - * - * Dropzones can receive the following events: - * - `dropactivate` and `dropdeactivate` when an acceptable drag starts and ends - * - `dragenter` and `dragleave` when a draggable enters and leaves the dropzone - * - `dragmove` when a draggable that has entered the dropzone is moved - * - `drop` when a draggable is dropped into this dropzone - * - * Use the `accept` option to allow only elements that match the given CSS - * selector or element. The value can be: - * - * - **an Element** - only that element can be dropped into this dropzone. - * - **a string**, - the element being dragged must match it as a CSS selector. - * - **`null`** - accept options is cleared - it accepts any element. - * - * Use the `overlap` option to set how drops are checked for. The allowed - * values are: - * - * - `'pointer'`, the pointer must be over the dropzone (default) - * - `'center'`, the draggable element's center must be over the dropzone - * - a number from 0-1 which is the `(intersection area) / (draggable area)`. - * e.g. `0.5` for drop to happen when half of the area of the draggable is - * over the dropzone - * - * Use the `checker` option to specify a function to check if a dragged - * element is over this Interactable. - * - | interact(target) - | .dropChecker(function(dragEvent, // related dragmove or dragend event - | event, // TouchEvent/PointerEvent/MouseEvent - | dropped, // bool result of the default checker - | dropzone, // dropzone Interactable - | dropElement, // dropzone elemnt - | draggable, // draggable Interactable - | draggableElement) {// draggable element - | - | return dropped && event.target.hasAttribute('allow-drop'); - | } - * - * - - options (boolean | object | null) #optional The new value to be set. - | interact('.drop').dropzone({ - | accept: '.can-drop' || document.getElementById('single-drop'), - | overlap: 'pointer' || 'center' || zeroToOne - | } - = (boolean | object) The current setting or this Interactable -\*/ -Interactable.prototype.dropzone = function (options) { - if (utils.isObject(options)) { - this.options.drop.enabled = options.enabled === false ? false : true; - - if (utils.isFunction(options.ondrop)) { - this.events.ondrop = options.ondrop; - } - if (utils.isFunction(options.ondropactivate)) { - this.events.ondropactivate = options.ondropactivate; - } - if (utils.isFunction(options.ondropdeactivate)) { - this.events.ondropdeactivate = options.ondropdeactivate; - } - if (utils.isFunction(options.ondragenter)) { - this.events.ondragenter = options.ondragenter; - } - if (utils.isFunction(options.ondragleave)) { - this.events.ondragleave = options.ondragleave; - } - if (utils.isFunction(options.ondropmove)) { - this.events.ondropmove = options.ondropmove; - } + // set pointer coordinate, time changes and speeds + setEventDeltas(this.pointerDelta, this.prevCoords, this.curCoords); + + if (!this.prepared.name) { return; } + + if (this.pointerWasMoved + // ignore movement while inertia is active + && (!this.inertiaStatus.active || (pointer instanceof InteractEvent && /inertiastart/.test(pointer.type)))) { + + // if just starting an action, calculate the pointer speed now + if (!this.interacting()) { + setEventDeltas(this.pointerDelta, this.prevCoords, this.curCoords); + + // check if a drag is in the correct axis + if (this.prepared.name === 'drag') { + var absX = Math.abs(dx), + absY = Math.abs(dy), + targetAxis = this.target.options.drag.axis, + axis = (absX > absY ? 'x' : absX < absY ? 'y' : 'xy'); + + // if the movement isn't in the axis of the interactable + if (axis !== 'xy' && targetAxis !== 'xy' && targetAxis !== axis) { + // cancel the prepared action + this.prepared.name = null; + + // then try to get a drag from another ineractable + + var element = eventTarget; + + // check element interactables + while (isElement(element)) { + var elementInteractable = interactables.get(element); + + if (elementInteractable + && elementInteractable !== this.target + && !elementInteractable.options.drag.manualStart + && elementInteractable.getAction(this.downPointer, this.downEvent, this, element).name === 'drag' + && checkAxis(axis, elementInteractable)) { + + this.prepared.name = 'drag'; + this.target = elementInteractable; + this.element = element; + break; + } + + element = parentElement(element); + } + + // if there's no drag from element interactables, + // check the selector interactables + if (!this.prepared.name) { + var thisInteraction = this; + + var getDraggable = function (interactable, selector, context) { + var elements = ie8MatchesSelector + ? context.querySelectorAll(selector) + : undefined; + + if (interactable === thisInteraction.target) { return; } + + if (inContext(interactable, eventTarget) + && !interactable.options.drag.manualStart + && !testIgnore(interactable, element, eventTarget) + && testAllow(interactable, element, eventTarget) + && matchesSelector(element, selector, elements) + && interactable.getAction(thisInteraction.downPointer, thisInteraction.downEvent, thisInteraction, element).name === 'drag' + && checkAxis(axis, interactable) + && withinInteractionLimit(interactable, element, 'drag')) { + + return interactable; + } + }; + + element = eventTarget; + + while (isElement(element)) { + var selectorInteractable = interactables.forEachSelector(getDraggable); + + if (selectorInteractable) { + this.prepared.name = 'drag'; + this.target = selectorInteractable; + this.element = element; + break; + } + + element = parentElement(element); + } + } + } + } + } + + var starting = !!this.prepared.name && !this.interacting(); + + if (starting + && (this.target.options[this.prepared.name].manualStart + || !withinInteractionLimit(this.target, this.element, this.prepared))) { + this.stop(event); + return; + } + + if (this.prepared.name && this.target) { + if (starting) { + this.start(this.prepared, this.target, this.element); + } + + var shouldMove = this.setModifications(this.curCoords.page, preEnd); + + // move if snapping or restriction doesn't prevent it + if (shouldMove || starting) { + this.prevEvent = this[this.prepared.name + 'Move'](event); + } + + this.checkAndPreventDefault(event, this.target, this.element); + } + } - if (/^(pointer|center)$/.test(options.overlap)) { - this.options.drop.overlap = options.overlap; - } else if (utils.isNumber(options.overlap)) { - this.options.drop.overlap = Math.max(Math.min(1, options.overlap), 0); - } - if ('accept' in options) { - this.options.drop.accept = options.accept; - } - if ('checker' in options) { - this.options.drop.checker = options.checker; - } + copyCoords(this.prevCoords, this.curCoords); - return this; - } + if (this.dragging || this.resizing) { + this.autoScrollMove(pointer); + } + }, - if (utils.isBool(options)) { - this.options.drop.enabled = options; + dragStart: function (event) { + var dragEvent = new InteractEvent(this, event, 'drag', 'start', this.element); - if (!options) { - this.ondragenter = this.ondragleave = this.ondrop = this.ondropactivate = this.ondropdeactivate = null; - } + this.dragging = true; + this.target.fire(dragEvent); - return this; - } + // reset active dropzones + this.activeDrops.dropzones = []; + this.activeDrops.elements = []; + this.activeDrops.rects = []; - return this.options.drop; -}; + if (!this.dynamicDrop) { + this.setActiveDrops(this.element); + } -Interactable.prototype.dropCheck = function (dragEvent, event, draggable, draggableElement, dropElement, rect) { - var dropped = false; + var dropEvents = this.getDropEvents(event, dragEvent); - // if the dropzone has no rect (eg. display: none) - // call the custom dropChecker or just return false - if (!(rect = rect || this.getRect(dropElement))) { - return this.options.drop.checker ? this.options.drop.checker(dragEvent, event, dropped, this, dropElement, draggable, draggableElement) : false; - } + if (dropEvents.activate) { + this.fireActiveDrops(dropEvents.activate); + } - var dropOverlap = this.options.drop.overlap; + return dragEvent; + }, + + dragMove: function (event) { + var target = this.target, + dragEvent = new InteractEvent(this, event, 'drag', 'move', this.element), + draggableElement = this.element, + drop = this.getDrop(dragEvent, event, draggableElement); + + this.dropTarget = drop.dropzone; + this.dropElement = drop.element; + + var dropEvents = this.getDropEvents(event, dragEvent); + + target.fire(dragEvent); + + if (dropEvents.leave) { this.prevDropTarget.fire(dropEvents.leave); } + if (dropEvents.enter) { this.dropTarget.fire(dropEvents.enter); } + if (dropEvents.move ) { this.dropTarget.fire(dropEvents.move ); } + + this.prevDropTarget = this.dropTarget; + this.prevDropElement = this.dropElement; + + return dragEvent; + }, + + resizeStart: function (event) { + var resizeEvent = new InteractEvent(this, event, 'resize', 'start', this.element); + + if (this.prepared.edges) { + var startRect = this.target.getRect(this.element); + + /* + * When using the `resizable.square` or `resizable.preserveAspectRatio` options, resizing from one edge + * will affect another. E.g. with `resizable.square`, resizing to make the right edge larger will make + * the bottom edge larger by the same amount. We call these 'linked' edges. Any linked edges will depend + * on the active edges and the edge being interacted with. + */ + if (this.target.options.resize.square || this.target.options.resize.preserveAspectRatio) { + var linkedEdges = extend({}, this.prepared.edges); + + linkedEdges.top = linkedEdges.top || (linkedEdges.left && !linkedEdges.bottom); + linkedEdges.left = linkedEdges.left || (linkedEdges.top && !linkedEdges.right ); + linkedEdges.bottom = linkedEdges.bottom || (linkedEdges.right && !linkedEdges.top ); + linkedEdges.right = linkedEdges.right || (linkedEdges.bottom && !linkedEdges.left ); + + this.prepared._linkedEdges = linkedEdges; + } + else { + this.prepared._linkedEdges = null; + } + + // if using `resizable.preserveAspectRatio` option, record aspect ratio at the start of the resize + if (this.target.options.resize.preserveAspectRatio) { + this.resizeStartAspectRatio = startRect.width / startRect.height; + } + + this.resizeRects = { + start : startRect, + current : extend({}, startRect), + restricted: extend({}, startRect), + previous : extend({}, startRect), + delta : { + left: 0, right : 0, width : 0, + top : 0, bottom: 0, height: 0 + } + }; + + resizeEvent.rect = this.resizeRects.restricted; + resizeEvent.deltaRect = this.resizeRects.delta; + } - if (dropOverlap === 'pointer') { - var origin = utils.getOriginXY(draggable, draggableElement, 'drag'); - var page = utils.getPageXY(dragEvent); + this.target.fire(resizeEvent); + + this.resizing = true; + + return resizeEvent; + }, + + resizeMove: function (event) { + var resizeEvent = new InteractEvent(this, event, 'resize', 'move', this.element); + + var edges = this.prepared.edges, + invert = this.target.options.resize.invert, + invertible = invert === 'reposition' || invert === 'negate'; + + if (edges) { + var dx = resizeEvent.dx, + dy = resizeEvent.dy, + + start = this.resizeRects.start, + current = this.resizeRects.current, + restricted = this.resizeRects.restricted, + delta = this.resizeRects.delta, + previous = extend(this.resizeRects.previous, restricted), + + originalEdges = edges; + + // `resize.preserveAspectRatio` takes precedence over `resize.square` + if (this.target.options.resize.preserveAspectRatio) { + var resizeStartAspectRatio = this.resizeStartAspectRatio; + + edges = this.prepared._linkedEdges; + + if ((originalEdges.left && originalEdges.bottom) + || (originalEdges.right && originalEdges.top)) { + dy = -dx / resizeStartAspectRatio; + } + else if (originalEdges.left || originalEdges.right) { dy = dx / resizeStartAspectRatio; } + else if (originalEdges.top || originalEdges.bottom) { dx = dy * resizeStartAspectRatio; } + } + else if (this.target.options.resize.square) { + edges = this.prepared._linkedEdges; + + if ((originalEdges.left && originalEdges.bottom) + || (originalEdges.right && originalEdges.top)) { + dy = -dx; + } + else if (originalEdges.left || originalEdges.right) { dy = dx; } + else if (originalEdges.top || originalEdges.bottom) { dx = dy; } + } + + // update the 'current' rect without modifications + if (edges.top ) { current.top += dy; } + if (edges.bottom) { current.bottom += dy; } + if (edges.left ) { current.left += dx; } + if (edges.right ) { current.right += dx; } + + if (invertible) { + // if invertible, copy the current rect + extend(restricted, current); + + if (invert === 'reposition') { + // swap edge values if necessary to keep width/height positive + var swap; + + if (restricted.top > restricted.bottom) { + swap = restricted.top; + + restricted.top = restricted.bottom; + restricted.bottom = swap; + } + if (restricted.left > restricted.right) { + swap = restricted.left; + + restricted.left = restricted.right; + restricted.right = swap; + } + } + } + else { + // if not invertible, restrict to minimum of 0x0 rect + restricted.top = Math.min(current.top, start.bottom); + restricted.bottom = Math.max(current.bottom, start.top); + restricted.left = Math.min(current.left, start.right); + restricted.right = Math.max(current.right, start.left); + } + + restricted.width = restricted.right - restricted.left; + restricted.height = restricted.bottom - restricted.top ; + + for (var edge in restricted) { + delta[edge] = restricted[edge] - previous[edge]; + } + + resizeEvent.edges = this.prepared.edges; + resizeEvent.rect = restricted; + resizeEvent.deltaRect = delta; + } - page.x += origin.x; - page.y += origin.y; + this.target.fire(resizeEvent); - var horizontal = page.x > rect.left && page.x < rect.right; - var vertical = page.y > rect.top && page.y < rect.bottom; + return resizeEvent; + }, - dropped = horizontal && vertical; - } + gestureStart: function (event) { + var gestureEvent = new InteractEvent(this, event, 'gesture', 'start', this.element); - var dragRect = draggable.getRect(draggableElement); + gestureEvent.ds = 0; - if (dragRect && dropOverlap === 'center') { - var cx = dragRect.left + dragRect.width / 2; - var cy = dragRect.top + dragRect.height / 2; + this.gesture.startDistance = this.gesture.prevDistance = gestureEvent.distance; + this.gesture.startAngle = this.gesture.prevAngle = gestureEvent.angle; + this.gesture.scale = 1; - dropped = cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom; - } + this.gesturing = true; - if (dragRect && utils.isNumber(dropOverlap)) { - var overlapArea = Math.max(0, Math.min(rect.right, dragRect.right) - Math.max(rect.left, dragRect.left)) * Math.max(0, Math.min(rect.bottom, dragRect.bottom) - Math.max(rect.top, dragRect.top)); + this.target.fire(gestureEvent); - var overlapRatio = overlapArea / (dragRect.width * dragRect.height); + return gestureEvent; + }, - dropped = overlapRatio >= dropOverlap; - } + gestureMove: function (event) { + if (!this.pointerIds.length) { + return this.prevEvent; + } - if (this.options.drop.checker) { - dropped = this.options.drop.checker(dragEvent, event, dropped, this, dropElement, draggable, draggableElement); - } + var gestureEvent; - return dropped; -}; + gestureEvent = new InteractEvent(this, event, 'gesture', 'move', this.element); + gestureEvent.ds = gestureEvent.scale - this.gesture.scale; -Interactable.signals.on('unset', function (_ref7) { - var interactable = _ref7.interactable; + this.target.fire(gestureEvent); - interactable.dropzone(false); -}); + this.gesture.prevAngle = gestureEvent.angle; + this.gesture.prevDistance = gestureEvent.distance; -Interactable.settingsMethods.push('dropChecker'); + if (gestureEvent.scale !== Infinity && + gestureEvent.scale !== null && + gestureEvent.scale !== undefined && + !isNaN(gestureEvent.scale)) { -Interaction.signals.on('new', function (interaction) { - interaction.dropTarget = null; // the dropzone a drag target might be dropped into - interaction.dropElement = null; // the element at the time of checking - interaction.prevDropTarget = null; // the dropzone that was recently dragged away from - interaction.prevDropElement = null; // the element at the time of checking - interaction.dropEvents = null; // the dropEvents related to the current drag event + this.gesture.scale = gestureEvent.scale; + } - interaction.activeDrops = { - dropzones: [], // the dropzones that are mentioned below - elements: [], // elements of dropzones that accept the target draggable - rects: [] }; -}); + return gestureEvent; + }, -// the rects of the elements mentioned above -Interaction.signals.on('stop', function (_ref8) { - var interaction = _ref8.interaction; + pointerHold: function (pointer, event, eventTarget) { + this.collectEventTargets(pointer, event, eventTarget, 'hold'); + }, - interaction.dropTarget = interaction.dropElement = interaction.prevDropTarget = interaction.prevDropElement = null; -}); + pointerUp: function (pointer, event, eventTarget, curEventTarget) { + var pointerIndex = this.mouse? 0 : indexOf(this.pointerIds, getPointerId(pointer)); -/*\ - * interact.dynamicDrop - [ method ] - * - * Returns or sets whether the dimensions of dropzone elements are - * calculated on every dragmove or only on dragstart for the default - * dropChecker - * - - newValue (boolean) #optional True to check on each move. False to check only before start - = (boolean | interact) The current setting or interact -\*/ -interact.dynamicDrop = function (newValue) { - if (utils.isBool(newValue)) { - //if (dragging && dynamicDrop !== newValue && !newValue) { - //calcRects(dropzones); - //} - - dynamicDrop = newValue; - - return interact; - } - return dynamicDrop; -}; - -utils.merge(Interactable.eventTypes, ['dragenter', 'dragleave', 'dropactivate', 'dropdeactivate', 'dropmove', 'drop']); -actions.methodDict.drop = 'dropzone'; - -defaultOptions.drop = drop.defaults; - -module.exports = drop; - -},{"../InteractEvent":3,"../Interactable":4,"../Interaction":5,"../defaultOptions":17,"../interact":20,"../scope":29,"../utils":39,"./index":9}],8:[function(require,module,exports){ -var actions = require('./index'); -var utils = require('../utils'); -var InteractEvent = require('../InteractEvent'); -var Interactable = require('../Interactable'); -var Interaction = require('../Interaction'); -var defaultOptions = require('../defaultOptions'); - -var gesture = { - defaults: { - enabled: false, - origin: null, - restrict: null - }, - - checker: function (pointer, event, interactable, element, interaction) { - if (interaction.pointerIds.length >= 2) { - return { name: 'gesture' }; - } + clearTimeout(this.holdTimers[pointerIndex]); - return null; - }, + this.collectEventTargets(pointer, event, eventTarget, 'up' ); + this.collectEventTargets(pointer, event, eventTarget, 'tap'); - getCursor: function () { - return ''; - } -}; + this.pointerEnd(pointer, event, eventTarget, curEventTarget); -Interaction.signals.on('action-start', function (_ref) { - var interaction = _ref.interaction; - var event = _ref.event; + this.removePointer(pointer); + }, - if (interaction.prepared.name !== 'gesture') { - return; - } + pointerCancel: function (pointer, event, eventTarget, curEventTarget) { + var pointerIndex = this.mouse? 0 : indexOf(this.pointerIds, getPointerId(pointer)); - var gestureEvent = new InteractEvent(interaction, event, 'gesture', 'start', interaction.element); + clearTimeout(this.holdTimers[pointerIndex]); - gestureEvent.ds = 0; + this.collectEventTargets(pointer, event, eventTarget, 'cancel'); + this.pointerEnd(pointer, event, eventTarget, curEventTarget); - interaction.gesture.startDistance = interaction.gesture.prevDistance = gestureEvent.distance; - interaction.gesture.startAngle = interaction.gesture.prevAngle = gestureEvent.angle; - interaction.gesture.scale = 1; + this.removePointer(pointer); + }, - interaction._interacting = true; + // http://www.quirksmode.org/dom/events/click.html + // >Events leading to dblclick + // + // IE8 doesn't fire down event before dblclick. + // This workaround tries to fire a tap and doubletap after dblclick + ie8Dblclick: function (pointer, event, eventTarget) { + if (this.prevTap + && event.clientX === this.prevTap.clientX + && event.clientY === this.prevTap.clientY + && eventTarget === this.prevTap.target) { - interaction.target.fire(gestureEvent); - interaction.prevEvent = gestureEvent; -}); + this.downTargets[0] = eventTarget; + this.downTimes[0] = new Date().getTime(); + this.collectEventTargets(pointer, event, eventTarget, 'tap'); + } + }, + + // End interact move events and stop auto-scroll unless inertia is enabled + pointerEnd: function (pointer, event, eventTarget, curEventTarget) { + var endEvent, + target = this.target, + options = target && target.options, + inertiaOptions = options && this.prepared.name && options[this.prepared.name].inertia, + inertiaStatus = this.inertiaStatus; + + if (this.interacting()) { + + if (inertiaStatus.active && !inertiaStatus.ending) { return; } + + var pointerSpeed, + now = new Date().getTime(), + inertiaPossible = false, + inertia = false, + smoothEnd = false, + endSnap = checkSnap(target, this.prepared.name) && options[this.prepared.name].snap.endOnly, + endRestrict = checkRestrict(target, this.prepared.name) && options[this.prepared.name].restrict.endOnly, + dx = 0, + dy = 0, + startEvent; + + if (this.dragging) { + if (options.drag.axis === 'x' ) { pointerSpeed = Math.abs(this.pointerDelta.client.vx); } + else if (options.drag.axis === 'y' ) { pointerSpeed = Math.abs(this.pointerDelta.client.vy); } + else /*options.drag.axis === 'xy'*/{ pointerSpeed = this.pointerDelta.client.speed; } + } + else { + pointerSpeed = this.pointerDelta.client.speed; + } + + // check if inertia should be started + inertiaPossible = (inertiaOptions && inertiaOptions.enabled + && this.prepared.name !== 'gesture' + && event !== inertiaStatus.startEvent); + + inertia = (inertiaPossible + && (now - this.curCoords.timeStamp) < 50 + && pointerSpeed > inertiaOptions.minSpeed + && pointerSpeed > inertiaOptions.endSpeed); + + if (inertiaPossible && !inertia && (endSnap || endRestrict)) { + + var snapRestrict = {}; + + snapRestrict.snap = snapRestrict.restrict = snapRestrict; + + if (endSnap) { + this.setSnapping(this.curCoords.page, snapRestrict); + if (snapRestrict.locked) { + dx += snapRestrict.dx; + dy += snapRestrict.dy; + } + } + + if (endRestrict) { + this.setRestriction(this.curCoords.page, snapRestrict); + if (snapRestrict.restricted) { + dx += snapRestrict.dx; + dy += snapRestrict.dy; + } + } + + if (dx || dy) { + smoothEnd = true; + } + } + + if (inertia || smoothEnd) { + copyCoords(inertiaStatus.upCoords, this.curCoords); + + this.pointers[0] = inertiaStatus.startEvent = startEvent = + new InteractEvent(this, event, this.prepared.name, 'inertiastart', this.element); + + inertiaStatus.t0 = now; + + target.fire(inertiaStatus.startEvent); + + if (inertia) { + inertiaStatus.vx0 = this.pointerDelta.client.vx; + inertiaStatus.vy0 = this.pointerDelta.client.vy; + inertiaStatus.v0 = pointerSpeed; + + this.calcInertia(inertiaStatus); + + var page = extend({}, this.curCoords.page), + origin = getOriginXY(target, this.element), + statusObject; + + page.x = page.x + inertiaStatus.xe - origin.x; + page.y = page.y + inertiaStatus.ye - origin.y; + + statusObject = { + useStatusXY: true, + x: page.x, + y: page.y, + dx: 0, + dy: 0, + snap: null + }; + + statusObject.snap = statusObject; + + dx = dy = 0; + + if (endSnap) { + var snap = this.setSnapping(this.curCoords.page, statusObject); + + if (snap.locked) { + dx += snap.dx; + dy += snap.dy; + } + } + + if (endRestrict) { + var restrict = this.setRestriction(this.curCoords.page, statusObject); + + if (restrict.restricted) { + dx += restrict.dx; + dy += restrict.dy; + } + } + + inertiaStatus.modifiedXe += dx; + inertiaStatus.modifiedYe += dy; + + inertiaStatus.i = reqFrame(this.boundInertiaFrame); + } + else { + inertiaStatus.smoothEnd = true; + inertiaStatus.xe = dx; + inertiaStatus.ye = dy; -Interaction.signals.on('action-move', function (_ref2) { - var interaction = _ref2.interaction; - var event = _ref2.event; + inertiaStatus.sx = inertiaStatus.sy = 0; - if (interaction.prepared.name !== 'gesture') { - return; - } + inertiaStatus.i = reqFrame(this.boundSmoothEndFrame); + } - var gestureEvent = new InteractEvent(interaction, event, 'gesture', 'move', interaction.element); + inertiaStatus.active = true; + return; + } - gestureEvent.ds = gestureEvent.scale - interaction.gesture.scale; + if (endSnap || endRestrict) { + // fire a move event at the snapped coordinates + this.pointerMove(pointer, event, eventTarget, curEventTarget, true); + } + } - interaction.target.fire(gestureEvent); + if (this.dragging) { + endEvent = new InteractEvent(this, event, 'drag', 'end', this.element); - interaction.gesture.prevAngle = gestureEvent.angle; - interaction.gesture.prevDistance = gestureEvent.distance; + var draggableElement = this.element, + drop = this.getDrop(endEvent, event, draggableElement); - if (gestureEvent.scale !== Infinity && gestureEvent.scale !== null && gestureEvent.scale !== undefined && !isNaN(gestureEvent.scale)) { + this.dropTarget = drop.dropzone; + this.dropElement = drop.element; - interaction.gesture.scale = gestureEvent.scale; - } + var dropEvents = this.getDropEvents(event, endEvent); - interaction.prevEvent = gestureEvent; + if (dropEvents.leave) { this.prevDropTarget.fire(dropEvents.leave); } + if (dropEvents.enter) { this.dropTarget.fire(dropEvents.enter); } + if (dropEvents.drop ) { this.dropTarget.fire(dropEvents.drop ); } + if (dropEvents.deactivate) { + this.fireActiveDrops(dropEvents.deactivate); + } - // if the action was ended in a gesturemove listener - if (!interaction.interacting()) { - return false; - } -}); + target.fire(endEvent); + } + else if (this.resizing) { + endEvent = new InteractEvent(this, event, 'resize', 'end', this.element); + target.fire(endEvent); + } + else if (this.gesturing) { + endEvent = new InteractEvent(this, event, 'gesture', 'end', this.element); + target.fire(endEvent); + } -Interaction.signals.on('action-end', function (_ref3) { - var interaction = _ref3.interaction; - var event = _ref3.event; + this.stop(event); + }, - if (interaction.prepared.name !== 'gesture') { - return; - } + collectDrops: function (element) { + var drops = [], + elements = [], + i; - var gestureEvent = new InteractEvent(interaction, event, 'gesture', 'end', interaction.element); + element = element || this.element; - interaction.target.fire(gestureEvent); - interaction.prevEvent = gestureEvent; -}); + // collect all dropzones and their elements which qualify for a drop + for (i = 0; i < interactables.length; i++) { + if (!interactables[i].options.drop.enabled) { continue; } -/*\ - * Interactable.gesturable - [ method ] - * - * Gets or sets whether multitouch gestures can be performed on the - * Interactable's element - * - = (boolean) Indicates if this can be the target of gesture events - | var isGestureable = interact(element).gesturable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on gesture events (makes the Interactable gesturable) - = (object) this Interactable - | interact(element).gesturable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | // limit multiple gestures. - | // See the explanation in @Interactable.draggable example - | max: Infinity, - | maxPerElement: 1, - | }); -\*/ -Interactable.prototype.gesturable = function (options) { - if (utils.isObject(options)) { - this.options.gesture.enabled = options.enabled === false ? false : true; - this.setPerAction('gesture', options); - this.setOnEvents('gesture', options); - - return this; - } - - if (utils.isBool(options)) { - this.options.gesture.enabled = options; - - if (!options) { - this.ongesturestart = this.ongesturestart = this.ongestureend = null; - } + var current = interactables[i], + accept = current.options.drop.accept; - return this; - } - - return this.options.gesture; -}; - -InteractEvent.signals.on('set-delta', function (_ref4) { - var interaction = _ref4.interaction; - var iEvent = _ref4.iEvent; - var action = _ref4.action; - var event = _ref4.event; - var starting = _ref4.starting; - var ending = _ref4.ending; - var deltaSource = _ref4.deltaSource; - - if (action !== 'gesture') { - return; - } - - var pointers = interaction.pointers; - - iEvent.touches = [pointers[0], pointers[1]]; - - if (starting) { - iEvent.distance = utils.touchDistance(pointers, deltaSource); - iEvent.box = utils.touchBBox(pointers); - iEvent.scale = 1; - iEvent.ds = 0; - iEvent.angle = utils.touchAngle(pointers, undefined, deltaSource); - iEvent.da = 0; - } else if (ending || event instanceof InteractEvent) { - iEvent.distance = interaction.prevEvent.distance; - iEvent.box = interaction.prevEvent.box; - iEvent.scale = interaction.prevEvent.scale; - iEvent.ds = iEvent.scale - 1; - iEvent.angle = interaction.prevEvent.angle; - iEvent.da = iEvent.angle - interaction.gesture.startAngle; - } else { - iEvent.distance = utils.touchDistance(pointers, deltaSource); - iEvent.box = utils.touchBBox(pointers); - iEvent.scale = iEvent.distance / interaction.gesture.startDistance; - iEvent.angle = utils.touchAngle(pointers, interaction.gesture.prevAngle, deltaSource); - - iEvent.ds = iEvent.scale - interaction.gesture.prevScale; - iEvent.da = iEvent.angle - interaction.gesture.prevAngle; - } -}); - -Interaction.signals.on('new', function (interaction) { - interaction.gesture = { - start: { x: 0, y: 0 }, - - startDistance: 0, // distance between two touches of touchStart - prevDistance: 0, - distance: 0, - - scale: 1, // gesture.distance / gesture.startDistance - - startAngle: 0, // angle of line joining two touches - prevAngle: 0 }; -}); - -// angle of the previous gesture event -actions.gesture = gesture; -actions.names.push('gesture'); -utils.merge(Interactable.eventTypes, ['gesturestart', 'gesturemove', 'gestureend']); -actions.methodDict.gesture = 'gesturable'; - -defaultOptions.gesture = gesture.defaults; - -module.exports = gesture; - -},{"../InteractEvent":3,"../Interactable":4,"../Interaction":5,"../defaultOptions":17,"../utils":39,"./index":9}],9:[function(require,module,exports){ -var actions = { - names: [], - methodDict: {} -}; - -module.exports = actions; - -},{}],10:[function(require,module,exports){ -var actions = require('./index'); -var utils = require('../utils'); -var browser = require('../utils/browser'); -var InteractEvent = require('../InteractEvent'); -var Interactable = require('../Interactable'); -var Interaction = require('../Interaction'); -var defaultOptions = require('../defaultOptions'); - -// Less Precision with touch input -var defaultMargin = browser.supportsTouch || browser.supportsPointerEvent ? 20 : 10; - -var resize = { - defaults: { - enabled: false, - mouseButtons: null, - - origin: null, - snap: null, - restrict: null, - inertia: null, - autoScroll: null, - - square: false, - preserveAspectRatio: false, - axis: 'xy', - - // use default margin - margin: NaN, - - // object with props left, right, top, bottom which are - // true/false values to resize when the pointer is over that edge, - // CSS selectors to match the handles for each direction - // or the Elements for each handle - edges: null, - - // a value of 'none' will limit the resize rect to a minimum of 0x0 - // 'negate' will alow the rect to have negative width/height - // 'reposition' will keep the width/height positive by swapping - // the top and bottom edges and/or swapping the left and right edges - invert: 'none' - }, - - checker: function (pointer, event, interactable, element, interaction, rect) { - if (!rect) { - return null; - } + // test the draggable element against the dropzone's accept setting + if ((isElement(accept) && accept !== element) + || (isString(accept) + && !matchesSelector(element, accept))) { - var page = utils.extend({}, interaction.curCoords.page); - var options = interactable.options; + continue; + } - if (options.resize.enabled) { - var resizeOptions = options.resize; - var resizeEdges = { left: false, right: false, top: false, bottom: false }; + // query for new elements if necessary + var dropElements = current.selector? current._context.querySelectorAll(current.selector) : [current._element]; - // if using resize.edges - if (utils.isObject(resizeOptions.edges)) { - for (var edge in resizeEdges) { - resizeEdges[edge] = checkResizeEdge(edge, resizeOptions.edges[edge], page, interaction._eventTarget, element, rect, resizeOptions.margin || defaultMargin); - } - - resizeEdges.left = resizeEdges.left && !resizeEdges.right; - resizeEdges.top = resizeEdges.top && !resizeEdges.bottom; + for (var j = 0, len = dropElements.length; j < len; j++) { + var currentElement = dropElements[j]; - if (resizeEdges.left || resizeEdges.right || resizeEdges.top || resizeEdges.bottom) { - return { - name: 'resize', - edges: resizeEdges - }; - } - } else { - var right = options.resize.axis !== 'y' && page.x > rect.right - defaultMargin; - var bottom = options.resize.axis !== 'x' && page.y > rect.bottom - defaultMargin; - - if (right || bottom) { - return { - name: 'resize', - axes: (right ? 'x' : '') + (bottom ? 'y' : '') - }; - } - } - } + if (currentElement === element) { + continue; + } - return null; - }, - - cursors: browser.isIe9OrOlder ? { - x: 'e-resize', - y: 's-resize', - xy: 'se-resize', - - top: 'n-resize', - left: 'w-resize', - bottom: 's-resize', - right: 'e-resize', - topleft: 'se-resize', - bottomright: 'se-resize', - topright: 'ne-resize', - bottomleft: 'ne-resize' - } : { - x: 'ew-resize', - y: 'ns-resize', - xy: 'nwse-resize', - - top: 'ns-resize', - left: 'ew-resize', - bottom: 'ns-resize', - right: 'ew-resize', - topleft: 'nwse-resize', - bottomright: 'nwse-resize', - topright: 'nesw-resize', - bottomleft: 'nesw-resize' - }, - - getCursor: function (action) { - if (action.axis) { - return resize.cursors[action.name + action.axis]; - } else if (action.edges) { - var cursorKey = ''; - var edgeNames = ['top', 'bottom', 'left', 'right']; - - for (var i = 0; i < 4; i++) { - if (action.edges[edgeNames[i]]) { - cursorKey += edgeNames[i]; - } - } + drops.push(current); + elements.push(currentElement); + } + } - return resize.cursors[cursorKey]; - } - } -}; - -Interaction.signals.on('action-start', function (_ref) { - var interaction = _ref.interaction; - var event = _ref.event; - - if (interaction.prepared.name !== 'resize') { - return; - } - - var resizeEvent = new InteractEvent(interaction, event, 'resize', 'start', interaction.element); - - if (interaction.prepared.edges) { - var startRect = interaction.target.getRect(interaction.element); - var resizeOptions = interaction.target.options.resize; - - /* - * When using the `resizable.square` or `resizable.preserveAspectRatio` options, resizing from one edge - * will affect another. E.g. with `resizable.square`, resizing to make the right edge larger will make - * the bottom edge larger by the same amount. We call these 'linked' edges. Any linked edges will depend - * on the active edges and the edge being interacted with. - */ - if (resizeOptions.square || resizeOptions.preserveAspectRatio) { - var linkedEdges = utils.extend({}, interaction.prepared.edges); - - linkedEdges.top = linkedEdges.top || linkedEdges.left && !linkedEdges.bottom; - linkedEdges.left = linkedEdges.left || linkedEdges.top && !linkedEdges.right; - linkedEdges.bottom = linkedEdges.bottom || linkedEdges.right && !linkedEdges.top; - linkedEdges.right = linkedEdges.right || linkedEdges.bottom && !linkedEdges.left; - - interaction.prepared._linkedEdges = linkedEdges; - } else { - interaction.prepared._linkedEdges = null; - } + return { + dropzones: drops, + elements: elements + }; + }, + + fireActiveDrops: function (event) { + var i, + current, + currentElement, + prevElement; + + // loop through all active dropzones and trigger event + for (i = 0; i < this.activeDrops.dropzones.length; i++) { + current = this.activeDrops.dropzones[i]; + currentElement = this.activeDrops.elements [i]; + + // prevent trigger of duplicate events on same element + if (currentElement !== prevElement) { + // set current element as event target + event.target = currentElement; + current.fire(event); + } + prevElement = currentElement; + } + }, - // if using `resizable.preserveAspectRatio` option, record aspect ratio at the start of the resize - if (resizeOptions.preserveAspectRatio) { - interaction.resizeStartAspectRatio = startRect.width / startRect.height; - } + // Collect a new set of possible drops and save them in activeDrops. + // setActiveDrops should always be called when a drag has just started or a + // drag event happens while dynamicDrop is true + setActiveDrops: function (dragElement) { + // get dropzones and their elements that could receive the draggable + var possibleDrops = this.collectDrops(dragElement, true); - interaction.resizeRects = { - start: startRect, - current: utils.extend({}, startRect), - restricted: utils.extend({}, startRect), - previous: utils.extend({}, startRect), - delta: { - left: 0, right: 0, width: 0, - top: 0, bottom: 0, height: 0 - } - }; + this.activeDrops.dropzones = possibleDrops.dropzones; + this.activeDrops.elements = possibleDrops.elements; + this.activeDrops.rects = []; - resizeEvent.rect = interaction.resizeRects.restricted; - resizeEvent.deltaRect = interaction.resizeRects.delta; - } + for (var i = 0; i < this.activeDrops.dropzones.length; i++) { + this.activeDrops.rects[i] = this.activeDrops.dropzones[i].getRect(this.activeDrops.elements[i]); + } + }, - interaction.target.fire(resizeEvent); + getDrop: function (dragEvent, event, dragElement) { + var validDrops = []; - interaction._interacting = true; + if (dynamicDrop) { + this.setActiveDrops(dragElement); + } - interaction.prevEvent = resizeEvent; -}); + // collect all dropzones and their elements which qualify for a drop + for (var j = 0; j < this.activeDrops.dropzones.length; j++) { + var current = this.activeDrops.dropzones[j], + currentElement = this.activeDrops.elements [j], + rect = this.activeDrops.rects [j]; -Interaction.signals.on('action-move', function (_ref2) { - var interaction = _ref2.interaction; - var event = _ref2.event; + validDrops.push(current.dropCheck(dragEvent, event, this.target, dragElement, currentElement, rect) + ? currentElement + : null); + } - if (interaction.prepared.name !== 'resize') { - return; - } + // get the most appropriate dropzone based on DOM depth and order + var dropIndex = indexOfDeepestElement(validDrops), + dropzone = this.activeDrops.dropzones[dropIndex] || null, + element = this.activeDrops.elements [dropIndex] || null; + + return { + dropzone: dropzone, + element: element + }; + }, + + getDropEvents: function (pointerEvent, dragEvent) { + var dropEvents = { + enter : null, + leave : null, + activate : null, + deactivate: null, + move : null, + drop : null + }; + + if (this.dropElement !== this.prevDropElement) { + // if there was a prevDropTarget, create a dragleave event + if (this.prevDropTarget) { + dropEvents.leave = { + target : this.prevDropElement, + dropzone : this.prevDropTarget, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + timeStamp : dragEvent.timeStamp, + type : 'dragleave' + }; + + dragEvent.dragLeave = this.prevDropElement; + dragEvent.prevDropzone = this.prevDropTarget; + } + // if the dropTarget is not null, create a dragenter event + if (this.dropTarget) { + dropEvents.enter = { + target : this.dropElement, + dropzone : this.dropTarget, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + timeStamp : dragEvent.timeStamp, + type : 'dragenter' + }; + + dragEvent.dragEnter = this.dropElement; + dragEvent.dropzone = this.dropTarget; + } + } - var resizeEvent = new InteractEvent(interaction, event, 'resize', 'move', interaction.element); - var resizeOptions = interaction.target.options.resize; - var invert = resizeOptions.invert; - var invertible = invert === 'reposition' || invert === 'negate'; + if (dragEvent.type === 'dragend' && this.dropTarget) { + dropEvents.drop = { + target : this.dropElement, + dropzone : this.dropTarget, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + timeStamp : dragEvent.timeStamp, + type : 'drop' + }; + + dragEvent.dropzone = this.dropTarget; + } + if (dragEvent.type === 'dragstart') { + dropEvents.activate = { + target : null, + dropzone : null, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + timeStamp : dragEvent.timeStamp, + type : 'dropactivate' + }; + } + if (dragEvent.type === 'dragend') { + dropEvents.deactivate = { + target : null, + dropzone : null, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + timeStamp : dragEvent.timeStamp, + type : 'dropdeactivate' + }; + } + if (dragEvent.type === 'dragmove' && this.dropTarget) { + dropEvents.move = { + target : this.dropElement, + dropzone : this.dropTarget, + relatedTarget: dragEvent.target, + draggable : dragEvent.interactable, + dragEvent : dragEvent, + interaction : this, + dragmove : dragEvent, + timeStamp : dragEvent.timeStamp, + type : 'dropmove' + }; + dragEvent.dropzone = this.dropTarget; + } - var edges = interaction.prepared.edges; + return dropEvents; + }, - if (edges) { - var start = interaction.resizeRects.start; - var current = interaction.resizeRects.current; - var restricted = interaction.resizeRects.restricted; - var delta = interaction.resizeRects.delta; - var previous = utils.extend(interaction.resizeRects.previous, restricted); - var originalEdges = edges; + currentAction: function () { + return (this.dragging && 'drag') || (this.resizing && 'resize') || (this.gesturing && 'gesture') || null; + }, - var dx = resizeEvent.dx; - var dy = resizeEvent.dy; + interacting: function () { + return this.dragging || this.resizing || this.gesturing; + }, - if (resizeOptions.preserveAspectRatio || resizeOptions.square) { - // `resize.preserveAspectRatio` takes precedence over `resize.square` - var startAspectRatio = resizeOptions.preserveAspectRatio ? interaction.resizeStartAspectRatio : 1; + clearTargets: function () { + this.target = this.element = null; - edges = interaction.prepared._linkedEdges; + this.dropTarget = this.dropElement = this.prevDropTarget = this.prevDropElement = null; + }, - if (originalEdges.left && originalEdges.bottom || originalEdges.right && originalEdges.top) { - dy = -dx / startAspectRatio; - } else if (originalEdges.left || originalEdges.right) { - dy = dx / startAspectRatio; - } else if (originalEdges.top || originalEdges.bottom) { - dx = dy * startAspectRatio; - } - } + stop: function (event) { + if (this.interacting()) { + autoScroll.stop(); + this.matches = []; + this.matchElements = []; - // update the 'current' rect without modifications - if (edges.top) { - current.top += dy; - } - if (edges.bottom) { - current.bottom += dy; - } - if (edges.left) { - current.left += dx; - } - if (edges.right) { - current.right += dx; - } + var target = this.target; - if (invertible) { - // if invertible, copy the current rect - utils.extend(restricted, current); + if (target.options.styleCursor) { + target._doc.documentElement.style.cursor = ''; + } - if (invert === 'reposition') { - // swap edge values if necessary to keep width/height positive - var swap = undefined; + // prevent Default only if were previously interacting + if (event && isFunction(event.preventDefault)) { + this.checkAndPreventDefault(event, target, this.element); + } - if (restricted.top > restricted.bottom) { - swap = restricted.top; + if (this.dragging) { + this.activeDrops.dropzones = this.activeDrops.elements = this.activeDrops.rects = null; + } + } - restricted.top = restricted.bottom; - restricted.bottom = swap; - } - if (restricted.left > restricted.right) { - swap = restricted.left; + this.clearTargets(); - restricted.left = restricted.right; - restricted.right = swap; - } - } - } else { - // if not invertible, restrict to minimum of 0x0 rect - restricted.top = Math.min(current.top, start.bottom); - restricted.bottom = Math.max(current.bottom, start.top); - restricted.left = Math.min(current.left, start.right); - restricted.right = Math.max(current.right, start.left); - } + this.pointerIsDown = this.snapStatus.locked = this.dragging = this.resizing = this.gesturing = false; + this.prepared.name = this.prevEvent = null; + this.inertiaStatus.resumeDx = this.inertiaStatus.resumeDy = 0; - restricted.width = restricted.right - restricted.left; - restricted.height = restricted.bottom - restricted.top; + // remove pointers if their ID isn't in this.pointerIds + for (var i = 0; i < this.pointers.length; i++) { + if (indexOf(this.pointerIds, getPointerId(this.pointers[i])) === -1) { + this.pointers.splice(i, 1); + } + } + }, - for (var edge in restricted) { - delta[edge] = restricted[edge] - previous[edge]; - } + inertiaFrame: function () { + var inertiaStatus = this.inertiaStatus, + options = this.target.options[this.prepared.name].inertia, + lambda = options.resistance, + t = new Date().getTime() / 1000 - inertiaStatus.t0; - resizeEvent.edges = interaction.prepared.edges; - resizeEvent.rect = restricted; - resizeEvent.deltaRect = delta; - } + if (t < inertiaStatus.te) { - interaction.target.fire(resizeEvent); + var progress = 1 - (Math.exp(-lambda * t) - inertiaStatus.lambda_v0) / inertiaStatus.one_ve_v0; - interaction.prevEvent = resizeEvent; + if (inertiaStatus.modifiedXe === inertiaStatus.xe && inertiaStatus.modifiedYe === inertiaStatus.ye) { + inertiaStatus.sx = inertiaStatus.xe * progress; + inertiaStatus.sy = inertiaStatus.ye * progress; + } + else { + var quadPoint = getQuadraticCurvePoint( + 0, 0, + inertiaStatus.xe, inertiaStatus.ye, + inertiaStatus.modifiedXe, inertiaStatus.modifiedYe, + progress); - // if the action was ended in a resizemove listener - if (!interaction.interacting()) { - return false; - } -}); + inertiaStatus.sx = quadPoint.x; + inertiaStatus.sy = quadPoint.y; + } -Interaction.signals.on('action-end', function (_ref3) { - var interaction = _ref3.interaction; - var event = _ref3.event; + this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - if (interaction.prepared.name !== 'resize') { - return; - } + inertiaStatus.i = reqFrame(this.boundInertiaFrame); + } + else { + inertiaStatus.ending = true; - var resizeEvent = new InteractEvent(interaction, event, 'resize', 'end', interaction.element); + inertiaStatus.sx = inertiaStatus.modifiedXe; + inertiaStatus.sy = inertiaStatus.modifiedYe; - interaction.target.fire(resizeEvent); - interaction.prevEvent = resizeEvent; -}); + this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); + this.pointerEnd(inertiaStatus.startEvent, inertiaStatus.startEvent); -/*\ - * Interactable.resizable - [ method ] - * - * Gets or sets whether resize actions can be performed on the - * Interactable - * - = (boolean) Indicates if this can be the target of resize elements - | var isResizeable = interact('input[type=text]').resizable(); - * or - - options (boolean | object) #optional true/false or An object with event listeners to be fired on resize events (object makes the Interactable resizable) - = (object) This Interactable - | interact(element).resizable({ - | onstart: function (event) {}, - | onmove : function (event) {}, - | onend : function (event) {}, - | - | edges: { - | top : true, // Use pointer coords to check for resize. - | left : false, // Disable resizing from left edge. - | bottom: '.resize-s',// Resize if pointer target matches selector - | right : handleEl // Resize if pointer target is the given Element - | }, - | - | // Width and height can be adjusted independently. When `true`, width and - | // height are adjusted at a 1:1 ratio. - | square: false, - | - | // Width and height can be adjusted independently. When `true`, width and - | // height maintain the aspect ratio they had when resizing started. - | preserveAspectRatio: false, - | - | // a value of 'none' will limit the resize rect to a minimum of 0x0 - | // 'negate' will allow the rect to have negative width/height - | // 'reposition' will keep the width/height positive by swapping - | // the top and bottom edges and/or swapping the left and right edges - | invert: 'none' || 'negate' || 'reposition' - | - | // limit multiple resizes. - | // See the explanation in the @Interactable.draggable example - | max: Infinity, - | maxPerElement: 1, - | }); - \*/ -Interactable.prototype.resizable = function (options) { - if (utils.isObject(options)) { - this.options.resize.enabled = options.enabled === false ? false : true; - this.setPerAction('resize', options); - this.setOnEvents('resize', options); - - if (/^x$|^y$|^xy$/.test(options.axis)) { - this.options.resize.axis = options.axis; - } else if (options.axis === null) { - this.options.resize.axis = defaultOptions.resize.axis; - } + inertiaStatus.active = inertiaStatus.ending = false; + } + }, - if (utils.isBool(options.preserveAspectRatio)) { - this.options.resize.preserveAspectRatio = options.preserveAspectRatio; - } else if (utils.isBool(options.square)) { - this.options.resize.square = options.square; - } + smoothEndFrame: function () { + var inertiaStatus = this.inertiaStatus, + t = new Date().getTime() - inertiaStatus.t0, + duration = this.target.options[this.prepared.name].inertia.smoothEndDuration; - return this; - } - if (utils.isBool(options)) { - this.options.resize.enabled = options; + if (t < duration) { + inertiaStatus.sx = easeOutQuad(t, 0, inertiaStatus.xe, duration); + inertiaStatus.sy = easeOutQuad(t, 0, inertiaStatus.ye, duration); - if (!options) { - this.onresizestart = this.onresizestart = this.onresizeend = null; - } + this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); - return this; - } - return this.options.resize; -}; - -function checkResizeEdge(name, value, page, element, interactableElement, rect, margin) { - // false, '', undefined, null - if (!value) { - return false; - } - - // true value, use pointer coords and element rect - if (value === true) { - // if dimensions are negative, "switch" edges - var width = utils.isNumber(rect.width) ? rect.width : rect.right - rect.left; - var height = utils.isNumber(rect.height) ? rect.height : rect.bottom - rect.top; - - if (width < 0) { - if (name === 'left') { - name = 'right'; - } else if (name === 'right') { - name = 'left'; - } - } - if (height < 0) { - if (name === 'top') { - name = 'bottom'; - } else if (name === 'bottom') { - name = 'top'; - } - } + inertiaStatus.i = reqFrame(this.boundSmoothEndFrame); + } + else { + inertiaStatus.ending = true; - if (name === 'left') { - return page.x < (width >= 0 ? rect.left : rect.right) + margin; - } - if (name === 'top') { - return page.y < (height >= 0 ? rect.top : rect.bottom) + margin; - } + inertiaStatus.sx = inertiaStatus.xe; + inertiaStatus.sy = inertiaStatus.ye; - if (name === 'right') { - return page.x > (width >= 0 ? rect.right : rect.left) - margin; - } - if (name === 'bottom') { - return page.y > (height >= 0 ? rect.bottom : rect.top) - margin; - } - } - - // the remaining checks require an element - if (!utils.isElement(element)) { - return false; - } - - return utils.isElement(value) - // the value is an element to use as a resize handle - ? value === element - // otherwise check if element matches value as selector - : utils.matchesUpTo(element, value, interactableElement); -} - -Interaction.signals.on('new', function (interaction) { - interaction.resizeAxes = 'xy'; -}); - -InteractEvent.signals.on('set-delta', function (_ref4) { - var interaction = _ref4.interaction; - var iEvent = _ref4.iEvent; - var action = _ref4.action; - - if (action !== 'resize' || !interaction.resizeAxes) { - return; - } - - var options = interaction.target.options; - - if (options.resize.square) { - if (interaction.resizeAxes === 'y') { - iEvent.dx = iEvent.dy; - } else { - iEvent.dy = iEvent.dx; - } - iEvent.axes = 'xy'; - } else { - iEvent.axes = interaction.resizeAxes; - - if (interaction.resizeAxes === 'x') { - iEvent.dy = 0; - } else if (interaction.resizeAxes === 'y') { - iEvent.dx = 0; - } - } -}); - -actions.resize = resize; -actions.names.push('resize'); -utils.merge(Interactable.eventTypes, ['resizestart', 'resizemove', 'resizeinertiastart', 'resizeinertiaresume', 'resizeend']); -actions.methodDict.resize = 'resizable'; - -defaultOptions.resize = resize.defaults; - -module.exports = resize; - -},{"../InteractEvent":3,"../Interactable":4,"../Interaction":5,"../defaultOptions":17,"../utils":39,"../utils/browser":32,"./index":9}],11:[function(require,module,exports){ -var raf = require('./utils/raf'); -var getWindow = require('./utils/window').getWindow; -var isWindow = require('./utils/isType').isWindow; -var domUtils = require('./utils/domUtils'); -var Interaction = require('./Interaction'); -var defaultOptions = require('./defaultOptions'); - -var autoScroll = { - defaults: { - enabled: false, - container: null, // the item that is scrolled (Window or HTMLElement) - margin: 60, - speed: 300 }, - - // the scroll speed in pixels per second - interaction: null, - i: null, // the handle returned by window.setInterval - x: 0, y: 0, // Direction each pulse is to scroll in - - isScrolling: false, - prevTime: 0, - - start: function (interaction) { - autoScroll.isScrolling = true; - raf.cancel(autoScroll.i); - - autoScroll.interaction = interaction; - autoScroll.prevTime = new Date().getTime(); - autoScroll.i = raf.request(autoScroll.scroll); - }, - - stop: function () { - autoScroll.isScrolling = false; - raf.cancel(autoScroll.i); - }, - - // scroll the window by the values in scroll.x/y - scroll: function () { - var options = autoScroll.interaction.target.options[autoScroll.interaction.prepared.name].autoScroll; - var container = options.container || getWindow(autoScroll.interaction.element); - var now = new Date().getTime(); - // change in time in seconds - var dt = (now - autoScroll.prevTime) / 1000; - // displacement - var s = options.speed * dt; - - if (s >= 1) { - if (isWindow(container)) { - container.scrollBy(autoScroll.x * s, autoScroll.y * s); - } else if (container) { - container.scrollLeft += autoScroll.x * s; - container.scrollTop += autoScroll.y * s; - } - - autoScroll.prevTime = now; - } + this.pointerMove(inertiaStatus.startEvent, inertiaStatus.startEvent); + this.pointerEnd(inertiaStatus.startEvent, inertiaStatus.startEvent); - if (autoScroll.isScrolling) { - raf.cancel(autoScroll.i); - autoScroll.i = raf.request(autoScroll.scroll); - } - }, - check: function (interactable, actionName) { - var options = interactable.options; - - return options[actionName].autoScroll && options[actionName].autoScroll.enabled; - }, - onInteractionMove: function (_ref) { - var interaction = _ref.interaction; - var pointer = _ref.pointer; - - if (!(interaction.interacting() && autoScroll.check(interaction.target, interaction.prepared.name))) { - return; - } + inertiaStatus.smoothEnd = + inertiaStatus.active = inertiaStatus.ending = false; + } + }, - if (interaction.simulation) { - autoScroll.x = autoScroll.y = 0; - return; - } + addPointer: function (pointer) { + var id = getPointerId(pointer), + index = this.mouse? 0 : indexOf(this.pointerIds, id); - var top = undefined; - var right = undefined; - var bottom = undefined; - var left = undefined; - - var options = interaction.target.options[interaction.prepared.name].autoScroll; - var container = options.container || getWindow(interaction.element); - - if (isWindow(container)) { - left = pointer.clientX < autoScroll.margin; - top = pointer.clientY < autoScroll.margin; - right = pointer.clientX > container.innerWidth - autoScroll.margin; - bottom = pointer.clientY > container.innerHeight - autoScroll.margin; - } else { - var rect = domUtils.getElementClientRect(container); - - left = pointer.clientX < rect.left + autoScroll.margin; - top = pointer.clientY < rect.top + autoScroll.margin; - right = pointer.clientX > rect.right - autoScroll.margin; - bottom = pointer.clientY > rect.bottom - autoScroll.margin; - } + if (index === -1) { + index = this.pointerIds.length; + } - autoScroll.x = right ? 1 : left ? -1 : 0; - autoScroll.y = bottom ? 1 : top ? -1 : 0; + this.pointerIds[index] = id; + this.pointers[index] = pointer; - if (!autoScroll.isScrolling) { - // set the autoScroll properties to those of the target - autoScroll.margin = options.margin; - autoScroll.speed = options.speed; + return index; + }, - autoScroll.start(interaction); - } - } -}; + removePointer: function (pointer) { + var id = getPointerId(pointer), + index = this.mouse? 0 : indexOf(this.pointerIds, id); -Interaction.signals.on('stop-active', function () { - autoScroll.stop(); -}); + if (index === -1) { return; } -Interaction.signals.on('action-move', autoScroll.onInteractionMove); + this.pointers .splice(index, 1); + this.pointerIds .splice(index, 1); + this.downTargets.splice(index, 1); + this.downTimes .splice(index, 1); + this.holdTimers .splice(index, 1); + }, -defaultOptions.perAction.autoScroll = autoScroll.defaults; + recordPointer: function (pointer) { + var index = this.mouse? 0: indexOf(this.pointerIds, getPointerId(pointer)); -module.exports = autoScroll; + if (index === -1) { return; } -},{"./Interaction":5,"./defaultOptions":17,"./utils/domUtils":34,"./utils/isType":41,"./utils/raf":45,"./utils/window":46}],12:[function(require,module,exports){ -var autoStart = require('./index'); -var Interaction = require('../Interaction'); + this.pointers[index] = pointer; + }, -Interaction.signals.on('new', function (interaction) { - interaction.delayTimer = null; -}); + collectEventTargets: function (pointer, event, eventTarget, eventType) { + var pointerIndex = this.mouse? 0 : indexOf(this.pointerIds, getPointerId(pointer)); -autoStart.signals.on('prepared', function (_ref) { - var interaction = _ref.interaction; + // do not fire a tap event if the pointer was moved before being lifted + if (eventType === 'tap' && (this.pointerWasMoved + // or if the pointerup target is different to the pointerdown target + || !(this.downTargets[pointerIndex] && this.downTargets[pointerIndex] === eventTarget))) { + return; + } - var actionName = interaction.prepared.name; + var targets = [], + elements = [], + element = eventTarget; + + function collectSelectors (interactable, selector, context) { + var els = ie8MatchesSelector + ? context.querySelectorAll(selector) + : undefined; + + if (interactable._iEvents[eventType] + && isElement(element) + && inContext(interactable, element) + && !testIgnore(interactable, element, eventTarget) + && testAllow(interactable, element, eventTarget) + && matchesSelector(element, selector, els)) { + + targets.push(interactable); + elements.push(element); + } + } - if (!actionName) { - return; - } + while (element) { + if (interact.isSet(element) && interact(element)._iEvents[eventType]) { + targets.push(interact(element)); + elements.push(element); + } - var delay = interaction.target.options[actionName].delay; + interactables.forEachSelector(collectSelectors); - if (delay > 0) { - interaction.delayTimer = setTimeout(function () { - interaction.start(interaction.prepared, interaction.target, interaction.element); - }, delay); - } -}); + element = parentElement(element); + } -Interaction.signals.on('move', function (_ref2) { - var interaction = _ref2.interaction; - var duplicate = _ref2.duplicate; + // create the tap event even if there are no listeners so that + // doubletap can still be created and fired + if (targets.length || eventType === 'tap') { + this.firePointers(pointer, event, eventTarget, targets, elements, eventType); + } + }, + + firePointers: function (pointer, event, eventTarget, targets, elements, eventType) { + var pointerIndex = this.mouse? 0 : indexOf(this.pointerIds, getPointerId(pointer)), + pointerEvent = {}, + i, + // for tap events + interval, createNewDoubleTap; + + // if it's a doubletap then the event properties would have been + // copied from the tap event and provided as the pointer argument + if (eventType === 'doubletap') { + pointerEvent = pointer; + } + else { + pointerExtend(pointerEvent, event); + if (event !== pointer) { + pointerExtend(pointerEvent, pointer); + } + + pointerEvent.preventDefault = preventOriginalDefault; + pointerEvent.stopPropagation = InteractEvent.prototype.stopPropagation; + pointerEvent.stopImmediatePropagation = InteractEvent.prototype.stopImmediatePropagation; + pointerEvent.interaction = this; + + pointerEvent.timeStamp = new Date().getTime(); + pointerEvent.originalEvent = event; + pointerEvent.originalPointer = pointer; + pointerEvent.type = eventType; + pointerEvent.pointerId = getPointerId(pointer); + pointerEvent.pointerType = this.mouse? 'mouse' : !supportsPointerEvent? 'touch' + : isString(pointer.pointerType) + ? pointer.pointerType + : [,,'touch', 'pen', 'mouse'][pointer.pointerType]; + } - if (interaction.pointerWasMoved && !duplicate) { - clearTimeout(interaction.delayTimer); - } -}); + if (eventType === 'tap') { + pointerEvent.dt = pointerEvent.timeStamp - this.downTimes[pointerIndex]; -// prevent regular down->move autoStart -autoStart.signals.on('before-start', function (_ref3) { - var interaction = _ref3.interaction; + interval = pointerEvent.timeStamp - this.tapTime; + createNewDoubleTap = !!(this.prevTap && this.prevTap.type !== 'doubletap' + && this.prevTap.target === pointerEvent.target + && interval < 500); - var actionName = interaction.prepared.name; + pointerEvent.double = createNewDoubleTap; - if (!actionName) { - return; - } + this.tapTime = pointerEvent.timeStamp; + } - var delay = interaction.target.options[actionName].delay; + for (i = 0; i < targets.length; i++) { + pointerEvent.currentTarget = elements[i]; + pointerEvent.interactable = targets[i]; + targets[i].fire(pointerEvent); - if (delay > 0) { - interaction.prepared.name = null; - } -}); + if (pointerEvent.immediatePropagationStopped + ||(pointerEvent.propagationStopped && elements[i + 1] !== pointerEvent.currentTarget)) { + break; + } + } -},{"../Interaction":5,"./index":15}],13:[function(require,module,exports){ -var autoStart = require('./index'); -var scope = require('../scope'); -var browser = require('../utils/browser'); + if (createNewDoubleTap) { + var doubleTap = {}; -var _require = require('../utils/isType'); + extend(doubleTap, pointerEvent); -var isElement = _require.isElement; + doubleTap.dt = interval; + doubleTap.type = 'doubletap'; -var _require2 = require('../utils/domUtils'); + this.collectEventTargets(doubleTap, event, eventTarget, 'doubletap'); -var matchesSelector = _require2.matchesSelector; -var parentNode = _require2.parentNode; + this.prevTap = doubleTap; + } + else if (eventType === 'tap') { + this.prevTap = pointerEvent; + } + }, -require('./index').setActionDefaults(require('../actions/drag')); + validateSelector: function (pointer, event, matches, matchElements) { + for (var i = 0, len = matches.length; i < len; i++) { + var match = matches[i], + matchElement = matchElements[i], + action = validateAction(match.getAction(pointer, event, this, matchElement), match); -autoStart.signals.on('before-start', function (_ref) { - var interaction = _ref.interaction; - var eventTarget = _ref.eventTarget; - var dx = _ref.dx; - var dy = _ref.dy; + if (action && withinInteractionLimit(match, matchElement, action)) { + this.target = match; + this.element = matchElement; - if (interaction.prepared.name !== 'drag') { - return; - } + return action; + } + } + }, - // check if a drag is in the correct axis - var absX = Math.abs(dx); - var absY = Math.abs(dy); - var dragOptions = interaction.target.options.drag; - var startAxis = dragOptions.startAxis; - var currentAxis = absX > absY ? 'x' : absX < absY ? 'y' : 'xy'; + setSnapping: function (pageCoords, status) { + var snap = this.target.options[this.prepared.name].snap, + targets = [], + target, + page, + i; - interaction.prepared.axis = dragOptions.lockAxis === 'start' ? currentAxis[0] // always lock to one axis even if currentAxis === 'xy' - : dragOptions.lockAxis; + status = status || this.snapStatus; - // if the movement isn't in the startAxis of the interactable - if (currentAxis !== 'xy' && startAxis !== 'xy' && startAxis !== currentAxis) { - // cancel the prepared action - interaction.prepared.name = null; + if (status.useStatusXY) { + page = { x: status.x, y: status.y }; + } + else { + var origin = getOriginXY(this.target, this.element); - // then try to get a drag from another ineractable + page = extend({}, pageCoords); - if (!interaction.prepared.name) { - (function () { + page.x -= origin.x; + page.y -= origin.y; + } - var element = eventTarget; + status.realX = page.x; + status.realY = page.y; - var getDraggable = function (interactable, selector, context) { - var elements = browser.useMatchesSelectorPolyfill ? context.querySelectorAll(selector) : undefined; + page.x = page.x - this.inertiaStatus.resumeDx; + page.y = page.y - this.inertiaStatus.resumeDy; - if (interactable === interaction.target) { - return; - } + var len = snap.targets? snap.targets.length : 0; - var options = interactable.options; + for (var relIndex = 0; relIndex < this.snapOffsets.length; relIndex++) { + var relative = { + x: page.x - this.snapOffsets[relIndex].x, + y: page.y - this.snapOffsets[relIndex].y + }; - if (!options.drag.manualStart && !interactable.testIgnoreAllow(options, element, eventTarget) && matchesSelector(element, selector, elements)) { + for (i = 0; i < len; i++) { + if (isFunction(snap.targets[i])) { + target = snap.targets[i](relative.x, relative.y, this); + } + else { + target = snap.targets[i]; + } - var _action = interactable.getAction(interaction.downPointer, interaction.downEvent, interaction, element); + if (!target) { continue; } - if (_action && _action.name === 'drag' && checkStartAxis(currentAxis, interactable) && autoStart.validateAction(_action, interactable, element)) { + targets.push({ + x: isNumber(target.x) ? (target.x + this.snapOffsets[relIndex].x) : relative.x, + y: isNumber(target.y) ? (target.y + this.snapOffsets[relIndex].y) : relative.y, - return interactable; + range: isNumber(target.range)? target.range: snap.range + }); + } } - } - }; - var action = null; - - // check all interactables - while (isElement(element)) { - var elementInteractable = scope.interactables.get(element); - - if (elementInteractable && elementInteractable !== interaction.target && !elementInteractable.options.drag.manualStart) { + var closest = { + target: null, + inRange: false, + distance: 0, + range: 0, + dx: 0, + dy: 0 + }; + + for (i = 0, len = targets.length; i < len; i++) { + target = targets[i]; + + var range = target.range, + dx = target.x - page.x, + dy = target.y - page.y, + distance = hypot(dx, dy), + inRange = distance <= range; + + // Infinite targets count as being out of range + // compared to non infinite ones that are in range + if (range === Infinity && closest.inRange && closest.range !== Infinity) { + inRange = false; + } + + if (!closest.target || (inRange + // is the closest target in range? + ? (closest.inRange && range !== Infinity + // the pointer is relatively deeper in this target + ? distance / range < closest.distance / closest.range + // this target has Infinite range and the closest doesn't + : (range === Infinity && closest.range !== Infinity) + // OR this target is closer that the previous closest + || distance < closest.distance) + // The other is not in range and the pointer is closer to this target + : (!closest.inRange && distance < closest.distance))) { + + if (range === Infinity) { + inRange = true; + } + + closest.target = target; + closest.distance = distance; + closest.range = range; + closest.inRange = inRange; + closest.dx = dx; + closest.dy = dy; + + status.range = range; + } + } - action = elementInteractable.getAction(interaction.downPointer, interaction.downEvent, interaction, element); - } - if (action && action.name === 'drag' && checkStartAxis(currentAxis, elementInteractable)) { + var snapChanged; - interaction.prepared.name = 'drag'; - interaction.target = elementInteractable; - interaction.element = element; - break; - } + if (closest.target) { + snapChanged = (status.snappedX !== closest.target.x || status.snappedY !== closest.target.y); - var selectorInteractable = scope.interactables.forEachSelector(getDraggable, element); + status.snappedX = closest.target.x; + status.snappedY = closest.target.y; + } + else { + snapChanged = true; - if (selectorInteractable) { - interaction.prepared.name = 'drag'; - interaction.target = selectorInteractable; - interaction.element = element; - break; - } + status.snappedX = NaN; + status.snappedY = NaN; + } - element = parentNode(element); - } - })(); - } - } -}); - -function checkStartAxis(startAxis, interactable) { - if (!interactable) { - return false; - } - - var thisAxis = interactable.options.drag.startAxis; - - return startAxis === 'xy' || thisAxis === 'xy' || thisAxis === startAxis; -} - -},{"../actions/drag":6,"../scope":29,"../utils/browser":32,"../utils/domUtils":34,"../utils/isType":41,"./index":15}],14:[function(require,module,exports){ -require('./index').setActionDefaults(require('../actions/gesture')); - -},{"../actions/gesture":8,"./index":15}],15:[function(require,module,exports){ -var interact = require('../interact'); -var Interactable = require('../Interactable'); -var Interaction = require('../Interaction'); -var actions = require('../actions'); -var defaultOptions = require('../defaultOptions'); -var browser = require('../utils/browser'); -var scope = require('../scope'); -var utils = require('../utils'); -var signals = require('../utils/Signals')['new'](); - -var autoStart = { - signals: signals, - withinInteractionLimit: withinInteractionLimit, - // Allow this many interactions to happen simultaneously - maxInteractions: Infinity, - perActionDefaults: { - manualStart: false, - max: Infinity, - maxPerElement: 1 - }, - setActionDefaults: function (action) { - utils.extend(action.defaults, autoStart.perActionDefaults); - } -}; - -// set cursor style on mousedown -Interaction.signals.on('down', function (_ref2) { - var interaction = _ref2.interaction; - var pointer = _ref2.pointer; - var event = _ref2.event; - var eventTarget = _ref2.eventTarget; - - if (interaction.interacting()) { - return; - } - - var actionInfo = getActionInfo(interaction, pointer, event, eventTarget); - prepare(interaction, actionInfo); -}); - -// set cursor style on mousemove -Interaction.signals.on('move', function (_ref3) { - var interaction = _ref3.interaction; - var pointer = _ref3.pointer; - var event = _ref3.event; - var eventTarget = _ref3.eventTarget; - - if (!interaction.mouse || interaction.pointerIsDown || interaction.interacting()) { - return; - } - - var actionInfo = getActionInfo(interaction, pointer, event, eventTarget); - prepare(interaction, actionInfo); -}); - -Interaction.signals.on('move', function (arg) { - var interaction = arg.interaction; - var event = arg.event; - - if (!interaction.pointerIsDown || interaction.interacting() || !interaction.pointerWasMoved || !interaction.prepared.name) { - return; - } - - signals.fire('before-start', arg); - - var target = interaction.target; - - if (interaction.prepared.name && target) { - // check manualStart and interaction limit - if (target.options[interaction.prepared.name].manualStart || !withinInteractionLimit(target, interaction.element, interaction.prepared)) { - interaction.stop(event); - } else { - interaction.start(interaction.prepared, target, interaction.element); - } - } -}); - -// Check if the current target supports the action. -// If so, return the validated action. Otherwise, return null -function validateAction(action, interactable, element) { - if (utils.isObject(action) && interactable.options[action.name].enabled && withinInteractionLimit(interactable, element, action)) { - return action; - } - - return null; -} - -function validateSelector(interaction, pointer, event, matches, matchElements) { - for (var i = 0, len = matches.length; i < len; i++) { - var match = matches[i]; - var matchElement = matchElements[i]; - var action = validateAction(match.getAction(pointer, event, interaction, matchElement), match, matchElement); - - if (action) { - return { - action: action, - target: match, - element: matchElement - }; - } - } + status.dx = closest.dx; + status.dy = closest.dy; - return {}; -} + status.changed = (snapChanged || (closest.inRange && !status.locked)); + status.locked = closest.inRange; -function getActionInfo(interaction, pointer, event, eventTarget) { - var matches = []; - var matchElements = []; + return status; + }, - var element = eventTarget; - var action = null; + setRestriction: function (pageCoords, status) { + var target = this.target, + restrict = target && target.options[this.prepared.name].restrict, + restriction = restrict && restrict.restriction, + page; - function pushMatches(interactable, selector, context) { - var elements = browser.useMatchesSelectorPolyfill ? context.querySelectorAll(selector) : undefined; - var options = interactable.options; + if (!restriction) { + return status; + } - if (interactable.testIgnoreAllow(options, element, eventTarget) && utils.matchesSelector(element, selector, elements)) { + status = status || this.restrictStatus; - matches.push(interactable); - matchElements.push(element); - } - } + page = status.useStatusXY + ? page = { x: status.x, y: status.y } + : page = extend({}, pageCoords); - while (utils.isElement(element)) { - matches = []; - matchElements = []; + if (status.snap && status.snap.locked) { + page.x += status.snap.dx || 0; + page.y += status.snap.dy || 0; + } - var elementInteractable = scope.interactables.get(element); + page.x -= this.inertiaStatus.resumeDx; + page.y -= this.inertiaStatus.resumeDy; - if (elementInteractable && (action = validateAction(elementInteractable.getAction(pointer, event, interaction, element), elementInteractable, element)) && !elementInteractable.options[action.name].manualStart) { - return { - element: element, - action: action, - target: elementInteractable - }; - } else { - scope.interactables.forEachSelector(pushMatches, element); + status.dx = 0; + status.dy = 0; + status.restricted = false; - var actionInfo = validateSelector(interaction, pointer, event, matches, matchElements); + var rect, restrictedX, restrictedY; - if (actionInfo.action && !actionInfo.target.options[actionInfo.action.name].manualStart) { - return actionInfo; - } - } + if (isString(restriction)) { + if (restriction === 'parent') { + restriction = parentElement(this.element); + } + else if (restriction === 'self') { + restriction = target.getRect(this.element); + } + else { + restriction = closest(this.element, restriction); + } - element = utils.parentNode(element); - } + if (!restriction) { return status; } + } - return {}; -} + if (isFunction(restriction)) { + restriction = restriction(page.x, page.y, this.element); + } -function prepare(interaction, _ref4) { - var action = _ref4.action; - var target = _ref4.target; - var element = _ref4.element; + if (isElement(restriction)) { + restriction = getElementRect(restriction); + } - action = action || {}; + rect = restriction; - if (interaction.target && interaction.target.options.styleCursor) { - interaction.target._doc.documentElement.style.cursor = ''; - } + if (!restriction) { + restrictedX = page.x; + restrictedY = page.y; + } + // object is assumed to have + // x, y, width, height or + // left, top, right, bottom + else if ('x' in restriction && 'y' in restriction) { + restrictedX = Math.max(Math.min(rect.x + rect.width - this.restrictOffset.right , page.x), rect.x + this.restrictOffset.left); + restrictedY = Math.max(Math.min(rect.y + rect.height - this.restrictOffset.bottom, page.y), rect.y + this.restrictOffset.top ); + } + else { + restrictedX = Math.max(Math.min(rect.right - this.restrictOffset.right , page.x), rect.left + this.restrictOffset.left); + restrictedY = Math.max(Math.min(rect.bottom - this.restrictOffset.bottom, page.y), rect.top + this.restrictOffset.top ); + } - interaction.target = target; - interaction.element = element; - utils.copyAction(interaction.prepared, action); + status.dx = restrictedX - page.x; + status.dy = restrictedY - page.y; - if (target && target.options.styleCursor) { - var cursor = action ? actions[action.name].getCursor(action) : ''; - interaction.target._doc.documentElement.style.cursor = cursor; - } + status.changed = status.restrictedX !== restrictedX || status.restrictedY !== restrictedY; + status.restricted = !!(status.dx || status.dy); - signals.fire('prepared', { interaction: interaction }); -} + status.restrictedX = restrictedX; + status.restrictedY = restrictedY; -Interaction.signals.on('stop', function (_ref5) { - var interaction = _ref5.interaction; + return status; + }, - var target = interaction.target; + checkAndPreventDefault: function (event, interactable, element) { + if (!(interactable = interactable || this.target)) { return; } - if (target && target.options.styleCursor) { - target._doc.documentElement.style.cursor = ''; - } -}); + var options = interactable.options, + prevent = options.preventDefault; -Interactable.prototype.getAction = function (pointer, event, interaction, element) { - var action = this.defaultActionChecker(pointer, event, interaction, element); + if (prevent === 'auto' && element && !/^(input|select|textarea)$/i.test(event.target.nodeName)) { + // do not preventDefault on pointerdown if the prepared action is a drag + // and dragging can only start from a certain direction - this allows + // a touch to pan the viewport if a drag isn't in the right direction + if (/down|start/i.test(event.type) + && this.prepared.name === 'drag' && options.drag.axis !== 'xy') { - if (this.options.actionChecker) { - return this.options.actionChecker(pointer, event, action, this, element, interaction); - } + return; + } - return action; -}; + // with manualStart, only preventDefault while interacting + if (options[this.prepared.name] && options[this.prepared.name].manualStart + && !this.interacting()) { + return; + } -/*\ - * Interactable.actionChecker - [ method ] - * - * Gets or sets the function used to check action to be performed on - * pointerDown - * - - checker (function | null) #optional A function which takes a pointer event, defaultAction string, interactable, element and interaction as parameters and returns an object with name property 'drag' 'resize' or 'gesture' and optionally an `edges` object with boolean 'top', 'left', 'bottom' and right props. - = (Function | Interactable) The checker function or this Interactable - * - | interact('.resize-drag') - | .resizable(true) - | .draggable(true) - | .actionChecker(function (pointer, event, action, interactable, element, interaction) { - | - | if (interact.matchesSelector(event.target, '.drag-handle') { - | // force drag with handle target - | action.name = drag; - | } - | else { - | // resize from the top and right edges - | action.name = 'resize'; - | action.edges = { top: true, right: true }; - | } - | - | return action; - | }); -\*/ -Interactable.prototype.actionChecker = function (checker) { - if (utils.isFunction(checker)) { - this.options.actionChecker = checker; - - return this; - } - - if (checker === null) { - delete this.options.actionChecker; - - return this; - } - - return this.options.actionChecker; -}; - -/*\ - * Interactable.styleCursor - [ method ] - * - * Returns or sets whether the the cursor should be changed depending on the - * action that would be performed if the mouse were pressed and dragged. - * - - newValue (boolean) #optional - = (boolean | Interactable) The current setting or this Interactable -\*/ -Interactable.prototype.styleCursor = function (newValue) { - if (utils.isBool(newValue)) { - this.options.styleCursor = newValue; - - return this; - } - - if (newValue === null) { - delete this.options.styleCursor; - - return this; - } - - return this.options.styleCursor; -}; - -Interactable.prototype.defaultActionChecker = function (pointer, event, interaction, element) { - var rect = this.getRect(element); - var buttons = event.buttons || ({ - 0: 1, - 1: 4, - 3: 8, - 4: 16 - })[event.button]; - var action = null; - - for (var _iterator = actions.names, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + event.preventDefault(); + return; + } - var actionName = _ref; + if (prevent === 'always') { + event.preventDefault(); + return; + } + }, + + calcInertia: function (status) { + var inertiaOptions = this.target.options[this.prepared.name].inertia, + lambda = inertiaOptions.resistance, + inertiaDur = -Math.log(inertiaOptions.endSpeed / status.v0) / lambda; + + status.x0 = this.prevEvent.pageX; + status.y0 = this.prevEvent.pageY; + status.t0 = status.startEvent.timeStamp / 1000; + status.sx = status.sy = 0; + + status.modifiedXe = status.xe = (status.vx0 - inertiaDur) / lambda; + status.modifiedYe = status.ye = (status.vy0 - inertiaDur) / lambda; + status.te = inertiaDur; + + status.lambda_v0 = lambda / status.v0; + status.one_ve_v0 = 1 - inertiaOptions.endSpeed / status.v0; + }, + + autoScrollMove: function (pointer) { + if (!(this.interacting() + && checkAutoScroll(this.target, this.prepared.name))) { + return; + } - // check mouseButton setting if the pointer is down - if (interaction.pointerIsDown && interaction.mouse && (buttons & this.options[actionName].mouseButtons) === 0) { - continue; - } + if (this.inertiaStatus.active) { + autoScroll.x = autoScroll.y = 0; + return; + } - action = actions[actionName].checker(pointer, event, this, element, interaction, rect); + var top, + right, + bottom, + left, + options = this.target.options[this.prepared.name].autoScroll, + container = options.container || getWindow(this.element); + + if (isWindow(container)) { + left = pointer.clientX < autoScroll.margin; + top = pointer.clientY < autoScroll.margin; + right = pointer.clientX > container.innerWidth - autoScroll.margin; + bottom = pointer.clientY > container.innerHeight - autoScroll.margin; + } + else { + var rect = getElementClientRect(container); - if (action) { - return action; - } - } -}; - -function withinInteractionLimit(interactable, element, action) { - var options = interactable.options; - var maxActions = options[action.name].max; - var maxPerElement = options[action.name].maxPerElement; - var activeInteractions = 0; - var targetCount = 0; - var targetElementCount = 0; - - // no actions if any of these values == 0 - if (!(maxActions && maxPerElement && autoStart.maxInteractions)) { - return; - } - - for (var i = 0, len = scope.interactions.length; i < len; i++) { - var interaction = scope.interactions[i]; - var otherAction = interaction.prepared.name; - - if (!interaction.interacting()) { - continue; - } + left = pointer.clientX < rect.left + autoScroll.margin; + top = pointer.clientY < rect.top + autoScroll.margin; + right = pointer.clientX > rect.right - autoScroll.margin; + bottom = pointer.clientY > rect.bottom - autoScroll.margin; + } - activeInteractions++; + autoScroll.x = (right ? 1: left? -1: 0); + autoScroll.y = (bottom? 1: top? -1: 0); - if (activeInteractions >= autoStart.maxInteractions) { - return false; - } + if (!autoScroll.isScrolling) { + // set the autoScroll properties to those of the target + autoScroll.margin = options.margin; + autoScroll.speed = options.speed; - if (interaction.target !== interactable) { - continue; - } + autoScroll.start(this); + } + }, - targetCount += otherAction === action.name | 0; + _updateEventTargets: function (target, currentTarget) { + this._eventTarget = target; + this._curEventTarget = currentTarget; + } - if (targetCount >= maxActions) { - return false; - } + }; - if (interaction.element === element) { - targetElementCount++; + function getInteractionFromPointer (pointer, eventType, eventTarget) { + var i = 0, len = interactions.length, + mouseEvent = (/mouse/i.test(pointer.pointerType || eventType) + // MSPointerEvent.MSPOINTER_TYPE_MOUSE + || pointer.pointerType === 4 + || !event.pointerType + ), + interaction; + + var id = getPointerId(pointer); + + // try to resume inertia with a new pointer + if (/down|start/i.test(eventType)) { + for (i = 0; i < len; i++) { + interaction = interactions[i]; + + var element = eventTarget; + + if (interaction.inertiaStatus.active && interaction.target.options[interaction.prepared.name].inertia.allowResume + && (interaction.mouse === mouseEvent)) { + while (element) { + // if the element is the interaction element + if (element === interaction.element) { + return interaction; + } + element = parentElement(element); + } + } + } + } - if (otherAction !== action.name || targetElementCount >= maxPerElement) { - return false; - } - } - } - - return autoStart.maxInteractions > 0; -} - -/*\ - * interact.maxInteractions - [ method ] - ** - * Returns or sets the maximum number of concurrent interactions allowed. - * By default only 1 interaction is allowed at a time (for backwards - * compatibility). To allow multiple interactions on the same Interactables - * and elements, you need to enable it in the draggable, resizable and - * gesturable `'max'` and `'maxPerElement'` options. - ** - - newValue (number) #optional Any number. newValue <= 0 means no interactions. -\*/ -interact.maxInteractions = function (newValue) { - if (utils.isNumber(newValue)) { - autoStart.maxInteractions = newValue; - - return this; - } - - return autoStart.maxInteractions; -}; - -Interactable.settingsMethods.push('styleCursor'); -Interactable.settingsMethods.push('actionChecker'); -Interactable.settingsMethods.push('ignoreFrom'); -Interactable.settingsMethods.push('allowFrom'); - -defaultOptions.base.actionChecker = null; -defaultOptions.base.ignoreFrom = null; -defaultOptions.base.allowFrom = null; -defaultOptions.base.styleCursor = true; - -utils.extend(defaultOptions.perAction, autoStart.perActionDefaults); - -module.exports = autoStart; - -},{"../Interactable":4,"../Interaction":5,"../actions":9,"../defaultOptions":17,"../interact":20,"../scope":29,"../utils":39,"../utils/Signals":30,"../utils/browser":32}],16:[function(require,module,exports){ -require('./index').setActionDefaults(require('../actions/resize')); - -},{"../actions/resize":10,"./index":15}],17:[function(require,module,exports){ -module.exports = { - base: { - accept: null, - preventDefault: 'auto', - deltaSource: 'page', - allowFrom: null - }, - - perAction: { - origin: { x: 0, y: 0 }, - - // only allow left button by default - // see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons#Return_value - mouseButtons: 1, - - inertia: { - enabled: false, - resistance: 10, // the lambda in exponential decay - minSpeed: 100, // target speed must be above this for inertia to start - endSpeed: 10, // the speed at which inertia is slow enough to stop - allowResume: true, // allow resuming an action in inertia phase - smoothEndDuration: 300 } - } -}; -// animate to snap/restrict endOnly if there's no inertia - -},{}],18:[function(require,module,exports){ -/* browser entry point */ - -// Legacy browser support -require('./legacyBrowsers'); - -// pointerEvents -require('./pointerEvents'); -require('./pointerEvents/holdRepeat'); -require('./pointerEvents/interactableTargets'); - -// inertia -require('./inertia'); - -// modifiers -require('./modifiers/snap'); -require('./modifiers/restrict'); - -// delay -require('./autoStart/delay'); - -// actions -require('./actions/gesture'); -require('./actions/resize'); -require('./actions/drag'); -require('./actions/drop'); - -// autoStart actions -require('./autoStart/gesture'); -require('./autoStart/resize'); -require('./autoStart/drag'); - -// Interactable preventDefault setting -require('./interactablePreventDefault.js'); - -// autoScroll -require('./autoScroll'); - -// export interact -module.exports = require('./interact'); - -},{"./actions/drag":6,"./actions/drop":7,"./actions/gesture":8,"./actions/resize":10,"./autoScroll":11,"./autoStart/delay":12,"./autoStart/drag":13,"./autoStart/gesture":14,"./autoStart/resize":16,"./inertia":19,"./interact":20,"./interactablePreventDefault.js":21,"./legacyBrowsers":22,"./modifiers/restrict":24,"./modifiers/snap":25,"./pointerEvents":27,"./pointerEvents/holdRepeat":26,"./pointerEvents/interactableTargets":28}],19:[function(require,module,exports){ -var InteractEvent = require('./InteractEvent'); -var Interaction = require('./Interaction'); -var modifiers = require('./modifiers'); -var utils = require('./utils'); -var animationFrame = require('./utils/raf'); - -Interaction.signals.on('new', function (interaction) { - interaction.inertiaStatus = { - active: false, - smoothEnd: false, - allowResume: false, - - startEvent: null, - upCoords: {}, - - xe: 0, ye: 0, - sx: 0, sy: 0, - - t0: 0, - vx0: 0, vys: 0, - duration: 0, - - lambda_v0: 0, - one_ve_v0: 0, - i: null - }; - - interaction.boundInertiaFrame = function () { - return inertiaFrame.apply(interaction); - }; - interaction.boundSmoothEndFrame = function () { - return smoothEndFrame.apply(interaction); - }; -}); - -Interaction.signals.on('down', function (_ref) { - var interaction = _ref.interaction; - var event = _ref.event; - var pointer = _ref.pointer; - var eventTarget = _ref.eventTarget; - - var status = interaction.inertiaStatus; - - // Check if the down event hits the current inertia target - if (status.active) { - var element = eventTarget; - - // climb up the DOM tree from the event target - while (utils.isElement(element)) { - - // if interaction element is the current inertia target element - if (element === interaction.element) { - // stop inertia - animationFrame.cancel(status.i); - status.active = false; - interaction.simulation = null; - - // update pointers to the down event's coordinates - interaction.updatePointer(pointer); - utils.setCoords(interaction.curCoords, interaction.pointers); - - // fire appropriate signals - var signalArg = { interaction: interaction }; - Interaction.signals.fire('before-action-move', signalArg); - Interaction.signals.fire('action-resume', signalArg); - - // fire a reume event - var resumeEvent = new InteractEvent(interaction, event, interaction.prepared.name, 'inertiaresume', interaction.element); - - interaction.target.fire(resumeEvent); - interaction.prevEvent = resumeEvent; - modifiers.resetStatuses(interaction.modifierStatuses); - - utils.copyCoords(interaction.prevCoords, interaction.curCoords); - break; - } - - element = utils.parentNode(element); - } - } -}); + // if it's a mouse interaction + if (mouseEvent || !(supportsTouch || supportsPointerEvent)) { -Interaction.signals.on('up', function (_ref2) { - var interaction = _ref2.interaction; - var event = _ref2.event; + // find a mouse interaction that's not in inertia phase + for (i = 0; i < len; i++) { + if (interactions[i].mouse && !interactions[i].inertiaStatus.active) { + return interactions[i]; + } + } - var status = interaction.inertiaStatus; + // find any interaction specifically for mouse. + // if the eventType is a mousedown, and inertia is active + // ignore the interaction + for (i = 0; i < len; i++) { + if (interactions[i].mouse && !(/down/.test(eventType) && interactions[i].inertiaStatus.active)) { + return interaction; + } + } - if (!interaction.interacting() || status.active) { - return; - } + // create a new interaction for mouse + interaction = new Interaction(); + interaction.mouse = true; - var target = interaction.target; - var options = target && target.options; - var inertiaOptions = options && interaction.prepared.name && options[interaction.prepared.name].inertia; + return interaction; + } - var now = new Date().getTime(); - var statuses = {}; - var page = utils.extend({}, interaction.curCoords.page); - var pointerSpeed = interaction.pointerDelta.client.speed; - var inertiaPossible = false; - var inertia = false; - var smoothEnd = false; - var modifierResult = undefined; + // get interaction that has this pointer + for (i = 0; i < len; i++) { + if (contains(interactions[i].pointerIds, id)) { + return interactions[i]; + } + } - // check if inertia should be started - inertiaPossible = inertiaOptions && inertiaOptions.enabled && interaction.prepared.name !== 'gesture' && event !== status.startEvent; + // at this stage, a pointerUp should not return an interaction + if (/up|end|out/i.test(eventType)) { + return null; + } - inertia = inertiaPossible && now - interaction.curCoords.timeStamp < 50 && pointerSpeed > inertiaOptions.minSpeed && pointerSpeed > inertiaOptions.endSpeed; + // get first idle interaction + for (i = 0; i < len; i++) { + interaction = interactions[i]; - // smoothEnd - if (inertiaPossible && !inertia) { - modifiers.resetStatuses(statuses); + if ((!interaction.prepared.name || (interaction.target.options.gesture.enabled)) + && !interaction.interacting() + && !(!mouseEvent && interaction.mouse)) { - modifierResult = modifiers.setAll(interaction, page, statuses, true, true); + return interaction; + } + } - if (modifierResult.shouldMove && modifierResult.locked) { - smoothEnd = true; + return new Interaction(); } - } - - if (!(inertia || smoothEnd)) { - return; - } - - utils.copyCoords(status.upCoords, interaction.curCoords); - interaction.pointers[0] = status.startEvent = new InteractEvent(interaction, event, interaction.prepared.name, 'inertiastart', interaction.element); + function doOnInteractions (method) { + return (function (event) { + var interaction, + eventTarget = getActualElement(event.path + ? event.path[0] + : event.target), + curEventTarget = getActualElement(event.currentTarget), + i; - status.t0 = now; + if (supportsTouch && /touch/.test(event.type)) { + prevTouchTime = new Date().getTime(); - status.active = true; - status.allowResume = inertiaOptions.allowResume; - interaction.simulation = status; + for (i = 0; i < event.changedTouches.length; i++) { + var pointer = event.changedTouches[i]; - target.fire(status.startEvent); + interaction = getInteractionFromPointer(pointer, event.type, eventTarget); - if (inertia) { - status.vx0 = interaction.pointerDelta.client.vx; - status.vy0 = interaction.pointerDelta.client.vy; - status.v0 = pointerSpeed; + if (!interaction) { continue; } - calcInertia(interaction, status); + interaction._updateEventTargets(eventTarget, curEventTarget); - utils.extend(page, interaction.curCoords.page); - - page.x += status.xe; - page.y += status.ye; + interaction[method](pointer, event, eventTarget, curEventTarget); + } + } + else { + if (!supportsPointerEvent && /mouse/.test(event.type)) { + // ignore mouse events while touch interactions are active + for (i = 0; i < interactions.length; i++) { + if (!interactions[i].mouse && interactions[i].pointerIsDown) { + return; + } + } - modifiers.resetStatuses(statuses); + // try to ignore mouse events that are simulated by the browser + // after a touch event + if (new Date().getTime() - prevTouchTime < 500) { + return; + } + } - modifierResult = modifiers.setAll(interaction, page, statuses, true, true); + interaction = getInteractionFromPointer(event, event.type, eventTarget); - status.modifiedXe += modifierResult.dx; - status.modifiedYe += modifierResult.dy; + if (!interaction) { return; } - status.i = animationFrame.request(interaction.boundInertiaFrame); - } else { - status.smoothEnd = true; - status.xe = modifierResult.dx; - status.ye = modifierResult.dy; + interaction._updateEventTargets(eventTarget, curEventTarget); - status.sx = status.sy = 0; + interaction[method](event, event, eventTarget, curEventTarget); + } + }); + } - status.i = animationFrame.request(interaction.boundSmoothEndFrame); - } -}); + function InteractEvent (interaction, event, action, phase, element, related) { + var client, + page, + target = interaction.target, + snapStatus = interaction.snapStatus, + restrictStatus = interaction.restrictStatus, + pointers = interaction.pointers, + deltaSource = (target && target.options || defaultOptions).deltaSource, + sourceX = deltaSource + 'X', + sourceY = deltaSource + 'Y', + options = target? target.options: defaultOptions, + origin = getOriginXY(target, element), + starting = phase === 'start', + ending = phase === 'end', + coords = starting? interaction.startCoords : interaction.curCoords; + + element = element || interaction.element; + + page = extend({}, coords.page); + client = extend({}, coords.client); + + page.x -= origin.x; + page.y -= origin.y; + + client.x -= origin.x; + client.y -= origin.y; + + var relativePoints = options[action].snap && options[action].snap.relativePoints ; + + if (checkSnap(target, action) && !(starting && relativePoints && relativePoints.length)) { + this.snap = { + range : snapStatus.range, + locked : snapStatus.locked, + x : snapStatus.snappedX, + y : snapStatus.snappedY, + realX : snapStatus.realX, + realY : snapStatus.realY, + dx : snapStatus.dx, + dy : snapStatus.dy + }; + + if (snapStatus.locked) { + page.x += snapStatus.dx; + page.y += snapStatus.dy; + client.x += snapStatus.dx; + client.y += snapStatus.dy; + } + } -Interaction.signals.on('stop-active', function (_ref3) { - var interaction = _ref3.interaction; + if (checkRestrict(target, action) && !(starting && options[action].restrict.elementRect) && restrictStatus.restricted) { + page.x += restrictStatus.dx; + page.y += restrictStatus.dy; + client.x += restrictStatus.dx; + client.y += restrictStatus.dy; - var status = interaction.inertiaStatus; + this.restrict = { + dx: restrictStatus.dx, + dy: restrictStatus.dy + }; + } - if (status.active) { - animationFrame.cancel(status.i); - status.active = false; - interaction.simulation = null; - } -}); + this.pageX = page.x; + this.pageY = page.y; + this.clientX = client.x; + this.clientY = client.y; + + this.x0 = interaction.startCoords.page.x - origin.x; + this.y0 = interaction.startCoords.page.y - origin.y; + this.clientX0 = interaction.startCoords.client.x - origin.x; + this.clientY0 = interaction.startCoords.client.y - origin.y; + this.ctrlKey = event.ctrlKey; + this.altKey = event.altKey; + this.shiftKey = event.shiftKey; + this.metaKey = event.metaKey; + this.button = event.button; + this.buttons = event.buttons; + this.target = element; + this.t0 = interaction.downTimes[0]; + this.type = action + (phase || ''); + + this.interaction = interaction; + this.interactable = target; + + var inertiaStatus = interaction.inertiaStatus; + + if (inertiaStatus.active) { + this.detail = 'inertia'; + } -function calcInertia(interaction, status) { - var inertiaOptions = interaction.target.options[interaction.prepared.name].inertia; - var lambda = inertiaOptions.resistance; - var inertiaDur = -Math.log(inertiaOptions.endSpeed / status.v0) / lambda; + if (related) { + this.relatedTarget = related; + } - status.x0 = interaction.prevEvent.pageX; - status.y0 = interaction.prevEvent.pageY; - status.t0 = status.startEvent.timeStamp / 1000; - status.sx = status.sy = 0; + // end event dx, dy is difference between start and end points + if (ending) { + if (deltaSource === 'client') { + this.dx = client.x - interaction.startCoords.client.x; + this.dy = client.y - interaction.startCoords.client.y; + } + else { + this.dx = page.x - interaction.startCoords.page.x; + this.dy = page.y - interaction.startCoords.page.y; + } + } + else if (starting) { + this.dx = 0; + this.dy = 0; + } + // copy properties from previousmove if starting inertia + else if (phase === 'inertiastart') { + this.dx = interaction.prevEvent.dx; + this.dy = interaction.prevEvent.dy; + } + else { + if (deltaSource === 'client') { + this.dx = client.x - interaction.prevEvent.clientX; + this.dy = client.y - interaction.prevEvent.clientY; + } + else { + this.dx = page.x - interaction.prevEvent.pageX; + this.dy = page.y - interaction.prevEvent.pageY; + } + } + if (interaction.prevEvent && interaction.prevEvent.detail === 'inertia' + && !inertiaStatus.active + && options[action].inertia && options[action].inertia.zeroResumeDelta) { - status.modifiedXe = status.xe = (status.vx0 - inertiaDur) / lambda; - status.modifiedYe = status.ye = (status.vy0 - inertiaDur) / lambda; - status.te = inertiaDur; + inertiaStatus.resumeDx += this.dx; + inertiaStatus.resumeDy += this.dy; - status.lambda_v0 = lambda / status.v0; - status.one_ve_v0 = 1 - inertiaOptions.endSpeed / status.v0; -} + this.dx = this.dy = 0; + } -function inertiaFrame() { - updateInertiaCoords(this); - utils.setCoordDeltas(this.pointerDelta, this.prevCoords, this.curCoords); + if (action === 'resize' && interaction.resizeAxes) { + if (options.resize.square) { + if (interaction.resizeAxes === 'y') { + this.dx = this.dy; + } + else { + this.dy = this.dx; + } + this.axes = 'xy'; + } + else { + this.axes = interaction.resizeAxes; + + if (interaction.resizeAxes === 'x') { + this.dy = 0; + } + else if (interaction.resizeAxes === 'y') { + this.dx = 0; + } + } + } + else if (action === 'gesture') { + this.touches = [pointers[0], pointers[1]]; + + if (starting) { + this.distance = touchDistance(pointers, deltaSource); + this.box = touchBBox(pointers); + this.scale = 1; + this.ds = 0; + this.angle = touchAngle(pointers, undefined, deltaSource); + this.da = 0; + } + else if (ending || event instanceof InteractEvent) { + this.distance = interaction.prevEvent.distance; + this.box = interaction.prevEvent.box; + this.scale = interaction.prevEvent.scale; + this.ds = this.scale - 1; + this.angle = interaction.prevEvent.angle; + this.da = this.angle - interaction.gesture.startAngle; + } + else { + this.distance = touchDistance(pointers, deltaSource); + this.box = touchBBox(pointers); + this.scale = this.distance / interaction.gesture.startDistance; + this.angle = touchAngle(pointers, interaction.gesture.prevAngle, deltaSource); + + this.ds = this.scale - interaction.gesture.prevScale; + this.da = this.angle - interaction.gesture.prevAngle; + } + } - var status = this.inertiaStatus; - var options = this.target.options[this.prepared.name].inertia; - var lambda = options.resistance; - var t = new Date().getTime() / 1000 - status.t0; + if (starting) { + this.timeStamp = interaction.downTimes[0]; + this.dt = 0; + this.duration = 0; + this.speed = 0; + this.velocityX = 0; + this.velocityY = 0; + } + else if (phase === 'inertiastart') { + this.timeStamp = interaction.prevEvent.timeStamp; + this.dt = interaction.prevEvent.dt; + this.duration = interaction.prevEvent.duration; + this.speed = interaction.prevEvent.speed; + this.velocityX = interaction.prevEvent.velocityX; + this.velocityY = interaction.prevEvent.velocityY; + } + else { + this.timeStamp = new Date().getTime(); + this.dt = this.timeStamp - interaction.prevEvent.timeStamp; + this.duration = this.timeStamp - interaction.downTimes[0]; + + if (event instanceof InteractEvent) { + var dx = this[sourceX] - interaction.prevEvent[sourceX], + dy = this[sourceY] - interaction.prevEvent[sourceY], + dt = this.dt / 1000; + + this.speed = hypot(dx, dy) / dt; + this.velocityX = dx / dt; + this.velocityY = dy / dt; + } + // if normal move or end event, use previous user event coords + else { + // speed and velocity in pixels per second + this.speed = interaction.pointerDelta[deltaSource].speed; + this.velocityX = interaction.pointerDelta[deltaSource].vx; + this.velocityY = interaction.pointerDelta[deltaSource].vy; + } + } - if (t < status.te) { + if ((ending || phase === 'inertiastart') + && interaction.prevEvent.speed > 600 && this.timeStamp - interaction.prevEvent.timeStamp < 150) { - var progress = 1 - (Math.exp(-lambda * t) - status.lambda_v0) / status.one_ve_v0; + var angle = 180 * Math.atan2(interaction.prevEvent.velocityY, interaction.prevEvent.velocityX) / Math.PI, + overlap = 22.5; - if (status.modifiedXe === status.xe && status.modifiedYe === status.ye) { - status.sx = status.xe * progress; - status.sy = status.ye * progress; - } else { - var quadPoint = utils.getQuadraticCurvePoint(0, 0, status.xe, status.ye, status.modifiedXe, status.modifiedYe, progress); + if (angle < 0) { + angle += 360; + } - status.sx = quadPoint.x; - status.sy = quadPoint.y; + var left = 135 - overlap <= angle && angle < 225 + overlap, + up = 225 - overlap <= angle && angle < 315 + overlap, + + right = !left && (315 - overlap <= angle || angle < 45 + overlap), + down = !up && 45 - overlap <= angle && angle < 135 + overlap; + + this.swipe = { + up : up, + down : down, + left : left, + right: right, + angle: angle, + speed: interaction.prevEvent.speed, + velocity: { + x: interaction.prevEvent.velocityX, + y: interaction.prevEvent.velocityY + } + }; + } } - this.doMove(); - - status.i = animationFrame.request(this.boundInertiaFrame); - } else { - status.sx = status.modifiedXe; - status.sy = status.modifiedYe; - - this.doMove(); - this.end(status.startEvent); - status.active = false; - this.simulation = null; - } + InteractEvent.prototype = { + preventDefault: blank, + stopImmediatePropagation: function () { + this.immediatePropagationStopped = this.propagationStopped = true; + }, + stopPropagation: function () { + this.propagationStopped = true; + } + }; - utils.copyCoords(this.prevCoords, this.curCoords); -} + function preventOriginalDefault () { + this.originalEvent.preventDefault(); + } -function smoothEndFrame() { - updateInertiaCoords(this); + function getActionCursor (action) { + var cursor = ''; - var status = this.inertiaStatus; - var t = new Date().getTime() - status.t0; - var duration = this.target.options[this.prepared.name].inertia.smoothEndDuration; + if (action.name === 'drag') { + cursor = actionCursors.drag; + } + if (action.name === 'resize') { + if (action.axis) { + cursor = actionCursors[action.name + action.axis]; + } + else if (action.edges) { + var cursorKey = 'resize', + edgeNames = ['top', 'bottom', 'left', 'right']; - if (t < duration) { - status.sx = utils.easeOutQuad(t, 0, status.xe, duration); - status.sy = utils.easeOutQuad(t, 0, status.ye, duration); + for (var i = 0; i < 4; i++) { + if (action.edges[edgeNames[i]]) { + cursorKey += edgeNames[i]; + } + } - this.pointerMove(status.startEvent, status.startEvent); + cursor = actionCursors[cursorKey]; + } + } - status.i = animationFrame.request(this.boundSmoothEndFrame); - } else { - status.sx = status.xe; - status.sy = status.ye; + return cursor; + } - this.pointerMove(status.startEvent, status.startEvent); - this.end(status.startEvent); + function checkResizeEdge (name, value, page, element, interactableElement, rect, margin) { + // false, '', undefined, null + if (!value) { return false; } - status.smoothEnd = status.active = false; - this.simulation = null; - } -} + // true value, use pointer coords and element rect + if (value === true) { + // if dimensions are negative, "switch" edges + var width = isNumber(rect.width)? rect.width : rect.right - rect.left, + height = isNumber(rect.height)? rect.height : rect.bottom - rect.top; -function updateInertiaCoords(interaction) { - var status = interaction.inertiaStatus; + if (width < 0) { + if (name === 'left' ) { name = 'right'; } + else if (name === 'right') { name = 'left' ; } + } + if (height < 0) { + if (name === 'top' ) { name = 'bottom'; } + else if (name === 'bottom') { name = 'top' ; } + } - // return if inertia isn't running - if (!status.active) { - return; - } + if (name === 'left' ) { return page.x < ((width >= 0? rect.left: rect.right ) + margin); } + if (name === 'top' ) { return page.y < ((height >= 0? rect.top : rect.bottom) + margin); } - var pageUp = status.upCoords.page; - var clientUp = status.upCoords.client; + if (name === 'right' ) { return page.x > ((width >= 0? rect.right : rect.left) - margin); } + if (name === 'bottom') { return page.y > ((height >= 0? rect.bottom: rect.top ) - margin); } + } - utils.setCoords(interaction.curCoords, [{ - pageX: pageUp.x + status.sx, - pageY: pageUp.y + status.sy, - clientX: clientUp.x + status.sx, - clientY: clientUp.y + status.sy - }]); -} + // the remaining checks require an element + if (!isElement(element)) { return false; } + + return isElement(value) + // the value is an element to use as a resize handle + ? value === element + // otherwise check if element matches value as selector + : matchesUpTo(element, value, interactableElement); + } + + function defaultActionChecker (pointer, interaction, element) { + var rect = this.getRect(element), + shouldResize = false, + action = null, + resizeAxes = null, + resizeEdges, + page = extend({}, interaction.curCoords.page), + options = this.options; + + if (!rect) { return null; } + + if (actionIsEnabled.resize && options.resize.enabled) { + var resizeOptions = options.resize; + + resizeEdges = { + left: false, right: false, top: false, bottom: false + }; + + // if using resize.edges + if (isObject(resizeOptions.edges)) { + for (var edge in resizeEdges) { + resizeEdges[edge] = checkResizeEdge(edge, + resizeOptions.edges[edge], + page, + interaction._eventTarget, + element, + rect, + resizeOptions.margin || margin); + } + + resizeEdges.left = resizeEdges.left && !resizeEdges.right; + resizeEdges.top = resizeEdges.top && !resizeEdges.bottom; + + shouldResize = resizeEdges.left || resizeEdges.right || resizeEdges.top || resizeEdges.bottom; + } + else { + var right = options.resize.axis !== 'y' && page.x > (rect.right - margin), + bottom = options.resize.axis !== 'x' && page.y > (rect.bottom - margin); -},{"./InteractEvent":3,"./Interaction":5,"./modifiers":23,"./utils":39,"./utils/raf":45}],20:[function(require,module,exports){ -var browser = require('./utils/browser'); -var events = require('./utils/events'); -var utils = require('./utils'); -var scope = require('./scope'); -var Interactable = require('./Interactable'); -var Interaction = require('./Interaction'); + shouldResize = right || bottom; + resizeAxes = (right? 'x' : '') + (bottom? 'y' : ''); + } + } -var globalEvents = {}; + action = shouldResize + ? 'resize' + : actionIsEnabled.drag && options.drag.enabled + ? 'drag' + : null; -/*\ - * interact - [ method ] - * - * The methods of this variable can be used to set elements as - * interactables and also to change various default settings. - * - * Calling it as a function and passing an element or a valid CSS selector - * string returns an Interactable object which has various methods to - * configure it. - * - - element (Element | string) The HTML or SVG Element to interact with or CSS selector - = (object) An @Interactable - * - > Usage - | interact('#draggable').draggable(true); - | - | var rectables = interact('rect'); - | rectables - | .gesturable(true) - | .on('gesturemove', function (event) { - | // ... - | }); -\*/ -function interact(element, options) { - var interactable = scope.interactables.get(element, options); - - if (!interactable) { - interactable = new Interactable(element, options); - interactable.events.global = globalEvents; - } - - return interactable; -} - -/*\ - * interact.isSet - [ method ] - * - * Check if an element has been set - - element (Element) The Element being searched for - = (boolean) Indicates if the element or CSS selector was previously passed to interact -\*/ -interact.isSet = function (element, options) { - return scope.interactables.indexOfElement(element, options && options.context) !== -1; -}; - -/*\ - * interact.on - [ method ] - * - * Adds a global listener for an InteractEvent or adds a DOM event to - * `document` - * - - type (string | array | object) The types of events to listen for - - listener (function) The function event (s) - - useCapture (boolean) #optional useCapture flag for addEventListener - = (object) interact -\*/ -interact.on = function (type, listener, useCapture) { - if (utils.isString(type) && type.search(' ') !== -1) { - type = type.trim().split(/ +/); - } - - if (utils.isArray(type)) { - for (var _iterator = type, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var eventType = _ref; - - interact.on(eventType, listener, useCapture); - } + if (actionIsEnabled.gesture + && interaction.pointerIds.length >=2 + && !(interaction.dragging || interaction.resizing)) { + action = 'gesture'; + } - return interact; - } + if (action) { + return { + name: action, + axis: resizeAxes, + edges: resizeEdges + }; + } - if (utils.isObject(type)) { - for (var prop in type) { - interact.on(prop, type[prop], listener); + return null; } - return interact; - } - - // if it is an InteractEvent type, add listener to globalEvents - if (utils.contains(Interactable.eventTypes, type)) { - // if this type of event was never bound - if (!globalEvents[type]) { - globalEvents[type] = [listener]; - } else { - globalEvents[type].push(listener); - } - } - // If non InteractEvent type, addEventListener to document - else { - events.add(scope.document, type, listener, useCapture); - } + // Check if action is enabled globally and the current target supports it + // If so, return the validated action. Otherwise, return null + function validateAction (action, interactable) { + if (!isObject(action)) { return null; } - return interact; -}; + var actionName = action.name, + options = interactable.options; -/*\ - * interact.off - [ method ] - * - * Removes a global InteractEvent listener or DOM event from `document` - * - - type (string | array | object) The types of events that were listened for - - listener (function) The listener function to be removed - - useCapture (boolean) #optional useCapture flag for removeEventListener - = (object) interact - \*/ -interact.off = function (type, listener, useCapture) { - if (utils.isString(type) && type.search(' ') !== -1) { - type = type.trim().split(/ +/); - } - - if (utils.isArray(type)) { - for (var _iterator2 = type, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var eventType = _ref2; - - interact.off(eventType, listener, useCapture); - } + if (( (actionName === 'resize' && options.resize.enabled ) + || (actionName === 'drag' && options.drag.enabled ) + || (actionName === 'gesture' && options.gesture.enabled)) + && actionIsEnabled[actionName]) { - return interact; - } + if (actionName === 'resize' || actionName === 'resizeyx') { + actionName = 'resizexy'; + } - if (utils.isObject(type)) { - for (var prop in type) { - interact.off(prop, type[prop], listener); + return action; + } + return null; } - return interact; - } + var listeners = {}, + interactionListeners = [ + 'dragStart', 'dragMove', 'resizeStart', 'resizeMove', 'gestureStart', 'gestureMove', + 'pointerOver', 'pointerOut', 'pointerHover', 'selectorDown', + 'pointerDown', 'pointerMove', 'pointerUp', 'pointerCancel', 'pointerEnd', + 'addPointer', 'removePointer', 'recordPointer', 'autoScrollMove' + ]; - if (!utils.contains(Interactable.eventTypes, type)) { - events.remove(scope.document, type, listener, useCapture); - } else { - var index = undefined; + for (var i = 0, len = interactionListeners.length; i < len; i++) { + var name = interactionListeners[i]; - if (type in globalEvents && (index = utils.indexOf(globalEvents[type], listener)) !== -1) { - globalEvents[type].splice(index, 1); + listeners[name] = doOnInteractions(name); } - } - - return interact; -}; -/*\ - * interact.debug - [ method ] - * - * Returns an object which exposes internal data - = (object) An object with properties that outline the current state and expose internal functions and variables -\*/ -interact.debug = function () { - return scope; -}; - -// expose the functions used to calculate multi-touch properties -interact.getPointerAverage = utils.pointerAverage; -interact.getTouchBBox = utils.touchBBox; -interact.getTouchDistance = utils.touchDistance; -interact.getTouchAngle = utils.touchAngle; - -interact.getElementRect = utils.getElementRect; -interact.getElementClientRect = utils.getElementClientRect; -interact.matchesSelector = utils.matchesSelector; -interact.closest = utils.closest; - -/*\ - * interact.supportsTouch - [ method ] - * - = (boolean) Whether or not the browser supports touch input -\*/ -interact.supportsTouch = function () { - return browser.supportsTouch; -}; - -/*\ - * interact.supportsPointerEvent - [ method ] - * - = (boolean) Whether or not the browser supports PointerEvents -\*/ -interact.supportsPointerEvent = function () { - return browser.supportsPointerEvent; -}; - -/*\ - * interact.stop - [ method ] - * - * Cancels all interactions (end events are not fired) - * - - event (Event) An event on which to call preventDefault() - = (object) interact -\*/ -interact.stop = function (event) { - for (var i = scope.interactions.length - 1; i >= 0; i--) { - scope.interactions[i].stop(event); - } - - return interact; -}; - -/*\ - * interact.pointerMoveTolerance - [ method ] - * Returns or sets the distance the pointer must be moved before an action - * sequence occurs. This also affects tolerance for tap events. - * - - newValue (number) #optional The movement from the start position must be greater than this value - = (number | Interactable) The current setting or interact -\*/ -interact.pointerMoveTolerance = function (newValue) { - if (utils.isNumber(newValue)) { - Interaction.pointerMoveTolerance = newValue; + // bound to the interactable context when a DOM event + // listener is added to a selector interactable + function delegateListener (event, useCapture) { + var fakeEvent = {}, + delegated = delegatedEvents[event.type], + eventTarget = getActualElement(event.path + ? event.path[0] + : event.target), + element = eventTarget; - return this; - } + useCapture = useCapture? true: false; - return Interaction.pointerMoveTolerance; -}; + // duplicate the event so that currentTarget can be changed + for (var prop in event) { + fakeEvent[prop] = event[prop]; + } -interact.addDocument = scope.addDocument; -interact.removeDocument = scope.removeDocument; + fakeEvent.originalEvent = event; + fakeEvent.preventDefault = preventOriginalDefault; -scope.interact = interact; + // climb up document tree looking for selector matches + while (isElement(element)) { + for (var i = 0; i < delegated.selectors.length; i++) { + var selector = delegated.selectors[i], + context = delegated.contexts[i]; -module.exports = interact; + if (matchesSelector(element, selector) + && nodeContains(context, eventTarget) + && nodeContains(context, element)) { -},{"./Interactable":4,"./Interaction":5,"./scope":29,"./utils":39,"./utils/browser":32,"./utils/events":35}],21:[function(require,module,exports){ -var Interactable = require('./Interactable'); -var Interaction = require('./Interaction'); -var scope = require('./scope'); -var isType = require('./utils/isType'); + var listeners = delegated.listeners[i]; -var _require = require('./utils/domUtils'); + fakeEvent.currentTarget = element; -var nodeContains = _require.nodeContains; -var matchesSelector = _require.matchesSelector; + for (var j = 0; j < listeners.length; j++) { + if (listeners[j][1] === useCapture) { + listeners[j][0](fakeEvent); + } + } + } + } -/*\ - * Interactable.preventDefault - [ method ] - * - * Returns or sets whether to prevent the browser's default behaviour - * in response to pointer events. Can be set to: - * - `'always'` to always prevent - * - `'never'` to never prevent - * - `'auto'` to let interact.js try to determine what would be best - * - - newValue (string) #optional `true`, `false` or `'auto'` - = (string | Interactable) The current setting or this Interactable -\*/ -Interactable.prototype.preventDefault = function (newValue) { - if (/^(always|never|auto)$/.test(newValue)) { - this.options.preventDefault = newValue; - return this; - } - - if (isType.isBool(newValue)) { - this.options.preventDefault = newValue ? 'always' : 'never'; - return this; - } - - return this.options.preventDefault; -}; - -Interactable.prototype.checkAndPreventDefault = function (event) { - var setting = this.options.preventDefault; - - if (setting === 'never') { - return; - } - - if (setting === 'always') { - event.preventDefault(); - return; - } - - // setting === 'auto' - - // don't preventDefault of pointerdown events - if (/^(mouse|pointer|touch)*(down|start)/i.test(event.type)) { - return; - } - - // don't preventDefault on editable elements - if (matchesSelector(event.target, 'input,select,textarea,[contenteditable=true],[contenteditable=true] *')) { - return; - } - - event.preventDefault(); -}; - -function onInteractionEvent(_ref2) { - var interaction = _ref2.interaction; - var event = _ref2.event; - - if (interaction.target) { - interaction.target.checkAndPreventDefault(event); - } -} - -var _arr = ['down', 'move', 'up', 'cancel']; -for (var _i = 0; _i < _arr.length; _i++) { - var eventSignal = _arr[_i]; - Interaction.signals.on(eventSignal, onInteractionEvent); -} - -// prevent native HTML5 drag on interact.js target elements -Interaction.docEvents.dragstart = function preventNativeDrag(event) { - for (var _iterator = scope.interactions, _isArray = Array.isArray(_iterator), _i2 = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i2 >= _iterator.length) break; - _ref = _iterator[_i2++]; - } else { - _i2 = _iterator.next(); - if (_i2.done) break; - _ref = _i2.value; + element = parentElement(element); + } } - var interaction = _ref; + function delegateUseCapture (event) { + return delegateListener.call(this, event, true); + } - if (interaction.element && (interaction.element === event.target || nodeContains(interaction.element, event.target))) { + interactables.indexOfElement = function indexOfElement (element, context) { + context = context || document; - interaction.target.checkAndPreventDefault(event); - return; - } - } -}; - -},{"./Interactable":4,"./Interaction":5,"./scope":29,"./utils/domUtils":34,"./utils/isType":41}],22:[function(require,module,exports){ -var scope = require('./scope'); -var events = require('./utils/events'); -var browser = require('./utils/browser'); -var iFinder = require('./utils/interactionFinder'); - -var toString = Object.prototype.toString; - -if (!window.Array.isArray) { - window.Array.isArray = function (obj) { - return toString.call(obj) === '[object Array]'; - }; -} - -if (!String.prototype.trim) { - String.prototype.trim = function () { - return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); - }; -} - -// http://www.quirksmode.org/dom/events/click.html -// >Events leading to dblclick -// -// IE8 doesn't fire down event before dblclick. -// This workaround tries to fire a tap and doubletap after dblclick -function onIE8Dblclick(event) { - var interaction = iFinder.search(event, event.type, event.target); - - if (!interaction) { - return; - } - - if (interaction.prevTap && event.clientX === interaction.prevTap.clientX && event.clientY === interaction.prevTap.clientY && event.target === interaction.prevTap.target) { - - interaction.downTargets[0] = event.target; - interaction.downTimes[0] = new Date().getTime(); - - scope.pointerEvents.collectEventTargets(interaction, event, event, event.target, 'tap'); - } -} - -if (browser.isIE8) { - (function () { - var selectFix = function (event) { - for (var _iterator = scope.interactions, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + for (var i = 0; i < this.length; i++) { + var interactable = this[i]; - var interaction = _ref; + if ((interactable.selector === element + && (interactable._context === context)) + || (!interactable.selector && interactable._element === element)) { - if (interaction.interacting()) { - interaction.target.checkAndPreventDefault(event); + return i; + } } - } + return -1; }; - var onDocIE8 = function onDocIE8(_ref2, signalName) { - var doc = _ref2.doc; - var win = _ref2.win; - - var eventMethod = signalName.indexOf('listen') === 0 ? events.add : events.remove; - - // For IE's lack of Event#preventDefault - eventMethod(doc, 'selectstart', selectFix); - - if (scope.pointerEvents) { - eventMethod(doc, 'dblclick', onIE8Dblclick); - } + interactables.get = function interactableGet (element, options) { + return this[this.indexOfElement(element, options && options.context)]; }; - scope.signals.on('add-document', onDocIE8); - scope.signals.on('remove-document', onDocIE8); - })(); -} + interactables.forEachSelector = function (callback) { + for (var i = 0; i < this.length; i++) { + var interactable = this[i]; -module.exports = null; + if (!interactable.selector) { + continue; + } -},{"./scope":29,"./utils/browser":32,"./utils/events":35,"./utils/interactionFinder":40}],23:[function(require,module,exports){ -var InteractEvent = require('../InteractEvent'); -var Interaction = require('../Interaction'); -var extend = require('../utils/extend'); + var ret = callback(interactable, interactable.selector, interactable._context, i, this); -var modifiers = { - names: [], + if (ret !== undefined) { + return ret; + } + } + }; - setOffsets: function (interaction, coords) { - var target = interaction.target; - var element = interaction.element; + /*\ + * interact + [ method ] + * + * The methods of this variable can be used to set elements as + * interactables and also to change various default settings. + * + * Calling it as a function and passing an element or a valid CSS selector + * string returns an Interactable object which has various methods to + * configure it. + * + - element (Element | string) The HTML or SVG Element to interact with or CSS selector + = (object) An @Interactable + * + > Usage + | interact(document.getElementById('draggable')).draggable(true); + | + | var rectables = interact('rect'); + | rectables + | .gesturable(true) + | .on('gesturemove', function (event) { + | // something cool... + | }) + | .autoScroll(true); + \*/ + function interact (element, options) { + return interactables.get(element, options) || new Interactable(element, options); + } + + /*\ + * Interactable + [ property ] + ** + * Object type returned by @interact + \*/ + function Interactable (element, options) { + this._element = element; + this._iEvents = this._iEvents || {}; + + var _window; + + if (trySelector(element)) { + this.selector = element; + + var context = options && options.context; + + _window = context? getWindow(context) : window; + + if (context && (_window.Node + ? context instanceof _window.Node + : (isElement(context) || context === _window.document))) { + + this._context = context; + } + } + else { + _window = getWindow(element); + + if (isElement(element, _window)) { + + if (PointerEvent) { + events.add(this._element, pEventTypes.down, listeners.pointerDown ); + events.add(this._element, pEventTypes.move, listeners.pointerHover); + } + else { + events.add(this._element, 'mousedown' , listeners.pointerDown ); + events.add(this._element, 'mousemove' , listeners.pointerHover); + events.add(this._element, 'touchstart', listeners.pointerDown ); + events.add(this._element, 'touchmove' , listeners.pointerHover); + } + } + } - var rect = target.getRect(element); + this._doc = _window.document; - if (rect) { - interaction.startOffset.left = coords.page.x - rect.left; - interaction.startOffset.top = coords.page.y - rect.top; + if (!contains(documents, this._doc)) { + listenToDocument(this._doc); + } - interaction.startOffset.right = rect.right - coords.page.x; - interaction.startOffset.bottom = rect.bottom - coords.page.y; + interactables.push(this); - if (!('width' in rect)) { - rect.width = rect.right - rect.left; - } - if (!('height' in rect)) { - rect.height = rect.bottom - rect.top; - } - } else { - interaction.startOffset.left = interaction.startOffset.top = interaction.startOffset.right = interaction.startOffset.bottom = 0; + this.set(options); } - modifiers.setModifierOffsets(interaction, target, element, rect, interaction.modifierOffsets); - }, + Interactable.prototype = { + setOnEvents: function (action, phases) { + if (action === 'drop') { + if (isFunction(phases.ondrop) ) { this.ondrop = phases.ondrop ; } + if (isFunction(phases.ondropactivate) ) { this.ondropactivate = phases.ondropactivate ; } + if (isFunction(phases.ondropdeactivate)) { this.ondropdeactivate = phases.ondropdeactivate; } + if (isFunction(phases.ondragenter) ) { this.ondragenter = phases.ondragenter ; } + if (isFunction(phases.ondragleave) ) { this.ondragleave = phases.ondragleave ; } + if (isFunction(phases.ondropmove) ) { this.ondropmove = phases.ondropmove ; } + } + else { + action = 'on' + action; - setModifierOffsets: function (interaction, interactable, element, rect, offsets) { - for (var i = 0; i < modifiers.names.length; i++) { - var modifierName = modifiers.names[i]; + if (isFunction(phases.onstart) ) { this[action + 'start' ] = phases.onstart ; } + if (isFunction(phases.onmove) ) { this[action + 'move' ] = phases.onmove ; } + if (isFunction(phases.onend) ) { this[action + 'end' ] = phases.onend ; } + if (isFunction(phases.oninertiastart)) { this[action + 'inertiastart' ] = phases.oninertiastart ; } + } - offsets[modifierName] = modifiers[modifiers.names[i]].setOffset(interaction, interactable, element, rect, interaction.startOffset); - } - }, - - setAll: function (interaction, coordsArg, statuses, preEnd, requireEndOnly) { - var result = { - dx: 0, - dy: 0, - changed: false, - locked: false, - shouldMove: true - }; - var target = interaction.target; - var coords = extend({}, coordsArg); + return this; + }, + + /*\ + * Interactable.draggable + [ method ] + * + * Gets or sets whether drag actions can be performed on the + * Interactable + * + = (boolean) Indicates if this can be the target of drag events + | var isDraggable = interact('ul li').draggable(); + * or + - options (boolean | object) #optional true/false or An object with event listeners to be fired on drag events (object makes the Interactable draggable) + = (object) This Interactable + | interact(element).draggable({ + | onstart: function (event) {}, + | onmove : function (event) {}, + | onend : function (event) {}, + | + | // the axis in which the first movement must be + | // for the drag sequence to start + | // 'xy' by default - any direction + | axis: 'x' || 'y' || 'xy', + | + | // max number of drags that can happen concurrently + | // with elements of this Interactable. Infinity by default + | max: Infinity, + | + | // max number of drags that can target the same element+Interactable + | // 1 by default + | maxPerElement: 2 + | }); + \*/ + draggable: function (options) { + if (isObject(options)) { + this.options.drag.enabled = options.enabled === false? false: true; + this.setPerAction('drag', options); + this.setOnEvents('drag', options); + + if (/^x$|^y$|^xy$/.test(options.axis)) { + this.options.drag.axis = options.axis; + } + else if (options.axis === null) { + delete this.options.drag.axis; + } + + return this; + } - var currentStatus = undefined; + if (isBool(options)) { + this.options.drag.enabled = options; - for (var _iterator = modifiers.names, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + return this; + } - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + return this.options.drag; + }, + + setPerAction: function (action, options) { + // for all the default per-action options + for (var option in options) { + // if this option exists for this action + if (option in defaultOptions[action]) { + // if the option in the options arg is an object value + if (isObject(options[option])) { + // duplicate the object + this.options[action][option] = extend(this.options[action][option] || {}, options[option]); + + if (isObject(defaultOptions.perAction[option]) && 'enabled' in defaultOptions.perAction[option]) { + this.options[action][option].enabled = options[option].enabled === false? false : true; + } + } + else if (isBool(options[option]) && isObject(defaultOptions.perAction[option])) { + this.options[action][option].enabled = options[option]; + } + else if (options[option] !== undefined) { + // or if it's not undefined, do a plain assignment + this.options[action][option] = options[option]; + } + } + } + }, + + /*\ + * Interactable.dropzone + [ method ] + * + * Returns or sets whether elements can be dropped onto this + * Interactable to trigger drop events + * + * Dropzones can receive the following events: + * - `dropactivate` and `dropdeactivate` when an acceptable drag starts and ends + * - `dragenter` and `dragleave` when a draggable enters and leaves the dropzone + * - `dragmove` when a draggable that has entered the dropzone is moved + * - `drop` when a draggable is dropped into this dropzone + * + * Use the `accept` option to allow only elements that match the given CSS selector or element. + * + * Use the `overlap` option to set how drops are checked for. The allowed values are: + * - `'pointer'`, the pointer must be over the dropzone (default) + * - `'center'`, the draggable element's center must be over the dropzone + * - a number from 0-1 which is the `(intersection area) / (draggable area)`. + * e.g. `0.5` for drop to happen when half of the area of the + * draggable is over the dropzone + * + - options (boolean | object | null) #optional The new value to be set. + | interact('.drop').dropzone({ + | accept: '.can-drop' || document.getElementById('single-drop'), + | overlap: 'pointer' || 'center' || zeroToOne + | } + = (boolean | object) The current setting or this Interactable + \*/ + dropzone: function (options) { + if (isObject(options)) { + this.options.drop.enabled = options.enabled === false? false: true; + this.setOnEvents('drop', options); + + if (/^(pointer|center)$/.test(options.overlap)) { + this.options.drop.overlap = options.overlap; + } + else if (isNumber(options.overlap)) { + this.options.drop.overlap = Math.max(Math.min(1, options.overlap), 0); + } + if ('accept' in options) { + this.options.drop.accept = options.accept; + } + if ('checker' in options) { + this.options.drop.checker = options.checker; + } + + return this; + } - var modifierName = _ref; + if (isBool(options)) { + this.options.drop.enabled = options; - var modifier = modifiers[modifierName]; + return this; + } - if (!modifier.shouldDo(target, interaction.prepared.name, preEnd, requireEndOnly)) { - continue; - } + return this.options.drop; + }, - currentStatus = modifier.set(coords, interaction, statuses[modifierName]); + dropCheck: function (dragEvent, event, draggable, draggableElement, dropElement, rect) { + var dropped = false; - if (currentStatus.locked) { - coords.x += currentStatus.dx; - coords.y += currentStatus.dy; + // if the dropzone has no rect (eg. display: none) + // call the custom dropChecker or just return false + if (!(rect = rect || this.getRect(dropElement))) { + return (this.options.drop.checker + ? this.options.drop.checker(dragEvent, event, dropped, this, dropElement, draggable, draggableElement) + : false); + } - result.dx += currentStatus.dx; - result.dy += currentStatus.dy; + var dropOverlap = this.options.drop.overlap; - result.locked = true; - } - } + if (dropOverlap === 'pointer') { + var page = getPageXY(dragEvent), + origin = getOriginXY(draggable, draggableElement), + horizontal, + vertical; - // a move should be fired if the modified coords of - // the last modifier status that was calculated changes - result.shouldMove = !currentStatus || currentStatus.changed; + page.x += origin.x; + page.y += origin.y; - return result; - }, + horizontal = (page.x > rect.left) && (page.x < rect.right); + vertical = (page.y > rect.top ) && (page.y < rect.bottom); - resetStatuses: function (statuses) { - for (var _iterator2 = modifiers.names, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; + dropped = horizontal && vertical; + } - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } + var dragRect = draggable.getRect(draggableElement); - var modifierName = _ref2; + if (dropOverlap === 'center') { + var cx = dragRect.left + dragRect.width / 2, + cy = dragRect.top + dragRect.height / 2; - statuses[modifierName] = modifiers[modifierName].reset(statuses[modifierName] || {}); - } + dropped = cx >= rect.left && cx <= rect.right && cy >= rect.top && cy <= rect.bottom; + } - return statuses; - }, + if (isNumber(dropOverlap)) { + var overlapArea = (Math.max(0, Math.min(rect.right , dragRect.right ) - Math.max(rect.left, dragRect.left)) + * Math.max(0, Math.min(rect.bottom, dragRect.bottom) - Math.max(rect.top , dragRect.top ))), + overlapRatio = overlapArea / (dragRect.width * dragRect.height); - start: function (_ref3, signalName) { - var interaction = _ref3.interaction; + dropped = overlapRatio >= dropOverlap; + } - modifiers.setOffsets(interaction, signalName === 'action-resume' ? interaction.curCoords : interaction.startCoords); + if (this.options.drop.checker) { + dropped = this.options.drop.checker(dragEvent, event, dropped, this, dropElement, draggable, draggableElement); + } - modifiers.resetStatuses(interaction.modifierStatuses); - modifiers.setAll(interaction, interaction.startCoords.page, interaction.modifierStatuses); - } -}; + return dropped; + }, + + /*\ + * Interactable.dropChecker + [ method ] + * + * DEPRECATED. Use interactable.dropzone({ checker: function... }) instead. + * + * Gets or sets the function used to check if a dragged element is + * over this Interactable. + * + - checker (function) #optional The function that will be called when checking for a drop + = (Function | Interactable) The checker function or this Interactable + * + * The checker function takes the following arguments: + * + - dragEvent (InteractEvent) The related dragmove or dragend event + - event (TouchEvent | PointerEvent | MouseEvent) The user move/up/end Event related to the dragEvent + - dropped (boolean) The value from the default drop checker + - dropzone (Interactable) The dropzone interactable + - dropElement (Element) The dropzone element + - draggable (Interactable) The Interactable being dragged + - draggableElement (Element) The actual element that's being dragged + * + > Usage: + | interact(target) + | .dropChecker(function(dragEvent, // related dragmove or dragend event + | event, // TouchEvent/PointerEvent/MouseEvent + | dropped, // bool result of the default checker + | dropzone, // dropzone Interactable + | dropElement, // dropzone elemnt + | draggable, // draggable Interactable + | draggableElement) {// draggable element + | + | return dropped && event.target.hasAttribute('allow-drop'); + | } + \*/ + dropChecker: function (checker) { + if (isFunction(checker)) { + this.options.drop.checker = checker; + + return this; + } + if (checker === null) { + delete this.options.getRect; -Interaction.signals.on('new', function (interaction) { - interaction.startOffset = { left: 0, right: 0, top: 0, bottom: 0 }; - interaction.modifierOffsets = {}; - interaction.modifierStatuses = modifiers.resetStatuses({}); -}); + return this; + } -Interaction.signals.on('action-start', modifiers.start); -Interaction.signals.on('action-resume', modifiers.start); + return this.options.drop.checker; + }, + + /*\ + * Interactable.accept + [ method ] + * + * Deprecated. add an `accept` property to the options object passed to + * @Interactable.dropzone instead. + * + * Gets or sets the Element or CSS selector match that this + * Interactable accepts if it is a dropzone. + * + - newValue (Element | string | null) #optional + * If it is an Element, then only that element can be dropped into this dropzone. + * If it is a string, the element being dragged must match it as a selector. + * If it is null, the accept options is cleared - it accepts any element. + * + = (string | Element | null | Interactable) The current accept option if given `undefined` or this Interactable + \*/ + accept: function (newValue) { + if (isElement(newValue)) { + this.options.drop.accept = newValue; + + return this; + } -Interaction.signals.on('before-action-move', function (_ref4) { - var interaction = _ref4.interaction; - var preEnd = _ref4.preEnd; - var interactingBeforeMove = _ref4.interactingBeforeMove; + // test if it is a valid CSS selector + if (trySelector(newValue)) { + this.options.drop.accept = newValue; - var modifierResult = modifiers.setAll(interaction, interaction.curCoords.page, interaction.modifierStatuses, preEnd); + return this; + } - // don't fire an action move if a modifier would keep the event in the same - // cordinates as before - if (!modifierResult.shouldMove && interactingBeforeMove) { - interaction._dontFireMove = true; - } -}); + if (newValue === null) { + delete this.options.drop.accept; -Interaction.signals.on('action-end', function (_ref5) { - var interaction = _ref5.interaction; - var event = _ref5.event; + return this; + } - for (var i = 0; i < modifiers.names.length; i++) { - // if the endOnly option is true for any modifier - if (modifiers[modifiers.names[i]].shouldDo(interaction.target, interaction.prepared.name, true, true)) { - // fire a move event at the modified coordinates - interaction.doMove({ event: event, preEnd: true }); - break; - } - } -}); - -InteractEvent.signals.on('set-xy', function (_ref6) { - var iEvent = _ref6.iEvent; - var interaction = _ref6.interaction; - var page = _ref6.page; - var client = _ref6.client; - var phase = _ref6.phase; - var actionName = _ref6.action; - - var target = interaction.target; - - for (var i = 0; i < modifiers.names.length; i++) { - var modifierName = modifiers.names[i]; - var modifier = modifiers[modifierName]; - - iEvent[modifierName] = modifier.modifyCoords(page, client, target, interaction.modifierStatuses[modifierName], actionName, phase); - } -}); - -module.exports = modifiers; - -},{"../InteractEvent":3,"../Interaction":5,"../utils/extend":36}],24:[function(require,module,exports){ -var modifiers = require('./index'); -var utils = require('../utils'); -var defaultOptions = require('../defaultOptions'); - -var restrict = { - defaults: { - enabled: false, - endOnly: false, - restriction: null, - elementRect: null - }, - - shouldDo: function (interactable, actionName, preEnd, requireEndOnly) { - var restrictOptions = interactable.options[actionName].restrict; - - return restrictOptions && restrictOptions.enabled && (preEnd || !restrictOptions.endOnly) && (!requireEndOnly || restrictOptions.endOnly); - }, - - setOffset: function (interaction, interactable, element, rect, startOffset) { - var elementRect = interactable.options[interaction.prepared.name].restrict.elementRect; - var offset = {}; - - if (rect && elementRect) { - offset.left = startOffset.left - rect.width * elementRect.left; - offset.top = startOffset.top - rect.height * elementRect.top; - - offset.right = startOffset.right - rect.width * (1 - elementRect.right); - offset.bottom = startOffset.bottom - rect.height * (1 - elementRect.bottom); - } else { - offset.left = offset.top = offset.right = offset.bottom = 0; - } + return this.options.drop.accept; + }, + + /*\ + * Interactable.resizable + [ method ] + * + * Gets or sets whether resize actions can be performed on the + * Interactable + * + = (boolean) Indicates if this can be the target of resize elements + | var isResizeable = interact('input[type=text]').resizable(); + * or + - options (boolean | object) #optional true/false or An object with event listeners to be fired on resize events (object makes the Interactable resizable) + = (object) This Interactable + | interact(element).resizable({ + | onstart: function (event) {}, + | onmove : function (event) {}, + | onend : function (event) {}, + | + | edges: { + | top : true, // Use pointer coords to check for resize. + | left : false, // Disable resizing from left edge. + | bottom: '.resize-s',// Resize if pointer target matches selector + | right : handleEl // Resize if pointer target is the given Element + | }, + | + | // Width and height can be adjusted independently. When `true`, width and + | // height are adjusted at a 1:1 ratio. + | square: false, + | + | // Width and height can be adjusted independently. When `true`, width and + | // height maintain the aspect ratio they had when resizing started. + | preserveAspectRatio: false, + | + | // a value of 'none' will limit the resize rect to a minimum of 0x0 + | // 'negate' will allow the rect to have negative width/height + | // 'reposition' will keep the width/height positive by swapping + | // the top and bottom edges and/or swapping the left and right edges + | invert: 'none' || 'negate' || 'reposition' + | + | // limit multiple resizes. + | // See the explanation in the @Interactable.draggable example + | max: Infinity, + | maxPerElement: 1, + | }); + \*/ + resizable: function (options) { + if (isObject(options)) { + this.options.resize.enabled = options.enabled === false? false: true; + this.setPerAction('resize', options); + this.setOnEvents('resize', options); + + if (/^x$|^y$|^xy$/.test(options.axis)) { + this.options.resize.axis = options.axis; + } + else if (options.axis === null) { + this.options.resize.axis = defaultOptions.resize.axis; + } + + if (isBool(options.preserveAspectRatio)) { + this.options.resize.preserveAspectRatio = options.preserveAspectRatio; + } + else if (isBool(options.square)) { + this.options.resize.square = options.square; + } + + return this; + } + if (isBool(options)) { + this.options.resize.enabled = options; - return offset; - }, + return this; + } + return this.options.resize; + }, + + /*\ + * Interactable.squareResize + [ method ] + * + * Deprecated. Add a `square: true || false` property to @Interactable.resizable instead + * + * Gets or sets whether resizing is forced 1:1 aspect + * + = (boolean) Current setting + * + * or + * + - newValue (boolean) #optional + = (object) this Interactable + \*/ + squareResize: function (newValue) { + if (isBool(newValue)) { + this.options.resize.square = newValue; + + return this; + } - set: function (pageCoords, interaction, status) { - var target = interaction.target; - var restrictOptions = target && target.options[interaction.prepared.name].restrict; - var restriction = restrictOptions && restrictOptions.restriction; + if (newValue === null) { + delete this.options.resize.square; - if (!restriction) { - return status; - } + return this; + } - var page = status.useStatusXY ? { x: status.x, y: status.y } : utils.extend({}, pageCoords); - - status.dx = 0; - status.dy = 0; - status.locked = false; - - if (utils.isString(restriction)) { - if (restriction === 'parent') { - restriction = utils.parentNode(interaction.element); - } else if (restriction === 'self') { - restriction = target.getRect(interaction.element); - } else { - restriction = utils.closest(interaction.element, restriction); - } - - if (!restriction) { - return status; - } - } - - if (utils.isFunction(restriction)) { - restriction = restriction(page.x, page.y, interaction.element); - } - - if (utils.isElement(restriction)) { - restriction = utils.getElementRect(restriction); - } - - var rect = restriction; - var restrictedX = undefined; - var restrictedY = undefined; - - var offset = interaction.modifierOffsets.restrict; - - if (!restriction) { - restrictedX = page.x; - restrictedY = page.y; - } - // object is assumed to have - // x, y, width, height or - // left, top, right, bottom - else if ('x' in restriction && 'y' in restriction) { - restrictedX = Math.max(Math.min(rect.x + rect.width - offset.right, page.x), rect.x + offset.left); - restrictedY = Math.max(Math.min(rect.y + rect.height - offset.bottom, page.y), rect.y + offset.top); - } else { - restrictedX = Math.max(Math.min(rect.right - offset.right, page.x), rect.left + offset.left); - restrictedY = Math.max(Math.min(rect.bottom - offset.bottom, page.y), rect.top + offset.top); - } - - status.dx = restrictedX - page.x; - status.dy = restrictedY - page.y; - - status.changed = status.restrictedX !== restrictedX || status.restrictedY !== restrictedY; - status.locked = !!(status.dx || status.dy); - - status.restrictedX = restrictedX; - status.restrictedY = restrictedY; - - return status; - }, - - reset: function (status) { - status.dx = status.dy = 0; - status.modifiedX = status.modifiedY = NaN; - status.locked = false; - status.changed = true; - - return status; - }, - - modifyCoords: function (page, client, interactable, status, actionName, phase) { - var options = interactable.options[actionName].restrict; - var elementRect = options && options.elementRect; - - if (options && options.enabled && !(phase === 'start' && elementRect && status.locked)) { - - if (status.locked) { - page.x += status.dx; - page.y += status.dy; - client.x += status.dx; - client.y += status.dy; - - return { - dx: status.dx, - dy: status.dy - }; - } - } - } -}; - -modifiers.restrict = restrict; -modifiers.names.push('restrict'); - -defaultOptions.perAction.restrict = restrict.defaults; - -module.exports = restrict; - -},{"../defaultOptions":17,"../utils":39,"./index":23}],25:[function(require,module,exports){ -var modifiers = require('./index'); -var interact = require('../interact'); -var utils = require('../utils'); -var defaultOptions = require('../defaultOptions'); - -var snap = { - defaults: { - enabled: false, - endOnly: false, - range: Infinity, - targets: null, - offsets: null, - - relativePoints: null - }, - - shouldDo: function (interactable, actionName, preEnd, requireEndOnly) { - var snapOptions = interactable.options[actionName].snap; - - return snapOptions && snapOptions.enabled && (preEnd || !snapOptions.endOnly) && (!requireEndOnly || snapOptions.endOnly); - }, - - setOffset: function (interaction, interactable, element, rect, startOffset) { - var offsets = []; - var origin = utils.getOriginXY(interactable, element, interaction.prepared.name); - var snapOptions = interactable.options[interaction.prepared.name].snap || {}; - var snapOffset = undefined; - - if (snapOptions.offset === 'startCoords') { - snapOffset = { - x: interaction.startCoords.page.x - origin.x, - y: interaction.startCoords.page.y - origin.y - }; - } else if (snapOptions.offset === 'self') { - snapOffset = { - x: rect.left - origin.x, - y: rect.top - origin.y - }; - } else { - snapOffset = snapOptions.offset || { x: 0, y: 0 }; - } - - if (rect && snapOptions.relativePoints && snapOptions.relativePoints.length) { - for (var _iterator = snapOptions.relativePoints, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var relativeX = _ref.x; - var relativeY = _ref.y; - - offsets.push({ - x: startOffset.left - rect.width * relativeX + snapOffset.x, - y: startOffset.top - rect.height * relativeY + snapOffset.y - }); - } - } else { - offsets.push(snapOffset); - } - - return offsets; - }, - - set: function (pageCoords, interaction, status) { - var snapOptions = interaction.target.options[interaction.prepared.name].snap; - var targets = []; - var target = undefined; - var page = undefined; - var i = undefined; - - if (status.useStatusXY) { - page = { x: status.x, y: status.y }; - } else { - var origin = utils.getOriginXY(interaction.target, interaction.element, interaction.prepared.name); - - page = utils.extend({}, pageCoords); - - page.x -= origin.x; - page.y -= origin.y; - } - - status.realX = page.x; - status.realY = page.y; - - var offsets = interaction.modifierOffsets.snap; - var len = snapOptions.targets ? snapOptions.targets.length : 0; - - for (var _iterator2 = offsets, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } - - var offsetX = _ref2.x; - var offsetY = _ref2.y; - - var relativeX = page.x - offsetX; - var relativeY = page.y - offsetY; - - for (var _iterator3 = snapOptions.targets, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - var snapTarget = _ref3; - - if (utils.isFunction(snapTarget)) { - target = snapTarget(relativeX, relativeY, interaction); - } else { - target = snapTarget; - } - - if (!target) { - continue; - } - - targets.push({ - x: utils.isNumber(target.x) ? target.x + offsetX : relativeX, - y: utils.isNumber(target.y) ? target.y + offsetY : relativeY, - - range: utils.isNumber(target.range) ? target.range : snapOptions.range - }); - } - } - - var closest = { - target: null, - inRange: false, - distance: 0, - range: 0, - dx: 0, - dy: 0 - }; - - for (i = 0, len = targets.length; i < len; i++) { - target = targets[i]; - - var range = target.range; - var dx = target.x - page.x; - var dy = target.y - page.y; - var distance = utils.hypot(dx, dy); - var inRange = distance <= range; - - // Infinite targets count as being out of range - // compared to non infinite ones that are in range - if (range === Infinity && closest.inRange && closest.range !== Infinity) { - inRange = false; - } - - if (!closest.target || (inRange - // is the closest target in range? - ? closest.inRange && range !== Infinity - // the pointer is relatively deeper in this target - ? distance / range < closest.distance / closest.range - // this target has Infinite range and the closest doesn't - : range === Infinity && closest.range !== Infinity || - // OR this target is closer that the previous closest - distance < closest.distance : - // The other is not in range and the pointer is closer to this target - !closest.inRange && distance < closest.distance)) { - - closest.target = target; - closest.distance = distance; - closest.range = range; - closest.inRange = inRange; - closest.dx = dx; - closest.dy = dy; - - status.range = range; - } - } - - var snapChanged = undefined; - - if (closest.target) { - snapChanged = status.snappedX !== closest.target.x || status.snappedY !== closest.target.y; - - status.snappedX = closest.target.x; - status.snappedY = closest.target.y; - } else { - snapChanged = true; - - status.snappedX = NaN; - status.snappedY = NaN; - } - - status.dx = closest.dx; - status.dy = closest.dy; - - status.changed = snapChanged || closest.inRange && !status.locked; - status.locked = closest.inRange; - - return status; - }, - - reset: function (status) { - status.dx = status.dy = 0; - status.snappedX = status.snappedY = NaN; - status.locked = false; - status.changed = true; - - return status; - }, - - modifyCoords: function (page, client, interactable, status, actionName, phase) { - var snapOptions = interactable.options[actionName].snap; - var relativePoints = snapOptions && snapOptions.relativePoints; - - if (snapOptions && snapOptions.enabled && !(phase === 'start' && relativePoints && relativePoints.length)) { - - if (status.locked) { - page.x += status.dx; - page.y += status.dy; - client.x += status.dx; - client.y += status.dy; - } - - return { - range: status.range, - locked: status.locked, - x: status.snappedX, - y: status.snappedY, - realX: status.realX, - realY: status.realY, - dx: status.dx, - dy: status.dy - }; - } - } -}; - -interact.createSnapGrid = function (grid) { - return function (x, y) { - var limits = grid.limits || { - left: -Infinity, - right: Infinity, - top: -Infinity, - bottom: Infinity - }; - var offsetX = 0; - var offsetY = 0; - - if (utils.isObject(grid.offset)) { - offsetX = grid.offset.x; - offsetY = grid.offset.y; - } - - var gridx = Math.round((x - offsetX) / grid.x); - var gridy = Math.round((y - offsetY) / grid.y); - - var newX = Math.max(limits.left, Math.min(limits.right, gridx * grid.x + offsetX)); - var newY = Math.max(limits.top, Math.min(limits.bottom, gridy * grid.y + offsetY)); - - return { - x: newX, - y: newY, - range: grid.range - }; - }; -}; - -modifiers.snap = snap; -modifiers.names.push('snap'); - -defaultOptions.perAction.snap = snap.defaults; - -module.exports = snap; - -},{"../defaultOptions":17,"../interact":20,"../utils":39,"./index":23}],26:[function(require,module,exports){ -var pointerEvents = require('./index.js'); -var Interaction = require('../Interaction'); - -pointerEvents.signals.on('new', function (_ref) { - var pointerEvent = _ref.pointerEvent; - - pointerEvent.count = (pointerEvent.count || 0) + 1; -}); - -pointerEvents.signals.on('fired', function (_ref2) { - var interaction = _ref2.interaction; - var pointerEvent = _ref2.pointerEvent; - var eventTarget = _ref2.eventTarget; - var targets = _ref2.targets; - - if (pointerEvent.type !== 'hold') { - return; - } - - // get the repeat interval from the first eventable - var interval = targets[0].eventable.options.holdRepeatInterval; - - // don't repeat if the interval is 0 or less - if (interval <= 0) { - return; - } - - // set a timeout to fire the holdrepeat event - interaction.holdIntervalHandle = setTimeout(function () { - pointerEvents.collectEventTargets(interaction, pointerEvent, pointerEvent, eventTarget, 'hold'); - }, interval); -}); - -function endHoldRepeat(_ref3) { - var interaction = _ref3.interaction; - - // set the interaction's holdStopTime property - // to stop further holdRepeat events - if (interaction.holdIntervalHandle) { - clearInterval(interaction.holdIntervalHandle); - interaction.holdIntervalHandle = null; - } -} - -var _arr = ['move', 'up', 'cancel', 'endall']; -for (var _i = 0; _i < _arr.length; _i++) { - var signal = _arr[_i]; - Interaction.signals.on(signal, endHoldRepeat); -} - -// don't repeat by default -pointerEvents.defaults.holdRepeatInterval = 0; -pointerEvents.types.push('holdrepeat'); - -},{"../Interaction":5,"./index.js":27}],27:[function(require,module,exports){ -var scope = require('../scope'); -var Interaction = require('../Interaction'); -var utils = require('../utils'); -var browser = require('../utils/browser'); -var defaults = require('../defaultOptions'); -var signals = require('../utils/Signals')['new'](); - -var _require = require('../utils/arr'); - -var filter = _require.filter; - -var simpleSignals = ['down', 'up', 'up', 'cancel']; -var simpleEvents = ['down', 'up', 'tap', 'cancel']; - -function preventOriginalDefault() { - this.originalEvent.preventDefault(); -} - -function stopImmediatePropagation() { - this.immediatePropagationStopped = this.propagationStopped = true; -} - -function stopPropagation() { - this.propagationStopped = true; -} - -function firePointers(interaction, pointer, event, eventTarget, targets, eventType) { - var pointerIndex = interaction.getPointerIndex(pointer); - var pointerEvent = {}; - var i = undefined; - // for tap events - var interval = undefined; - var createNewDoubleTap = undefined; - - // if it's a doubletap then the event properties would have been - // copied from the tap event and provided as the pointer argument - if (eventType === 'doubletap') { - pointerEvent = pointer; - } else { - utils.pointerExtend(pointerEvent, event); - if (event !== pointer) { - utils.pointerExtend(pointerEvent, pointer); - } - - pointerEvent.preventDefault = preventOriginalDefault; - pointerEvent.stopPropagation = stopPropagation; - pointerEvent.stopImmediatePropagation = stopImmediatePropagation; - pointerEvent.interaction = interaction; - - pointerEvent.timeStamp = new Date().getTime(); - pointerEvent.originalEvent = event; - pointerEvent.type = eventType; - pointerEvent.pointerId = utils.getPointerId(pointer); - pointerEvent.pointerType = interaction.mouse ? 'mouse' : !browser.supportsPointerEvent ? 'touch' : utils.isString(pointer.pointerType) ? pointer.pointerType : [undefined, undefined, 'touch', 'pen', 'mouse'][pointer.pointerType]; - } - - if (eventType === 'tap') { - pointerEvent.dt = pointerEvent.timeStamp - interaction.downTimes[pointerIndex]; - - interval = pointerEvent.timeStamp - interaction.tapTime; - createNewDoubleTap = !!(interaction.prevTap && interaction.prevTap.type !== 'doubletap' && interaction.prevTap.target === pointerEvent.target && interval < 500); - - pointerEvent.double = createNewDoubleTap; - - interaction.tapTime = pointerEvent.timeStamp; - } + return this.options.resize.square; + }, + + /*\ + * Interactable.gesturable + [ method ] + * + * Gets or sets whether multitouch gestures can be performed on the + * Interactable's element + * + = (boolean) Indicates if this can be the target of gesture events + | var isGestureable = interact(element).gesturable(); + * or + - options (boolean | object) #optional true/false or An object with event listeners to be fired on gesture events (makes the Interactable gesturable) + = (object) this Interactable + | interact(element).gesturable({ + | onstart: function (event) {}, + | onmove : function (event) {}, + | onend : function (event) {}, + | + | // limit multiple gestures. + | // See the explanation in @Interactable.draggable example + | max: Infinity, + | maxPerElement: 1, + | }); + \*/ + gesturable: function (options) { + if (isObject(options)) { + this.options.gesture.enabled = options.enabled === false? false: true; + this.setPerAction('gesture', options); + this.setOnEvents('gesture', options); + + return this; + } - var signalArg = { - interaction: interaction, - pointerEvent: pointerEvent, - pointer: pointer, - event: event, - eventTarget: eventTarget, - targets: targets - }; + if (isBool(options)) { + this.options.gesture.enabled = options; - signals.fire('new', signalArg); + return this; + } - for (i = 0; i < targets.length; i++) { - var target = targets[i]; + return this.options.gesture; + }, + + /*\ + * Interactable.autoScroll + [ method ] + ** + * Deprecated. Add an `autoscroll` property to the options object + * passed to @Interactable.draggable or @Interactable.resizable instead. + * + * Returns or sets whether dragging and resizing near the edges of the + * window/container trigger autoScroll for this Interactable + * + = (object) Object with autoScroll properties + * + * or + * + - options (object | boolean) #optional + * options can be: + * - an object with margin, distance and interval properties, + * - true or false to enable or disable autoScroll or + = (Interactable) this Interactable + \*/ + autoScroll: function (options) { + if (isObject(options)) { + options = extend({ actions: ['drag', 'resize']}, options); + } + else if (isBool(options)) { + options = { actions: ['drag', 'resize'], enabled: options }; + } - pointerEvent.currentTarget = target.element; + return this.setOptions('autoScroll', options); + }, + + /*\ + * Interactable.snap + [ method ] + ** + * Deprecated. Add a `snap` property to the options object passed + * to @Interactable.draggable or @Interactable.resizable instead. + * + * Returns or sets if and how action coordinates are snapped. By + * default, snapping is relative to the pointer coordinates. You can + * change this by setting the + * [`elementOrigin`](https://github.com/taye/interact.js/pull/72). + ** + = (boolean | object) `false` if snap is disabled; object with snap properties if snap is enabled + ** + * or + ** + - options (object | boolean | null) #optional + = (Interactable) this Interactable + > Usage + | interact(document.querySelector('#thing')).snap({ + | targets: [ + | // snap to this specific point + | { + | x: 100, + | y: 100, + | range: 25 + | }, + | // give this function the x and y page coords and snap to the object returned + | function (x, y) { + | return { + | x: x, + | y: (75 + 50 * Math.sin(x * 0.04)), + | range: 40 + | }; + | }, + | // create a function that snaps to a grid + | interact.createSnapGrid({ + | x: 50, + | y: 50, + | range: 10, // optional + | offset: { x: 5, y: 10 } // optional + | }) + | ], + | // do not snap during normal movement. + | // Instead, trigger only one snapped move event + | // immediately before the end event. + | endOnly: true, + | + | relativePoints: [ + | { x: 0, y: 0 }, // snap relative to the top left of the element + | { x: 1, y: 1 }, // and also to the bottom right + | ], + | + | // offset the snap target coordinates + | // can be an object with x/y or 'startCoords' + | offset: { x: 50, y: 50 } + | } + | }); + \*/ + snap: function (options) { + var ret = this.setOptions('snap', options); + + if (ret === this) { return this; } + + return ret.drag; + }, + + setOptions: function (option, options) { + var actions = options && isArray(options.actions) + ? options.actions + : ['drag']; + + var i; + + if (isObject(options) || isBool(options)) { + for (i = 0; i < actions.length; i++) { + var action = /resize/.test(actions[i])? 'resize' : actions[i]; + + if (!isObject(this.options[action])) { continue; } + + var thisOption = this.options[action][option]; + + if (isObject(options)) { + extend(thisOption, options); + thisOption.enabled = options.enabled === false? false: true; + + if (option === 'snap') { + if (thisOption.mode === 'grid') { + thisOption.targets = [ + interact.createSnapGrid(extend({ + offset: thisOption.gridOffset || { x: 0, y: 0 } + }, thisOption.grid || {})) + ]; + } + else if (thisOption.mode === 'anchor') { + thisOption.targets = thisOption.anchors; + } + else if (thisOption.mode === 'path') { + thisOption.targets = thisOption.paths; + } + + if ('elementOrigin' in options) { + thisOption.relativePoints = [options.elementOrigin]; + } + } + } + else if (isBool(options)) { + thisOption.enabled = options; + } + } + + return this; + } - for (var prop in target.props || {}) { - pointerEvent[prop] = target.props[prop]; - } + var ret = {}, + allActions = ['drag', 'resize', 'gesture']; - var _utils$getOriginXY = utils.getOriginXY(target.eventable, target.element); + for (i = 0; i < allActions.length; i++) { + if (option in defaultOptions[allActions[i]]) { + ret[allActions[i]] = this.options[allActions[i]][option]; + } + } - var originX = _utils$getOriginXY.x; - var originY = _utils$getOriginXY.y; + return ret; + }, + + + /*\ + * Interactable.inertia + [ method ] + ** + * Deprecated. Add an `inertia` property to the options object passed + * to @Interactable.draggable or @Interactable.resizable instead. + * + * Returns or sets if and how events continue to run after the pointer is released + ** + = (boolean | object) `false` if inertia is disabled; `object` with inertia properties if inertia is enabled + ** + * or + ** + - options (object | boolean | null) #optional + = (Interactable) this Interactable + > Usage + | // enable and use default settings + | interact(element).inertia(true); + | + | // enable and use custom settings + | interact(element).inertia({ + | // value greater than 0 + | // high values slow the object down more quickly + | resistance : 16, + | + | // the minimum launch speed (pixels per second) that results in inertia start + | minSpeed : 200, + | + | // inertia will stop when the object slows down to this speed + | endSpeed : 20, + | + | // boolean; should actions be resumed when the pointer goes down during inertia + | allowResume : true, + | + | // boolean; should the jump when resuming from inertia be ignored in event.dx/dy + | zeroResumeDelta: false, + | + | // if snap/restrict are set to be endOnly and inertia is enabled, releasing + | // the pointer without triggering inertia will animate from the release + | // point to the snaped/restricted point in the given amount of time (ms) + | smoothEndDuration: 300, + | + | // an array of action types that can have inertia (no gesture) + | actions : ['drag', 'resize'] + | }); + | + | // reset custom settings and use all defaults + | interact(element).inertia(null); + \*/ + inertia: function (options) { + var ret = this.setOptions('inertia', options); + + if (ret === this) { return this; } + + return ret.drag; + }, + + getAction: function (pointer, event, interaction, element) { + var action = this.defaultActionChecker(pointer, interaction, element); + + if (this.options.actionChecker) { + return this.options.actionChecker(pointer, event, action, this, element, interaction); + } - pointerEvent.pageX -= originX; - pointerEvent.pageY -= originY; - pointerEvent.clientX -= originX; - pointerEvent.clientY -= originY; + return action; + }, + + defaultActionChecker: defaultActionChecker, + + /*\ + * Interactable.actionChecker + [ method ] + * + * Gets or sets the function used to check action to be performed on + * pointerDown + * + - checker (function | null) #optional A function which takes a pointer event, defaultAction string, interactable, element and interaction as parameters and returns an object with name property 'drag' 'resize' or 'gesture' and optionally an `edges` object with boolean 'top', 'left', 'bottom' and right props. + = (Function | Interactable) The checker function or this Interactable + * + | interact('.resize-drag') + | .resizable(true) + | .draggable(true) + | .actionChecker(function (pointer, event, action, interactable, element, interaction) { + | + | if (interact.matchesSelector(event.target, '.drag-handle') { + | // force drag with handle target + | action.name = drag; + | } + | else { + | // resize from the top and right edges + | action.name = 'resize'; + | action.edges = { top: true, right: true }; + | } + | + | return action; + | }); + \*/ + actionChecker: function (checker) { + if (isFunction(checker)) { + this.options.actionChecker = checker; + + return this; + } - pointerEvent.eventable = target.eventable; + if (checker === null) { + delete this.options.actionChecker; - target.eventable.fire(pointerEvent); + return this; + } - pointerEvent.pageX += originX; - pointerEvent.pageY += originY; - pointerEvent.clientX += originX; - pointerEvent.clientY += originY; + return this.options.actionChecker; + }, + + /*\ + * Interactable.getRect + [ method ] + * + * The default function to get an Interactables bounding rect. Can be + * overridden using @Interactable.rectChecker. + * + - element (Element) #optional The element to measure. + = (object) The object's bounding rectangle. + o { + o top : 0, + o left : 0, + o bottom: 0, + o right : 0, + o width : 0, + o height: 0 + o } + \*/ + getRect: function rectCheck (element) { + element = element || this._element; + + if (this.selector && !(isElement(element))) { + element = this._context.querySelector(this.selector); + } - if (pointerEvent.immediatePropagationStopped || pointerEvent.propagationStopped && i + 1 < targets.length && targets[i + 1].element !== pointerEvent.currentTarget) { - break; - } - } - - signals.fire('fired', signalArg); - - if (createNewDoubleTap) { - var doubleTap = {}; - - utils.extend(doubleTap, pointerEvent); - - doubleTap.dt = interval; - doubleTap.type = 'doubletap'; - - collectEventTargets(interaction, doubleTap, event, eventTarget, 'doubletap'); - - interaction.prevTap = doubleTap; - } else if (eventType === 'tap') { - interaction.prevTap = pointerEvent; - } -} - -function collectEventTargets(interaction, pointer, event, eventTarget, eventType) { - var pointerIndex = interaction.getPointerIndex(pointer); - - // do not fire a tap event if the pointer was moved before being lifted - if (eventType === 'tap' && (interaction.pointerWasMoved - // or if the pointerup target is different to the pointerdown target - || !(interaction.downTargets[pointerIndex] && interaction.downTargets[pointerIndex] === eventTarget))) { - return; - } - - var targets = []; - var path = utils.getPath(eventTarget); - var signalArg = { - targets: targets, - interaction: interaction, - pointer: pointer, - event: event, - eventTarget: eventTarget, - eventType: eventType, - path: path, - element: null - }; - - for (var _iterator = path, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + return getElementRect(element); + }, + + /*\ + * Interactable.rectChecker + [ method ] + * + * Returns or sets the function used to calculate the interactable's + * element's rectangle + * + - checker (function) #optional A function which returns this Interactable's bounding rectangle. See @Interactable.getRect + = (function | object) The checker function or this Interactable + \*/ + rectChecker: function (checker) { + if (isFunction(checker)) { + this.getRect = checker; + + return this; + } - var element = _ref; + if (checker === null) { + delete this.options.getRect; - signalArg.element = element; + return this; + } - signals.fire('collect-targets', signalArg); - } + return this.getRect; + }, + + /*\ + * Interactable.styleCursor + [ method ] + * + * Returns or sets whether the action that would be performed when the + * mouse on the element are checked on `mousemove` so that the cursor + * may be styled appropriately + * + - newValue (boolean) #optional + = (boolean | Interactable) The current setting or this Interactable + \*/ + styleCursor: function (newValue) { + if (isBool(newValue)) { + this.options.styleCursor = newValue; + + return this; + } - if (eventType === 'hold') { - targets = filter(targets, function (target) { - return target.eventable.options.holdDuration === interaction.holdTimers[pointerIndex].duration; - }); - } + if (newValue === null) { + delete this.options.styleCursor; - // create the tap event even if there are no listeners so that - // doubletap can still be created and fired - if (targets.length || eventType === 'tap') { - firePointers(interaction, pointer, event, eventTarget, targets, eventType); - } -} + return this; + } -Interaction.signals.on('move', function (_ref3) { - var interaction = _ref3.interaction; - var pointer = _ref3.pointer; - var event = _ref3.event; - var eventTarget = _ref3.eventTarget; - var duplicateMove = _ref3.duplicateMove; + return this.options.styleCursor; + }, + + /*\ + * Interactable.preventDefault + [ method ] + * + * Returns or sets whether to prevent the browser's default behaviour + * in response to pointer events. Can be set to: + * - `'always'` to always prevent + * - `'never'` to never prevent + * - `'auto'` to let interact.js try to determine what would be best + * + - newValue (string) #optional `true`, `false` or `'auto'` + = (string | Interactable) The current setting or this Interactable + \*/ + preventDefault: function (newValue) { + if (/^(always|never|auto)$/.test(newValue)) { + this.options.preventDefault = newValue; + return this; + } - var pointerIndex = interaction.getPointerIndex(pointer); + if (isBool(newValue)) { + this.options.preventDefault = newValue? 'always' : 'never'; + return this; + } - if (!duplicateMove && (!interaction.pointerIsDown || interaction.pointerWasMoved)) { - if (interaction.pointerIsDown) { - clearTimeout(interaction.holdTimers[pointerIndex].timeout); - } + return this.options.preventDefault; + }, + + /*\ + * Interactable.origin + [ method ] + * + * Gets or sets the origin of the Interactable's element. The x and y + * of the origin will be subtracted from action event coordinates. + * + - origin (object | string) #optional An object eg. { x: 0, y: 0 } or string 'parent', 'self' or any CSS selector + * OR + - origin (Element) #optional An HTML or SVG Element whose rect will be used + ** + = (object) The current origin or this Interactable + \*/ + origin: function (newValue) { + if (trySelector(newValue)) { + this.options.origin = newValue; + return this; + } + else if (isObject(newValue)) { + this.options.origin = newValue; + return this; + } - collectEventTargets(interaction, pointer, event, eventTarget, 'move'); - } -}); - -Interaction.signals.on('down', function (_ref4) { - var interaction = _ref4.interaction; - var pointer = _ref4.pointer; - var event = _ref4.event; - var eventTarget = _ref4.eventTarget; - var pointerIndex = _ref4.pointerIndex; - - // copy event to be used in timeout for IE8 - var eventCopy = browser.isIE8 ? utils.extend({}, event) : event; - var timers = interaction.holdTimers; - - if (!timers[pointerIndex]) { - timers[pointerIndex] = { duration: Infinity, timeout: null }; - } - - var timer = timers[pointerIndex]; - var path = utils.getPath(eventTarget); - var signalArg = { - interaction: interaction, - pointer: pointer, - event: event, - eventTarget: eventTarget, - eventType: 'hold', - targets: [], - path: path, - element: null - }; - - for (var _iterator2 = path, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } + return this.options.origin; + }, + + /*\ + * Interactable.deltaSource + [ method ] + * + * Returns or sets the mouse coordinate types used to calculate the + * movement of the pointer. + * + - newValue (string) #optional Use 'client' if you will be scrolling while interacting; Use 'page' if you want autoScroll to work + = (string | object) The current deltaSource or this Interactable + \*/ + deltaSource: function (newValue) { + if (newValue === 'page' || newValue === 'client') { + this.options.deltaSource = newValue; + + return this; + } - var element = _ref2; + return this.options.deltaSource; + }, + + /*\ + * Interactable.restrict + [ method ] + ** + * Deprecated. Add a `restrict` property to the options object passed to + * @Interactable.draggable, @Interactable.resizable or @Interactable.gesturable instead. + * + * Returns or sets the rectangles within which actions on this + * interactable (after snap calculations) are restricted. By default, + * restricting is relative to the pointer coordinates. You can change + * this by setting the + * [`elementRect`](https://github.com/taye/interact.js/pull/72). + ** + - options (object) #optional an object with keys drag, resize, and/or gesture whose values are rects, Elements, CSS selectors, or 'parent' or 'self' + = (object) The current restrictions object or this Interactable + ** + | interact(element).restrict({ + | // the rect will be `interact.getElementRect(element.parentNode)` + | drag: element.parentNode, + | + | // x and y are relative to the the interactable's origin + | resize: { x: 100, y: 100, width: 200, height: 200 } + | }) + | + | interact('.draggable').restrict({ + | // the rect will be the selected element's parent + | drag: 'parent', + | + | // do not restrict during normal movement. + | // Instead, trigger only one restricted move event + | // immediately before the end event. + | endOnly: true, + | + | // https://github.com/taye/interact.js/pull/72#issue-41813493 + | elementRect: { top: 0, left: 0, bottom: 1, right: 1 } + | }); + \*/ + restrict: function (options) { + if (!isObject(options)) { + return this.setOptions('restrict', options); + } - signalArg.element = element; + var actions = ['drag', 'resize', 'gesture'], + ret; - signals.fire('collect-targets', signalArg); - } + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; - if (!signalArg.targets.length) { - return; - } + if (action in options) { + var perAction = extend({ + actions: [action], + restriction: options[action] + }, options); - var minDuration = Infinity; + ret = this.setOptions('restrict', perAction); + } + } - for (var i = 0; i < signalArg.targets.length; i++) { - var target = signalArg.targets[i]; - var holdDuration = target.eventable.options.holdDuration; + return ret; + }, + + /*\ + * Interactable.context + [ method ] + * + * Gets the selector context Node of the Interactable. The default is `window.document`. + * + = (Node) The context Node of this Interactable + ** + \*/ + context: function () { + return this._context; + }, + + _context: document, + + /*\ + * Interactable.ignoreFrom + [ method ] + * + * If the target of the `mousedown`, `pointerdown` or `touchstart` + * event or any of it's parents match the given CSS selector or + * Element, no drag/resize/gesture is started. + * + - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to not ignore any elements + = (string | Element | object) The current ignoreFrom value or this Interactable + ** + | interact(element, { ignoreFrom: document.getElementById('no-action') }); + | // or + | interact(element).ignoreFrom('input, textarea, a'); + \*/ + ignoreFrom: function (newValue) { + if (trySelector(newValue)) { // CSS selector to match event.target + this.options.ignoreFrom = newValue; + return this; + } - if (holdDuration < minDuration) { - minDuration = holdDuration; - } - } + if (isElement(newValue)) { // specific element + this.options.ignoreFrom = newValue; + return this; + } - timer.duration = minDuration; - timer.timeout = setTimeout(function () { + return this.options.ignoreFrom; + }, + + /*\ + * Interactable.allowFrom + [ method ] + * + * A drag/resize/gesture is started only If the target of the + * `mousedown`, `pointerdown` or `touchstart` event or any of it's + * parents match the given CSS selector or Element. + * + - newValue (string | Element | null) #optional a CSS selector string, an Element or `null` to allow from any element + = (string | Element | object) The current allowFrom value or this Interactable + ** + | interact(element, { allowFrom: document.getElementById('drag-handle') }); + | // or + | interact(element).allowFrom('.handle'); + \*/ + allowFrom: function (newValue) { + if (trySelector(newValue)) { // CSS selector to match event.target + this.options.allowFrom = newValue; + return this; + } - collectEventTargets(interaction, browser.isIE8 ? eventCopy : pointer, eventCopy, eventTarget, 'hold'); - }, minDuration); -}); + if (isElement(newValue)) { // specific element + this.options.allowFrom = newValue; + return this; + } -['up', 'cancel'].forEach(function (signalName) { - Interaction.signals.on(signalName, function (_ref5) { - var interaction = _ref5.interaction; - var pointerIndex = _ref5.pointerIndex; + return this.options.allowFrom; + }, + + /*\ + * Interactable.element + [ method ] + * + * If this is not a selector Interactable, it returns the element this + * interactable represents + * + = (Element) HTML / SVG Element + \*/ + element: function () { + return this._element; + }, + + /*\ + * Interactable.fire + [ method ] + * + * Calls listeners for the given InteractEvent type bound globally + * and directly to this Interactable + * + - iEvent (InteractEvent) The InteractEvent object to be fired on this Interactable + = (Interactable) this Interactable + \*/ + fire: function (iEvent) { + if (!(iEvent && iEvent.type) || !contains(eventTypes, iEvent.type)) { + return this; + } - if (interaction.holdTimers[pointerIndex]) { - clearTimeout(interaction.holdTimers[pointerIndex].timeout); - } - }); -}); - -function createSignalListener(event) { - return function (arg) { - collectEventTargets(arg.interaction, arg.pointer, arg.event, arg.eventTarget, event); - }; -} - -for (var i = 0; i < simpleSignals.length; i++) { - Interaction.signals.on(simpleSignals[i], createSignalListener(simpleEvents[i])); -} - -Interaction.signals.on('new', function (interaction) { - interaction.prevTap = null; // the most recent tap event on this interaction - interaction.tapTime = 0; // time of the most recent tap event -}); - -defaults.pointerEvents = { - holdDuration: 600, - ignoreFrom: null, - allowFrom: null, - origin: { x: 0, y: 0 } -}; - -module.exports = scope.pointerEvents = { - firePointers: firePointers, - collectEventTargets: collectEventTargets, - preventOriginalDefault: preventOriginalDefault, - signals: signals, - defaults: defaults.pointerEvents, - types: ['down', 'move', 'up', 'cancel', 'tap', 'doubletap', 'hold'] -}; - -},{"../Interaction":5,"../defaultOptions":17,"../scope":29,"../utils":39,"../utils/Signals":30,"../utils/arr":31,"../utils/browser":32}],28:[function(require,module,exports){ -var pointerEvents = require('./index'); -var Interactable = require('../Interactable'); -var browser = require('../utils/browser'); -var isType = require('../utils/isType'); -var domUtils = require('../utils/domUtils'); -var scope = require('../scope'); -var extend = require('../utils/extend'); - -var _require = require('../utils/arr'); - -var merge = _require.merge; - -pointerEvents.signals.on('collect-targets', function (_ref) { - var targets = _ref.targets; - var element = _ref.element; - var eventType = _ref.eventType; - var eventTarget = _ref.eventTarget; - - function collectSelectors(interactable, selector, context) { - var els = browser.useMatchesSelectorPolyfill ? context.querySelectorAll(selector) : undefined; - - var eventable = interactable.events; - var options = eventable.options; - - if (eventable[eventType] && isType.isElement(element) && domUtils.matchesSelector(element, selector, els) && interactable.testIgnoreAllow(options, element, eventTarget)) { - - targets.push({ - element: element, - eventable: eventable, - props: { interactable: interactable } - }); - } - } + var listeners, + i, + len, + onEvent = 'on' + iEvent.type, + funcName = ''; - var interactable = scope.interactables.get(element); + // Interactable#on() listeners + if (iEvent.type in this._iEvents) { + listeners = this._iEvents[iEvent.type]; - if (interactable) { - var eventable = interactable.events; - var options = eventable.options; + for (i = 0, len = listeners.length; i < len && !iEvent.immediatePropagationStopped; i++) { + funcName = listeners[i].name; + listeners[i](iEvent); + } + } - if (eventable[eventType] && interactable.testIgnoreAllow(options, element, eventTarget)) { - targets.push({ - element: element, - eventable: eventable, - props: { interactable: interactable } - }); - } - } + // interactable.onevent listener + if (isFunction(this[onEvent])) { + funcName = this[onEvent].name; + this[onEvent](iEvent); + } - scope.interactables.forEachSelector(collectSelectors, element); -}); + // interact.on() listeners + if (iEvent.type in globalEvents && (listeners = globalEvents[iEvent.type])) { -Interactable.signals.on('new', function (_ref2) { - var interactable = _ref2.interactable; + for (i = 0, len = listeners.length; i < len && !iEvent.immediatePropagationStopped; i++) { + funcName = listeners[i].name; + listeners[i](iEvent); + } + } - interactable.events.getRect = function (element) { - return interactable.getRect(element); - }; -}); + return this; + }, + + /*\ + * Interactable.on + [ method ] + * + * Binds a listener for an InteractEvent or DOM event. + * + - eventType (string | array | object) The types of events to listen for + - listener (function) The function to be called on the given event(s) + - useCapture (boolean) #optional useCapture flag for addEventListener + = (object) This Interactable + \*/ + on: function (eventType, listener, useCapture) { + var i; + + if (isString(eventType) && eventType.search(' ') !== -1) { + eventType = eventType.trim().split(/ +/); + } -Interactable.signals.on('set', function (_ref3) { - var interactable = _ref3.interactable; - var options = _ref3.options; + if (isArray(eventType)) { + for (i = 0; i < eventType.length; i++) { + this.on(eventType[i], listener, useCapture); + } - extend(interactable.events.options, pointerEvents.defaults); - extend(interactable.events.options, options); -}); + return this; + } -merge(Interactable.eventTypes, pointerEvents.types); + if (isObject(eventType)) { + for (var prop in eventType) { + this.on(prop, eventType[prop], listener); + } -Interactable.prototype.pointerEvents = function (options) { - extend(this.events.options, options); + return this; + } - return this; -}; + if (eventType === 'wheel') { + eventType = wheelEvent; + } -var __backCompatOption = Interactable.prototype._backCompatOption; + // convert to boolean + useCapture = useCapture? true: false; + + if (contains(eventTypes, eventType)) { + // if this type of event was never bound to this Interactable + if (!(eventType in this._iEvents)) { + this._iEvents[eventType] = [listener]; + } + else { + this._iEvents[eventType].push(listener); + } + } + // delegated event for selector + else if (this.selector) { + if (!delegatedEvents[eventType]) { + delegatedEvents[eventType] = { + selectors: [], + contexts : [], + listeners: [] + }; + + // add delegate listener functions + for (i = 0; i < documents.length; i++) { + events.add(documents[i], eventType, delegateListener); + events.add(documents[i], eventType, delegateUseCapture, true); + } + } + + var delegated = delegatedEvents[eventType], + index; + + for (index = delegated.selectors.length - 1; index >= 0; index--) { + if (delegated.selectors[index] === this.selector + && delegated.contexts[index] === this._context) { + break; + } + } + + if (index === -1) { + index = delegated.selectors.length; + + delegated.selectors.push(this.selector); + delegated.contexts .push(this._context); + delegated.listeners.push([]); + } + + // keep listener and useCapture flag + delegated.listeners[index].push([listener, useCapture]); + } + else { + events.add(this._element, eventType, listener, useCapture); + } -Interactable.prototype._backCompatOption = function (optionName, newValue) { - var ret = __backCompatOption.call(this, optionName, newValue); + return this; + }, + + /*\ + * Interactable.off + [ method ] + * + * Removes an InteractEvent or DOM event listener + * + - eventType (string | array | object) The types of events that were listened for + - listener (function) The listener function to be removed + - useCapture (boolean) #optional useCapture flag for removeEventListener + = (object) This Interactable + \*/ + off: function (eventType, listener, useCapture) { + var i; + + if (isString(eventType) && eventType.search(' ') !== -1) { + eventType = eventType.trim().split(/ +/); + } - if (ret === this) { - this.events.options[optionName] = newValue; - } + if (isArray(eventType)) { + for (i = 0; i < eventType.length; i++) { + this.off(eventType[i], listener, useCapture); + } - return ret; -}; + return this; + } -Interactable.settingsMethods.push('pointerEvents'); + if (isObject(eventType)) { + for (var prop in eventType) { + this.off(prop, eventType[prop], listener); + } -},{"../Interactable":4,"../scope":29,"../utils/arr":31,"../utils/browser":32,"../utils/domUtils":34,"../utils/extend":36,"../utils/isType":41,"./index":27}],29:[function(require,module,exports){ -var utils = require('./utils'); -var extend = require('./utils/extend'); -var events = require('./utils/events'); -var signals = require('./utils/Signals')['new'](); + return this; + } -var scope = { - signals: signals, - events: events, - utils: utils, + var eventList, + index = -1; - documents: [], // all documents being listened to + // convert to boolean + useCapture = useCapture? true: false; - addDocument: function (doc, win) { - // do nothing if document is already known - if (utils.contains(scope.documents, doc)) { - return false; - } + if (eventType === 'wheel') { + eventType = wheelEvent; + } - win = win || scope.getWindow(doc); + // if it is an action event type + if (contains(eventTypes, eventType)) { + eventList = this._iEvents[eventType]; - scope.documents.push(doc); - events.documents.push(doc); + if (eventList && (index = indexOf(eventList, listener)) !== -1) { + this._iEvents[eventType].splice(index, 1); + } + } + // delegated event + else if (this.selector) { + var delegated = delegatedEvents[eventType], + matchFound = false; + + if (!delegated) { return this; } + + // count from last index of delegated to 0 + for (index = delegated.selectors.length - 1; index >= 0; index--) { + // look for matching selector and context Node + if (delegated.selectors[index] === this.selector + && delegated.contexts[index] === this._context) { + + var listeners = delegated.listeners[index]; + + // each item of the listeners array is an array: [function, useCaptureFlag] + for (i = listeners.length - 1; i >= 0; i--) { + var fn = listeners[i][0], + useCap = listeners[i][1]; + + // check if the listener functions and useCapture flags match + if (fn === listener && useCap === useCapture) { + // remove the listener from the array of listeners + listeners.splice(i, 1); + + // if all listeners for this interactable have been removed + // remove the interactable from the delegated arrays + if (!listeners.length) { + delegated.selectors.splice(index, 1); + delegated.contexts .splice(index, 1); + delegated.listeners.splice(index, 1); + + // remove delegate function from context + events.remove(this._context, eventType, delegateListener); + events.remove(this._context, eventType, delegateUseCapture, true); + + // remove the arrays if they are empty + if (!delegated.selectors.length) { + delegatedEvents[eventType] = null; + } + } + + // only remove one listener + matchFound = true; + break; + } + } + + if (matchFound) { break; } + } + } + } + // remove listener from this Interatable's element + else { + events.remove(this._element, eventType, listener, useCapture); + } - // don't add an unload event for the main document - // so that the page may be cached in browser history - if (doc !== scope.document) { - events.add(win, 'unload', scope.onWindowUnload); - } + return this; + }, + + /*\ + * Interactable.set + [ method ] + * + * Reset the options of this Interactable + - options (object) The new settings to apply + = (object) This Interactable + \*/ + set: function (options) { + if (!isObject(options)) { + options = {}; + } - signals.fire('add-document', { doc: doc, win: win }); - }, + this.options = extend({}, defaultOptions.base); - removeDocument: function (doc, win) { - var index = utils.indexOf(scope.documents, doc); + var i, + actions = ['drag', 'drop', 'resize', 'gesture'], + methods = ['draggable', 'dropzone', 'resizable', 'gesturable'], + perActions = extend(extend({}, defaultOptions.perAction), options[action] || {}); - win = win || scope.getWindow(doc); + for (i = 0; i < actions.length; i++) { + var action = actions[i]; - events.remove(win, 'unload', scope.onWindowUnload); + this.options[action] = extend({}, defaultOptions[action]); - scope.documents.splice(index, 1); - events.documents.splice(index, 1); + this.setPerAction(action, perActions); - signals.fire('remove-document', { win: win, doc: doc }); - }, + this[methods[i]](options[action]); + } - onWindowUnload: function () { - scope.removeDocument(this.document, this); - } -}; + var settings = [ + 'accept', 'actionChecker', 'allowFrom', 'deltaSource', + 'dropChecker', 'ignoreFrom', 'origin', 'preventDefault', + 'rectChecker', 'styleCursor' + ]; -extend(scope, require('./utils/window')); -extend(scope, require('./utils/domObjects')); + for (i = 0, len = settings.length; i < len; i++) { + var setting = settings[i]; -module.exports = scope; + this.options[setting] = defaultOptions.base[setting]; -},{"./utils":39,"./utils/Signals":30,"./utils/domObjects":33,"./utils/events":35,"./utils/extend":36,"./utils/window":46}],30:[function(require,module,exports){ -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + if (setting in options) { + this[setting](options[setting]); + } + } -var _require = require('./arr'); + return this; + }, + + /*\ + * Interactable.unset + [ method ] + * + * Remove this interactable from the list of interactables and remove + * it's drag, drop, resize and gesture capabilities + * + = (object) @interact + \*/ + unset: function () { + events.remove(this._element, 'all'); + + if (!isString(this.selector)) { + events.remove(this, 'all'); + if (this.options.styleCursor) { + this._element.style.cursor = ''; + } + } + else { + // remove delegated events + for (var type in delegatedEvents) { + var delegated = delegatedEvents[type]; + + for (var i = 0; i < delegated.selectors.length; i++) { + if (delegated.selectors[i] === this.selector + && delegated.contexts[i] === this._context) { + + delegated.selectors.splice(i, 1); + delegated.contexts .splice(i, 1); + delegated.listeners.splice(i, 1); + + // remove the arrays if they are empty + if (!delegated.selectors.length) { + delegatedEvents[type] = null; + } + } + + events.remove(this._context, type, delegateListener); + events.remove(this._context, type, delegateUseCapture, true); + + break; + } + } + } -var indexOf = _require.indexOf; + this.dropzone(false); -var Signals = (function () { - function Signals() { - _classCallCheck(this, Signals); + interactables.splice(indexOf(interactables, this), 1); - this.listeners = { - // signalName: [listeners], + return interact; + } }; - } - - Signals.prototype.on = function on(name, listener) { - if (!this.listeners[name]) { - this.listeners[name] = [listener]; - return; - } - - this.listeners[name].push(listener); - }; - - Signals.prototype.off = function off(name, listener) { - if (!this.listeners[name]) { - return; - } - - var index = indexOf(this.listeners[name], listener); - - if (index !== -1) { - this.listeners[name].splice(index, 1); - } - }; - - Signals.prototype.fire = function fire(name, arg) { - var targetListeners = this.listeners[name]; - - if (!targetListeners) { - return; - } - - for (var i = 0; i < targetListeners.length; i++) { - if (targetListeners[i](arg, name) === false) { - return; - } - } - }; - - return Signals; -})(); - -Signals['new'] = function () { - return new Signals(); -}; - -module.exports = Signals; -},{"./arr":31}],31:[function(require,module,exports){ -function indexOf(array, target) { - for (var i = 0, len = array.length; i < len; i++) { - if (array[i] === target) { - return i; - } - } - - return -1; -} - -function contains(array, target) { - return indexOf(array, target) !== -1; -} - -function merge(target, source) { - for (var i = 0; i < source.length; i++) { - target.push(source[i]); - } - - return target; -} - -function filter(array, test) { - var result = []; - - for (var i = 0; i < array.length; i++) { - if (test(array[i])) { - result.push(array[i]); - } - } - - return result; -} - -module.exports = { - indexOf: indexOf, - contains: contains, - merge: merge, - filter: filter -}; - -},{}],32:[function(require,module,exports){ -var win = require('./window'); -var isType = require('./isType'); -var domObjects = require('./domObjects'); - -var browser = { - // Does the browser support touch input? - supportsTouch: !!('ontouchstart' in win.window || isType.isFunction(win.window.DocumentTouch) && domObjects.document instanceof win.DocumentTouch), - - // Does the browser support PointerEvents - supportsPointerEvent: !!domObjects.PointerEvent, - - isIE8: 'attachEvent' in win.window && !('addEventListener' in win.window), - - // Opera Mobile must be handled differently - isOperaMobile: navigator.appName === 'Opera' && browser.supportsTouch && navigator.userAgent.match('Presto'), - - // scrolling doesn't change the result of getClientRects on iOS 7 - isIOS7: /iP(hone|od|ad)/.test(navigator.platform) && /OS 7[^\d]/.test(navigator.appVersion), - - isIe9OrOlder: /MSIE (8|9)/.test(navigator.userAgent), - - // prefix matchesSelector - prefixedMatchesSelector: 'matches' in Element.prototype ? 'matches' : 'webkitMatchesSelector' in Element.prototype ? 'webkitMatchesSelector' : 'mozMatchesSelector' in Element.prototype ? 'mozMatchesSelector' : 'oMatchesSelector' in Element.prototype ? 'oMatchesSelector' : 'msMatchesSelector', - - useMatchesSelectorPolyfill: false, - - pEventTypes: domObjects.PointerEvent ? domObjects.PointerEvent === win.window.MSPointerEvent ? { - up: 'MSPointerUp', - down: 'MSPointerDown', - over: 'mouseover', - out: 'mouseout', - move: 'MSPointerMove', - cancel: 'MSPointerCancel' - } : { - up: 'pointerup', - down: 'pointerdown', - over: 'pointerover', - out: 'pointerout', - move: 'pointermove', - cancel: 'pointercancel' - } : null, - - // because Webkit and Opera still use 'mousewheel' event type - wheelEvent: 'onmousewheel' in domObjects.document ? 'mousewheel' : 'wheel' - -}; - -browser.useMatchesSelectorPolyfill = !isType.isFunction(Element.prototype[browser.prefixedMatchesSelector]); - -module.exports = browser; - -},{"./domObjects":33,"./isType":41,"./window":46}],33:[function(require,module,exports){ -var domObjects = {}; -var win = require('./window').window; + function warnOnce (method, message) { + var warned = false; -function blank() {} - -domObjects.document = win.document; -domObjects.DocumentFragment = win.DocumentFragment || blank; -domObjects.SVGElement = win.SVGElement || blank; -domObjects.SVGSVGElement = win.SVGSVGElement || blank; -domObjects.SVGElementInstance = win.SVGElementInstance || blank; -domObjects.HTMLElement = win.HTMLElement || win.Element; - -domObjects.Event = win.Event; -domObjects.Touch = win.Touch || blank; -domObjects.PointerEvent = win.PointerEvent || win.MSPointerEvent; - -module.exports = domObjects; - -},{"./window":46}],34:[function(require,module,exports){ -var win = require('./window'); -var browser = require('./browser'); -var isType = require('./isType'); -var domObjects = require('./domObjects'); - -var domUtils = { - nodeContains: function (parent, child) { - while (child) { - if (child === parent) { - return true; - } - - child = child.parentNode; - } - - return false; - }, - - closest: function (element, selector) { - while (isType.isElement(element)) { - if (domUtils.matchesSelector(element, selector)) { - return element; - } - - element = domUtils.parentNode(element); - } - - return null; - }, - - parentNode: function (node) { - var parent = node.parentNode; - - if (isType.isDocFrag(parent)) { - // skip past #shado-root fragments - while ((parent = parent.host) && isType.isDocFrag(parent)) { - continue; - } + return function () { + if (!warned) { + window.console.warn(message); + warned = true; + } - return parent; + return method.apply(this, arguments); + }; } - return parent; - }, - - // taken from http://tanalin.com/en/blog/2012/12/matches-selector-ie8/ and modified - matchesSelectorPolyfill: browser.useMatchesSelectorPolyfill ? function (element, selector, elems) { - elems = elems || element.parentNode.querySelectorAll(selector); + Interactable.prototype.snap = warnOnce(Interactable.prototype.snap, + 'Interactable#snap is deprecated. See the new documentation for snapping at http://interactjs.io/docs/snapping'); + Interactable.prototype.restrict = warnOnce(Interactable.prototype.restrict, + 'Interactable#restrict is deprecated. See the new documentation for resticting at http://interactjs.io/docs/restriction'); + Interactable.prototype.inertia = warnOnce(Interactable.prototype.inertia, + 'Interactable#inertia is deprecated. See the new documentation for inertia at http://interactjs.io/docs/inertia'); + Interactable.prototype.autoScroll = warnOnce(Interactable.prototype.autoScroll, + 'Interactable#autoScroll is deprecated. See the new documentation for autoScroll at http://interactjs.io/docs/#autoscroll'); + Interactable.prototype.squareResize = warnOnce(Interactable.prototype.squareResize, + 'Interactable#squareResize is deprecated. See http://interactjs.io/docs/#resize-square'); + + Interactable.prototype.accept = warnOnce(Interactable.prototype.accept, + 'Interactable#accept is deprecated. use Interactable#dropzone({ accept: target }) instead'); + Interactable.prototype.dropChecker = warnOnce(Interactable.prototype.dropChecker, + 'Interactable#dropChecker is deprecated. use Interactable#dropzone({ dropChecker: checkerFunction }) instead'); + Interactable.prototype.context = warnOnce(Interactable.prototype.context, + 'Interactable#context as a method is deprecated. It will soon be a DOM Node instead'); + + /*\ + * interact.isSet + [ method ] + * + * Check if an element has been set + - element (Element) The Element being searched for + = (boolean) Indicates if the element or CSS selector was previously passed to interact + \*/ + interact.isSet = function(element, options) { + return interactables.indexOfElement(element, options && options.context) !== -1; + }; - for (var i = 0, len = elems.length; i < len; i++) { - if (elems[i] === element) { - return true; - } - } + /*\ + * interact.on + [ method ] + * + * Adds a global listener for an InteractEvent or adds a DOM event to + * `document` + * + - type (string | array | object) The types of events to listen for + - listener (function) The function to be called on the given event(s) + - useCapture (boolean) #optional useCapture flag for addEventListener + = (object) interact + \*/ + interact.on = function (type, listener, useCapture) { + if (isString(type) && type.search(' ') !== -1) { + type = type.trim().split(/ +/); + } - return false; - } : null, + if (isArray(type)) { + for (var i = 0; i < type.length; i++) { + interact.on(type[i], listener, useCapture); + } - matchesSelector: function (element, selector, nodeList) { - if (browser.useMatchesSelectorPolyfill) { - return domUtils.matchesSelectorPolyfill(element, selector, nodeList); - } + return interact; + } - // remove /deep/ from selectors if shadowDOM polyfill is used - if (win.window !== win.realWindow) { - selector = selector.replace(/\/deep\//g, ' '); - } + if (isObject(type)) { + for (var prop in type) { + interact.on(prop, type[prop], listener); + } - return element[browser.prefixedMatchesSelector](selector); - }, - - // Test for the element that's "above" all other qualifiers - indexOfDeepestElement: function (elements) { - var deepestZoneParents = []; - var dropzoneParents = []; - var dropzone = undefined; - var deepestZone = elements[0]; - var index = deepestZone ? 0 : -1; - var parent = undefined; - var child = undefined; - var i = undefined; - var n = undefined; - - for (i = 1; i < elements.length; i++) { - dropzone = elements[i]; - - // an element might belong to multiple selector dropzones - if (!dropzone || dropzone === deepestZone) { - continue; - } - - if (!deepestZone) { - deepestZone = dropzone; - index = i; - continue; - } - - // check if the deepest or current are document.documentElement or document.rootElement - // - if the current dropzone is, do nothing and continue - if (dropzone.parentNode === dropzone.ownerDocument) { - continue; - } - // - if deepest is, update with the current dropzone and continue to next - else if (deepestZone.parentNode === dropzone.ownerDocument) { - deepestZone = dropzone; - index = i; - continue; + return interact; } - if (!deepestZoneParents.length) { - parent = deepestZone; - while (parent.parentNode && parent.parentNode !== parent.ownerDocument) { - deepestZoneParents.unshift(parent); - parent = parent.parentNode; + // if it is an InteractEvent type, add listener to globalEvents + if (contains(eventTypes, type)) { + // if this type of event was never bound + if (!globalEvents[type]) { + globalEvents[type] = [listener]; + } + else { + globalEvents[type].push(listener); + } } - } - - // if this element is an svg element and the current deepest is - // an HTMLElement - if (deepestZone instanceof domObjects.HTMLElement && dropzone instanceof domObjects.SVGElement && !(dropzone instanceof domObjects.SVGSVGElement)) { - - if (dropzone === deepestZone.parentNode) { - continue; + // If non InteractEvent type, addEventListener to document + else { + events.add(document, type, listener, useCapture); } - parent = dropzone.ownerSVGElement; - } else { - parent = dropzone; - } - - dropzoneParents = []; - - while (parent.parentNode !== parent.ownerDocument) { - dropzoneParents.unshift(parent); - parent = parent.parentNode; - } - - n = 0; - - // get (position of last common ancestor) + 1 - while (dropzoneParents[n] && dropzoneParents[n] === deepestZoneParents[n]) { - n++; - } - - var parents = [dropzoneParents[n - 1], dropzoneParents[n], deepestZoneParents[n]]; - - child = parents[0].lastChild; - - while (child) { - if (child === parents[1]) { - deepestZone = dropzone; - index = i; - deepestZoneParents = []; + return interact; + }; - break; - } else if (child === parents[2]) { - break; + /*\ + * interact.off + [ method ] + * + * Removes a global InteractEvent listener or DOM event from `document` + * + - type (string | array | object) The types of events that were listened for + - listener (function) The listener function to be removed + - useCapture (boolean) #optional useCapture flag for removeEventListener + = (object) interact + \*/ + interact.off = function (type, listener, useCapture) { + if (isString(type) && type.search(' ') !== -1) { + type = type.trim().split(/ +/); } - child = child.previousSibling; - } - } - - return index; - }, + if (isArray(type)) { + for (var i = 0; i < type.length; i++) { + interact.off(type[i], listener, useCapture); + } - matchesUpTo: function (element, selector, limit) { - while (isType.isElement(element)) { - if (domUtils.matchesSelector(element, selector)) { - return true; - } + return interact; + } - element = domUtils.parentNode(element); + if (isObject(type)) { + for (var prop in type) { + interact.off(prop, type[prop], listener); + } - if (element === limit) { - return domUtils.matchesSelector(element, selector); - } - } + return interact; + } - return false; - }, + if (!contains(eventTypes, type)) { + events.remove(document, type, listener, useCapture); + } + else { + var index; - getActualElement: function (element) { - return element instanceof domObjects.SVGElementInstance ? element.correspondingUseElement : element; - }, + if (type in globalEvents + && (index = indexOf(globalEvents[type], listener)) !== -1) { + globalEvents[type].splice(index, 1); + } + } - getScrollXY: function (relevantWindow) { - relevantWindow = relevantWindow || win.window; - return { - x: relevantWindow.scrollX || relevantWindow.document.documentElement.scrollLeft, - y: relevantWindow.scrollY || relevantWindow.document.documentElement.scrollTop - }; - }, - - getElementClientRect: function (element) { - var clientRect = element instanceof domObjects.SVGElement ? element.getBoundingClientRect() : element.getClientRects()[0]; - - return clientRect && { - left: clientRect.left, - right: clientRect.right, - top: clientRect.top, - bottom: clientRect.bottom, - width: clientRect.width || clientRect.right - clientRect.left, - height: clientRect.height || clientRect.bottom - clientRect.top + return interact; }; - }, - - getElementRect: function (element) { - var clientRect = domUtils.getElementClientRect(element); - - if (!browser.isIOS7 && clientRect) { - var _scroll = domUtils.getScrollXY(win.getWindow(element)); - - clientRect.left += _scroll.x; - clientRect.right += _scroll.x; - clientRect.top += _scroll.y; - clientRect.bottom += _scroll.y; - } - return clientRect; - }, + /*\ + * interact.enableDragging + [ method ] + * + * Deprecated. + * + * Returns or sets whether dragging is enabled for any Interactables + * + - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables + = (boolean | object) The current setting or interact + \*/ + interact.enableDragging = warnOnce(function (newValue) { + if (newValue !== null && newValue !== undefined) { + actionIsEnabled.drag = newValue; + + return interact; + } + return actionIsEnabled.drag; + }, 'interact.enableDragging is deprecated and will soon be removed.'); + + /*\ + * interact.enableResizing + [ method ] + * + * Deprecated. + * + * Returns or sets whether resizing is enabled for any Interactables + * + - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables + = (boolean | object) The current setting or interact + \*/ + interact.enableResizing = warnOnce(function (newValue) { + if (newValue !== null && newValue !== undefined) { + actionIsEnabled.resize = newValue; + + return interact; + } + return actionIsEnabled.resize; + }, 'interact.enableResizing is deprecated and will soon be removed.'); + + /*\ + * interact.enableGesturing + [ method ] + * + * Deprecated. + * + * Returns or sets whether gesturing is enabled for any Interactables + * + - newValue (boolean) #optional `true` to allow the action; `false` to disable action for all Interactables + = (boolean | object) The current setting or interact + \*/ + interact.enableGesturing = warnOnce(function (newValue) { + if (newValue !== null && newValue !== undefined) { + actionIsEnabled.gesture = newValue; + + return interact; + } + return actionIsEnabled.gesture; + }, 'interact.enableGesturing is deprecated and will soon be removed.'); - getPath: function (element) { - var path = []; + interact.eventTypes = eventTypes; - while (element) { - path.push(element); - element = domUtils.parentNode(element); - } + /*\ + * interact.debug + [ method ] + * + * Returns debugging data + = (object) An object with properties that outline the current state and expose internal functions and variables + \*/ + interact.debug = function () { + var interaction = interactions[0] || new Interaction(); - return path; - } -}; - -module.exports = domUtils; - -},{"./browser":32,"./domObjects":33,"./isType":41,"./window":46}],35:[function(require,module,exports){ -var arr = require('./arr'); -var isType = require('./isType'); -var domUtils = require('./domUtils'); -var indexOf = arr.indexOf; -var contains = arr.contains; -var getWindow = require('./window').getWindow; -var pExtend = require('./pointerExtend'); - -var useAttachEvent = 'attachEvent' in window && !('addEventListener' in window); -var addEvent = useAttachEvent ? 'attachEvent' : 'addEventListener'; -var removeEvent = useAttachEvent ? 'detachEvent' : 'removeEventListener'; -var on = useAttachEvent ? 'on' : ''; - -var elements = []; -var targets = []; -var attachedListeners = []; - -// { -// type: { -// selectors: ['selector', ...], -// contexts : [document, ...], -// listeners: [[listener, useCapture], ...] -// } -// } -var delegatedEvents = {}; - -var documents = []; - -function add(element, type, listener, useCapture) { - var elementIndex = indexOf(elements, element); - var target = targets[elementIndex]; - - if (!target) { - target = { - events: {}, - typeCount: 0 + return { + interactions : interactions, + target : interaction.target, + dragging : interaction.dragging, + resizing : interaction.resizing, + gesturing : interaction.gesturing, + prepared : interaction.prepared, + matches : interaction.matches, + matchElements : interaction.matchElements, + + prevCoords : interaction.prevCoords, + startCoords : interaction.startCoords, + + pointerIds : interaction.pointerIds, + pointers : interaction.pointers, + addPointer : listeners.addPointer, + removePointer : listeners.removePointer, + recordPointer : listeners.recordPointer, + + snap : interaction.snapStatus, + restrict : interaction.restrictStatus, + inertia : interaction.inertiaStatus, + + downTime : interaction.downTimes[0], + downEvent : interaction.downEvent, + downPointer : interaction.downPointer, + prevEvent : interaction.prevEvent, + + Interactable : Interactable, + interactables : interactables, + pointerIsDown : interaction.pointerIsDown, + defaultOptions : defaultOptions, + defaultActionChecker : defaultActionChecker, + + actionCursors : actionCursors, + dragMove : listeners.dragMove, + resizeMove : listeners.resizeMove, + gestureMove : listeners.gestureMove, + pointerUp : listeners.pointerUp, + pointerDown : listeners.pointerDown, + pointerMove : listeners.pointerMove, + pointerHover : listeners.pointerHover, + + eventTypes : eventTypes, + + events : events, + globalEvents : globalEvents, + delegatedEvents : delegatedEvents, + + prefixedPropREs : prefixedPropREs + }; }; - elementIndex = elements.push(element) - 1; - targets.push(target); - - attachedListeners.push(useAttachEvent ? { - supplied: [], - wrapped: [], - useCount: [] - } : null); - } - - if (!target.events[type]) { - target.events[type] = []; - target.typeCount++; - } - - if (!contains(target.events[type], listener)) { - var ret = undefined; - - if (useAttachEvent) { - var _attachedListeners$elementIndex = attachedListeners[elementIndex]; - var supplied = _attachedListeners$elementIndex.supplied; - var wrapped = _attachedListeners$elementIndex.wrapped; - var useCount = _attachedListeners$elementIndex.useCount; - - var listenerIndex = indexOf(supplied, listener); - - var wrappedListener = wrapped[listenerIndex] || function (event) { - if (!event.immediatePropagationStopped) { - event.target = event.srcElement; - event.currentTarget = element; - - event.preventDefault = event.preventDefault || preventDef; - event.stopPropagation = event.stopPropagation || stopProp; - event.stopImmediatePropagation = event.stopImmediatePropagation || stopImmProp; - - if (/mouse|click/.test(event.type)) { - event.pageX = event.clientX + getWindow(element).document.documentElement.scrollLeft; - event.pageY = event.clientY + getWindow(element).document.documentElement.scrollTop; - } - - listener(event); - } - }; - - ret = element[addEvent](on + type, wrappedListener, !!useCapture); - - if (listenerIndex === -1) { - supplied.push(listener); - wrapped.push(wrappedListener); - useCount.push(1); - } else { - useCount[listenerIndex]++; - } - } else { - ret = element[addEvent](type, listener, !!useCapture); - } - target.events[type].push(listener); - - return ret; - } -} - -function remove(element, type, listener, useCapture) { - var elementIndex = indexOf(elements, element); - var target = targets[elementIndex]; - - if (!target || !target.events) { - return; - } - - var wrappedListener = listener; - var listeners = undefined; - var listenerIndex = undefined; - - if (useAttachEvent) { - listeners = attachedListeners[elementIndex]; - listenerIndex = indexOf(listeners.supplied, listener); - wrappedListener = listeners.wrapped[listenerIndex]; - } - - if (type === 'all') { - for (type in target.events) { - if (target.events.hasOwnProperty(type)) { - remove(element, type, 'all'); - } - } - return; - } - - if (target.events[type]) { - var len = target.events[type].length; - - if (listener === 'all') { - for (var i = 0; i < len; i++) { - remove(element, type, target.events[type][i], !!useCapture); - } - return; - } else { - for (var i = 0; i < len; i++) { - if (target.events[type][i] === listener) { - element[removeEvent](on + type, wrappedListener, !!useCapture); - target.events[type].splice(i, 1); - - if (useAttachEvent && listeners) { - listeners.useCount[listenerIndex]--; - if (listeners.useCount[listenerIndex] === 0) { - listeners.supplied.splice(listenerIndex, 1); - listeners.wrapped.splice(listenerIndex, 1); - listeners.useCount.splice(listenerIndex, 1); - } - } - - break; + // expose the functions used to calculate multi-touch properties + interact.getPointerAverage = pointerAverage; + interact.getTouchBBox = touchBBox; + interact.getTouchDistance = touchDistance; + interact.getTouchAngle = touchAngle; + + interact.getElementRect = getElementRect; + interact.getElementClientRect = getElementClientRect; + interact.matchesSelector = matchesSelector; + interact.closest = closest; + + /*\ + * interact.margin + [ method ] + * + * Deprecated. Use `interact(target).resizable({ margin: number });` instead. + * Returns or sets the margin for autocheck resizing used in + * @Interactable.getAction. That is the distance from the bottom and right + * edges of an element clicking in which will start resizing + * + - newValue (number) #optional + = (number | interact) The current margin value or interact + \*/ + interact.margin = warnOnce(function (newvalue) { + if (isNumber(newvalue)) { + margin = newvalue; + + return interact; } - } - } - - if (target.events[type] && target.events[type].length === 0) { - target.events[type] = null; - target.typeCount--; - } - } - - if (!target.typeCount) { - targets.splice(elementIndex, 1); - elements.splice(elementIndex, 1); - attachedListeners.splice(elementIndex, 1); - } -} - -function addDelegate(selector, context, type, listener, useCapture) { - if (!delegatedEvents[type]) { - delegatedEvents[type] = { - selectors: [], - contexts: [], - listeners: [] + return margin; + }, + 'interact.margin is deprecated. Use interact(target).resizable({ margin: number }); instead.') ; + + /*\ + * interact.supportsTouch + [ method ] + * + = (boolean) Whether or not the browser supports touch input + \*/ + interact.supportsTouch = function () { + return supportsTouch; }; - // add delegate listener functions - for (var i = 0; i < documents.length; i++) { - add(documents[i], type, delegateListener); - add(documents[i], type, delegateUseCapture, true); - } - } - - var delegated = delegatedEvents[type]; - var index = undefined; - - for (index = delegated.selectors.length - 1; index >= 0; index--) { - if (delegated.selectors[index] === selector && delegated.contexts[index] === context) { - break; - } - } - - if (index === -1) { - index = delegated.selectors.length; - - delegated.selectors.push(selector); - delegated.contexts.push(context); - delegated.listeners.push([]); - } - - // keep listener and useCapture flag - delegated.listeners[index].push([listener, useCapture]); -} - -function removeDelegate(selector, context, type, listener, useCapture) { - var delegated = delegatedEvents[type]; - var matchFound = false; - var index = undefined; - - if (!delegated) { - return; - } - - // count from last index of delegated to 0 - for (index = delegated.selectors.length - 1; index >= 0; index--) { - // look for matching selector and context Node - if (delegated.selectors[index] === selector && delegated.contexts[index] === context) { - - var listeners = delegated.listeners[index]; - - // each item of the listeners array is an array: [function, useCaptureFlag] - for (var i = listeners.length - 1; i >= 0; i--) { - var fn = listeners[i][0]; - var useCap = listeners[i][1]; - - // check if the listener functions and useCapture flags match - if (fn === listener && useCap === useCapture) { - // remove the listener from the array of listeners - listeners.splice(i, 1); - - // if all listeners for this interactable have been removed - // remove the interactable from the delegated arrays - if (!listeners.length) { - delegated.selectors.splice(index, 1); - delegated.contexts.splice(index, 1); - delegated.listeners.splice(index, 1); - - // remove delegate function from context - remove(context, type, delegateListener); - remove(context, type, delegateUseCapture, true); - - // remove the arrays if they are empty - if (!delegated.selectors.length) { - delegatedEvents[type] = null; - } - } + /*\ + * interact.supportsPointerEvent + [ method ] + * + = (boolean) Whether or not the browser supports PointerEvents + \*/ + interact.supportsPointerEvent = function () { + return supportsPointerEvent; + }; - // only remove one listener - matchFound = true; - break; + /*\ + * interact.stop + [ method ] + * + * Cancels all interactions (end events are not fired) + * + - event (Event) An event on which to call preventDefault() + = (object) interact + \*/ + interact.stop = function (event) { + for (var i = interactions.length - 1; i >= 0; i--) { + interactions[i].stop(event); } - } - - if (matchFound) { - break; - } - } - } -} - -// bound to the interactable context when a DOM event -// listener is added to a selector interactable -function delegateListener(event, useCapture) { - var fakeEvent = {}; - var delegated = delegatedEvents[event.type]; - var eventTarget = domUtils.getActualElement(event.path ? event.path[0] : event.target); - var element = eventTarget; - - useCapture = useCapture ? true : false; - - // duplicate the event so that currentTarget can be changed - pExtend(fakeEvent, event); - fakeEvent.originalEvent = event; - fakeEvent.preventDefault = preventOriginalDefault; - - // climb up document tree looking for selector matches - while (isType.isElement(element)) { - for (var i = 0; i < delegated.selectors.length; i++) { - var selector = delegated.selectors[i]; - var context = delegated.contexts[i]; - - if (domUtils.matchesSelector(element, selector) && domUtils.nodeContains(context, eventTarget) && domUtils.nodeContains(context, element)) { - - var listeners = delegated.listeners[i]; - - fakeEvent.currentTarget = element; + return interact; + }; - for (var j = 0; j < listeners.length; j++) { - if (listeners[j][1] === useCapture) { - listeners[j][0](fakeEvent); - } + /*\ + * interact.dynamicDrop + [ method ] + * + * Returns or sets whether the dimensions of dropzone elements are + * calculated on every dragmove or only on dragstart for the default + * dropChecker + * + - newValue (boolean) #optional True to check on each move. False to check only before start + = (boolean | interact) The current setting or interact + \*/ + interact.dynamicDrop = function (newValue) { + if (isBool(newValue)) { + //if (dragging && dynamicDrop !== newValue && !newValue) { + //calcRects(dropzones); + //} + + dynamicDrop = newValue; + + return interact; } - } - } - - element = domUtils.parentNode(element); - } -} - -function delegateUseCapture(event) { - return delegateListener.call(this, event, true); -} - -function preventDef() { - this.returnValue = false; -} - -function preventOriginalDefault() { - this.originalEvent.preventDefault(); -} - -function stopProp() { - this.cancelBubble = true; -} - -function stopImmProp() { - this.cancelBubble = true; - this.immediatePropagationStopped = true; -} - -module.exports = { - add: add, - remove: remove, - - addDelegate: addDelegate, - removeDelegate: removeDelegate, - - delegateListener: delegateListener, - delegateUseCapture: delegateUseCapture, - delegatedEvents: delegatedEvents, - documents: documents, - - useAttachEvent: useAttachEvent, - - _elements: elements, - _targets: targets, - _attachedListeners: attachedListeners -}; - -},{"./arr":31,"./domUtils":34,"./isType":41,"./pointerExtend":43,"./window":46}],36:[function(require,module,exports){ -module.exports = function extend(dest, source) { - for (var prop in source) { - dest[prop] = source[prop]; - } - return dest; -}; - -},{}],37:[function(require,module,exports){ -var _require = require('./domUtils'); - -var closest = _require.closest; -var parentNode = _require.parentNode; -var getElementRect = _require.getElementRect; - -var _require2 = require('./isType'); - -var isElement = _require2.isElement; -var isFunction = _require2.isFunction; -var trySelector = _require2.trySelector; + return dynamicDrop; + }; -module.exports = function (target, element, action) { - var origin = action ? target.options[action].origin : target.options.origin; + /*\ + * interact.pointerMoveTolerance + [ method ] + * Returns or sets the distance the pointer must be moved before an action + * sequence occurs. This also affects tolerance for tap events. + * + - newValue (number) #optional The movement from the start position must be greater than this value + = (number | Interactable) The current setting or interact + \*/ + interact.pointerMoveTolerance = function (newValue) { + if (isNumber(newValue)) { + pointerMoveTolerance = newValue; + + return this; + } - if (origin === 'parent') { - origin = parentNode(element); - } else if (origin === 'self') { - origin = target.getRect(element); - } else if (trySelector(origin)) { - origin = closest(element, origin) || { x: 0, y: 0 }; - } + return pointerMoveTolerance; + }; - if (isFunction(origin)) { - origin = origin(target && element); - } + /*\ + * interact.maxInteractions + [ method ] + ** + * Returns or sets the maximum number of concurrent interactions allowed. + * By default only 1 interaction is allowed at a time (for backwards + * compatibility). To allow multiple interactions on the same Interactables + * and elements, you need to enable it in the draggable, resizable and + * gesturable `'max'` and `'maxPerElement'` options. + ** + - newValue (number) #optional Any number. newValue <= 0 means no interactions. + \*/ + interact.maxInteractions = function (newValue) { + if (isNumber(newValue)) { + maxInteractions = newValue; + + return this; + } - if (isElement(origin)) { - origin = getElementRect(origin); - } + return maxInteractions; + }; - origin.x = 'x' in origin ? origin.x : origin.left; - origin.y = 'y' in origin ? origin.y : origin.top; + interact.createSnapGrid = function (grid) { + return function (x, y) { + var offsetX = 0, + offsetY = 0; - return origin; -}; + if (isObject(grid.offset)) { + offsetX = grid.offset.x; + offsetY = grid.offset.y; + } -},{"./domUtils":34,"./isType":41}],38:[function(require,module,exports){ -module.exports = function (x, y) { - return Math.sqrt(x * x + y * y); -}; + var gridx = Math.round((x - offsetX) / grid.x), + gridy = Math.round((y - offsetY) / grid.y), -},{}],39:[function(require,module,exports){ -var extend = require('./extend'); -var win = require('./window'); + newX = gridx * grid.x + offsetX, + newY = gridy * grid.y + offsetY; -var utils = { - warnOnce: function (method, message) { - var warned = false; - - return function () { - if (!warned) { - win.window.console.warn(message); - warned = true; - } - - return method.apply(this, arguments); - }; - }, - - // http://stackoverflow.com/a/5634528/2280888 - _getQBezierValue: function (t, p1, p2, p3) { - var iT = 1 - t; - return iT * iT * p1 + 2 * iT * t * p2 + t * t * p3; - }, - - getQuadraticCurvePoint: function (startX, startY, cpX, cpY, endX, endY, position) { - return { - x: utils._getQBezierValue(position, startX, cpX, endX), - y: utils._getQBezierValue(position, startY, cpY, endY) + return { + x: newX, + y: newY, + range: grid.range + }; + }; }; - }, - - // http://gizma.com/easing/ - easeOutQuad: function (t, b, c, d) { - t /= d; - return -c * t * (t - 2) + b; - }, - copyAction: function (dest, src) { - dest.name = src.name; - dest.axis = src.axis; - dest.edges = src.edges; - - return dest; - }, - - getStringOptionResult: function (value, interactable, element) { - if (!utils.isString(value)) { - return null; - } - - if (value === 'parent') { - value = utils.parentNode(element); - } else if (value === 'self') { - value = interactable.getRect(element); - } else { - value = utils.closest(element, value); + function endAllInteractions (event) { + for (var i = 0; i < interactions.length; i++) { + interactions[i].pointerEnd(event, event); + } } - return value; - }, - - extend: extend, - hypot: require('./hypot'), - getOriginXY: require('./getOriginXY') -}; + function listenToDocument (doc) { + if (contains(documents, doc)) { return; } -extend(utils, require('./arr')); -extend(utils, require('./isType')); -extend(utils, require('./domUtils')); -extend(utils, require('./pointerUtils')); + var win = doc.defaultView || doc.parentWindow; -module.exports = utils; - -},{"./arr":31,"./domUtils":34,"./extend":36,"./getOriginXY":37,"./hypot":38,"./isType":41,"./pointerUtils":44,"./window":46}],40:[function(require,module,exports){ -var scope = require('../scope'); -var utils = require('./index'); -var browser = require('./browser'); - -var finder = { - methodOrder: ['simulationResume', 'mouse', 'hasPointer', 'idle'], - - search: function (pointer, eventType, eventTarget) { - var mouseEvent = /mouse/i.test(pointer.pointerType || eventType) - // MSPointerEvent.MSPOINTER_TYPE_MOUSE - || pointer.pointerType === 4; - var pointerId = utils.getPointerId(pointer); - var details = { pointer: pointer, pointerId: pointerId, mouseEvent: mouseEvent, eventType: eventType, eventTarget: eventTarget }; + // add delegate event listener + for (var eventType in delegatedEvents) { + events.add(doc, eventType, delegateListener); + events.add(doc, eventType, delegateUseCapture, true); + } - for (var _iterator = finder.methodOrder, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + if (PointerEvent) { + if (PointerEvent === win.MSPointerEvent) { + pEventTypes = { + up: 'MSPointerUp', down: 'MSPointerDown', over: 'mouseover', + out: 'mouseout', move: 'MSPointerMove', cancel: 'MSPointerCancel' }; + } + else { + pEventTypes = { + up: 'pointerup', down: 'pointerdown', over: 'pointerover', + out: 'pointerout', move: 'pointermove', cancel: 'pointercancel' }; + } - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + events.add(doc, pEventTypes.down , listeners.selectorDown ); + events.add(doc, pEventTypes.move , listeners.pointerMove ); + events.add(doc, pEventTypes.over , listeners.pointerOver ); + events.add(doc, pEventTypes.out , listeners.pointerOut ); + events.add(doc, pEventTypes.up , listeners.pointerUp ); + events.add(doc, pEventTypes.cancel, listeners.pointerCancel); - var method = _ref; + // autoscroll + events.add(doc, pEventTypes.move, listeners.autoScrollMove); + } + else { + events.add(doc, 'mousedown', listeners.selectorDown); + events.add(doc, 'mousemove', listeners.pointerMove ); + events.add(doc, 'mouseup' , listeners.pointerUp ); + events.add(doc, 'mouseover', listeners.pointerOver ); + events.add(doc, 'mouseout' , listeners.pointerOut ); + + events.add(doc, 'touchstart' , listeners.selectorDown ); + events.add(doc, 'touchmove' , listeners.pointerMove ); + events.add(doc, 'touchend' , listeners.pointerUp ); + events.add(doc, 'touchcancel', listeners.pointerCancel); + + // autoscroll + events.add(doc, 'mousemove', listeners.autoScrollMove); + events.add(doc, 'touchmove', listeners.autoScrollMove); + } - var interaction = finder[method](details); + events.add(win, 'blur', endAllInteractions); - if (interaction) { - return interaction; - } - } - }, + try { + if (win.frameElement) { + var parentDoc = win.frameElement.ownerDocument, + parentWindow = parentDoc.defaultView; - // try to resume simulation with a new pointer - simulationResume: function (_ref7) { - var mouseEvent = _ref7.mouseEvent; - var eventType = _ref7.eventType; - var eventTarget = _ref7.eventTarget; + events.add(parentDoc , 'mouseup' , listeners.pointerEnd); + events.add(parentDoc , 'touchend' , listeners.pointerEnd); + events.add(parentDoc , 'touchcancel' , listeners.pointerEnd); + events.add(parentDoc , 'pointerup' , listeners.pointerEnd); + events.add(parentDoc , 'MSPointerUp' , listeners.pointerEnd); + events.add(parentWindow, 'blur' , endAllInteractions ); + } + } + catch (error) { + interact.windowParentError = error; + } - if (!/down|start/i.test(eventType)) { - return null; - } + // prevent native HTML5 drag on interact.js target elements + events.add(doc, 'dragstart', function (event) { + for (var i = 0; i < interactions.length; i++) { + var interaction = interactions[i]; - for (var _iterator2 = scope.interactions, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; + if (interaction.element + && (interaction.element === event.target + || nodeContains(interaction.element, event.target))) { - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } + interaction.checkAndPreventDefault(event, interaction.target, interaction.element); + return; + } + } + }); - var interaction = _ref2; + if (events.useAttachEvent) { + // For IE's lack of Event#preventDefault + events.add(doc, 'selectstart', function (event) { + var interaction = interactions[0]; - var element = eventTarget; + if (interaction.currentAction()) { + interaction.checkAndPreventDefault(event); + } + }); - if (interaction.simulation && interaction.simulation.allowResume && interaction.mouse === mouseEvent) { - while (element) { - // if the element is the interaction element - if (element === interaction.element) { - return interaction; - } - element = utils.parentNode(element); + // For IE's bad dblclick event sequence + events.add(doc, 'dblclick', doOnInteractions('ie8Dblclick')); } - } - } - return null; - }, - - // if it's a mouse interaction - mouse: function (_ref8) { - var pointerId = _ref8.pointerId; - var mouseEvent = _ref8.mouseEvent; - var eventType = _ref8.eventType; - - if (!mouseEvent && (browser.supportsTouch || browser.supportsPointerEvent)) { - return null; + documents.push(doc); } - var firstNonActive = undefined; - - for (var _iterator3 = scope.interactions, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; - - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; - } - - var interaction = _ref3; - - if (interaction.mouse) { - // if it's a down event, skip interactions with running simulations - if (interaction.simulation && !utils.contains(interaction.pointerIds, pointerId)) { - continue; - } + listenToDocument(document); - // if the interaction is active, return it immediately - if (interaction.interacting()) { - return interaction; + function indexOf (array, target) { + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === target) { + return i; + } } - // otherwise save it and look for another active interaction - else if (!firstNonActive) { - firstNonActive = interaction; - } - } - } - // if no active mouse interaction was found use the first inactive mouse - // interaction - if (firstNonActive) { - return firstNonActive; + return -1; } - // Find any interaction specifically for mouse. - // ignore the interaction if the eventType is a mousedown, and a simulation - // is active - for (var _iterator4 = scope.interactions, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref4; - - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref4 = _iterator4[_i4++]; - } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref4 = _i4.value; - } - - var interaction = _ref4; - - if (interaction.mouse && !(/down/.test(eventType) && interaction.simulation)) { - return interaction; - } + function contains (array, target) { + return indexOf(array, target) !== -1; } - return null; - }, - - // get interaction that has this pointer - hasPointer: function (_ref9) { - var pointerId = _ref9.pointerId; - - for (var _iterator5 = scope.interactions, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref5; - - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref5 = _iterator5[_i5++]; - } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref5 = _i5.value; - } - - var interaction = _ref5; - - if (utils.contains(interaction.pointerIds, pointerId)) { - return interaction; - } - } - }, - - // get first idle interaction - idle: function (_ref10) { - var mouseEvent = _ref10.mouseEvent; - - for (var _iterator6 = scope.interactions, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref6; - - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref6 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref6 = _i6.value; - } - - var interaction = _ref6; - - // if there's already a pointer held down - if (interaction.pointerIds.length === 1) { - var target = interaction.target; - // don't add this pointer if there is a target interactable and it - // isn't gesturable - if (target && !target.options.gesture.enabled) { - continue; + function matchesSelector (element, selector, nodeList) { + if (ie8MatchesSelector) { + return ie8MatchesSelector(element, selector, nodeList); } - } - // maximum of 2 pointers per interaction - else if (interaction.pointerIds.length >= 2) { - continue; - } - - if (!interaction.interacting() && !(!mouseEvent && interaction.mouse)) { - return interaction; - } - } - return null; - } -}; - -module.exports = finder; - -},{"../scope":29,"./browser":32,"./index":39}],41:[function(require,module,exports){ -var win = require('./window'); -var isWindow = require('./isWindow'); -var domObjects = require('./domObjects'); + // remove /deep/ from selectors if shadowDOM polyfill is used + if (window !== realWindow) { + selector = selector.replace(/\/deep\//g, ' '); + } -var isType = { - isElement: function (o) { - if (!o || typeof o !== 'object') { - return false; + return element[prefixedMatchesSelector](selector); } - var _window = win.getWindow(o) || win.window; - - return (/object|function/.test(typeof _window.Element) ? o instanceof _window.Element //DOM2 - : o.nodeType === 1 && typeof o.nodeName === 'string' - ); - }, - - isArray: null, - - isWindow: function (thing) { - return thing === win.window || isWindow(thing); - }, - - isDocFrag: function (thing) { - return isType.isObject(thing) && thing.nodeType === 11; - }, - - isObject: function (thing) { - return !!thing && typeof thing === 'object'; - }, - - isFunction: function (thing) { - return typeof thing === 'function'; - }, - - isNumber: function (thing) { - return typeof thing === 'number'; - }, - - isBool: function (thing) { - return typeof thing === 'boolean'; - }, - - isString: function (thing) { - return typeof thing === 'string'; - }, + function matchesUpTo (element, selector, limit) { + while (isElement(element)) { + if (matchesSelector(element, selector)) { + return true; + } - trySelector: function (value) { - if (!isType.isString(value)) { - return false; - } + element = parentElement(element); - // an exception will be raised if it is invalid - domObjects.document.querySelector(value); - return true; - } -}; - -isType.isArray = function (thing) { - return isType.isObject(thing) && typeof thing.length !== 'undefined' && isType.isFunction(thing.splice); -}; - -module.exports = isType; - -},{"./domObjects":33,"./isWindow":42,"./window":46}],42:[function(require,module,exports){ -module.exports = function (thing) { - return !!(thing && thing.Window) && thing instanceof thing.Window; -}; - -},{}],43:[function(require,module,exports){ -function pointerExtend(dest, source) { - for (var prop in source) { - var prefixedPropREs = module.exports.prefixedPropREs; - var deprecated = false; - - // skip deprecated prefixed properties - for (var vendor in prefixedPropREs) { - if (prop.indexOf(vendor) === 0 && prefixedPropREs[vendor].test(prop)) { - deprecated = true; - break; - } - } + if (element === limit) { + return matchesSelector(element, selector); + } + } - if (!deprecated) { - dest[prop] = source[prop]; - } - } - return dest; -} - -pointerExtend.prefixedPropREs = { - webkit: /(Movement[XY]|Radius[XY]|RotationAngle|Force)$/ -}; - -module.exports = pointerExtend; - -},{}],44:[function(require,module,exports){ -var hypot = require('./hypot'); -var browser = require('./browser'); -var dom = require('./domObjects'); -var isType = require('./isType'); -var pointerExtend = require('./pointerExtend'); - -var pointerUtils = { - copyCoords: function (dest, src) { - dest.page = dest.page || {}; - dest.page.x = src.page.x; - dest.page.y = src.page.y; - - dest.client = dest.client || {}; - dest.client.x = src.client.x; - dest.client.y = src.client.y; - - dest.timeStamp = src.timeStamp; - }, - - setCoordDeltas: function (targetObj, prev, cur) { - targetObj.page.x = cur.page.x - prev.page.x; - targetObj.page.y = cur.page.y - prev.page.y; - targetObj.client.x = cur.client.x - prev.client.x; - targetObj.client.y = cur.client.y - prev.client.y; - targetObj.timeStamp = cur.timeStamp - prev.timeStamp; - - // set pointer velocity - var dt = Math.max(targetObj.timeStamp / 1000, 0.001); - - targetObj.page.speed = hypot(targetObj.page.x, targetObj.page.y) / dt; - targetObj.page.vx = targetObj.page.x / dt; - targetObj.page.vy = targetObj.page.y / dt; - - targetObj.client.speed = hypot(targetObj.client.x, targetObj.page.y) / dt; - targetObj.client.vx = targetObj.client.x / dt; - targetObj.client.vy = targetObj.client.y / dt; - }, - - isNativePointer: function (pointer) { - return pointer instanceof dom.Event || pointer instanceof dom.Touch; - }, - - // Get specified X/Y coords for mouse or event.touches[0] - getXY: function (type, pointer, xy) { - xy = xy || {}; - type = type || 'page'; - - xy.x = pointer[type + 'X']; - xy.y = pointer[type + 'Y']; - - return xy; - }, - - getPageXY: function (pointer, page) { - page = page || {}; - - // Opera Mobile handles the viewport and scrolling oddly - if (browser.isOperaMobile && pointerUtils.isNativePointer(pointer)) { - pointerUtils.getXY('screen', pointer, page); - - page.x += window.scrollX; - page.y += window.scrollY; - } else { - pointerUtils.getXY('page', pointer, page); + return false; } - return page; - }, + // For IE8's lack of an Element#matchesSelector + // taken from http://tanalin.com/en/blog/2012/12/matches-selector-ie8/ and modified + if (!(prefixedMatchesSelector in Element.prototype) || !isFunction(Element.prototype[prefixedMatchesSelector])) { + ie8MatchesSelector = function (element, selector, elems) { + elems = elems || element.parentNode.querySelectorAll(selector); - getClientXY: function (pointer, client) { - client = client || {}; + for (var i = 0, len = elems.length; i < len; i++) { + if (elems[i] === element) { + return true; + } + } - if (browser.isOperaMobile && pointerUtils.isNativePointer(pointer)) { - // Opera Mobile handles the viewport and scrolling oddly - pointerUtils.getXY('screen', pointer, client); - } else { - pointerUtils.getXY('client', pointer, client); + return false; + }; } - return client; - }, - - getPointerId: function (pointer) { - return isType.isNumber(pointer.pointerId) ? pointer.pointerId : pointer.identifier; - }, - - setCoords: function (targetObj, pointers, timeStamp) { - var pointer = pointers.length > 1 ? pointerUtils.pointerAverage(pointers) : pointers[0]; - - var tmpXY = {}; - - pointerUtils.getPageXY(pointer, tmpXY); - targetObj.page.x = tmpXY.x; - targetObj.page.y = tmpXY.y; - - pointerUtils.getClientXY(pointer, tmpXY); - targetObj.client.x = tmpXY.x; - targetObj.client.y = tmpXY.y; - - targetObj.timeStamp = isType.isNumber(timeStamp) ? timeStamp : new Date().getTime(); - }, - - pointerExtend: pointerExtend, - - getTouchPair: function (event) { - var touches = []; + // requestAnimationFrame polyfill + (function() { + var lastTime = 0, + vendors = ['ms', 'moz', 'webkit', 'o']; - // array of touches is supplied - if (isType.isArray(event)) { - touches[0] = event[0]; - touches[1] = event[1]; - } - // an event - else { - if (event.type === 'touchend') { - if (event.touches.length === 1) { - touches[0] = event.touches[0]; - touches[1] = event.changedTouches[0]; - } else if (event.touches.length === 0) { - touches[0] = event.changedTouches[0]; - touches[1] = event.changedTouches[1]; - } - } else { - touches[0] = event.touches[0]; - touches[1] = event.touches[1]; + for(var x = 0; x < vendors.length && !realWindow.requestAnimationFrame; ++x) { + reqFrame = realWindow[vendors[x]+'RequestAnimationFrame']; + cancelFrame = realWindow[vendors[x]+'CancelAnimationFrame'] || realWindow[vendors[x]+'CancelRequestAnimationFrame']; } - } - - return touches; - }, - - pointerAverage: function (pointers) { - var average = { - pageX: 0, - pageY: 0, - clientX: 0, - clientY: 0, - screenX: 0, - screenY: 0 - }; - for (var _iterator = pointers, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + if (!reqFrame) { + reqFrame = function(callback) { + var currTime = new Date().getTime(), + timeToCall = Math.max(0, 16 - (currTime - lastTime)), + id = setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + if (!cancelFrame) { + cancelFrame = function(id) { + clearTimeout(id); + }; + } + }()); - var pointer = _ref; + /* global exports: true, module, define */ - for (var prop in average) { - average[prop] += pointer[prop]; - } + // http://documentcloud.github.io/underscore/docs/underscore.html#section-11 + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = interact; + } + exports.interact = interact; } - for (var prop in average) { - average[prop] /= pointers.length; + // AMD + else if (typeof define === 'function' && define.amd) { + define('interact', function() { + return interact; + }); } - - return average; - }, - - touchBBox: function (event) { - if (!event.length && !(event.touches && event.touches.length > 1)) { - return; + else { + realWindow.interact = interact; } - var touches = pointerUtils.getTouchPair(event); - var minX = Math.min(touches[0].pageX, touches[1].pageX); - var minY = Math.min(touches[0].pageY, touches[1].pageY); - var maxX = Math.max(touches[0].pageX, touches[1].pageX); - var maxY = Math.max(touches[0].pageY, touches[1].pageY); - - return { - x: minX, - y: minY, - left: minX, - top: minY, - width: maxX - minX, - height: maxY - minY - }; - }, - - touchDistance: function (event, deltaSource) { - var sourceX = deltaSource + 'X'; - var sourceY = deltaSource + 'Y'; - var touches = pointerUtils.getTouchPair(event); - - var dx = touches[0][sourceX] - touches[1][sourceX]; - var dy = touches[0][sourceY] - touches[1][sourceY]; - - return hypot(dx, dy); - }, - - touchAngle: function (event, prevAngle, deltaSource) { - var sourceX = deltaSource + 'X'; - var sourceY = deltaSource + 'Y'; - var touches = pointerUtils.getTouchPair(event); - var dx = touches[1][sourceX] - touches[0][sourceX]; - var dy = touches[1][sourceY] - touches[0][sourceY]; - var angle = 180 * Math.atan2(dy, dx) / Math.PI; - - return angle; - } -}; - -module.exports = pointerUtils; - -},{"./browser":32,"./domObjects":33,"./hypot":38,"./isType":41,"./pointerExtend":43}],45:[function(require,module,exports){ -var vendors = ['ms', 'moz', 'webkit', 'o']; -var lastTime = 0; -var request = undefined; -var cancel = undefined; - -for (var x = 0; x < vendors.length && !window.requestAnimationFrame; x++) { - request = window[vendors[x] + 'RequestAnimationFrame']; - cancel = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']; -} - -if (!request) { - request = function (callback) { - var currTime = new Date().getTime(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = setTimeout(function () { - callback(currTime + timeToCall); - }, timeToCall); - - lastTime = currTime + timeToCall; - return id; - }; -} - -if (!cancel) { - cancel = function (id) { - clearTimeout(id); - }; -} - -module.exports = { - request: request, - cancel: cancel -}; - -},{}],46:[function(require,module,exports){ -var win = module.exports; -var isWindow = require('./isWindow'); - -function init(window) { - // get wrapped window if using Shadow DOM polyfill - - win.realWindow = window; - - // create a TextNode - var el = window.document.createTextNode(''); - - // check if it's wrapped by a polyfill - if (el.ownerDocument !== window.document && typeof window.wrap === 'function' && window.wrap(el) === el) { - // return wrapped window - win.window = window.wrap(window); - } - - // no Shadow DOM polyfil or native implementation - win.window = window; -} - -if (typeof window === 'undefined') { - win.window = undefined; - win.realWindow = undefined; -} else { - init(window); -} - -win.getWindow = function getWindow(node) { - if (isWindow(node)) { - return node; - } - - var rootNode = node.ownerDocument || node; - - return rootNode.defaultView || rootNode.parentWindow || win.window; -}; - -win.init = init; - -},{"./isWindow":42}]},{},[1])(1) -}); +} (typeof window === 'undefined'? undefined : window));